зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1659589: Handle importing stylesheet from file using resource watcher mechanism to keep consistency. r=ochameau,devtools-backward-compat-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D87040
This commit is contained in:
Родитель
4afed2d20f
Коммит
0adc3db50a
|
@ -417,6 +417,16 @@ class SourceMapURLService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to wait for the full retrieval of JS Sources and stylesheets.
|
||||
* This function is especially useful for tests in order to avoid closing
|
||||
* the toolbox with pending requests.
|
||||
* This may return null if no source are being mapped.
|
||||
*/
|
||||
waitForPendingSources() {
|
||||
return this._sourcesLoading;
|
||||
}
|
||||
|
||||
_ensureAllSourcesPopulated() {
|
||||
if (!this._prefValue) {
|
||||
return null;
|
||||
|
|
|
@ -160,6 +160,20 @@ class StyleSheetsFront extends FrontClassWithSpec(styleSheetsSpec) {
|
|||
// Attribute name from which to retrieve the actorID out of the target actor's form
|
||||
this.formAttributeName = "styleSheetsActor";
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
// FF81+ getTraits() is supported.
|
||||
const { traits } = await super.getTraits();
|
||||
this._traits = traits;
|
||||
} catch (e) {
|
||||
this._traits = {};
|
||||
}
|
||||
}
|
||||
|
||||
get traits() {
|
||||
return this._traits;
|
||||
}
|
||||
}
|
||||
|
||||
exports.StyleSheetsFront = StyleSheetsFront;
|
||||
|
|
|
@ -10,6 +10,7 @@ const { loader, require } = ChromeUtils.import(
|
|||
"resource://devtools/shared/Loader.jsm"
|
||||
);
|
||||
const Services = require("Services");
|
||||
const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
|
||||
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
||||
const { OS } = require("resource://gre/modules/osfile.jsm");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
@ -301,20 +302,19 @@ StyleEditorUI.prototype = {
|
|||
* Add an editor for this stylesheet. Add editors for its original sources
|
||||
* instead (e.g. Sass sources), if applicable.
|
||||
*
|
||||
* @param {StyleSheetFront} styleSheet
|
||||
* Style sheet to add to style editor
|
||||
* @param {Boolean} isNew
|
||||
* True if this style sheet was created by a call to the
|
||||
* style sheets actor's @see addStyleSheet method.
|
||||
* @param {Resource} resource
|
||||
* The STYLESHEET resource which is received from resource watcher.
|
||||
* @return {Promise}
|
||||
* A promise that resolves to the style sheet's editor when the style sheet has
|
||||
* been fully loaded. If the style sheet has a source map, and source mapping
|
||||
* is enabled, then the promise resolves to null.
|
||||
*/
|
||||
_addStyleSheet: function(styleSheet, isNew) {
|
||||
_addStyleSheet: function(resource) {
|
||||
const { styleSheet } = resource;
|
||||
|
||||
if (!this._seenSheets.has(styleSheet)) {
|
||||
const promise = (async () => {
|
||||
let editor = await this._addStyleSheetEditor(styleSheet, isNew);
|
||||
let editor = await this._addStyleSheetEditor(resource);
|
||||
|
||||
const sourceMapService = this._toolbox.sourceMapService;
|
||||
|
||||
|
@ -331,7 +331,7 @@ StyleEditorUI.prototype = {
|
|||
actorID: id,
|
||||
sourceMapURL,
|
||||
sourceMapBaseURL,
|
||||
} = styleSheet;
|
||||
} = resource.styleSheet;
|
||||
const sources = await sourceMapService.getOriginalURLs({
|
||||
id,
|
||||
url: href || nodeHref,
|
||||
|
@ -356,7 +356,11 @@ StyleEditorUI.prototype = {
|
|||
original.styleSheetIndex = styleSheet.styleSheetIndex;
|
||||
original.relatedStyleSheet = styleSheet;
|
||||
original.relatedEditorName = parentEditorName;
|
||||
await this._addStyleSheetEditor(original);
|
||||
|
||||
const dummyResource = Object.assign({}, resource, {
|
||||
styleSheet: original,
|
||||
});
|
||||
await this._addStyleSheetEditor(dummyResource);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,18 +385,16 @@ StyleEditorUI.prototype = {
|
|||
*
|
||||
* @param {StyleSheet} styleSheet
|
||||
* Object representing stylesheet
|
||||
* @param {Boolean} isNew
|
||||
* Optional if stylesheet is a new sheet created by user
|
||||
* @return {(Number|undefined)}
|
||||
* Optional Integer representing the index of the current stylesheet
|
||||
* among all stylesheets of its type (inline or user-created)
|
||||
*/
|
||||
_getNextFriendlyIndex: function(styleSheet, isNew) {
|
||||
_getNextFriendlyIndex: function(styleSheet) {
|
||||
if (styleSheet.href) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return isNew
|
||||
return styleSheet.isNew
|
||||
? this._getNewStyleSheetsCount()
|
||||
: this._getInlineStyleSheetsCount();
|
||||
},
|
||||
|
@ -400,30 +402,18 @@ StyleEditorUI.prototype = {
|
|||
/**
|
||||
* Add a new editor to the UI for a source.
|
||||
*
|
||||
* @param {StyleSheet|OriginalSource} styleSheet
|
||||
* Object representing stylesheet
|
||||
* @param {Boolean} isNew
|
||||
* Optional if stylesheet is a new sheet created by user
|
||||
* @param {Resource} resource
|
||||
* The resource which is received from resource watcher.
|
||||
* @return {Promise} that is resolved with the created StyleSheetEditor when
|
||||
* the editor is fully initialized or rejected on error.
|
||||
*/
|
||||
async _addStyleSheetEditor(styleSheet, isNew) {
|
||||
// recall location of saved file for this sheet after page reload
|
||||
let file = null;
|
||||
const identifier = this.getStyleSheetIdentifier(styleSheet);
|
||||
const savedFile = this.savedLocations[identifier];
|
||||
if (savedFile) {
|
||||
file = savedFile;
|
||||
}
|
||||
|
||||
async _addStyleSheetEditor(resource) {
|
||||
const editor = new StyleSheetEditor(
|
||||
styleSheet,
|
||||
resource,
|
||||
this._window,
|
||||
file,
|
||||
isNew,
|
||||
this._walker,
|
||||
this._highlighter,
|
||||
this._getNextFriendlyIndex(styleSheet, isNew)
|
||||
this._getNextFriendlyIndex(resource.styleSheet)
|
||||
);
|
||||
|
||||
editor.on("property-change", this._summaryChange.bind(this, editor));
|
||||
|
@ -437,6 +427,10 @@ StyleEditorUI.prototype = {
|
|||
await editor.fetchSource();
|
||||
this._sourceLoaded(editor);
|
||||
|
||||
if (resource.styleSheet.fileName) {
|
||||
this.emit("test:editor-updated", editor);
|
||||
}
|
||||
|
||||
return editor;
|
||||
},
|
||||
|
||||
|
@ -478,13 +472,20 @@ StyleEditorUI.prototype = {
|
|||
const stylesheetsFront = await this.currentTarget.getFront(
|
||||
"stylesheets"
|
||||
);
|
||||
const styleSheet = await stylesheetsFront.addStyleSheet(source);
|
||||
const editor = await this._addStyleSheet(styleSheet, true);
|
||||
if (editor) {
|
||||
editor.savedFile = selectedFile;
|
||||
|
||||
if (stylesheetsFront.traits.isFileNameSupported) {
|
||||
// FF81+ addStyleSheet of StyleSheetsFront supports file name parameter.
|
||||
stylesheetsFront.addStyleSheet(source, selectedFile.path);
|
||||
} else {
|
||||
const styleSheet = await stylesheetsFront.addStyleSheet(source);
|
||||
styleSheet.isNew = true;
|
||||
const editor = await this._addStyleSheet({ styleSheet });
|
||||
if (editor) {
|
||||
editor.savedFile = selectedFile;
|
||||
}
|
||||
// Just for testing purposes.
|
||||
this.emit("test:editor-updated", editor);
|
||||
}
|
||||
// Just for testing purposes.
|
||||
this.emit("test:editor-updated", editor);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1171,9 +1172,24 @@ StyleEditorUI.prototype = {
|
|||
this._root.classList.remove("loading");
|
||||
},
|
||||
|
||||
async _handleStyleSheetResource({ styleSheet, isNew }) {
|
||||
async _handleStyleSheetResource(resource) {
|
||||
try {
|
||||
await this._addStyleSheet(styleSheet, isNew);
|
||||
// The fileName is in styleSheet means this stylesheet was imported from file by user.
|
||||
const { styleSheet } = resource;
|
||||
const { fileName } = resource.styleSheet;
|
||||
let file = fileName ? new FileUtils.File(fileName) : null;
|
||||
|
||||
// recall location of saved file for this sheet after page reload
|
||||
if (!file) {
|
||||
const identifier = this.getStyleSheetIdentifier(styleSheet);
|
||||
const savedFile = this.savedLocations[identifier];
|
||||
if (savedFile) {
|
||||
file = savedFile;
|
||||
}
|
||||
}
|
||||
styleSheet.file = file;
|
||||
|
||||
await this._addStyleSheet(resource);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.emit("error", { key: LOAD_ERROR, level: "warning" });
|
||||
|
|
|
@ -60,14 +60,10 @@ const EMIT_MEDIA_RULES_THROTTLING = 500;
|
|||
* 'source-editor-load': The source editor for this editor has been loaded
|
||||
* 'error': An error has occured
|
||||
*
|
||||
* @param {StyleSheet|OriginalSource} styleSheet
|
||||
* Stylesheet or original source to show
|
||||
* @param {Resource} resource
|
||||
* The STYLESHEET resource which is received from resource watcher.
|
||||
* @param {DOMWindow} win
|
||||
* panel window for style editor
|
||||
* @param {nsIFile} file
|
||||
* Optional file that the sheet was imported from
|
||||
* @param {boolean} isNew
|
||||
* Optional whether the sheet was created by the user
|
||||
* @param {Walker} walker
|
||||
* Optional walker used for selectors autocompletion
|
||||
* @param {CustomHighlighterFront} highlighter
|
||||
|
@ -78,21 +74,19 @@ const EMIT_MEDIA_RULES_THROTTLING = 500;
|
|||
* among all stylesheets of its type (inline or user-created)
|
||||
*/
|
||||
function StyleSheetEditor(
|
||||
styleSheet,
|
||||
resource,
|
||||
win,
|
||||
file,
|
||||
isNew,
|
||||
walker,
|
||||
highlighter,
|
||||
styleSheetFriendlyIndex
|
||||
) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this.styleSheet = styleSheet;
|
||||
this.styleSheet = resource.styleSheet;
|
||||
this._inputElement = null;
|
||||
this.sourceEditor = null;
|
||||
this._window = win;
|
||||
this._isNew = isNew;
|
||||
this._isNew = this.styleSheet.isNew;
|
||||
this.walker = walker;
|
||||
this.highlighter = highlighter;
|
||||
this.styleSheetFriendlyIndex = styleSheetFriendlyIndex;
|
||||
|
@ -114,7 +108,7 @@ function StyleSheetEditor(
|
|||
|
||||
this._styleSheetFilePath = null;
|
||||
if (
|
||||
styleSheet.href &&
|
||||
this.styleSheet.href &&
|
||||
Services.io.extractScheme(this.styleSheet.href) == "file"
|
||||
) {
|
||||
this._styleSheetFilePath = this.styleSheet.href;
|
||||
|
@ -148,7 +142,7 @@ function StyleSheetEditor(
|
|||
}
|
||||
this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
|
||||
this.cssSheet.on("style-applied", this._onStyleApplied);
|
||||
this.savedFile = file;
|
||||
this.savedFile = this.styleSheet.file;
|
||||
this.linkCSSFile();
|
||||
}
|
||||
|
||||
|
|
|
@ -897,6 +897,14 @@ async function openNetMonitor(tab) {
|
|||
async function openConsole(tab) {
|
||||
const target = await TargetFactory.forTab(tab || gBrowser.selectedTab);
|
||||
const toolbox = await gDevTools.showToolbox(target, "webconsole");
|
||||
|
||||
// Approximately half of webconsole tests call source-map-url-service and load the source
|
||||
// codes and stylesheets. However, the processing of the request may have not been
|
||||
// finished when closing the windows and related toolboxes, it may cause test failure.
|
||||
// Hence, we call it explicitly, then wait for finishing the pending requests.
|
||||
toolbox.sourceMapURLService._ensureAllSourcesPopulated();
|
||||
await toolbox.sourceMapURLService.waitForPendingSources();
|
||||
|
||||
return toolbox.getCurrentPanel().hud;
|
||||
}
|
||||
|
||||
|
|
|
@ -617,10 +617,6 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
|
|||
return this.window.document;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
return { actor: this.actorID };
|
||||
},
|
||||
|
||||
initialize: function(conn, targetActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, targetActor.conn);
|
||||
|
||||
|
@ -639,11 +635,15 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
|
|||
this._onApplicableStateChanged,
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
// This is used when creating a new style sheet, so that we can
|
||||
// pass the correct flag when emitting our stylesheet-added event.
|
||||
// See addStyleSheet and _onNewStyleSheetActor for more details.
|
||||
this._nextStyleSheetIsNew = false;
|
||||
getTraits() {
|
||||
return {
|
||||
traits: {
|
||||
// FF81+ addStyleSheet supports file name parameter.
|
||||
isFileNameSupported: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
@ -682,9 +682,16 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
|
|||
* The new style sheet actor.
|
||||
*/
|
||||
_onNewStyleSheetActor: function(actor) {
|
||||
const info = this._addingStyleSheetInfo?.get(actor.rawSheet);
|
||||
this._addingStyleSheetInfo?.delete(actor.rawSheet);
|
||||
|
||||
// Forward it to the client side.
|
||||
this.emit("stylesheet-added", actor, this._nextStyleSheetIsNew);
|
||||
this._nextStyleSheetIsNew = false;
|
||||
this.emit(
|
||||
"stylesheet-added",
|
||||
actor,
|
||||
info ? info.isNew : false,
|
||||
info ? info.fileName : null
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -860,17 +867,12 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
|
|||
*
|
||||
* @param {object} request
|
||||
* Debugging protocol request object, with 'text property'
|
||||
* @param {string} fileName
|
||||
* If the stylesheet adding is from file, `fileName` indicates the path.
|
||||
* @return {object}
|
||||
* Object with 'styelSheet' property for form on new actor.
|
||||
*/
|
||||
addStyleSheet: function(text) {
|
||||
// This is a bit convoluted. The style sheet actor may be created
|
||||
// by a notification from platform. In this case, we can't easily
|
||||
// pass the "new" flag through to createStyleSheetActor, so we set
|
||||
// a flag locally and check it before sending an event to the
|
||||
// client. See |_onNewStyleSheetActor|.
|
||||
this._nextStyleSheetIsNew = true;
|
||||
|
||||
addStyleSheet: function(text, fileName = null) {
|
||||
const parent = this.document.documentElement;
|
||||
const style = this.document.createElementNS(
|
||||
"http://www.w3.org/1999/xhtml",
|
||||
|
@ -883,6 +885,16 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
|
|||
}
|
||||
parent.appendChild(style);
|
||||
|
||||
// This is a bit convoluted. The style sheet actor may be created
|
||||
// by a notification from platform. In this case, we can't easily
|
||||
// pass the "new" flag through to createStyleSheetActor, so we set
|
||||
// a flag locally and check it before sending an event to the
|
||||
// client. See |_onNewStyleSheetActor|.
|
||||
if (!this._addingStyleSheetInfo) {
|
||||
this._addingStyleSheetInfo = new WeakMap();
|
||||
}
|
||||
this._addingStyleSheetInfo.set(style.sheet, { isNew: true, fileName });
|
||||
|
||||
const actor = this.parentActor.createStyleSheetActor(style.sheet);
|
||||
return actor;
|
||||
},
|
||||
|
|
|
@ -16,10 +16,12 @@ module.exports = async function({ targetFront, onAvailable }) {
|
|||
const styleSheetsFront = await targetFront.getFront("stylesheets");
|
||||
try {
|
||||
const styleSheets = await styleSheetsFront.getStyleSheets();
|
||||
onAvailable(styleSheets.map(styleSheet => toResource(styleSheet, false)));
|
||||
onAvailable(
|
||||
styleSheets.map(styleSheet => toResource(styleSheet, false, null))
|
||||
);
|
||||
|
||||
styleSheetsFront.on("stylesheet-added", (styleSheet, isNew) => {
|
||||
onAvailable([toResource(styleSheet, isNew)]);
|
||||
styleSheetsFront.on("stylesheet-added", (styleSheet, isNew, fileName) => {
|
||||
onAvailable([toResource(styleSheet, isNew, fileName)]);
|
||||
});
|
||||
} catch (e) {
|
||||
// There are cases that the stylesheet front was destroyed already when/while calling
|
||||
|
@ -30,10 +32,9 @@ module.exports = async function({ targetFront, onAvailable }) {
|
|||
}
|
||||
};
|
||||
|
||||
function toResource(styleSheet, isNew) {
|
||||
function toResource(styleSheet, isNew, fileName) {
|
||||
return {
|
||||
resourceType: ResourceWatcher.TYPES.STYLESHEET,
|
||||
styleSheet,
|
||||
isNew,
|
||||
styleSheet: Object.assign(styleSheet, { isNew, fileName }),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ function findMatchingExpectedResource(resource) {
|
|||
}
|
||||
|
||||
async function assertResource(resource, expected) {
|
||||
const { resourceType, styleSheet, isNew } = resource;
|
||||
const { resourceType, styleSheet } = resource;
|
||||
is(
|
||||
resourceType,
|
||||
ResourceWatcher.TYPES.STYLESHEET,
|
||||
|
@ -156,5 +156,5 @@ async function assertResource(resource, expected) {
|
|||
is(styleText, expected.styleText, "Style text is correct");
|
||||
is(styleSheet.href, expected.href, "href is correct");
|
||||
is(styleSheet.nodeHref, expected.nodeHref, "nodeHref is correct");
|
||||
is(isNew, expected.isNew, "Flag isNew is correct");
|
||||
is(styleSheet.isNew, expected.isNew, "Flag isNew is correct");
|
||||
}
|
||||
|
|
|
@ -79,16 +79,24 @@ const styleSheetsSpec = generateActorSpec({
|
|||
type: "stylesheetAdded",
|
||||
sheet: Arg(0, "stylesheet"),
|
||||
isNew: Arg(1, "boolean"),
|
||||
fileName: Arg(2, "nullable:string"),
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
getTraits: {
|
||||
request: {},
|
||||
response: { traits: RetVal("json") },
|
||||
},
|
||||
getStyleSheets: {
|
||||
request: {},
|
||||
response: { styleSheets: RetVal("array:stylesheet") },
|
||||
},
|
||||
addStyleSheet: {
|
||||
request: { text: Arg(0, "string") },
|
||||
request: {
|
||||
text: Arg(0, "string"),
|
||||
fileName: Arg(1, "nullable:string"),
|
||||
},
|
||||
response: { styleSheet: RetVal("stylesheet") },
|
||||
},
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче