Bug 1450956 - Convert TabActor to protocol.js; r=ochameau
authoryulia <ystartsev@mozilla.com>
Wed, 09 May 2018 13:56:17 +0200
changeset 804150 7cfd6a1fa407af606ae048ea762266ce84332a0e
parent 804149 0beab09f80582c3a68471d09b9df8ebb07293b51
child 804151 a47957da1983ecb6d7b3ec323c84ec71421251b6
push id112312
push userbmo:standard8@mozilla.com
push dateTue, 05 Jun 2018 16:07:56 +0000
reviewersochameau
bugs1450956
milestone62.0a1
Bug 1450956 - Convert TabActor to protocol.js; r=ochameau MozReview-Commit-ID: EOkuPfDB7rg
devtools/server/actors/chrome.js
devtools/server/actors/content.js
devtools/server/actors/tab.js
devtools/server/actors/window.js
devtools/shared/specs/tab.js
--- a/devtools/server/actors/chrome.js
+++ b/devtools/server/actors/chrome.js
@@ -2,21 +2,21 @@
  * 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 { DebuggerServer } = require("../main");
-const { getChildDocShells, TabActor } = require("./tab");
+const { getChildDocShells, TabActor, tabPrototype } = require("./tab");
 const makeDebugger = require("./utils/make-debugger");
 
 const { extend } = require("devtools/shared/extend");
-const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
+const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const { tabSpec } = require("devtools/shared/specs/tab");
 
 /**
  * Creates a TabActor for debugging all the chrome content in the
  * current process. Most of the implementation is inherited from TabActor.
  * ChromeActor is a child of RootActor, it can be instanciated via
  * RootActor.getProcess request.
  * ChromeActor exposes all tab actors via its form() request, like TabActor.
@@ -36,22 +36,20 @@ const { tabSpec } = require("devtools/sh
  */
 
 /**
  * Protocol.js expects only the prototype object, and does not maintain the prototype
  * chain when it constructs the ActorClass. For this reason we are using `extend` to
  * maintain the properties of TabActor.prototype
  * */
 
-const chromePrototype = extend({}, TabActor.prototype);
+const chromePrototype = extend({}, tabPrototype);
 
 chromePrototype.initialize = function(connection) {
-  Actor.prototype.initialize.call(this, connection);
-  TabActor.call(this, connection);
-
+  TabActor.prototype.initialize.call(this, connection);
   // This creates a Debugger instance for chrome debugging all globals.
   this.makeDebugger = makeDebugger.bind(null, {
     findDebuggees: dbg => dbg.findAllGlobals(),
     shouldAddNewGlobalAsDebuggee: () => true
   });
 
   // Ensure catching the creation of any new content docshell
   this.listenForNewDocShells = true;
--- a/devtools/server/actors/content.js
+++ b/devtools/server/actors/content.js
@@ -1,19 +1,19 @@
 /* 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 { Cr } = require("chrome");
-var { TabActor } = require("devtools/server/actors/tab");
+var { TabActor, tabPrototype } = require("devtools/server/actors/tab");
 
 const { extend } = require("devtools/shared/extend");
-const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
+const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const { tabSpec } = require("devtools/shared/specs/tab");
 
 /**
  * Tab actor for documents living in a child process.
  *
  * Depends on TabActor, defined in tab.js.
  */
 
@@ -32,22 +32,21 @@ const { tabSpec } = require("devtools/sh
  */
 
 /**
  * Protocol.js expects only the prototype object, and does not maintain the prototype
  * chain when it constructs the ActorClass. For this reason we are using `extend` to
  * maintain the properties of TabActor.prototype
  * */
 
-const contentPrototype = extend({}, TabActor.prototype);
+const contentPrototype = extend({}, tabPrototype);
 
 contentPrototype.initialize = function(connection, chromeGlobal) {
   this._chromeGlobal = chromeGlobal;
-  Actor.prototype.initialize.call(this, connection);
-  TabActor.call(this, connection, chromeGlobal);
+  TabActor.prototype.initialize.call(this, connection, chromeGlobal);
   this.traits.reconfigure = false;
   this._sendForm = this._sendForm.bind(this);
   this._chromeGlobal.addMessageListener("debug:form", this._sendForm);
 
   Object.defineProperty(this, "docShell", {
     value: this._chromeGlobal.docShell,
     configurable: true
   });
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -18,21 +18,23 @@ const ChromeUtils = require("ChromeUtils
 var {
   ActorPool, createExtraActors, appendExtraActors
 } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
-const EventEmitter = require("devtools/shared/event-emitter");
 const InspectorUtils = require("InspectorUtils");
 
 const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm";
 
+const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
+const { tabSpec } = require("devtools/shared/specs/tab");
+
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
 loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM);
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
 loader.lazyRequireGetter(this, "getSheetText", "devtools/server/actors/stylesheets", true);
 
@@ -79,172 +81,170 @@ exports.getChildDocShells = getChildDocS
  * Browser-specific actors.
  */
 
 function getInnerId(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 }
 
-/**
- * Creates a TabActor whose main goal is to manage lifetime and
- * expose the tab actors being registered via DebuggerServer.registerModule.
- * But also track the lifetime of the document being tracked.
- *
- * ### Main requests:
- *
- * `attach`/`detach` requests:
- *  - start/stop document watching:
- *    Starts watching for new documents and emits `tabNavigated` and
- *    `frameUpdate` over RDP.
- *  - retrieve the thread actor:
- *    Instantiates a ThreadActor that can be later attached to in order to
- *    debug JS sources in the document.
- * `switchToFrame`:
- *  Change the targeted document of the whole TabActor, and its child tab actors
- *  to an iframe or back to its original document.
- *
- * Most of the TabActor properties (like `chromeEventHandler` or `docShells`)
- * are meant to be used by the various child tab actors.
- *
- * ### RDP events:
- *
- *  - `tabNavigated`:
- *    Sent when the tab is about to navigate or has just navigated to
- *    a different document.
- *    This event contains the following attributes:
- *     * url (string) The new URI being loaded.
- *     * nativeConsoleAPI (boolean) `false` if the console API of the page has
- *                                          been overridden (e.g. by Firebug),
- *                                  `true`  if the Gecko implementation is used.
- *     * state (string) `start` if we just start requesting the new URL,
- *                      `stop`  if the new URL is done loading.
- *     * isFrameSwitching (boolean) Indicates the event is dispatched when
- *                                  switching the TabActor context to
- *                                  a different frame. When we switch to
- *                                  an iframe, there is no document load.
- *                                  The targeted document is most likely
- *                                  going to be already done loading.
- *     * title (string) The document title being loaded.
- *                      (sent only on state=stop)
- *
- *  - `frameUpdate`:
- *    Sent when there was a change in the child frames contained in the document
- *    or when the tab's context was switched to another frame.
- *    This event can have four different forms depending on the type of change:
- *    * One or many frames are updated:
- *      { frames: [{ id, url, title, parentID }, ...] }
- *    * One frame got destroyed:
- *      { frames: [{ id, destroy: true }]}
- *    * All frames got destroyed:
- *      { destroyAll: true }
- *    * We switched the context of the TabActor to a specific frame:
- *      { selected: #id }
- *
- * ### Internal, non-rdp events:
- * Various events are also dispatched on the TabActor itself that are not
- * related to RDP, so, not sent to the client. They all relate to the documents
- * tracked by the TabActor (its main targeted document, but also any of its
- * iframes).
- *  - will-navigate
- *    This event fires once navigation starts.
- *    All pending user prompts are dealt with,
- *    but it is fired before the first request starts.
- *  - navigate
- *    This event is fired once the document's readyState is "complete".
- *  - window-ready
- *    This event is fired in various distinct scenarios:
- *     * When a new Window object is crafted, equivalent of `DOMWindowCreated`.
- *       It is dispatched before any page script is executed.
- *     * We will have already received a window-ready event for this window
- *       when it was created, but we received a window-destroyed event when
- *       it was frozen into the bfcache, and now the user navigated back to
- *       this page, so it's now live again and we should resume handling it.
- *     * For each existing document, when an `attach` request is received.
- *       At this point scripts in the page will be already loaded.
- *     * When `swapFrameLoaders` is used, such as with moving tabs between
- *       windows or toggling Responsive Design Mode.
- *  - window-destroyed
- *    This event is fired in two cases:
- *     * When the window object is destroyed, i.e. when the related document
- *       is garbage collected. This can happen when the tab is closed or the
- *       iframe is removed from the DOM.
- *       It is equivalent of `inner-window-destroyed` event.
- *     * When the page goes into the bfcache and gets frozen.
- *       The equivalent of `pagehide`.
- *  - changed-toplevel-document
- *    This event fires when we switch the TabActor targeted document
- *    to one of its iframes, or back to its original top document.
- *    It is dispatched between window-destroyed and window-ready.
- *  - stylesheet-added
- *    This event is fired when a StyleSheetActor is created.
- *    It contains the following attribute :
- *     * actor (StyleSheetActor) The created actor.
- *
- * Note that *all* these events are dispatched in the following order
- * when we switch the context of the TabActor to a given iframe:
- *  - will-navigate
- *  - window-destroyed
- *  - changed-toplevel-document
- *  - window-ready
- *  - navigate
- *
- * This class is subclassed by ContentActor and others.
- * Subclasses are expected to implement a getter for the docShell property.
- *
- * @param connection DebuggerServerConnection
- *        The conection to the client.
- */
-function TabActor(connection) {
-  // This usage of decorate should be removed in favor of using ES6 extends EventEmitter.
-  // See Bug 1394816.
-  EventEmitter.decorate(this);
+const tabPrototype = {
 
-  this.conn = connection;
-  this._tabActorPool = null;
-  // A map of actor names to actor instances provided by extensions.
-  this._extraActors = {};
-  this._exited = false;
-  this._sources = null;
-
-  // Map of DOM stylesheets to StyleSheetActors
-  this._styleSheetActors = new Map();
-
-  this._shouldAddNewGlobalAsDebuggee =
-    this._shouldAddNewGlobalAsDebuggee.bind(this);
-
-  this.makeDebugger = makeDebugger.bind(null, {
-    findDebuggees: () => {
-      return this.windows.concat(this.webextensionsContentScriptGlobals);
-    },
-    shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
-  });
+  /**
+   * Creates a TabActor whose main goal is to manage lifetime and
+   * expose the tab actors being registered via DebuggerServer.registerModule.
+   * But also track the lifetime of the document being tracked.
+   *
+   * ### Main requests:
+   *
+   * `attach`/`detach` requests:
+   *  - start/stop document watching:
+   *    Starts watching for new documents and emits `tabNavigated` and
+   *    `frameUpdate` over RDP.
+   *  - retrieve the thread actor:
+   *    Instantiates a ThreadActor that can be later attached to in order to
+   *    debug JS sources in the document.
+   * `switchToFrame`:
+   *  Change the targeted document of the whole TabActor, and its child tab actors
+   *  to an iframe or back to its original document.
+   *
+   * Most of the TabActor properties (like `chromeEventHandler` or `docShells`)
+   * are meant to be used by the various child tab actors.
+   *
+   * ### RDP events:
+   *
+   *  - `tabNavigated`:
+   *    Sent when the tab is about to navigate or has just navigated to
+   *    a different document.
+   *    This event contains the following attributes:
+   *     * url (string) The new URI being loaded.
+   *     * nativeConsoleAPI (boolean) `false` if the console API of the page has
+   *                                          been overridden (e.g. by Firebug),
+   *                                  `true`  if the Gecko implementation is used.
+   *     * state (string) `start` if we just start requesting the new URL,
+   *                      `stop`  if the new URL is done loading.
+   *     * isFrameSwitching (boolean) Indicates the event is dispatched when
+   *                                  switching the TabActor context to
+   *                                  a different frame. When we switch to
+   *                                  an iframe, there is no document load.
+   *                                  The targeted document is most likely
+   *                                  going to be already done loading.
+   *     * title (string) The document title being loaded.
+   *                      (sent only on state=stop)
+   *
+   *  - `frameUpdate`:
+   *    Sent when there was a change in the child frames contained in the document
+   *    or when the tab's context was switched to another frame.
+   *    This event can have four different forms depending on the type of change:
+   *    * One or many frames are updated:
+   *      { frames: [{ id, url, title, parentID }, ...] }
+   *    * One frame got destroyed:
+   *      { frames: [{ id, destroy: true }]}
+   *    * All frames got destroyed:
+   *      { destroyAll: true }
+   *    * We switched the context of the TabActor to a specific frame:
+   *      { selected: #id }
+   *
+   * ### Internal, non-rdp events:
+   * Various events are also dispatched on the TabActor itself that are not
+   * related to RDP, so, not sent to the client. They all relate to the documents
+   * tracked by the TabActor (its main targeted document, but also any of its
+   * iframes).
+   *  - will-navigate
+   *    This event fires once navigation starts.
+   *    All pending user prompts are dealt with,
+   *    but it is fired before the first request starts.
+   *  - navigate
+   *    This event is fired once the document's readyState is "complete".
+   *  - window-ready
+   *    This event is fired in various distinct scenarios:
+   *     * When a new Window object is crafted, equivalent of `DOMWindowCreated`.
+   *       It is dispatched before any page script is executed.
+   *     * We will have already received a window-ready event for this window
+   *       when it was created, but we received a window-destroyed event when
+   *       it was frozen into the bfcache, and now the user navigated back to
+   *       this page, so it's now live again and we should resume handling it.
+   *     * For each existing document, when an `attach` request is received.
+   *       At this point scripts in the page will be already loaded.
+   *     * When `swapFrameLoaders` is used, such as with moving tabs between
+   *       windows or toggling Responsive Design Mode.
+   *  - window-destroyed
+   *    This event is fired in two cases:
+   *     * When the window object is destroyed, i.e. when the related document
+   *       is garbage collected. This can happen when the tab is closed or the
+   *       iframe is removed from the DOM.
+   *       It is equivalent of `inner-window-destroyed` event.
+   *     * When the page goes into the bfcache and gets frozen.
+   *       The equivalent of `pagehide`.
+   *  - changed-toplevel-document
+   *    This event fires when we switch the TabActor targeted document
+   *    to one of its iframes, or back to its original top document.
+   *    It is dispatched between window-destroyed and window-ready.
+   *  - stylesheet-added
+   *    This event is fired when a StyleSheetActor is created.
+   *    It contains the following attribute :
+   *     * actor (StyleSheetActor) The created actor.
+   *
+   * Note that *all* these events are dispatched in the following order
+   * when we switch the context of the TabActor to a given iframe:
+   *  - will-navigate
+   *  - window-destroyed
+   *  - changed-toplevel-document
+   *  - window-ready
+   *  - navigate
+   *
+   * This class is subclassed by ContentActor and others.
+   * Subclasses are expected to implement a getter for the docShell property.
+   *
+   * @param connection DebuggerServerConnection
+   *        The conection to the client.
+   */
+  initialize: function(connection) {
+    Actor.prototype.initialize.call(this, connection);
 
-  // Flag eventually overloaded by sub classes in order to watch new docshells
-  // Used by the ChromeActor to list all frames in the Browser Toolbox
-  this.listenForNewDocShells = false;
+    this._tabActorPool = null;
+    // A map of actor names to actor instances provided by extensions.
+    this._extraActors = {};
+    this._exited = false;
+    this._sources = null;
+
+    // Map of DOM stylesheets to StyleSheetActors
+    this._styleSheetActors = new Map();
+
+    this._shouldAddNewGlobalAsDebuggee =
+      this._shouldAddNewGlobalAsDebuggee.bind(this);
+
+    this.makeDebugger = makeDebugger.bind(null, {
+      findDebuggees: () => {
+        return this.windows.concat(this.webextensionsContentScriptGlobals);
+      },
+      shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
+    });
 
-  this.traits = {
-    reconfigure: true,
-    // Supports frame listing via `listFrames` request and `frameUpdate` events
-    // as well as frame switching via `switchToFrame` request
-    frames: true,
-    // Do not require to send reconfigure request to reset the document state
-    // to what it was before using the TabActor
-    noTabReconfigureOnClose: true,
-    // Supports the logInPage request.
-    logInPage: true,
-  };
+    // Flag eventually overloaded by sub classes in order to watch new docshells
+    // Used by the ChromeActor to list all frames in the Browser Toolbox
+    this.listenForNewDocShells = false;
 
-  this._workerActorList = null;
-  this._workerActorPool = null;
-  this._onWorkerActorListChanged = this._onWorkerActorListChanged.bind(this);
-}
+    this.traits = {
+      reconfigure: true,
+      // Supports frame listing via `listFrames` request and `frameUpdate` events
+      // as well as frame switching via `switchToFrame` request
+      frames: true,
+      // Do not require to send reconfigure request to reset the document state
+      // to what it was before using the TabActor
+      noTabReconfigureOnClose: true,
+      // Supports the logInPage request.
+      logInPage: true,
+    };
 
-TabActor.prototype = {
+    this._workerActorList = null;
+    this._workerActorPool = null;
+    this._onWorkerActorListChanged = this._onWorkerActorListChanged.bind(this);
+  },
+
   traits: null,
 
   // Optional console API listener options (e.g. used by the WebExtensionActor to
   // filter console messages by addonID), set to an empty (no options) object by default.
   consoleAPIListenerOptions: {},
 
   // Optional TabSources filter function (e.g. used by the WebExtensionActor to filter
   // sources by addonID), allow all sources by default.
@@ -487,16 +487,17 @@ TabActor.prototype = {
     this._appendExtraActors(response);
     return response;
   },
 
   /**
    * Called when the actor is removed from the connection.
    */
   destroy() {
+    Actor.prototype.destroy.call(this);
     this.exit();
   },
 
   /**
    * Called by the root actor when the underlying tab is closed.
    */
   exit() {
     if (this.exited) {
@@ -1458,34 +1459,18 @@ TabActor.prototype = {
       if (this._tabActorPool.has(actor)) {
         this._tabActorPool.removeActor(actor);
       }
       delete this._extraActors[name];
     }
   },
 };
 
-/**
- * The request types this actor can handle.
- */
-TabActor.prototype.requestTypes = {
-  "attach": TabActor.prototype.attach,
-  "detach": TabActor.prototype.detach,
-  "focus": TabActor.prototype.focus,
-  "reload": TabActor.prototype.reload,
-  "navigateTo": TabActor.prototype.navigateTo,
-  "reconfigure": TabActor.prototype.reconfigure,
-  "ensureCSSErrorReportingEnabled": TabActor.prototype.ensureCSSErrorReportingEnabled,
-  "switchToFrame": TabActor.prototype.switchToFrame,
-  "listFrames": TabActor.prototype.listFrames,
-  "listWorkers": TabActor.prototype.listWorkers,
-  "logInPage": TabActor.prototype.logInPage,
-};
-
-exports.TabActor = TabActor;
+exports.tabPrototype = tabPrototype;
+exports.TabActor = ActorClassWithSpec(tabSpec, tabPrototype);
 
 /**
  * The DebuggerProgressListener object is an nsIWebProgressListener which
  * handles onStateChange events for the inspected browser. If the user tries to
  * navigate away from a paused page, the listener makes sure that the debuggee
  * is resumed before the navigation begins.
  *
  * @param TabActor aTabActor
--- a/devtools/server/actors/window.js
+++ b/devtools/server/actors/window.js
@@ -1,20 +1,20 @@
 /* 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");
+const { TabActor, tabPrototype } = require("./tab");
 
 const { extend } = require("devtools/shared/extend");
-const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
+const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const { tabSpec } = require("devtools/shared/specs/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.
  *
@@ -29,21 +29,20 @@ const { tabSpec } = require("devtools/sh
  * maintain the properties of TabActor.prototype
  *
  * @param connection DebuggerServerConnection
  *        The connection to the client.
  * @param window DOMWindow
  *        The window.
  */
 
-const windowPrototype = extend({}, TabActor.prototype);
+const windowPrototype = extend({}, tabPrototype);
 
 windowPrototype.initialize = function(connection, window) {
-  Actor.prototype.initialize.call(this, connection);
-  TabActor.call(this, connection);
+  TabActor.prototype.initialize.call(this, connection);
 
   const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDocShell);
   Object.defineProperty(this, "docShell", {
     value: docShell,
     configurable: true
   });
 };
--- a/devtools/shared/specs/tab.js
+++ b/devtools/shared/specs/tab.js
@@ -104,12 +104,12 @@ const tabSpec = generateActorSpec({
     logInPage: {
       request: {
         text: Option(0, "string"),
         category: Option(0, "string"),
         flags: Option(0, "string")
       },
       response: {}
     }
-  },
+  }
 });
 
 exports.tabSpec = tabSpec;