зеркало из https://github.com/mozilla/gecko-dev.git
148 строки
4.2 KiB
JavaScript
148 строки
4.2 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const { Cu } = require("chrome");
|
|
const InspectorUtils = require("InspectorUtils");
|
|
const EventEmitter = require("devtools/shared/event-emitter");
|
|
|
|
/**
|
|
* The CustomElementWatcher can be used to be notified if a custom element definition
|
|
* is created for a node.
|
|
*
|
|
* When a custom element is defined for a monitored name, an "element-defined" event is
|
|
* fired with the following Object argument:
|
|
* - {String} name: name of the custom element defined
|
|
* - {Set} Set of impacted node actors
|
|
*/
|
|
class CustomElementWatcher extends EventEmitter {
|
|
constructor(chromeEventHandler) {
|
|
super();
|
|
|
|
this.chromeEventHandler = chromeEventHandler;
|
|
this._onCustomElementDefined = this._onCustomElementDefined.bind(this);
|
|
this.chromeEventHandler.addEventListener(
|
|
"customelementdefined",
|
|
this._onCustomElementDefined
|
|
);
|
|
|
|
/**
|
|
* Each window keeps its own custom element registry, all of them are watched
|
|
* separately. The struture of the watchedRegistries is as follows
|
|
*
|
|
* WeakMap(
|
|
* registry -> Map (
|
|
* name -> Set(NodeActors)
|
|
* )
|
|
* )
|
|
*/
|
|
this.watchedRegistries = new WeakMap();
|
|
}
|
|
|
|
destroy() {
|
|
this.watchedRegistries = null;
|
|
this.chromeEventHandler.removeEventListener(
|
|
"customelementdefined",
|
|
this._onCustomElementDefined
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Watch for custom element definitions matching the name of the provided NodeActor.
|
|
*/
|
|
manageNode(nodeActor) {
|
|
if (!this._isValidNode(nodeActor)) {
|
|
return;
|
|
}
|
|
|
|
if (!this._shouldWatchDefinition(nodeActor)) {
|
|
return;
|
|
}
|
|
|
|
const registry = nodeActor.rawNode.ownerGlobal.customElements;
|
|
const registryMap = this._getMapForRegistry(registry);
|
|
|
|
const name = nodeActor.rawNode.localName;
|
|
const actorsSet = this._getActorsForName(name, registryMap);
|
|
actorsSet.add(nodeActor);
|
|
}
|
|
|
|
/**
|
|
* Stop watching the provided NodeActor.
|
|
*/
|
|
unmanageNode(nodeActor) {
|
|
if (!this._isValidNode(nodeActor)) {
|
|
return;
|
|
}
|
|
|
|
const win = nodeActor.rawNode.ownerGlobal;
|
|
const registry = win.customElements;
|
|
const registryMap = this._getMapForRegistry(registry);
|
|
const name = nodeActor.rawNode.localName;
|
|
if (registryMap.has(name)) {
|
|
registryMap.get(name).delete(nodeActor);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve the map of name->nodeActors for a given CustomElementsRegistry.
|
|
* Will create the map if not created yet.
|
|
*/
|
|
_getMapForRegistry(registry) {
|
|
if (!this.watchedRegistries.has(registry)) {
|
|
this.watchedRegistries.set(registry, new Map());
|
|
}
|
|
return this.watchedRegistries.get(registry);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the set of nodeActors for a given name and registry.
|
|
* Will create the set if not created yet.
|
|
*/
|
|
_getActorsForName(name, registryMap) {
|
|
if (!registryMap.has(name)) {
|
|
registryMap.set(name, new Set());
|
|
}
|
|
return registryMap.get(name);
|
|
}
|
|
|
|
_shouldWatchDefinition(nodeActor) {
|
|
const doc = nodeActor.rawNode.ownerDocument;
|
|
const namespaceURI = doc.documentElement.namespaceURI;
|
|
const name = nodeActor.rawNode.localName;
|
|
const isValidName = InspectorUtils.isCustomElementName(name, namespaceURI);
|
|
|
|
const customElements = doc.defaultView.customElements;
|
|
return isValidName && !customElements.get(name);
|
|
}
|
|
|
|
_onCustomElementDefined(event) {
|
|
const doc = event.target;
|
|
const registry = doc.defaultView.customElements;
|
|
const registryMap = this._getMapForRegistry(registry);
|
|
|
|
const name = event.detail;
|
|
const actors = this._getActorsForName(name, registryMap);
|
|
this.emit("element-defined", { name, actors });
|
|
registryMap.delete(name);
|
|
}
|
|
|
|
/**
|
|
* Some nodes (e.g. inside of <template> tags) don't have a documentElement or an
|
|
* ownerGlobal and can't be watched by this helper.
|
|
*/
|
|
_isValidNode(nodeActor) {
|
|
const node = nodeActor.rawNode;
|
|
return (
|
|
!Cu.isDeadWrapper(node) &&
|
|
node.ownerGlobal &&
|
|
node.ownerDocument &&
|
|
node.ownerDocument.documentElement
|
|
);
|
|
}
|
|
}
|
|
|
|
exports.CustomElementWatcher = CustomElementWatcher;
|