зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 9b16857ca48d (bug 1302702)
This commit is contained in:
Родитель
cda1cb7fd5
Коммит
5f9f503238
|
@ -164,9 +164,8 @@ var onConnectionReady = Task.async(function* ([aType, aTraits]) {
|
|||
*/
|
||||
function buildAddonLink(addon, parent) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = async function () {
|
||||
const isTabActor = addon.isWebExtension;
|
||||
openToolbox(addon, true, "webconsole", isTabActor);
|
||||
a.onclick = function () {
|
||||
openToolbox(addon, true, "jsdebugger", false);
|
||||
};
|
||||
|
||||
a.textContent = addon.name;
|
||||
|
|
|
@ -351,15 +351,15 @@ TabTarget.prototype = {
|
|||
},
|
||||
|
||||
get isAddon() {
|
||||
return !!(this._form && this._form.actor &&
|
||||
this._form.actor.match(/conn\d+\.addon\d+/)) || this.isWebExtension;
|
||||
return !!(this._form && this._form.actor && (
|
||||
this._form.actor.match(/conn\d+\.addon\d+/) ||
|
||||
this._form.actor.match(/conn\d+\.webExtension\d+/)
|
||||
));
|
||||
},
|
||||
|
||||
get isWebExtension() {
|
||||
return !!(this._form && this._form.actor && (
|
||||
this._form.actor.match(/conn\d+\.webExtension\d+/) ||
|
||||
this._form.actor.match(/child\d+\/webExtension\d+/)
|
||||
));
|
||||
return !!(this._form && this._form.actor &&
|
||||
this._form.actor.match(/conn\d+\.webExtension\d+/));
|
||||
},
|
||||
|
||||
get isLocalTab() {
|
||||
|
@ -375,7 +375,7 @@ TabTarget.prototype = {
|
|||
* for tools that support the Remote Debugging Protocol even for local
|
||||
* connections.
|
||||
*/
|
||||
makeRemote: async function () {
|
||||
makeRemote: function () {
|
||||
if (this._remote) {
|
||||
return this._remote.promise;
|
||||
}
|
||||
|
@ -398,22 +398,6 @@ TabTarget.prototype = {
|
|||
this._client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
// A local TabTarget will never perform chrome debugging.
|
||||
this._chrome = false;
|
||||
} else if (this._form.isWebExtension &&
|
||||
this.client.mainRoot.traits.webExtensionAddonConnect) {
|
||||
// The addonActor form is related to a WebExtensionParentActor instance,
|
||||
// which isn't a tab actor on its own, it is an actor living in the parent process
|
||||
// with access to the addon metadata, it can control the addon (e.g. reloading it)
|
||||
// and listen to the AddonManager events related to the lifecycle of the addon
|
||||
// (e.g. when the addon is disabled or uninstalled ).
|
||||
// To retrieve the TabActor instance, we call its "connect" method,
|
||||
// (which fetches the TabActor form from a WebExtensionChildActor instance).
|
||||
let {form} = await this._client.request({
|
||||
to: this._form.actor, type: "connect",
|
||||
});
|
||||
|
||||
this._form = form;
|
||||
this._url = form.url;
|
||||
this._title = form.title;
|
||||
}
|
||||
|
||||
this._setupRemoteListeners();
|
||||
|
|
|
@ -46,11 +46,17 @@ var connect = Task.async(function*() {
|
|||
if (addonID) {
|
||||
let { addons } = yield gClient.listAddons();
|
||||
let addonActor = addons.filter(addon => addon.id === addonID).pop();
|
||||
let isTabActor = addonActor.isWebExtension;
|
||||
openToolbox({form: addonActor, chrome: true, isTabActor});
|
||||
openToolbox({
|
||||
form: addonActor,
|
||||
chrome: true,
|
||||
isTabActor: addonActor.isWebExtension ? true : false
|
||||
});
|
||||
} else {
|
||||
let response = yield gClient.getProcess();
|
||||
openToolbox({form: response.form, chrome: true});
|
||||
openToolbox({
|
||||
form: response.form,
|
||||
chrome: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ DevToolsModules(
|
|||
'webbrowser.js',
|
||||
'webconsole.js',
|
||||
'webextension-inspected-window.js',
|
||||
'webextension-parent.js',
|
||||
'webextension.js',
|
||||
'webgl.js',
|
||||
'window.js',
|
||||
|
|
|
@ -190,10 +190,7 @@ RootActor.prototype = {
|
|||
heapSnapshots: true,
|
||||
// Whether or not the timeline actor can emit DOMContentLoaded and Load
|
||||
// markers, currently in use by the network monitor. Fx45+
|
||||
documentLoadingMarkers: true,
|
||||
// Whether or not the webextension addon actor have to be connected
|
||||
// to retrieve the extension child process tab actors.
|
||||
webExtensionAddonConnect: true,
|
||||
documentLoadingMarkers: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -597,12 +597,6 @@ TabActor.prototype = {
|
|||
this._updateChildDocShells();
|
||||
},
|
||||
|
||||
_unwatchDocShell(docShell) {
|
||||
if (this._progressListener) {
|
||||
this._progressListener.unwatch(docShell);
|
||||
}
|
||||
},
|
||||
|
||||
onSwitchToFrame(request) {
|
||||
let windowId = request.windowId;
|
||||
let win;
|
||||
|
@ -706,43 +700,9 @@ TabActor.prototype = {
|
|||
},
|
||||
|
||||
_onDocShellDestroy(docShell) {
|
||||
// Stop watching this docshell (the unwatch() method will check if we
|
||||
// started watching it before).
|
||||
this._unwatchDocShell(docShell);
|
||||
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
this._notifyDocShellDestroy(webProgress);
|
||||
|
||||
if (webProgress.DOMWindow == this._originalWindow) {
|
||||
// If the original top level document we connected to is removed,
|
||||
// we try to switch to any other top level document
|
||||
let rootDocShells = this.docShells
|
||||
.filter(d => {
|
||||
return d != this.docShell &&
|
||||
this._isRootDocShell(d);
|
||||
});
|
||||
if (rootDocShells.length > 0) {
|
||||
let newRoot = rootDocShells[0];
|
||||
this._originalWindow = newRoot.DOMWindow;
|
||||
this._changeTopLevelDocument(this._originalWindow);
|
||||
} else {
|
||||
// If for some reason (typically during Firefox shutdown), the original
|
||||
// document is destroyed, and there is no other top level docshell,
|
||||
// we detach the tab actor to unregister all listeners and prevent any
|
||||
// exception
|
||||
this.exit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the currently targeted context is destroyed,
|
||||
// and we aren't on the top-level document,
|
||||
// we have to switch to the top-level one.
|
||||
if (webProgress.DOMWindow == this.window &&
|
||||
this.window != this._originalWindow) {
|
||||
this._changeTopLevelDocument(this._originalWindow);
|
||||
}
|
||||
},
|
||||
|
||||
_isRootDocShell(docShell) {
|
||||
|
@ -755,34 +715,36 @@ TabActor.prototype = {
|
|||
return !docShell.parent;
|
||||
},
|
||||
|
||||
_docShellToWindow(docShell) {
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
let window = webProgress.DOMWindow;
|
||||
let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
let parentID = undefined;
|
||||
// Ignore the parent of the original document on non-e10s firefox,
|
||||
// as we get the xul window as parent and don't care about it.
|
||||
if (window.parent && window != this._originalWindow) {
|
||||
parentID = window.parent
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
parentID,
|
||||
url: window.location.href,
|
||||
title: window.document.title,
|
||||
};
|
||||
},
|
||||
|
||||
// Convert docShell list to windows objects list being sent to the client
|
||||
_docShellsToWindows(docshells) {
|
||||
return docshells.map(docShell => this._docShellToWindow(docShell));
|
||||
return docshells.map(docShell => {
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
let window = webProgress.DOMWindow;
|
||||
let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
let parentID = undefined;
|
||||
// Ignore the parent of the original document on non-e10s firefox,
|
||||
// as we get the xul window as parent and don't care about it.
|
||||
if (window.parent && window != this._originalWindow) {
|
||||
parentID = window.parent
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
}
|
||||
|
||||
// Collect the addonID from the document origin attributes.
|
||||
let addonID = window.document.nodePrincipal.addonId;
|
||||
|
||||
return {
|
||||
id,
|
||||
parentID,
|
||||
addonID,
|
||||
url: window.location.href,
|
||||
title: window.document.title,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
_notifyDocShellsUpdate(docshells) {
|
||||
|
@ -818,6 +780,41 @@ TabActor.prototype = {
|
|||
destroy: true
|
||||
}]
|
||||
});
|
||||
|
||||
// Stop watching this docshell (the unwatch() method will check if we
|
||||
// started watching it before).
|
||||
webProgress.QueryInterface(Ci.nsIDocShell);
|
||||
this._progressListener.unwatch(webProgress);
|
||||
|
||||
if (webProgress.DOMWindow == this._originalWindow) {
|
||||
// If the original top level document we connected to is removed,
|
||||
// we try to switch to any other top level document
|
||||
let rootDocShells = this.docShells
|
||||
.filter(d => {
|
||||
return d != this.docShell &&
|
||||
this._isRootDocShell(d);
|
||||
});
|
||||
if (rootDocShells.length > 0) {
|
||||
let newRoot = rootDocShells[0];
|
||||
this._originalWindow = newRoot.DOMWindow;
|
||||
this._changeTopLevelDocument(this._originalWindow);
|
||||
} else {
|
||||
// If for some reason (typically during Firefox shutdown), the original
|
||||
// document is destroyed, and there is no other top level docshell,
|
||||
// we detach the tab actor to unregister all listeners and prevent any
|
||||
// exception
|
||||
this.exit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the currently targeted context is destroyed,
|
||||
// and we aren't on the top-level document,
|
||||
// we have to switch to the top-level one.
|
||||
if (webProgress.DOMWindow == this.window &&
|
||||
this.window != this._originalWindow) {
|
||||
this._changeTopLevelDocument(this._originalWindow);
|
||||
}
|
||||
},
|
||||
|
||||
_notifyDocShellDestroyAll() {
|
||||
|
@ -869,7 +866,7 @@ TabActor.prototype = {
|
|||
// Check for docShell availability, as it can be already gone
|
||||
// during Firefox shutdown.
|
||||
if (this.docShell) {
|
||||
this._unwatchDocShell(this.docShell);
|
||||
this._progressListener.unwatch(this.docShell);
|
||||
this._restoreDocumentSettings();
|
||||
}
|
||||
if (this._progressListener) {
|
||||
|
|
|
@ -14,7 +14,7 @@ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|||
|
||||
loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
|
||||
loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
|
||||
loader.lazyRequireGetter(this, "WebExtensionParentActor", "devtools/server/actors/webextension-parent", true);
|
||||
loader.lazyRequireGetter(this, "WebExtensionActor", "devtools/server/actors/webextension", true);
|
||||
loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
|
||||
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker-list", true);
|
||||
loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
|
||||
|
@ -835,7 +835,7 @@ BrowserAddonList.prototype.getList = function () {
|
|||
let actor = this._actorByAddonId.get(addon.id);
|
||||
if (!actor) {
|
||||
if (addon.isWebExtension) {
|
||||
actor = new WebExtensionParentActor(this._connection, addon);
|
||||
actor = new WebExtensionActor(this._connection, addon);
|
||||
} else {
|
||||
actor = new BrowserAddonActor(this._connection, addon);
|
||||
}
|
||||
|
@ -843,10 +843,8 @@ BrowserAddonList.prototype.getList = function () {
|
|||
this._actorByAddonId.set(addon.id, actor);
|
||||
}
|
||||
}
|
||||
|
||||
deferred.resolve([...this._actorByAddonId].map(([_, actor]) => actor));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
/* 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 {DebuggerServer} = require("devtools/server/main");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {webExtensionSpec} = require("devtools/shared/specs/webextension-parent");
|
||||
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
|
||||
|
||||
/**
|
||||
* Creates the actor that represents the addon in the parent process, which connects
|
||||
* itself to a WebExtensionChildActor counterpart which is created in the
|
||||
* extension process (or in the main process if the WebExtensions OOP mode is disabled).
|
||||
*
|
||||
* The WebExtensionParentActor subscribes itself as an AddonListener on the AddonManager
|
||||
* and forwards this events to child actor (e.g. on addon reload or when the addon is
|
||||
* uninstalled completely) and connects to the child extension process using a `browser`
|
||||
* element provided by the extension internals (it is not related to any single extension,
|
||||
* but it will be created automatically to the currently selected "WebExtensions OOP mode"
|
||||
* and it persist across the extension reloads (it is destroyed once the actor exits).
|
||||
* WebExtensionActor is a child of RootActor, it can be retrieved via
|
||||
* RootActor.listAddons request.
|
||||
*
|
||||
* @param {DebuggerServerConnection} conn
|
||||
* The connection to the client.
|
||||
* @param {AddonWrapper} addon
|
||||
* The target addon.
|
||||
*/
|
||||
const WebExtensionParentActor = protocol.ActorClassWithSpec(webExtensionSpec, {
|
||||
initialize(conn, addon) {
|
||||
this.conn = conn;
|
||||
this.addon = addon;
|
||||
this.id = addon.id;
|
||||
this._childFormPromise = null;
|
||||
|
||||
AddonManager.addAddonListener(this);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
AddonManager.removeAddonListener(this);
|
||||
|
||||
this.addon = null;
|
||||
this._childFormPromise = null;
|
||||
|
||||
if (this._destroyProxyChildActor) {
|
||||
this._destroyProxyChildActor();
|
||||
delete this._destroyProxyChildActor;
|
||||
}
|
||||
},
|
||||
|
||||
setOptions() {
|
||||
// NOTE: not used anymore for webextensions, still used in the legacy addons,
|
||||
// addon manager is currently going to call it automatically on every addon.
|
||||
},
|
||||
|
||||
reload() {
|
||||
return this.addon.reload().then(() => {
|
||||
return {};
|
||||
});
|
||||
},
|
||||
|
||||
form() {
|
||||
return {
|
||||
actor: this.actorID,
|
||||
id: this.id,
|
||||
name: this.addon.name,
|
||||
iconURL: this.addon.iconURL,
|
||||
debuggable: this.addon.isDebuggable,
|
||||
temporarilyInstalled: this.addon.temporarilyInstalled,
|
||||
isWebExtension: true,
|
||||
};
|
||||
},
|
||||
|
||||
connect() {
|
||||
if (this._childFormPormise) {
|
||||
return this._childFormPromise;
|
||||
}
|
||||
|
||||
let proxy = new ProxyChildActor(this.conn, this);
|
||||
this._childFormPromise = proxy.connect().then(form => {
|
||||
// Merge into the child actor form, some addon metadata
|
||||
// (e.g. the addon name shown in the addon debugger window title).
|
||||
return Object.assign(form, {
|
||||
id: this.addon.id,
|
||||
name: this.addon.name,
|
||||
iconURL: this.addon.iconURL,
|
||||
// Set the isOOP attribute on the connected child actor form.
|
||||
isOOP: proxy.isOOP,
|
||||
});
|
||||
});
|
||||
this._destroyProxyChildActor = () => proxy.destroy();
|
||||
|
||||
return this._childFormPromise;
|
||||
},
|
||||
|
||||
// ProxyChildActor callbacks.
|
||||
|
||||
onProxyChildActorDestroy() {
|
||||
// Invalidate the cached child actor and form Promise
|
||||
// if the child actor exits.
|
||||
this._childFormPromise = null;
|
||||
delete this._destroyProxyChildActor;
|
||||
},
|
||||
|
||||
// AddonManagerListener callbacks.
|
||||
|
||||
onInstalled(addon) {
|
||||
if (addon.id != this.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the AddonManager's addon object on reload/update.
|
||||
this.addon = addon;
|
||||
},
|
||||
|
||||
onUninstalled(addon) {
|
||||
if (addon != this.addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
});
|
||||
|
||||
exports.WebExtensionParentActor = WebExtensionParentActor;
|
||||
|
||||
function ProxyChildActor(connection, parentActor) {
|
||||
this._conn = connection;
|
||||
this._parentActor = parentActor;
|
||||
this.addonId = parentActor.id;
|
||||
|
||||
this._onChildExit = this._onChildExit.bind(this);
|
||||
|
||||
this._form = null;
|
||||
this._browser = null;
|
||||
this._childActorID = null;
|
||||
}
|
||||
|
||||
ProxyChildActor.prototype = {
|
||||
/**
|
||||
* Connect the webextension child actor.
|
||||
*/
|
||||
async connect() {
|
||||
if (this._browser) {
|
||||
throw new Error("This actor is already connected to the extension process");
|
||||
}
|
||||
|
||||
// Called when the debug browser element has been destroyed
|
||||
// (no actor is using it anymore to connect the child extension process).
|
||||
const onDestroy = this.destroy.bind(this);
|
||||
|
||||
this._browser = await ExtensionParent.DebugUtils.getExtensionProcessBrowser(this);
|
||||
|
||||
this._form = await DebuggerServer.connectToChild(this._conn, this._browser, onDestroy,
|
||||
{addonId: this.addonId});
|
||||
|
||||
this._childActorID = this._form.actor;
|
||||
|
||||
// Exit the proxy child actor if the child actor has been destroyed.
|
||||
this._mm.addMessageListener("debug:webext_child_exit", this._onChildExit);
|
||||
|
||||
return this._form;
|
||||
},
|
||||
|
||||
get isOOP() {
|
||||
return this._browser ? this._browser.isRemoteBrowser : undefined;
|
||||
},
|
||||
|
||||
get _mm() {
|
||||
return this._browser && (
|
||||
this._browser.messageManager ||
|
||||
this._browser.frameLoader.messageManager);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
if (this._mm) {
|
||||
this._mm.removeMessageListener("debug:webext_child_exit", this._onChildExit);
|
||||
|
||||
this._mm.sendAsyncMessage("debug:webext_parent_exit", {
|
||||
actor: this._childActorID,
|
||||
});
|
||||
|
||||
ExtensionParent.DebugUtils.releaseExtensionProcessBrowser(this);
|
||||
}
|
||||
|
||||
if (this._parentActor) {
|
||||
this._parentActor.onProxyChildActorDestroy();
|
||||
}
|
||||
|
||||
this._parentActor = null;
|
||||
this._browser = null;
|
||||
this._childActorID = null;
|
||||
this._form = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the child actor exit.
|
||||
*/
|
||||
_onChildExit(msg) {
|
||||
if (msg.json.actor !== this._childActorID) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
};
|
|
@ -4,77 +4,63 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Ci, Cu, Cc } = require("chrome");
|
||||
const { Ci, Cu } = require("chrome");
|
||||
const Services = require("Services");
|
||||
|
||||
const { ChromeActor } = require("./chrome");
|
||||
const makeDebugger = require("./utils/make-debugger");
|
||||
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var { assert } = DevToolsUtils;
|
||||
|
||||
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
|
||||
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
|
||||
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
loader.lazyImporter(this, "XPIProvider", "resource://gre/modules/addons/XPIProvider.jsm");
|
||||
|
||||
const FALLBACK_DOC_MESSAGE = "Your addon does not have any document opened yet.";
|
||||
|
||||
/**
|
||||
* Creates a TabActor for debugging all the contexts associated to a target WebExtensions
|
||||
* add-on running in a child extension process.
|
||||
* add-on.
|
||||
* Most of the implementation is inherited from ChromeActor (which inherits most of its
|
||||
* implementation from TabActor).
|
||||
* WebExtensionChildActor is created by a WebExtensionParentActor counterpart, when its
|
||||
* parent actor's `connect` method has been called (on the listAddons RDP package),
|
||||
* it runs in the same process that the extension is running into (which can be the main
|
||||
* process if the extension is running in non-oop mode, or the child extension process
|
||||
* if the extension is running in oop-mode).
|
||||
*
|
||||
* A WebExtensionChildActor contains all tab actors, like a regular ChromeActor
|
||||
* or TabActor.
|
||||
* WebExtensionActor is a child of RootActor, it can be retrieved via
|
||||
* RootActor.listAddons request.
|
||||
* WebExtensionActor exposes all tab actors via its form() request, like TabActor.
|
||||
*
|
||||
* History lecture:
|
||||
* - The add-on actors used to not inherit TabActor because of the different way the
|
||||
* The add-on actors used to not inherit TabActor because of the different way the
|
||||
* add-on APIs where exposed to the add-on itself, and for this reason the Addon Debugger
|
||||
* has only a sub-set of the feature available in the Tab or in the Browser Toolbox.
|
||||
* - In a WebExtensions add-on all the provided contexts (background, popups etc.),
|
||||
* In a WebExtensions add-on all the provided contexts (background and popup pages etc.),
|
||||
* besides the Content Scripts which run in the content process, hooked to an existent
|
||||
* tab, by creating a new WebExtensionActor which inherits from ChromeActor, we can
|
||||
* provide a full features Addon Toolbox (which is basically like a BrowserToolbox which
|
||||
* filters the visible sources and frames to the one that are related to the target
|
||||
* add-on).
|
||||
* - When the WebExtensions OOP mode has been introduced, this actor has been refactored
|
||||
* and moved from the main process to the new child extension process.
|
||||
*
|
||||
* @param {DebuggerServerConnection} conn
|
||||
* @param conn DebuggerServerConnection
|
||||
* The connection to the client.
|
||||
* @param {nsIMessageSender} chromeGlobal.
|
||||
* The chromeGlobal where this actor has been injected by the
|
||||
* DebuggerServer.connectToChild method.
|
||||
* @param {string} prefix
|
||||
* the custom RDP prefix to use.
|
||||
* @param {string} addonId
|
||||
* the addonId of the target WebExtension.
|
||||
* @param addon AddonWrapper
|
||||
* The target addon.
|
||||
*/
|
||||
function WebExtensionChildActor(conn, chromeGlobal, prefix, addonId) {
|
||||
function WebExtensionActor(conn, addon) {
|
||||
ChromeActor.call(this, conn);
|
||||
|
||||
this._chromeGlobal = chromeGlobal;
|
||||
this._prefix = prefix;
|
||||
this.id = addonId;
|
||||
this.id = addon.id;
|
||||
this.addon = addon;
|
||||
|
||||
// Bind the _allowSource helper to this, it is used in the
|
||||
// TabActor to lazily create the TabSources instance.
|
||||
this._allowSource = this._allowSource.bind(this);
|
||||
this._onParentExit = this._onParentExit.bind(this);
|
||||
|
||||
this._chromeGlobal.addMessageListener("debug:webext_parent_exit", this._onParentExit);
|
||||
|
||||
// Set the consoleAPIListener filtering options
|
||||
// (retrieved and used in the related webconsole child actor).
|
||||
this.consoleAPIListenerOptions = {
|
||||
addonId: this.id,
|
||||
addonId: addon.id,
|
||||
};
|
||||
|
||||
this.aps = Cc["@mozilla.org/addons/policy-service;1"]
|
||||
.getService(Ci.nsIAddonPolicyService);
|
||||
|
||||
// This creates a Debugger instance for debugging all the add-on globals.
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: dbg => {
|
||||
|
@ -83,50 +69,135 @@ function WebExtensionChildActor(conn, chromeGlobal, prefix, addonId) {
|
|||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee.bind(this),
|
||||
});
|
||||
|
||||
// Try to discovery an existent extension page to attach (which will provide the initial
|
||||
// URL shown in the window tittle when the addon debugger is opened).
|
||||
let extensionWindow = this._searchForExtensionWindow();
|
||||
// Discover the preferred debug global for the target addon
|
||||
this.preferredTargetWindow = null;
|
||||
this._findAddonPreferredTargetWindow();
|
||||
|
||||
if (extensionWindow) {
|
||||
this._setWindow(extensionWindow);
|
||||
}
|
||||
AddonManager.addAddonListener(this);
|
||||
}
|
||||
exports.WebExtensionChildActor = WebExtensionChildActor;
|
||||
exports.WebExtensionActor = WebExtensionActor;
|
||||
|
||||
WebExtensionChildActor.prototype = Object.create(ChromeActor.prototype);
|
||||
WebExtensionActor.prototype = Object.create(ChromeActor.prototype);
|
||||
|
||||
WebExtensionChildActor.prototype.actorPrefix = "webExtension";
|
||||
WebExtensionChildActor.prototype.constructor = WebExtensionChildActor;
|
||||
WebExtensionActor.prototype.actorPrefix = "webExtension";
|
||||
WebExtensionActor.prototype.constructor = WebExtensionActor;
|
||||
|
||||
// NOTE: This is needed to catch in the webextension webconsole all the
|
||||
// errors raised by the WebExtension internals that are not currently
|
||||
// associated with any window.
|
||||
WebExtensionChildActor.prototype.isRootActor = true;
|
||||
WebExtensionActor.prototype.isRootActor = true;
|
||||
|
||||
WebExtensionActor.prototype.form = function () {
|
||||
assert(this.actorID, "addon should have an actorID.");
|
||||
|
||||
let baseForm = ChromeActor.prototype.form.call(this);
|
||||
|
||||
return Object.assign(baseForm, {
|
||||
actor: this.actorID,
|
||||
id: this.id,
|
||||
name: this.addon.name,
|
||||
url: this.addon.sourceURI ? this.addon.sourceURI.spec : undefined,
|
||||
iconURL: this.addon.iconURL,
|
||||
debuggable: this.addon.isDebuggable,
|
||||
temporarilyInstalled: this.addon.temporarilyInstalled,
|
||||
isWebExtension: this.addon.isWebExtension,
|
||||
});
|
||||
};
|
||||
|
||||
WebExtensionActor.prototype._attach = function () {
|
||||
// NOTE: we need to be sure that `this.window` can return a
|
||||
// window before calling the ChromeActor.onAttach, or the TabActor
|
||||
// will not be subscribed to the child doc shell updates.
|
||||
|
||||
// If a preferredTargetWindow exists, set it as the target for this actor
|
||||
// when the client request to attach this actor.
|
||||
if (this.preferredTargetWindow) {
|
||||
this._setWindow(this.preferredTargetWindow);
|
||||
} else {
|
||||
this._createFallbackWindow();
|
||||
}
|
||||
|
||||
// Call ChromeActor's _attach to listen for any new/destroyed chrome docshell
|
||||
ChromeActor.prototype._attach.apply(this);
|
||||
};
|
||||
|
||||
WebExtensionActor.prototype._detach = function () {
|
||||
this._destroyFallbackWindow();
|
||||
|
||||
// Call ChromeActor's _detach to unsubscribe new/destroyed chrome docshell listeners.
|
||||
ChromeActor.prototype._detach.apply(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the actor is removed from the connection.
|
||||
*/
|
||||
WebExtensionChildActor.prototype.exit = function () {
|
||||
if (this._chromeGlobal) {
|
||||
let chromeGlobal = this._chromeGlobal;
|
||||
this._chromeGlobal = null;
|
||||
|
||||
chromeGlobal.removeMessageListener("debug:webext_parent_exit", this._onParentExit);
|
||||
|
||||
chromeGlobal.sendAsyncMessage("debug:webext_child_exit", {
|
||||
actor: this.actorID
|
||||
});
|
||||
}
|
||||
WebExtensionActor.prototype.exit = function () {
|
||||
AddonManager.removeAddonListener(this);
|
||||
|
||||
this.preferredTargetWindow = null;
|
||||
this.addon = null;
|
||||
this.id = null;
|
||||
|
||||
return ChromeActor.prototype.exit.apply(this);
|
||||
};
|
||||
|
||||
// Private helpers.
|
||||
// Addon Specific Remote Debugging requestTypes and methods.
|
||||
|
||||
WebExtensionChildActor.prototype._createFallbackWindow = function () {
|
||||
/**
|
||||
* Reloads the addon.
|
||||
*/
|
||||
WebExtensionActor.prototype.onReload = function () {
|
||||
return this.addon.reload()
|
||||
.then(() => {
|
||||
// send an empty response
|
||||
return {};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the preferred global for the add-on (called from the AddonManager).
|
||||
*/
|
||||
WebExtensionActor.prototype.setOptions = function (addonOptions) {
|
||||
if ("global" in addonOptions) {
|
||||
// Set the proposed debug global as the preferred target window
|
||||
// (the actor will eventually set it as the target once it is attached)
|
||||
this.preferredTargetWindow = addonOptions.global;
|
||||
}
|
||||
};
|
||||
|
||||
// AddonManagerListener callbacks.
|
||||
|
||||
WebExtensionActor.prototype.onInstalled = function (addon) {
|
||||
if (addon.id != this.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the AddonManager's addon object on reload/update.
|
||||
this.addon = addon;
|
||||
};
|
||||
|
||||
WebExtensionActor.prototype.onUninstalled = function (addon) {
|
||||
if (addon != this.addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.exit();
|
||||
};
|
||||
|
||||
WebExtensionActor.prototype.onPropertyChanged = function (addon, changedPropNames) {
|
||||
if (addon != this.addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh the preferred debug global on disabled/reloaded/upgraded addon.
|
||||
if (changedPropNames.includes("debugGlobal")) {
|
||||
this._findAddonPreferredTargetWindow();
|
||||
}
|
||||
};
|
||||
|
||||
// Private helpers
|
||||
|
||||
WebExtensionActor.prototype._createFallbackWindow = function () {
|
||||
if (this.fallbackWindow) {
|
||||
// Skip if there is already an existent fallback window.
|
||||
return;
|
||||
|
@ -136,16 +207,26 @@ WebExtensionChildActor.prototype._createFallbackWindow = function () {
|
|||
// not defined for the target add-on or not yet when the actor instance has been
|
||||
// created).
|
||||
this.fallbackWebNav = Services.appShell.createWindowlessBrowser(true);
|
||||
this.fallbackWebNav.loadURI(
|
||||
`data:text/html;charset=utf-8,${FALLBACK_DOC_MESSAGE}`,
|
||||
0, null, null, null
|
||||
);
|
||||
|
||||
// Save the reference to the fallback DOMWindow.
|
||||
this.fallbackWindow = this.fallbackWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
this.fallbackDocShell = this.fallbackWebNav
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
// Insert the fallback doc message.
|
||||
this.fallbackWindow.document.body.innerText = FALLBACK_DOC_MESSAGE;
|
||||
Object.defineProperty(this, "docShell", {
|
||||
value: this.fallbackDocShell,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// Save the reference to the fallback DOMWindow
|
||||
this.fallbackWindow = this.fallbackDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype._destroyFallbackWindow = function () {
|
||||
WebExtensionActor.prototype._destroyFallbackWindow = function () {
|
||||
if (this.fallbackWebNav) {
|
||||
// Explicitly close the fallback windowless browser to prevent it to leak
|
||||
// (and to prevent it to freeze devtools xpcshell tests).
|
||||
|
@ -157,173 +238,65 @@ WebExtensionChildActor.prototype._destroyFallbackWindow = function () {
|
|||
}
|
||||
};
|
||||
|
||||
// Discovery an extension page to use as a default target window.
|
||||
// NOTE: This currently fail to discovery an extension page running in a
|
||||
// windowless browser when running in non-oop mode, and the background page
|
||||
// is set later using _onNewExtensionWindow.
|
||||
WebExtensionChildActor.prototype._searchForExtensionWindow = function () {
|
||||
let e = Services.ww.getWindowEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let window = e.getNext();
|
||||
|
||||
if (window.document.nodePrincipal.addonId == this.id) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Customized ChromeActor/TabActor hooks.
|
||||
|
||||
WebExtensionChildActor.prototype._onDocShellDestroy = function (docShell) {
|
||||
// Stop watching this docshell (the unwatch() method will check if we
|
||||
// started watching it before).
|
||||
this._unwatchDocShell(docShell);
|
||||
|
||||
// Let the _onDocShellDestroy notify that the docShell has been destroyed.
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
this._notifyDocShellDestroy(webProgress);
|
||||
|
||||
// If the destroyed docShell was the current docShell and the actor is
|
||||
// currently attached, switch to the fallback window
|
||||
if (this.attached && docShell == this.docShell) {
|
||||
// Creates a fallback window if it doesn't exist yet.
|
||||
this._createFallbackWindow();
|
||||
this._changeTopLevelDocument(this.fallbackWindow);
|
||||
}
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype._onNewExtensionWindow = function (window) {
|
||||
if (!this.window || this.window === this.fallbackWindow) {
|
||||
this._changeTopLevelDocument(window);
|
||||
}
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype._attach = function () {
|
||||
// NOTE: we need to be sure that `this.window` can return a
|
||||
// window before calling the ChromeActor.onAttach, or the TabActor
|
||||
// will not be subscribed to the child doc shell updates.
|
||||
|
||||
if (!this.window || this.window.document.nodePrincipal.addonId !== this.id) {
|
||||
// Discovery an existent extension page to attach.
|
||||
let extensionWindow = this._searchForExtensionWindow();
|
||||
|
||||
if (!extensionWindow) {
|
||||
this._createFallbackWindow();
|
||||
this._setWindow(this.fallbackWindow);
|
||||
} else {
|
||||
this._setWindow(extensionWindow);
|
||||
}
|
||||
}
|
||||
|
||||
// Call ChromeActor's _attach to listen for any new/destroyed chrome docshell
|
||||
ChromeActor.prototype._attach.apply(this);
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype._detach = function () {
|
||||
// Call ChromeActor's _detach to unsubscribe new/destroyed chrome docshell listeners.
|
||||
ChromeActor.prototype._detach.apply(this);
|
||||
|
||||
// Stop watching for new extension windows.
|
||||
this._destroyFallbackWindow();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the json details related to a docShell.
|
||||
* Discover the preferred debug global and switch to it if the addon has been attached.
|
||||
*/
|
||||
WebExtensionChildActor.prototype._docShellToWindow = function (docShell) {
|
||||
const baseWindowDetails = ChromeActor.prototype._docShellToWindow.call(this, docShell);
|
||||
WebExtensionActor.prototype._findAddonPreferredTargetWindow = function () {
|
||||
return new Promise(resolve => {
|
||||
let activeAddon = XPIProvider.activeAddons.get(this.id);
|
||||
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
let window = webProgress.DOMWindow;
|
||||
if (!activeAddon) {
|
||||
// The addon is not active, the background page is going to be destroyed,
|
||||
// navigate to the fallback window (if it already exists).
|
||||
resolve(null);
|
||||
} else {
|
||||
AddonManager.getAddonByInstanceID(activeAddon.instanceID)
|
||||
.then(privateWrapper => {
|
||||
let targetWindow = privateWrapper.getDebugGlobal();
|
||||
|
||||
// Collect the addonID from the document origin attributes and its sameType top level
|
||||
// frame.
|
||||
let addonID = window.document.nodePrincipal.addonId;
|
||||
let sameTypeRootAddonID = docShell.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.document.nodePrincipal.addonId;
|
||||
// Do not use the preferred global if it is not a DOMWindow as expected.
|
||||
if (!(targetWindow instanceof Ci.nsIDOMWindow)) {
|
||||
targetWindow = null;
|
||||
}
|
||||
|
||||
return Object.assign(baseWindowDetails, {
|
||||
addonID,
|
||||
sameTypeRootAddonID,
|
||||
resolve(targetWindow);
|
||||
});
|
||||
}
|
||||
}).then(preferredTargetWindow => {
|
||||
this.preferredTargetWindow = preferredTargetWindow;
|
||||
|
||||
if (!preferredTargetWindow) {
|
||||
// Create a fallback window if no preferred target window has been found.
|
||||
this._createFallbackWindow();
|
||||
} else if (this.attached) {
|
||||
// Change the top level document if the actor is already attached.
|
||||
this._changeTopLevelDocument(preferredTargetWindow);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of the json details related to an array/iterator of docShells.
|
||||
*/
|
||||
WebExtensionChildActor.prototype._docShellsToWindows = function (docshells) {
|
||||
WebExtensionActor.prototype._docShellsToWindows = function (docshells) {
|
||||
return ChromeActor.prototype._docShellsToWindows.call(this, docshells)
|
||||
.filter(windowDetails => {
|
||||
// Filter the docShells based on the addon id of the window or
|
||||
// its sameType top level frame.
|
||||
return windowDetails.addonID === this.id ||
|
||||
windowDetails.sameTypeRootAddonID === this.id;
|
||||
// filter the docShells based on the addon id
|
||||
return windowDetails.addonID == this.id;
|
||||
});
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype.isExtensionWindow = function (window) {
|
||||
return window.document.nodePrincipal.addonId == this.id;
|
||||
};
|
||||
|
||||
WebExtensionChildActor.prototype.isExtensionWindowDescendent = function (window) {
|
||||
// Check if the source is coming from a descendant docShell of an extension window.
|
||||
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
let rootWin = docShell.sameTypeRootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
return this.isExtensionWindow(rootWin);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the given source is associated with this addon and should be
|
||||
* added to the visible sources (retrieved and used by the webbrowser actor module).
|
||||
*/
|
||||
WebExtensionChildActor.prototype._allowSource = function (source) {
|
||||
// Use the source.element to detect the allowed source, if any.
|
||||
if (source.element) {
|
||||
let domEl = unwrapDebuggerObjectGlobal(source.element);
|
||||
return (this.isExtensionWindow(domEl.ownerGlobal) ||
|
||||
this.isExtensionWindowDescendent(domEl.ownerGlobal));
|
||||
}
|
||||
|
||||
// Fallback to check the uri if there is no source.element associated to the source.
|
||||
|
||||
// Retrieve the first component of source.url in the form "url1 -> url2 -> ...".
|
||||
let url = source.url.split(" -> ").pop();
|
||||
|
||||
// Filter out the code introduced by evaluating code in the webconsole.
|
||||
if (url === "debugger eval code") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let uri;
|
||||
|
||||
// Try to decode the url.
|
||||
WebExtensionActor.prototype._allowSource = function (source) {
|
||||
try {
|
||||
uri = Services.io.newURI(url);
|
||||
} catch (err) {
|
||||
Cu.reportError(`Unexpected invalid url: ${url}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter out resource and chrome sources (which are related to the loaded internals).
|
||||
if (["resource", "chrome", "file"].includes(uri.scheme)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
let addonID = this.aps.extensionURIToAddonId(uri);
|
||||
let uri = Services.io.newURI(source.url);
|
||||
let addonID = mapURIToAddonID(uri);
|
||||
|
||||
return addonID == this.id;
|
||||
} catch (err) {
|
||||
// extensionURIToAddonId raises an exception on non-extension URLs.
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -332,22 +305,11 @@ WebExtensionChildActor.prototype._allowSource = function (source) {
|
|||
* Return true if the given global is associated with this addon and should be
|
||||
* added as a debuggee, false otherwise.
|
||||
*/
|
||||
WebExtensionChildActor.prototype._shouldAddNewGlobalAsDebuggee = function (newGlobal) {
|
||||
WebExtensionActor.prototype._shouldAddNewGlobalAsDebuggee = function (newGlobal) {
|
||||
const global = unwrapDebuggerObjectGlobal(newGlobal);
|
||||
|
||||
if (global instanceof Ci.nsIDOMWindow) {
|
||||
// Filter out any global which contains a XUL document.
|
||||
if (global.document instanceof Ci.nsIDOMXULDocument) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Change top level document as a simulated frame switching.
|
||||
if (global.document.ownerGlobal && this.isExtensionWindow(global)) {
|
||||
this._onNewExtensionWindow(global.document.ownerGlobal);
|
||||
}
|
||||
|
||||
return global.document.ownerGlobal &&
|
||||
this.isExtensionWindowDescendent(global.document.ownerGlobal);
|
||||
return global.document.nodePrincipal.addonId == this.id;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -363,12 +325,9 @@ WebExtensionChildActor.prototype._shouldAddNewGlobalAsDebuggee = function (newGl
|
|||
return false;
|
||||
};
|
||||
|
||||
// Handlers for the messages received from the parent actor.
|
||||
|
||||
WebExtensionChildActor.prototype._onParentExit = function (msg) {
|
||||
if (msg.json.actor !== this.actorID) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.exit();
|
||||
};
|
||||
/**
|
||||
* Override WebExtensionActor requestTypes:
|
||||
* - redefined `reload`, which should reload the target addon
|
||||
* (instead of the entire browser as the regular ChromeActor does).
|
||||
*/
|
||||
WebExtensionActor.prototype.requestTypes.reload = WebExtensionActor.prototype.onReload;
|
||||
|
|
|
@ -17,6 +17,7 @@ try {
|
|||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const { dumpn } = DevToolsUtils;
|
||||
const { DebuggerServer, ActorPool } = require("devtools/server/main");
|
||||
const { ContentActor } = require("devtools/server/actors/childtab");
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
|
@ -33,22 +34,12 @@ try {
|
|||
|
||||
let mm = msg.target;
|
||||
let prefix = msg.data.prefix;
|
||||
let addonId = msg.data.addonId;
|
||||
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
connections.set(prefix, conn);
|
||||
|
||||
let actor;
|
||||
|
||||
if (addonId) {
|
||||
const { WebExtensionChildActor } = require("devtools/server/actors/webextension");
|
||||
actor = new WebExtensionChildActor(conn, chromeGlobal, prefix, addonId);
|
||||
} else {
|
||||
const { ContentActor } = require("devtools/server/actors/childtab");
|
||||
actor = new ContentActor(conn, chromeGlobal, prefix);
|
||||
}
|
||||
|
||||
let actor = new ContentActor(conn, chromeGlobal, prefix);
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
|
|
@ -1009,7 +1009,7 @@ var DebuggerServer = {
|
|||
* A promise object that is resolved once the connection is
|
||||
* established.
|
||||
*/
|
||||
connectToChild(connection, frame, onDestroy, {addonId} = {}) {
|
||||
connectToChild(connection, frame, onDestroy) {
|
||||
let deferred = SyncPromise.defer();
|
||||
|
||||
// Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
|
||||
|
@ -1122,9 +1122,6 @@ var DebuggerServer = {
|
|||
};
|
||||
|
||||
let destroy = DevToolsUtils.makeInfallible(function () {
|
||||
events.off(connection, "closed", destroy);
|
||||
Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
|
||||
|
||||
// provides hook to actor modules that need to exchange messages
|
||||
// between e10s parent and child processes
|
||||
parentModules.forEach(mod => {
|
||||
|
@ -1171,6 +1168,8 @@ var DebuggerServer = {
|
|||
|
||||
// Cleanup all listeners
|
||||
untrackMessageManager();
|
||||
Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
|
||||
events.off(connection, "closed", destroy);
|
||||
});
|
||||
|
||||
// Listen for various messages and frame events
|
||||
|
@ -1189,7 +1188,7 @@ var DebuggerServer = {
|
|||
// when user unplug the device or we lose the connection somehow.
|
||||
events.on(connection, "closed", destroy);
|
||||
|
||||
mm.sendAsyncMessage("debug:connect", { prefix, addonId });
|
||||
mm.sendAsyncMessage("debug:connect", { prefix });
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
|
|
@ -43,7 +43,6 @@ DevToolsModules(
|
|||
'timeline.js',
|
||||
'webaudio.js',
|
||||
'webextension-inspected-window.js',
|
||||
'webextension-parent.js',
|
||||
'webgl.js',
|
||||
'worker.js'
|
||||
)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/* 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");
|
||||
|
||||
const webExtensionSpec = generateActorSpec({
|
||||
typeName: "webExtensionAddon",
|
||||
|
||||
methods: {
|
||||
reload: {
|
||||
request: { },
|
||||
response: { addon: RetVal("json") },
|
||||
},
|
||||
|
||||
connect: {
|
||||
request: { },
|
||||
response: { form: RetVal("json") },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.webExtensionSpec = webExtensionSpec;
|
Загрузка…
Ссылка в новой задаче