/* 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 protocol = require("resource://devtools/shared/protocol.js");
const {
LongStringActor,
} = require("resource://devtools/server/actors/string.js");
const {
styleSheetsSpec,
} = require("resource://devtools/shared/specs/style-sheets.js");
const InspectorUtils = require("InspectorUtils");
loader.lazyRequireGetter(
this,
"UPDATE_GENERAL",
"resource://devtools/server/actors/style-sheet.js",
true
);
loader.lazyRequireGetter(
this,
"hasStyleSheetWatcherSupportForTarget",
"resource://devtools/server/actors/utils/stylesheets-manager.js",
true
);
/**
* Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
* stylesheets of a document.
*/
var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
/**
* The window we work with, taken from the parent actor.
*/
get window() {
return this.parentActor.window;
},
/**
* The current content document of the window we work with.
*/
get document() {
return this.window.document;
},
initialize(conn, targetActor) {
protocol.Actor.prototype.initialize.call(this, targetActor.conn);
this.parentActor = targetActor;
this._onApplicableStateChanged = this._onApplicableStateChanged.bind(this);
this._onNewStyleSheetActor = this._onNewStyleSheetActor.bind(this);
this._onWindowReady = this._onWindowReady.bind(this);
this._transitionSheetLoaded = false;
this.parentActor.on("stylesheet-added", this._onNewStyleSheetActor);
this.parentActor.on("window-ready", this._onWindowReady);
this.parentActor.chromeEventHandler.addEventListener(
"StyleSheetApplicableStateChanged",
this._onApplicableStateChanged,
true
);
},
getTraits() {
return {
traits: {},
};
},
destroy() {
for (const win of this.parentActor.windows) {
// This flag only exists for devtools, so we are free to clear
// it when we're done.
win.document.styleSheetChangeEventsEnabled = false;
}
this.parentActor.off("stylesheet-added", this._onNewStyleSheetActor);
this.parentActor.off("window-ready", this._onWindowReady);
this.parentActor.chromeEventHandler.removeEventListener(
"StyleSheetApplicableStateChanged",
this._onApplicableStateChanged,
true
);
protocol.Actor.prototype.destroy.call(this);
},
/**
* Event handler that is called when a the target actor emits window-ready.
*
* @param {Event} evt
* The triggering event.
*/
_onWindowReady(evt) {
this._addStyleSheets(evt.window);
},
/**
* Event handler that is called when a the target actor emits stylesheet-added.
*
* @param {StyleSheetActor} actor
* The new style sheet actor.
*/
_onNewStyleSheetActor(actor) {
const info = this._addingStyleSheetInfo?.get(actor.rawSheet);
this._addingStyleSheetInfo?.delete(actor.rawSheet);
// Forward it to the client side.
this.emit(
"stylesheet-added",
actor,
info ? info.isNew : false,
info ? info.fileName : null
);
},
/**
* Protocol method for getting a list of StyleSheetActors representing
* all the style sheets in this document.
*/
async getStyleSheets() {
let actors = [];
const windows = this.parentActor.windows;
for (const win of windows) {
const sheets = await this._addStyleSheets(win);
actors = actors.concat(sheets);
}
return actors;
},
/**
* Check if we should be showing this stylesheet.
*
* @param {DOMCSSStyleSheet} sheet
* Stylesheet we're interested in
*
* @return boolean
* Whether the stylesheet should be listed.
*/
_shouldListSheet(sheet) {
// Special case about:PreferenceStyleSheet, as it is generated on the
// fly and the URI is not registered with the about: handler.
// https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
if (sheet.href?.toLowerCase() === "about:preferencestylesheet") {
return false;
}
return true;
},
/**
* Event handler that is called when the state of applicable of style sheet is changed.
*
* For now, StyleSheetApplicableStateChanged event will be called at following timings.
* - Append of stylesheet to document
* - Append