зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1664941: Connect style actor and stylesheet watcher. r=jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D90651
This commit is contained in:
Родитель
72da707476
Коммит
473676fda0
|
@ -21,6 +21,13 @@ const {
|
|||
unescapeCSSComment,
|
||||
} = require("devtools/shared/css/parsing-utils");
|
||||
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
["getIndentationFromPrefs", "getIndentationFromString"],
|
||||
"devtools/shared/indentation",
|
||||
true
|
||||
);
|
||||
|
||||
// Used to test whether a newline appears anywhere in some text.
|
||||
const NEWLINE_RX = /[\r\n]/;
|
||||
// Used to test whether a bit of text starts an empty comment, either
|
||||
|
@ -474,7 +481,28 @@ RuleRewriter.prototype = {
|
|||
* that holds the default indentation that should be used
|
||||
* for edits to the rule.
|
||||
*/
|
||||
getDefaultIndentation: function() {
|
||||
getDefaultIndentation: async function() {
|
||||
if (!this.rule.parentStyleSheet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.rule.parentStyleSheet.resourceId) {
|
||||
const prefIndent = getIndentationFromPrefs();
|
||||
if (prefIndent) {
|
||||
const { indentUnit, indentWithTabs } = prefIndent;
|
||||
return indentWithTabs ? "\t" : " ".repeat(indentUnit);
|
||||
}
|
||||
|
||||
const styleSheetsFront = await this.rule.targetFront.getFront(
|
||||
"stylesheets"
|
||||
);
|
||||
const { str: source } = await styleSheetsFront.getText(
|
||||
this.rule.parentStyleSheet.resourceId
|
||||
);
|
||||
const { indentUnit, indentWithTabs } = getIndentationFromString(source);
|
||||
return indentWithTabs ? "\t" : " ".repeat(indentUnit);
|
||||
}
|
||||
|
||||
return this.rule.parentStyleSheet.guessIndentation();
|
||||
},
|
||||
|
||||
|
|
|
@ -223,6 +223,14 @@ class StyleRuleFront extends FrontClassWithSpec(styleRuleSpec) {
|
|||
}
|
||||
|
||||
get parentStyleSheet() {
|
||||
const resourceWatcher = this.parentFront.resourceWatcher;
|
||||
if (resourceWatcher) {
|
||||
return resourceWatcher.getResourceById(
|
||||
resourceWatcher.TYPES.STYLESHEET,
|
||||
this._form.parentStyleSheet
|
||||
);
|
||||
}
|
||||
|
||||
return this.conn.getFrontByID(this._form.parentStyleSheet);
|
||||
}
|
||||
|
||||
|
|
|
@ -1391,7 +1391,8 @@ function SelectorView(tree, selectorInfo) {
|
|||
};
|
||||
this.sourceMapURLService = this.tree.inspector.toolbox.sourceMapURLService;
|
||||
this._unsubscribeCallback = this.sourceMapURLService.subscribeByID(
|
||||
this.generatedLocation.sheet.actorID,
|
||||
this.generatedLocation.sheet.resourceId ||
|
||||
this.generatedLocation.sheet.actorID,
|
||||
this.generatedLocation.line,
|
||||
this.generatedLocation.column,
|
||||
this._updateLocation
|
||||
|
|
|
@ -205,6 +205,21 @@ Inspector.prototype = {
|
|||
// available for the top-level target.
|
||||
this._onFirstMarkupLoaded = this.once("markuploaded");
|
||||
|
||||
// If the server-side stylesheet watcher is enabled, we should start to watch
|
||||
// stylesheet resources before instanciating the inspector front since pageStyle
|
||||
// actor should refer the watcher.
|
||||
if (
|
||||
this.toolbox.resourceWatcher.hasWatcherSupport(
|
||||
this.toolbox.resourceWatcher.TYPES.STYLESHEET
|
||||
)
|
||||
) {
|
||||
this._isServerSideStyleSheetWatcherEnabled = true;
|
||||
await this.toolbox.resourceWatcher.watchResources(
|
||||
[this.toolbox.resourceWatcher.TYPES.STYLESHEET],
|
||||
{ onAvailable: this.onResourceAvailable }
|
||||
);
|
||||
}
|
||||
|
||||
await this.toolbox.targetList.watchTargets(
|
||||
[this.toolbox.targetList.TYPES.FRAME],
|
||||
this._onTargetAvailable,
|
||||
|
@ -258,6 +273,11 @@ Inspector.prototype = {
|
|||
async initInspectorFront(targetFront) {
|
||||
this.inspectorFront = await targetFront.getFront("inspector");
|
||||
this.walker = this.inspectorFront.walker;
|
||||
|
||||
// PageStyle front need the resource watcher when the server-side stylesheet watcher is enabled.
|
||||
if (this._isServerSideStyleSheetWatcherEnabled) {
|
||||
this.inspectorFront.pageStyle.resourceWatcher = this.toolbox.resourceWatcher;
|
||||
}
|
||||
},
|
||||
|
||||
get toolbox() {
|
||||
|
|
|
@ -348,7 +348,7 @@ RuleEditor.prototype = {
|
|||
this._unsubscribeSourceMap();
|
||||
}
|
||||
this._unsubscribeSourceMap = this.sourceMapURLService.subscribeByID(
|
||||
this.rule.sheet.actorID,
|
||||
this.rule.sheet.resourceId || this.rule.sheet.actorID,
|
||||
this.rule.ruleLine,
|
||||
this.rule.ruleColumn,
|
||||
this._updateLocation
|
||||
|
|
|
@ -37,7 +37,7 @@ exports.viewSourceInStyleEditor = async function(
|
|||
const originalLocation = stylesheetFront
|
||||
? await getOriginalLocation(
|
||||
toolbox,
|
||||
stylesheetFront.actorID,
|
||||
stylesheetFront.resourceId,
|
||||
generatedLine,
|
||||
generatedColumn
|
||||
)
|
||||
|
|
|
@ -14,7 +14,8 @@ add_task(async function() {
|
|||
// below because the test-page is loaded with chrome:// URL and, right now,
|
||||
// that means UA stylesheets are shown. So we avoid hardcoding the number of
|
||||
// stylesheets here.
|
||||
ok(ui.editors.length, "The UI contains style sheets.");
|
||||
await waitUntil(() => ui.editors.length);
|
||||
ok(true, "The UI contains style sheets.");
|
||||
|
||||
const rootEl = panel.panelWindow.document.getElementById(
|
||||
"style-editor-chrome"
|
||||
|
|
|
@ -45,6 +45,8 @@ add_task(async function() {
|
|||
content.console.log(msg);
|
||||
});
|
||||
await onMessage;
|
||||
|
||||
await hud.toolbox.sourceMapURLService.waitForSourcesLoading();
|
||||
}
|
||||
|
||||
const onConsolesDestroyed = waitForNEvents("web-console-destroyed", 4);
|
||||
|
|
|
@ -33,6 +33,12 @@ loader.lazyRequireGetter(
|
|||
"devtools/shared/layout/utils",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
["UPDATE_GENERAL", "UPDATE_PRESERVING_RULES"],
|
||||
"devtools/server/actors/stylesheets",
|
||||
true
|
||||
);
|
||||
|
||||
const TRANSITION_PSEUDO_CLASS = ":-moz-styleeditor-transitioning";
|
||||
const TRANSITION_DURATION_MS = 500;
|
||||
|
@ -49,14 +55,19 @@ const TRANSITION_SHEET =
|
|||
}
|
||||
`);
|
||||
|
||||
// If the user edits a stylesheet, we stash a copy of the edited text
|
||||
// here, keyed by the stylesheet. This way, if the tools are closed
|
||||
// and then reopened, the edited text will be available. A weak map
|
||||
// is used so that navigation by the user will eventually cause the
|
||||
// edited text to be collected.
|
||||
const modifiedStyleSheets = new WeakMap();
|
||||
|
||||
class StyleSheetWatcher {
|
||||
constructor() {
|
||||
this._resourceCount = 0;
|
||||
// The _styleSheetMap maps resouceId and following value.
|
||||
// The _styleSheetMap maps resourceId and following value.
|
||||
// {
|
||||
// styleSheet: Raw StyleSheet object.
|
||||
// modifiedText: Content of the stylesheet updated by update function.
|
||||
// In case not updating, this value is undefined.
|
||||
// }
|
||||
this._styleSheetMap = new Map();
|
||||
// List of all watched media queries. Change listeners are being registered from _getMediaRules.
|
||||
|
@ -97,11 +108,7 @@ class StyleSheetWatcher {
|
|||
styleSheets.push(...(await this._getStyleSheets(window)));
|
||||
}
|
||||
|
||||
this._onAvailable(
|
||||
await Promise.all(
|
||||
styleSheets.map(styleSheet => this._toResource(styleSheet))
|
||||
)
|
||||
);
|
||||
await this._notifyResourcesAvailable(styleSheets);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,11 +136,45 @@ class StyleSheetWatcher {
|
|||
// This triggers StyleSheetApplicableStateChanged event.
|
||||
parent.appendChild(style);
|
||||
|
||||
// This promise will be resolved when the resource for this stylesheet is available.
|
||||
let resolve = null;
|
||||
const promise = new Promise(r => {
|
||||
resolve = r;
|
||||
});
|
||||
|
||||
if (!this._stylesheetCreationData) {
|
||||
this._stylesheetCreationData = new WeakMap();
|
||||
}
|
||||
// style.sheet will be available after the style element is appneded.
|
||||
this._stylesheetCreationData.set(style.sheet, { fileName });
|
||||
this._stylesheetCreationData.set(style.sheet, {
|
||||
isCreatedByDevTools: true,
|
||||
fileName,
|
||||
resolve,
|
||||
});
|
||||
|
||||
await promise;
|
||||
|
||||
return style.sheet;
|
||||
}
|
||||
|
||||
async ensureResourceAvailable(styleSheet) {
|
||||
if (this.getResourceId(styleSheet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._notifyResourcesAvailable([styleSheet]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return resourceId of the given style sheet.
|
||||
*/
|
||||
getResourceId(styleSheet) {
|
||||
for (const [resourceId, value] of this._styleSheetMap.entries()) {
|
||||
if (styleSheet === value.styleSheet) {
|
||||
return resourceId;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,11 +185,29 @@ class StyleSheetWatcher {
|
|||
return styleSheet.ownerNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the style sheet of the given resource id.
|
||||
*/
|
||||
getStyleSheet(resourceId) {
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
return styleSheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of given stylesheet of the given resource id.
|
||||
*/
|
||||
getStyleSheetIndex(resourceId) {
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
return this._getStyleSheetIndex(styleSheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocol method to get the text of stylesheet of resourceId.
|
||||
*/
|
||||
async getText(resourceId) {
|
||||
const { styleSheet, modifiedText } = this._styleSheetMap.get(resourceId);
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
|
||||
const modifiedText = modifiedStyleSheets.get(styleSheet);
|
||||
|
||||
// modifiedText is the content of the stylesheet updated by update function.
|
||||
// In case not updating, this is undefined.
|
||||
|
@ -181,27 +240,33 @@ class StyleSheetWatcher {
|
|||
/**
|
||||
* Update the style sheet in place with new text.
|
||||
*
|
||||
* @param {object} request
|
||||
* 'text' - new text
|
||||
* 'transition' - whether to do CSS transition for change.
|
||||
* @param {String} resourceId
|
||||
* @param {String} text
|
||||
* New text.
|
||||
* @param {Boolean} transition
|
||||
* Whether to do CSS transition for change.
|
||||
* @param {Number} kind
|
||||
* Either UPDATE_PRESERVING_RULES or UPDATE_GENERAL
|
||||
*/
|
||||
async update(resourceId, text, transition) {
|
||||
async update(resourceId, text, transition, kind = UPDATE_GENERAL) {
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
|
||||
InspectorUtils.parseStyleSheet(styleSheet, text);
|
||||
modifiedStyleSheets.set(styleSheet, text);
|
||||
|
||||
this._styleSheetMap.set(resourceId, { styleSheet, modifiedText: text });
|
||||
|
||||
this._notifyPropertyChanged(
|
||||
resourceId,
|
||||
"ruleCount",
|
||||
styleSheet.cssRules.length
|
||||
);
|
||||
if (kind !== UPDATE_PRESERVING_RULES) {
|
||||
this._notifyPropertyChanged(
|
||||
resourceId,
|
||||
"ruleCount",
|
||||
styleSheet.cssRules.length
|
||||
);
|
||||
}
|
||||
|
||||
if (transition) {
|
||||
this._startTransition(resourceId);
|
||||
this._startTransition(resourceId, kind);
|
||||
} else {
|
||||
this._updateResource(resourceId, "style-applied");
|
||||
this._notifyResourceUpdated(resourceId, "style-applied", {
|
||||
event: { kind },
|
||||
});
|
||||
}
|
||||
|
||||
// Remove event handler from all media query list we set to.
|
||||
|
@ -210,10 +275,12 @@ class StyleSheetWatcher {
|
|||
}
|
||||
|
||||
const mediaRules = await this._getMediaRules(resourceId, styleSheet);
|
||||
this._updateResource(resourceId, "media-rules-changed", { mediaRules });
|
||||
this._notifyResourceUpdated(resourceId, "media-rules-changed", {
|
||||
resourceUpdates: { mediaRules },
|
||||
});
|
||||
}
|
||||
|
||||
_startTransition(resourceId) {
|
||||
_startTransition(resourceId, kind) {
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
const document = styleSheet.ownerNode.ownerDocument;
|
||||
const window = styleSheet.ownerNode.ownerGlobal;
|
||||
|
@ -232,18 +299,20 @@ class StyleSheetWatcher {
|
|||
// @see _onTransitionEnd
|
||||
window.clearTimeout(this._transitionTimeout);
|
||||
this._transitionTimeout = window.setTimeout(
|
||||
this._onTransitionEnd.bind(this, resourceId),
|
||||
this._onTransitionEnd.bind(this, resourceId, kind),
|
||||
TRANSITION_DURATION_MS + TRANSITION_BUFFER_MS
|
||||
);
|
||||
}
|
||||
|
||||
_onTransitionEnd(resourceId) {
|
||||
_onTransitionEnd(resourceId, kind) {
|
||||
const { styleSheet } = this._styleSheetMap.get(resourceId);
|
||||
const document = styleSheet.ownerNode.ownerDocument;
|
||||
|
||||
this._transitionTimeout = null;
|
||||
removePseudoClassLock(document.documentElement, TRANSITION_PSEUDO_CLASS);
|
||||
this._updateResource(resourceId, "style-applied");
|
||||
this._notifyResourceUpdated(resourceId, "style-applied", {
|
||||
event: { kind },
|
||||
});
|
||||
}
|
||||
|
||||
async _fetchStylesheet(styleSheet) {
|
||||
|
@ -395,19 +464,14 @@ class StyleSheetWatcher {
|
|||
}
|
||||
|
||||
_onMatchesChange(resourceId, index, mql) {
|
||||
this._onUpdated([
|
||||
{
|
||||
resourceType: STYLESHEET,
|
||||
resourceId,
|
||||
updateType: "matches-change",
|
||||
nestedResourceUpdates: [
|
||||
{
|
||||
path: ["mediaRules", index, "matches"],
|
||||
value: mql.matches,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
this._notifyResourceUpdated(resourceId, "matches-change", {
|
||||
nestedResourceUpdates: [
|
||||
{
|
||||
path: ["mediaRules", index, "matches"],
|
||||
value: mql.matches,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
_getNodeHref(styleSheet) {
|
||||
|
@ -495,8 +559,8 @@ class StyleSheetWatcher {
|
|||
}
|
||||
|
||||
_notifyPropertyChanged(resourceId, property, value) {
|
||||
this._updateResource(resourceId, "property-change", {
|
||||
[property]: value,
|
||||
this._notifyResourceUpdated(resourceId, "property-change", {
|
||||
resourceUpdates: { [property]: value },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -519,7 +583,7 @@ class StyleSheetWatcher {
|
|||
*/
|
||||
async _onApplicableStateChanged({ applicable, stylesheet: styleSheet }) {
|
||||
for (const existing of this._styleSheetMap.values()) {
|
||||
if (styleSheet === existing.styleSheet) {
|
||||
if (existing.styleSheet === styleSheet) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -532,14 +596,7 @@ class StyleSheetWatcher {
|
|||
this._shouldListSheet(styleSheet) &&
|
||||
!this._haveAncestorWithSameURL(styleSheet)
|
||||
) {
|
||||
const creationData = this._stylesheetCreationData?.get(styleSheet);
|
||||
this._onAvailable([
|
||||
await this._toResource(styleSheet, {
|
||||
isCreatedByDevTools: !!creationData,
|
||||
fileName: creationData?.fileName,
|
||||
}),
|
||||
]);
|
||||
this._stylesheetCreationData?.delete(styleSheet);
|
||||
await this._notifyResourcesAvailable([styleSheet]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -559,6 +616,7 @@ class StyleSheetWatcher {
|
|||
{ isCreatedByDevTools = false, fileName = null } = {}
|
||||
) {
|
||||
const resourceId = `stylesheet:${this._resourceCount++}`;
|
||||
|
||||
const resource = {
|
||||
resourceId,
|
||||
resourceType: STYLESHEET,
|
||||
|
@ -576,18 +634,46 @@ class StyleSheetWatcher {
|
|||
title: styleSheet.title,
|
||||
};
|
||||
|
||||
this._styleSheetMap.set(resource.resourceId, { styleSheet });
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
_updateResource(resourceId, updateType, resourceUpdates) {
|
||||
async _notifyResourcesAvailable(styleSheets) {
|
||||
const resources = await Promise.all(
|
||||
styleSheets.map(async styleSheet => {
|
||||
const creationData = this._stylesheetCreationData?.get(styleSheet);
|
||||
|
||||
const resource = await this._toResource(styleSheet, {
|
||||
isCreatedByDevTools: creationData?.isCreatedByDevTools,
|
||||
fileName: creationData?.fileName,
|
||||
});
|
||||
|
||||
this._styleSheetMap.set(resource.resourceId, { styleSheet });
|
||||
return resource;
|
||||
})
|
||||
);
|
||||
|
||||
await this._onAvailable(resources);
|
||||
|
||||
for (const styleSheet of styleSheets) {
|
||||
const creationData = this._stylesheetCreationData?.get(styleSheet);
|
||||
creationData?.resolve();
|
||||
this._stylesheetCreationData?.delete(styleSheet);
|
||||
}
|
||||
}
|
||||
|
||||
_notifyResourceUpdated(
|
||||
resourceId,
|
||||
updateType,
|
||||
{ resourceUpdates, nestedResourceUpdates, event }
|
||||
) {
|
||||
this._onUpdated([
|
||||
{
|
||||
resourceType: STYLESHEET,
|
||||
resourceId,
|
||||
updateType,
|
||||
resourceUpdates,
|
||||
nestedResourceUpdates,
|
||||
event,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,11 @@ const {
|
|||
ELEMENT_STYLE,
|
||||
} = require("devtools/shared/specs/styles");
|
||||
|
||||
const {
|
||||
TYPES,
|
||||
getResourceWatcher,
|
||||
} = require("devtools/server/actors/resources/index");
|
||||
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"CssLogic",
|
||||
|
@ -126,6 +131,20 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
this._observedRules = [];
|
||||
this._styleApplied = this._styleApplied.bind(this);
|
||||
this._watchedSheets = new Set();
|
||||
|
||||
const watcher = getResourceWatcher(
|
||||
this.inspector.targetActor,
|
||||
TYPES.STYLESHEET
|
||||
);
|
||||
|
||||
if (watcher) {
|
||||
this.styleSheetWatcher = watcher;
|
||||
this.onResourceUpdated = this.onResourceUpdated.bind(this);
|
||||
this.inspector.targetActor.on(
|
||||
"resource-updated-form",
|
||||
this.onResourceUpdated
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
@ -193,7 +212,7 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
// the keyframe cache.
|
||||
this.cssLogic.reset();
|
||||
if (kind === UPDATE_GENERAL) {
|
||||
this.emit("stylesheet-updated", styleSheet);
|
||||
this.emit("stylesheet-updated");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -235,6 +254,14 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
* The actor for this style sheet
|
||||
*/
|
||||
_sheetRef: function(sheet) {
|
||||
if (this.styleSheetWatcher) {
|
||||
// We need to clean up this function in bug 1672090 when server-side stylesheet
|
||||
// watcher is enabled.
|
||||
console.warn(
|
||||
"This function should not be called when server-side stylesheet watcher is enabled"
|
||||
);
|
||||
}
|
||||
|
||||
const targetActor = this.inspector.targetActor;
|
||||
const actor = targetActor.createStyleSheetActor(sheet);
|
||||
return actor;
|
||||
|
@ -953,6 +980,15 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
ruleSet.add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to clean up the following codes when server-side stylesheet
|
||||
// watcher is enabled in bug 1672090.
|
||||
if (this.styleSheetWatcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const rule of ruleSet) {
|
||||
if (rule.rawRule.parentStyleSheet) {
|
||||
const parent = this._sheetRef(rule.rawRule.parentStyleSheet);
|
||||
if (!sheetSet.has(parent)) {
|
||||
|
@ -1074,6 +1110,36 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
}
|
||||
},
|
||||
|
||||
onResourceUpdated(resources) {
|
||||
const kinds = new Set();
|
||||
|
||||
for (const resource of resources) {
|
||||
if (resource.resourceType !== TYPES.STYLESHEET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource.updateType !== "style-applied") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const kind = resource.event.kind;
|
||||
kinds.add(kind);
|
||||
|
||||
for (const styleActor of [...this.refMap.values()]) {
|
||||
const resourceId = this.styleSheetWatcher.getResourceId(
|
||||
styleActor._parentSheet
|
||||
);
|
||||
if (resource.resourceId === resourceId) {
|
||||
styleActor._onStyleApplied(kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const kind of kinds) {
|
||||
this._styleApplied(kind);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to addNewRule to get or create a style tag in the provided
|
||||
* document.
|
||||
|
@ -1116,8 +1182,20 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
* @returns {StyleRuleActor} the new rule
|
||||
*/
|
||||
async addNewRule(node, pseudoClasses) {
|
||||
const style = this.getStyleElement(node.rawNode.ownerDocument);
|
||||
const sheet = style.sheet;
|
||||
let sheet = null;
|
||||
if (this.styleSheetWatcher) {
|
||||
const doc = node.rawNode.ownerDocument;
|
||||
if (this.styleElements.has(doc)) {
|
||||
sheet = this.styleElements.get(doc);
|
||||
} else {
|
||||
sheet = await this.styleSheetWatcher.addStyleSheet(doc);
|
||||
this.styleElements.set(doc, sheet);
|
||||
}
|
||||
} else {
|
||||
const style = this.getStyleElement(node.rawNode.ownerDocument);
|
||||
sheet = style.sheet;
|
||||
}
|
||||
|
||||
const cssRules = sheet.cssRules;
|
||||
const rawNode = node.rawNode;
|
||||
const classes = [...rawNode.classList];
|
||||
|
@ -1137,12 +1215,19 @@ var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
|
|||
|
||||
const index = sheet.insertRule(selector + " {}", cssRules.length);
|
||||
|
||||
// If inserting the rule succeeded, go ahead and edit the source
|
||||
// text if requested.
|
||||
const sheetActor = this._sheetRef(sheet);
|
||||
let { str: authoredText } = await sheetActor.getText();
|
||||
authoredText += "\n" + selector + " {\n" + "}";
|
||||
await sheetActor.update(authoredText, false);
|
||||
if (this.styleSheetWatcher) {
|
||||
const resourceId = this.styleSheetWatcher.getResourceId(sheet);
|
||||
let authoredText = await this.styleSheetWatcher.getText(resourceId);
|
||||
authoredText += "\n" + selector + " {\n" + "}";
|
||||
await this.styleSheetWatcher.update(resourceId, authoredText, false);
|
||||
} else {
|
||||
// If inserting the rule succeeded, go ahead and edit the source
|
||||
// text if requested.
|
||||
const sheetActor = this._sheetRef(sheet);
|
||||
let { str: authoredText } = await sheetActor.getText();
|
||||
authoredText += "\n" + selector + " {\n" + "}";
|
||||
await sheetActor.update(authoredText, false);
|
||||
}
|
||||
|
||||
const cssRule = sheet.cssRules.item(index);
|
||||
const ruleActor = this._styleRef(cssRule);
|
||||
|
@ -1384,8 +1469,10 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
this.line = InspectorUtils.getRelativeRuleLine(this.rawRule);
|
||||
this.column = InspectorUtils.getRuleColumn(this.rawRule);
|
||||
this._parentSheet = this.rawRule.parentStyleSheet;
|
||||
this.sheetActor = this.pageStyle._sheetRef(this._parentSheet);
|
||||
this.sheetActor.on("style-applied", this._onStyleApplied);
|
||||
if (!this.pageStyle.styleSheetWatcher) {
|
||||
this.sheetActor = this.pageStyle._sheetRef(this._parentSheet);
|
||||
this.sheetActor.on("style-applied", this._onStyleApplied);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fake a rule
|
||||
|
@ -1434,7 +1521,7 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
// If a rule has been modified via CSSOM, then we should fall
|
||||
// back to non-authored editing.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
|
||||
!this.sheetActor.hasRulesModifiedByCSSOM() &&
|
||||
!InspectorUtils.hasRulesModifiedByCSSOM(this._parentSheet) &&
|
||||
// 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
|
||||
|
@ -1526,18 +1613,36 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
this.type === CSSRule.KEYFRAME_RULE
|
||||
? this.rawRule.keyText
|
||||
: this.rawRule.selectorText;
|
||||
data.source = {
|
||||
// Inline stylesheets have a null href; Use window URL instead.
|
||||
type: this.sheetActor.href ? "stylesheet" : "inline",
|
||||
href:
|
||||
this.sheetActor.href || this.sheetActor.window.location.toString(),
|
||||
id: this.sheetActor.actorID,
|
||||
index: this.sheetActor.styleSheetIndex,
|
||||
// Whether the stylesheet lives in a different frame than the host document.
|
||||
isFramed: this.sheetActor.ownerWindow !== this.sheetActor.window,
|
||||
};
|
||||
// Used to differentiate between changes to rules with identical selectors.
|
||||
data.ruleIndex = this._ruleIndex;
|
||||
|
||||
if (this.pageStyle.styleSheetWatcher) {
|
||||
const watcher = this.pageStyle.styleSheetWatcher;
|
||||
const sheet = this._parentSheet;
|
||||
const inspectorActor = this.pageStyle.inspector;
|
||||
const resourceId = watcher.getResourceId(sheet);
|
||||
const styleSheetIndex = watcher.getStyleSheetIndex(resourceId);
|
||||
data.source = {
|
||||
// Inline stylesheets have a null href; Use window URL instead.
|
||||
type: sheet.href ? "stylesheet" : "inline",
|
||||
href: sheet.href || inspectorActor.window.location.toString(),
|
||||
id: resourceId,
|
||||
index: styleSheetIndex,
|
||||
// Whether the stylesheet lives in a different frame than the host document.
|
||||
isFramed: inspectorActor.window !== inspectorActor.window.top,
|
||||
};
|
||||
} else {
|
||||
data.source = {
|
||||
// Inline stylesheets have a null href; Use window URL instead.
|
||||
type: this.sheetActor.href ? "stylesheet" : "inline",
|
||||
href:
|
||||
this.sheetActor.href || this.sheetActor.window.location.toString(),
|
||||
id: this.sheetActor.actorID,
|
||||
index: this.sheetActor.styleSheetIndex,
|
||||
// Whether the stylesheet lives in a different frame than the host document.
|
||||
isFramed: this.sheetActor.ownerWindow !== this.sheetActor.window,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -1594,9 +1699,15 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
}
|
||||
}
|
||||
if (this._parentSheet) {
|
||||
form.parentStyleSheet = this.pageStyle._sheetRef(
|
||||
this._parentSheet
|
||||
).actorID;
|
||||
if (this.pageStyle.styleSheetWatcher) {
|
||||
form.parentStyleSheet = this.pageStyle.styleSheetWatcher.getResourceId(
|
||||
this._parentSheet
|
||||
);
|
||||
} else {
|
||||
form.parentStyleSheet = this.pageStyle._sheetRef(
|
||||
this._parentSheet
|
||||
).actorID;
|
||||
}
|
||||
}
|
||||
|
||||
// One tricky thing here is that other methods in this actor must
|
||||
|
@ -1798,10 +1909,13 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
// the rules. Now, recompute our new rule from the style sheet,
|
||||
// so that we aren't left with a reference to a dangling rule.
|
||||
const oldRule = this.rawRule;
|
||||
const oldActor = this.pageStyle.refMap.get(oldRule);
|
||||
this.rawRule = this._getRuleFromIndex(this._parentSheet);
|
||||
// Also tell the page style so that future calls to _styleRef
|
||||
// return the same StyleRuleActor.
|
||||
this.pageStyle.updateStyleRef(oldRule, this.rawRule, this);
|
||||
if (oldActor) {
|
||||
// Also tell the page style so that future calls to _styleRef
|
||||
// return the same StyleRuleActor.
|
||||
this.pageStyle.updateStyleRef(oldRule, this.rawRule, this);
|
||||
}
|
||||
const line = InspectorUtils.getRelativeRuleLine(this.rawRule);
|
||||
const column = InspectorUtils.getRuleColumn(this.rawRule);
|
||||
if (line !== this.line || column !== this.column) {
|
||||
|
@ -1825,7 +1939,7 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
* ignore it and parse the stylehseet again. The authoredText
|
||||
* may be outdated if a descendant of this rule has changed.
|
||||
*/
|
||||
getAuthoredCssText: function(skipCache = false) {
|
||||
getAuthoredCssText: async function(skipCache = false) {
|
||||
if (!this.canSetRuleText || !SUPPORTED_RULE_TYPES.includes(this.type)) {
|
||||
return Promise.resolve("");
|
||||
}
|
||||
|
@ -1834,6 +1948,23 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
return Promise.resolve(this.authoredText);
|
||||
}
|
||||
|
||||
if (this.pageStyle.styleSheetWatcher) {
|
||||
await this.pageStyle.styleSheetWatcher.ensureResourceAvailable(
|
||||
this._parentSheet
|
||||
);
|
||||
const resourceId = this.pageStyle.styleSheetWatcher.getResourceId(
|
||||
this._parentSheet
|
||||
);
|
||||
const cssText = await this.pageStyle.styleSheetWatcher.getText(
|
||||
resourceId
|
||||
);
|
||||
const { text } = getRuleText(cssText, this.line, this.column);
|
||||
|
||||
// Cache the result on the rule actor to avoid parsing again next time
|
||||
this.authoredText = text;
|
||||
return this.authoredText;
|
||||
}
|
||||
|
||||
return this.sheetActor.getText().then(longStr => {
|
||||
const cssText = longStr.str;
|
||||
const { text } = getRuleText(cssText, this.line, this.column);
|
||||
|
@ -1874,7 +2005,23 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
} else {
|
||||
// Get the rule's authored text and skip any cached value.
|
||||
ruleBodyText = await this.getAuthoredCssText(true);
|
||||
const { str: stylesheetText } = await this.sheetActor.getText();
|
||||
|
||||
let stylesheetText = null;
|
||||
if (this.pageStyle.styleSheetWatcher) {
|
||||
await this.pageStyle.styleSheetWatcher.ensureResourceAvailable(
|
||||
this._parentSheet
|
||||
);
|
||||
const resourceId = this.pageStyle.styleSheetWatcher.getResourceId(
|
||||
this._parentSheet
|
||||
);
|
||||
stylesheetText = await this.pageStyle.styleSheetWatcher.getText(
|
||||
resourceId
|
||||
);
|
||||
} else {
|
||||
const { str } = await this.sheetActor.getText();
|
||||
stylesheetText = str;
|
||||
}
|
||||
|
||||
const [start, end] = getSelectorOffsets(
|
||||
stylesheetText,
|
||||
this.line,
|
||||
|
@ -1932,6 +2079,27 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
if (this.type === ELEMENT_STYLE) {
|
||||
// For element style rules, set the node's style attribute.
|
||||
this.rawNode.setAttributeDevtools("style", newText);
|
||||
} else if (this.pageStyle.styleSheetWatcher) {
|
||||
await this.pageStyle.styleSheetWatcher.ensureResourceAvailable(
|
||||
this._parentSheet
|
||||
);
|
||||
const resourceId = this.pageStyle.styleSheetWatcher.getResourceId(
|
||||
this._parentSheet
|
||||
);
|
||||
let cssText = await this.pageStyle.styleSheetWatcher.getText(resourceId);
|
||||
|
||||
const { offset, text } = getRuleText(cssText, this.line, this.column);
|
||||
cssText =
|
||||
cssText.substring(0, offset) +
|
||||
newText +
|
||||
cssText.substring(offset + text.length);
|
||||
|
||||
await this.pageStyle.styleSheetWatcher.update(
|
||||
resourceId,
|
||||
cssText,
|
||||
false,
|
||||
UPDATE_PRESERVING_RULES
|
||||
);
|
||||
} else {
|
||||
// For stylesheet rules, set the text in the stylesheet.
|
||||
const parentStyleSheet = this.pageStyle._sheetRef(this._parentSheet);
|
||||
|
@ -2040,18 +2208,49 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
return null;
|
||||
}
|
||||
|
||||
const sheetActor = this.pageStyle._sheetRef(parentStyleSheet);
|
||||
let { str: authoredText } = await sheetActor.getText();
|
||||
const [startOffset, endOffset] = getSelectorOffsets(
|
||||
authoredText,
|
||||
this.line,
|
||||
this.column
|
||||
);
|
||||
authoredText =
|
||||
authoredText.substring(0, startOffset) +
|
||||
value +
|
||||
authoredText.substring(endOffset);
|
||||
await sheetActor.update(authoredText, false, UPDATE_PRESERVING_RULES);
|
||||
if (this.pageStyle.styleSheetWatcher) {
|
||||
await this.pageStyle.styleSheetWatcher.ensureResourceAvailable(
|
||||
this._parentSheet
|
||||
);
|
||||
const resourceId = this.pageStyle.styleSheetWatcher.getResourceId(
|
||||
this._parentSheet
|
||||
);
|
||||
let authoredText = await this.pageStyle.styleSheetWatcher.getText(
|
||||
resourceId
|
||||
);
|
||||
|
||||
const [startOffset, endOffset] = getSelectorOffsets(
|
||||
authoredText,
|
||||
this.line,
|
||||
this.column
|
||||
);
|
||||
authoredText =
|
||||
authoredText.substring(0, startOffset) +
|
||||
value +
|
||||
authoredText.substring(endOffset);
|
||||
|
||||
await this.pageStyle.styleSheetWatcher.update(
|
||||
resourceId,
|
||||
authoredText,
|
||||
false,
|
||||
UPDATE_PRESERVING_RULES
|
||||
);
|
||||
} else {
|
||||
const sheetActor = this.pageStyle._sheetRef(parentStyleSheet);
|
||||
let { str: authoredText } = await sheetActor.getText();
|
||||
|
||||
const [startOffset, endOffset] = getSelectorOffsets(
|
||||
authoredText,
|
||||
this.line,
|
||||
this.column
|
||||
);
|
||||
authoredText =
|
||||
authoredText.substring(0, startOffset) +
|
||||
value +
|
||||
authoredText.substring(endOffset);
|
||||
|
||||
await sheetActor.update(authoredText, false, UPDATE_PRESERVING_RULES);
|
||||
}
|
||||
} else {
|
||||
const cssRules = parentStyleSheet.cssRules;
|
||||
const cssText = rule.cssText;
|
||||
|
|
|
@ -57,6 +57,19 @@ class ResourceWatcher {
|
|||
return this._cache.filter(r => r.resourceType === resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified resource cached in this watcher.
|
||||
*
|
||||
* @param {String} resourceType
|
||||
* @param {String} resourceId
|
||||
* @return {Object} resource cached in this watcher
|
||||
*/
|
||||
getResourceById(resourceType, resourceId) {
|
||||
return this._cache.find(
|
||||
r => r.resourceType === resourceType && r.resourceId === resourceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to start retrieving all already existing instances of given
|
||||
* type of resources and also start watching for the one to be created after.
|
||||
|
|
|
@ -96,7 +96,6 @@ const pageStyleSpec = generateActorSpec({
|
|||
events: {
|
||||
"stylesheet-updated": {
|
||||
type: "styleSheetUpdated",
|
||||
styleSheet: Arg(0, "stylesheet"),
|
||||
},
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче