diff options
Diffstat (limited to 'browser/devtools/styleeditor/StyleEditorDebuggee.jsm')
-rw-r--r-- | browser/devtools/styleeditor/StyleEditorDebuggee.jsm | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm new file mode 100644 index 000000000..48c346dd9 --- /dev/null +++ b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm @@ -0,0 +1,342 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["StyleEditorDebuggee", "StyleSheet"]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource:///modules/devtools/shared/event-emitter.js"); + +XPCOMUtils.defineLazyModuleGetter(this, "Promise", + "resource://gre/modules/commonjs/sdk/core/promise.js"); + +/** + * A StyleEditorDebuggee represents the document the style editor is debugging. + * It maintains a list of StyleSheet objects that represent the stylesheets in + * the target's document. It wraps remote debugging protocol comunications. + * + * It emits these events: + * 'document-load': debuggee's document is loaded, style sheets are argument + * 'stylesheets-cleared': The debuggee's stylesheets have been reset (e.g. the + * page navigated) + * + * @param {Target} target + * The target the debuggee is listening to + */ +let StyleEditorDebuggee = function(target) { + EventEmitter.decorate(this); + + this.styleSheets = []; + + this.clear = this.clear.bind(this); + this._onNewDocument = this._onNewDocument.bind(this); + this._onDocumentLoad = this._onDocumentLoad.bind(this); + + this._target = target; + this._actor = this.target.form.styleEditorActor; + + this.client.addListener("documentLoad", this._onDocumentLoad); + this._target.on("navigate", this._onNewDocument); + + this._onNewDocument(); +} + +StyleEditorDebuggee.prototype = { + /** + * list of StyleSheet objects for this target + */ + styleSheets: null, + + /** + * baseURIObject for the current document + */ + baseURI: null, + + /** + * The target we're debugging + */ + get target() { + return this._target; + }, + + /** + * Client for communicating with server with remote debug protocol. + */ + get client() { + return this._target.client; + }, + + /** + * Get the StyleSheet object with the given href. + * + * @param {string} href + * Url of the stylesheet to find + * @return {StyleSheet} + * StyleSheet with the matching href + */ + styleSheetFromHref: function(href) { + for (let sheet of this.styleSheets) { + if (sheet.href == href) { + return sheet; + } + } + return null; + }, + + /** + * Clear stylesheets and state. + */ + clear: function() { + this.baseURI = null; + this.clearStyleSheets(); + }, + + /** + * Clear stylesheets. + */ + clearStyleSheets: function() { + for (let stylesheet of this.styleSheets) { + stylesheet.destroy(); + } + this.styleSheets = []; + this.emit("stylesheets-cleared"); + }, + + /** + * Called when target is created or has navigated. + * Clear previous sheets and request new document's + */ + _onNewDocument: function() { + this.clear(); + + this._getBaseURI(); + + let message = { type: "newDocument" }; + this._sendRequest(message); + }, + + /** + * request baseURIObject information from the document + */ + _getBaseURI: function() { + let message = { type: "getBaseURI" }; + this._sendRequest(message, (response) => { + this.baseURI = response.baseURI; + }); + }, + + /** + * Handler for document load, forward event with + * all the stylesheets available on load. + * + * @param {string} type + * Event type + * @param {object} request + * Object with 'styleSheets' array of actor forms + */ + _onDocumentLoad: function(type, request) { + if (this.styleSheets.length > 0) { + this.clearStyleSheets(); + } + let sheets = []; + for (let form of request.styleSheets) { + let sheet = this._addStyleSheet(form); + sheets.push(sheet); + } + this.emit("document-load", sheets); + }, + + /** + * Create a new StyleSheet object from the form + * and add to our stylesheet list. + * + * @param {object} form + * Initial properties of the stylesheet + */ + _addStyleSheet: function(form) { + let sheet = new StyleSheet(form, this); + this.styleSheets.push(sheet); + return sheet; + }, + + /** + * Create a new stylesheet with the given text + * and attach it to the document. + * + * @param {string} text + * Initial text of the stylesheet + * @param {function} callback + * Function to call when the stylesheet has been added to the document + */ + createStyleSheet: function(text, callback) { + let message = { type: "newStyleSheet", text: text }; + this._sendRequest(message, (response) => { + let sheet = this._addStyleSheet(response.styleSheet); + callback(sheet); + }); + }, + + /** + * Send a request to our actor on the server + * + * @param {object} message + * Message to send to the actor + * @param {function} callback + * Function to call with reponse from actor + */ + _sendRequest: function(message, callback) { + message.to = this._actor; + this.client.request(message, callback); + }, + + /** + * Clean up and remove listeners + */ + destroy: function() { + this.clear(); + + this._target.off("navigate", this._onNewDocument); + } +} + +/** + * A StyleSheet object represents a stylesheet on the debuggee. It wraps + * communication with a complimentary StyleSheetActor on the server. + * + * It emits these events: + * 'source-load' - The full text source of the stylesheet has been fetched + * 'property-change' - Any property (e.g 'disabled') has changed + * 'style-applied' - A change has been applied to the live stylesheet on the server + * 'error' - An error occured when loading or saving stylesheet + * + * @param {object} form + * Initial properties of the stylesheet + * @param {StyleEditorDebuggee} debuggee + * Owner of the stylesheet + */ +let StyleSheet = function(form, debuggee) { + EventEmitter.decorate(this); + + this.debuggee = debuggee; + this._client = debuggee.client; + this._actor = form.actor; + + this._onSourceLoad = this._onSourceLoad.bind(this); + this._onPropertyChange = this._onPropertyChange.bind(this); + this._onError = this._onError.bind(this); + this._onStyleApplied = this._onStyleApplied.bind(this); + + this._client.addListener("sourceLoad-" + this._actor, this._onSourceLoad); + this._client.addListener("propertyChange-" + this._actor, this._onPropertyChange); + this._client.addListener("error-" + this._actor, this._onError); + this._client.addListener("styleApplied-" + this._actor, this._onStyleApplied); + + // set initial property values + for (let attr in form) { + this[attr] = form[attr]; + } +} + +StyleSheet.prototype = { + /** + * Toggle the disabled attribute of the stylesheet + */ + toggleDisabled: function() { + let message = { type: "toggleDisabled" }; + this._sendRequest(message); + }, + + /** + * Request that the source of the stylesheet be fetched. + * 'source-load' event will be fired when it's been fetched. + */ + fetchSource: function() { + let message = { type: "fetchSource" }; + this._sendRequest(message); + }, + + /** + * Update the stylesheet in place with the given full source. + * + * @param {string} sheetText + * Full text to update the stylesheet with + */ + update: function(sheetText) { + let message = { type: "update", text: sheetText, transition: true }; + this._sendRequest(message); + }, + + /** + * Handle source load event from the client. + * + * @param {string} type + * Event type + * @param {object} request + * Event details + */ + _onSourceLoad: function(type, request) { + this.emit("source-load", request.source); + }, + + /** + * Handle a property change on the stylesheet + * + * @param {string} type + * Event type + * @param {object} request + * Event details + */ + _onPropertyChange: function(type, request) { + this[request.property] = request.value; + this.emit("property-change", request.property); + }, + + /** + * Propogate errors from the server that relate to this stylesheet. + * + * @param {string} type + * Event type + * @param {object} request + * Event details + */ + _onError: function(type, request) { + this.emit("error", request.errorMessage); + }, + + /** + * Handle event when update has been successfully applied and propogate it. + */ + _onStyleApplied: function() { + this.emit("style-applied"); + }, + + /** + * Send a request to our actor on the server + * + * @param {object} message + * Message to send to the actor + * @param {function} callback + * Function to call with reponse from actor + */ + _sendRequest: function(message, callback) { + message.to = this._actor; + this._client.request(message, callback); + }, + + /** + * Clean up and remove event listeners + */ + destroy: function() { + this._client.removeListener("sourceLoad-" + this._actor, this._onSourceLoad); + this._client.removeListener("propertyChange-" + this._actor, this._onPropertyChange); + this._client.removeListener("error-" + this._actor, this._onError); + this._client.removeListener("styleApplied-" + this._actor, this._onStyleApplied); + } +} |