Bug 1664941: Connect style actor and stylesheet watcher. r=jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D90651
This commit is contained in:
Daisuke Akatsuka 2020-10-29 06:26:20 +00:00
Родитель 72da707476
Коммит 473676fda0
12 изменённых файлов: 462 добавлений и 105 удалений

Просмотреть файл

@ -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"),
},
},