diff --git a/LiveDevelopment.js b/LiveDevelopment.js index 37d7392..85ccb15 100644 --- a/LiveDevelopment.js +++ b/LiveDevelopment.js @@ -355,7 +355,7 @@ define(function (require, exports, module) { $(liveDoc).on("updateDoc", function (event, url) { var path = _server.urlToPath(url), doc = getLiveDocForPath(path); - doc.updateBrowser(); + doc._updateBrowser(); }); } } diff --git a/documents/LiveCSSDocument.js b/documents/LiveCSSDocument.js index f17d889..f3f28ae 100644 --- a/documents/LiveCSSDocument.js +++ b/documents/LiveCSSDocument.js @@ -94,16 +94,18 @@ define(function LiveCSSDocumentModule(require, exports, module) { * @return {jQuery.promise} Promise resolved with the text content of this CSS document */ LiveCSSDocument.prototype.getSourceFromBrowser = function () { - // TODO: Only used for unit testing. Need to add protocol API to extract stylesheet from browser side. -// var deferred = new $.Deferred(), -// styleSheetId = this._getStyleSheetHeader().styleSheetId, -// inspectorPromise = Inspector.CSS.getStyleSheetText(styleSheetId); -// -// inspectorPromise.then(function (res) { -// deferred.resolve(res.text); -// }, deferred.reject); -// -// return deferred.promise(); + // Only used for unit testing. + var deferred = new $.Deferred(); + + this.protocol.getStyleSheetText(this.doc.url) + .then(function (res) { + deferred.resolve(res.text); + }) + .fail(function (err) { + deferred.reject(err); + }); + + return deferred.promise(); }; /** @@ -119,13 +121,13 @@ define(function LiveCSSDocumentModule(require, exports, module) { /** * When the user edits the file, update the stylesheet in the browser and redraw highlights. */ - LiveCSSDocument.prototype.updateBrowser = function () { + LiveCSSDocument.prototype._updateBrowser = function () { var i; for (i = 0; i < this.roots.length; i++) { if (this.doc.url !== this.roots[i]) { // if it's not directly included through , // reload the original doc - $(this).triggerHandler("updateDoc", this.roots[i]); + //$(this).triggerHandler("updateDoc", this.roots[i]); } else { this.protocol.evaluate("_LD.reloadCSS(" + JSON.stringify(this.doc.url) + ", " + @@ -177,7 +179,7 @@ define(function LiveCSSDocumentModule(require, exports, module) { * @param {Object} change */ LiveCSSDocument.prototype.onChange = function (event, editor, change) { - this.updateBrowser(); + this._updateBrowser(); }; /** diff --git a/protocol/LiveDevProtocol.js b/protocol/LiveDevProtocol.js index 71f5a67..48defc4 100644 --- a/protocol/LiveDevProtocol.js +++ b/protocol/LiveDevProtocol.js @@ -292,6 +292,25 @@ define(function (require, exports, module) { ); } + /** + * Protocol method. Rretrieves the content of a given stylesheet + * @param {number|Array.} clients A client ID or array of client IDs that should navigate to the given URL. + * @param {string} url Absolute URL that identifies the stylesheet. + * @return {$.Promise} A promise that's resolved with the return value from the first client that responds + * to the method. + */ + function getStyleSheetText(url, clients) { + return _send( + { + method: "CSS.getStyleSheetText", + params: { + url: url + } + }, + clients + ); + } + /** * Closes the connection to the given client. Proxies to the transport. * @param {number} clientId @@ -315,6 +334,7 @@ define(function (require, exports, module) { exports.evaluate = evaluate; exports.reload = reload; exports.navigate = navigate; + exports.getStyleSheetText = getStyleSheetText; exports.close = close; exports.getConnectionIds = getConnectionIds; exports.closeAllConnections = closeAllConnections; diff --git a/protocol/remote/LiveDevProtocolRemote.js b/protocol/remote/LiveDevProtocolRemote.js index 7df32fe..986492e 100644 --- a/protocol/remote/LiveDevProtocolRemote.js +++ b/protocol/remote/LiveDevProtocolRemote.js @@ -102,7 +102,7 @@ return; } response.id = orig.id; - this.send(JSON.stringify(response)); + this.send(response); }, /** @@ -143,7 +143,7 @@ console.log("Runtime.evaluate"); var result = eval(msg.params.expression); MessageBroker.respond(msg, { - result: JSON.stringify(result) // TODO: in original protocol this is an object handle + result: result // TODO: in original protocol this is an object handle }); } }; @@ -243,6 +243,40 @@ // exposing ProtocolManager global._Brackets_LiveDev_ProtocolManager = ProtocolManager; + /** + * CSS Domain. + */ + var CSS = { + /** + * retrieves the content of the stylesheet + * TODO: it now depends on reloadCSS implementation + */ + getStyleSheetText: function (msg) { + var i, + sheet, + text; + for (i = 0; i < document.styleSheets.length; i++) { + sheet = document.styleSheets[i]; + // if it was not 'reloaded' + if ((!sheet.disabled) && (sheet.href === msg.params.url)) { + var j, + rules = document.styleSheets[i].cssRules; + text = ""; + for (j = 0; j < rules.length; j++) { + text += rules[j].cssText + '\n'; + } + } else if (sheet.ownerNode.id === msg.params.url) { // if it was already 'reloaded' + text = sheet.ownerNode.innerText; + } + } + MessageBroker.respond(msg, { + text: text + }); + } + }; + + MessageBroker.on("CSS.getStyleSheetText", CSS.getStyleSheetText); + /** * The remote handler for the protocol. */ diff --git a/unittests.js b/unittests.js index ee8af77..0eb68b1 100644 --- a/unittests.js +++ b/unittests.js @@ -49,6 +49,10 @@ define(function (require, exports, module) { var testFolder = FileUtils.getNativeModuleDirectoryPath(module) + "/unittest-files/", allSpacesRE = /\s+/gi; + function fixSpaces(str) { + return str.replace(allSpacesRE, " "); + } + beforeEach(function () { // Create a new window that will be shared by ALL tests in this spec. if (!testWindow) { @@ -222,5 +226,92 @@ define(function (require, exports, module) { }); }); }); + + describe("CSS Editing", function () { + + it("should push changes through browser connection", function () { + var localText, + browserText, + liveDoc, + curDoc; + + runs(function () { + waitsForDone(SpecRunnerUtils.openProjectFiles(["simple1.html"]), "SpecRunnerUtils.openProjectFiles simple1.html", 1000); + }); + + waitsForLiveDevelopmentToOpen(); + + runs(function () { + waitsForDone(SpecRunnerUtils.openProjectFiles(["simple1.css"]), "SpecRunnerUtils.openProjectFiles simple1.css", 1000); + }); + runs(function () { + curDoc = DocumentManager.getCurrentDocument(); + localText = curDoc.getText(); + localText += "\n .testClass { background-color:#090; }\n"; + curDoc.setText(localText); + }); + runs(function () { + liveDoc = LiveDevelopment.getLiveDocForPath(testFolder + "simple1.css"); + }); + var doneSyncing = false; + runs(function () { + liveDoc.getSourceFromBrowser().done(function (text) { + browserText = text; + }).always(function () { + doneSyncing = true; + }); + }); + waitsFor(function () { return doneSyncing; }, "Browser to sync changes", 5000); + + runs(function () { + console.log('local:' + localText + ' - browser:' + browserText); + expect(fixSpaces(browserText)).toBe(fixSpaces(localText)); + }); + }); + + it("should push in memory css changes made before the session starts", function () { + var localText, + browserText; + + runs(function () { + waitsForDone(SpecRunnerUtils.openProjectFiles(["simple1.css"]), "SpecRunnerUtils.openProjectFiles simple1.css", 1000); + }); + + runs(function () { + var curDoc = DocumentManager.getCurrentDocument(); + localText = curDoc.getText(); + localText += "\n .testClass { background-color:#090; }\n"; + curDoc.setText(localText); + + // Document should not be marked dirty + expect(LiveDevelopment.status).not.toBe(LiveDevelopment.STATUS_OUT_OF_SYNC); + }); + + runs(function () { + waitsForDone(SpecRunnerUtils.openProjectFiles(["simple1.html"]), "SpecRunnerUtils.openProjectFiles simple1.html", 1000); + }); + + waitsForLiveDevelopmentToOpen(); + + + var liveDoc, doneSyncing = false; + runs(function () { + liveDoc = LiveDevelopment.getLiveDocForPath(testFolder + "simple1.css"); + }); + + runs(function () { + liveDoc.getSourceFromBrowser().done(function (text) { + browserText = text; + }).always(function () { + doneSyncing = true; + }); + }); + waitsFor(function () { return doneSyncing; }, "Browser to sync changes", 10000); + + runs(function () { + expect(fixSpaces(browserText)).toBe(fixSpaces(localText)); + }); + }); + }); }); });