mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1771113 - [devtools] Notify about removed stylesheet resources. r=devtools-reviewers,ochameau.
Differential Revision: https://phabricator.services.mozilla.com/D185790
This commit is contained in:
parent
f086602a6f
commit
a825b29421
@ -19,6 +19,7 @@ class StyleSheetWatcher {
|
||||
this._onApplicableStylesheetAdded =
|
||||
this._onApplicableStylesheetAdded.bind(this);
|
||||
this._onStylesheetUpdated = this._onStylesheetUpdated.bind(this);
|
||||
this._onStylesheetRemoved = this._onStylesheetRemoved.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,10 +32,11 @@ class StyleSheetWatcher {
|
||||
* - onAvailable: mandatory function
|
||||
* This will be called for each resource.
|
||||
*/
|
||||
async watch(targetActor, { onAvailable, onUpdated }) {
|
||||
async watch(targetActor, { onAvailable, onUpdated, onDestroyed }) {
|
||||
this._targetActor = targetActor;
|
||||
this._onAvailable = onAvailable;
|
||||
this._onUpdated = onUpdated;
|
||||
this._onDestroyed = onDestroyed;
|
||||
|
||||
this._styleSheetsManager = targetActor.getStyleSheetsManager();
|
||||
|
||||
@ -47,6 +49,10 @@ class StyleSheetWatcher {
|
||||
"stylesheet-updated",
|
||||
this._onStylesheetUpdated
|
||||
);
|
||||
this._styleSheetsManager.on(
|
||||
"applicable-stylesheet-removed",
|
||||
this._onStylesheetRemoved
|
||||
);
|
||||
|
||||
// startWatching will emit applicable-stylesheet-added for already existing stylesheet
|
||||
await this._styleSheetsManager.startWatching();
|
||||
@ -60,6 +66,10 @@ class StyleSheetWatcher {
|
||||
this._notifyResourceUpdated(resourceId, updateKind, updates);
|
||||
}
|
||||
|
||||
_onStylesheetRemoved({ resourceId }) {
|
||||
return this._notifyResourcesDestroyed(resourceId);
|
||||
}
|
||||
|
||||
async _toResource(
|
||||
styleSheet,
|
||||
{ isCreatedByDevTools = false, fileName = null, resourceId } = {}
|
||||
@ -124,6 +134,15 @@ class StyleSheetWatcher {
|
||||
]);
|
||||
}
|
||||
|
||||
_notifyResourcesDestroyed(resourceId) {
|
||||
this._onDestroyed([
|
||||
{
|
||||
resourceType: STYLESHEET,
|
||||
resourceId,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._styleSheetsManager.off(
|
||||
"applicable-stylesheet-added",
|
||||
@ -133,6 +152,10 @@ class StyleSheetWatcher {
|
||||
"stylesheet-updated",
|
||||
this._onStylesheetUpdated
|
||||
);
|
||||
this._styleSheetsManager.off(
|
||||
"applicable-stylesheet-removed",
|
||||
this._onStylesheetRemoved
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ class StyleSheetsManager extends EventEmitter {
|
||||
|
||||
this._targetActor = targetActor;
|
||||
this._onApplicableStateChanged = this._onApplicableStateChanged.bind(this);
|
||||
this._onStylesheetRemoved = this._onStylesheetRemoved.bind(this);
|
||||
this._onTargetActorWindowReady = this._onTargetActorWindowReady.bind(this);
|
||||
}
|
||||
|
||||
@ -112,6 +113,11 @@ class StyleSheetsManager extends EventEmitter {
|
||||
this._onApplicableStateChanged,
|
||||
true
|
||||
);
|
||||
this._targetActor.chromeEventHandler.addEventListener(
|
||||
"StyleSheetRemoved",
|
||||
this._onStylesheetRemoved,
|
||||
true
|
||||
);
|
||||
this._watchStyleSheetChangeEvents();
|
||||
this._targetActor.on("window-ready", this._onTargetActorWindowReady);
|
||||
|
||||
@ -145,7 +151,7 @@ class StyleSheetsManager extends EventEmitter {
|
||||
|
||||
_watchStyleSheetChangeEventsForWindow(window) {
|
||||
// We have to set this flag in order to get the
|
||||
// StyleSheetApplicableStateChanged events. See Document.webidl.
|
||||
// StyleSheetApplicableStateChanged and StyleSheetRemoved events. See Document.webidl.
|
||||
window.document.styleSheetChangeEventsEnabled = true;
|
||||
}
|
||||
|
||||
@ -758,10 +764,11 @@ class StyleSheetsManager extends EventEmitter {
|
||||
* When appending <link>, <style> or changing `disabled` attribute to false,
|
||||
* `applicable` is passed as true. The other hand, when changing `disabled`
|
||||
* to true, this will be false.
|
||||
* NOTE: For now, StyleSheetApplicableStateChanged will not be called when removing the
|
||||
* link and style element.
|
||||
*
|
||||
* @param {StyleSheetApplicableStateChanged}
|
||||
* NOTE: StyleSheetApplicableStateChanged is _not_ called when removing the <link>/<style>,
|
||||
* but a StyleSheetRemovedEvent is emitted in such case (see _onStyleSheetRemoved)
|
||||
*
|
||||
* @param {StyleSheetApplicableStateChangedEvent}
|
||||
* The triggering event.
|
||||
*/
|
||||
_onApplicableStateChanged({ applicable, stylesheet: styleSheet }) {
|
||||
@ -779,6 +786,16 @@ class StyleSheetsManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler that is called when a style sheet is removed.
|
||||
*
|
||||
* @param {StyleSheetRemovedEvent}
|
||||
* The triggering event.
|
||||
*/
|
||||
_onStylesheetRemoved(event) {
|
||||
this._unregisterStyleSheet(event.stylesheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the stylesheet isn't registered yet, this function will generate an associated
|
||||
* resourceId and will emit an "applicable-stylesheet-added" event.
|
||||
@ -820,6 +837,25 @@ class StyleSheetsManager extends EventEmitter {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the stylesheet is registered, this function will emit an "applicable-stylesheet-removed" event
|
||||
* with the stylesheet resourceId.
|
||||
*
|
||||
* @param {StyleSheet} styleSheet
|
||||
*/
|
||||
_unregisterStyleSheet(styleSheet) {
|
||||
const existingResourceId = this._findStyleSheetResourceId(styleSheet);
|
||||
if (!existingResourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._styleSheetMap.delete(existingResourceId);
|
||||
this._styleSheetCreationData?.delete(styleSheet);
|
||||
this.emit("applicable-stylesheet-removed", {
|
||||
resourceId: existingResourceId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed styleSheet should be handled.
|
||||
*
|
||||
@ -860,6 +896,11 @@ class StyleSheetsManager extends EventEmitter {
|
||||
this._onApplicableStateChanged,
|
||||
true
|
||||
);
|
||||
this._targetActor.chromeEventHandler.removeEventListener(
|
||||
"StyleSheetRemoved",
|
||||
this._onStylesheetRemoved,
|
||||
true
|
||||
);
|
||||
this._unwatchStyleSheetChangeEvents();
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
@ -120,7 +120,7 @@ const ADDITIONAL_FROM_ACTOR_RESOURCE = {
|
||||
};
|
||||
|
||||
add_task(async function () {
|
||||
await testResourceAvailableFeature();
|
||||
await testResourceAvailableDestroyedFeature();
|
||||
await testResourceUpdateFeature();
|
||||
await testNestedResourceUpdateFeature();
|
||||
});
|
||||
@ -137,7 +137,7 @@ function pushAvailableResource(availableResources) {
|
||||
};
|
||||
}
|
||||
|
||||
async function testResourceAvailableFeature() {
|
||||
async function testResourceAvailableDestroyedFeature() {
|
||||
info("Check resource available feature of the ResourceCommand");
|
||||
|
||||
const tab = await addTab(STYLE_TEST_URL);
|
||||
@ -154,8 +154,10 @@ async function testResourceAvailableFeature() {
|
||||
|
||||
info("Check whether ResourceCommand gets existing stylesheet");
|
||||
const availableResources = [];
|
||||
const destroyedResources = [];
|
||||
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
|
||||
onAvailable: pushAvailableResource(availableResources),
|
||||
onDestroyed: resources => destroyedResources.push(...resources),
|
||||
});
|
||||
|
||||
is(
|
||||
@ -186,6 +188,7 @@ async function testResourceAvailableFeature() {
|
||||
text => {
|
||||
const document = content.document;
|
||||
const stylesheet = document.createElement("style");
|
||||
stylesheet.id = "inline-from-test";
|
||||
stylesheet.textContent = text;
|
||||
document.body.appendChild(stylesheet);
|
||||
}
|
||||
@ -233,6 +236,72 @@ async function testResourceAvailableFeature() {
|
||||
ADDITIONAL_FROM_ACTOR_RESOURCE
|
||||
);
|
||||
|
||||
info("Check resource destroyed feature of the ResourceCommand");
|
||||
is(destroyedResources.length, 0, "There was no removed stylesheets yet");
|
||||
|
||||
info("Remove inline stylesheet added in the test");
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.document.querySelector("#inline-from-test").remove();
|
||||
});
|
||||
await waitUntil(() => destroyedResources.length === 1);
|
||||
assertDestroyed(destroyedResources[0], {
|
||||
resourceId: availableResources.at(-3).resourceId,
|
||||
});
|
||||
|
||||
info("Remove existing top-level inline stylesheet");
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.document.querySelector("style").remove();
|
||||
});
|
||||
await waitUntil(() => destroyedResources.length === 2);
|
||||
assertDestroyed(destroyedResources[1], {
|
||||
resourceId: availableResources.find(
|
||||
resource =>
|
||||
findMatchingExpectedResource(resource) === EXISTING_RESOURCES[0]
|
||||
).resourceId,
|
||||
});
|
||||
|
||||
info("Remove existing top-level <link> stylesheet");
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.document.querySelector("link").remove();
|
||||
});
|
||||
await waitUntil(() => destroyedResources.length === 3);
|
||||
assertDestroyed(destroyedResources[2], {
|
||||
resourceId: availableResources.find(
|
||||
resource =>
|
||||
findMatchingExpectedResource(resource) === EXISTING_RESOURCES[1]
|
||||
).resourceId,
|
||||
});
|
||||
|
||||
info("Remove existing iframe inline stylesheet");
|
||||
const iframeBrowsingContext = await SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[],
|
||||
() => content.document.querySelector("iframe").browsingContext
|
||||
);
|
||||
|
||||
await SpecialPowers.spawn(iframeBrowsingContext, [], () => {
|
||||
content.document.querySelector("style").remove();
|
||||
});
|
||||
await waitUntil(() => destroyedResources.length === 4);
|
||||
assertDestroyed(destroyedResources[3], {
|
||||
resourceId: availableResources.find(
|
||||
resource =>
|
||||
findMatchingExpectedResource(resource) === EXISTING_RESOURCES[3]
|
||||
).resourceId,
|
||||
});
|
||||
|
||||
info("Remove existing iframe <link> stylesheet");
|
||||
await SpecialPowers.spawn(iframeBrowsingContext, [], () => {
|
||||
content.document.querySelector("link").remove();
|
||||
});
|
||||
await waitUntil(() => destroyedResources.length === 5);
|
||||
assertDestroyed(destroyedResources[4], {
|
||||
resourceId: availableResources.find(
|
||||
resource =>
|
||||
findMatchingExpectedResource(resource) === EXISTING_RESOURCES[4]
|
||||
).resourceId,
|
||||
});
|
||||
|
||||
targetCommand.destroy();
|
||||
await client.close();
|
||||
}
|
||||
@ -634,6 +703,15 @@ function assertUpdate(update, expected) {
|
||||
}
|
||||
}
|
||||
|
||||
function assertDestroyed(resource, expected) {
|
||||
is(
|
||||
resource.resourceType,
|
||||
ResourceCommand.TYPES.STYLESHEET,
|
||||
"Resource type is correct"
|
||||
);
|
||||
is(resource.resourceId, expected.resourceId, "resourceId is correct");
|
||||
}
|
||||
|
||||
function getResourceTimingCount(tab) {
|
||||
return ContentTask.spawn(tab.linkedBrowser, [], () => {
|
||||
return content.performance.getEntriesByType("resource").length;
|
||||
|
Loading…
Reference in New Issue
Block a user