Bug 1268134 - Add actor to target windows by ID. r=ochameau
MozReview-Commit-ID: EDwcd7j1yhH
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -61,16 +61,17 @@ DevToolsModules(
'tab.js',
'timeline.js',
'webaudio.js',
'webbrowser.js',
'webconsole.js',
'webextension-inspected-window.js',
'webextension.js',
'webgl.js',
+ 'window.js',
'worker-list.js',
'worker.js',
)
with Files('animation.js'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Animation Inspector')
with Files('breakpoint.js'):
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -2,23 +2,26 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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 { Cc, Ci, Cu } = require("chrome");
+const Services = require("Services");
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
loader.lazyGetter(this, "ppmm", () => {
return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
Ci.nsIMessageBroadcaster);
});
+loader.lazyRequireGetter(this, "WindowActor",
+ "devtools/server/actors/window", true);
/* Root actor for the remote debugging protocol. */
/**
* Create a remote debugging protocol root actor.
*
* @param connection
* The DebuggerServerConnection whose root actor we are constructing.
@@ -162,22 +165,25 @@ RootActor.prototype = {
// grabbing allocations from the MemoryActor are available for the performance tool
memoryActorAllocations: true,
// Added in Gecko 40, indicating that the backend isn't stupid about
// sending resumption packets on tab navigation.
noNeedToFakeResumptionOnNavigation: true,
// Added in Firefox 40. Indicates that the backend supports registering custom
// commands through the WebConsoleCommands API.
webConsoleCommands: true,
- // Whether root actor exposes tab actors
- // if allowChromeProcess is true, you can fetch a ChromeActor instance
- // to debug chrome and any non-content ressource via getProcess request
- // if allocChromeProcess is defined, but not true, it means that root actor
- // no longer expose tab actors, but also that getProcess forbids
- // exposing actors for security reasons
+ // Whether root actor exposes tab actors and access to any window.
+ // If allowChromeProcess is true, you can:
+ // * get a ChromeActor instance to debug chrome and any non-content
+ // resource via getProcess requests
+ // * get a WindowActor instance to debug windows which could be chrome,
+ // like browser windows via getWindow requests
+ // If allowChromeProcess is defined, but not true, it means that root actor
+ // no longer expose tab actors, but also that the above requests are
+ // forbidden for security reasons.
get allowChromeProcess() {
return DebuggerServer.allowChromeProcess;
},
// Whether or not `getProfile()` supports specifying a `startTime`
// and `endTime` to filter out samples. Fx40+
profilerDataFilterable: true,
// Whether or not the MemoryActor's heap snapshot abilities are
// fully equipped to handle heap snapshots for the memory tool. Fx44+
@@ -227,16 +233,17 @@ RootActor.prototype = {
}
if (typeof this._parameters.onShutdown === "function") {
this._parameters.onShutdown();
}
this._extraActors = null;
this.conn = null;
this._tabActorPool = null;
this._globalActorPool = null;
+ this._windowActorPool = null;
this._parameters = null;
this._chromeActor = null;
this._processActors.clear();
},
/* The 'listTabs' request and the 'tabListChanged' notification. */
/**
@@ -333,16 +340,48 @@ RootActor.prototype = {
}
return {
error: "noTab",
message: "Unexpected error while calling getTab(): " + error
};
});
},
+ onGetWindow: function ({ outerWindowID }) {
+ if (!DebuggerServer.allowChromeProcess) {
+ return {
+ from: this.actorID,
+ error: "forbidden",
+ message: "You are not allowed to debug windows."
+ };
+ }
+ let window = Services.wm.getOuterWindowWithId(outerWindowID);
+ if (!window) {
+ return {
+ from: this.actorID,
+ error: "notFound",
+ message: `No window found with outerWindowID ${outerWindowID}`,
+ };
+ }
+
+ if (!this._windowActorPool) {
+ this._windowActorPool = new ActorPool(this.conn);
+ this.conn.addActorPool(this._windowActorPool);
+ }
+
+ let actor = new WindowActor(this.conn, window);
+ actor.parentID = this.actorID;
+ this._windowActorPool.addActor(actor);
+
+ return {
+ from: this.actorID,
+ window: actor.form(),
+ };
+ },
+
onTabListChanged: function () {
this.conn.send({ from: this.actorID, type: "tabListChanged" });
/* It's a one-shot notification; no need to watch any more. */
this._parameters.tabList.onListChanged = null;
},
onListAddons: function () {
let addonList = this._parameters.addonList;
@@ -534,16 +573,17 @@ RootActor.prototype = {
delete this._extraActors[name];
}
}
};
RootActor.prototype.requestTypes = {
"listTabs": RootActor.prototype.onListTabs,
"getTab": RootActor.prototype.onGetTab,
+ "getWindow": RootActor.prototype.onGetWindow,
"listAddons": RootActor.prototype.onListAddons,
"listWorkers": RootActor.prototype.onListWorkers,
"listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations,
"listProcesses": RootActor.prototype.onListProcesses,
"getProcess": RootActor.prototype.onGetProcess,
"echo": RootActor.prototype.onEcho,
"protocolDescription": RootActor.prototype.onProtocolDescription
};
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/window.js
@@ -0,0 +1,83 @@
+/* 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 { Ci } = require("chrome");
+const Services = require("Services");
+const { TabActor } = require("./tab");
+
+/**
+ * Creates a WindowActor for debugging a single window, like a browser window in Firefox,
+ * but it can be used to reach any window in the process. (Currently this is parent
+ * process only because the root actor's `onGetWindow` doesn't try to cross process
+ * boundaries.) Both chrome and content windows are supported.
+ *
+ * Most of the implementation is inherited from TabActor. WindowActor exposes all tab
+ * actors via its form() request, like TabActor.
+ *
+ * You can request a specific window's actor via RootActor.getWindow().
+ *
+ * @param connection DebuggerServerConnection
+ * The connection to the client.
+ * @param window DOMWindow
+ * The window.
+ */
+function WindowActor(connection, window) {
+ TabActor.call(this, connection);
+
+ let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell);
+ Object.defineProperty(this, "docShell", {
+ value: docShell,
+ configurable: true
+ });
+}
+
+WindowActor.prototype = Object.create(TabActor.prototype);
+
+// Bug 1266561: This setting is mysteriously named, we should split up the
+// functionality that is triggered by it.
+WindowActor.prototype.isRootActor = true;
+
+WindowActor.prototype.observe = function (subject, topic, data) {
+ TabActor.prototype.observe.call(this, subject, topic, data);
+ if (!this.attached) {
+ return;
+ }
+ if (topic == "chrome-webnavigation-destroy") {
+ this._onDocShellDestroy(subject);
+ }
+};
+
+WindowActor.prototype._attach = function () {
+ if (this.attached) {
+ return false;
+ }
+
+ TabActor.prototype._attach.call(this);
+
+ // Listen for chrome docshells in addition to content docshells
+ if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
+ Services.obs.addObserver(this, "chrome-webnavigation-destroy", false);
+ }
+
+ return true;
+};
+
+WindowActor.prototype._detach = function () {
+ if (!this.attached) {
+ return false;
+ }
+
+ if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
+ Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
+ }
+
+ TabActor.prototype._detach.call(this);
+
+ return true;
+};
+
+exports.WindowActor = WindowActor;
--- a/devtools/server/docs/actor-hierarchy.md
+++ b/devtools/server/docs/actor-hierarchy.md
@@ -55,16 +55,21 @@ and returns its `actorID`. That's the ma
| Targets a worker (applies to various kinds like web worker, service
| worker, etc.).
| Returned by "listWorkers" request to the root actor to get all workers.
| Returned by "listWorkers" request to a BrowserTabActor to get workers for
| a specific tab.
| Returned by "listWorkers" request to a ChildProcessActor to get workers
| for the chrome of the child process.
|
+ |-- WindowActor (window.js)
+ | Targets a single window, such as a browser window in Firefox, but it can
+ | be used to reach any window in the parent process.
+ | Returned by "getWindow" request to the root actor.
+ |
|-- ChromeActor (chrome.js)
| Targets all resources in the parent process of firefox
| (chrome documents, JSM, JS XPCOM, etc.).
| Returned by "getProcess" request without any argument.
|
|-- ChildProcessActor (child-process.js)
| Targets the chrome of the child process (e10s).
| Returned by "getProcess" request with a id argument,