/* 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"; const {Cc, Ci} = require("chrome"); const Services = require("Services"); const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm"); const promise = require("promise"); const events = require("sdk/event/core"); const protocol = require("devtools/shared/protocol"); const {Arg, method, RetVal} = protocol; const {fetch} = require("devtools/shared/DevToolsUtils"); const {oldStyleSheetSpec, styleEditorSpec} = require("devtools/shared/specs/styleeditor"); loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic")); var TRANSITION_CLASS = "moz-styleeditor-transitioning"; var TRANSITION_DURATION_MS = 500; var TRANSITION_RULE = "\ :root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\ transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \ transition-delay: 0ms !important;\ transition-timing-function: ease-out !important;\ transition-property: all !important;\ }"; var LOAD_ERROR = "error-load"; var OldStyleSheetActor = protocol.ActorClassWithSpec(oldStyleSheetSpec, { toString: function() { return "[OldStyleSheetActor " + this.actorID + "]"; }, /** * Window of target */ get window() { return this._window || this.parentActor.window; }, /** * Document of target. */ get document() { return this.window.document; }, /** * URL of underlying stylesheet. */ get href() { return this.rawSheet.href; }, /** * Retrieve the index (order) of stylesheet in the document. * * @return number */ get styleSheetIndex() { if (this._styleSheetIndex == -1) { for (let i = 0; i < this.document.styleSheets.length; i++) { if (this.document.styleSheets[i] == this.rawSheet) { this._styleSheetIndex = i; break; } } } return this._styleSheetIndex; }, initialize: function (aStyleSheet, aParentActor, aWindow) { protocol.Actor.prototype.initialize.call(this, null); this.rawSheet = aStyleSheet; this.parentActor = aParentActor; this.conn = this.parentActor.conn; this._window = aWindow; // text and index are unknown until source load this.text = null; this._styleSheetIndex = -1; this._transitionRefCount = 0; // if this sheet has an @import, then it's rules are loaded async let ownerNode = this.rawSheet.ownerNode; if (ownerNode) { let onSheetLoaded = (event) => { ownerNode.removeEventListener("load", onSheetLoaded, false); this._notifyPropertyChanged("ruleCount"); }; ownerNode.addEventListener("load", onSheetLoaded, false); } }, /** * Get the current state of the actor * * @return {object} * With properties of the underlying stylesheet, plus 'text', * 'styleSheetIndex' and 'parentActor' if it's @imported */ form: function (detail) { if (detail === "actorid") { return this.actorID; } let docHref; if (this.rawSheet.ownerNode) { if (this.rawSheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) { docHref = this.rawSheet.ownerNode.location.href; } if (this.rawSheet.ownerNode.ownerDocument) { docHref = this.rawSheet.ownerNode.ownerDocument.location.href; } } let form = { actor: this.actorID, // actorID is set when this actor is added to a pool href: this.href, nodeHref: docHref, disabled: this.rawSheet.disabled, title: this.rawSheet.title, system: !CssLogic.isContentStylesheet(this.rawSheet), styleSheetIndex: this.styleSheetIndex }; try { form.ruleCount = this.rawSheet.cssRules.length; } catch (e) { // stylesheet had an @import rule that wasn't loaded yet } return form; }, /** * Toggle the disabled property of the style sheet * * @return {object} * 'disabled' - the disabled state after toggling. */ toggleDisabled: function () { this.rawSheet.disabled = !this.rawSheet.disabled; this._notifyPropertyChanged("disabled"); return this.rawSheet.disabled; }, /** * Send an event notifying that a property of the stylesheet * has changed. * * @param {string} property * Name of the changed property */ _notifyPropertyChanged: function (property) { events.emit(this, "property-change", property, this.form()[property]); }, /** * Fetch the source of the style sheet from its URL. Send a "sourceLoad" * event when it's been fetched. */ fetchSource: function () { this._getText().then((content) => { events.emit(this, "source-load", this.text); }); }, /** * Fetch the text for this stylesheet from the cache or network. Return * cached text if it's already been fetched. * * @return {Promise} * Promise that resolves with a string text of the stylesheet. */ _getText: function () { if (this.text) { return promise.resolve(this.text); } if (!this.href) { // this is an inline