gecko-dev/remote/targets/TargetList.jsm

174 строки
4.1 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";
var EXPORTED_SYMBOLS = ["TargetList"];
const { EventEmitter } = ChromeUtils.import(
"resource://gre/modules/EventEmitter.jsm"
);
const { TabTarget } = ChromeUtils.import(
"chrome://remote/content/targets/TabTarget.jsm"
);
const { MainProcessTarget } = ChromeUtils.import(
"chrome://remote/content/targets/MainProcessTarget.jsm"
);
const { TabObserver } = ChromeUtils.import(
"chrome://remote/content/observers/TargetObserver.jsm"
);
class TargetList {
constructor() {
// Target ID -> Target
this._targets = new Map();
EventEmitter.decorate(this);
}
/**
* Start listing and listening for all the debuggable targets
*/
async watchForTargets() {
await this.watchForTabs();
}
unwatchForTargets() {
this.unwatchForTabs();
}
/**
* Watch for all existing and new tabs being opened.
* So that we can create the related TabTarget instance for
* each of them.
*/
async watchForTabs() {
if (this.tabObserver) {
throw new Error("Targets is already watching for new tabs");
}
this.tabObserver = new TabObserver({ registerExisting: true });
this.tabObserver.on("open", async (eventName, tab) => {
const target = new TabTarget(this, tab.linkedBrowser);
this.registerTarget(target);
});
this.tabObserver.on("close", (eventName, tab) => {
const browser = tab.linkedBrowser;
// Ignore the browsers that haven't had time to initialize.
if (!browser.browsingContext) {
return;
}
const target = this.getByBrowsingContext(browser.browsingContext.id);
if (target) {
this.destroyTarget(target);
}
});
await this.tabObserver.start();
}
unwatchForTabs() {
if (this.tabObserver) {
this.tabObserver.stop();
this.tabObserver = null;
}
}
/**
* To be called right after instantiating a new Target instance.
* This will hold the new instance in the list and notify about
* its creation.
*/
registerTarget(target) {
this._targets.set(target.id, target);
this.emit("target-created", target);
}
/**
* To be called when the debuggable target has been destroy.
* So that we can notify it no longer exists and disconnect
* all connecting made to debug it.
*/
destroyTarget(target) {
target.destructor();
this._targets.delete(target.id);
this.emit("target-destroyed", target);
}
/**
* Destroy all the registered target of all kinds.
* This will end up dropping all connections made to debug any of them.
*/
destructor() {
for (const target of this) {
this.destroyTarget(target);
}
this._targets.clear();
if (this.mainProcessTarget) {
this.mainProcessTarget = null;
}
this.unwatchForTargets();
}
get size() {
return this._targets.size;
}
/**
* Get Target instance by target id
*
* @param {string} id
* Target id
*
* @return {Target}
*/
getById(id) {
return this._targets.get(id);
}
/**
* Get Target instance by browsing context id
*
* @param {number} id
* browsing context id
*
* @return {Target}
*/
getByBrowsingContext(id) {
let rv;
for (const target of this._targets.values()) {
if (target.browsingContext && target.browsingContext.id === id) {
rv = target;
break;
}
}
return rv;
}
/**
* Get the Target instance for the main process.
* This target is a singleton and only exposes a subset of domains.
*/
getMainProcessTarget() {
if (!this.mainProcessTarget) {
this.mainProcessTarget = new MainProcessTarget(this);
this.registerTarget(this.mainProcessTarget);
}
return this.mainProcessTarget;
}
*[Symbol.iterator]() {
for (const target of this._targets.values()) {
yield target;
}
}
toJSON() {
return [...this];
}
toString() {
return `[object TargetList ${this.size}]`;
}
}