Bug 1268134 - Add actor to target windows by ID. r=ochameau draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Thu, 14 Apr 2016 16:29:53 -0500
changeset 487696 62625e7adee6252ba014bd14a194e13e52e9f2d6
parent 487537 74cef34e1927a0c998ecb2fbe7836fd5cdcd43fa
child 487697 74018ee007f6775674a1665afa53f7c84b23f0be
push id46299
push userbmo:jryans@gmail.com
push dateWed, 22 Feb 2017 00:00:11 +0000
reviewersochameau
bugs1268134
milestone54.0a1
Bug 1268134 - Add actor to target windows by ID. r=ochameau MozReview-Commit-ID: EDwcd7j1yhH
devtools/server/actors/moz.build
devtools/server/actors/root.js
devtools/server/actors/window.js
devtools/server/docs/actor-hierarchy.md
--- 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,