Bug 1625930: Apply ResourceWatcher to source map. r=ochameau

Depends on D78240

Differential Revision: https://phabricator.services.mozilla.com/D78241
This commit is contained in:
Daisuke Akatsuka 2020-07-08 23:07:33 +00:00
parent d1f58c00a9
commit 80b6400e07
5 changed files with 63 additions and 65 deletions

View File

@ -27,7 +27,6 @@ class SourceMapURLService {
this._pendingURLSubscriptions = new Map();
this._urlToIDMap = new Map();
this._mapsById = new Map();
this._listeningStylesheetFront = null;
this._sourcesLoading = null;
this._runningCallback = false;
@ -205,13 +204,16 @@ class SourceMapURLService {
this._pendingURLSubscriptions.clear();
this._urlToIDMap.clear();
if (this._listeningStylesheetFront) {
this._listeningStylesheetFront.off(
"stylesheet-added",
this._onNewStyleSheet
try {
this._toolbox.resourceWatcher.unwatchResources(
[this._toolbox.resourceWatcher.TYPES.STYLESHEET],
{ onAvailable: this._onResourceAvailable }
);
this._listeningStylesheetFront = null;
} catch (e) {
// If unwatchResources is called before finishing process of watchResources,
// it throws an error during stopping listener.
}
this._sourcesLoading = null;
}
@ -426,39 +428,17 @@ class SourceMapURLService {
return;
}
this._onResourceAvailable = async ({ resource }) => {
if (this._sourcesLoading === sourcesLoading) {
this._onNewStyleSheet(resource.styleSheet);
}
};
await Promise.all([
(async () => {
if (!this._target.hasActor("styleSheets")) {
return;
}
try {
const front = await this._target.getFront("stylesheets");
if (this._listeningStylesheetFront) {
this._listeningStylesheetFront.off(
"stylesheet-added",
this._onNewStyleSheet
);
}
this._listeningStylesheetFront = front;
this._listeningStylesheetFront.on(
"stylesheet-added",
this._onNewStyleSheet
);
const sheets = await front.getStyleSheets();
if (this._sourcesLoading === sourcesLoading) {
// If we've cleared the state since starting this request,
// we don't want to populate these.
for (const sheet of sheets) {
this._onNewStyleSheet(sheet);
}
}
} catch (err) {
// Ignore any protocol-based errors.
}
})(),
this._toolbox.resourceWatcher.watchResources(
[this._toolbox.resourceWatcher.TYPES.STYLESHEET],
{ onAvailable: this._onResourceAvailable }
),
(async () => {
const { threadFront } = this._toolbox;
if (!threadFront) {

View File

@ -333,6 +333,10 @@ class EventEmitter {
EventEmitter.emit(this, ...args);
}
}
count(...args) {
return EventEmitter.count(this, ...args);
}
}
module.exports = EventEmitter;

View File

@ -14,12 +14,20 @@ module.exports = async function({ targetFront, onAvailable }) {
}
const styleSheetsFront = await targetFront.getFront("stylesheets");
const styleSheets = await styleSheetsFront.getStyleSheets();
onAvailable(styleSheets.map(styleSheet => toResource(styleSheet, false)));
try {
const styleSheets = await styleSheetsFront.getStyleSheets();
onAvailable(styleSheets.map(styleSheet => toResource(styleSheet, false)));
styleSheetsFront.on("stylesheet-added", (styleSheet, isNew) => {
onAvailable([toResource(styleSheet, isNew)]);
});
styleSheetsFront.on("stylesheet-added", (styleSheet, isNew) => {
onAvailable([toResource(styleSheet, isNew)]);
});
} catch (e) {
// There are cases that the stylesheet front was destroyed already when/while calling
// methods of stylesheet.
// Especially, since source map url service starts to watch the stylesheet resources
// lazily, the possibility will be extended.
console.warn("fetching stylesheets failed", e);
}
};
function toResource(styleSheet, isNew) {

View File

@ -70,7 +70,11 @@ class ResourceWatcher {
* existing resources.
*/
async watchResources(resources, options) {
const { onAvailable, ignoreExistingResources = false } = options;
const {
onAvailable,
onDestroyed,
ignoreExistingResources = false,
} = options;
// Cache the Watcher once for all, the first time we call `watch()`.
// This `watcher` attribute may be then used in any function of the ResourceWatcher after this.
@ -95,8 +99,16 @@ class ResourceWatcher {
await this._watchAllTargets();
for (const resource of resources) {
await this._startListening(resource);
this._registerListeners(resource, options);
// If we are registering the first listener, so start listening from the server about
// this one resource.
if (this._availableListeners.count(resource) === 0) {
await this._startListening(resource);
}
this._availableListeners.on(resource, onAvailable);
if (onDestroyed) {
this._destroyedListeners.on(resource, onDestroyed);
}
}
if (!ignoreExistingResources) {
@ -112,16 +124,24 @@ class ResourceWatcher {
const { onAvailable, onDestroyed } = options;
for (const resource of resources) {
this._availableListeners.off(resource, onAvailable);
if (onDestroyed) {
this._destroyedListeners.off(resource, onDestroyed);
}
this._stopListening(resource);
const hadAtLeastOneListener =
this._availableListeners.count(resource) > 0;
this._availableListeners.off(resource, onAvailable);
if (
hadAtLeastOneListener &&
this._availableListeners.count(resource) === 0
) {
this._stopListening(resource);
}
}
// Stop watching for targets if we removed the last listener.
let listeners = 0;
for (const count of this._listenerCount) {
for (const count of this._listenerCount.values()) {
listeners += count;
}
if (listeners <= 0) {
@ -129,20 +149,6 @@ class ResourceWatcher {
}
}
/**
* Register listeners to watch for a given type of resource.
*
* @param {Object}
* - {Function} onAvailable: mandatory
* - {Function} onDestroyed: optional
*/
_registerListeners(resource, { onAvailable, onDestroyed }) {
this._availableListeners.on(resource, onAvailable);
if (onDestroyed) {
this._destroyedListeners.on(resource, onDestroyed);
}
}
/**
* Start watching for all already existing and future targets.
*

View File

@ -28,8 +28,8 @@ const TESTS = {
hasMethod(emitter, "on") &&
hasMethod(emitter, "off") &&
hasMethod(emitter, "once") &&
!hasMethod(emitter, "decorate") &&
!hasMethod(emitter, "count"),
hasMethod(emitter, "count") &&
!hasMethod(emitter, "decorate"),
`Event Emitter ${
isAnEmitter ? "instance" : "mixin"
} has the expected methods.`