Bug 1508852 - Update Thunderbird devtools root actor and loader. r=darktrojan DONTBUILD
authorPhilipp Kewisch <mozilla@kewis.ch>
Wed, 21 Nov 2018 00:09:39 +0100
changeset 33770 d61233a819393705eb09d5ba4e14191be39ea1f1
parent 33769 7f0dd2a068dc9f63603cbe940cbb6f33a3f74159
child 33771 f91bceb3c9f70c4a8a44c9dfc5ce54d87ba2bc04
push id388
push userclokep@gmail.com
push dateMon, 28 Jan 2019 20:54:56 +0000
reviewersdarktrojan
bugs1508852
Bug 1508852 - Update Thunderbird devtools root actor and loader. r=darktrojan DONTBUILD
mail/base/content/tabmail.xml
mail/components/devtools/devtools-loader.js
mail/components/devtools/tb-root-actor.js
--- a/mail/base/content/tabmail.xml
+++ b/mail/base/content/tabmail.xml
@@ -1822,16 +1822,24 @@
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
       <field name="mOverCloseButton">false</field>
       <field name="mCorrespondingMenuitem">null</field>
+
+      <property name="linkedBrowser">
+        <getter><![CDATA[
+          let tabmail = document.getElementById("tabmail");
+          let tab = tabmail._getTabContextForTabbyThing(this, false)[1];
+          return tabmail.getBrowserForTab(tab);
+        ]]></getter>
+      </property>
     </implementation>
 
     <handlers>
       <handler event="mouseover">
         var anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "close-button")
           this.mOverCloseButton = true;
       </handler>
--- a/mail/components/devtools/devtools-loader.js
+++ b/mail/components/devtools/devtools-loader.js
@@ -1,82 +1,76 @@
 /* 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";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
 
 function DevToolsStartup() {}
 
 DevToolsStartup.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsICommandLineHandler]),
   classID: Components.ID("{089694e9-106a-4704-abf7-62a88545e194}"),
 
   helpInfo: "",
   handle(cmdLine) {
     this.initialize();
 
     // We want to overwrite the -devtools flag and open the toolbox instead
     let devtoolsFlag = cmdLine.handleFlag("devtools", false);
     if (devtoolsFlag) {
-        this.handleDevToolsFlag(cmdLine);
+      this.handleDevToolsFlag(cmdLine);
     }
   },
 
   handleDevToolsFlag(cmdLine) {
     ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm");
     BrowserToolboxProcess.init();
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
       cmdLine.preventDefault = true;
     }
   },
 
   initialize() {
-    var { devtools, require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", null);
-    var { DebuggerServer } = require("devtools/server/main");
-    var { gDevTools } = require("devtools/client/framework/devtools");
+    let { devtools, require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", null);
+    let { DebuggerServer } = require("devtools/server/main");
+    let { gDevTools } = require("devtools/client/framework/devtools");
 
-    if (DebuggerServer.chromeWindowType != "mail:3pane") {
-      // Set up the server chrome window type, make sure it can't be set
-      Object.defineProperty(DebuggerServer, "chromeWindowType", {
-        get: () => "mail:3pane",
-        set: () => {},
-        configurable: true,
-      });
-    }
+    // Set up the client and server chrome window type, make sure it can't be set
+    Object.defineProperty(DebuggerServer, "chromeWindowType", {
+      get: () => "mail:3pane",
+      set: () => {},
+      configurable: true,
+    });
+    Object.defineProperty(gDevTools, "chromeWindowType", {
+      get: () => "mail:3pane",
+      set: () => {},
+      configurable: true,
+    });
 
-    if (gDevTools.chromeWindowType != "mail:3pane") {
-      // Set up the client chrome window type, make sure it can't be set
-      Object.defineProperty(gDevTools, "chromeWindowType", {
-        get: () => "mail:3pane",
-        set: () => {},
-        configurable: true,
-      });
-    }
+    // Make sure our root actor is always registered, no matter how devtools are called.
+    let devtoolsRegisterActors = DebuggerServer.registerActors.bind(DebuggerServer);
+    DebuggerServer.registerActors = function(options) {
+      devtoolsRegisterActors(options);
+      if (options.root) {
+        const { createRootActor } = require("resource:///modules/tb-root-actor.js");
+        DebuggerServer.setRootActor(createRootActor);
+      }
+    };
 
     // Make the loader visible to the debugger by default and for the already
     // loaded instance. Thunderbird now also provides the Browser Toolbox for
     // chrome debugging, which uses its own separate loader instance.
     DevToolsLoader.prototype.invisibleToDebugger = false;
     devtools.invisibleToDebugger = false;
-
-    if (!DebuggerServer.initialized) {
-      // Initialize and load the toolkit/browser actors
-      DebuggerServer.init();
-      DebuggerServer.registerActors({ browser: true, root: true, tab: true, windowType: "mail:3pane" });
-    }
+    DebuggerServer.allowChromeProcess = true;
 
-    if (!DebuggerServer.createRootActor.isMailRootActor) {
-      // Register the Thunderbird root actor
-      DebuggerServer.registerModule("resource:///modules/tb-root-actor.js", {
-        prefix: "tb-root-actor",
-        constructor: "RootActor",
-        type: { global: true },
-      });
-    }
+    // Initialize and load the toolkit/browser actors. This will also call above function to set the
+    // Thunderbird root actor
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DevToolsStartup]);
--- a/mail/components/devtools/tb-root-actor.js
+++ b/mail/components/devtools/tb-root-actor.js
@@ -1,270 +1,66 @@
 /* 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/. */
 
-/* globals require, exports */
+/* globals loader, require, exports */
 
 /**
  * Actors for Thunderbird Developer Tools, for example the root actor or tab
  * list actor.
  */
 
-var Services = require("Services");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var promise = require("promise");
-var { RootActor } = require("devtools/server/actors/root");
-var { DebuggerServer } = require("devtools/server/main");
-var { BrowserTabList, BrowserTabActor, BrowserAddonList } = require("devtools/server/actors/webbrowser");
+var { ActorRegistry } = require("devtools/server/actors/utils/actor-registry");
+
+loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
+loader.lazyRequireGetter(this, "BrowserTabList", "devtools/server/actors/webbrowser", true);
+loader.lazyRequireGetter(this, "BrowserAddonList", "devtools/server/actors/webbrowser", true);
+loader.lazyRequireGetter(this, "sendShutdownEvent", "devtools/server/actors/webbrowser", true);
+loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true);
+loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker/worker-list", true);
+loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
 
 /**
  * Create the root actor for Thunderbird.
  *
  * @param aConnection       The debugger connection to create the actor for.
  * @return                  The mail actor for the connection.
  */
-function createRootActor(aConnection) {
+exports.createRootActor = function(aConnection) {
   let parameters = {
     tabList: new TBTabList(aConnection),
     addonList: new BrowserAddonList(aConnection),
-    globalActorFactories: DebuggerServer.globalActorFactories,
+    workerList: new WorkerTargetActorList(aConnection, {}),
+    serviceWorkerRegistrationList:
+      new ServiceWorkerRegistrationActorList(aConnection),
+    processList: new ProcessActorList(),
+    globalActorFactories: ActorRegistry.globalActorFactories,
     onShutdown: sendShutdownEvent,
   };
 
   // Create the root actor and set the application type
   let rootActor = new RootActor(aConnection, parameters);
-  if (DebuggerServer.chromeWindowType) {
-    rootActor.applicationType = DebuggerServer.chromeWindowType.split(":")[0];
-  }
+  rootActor.applicationType = "mail";
 
   return rootActor;
-}
-createRootActor.isMailRootActor = true;
-
-function getChromeWindowTypes() {
-  /** @return the list of main windows, see isMainWindow */
-  function getMainWindows() {
-    let found = [];
-    let windows = Services.wm.getEnumerator(null);
-    while (windows.hasMoreElements()) {
-      let win = windows.getNext();
-      if (isMainWindow(win)) {
-        found.push(win);
-      }
-    }
-    return found;
-  }
-
-  /**
-   * Check if the window is the "main window" by checking if the host part
-   * matches the basename of the filename.
-   *
-   * @param aWindow       The window to check
-   */
-  function isMainWindow(aWindow) {
-    let urlParser = Cc["@mozilla.org/network/url-parser;1?auth=no"]
-                      .getService(Ci.nsIURLParser);
-    let baseName, bnpos = {}, bnlen = {};
-    let path = aWindow.location.pathname;
-    urlParser.parseFilePath(path, path.length, {}, {}, bnpos, bnlen, {}, {});
-    baseName = path.substr(bnpos.value, bnlen.value);
-    return (aWindow.location.hostname == baseName);
-  }
-
-  return getMainWindows().map((win) => {
-    return win.document.documentElement.getAttribute("windowtype");
-  });
-}
-
-/**
- * Returns the window type of the passed window.
- */
-function appShellDOMWindowType(aWindow) {
-  // This is what nsIWindowMediator's enumerator checks.
-  return aWindow.document.documentElement.getAttribute("windowtype");
-}
-
-/**
- * Send a debugger shutdown event to all main windows.
- */
-function sendShutdownEvent() {
-  let windowTypes = getChromeWindowTypes();
-  for (let type of windowTypes) {
-    let enumerator = Services.wm.getEnumerator(type);
-    while (enumerator.hasMoreElements()) {
-      let win = enumerator.getNext();
-      let evt = win.document.createEvent("Event");
-      evt.initEvent("Debugger:Shutdown", true, false);
-      win.document.documentElement.dispatchEvent(evt);
-    }
-  }
-}
+};
 
 /**
- * The live list of tabs for Thunderbird. The term tab is taken from
- * Firefox tabs, where each browser tab shows up as a tab in the debugger.
- * This is not the case for Thunderbird, so we will be iterating the content
- * windows and presenting them as tabs instead.
- *
- * @param aConnection       The connection to create the tab list for.
+ * Thunderbird's version of the tab list. We don't have gBrowser, but tabmail has similar functions
+ * that will be helpful. The tabs displayed are those tabs in tabmail that have a browser element.
+ * This is mainly the contentTabs, but can also be others such as the start page.
  */
-function TBTabList(aConnection) {
-  this._connection = aConnection;
-  this._actorByBrowser = new Map();
-
-  // These windows should be checked for browser elements
-
-  this._checkedWindows = new Set(getChromeWindowTypes());
-}
-
-TBTabList.prototype = {
-  _onListChanged: null,
-  _actorByBrowser: null,
-  _checkedWindows: null,
-  _mustNotify: false,
-  _listeningToMediator: false,
-
-  get onListChanged() {
-    return this._onListChanged;
-  },
-
-  set onListChanged(v) {
-    if (v !== null && typeof v !== "function") {
-      throw Error("onListChanged property may only be set to 'null' or a function");
-    }
-    this._onListChanged = v;
-    this._checkListening();
-  },
-
-  _checkListening() {
-    let shouldListenToMediator =
-        ((this._onListChanged && this._mustNotify) ||
-         this._actorByBrowser.size > 0);
-
-    if (this._listeningToMediator !== shouldListenToMediator) {
-      let op = shouldListenToMediator ? "addListener" : "removeListener";
-      Services.wm[op](this);
-      this._listeningToMediator = shouldListenToMediator;
-    }
-  },
+class TBTabList extends BrowserTabList {
+  _getSelectedBrowser(window) {
+    let tabmail = window.document.getElementById("tabmail");
+    return tabmail ? tabmail.selectedBrowser : null;
+  }
 
-  _notifyListChanged() {
-    if (this._onListChanged && this._mustNotify) {
-      this._onListChanged();
-      this._mustNotify = false;
-    }
-  },
-
-  _getTopWindow() {
-    let winIter = Services.wm.getEnumerator(null, true);
-    while (winIter.hasMoreElements()) {
-      let win = winIter.getNext();
-      if (this._checkedWindows.has(appShellDOMWindowType(win))) {
-        // This is one of our windows, return it
-        return win;
-      }
-    }
-    return null;
-  },
-
-  getList() {
-    let topWindow = this._getTopWindow();
-
-    // Look for all browser elements in all the windows we care about
-    for (let winName of this._checkedWindows) {
-      let winIter = Services.wm.getEnumerator(winName);
-      while (winIter.hasMoreElements()) {
-        let win = winIter.getNext();
-        let foundSelected = false;
-        // Check for browser elements and create a tab actor for each.
-        // This will catch content tabs, the message reader and the
-        // multi-message reader.
-        for (let browser of win.document.getElementsByTagName("browser")) {
-          if (browser.currentURI.spec == "about:blank") {
-            // about:blank is not particularly interesting. Don't
-            // add it to the list.
-            continue;
-          }
-          let actor = this._actorByBrowser.get(browser);
-          if (!actor) {
-            actor = new BrowserTabActor(this._connection,
-                                        browser, null);
-            this._actorByBrowser.set(browser, actor);
-          }
-
-          // Select the first visible browser in the top xul
-          // window.
-          let bo = browser.boxObject;
-          actor.selected = foundSelected =
-              win == topWindow &&
-              !foundSelected &&
-              bo.height > 0 &&
-              bo.width > 0;
-        }
-      }
+  _getChildren(window) {
+    let tabmail = window.document.getElementById("tabmail");
+    if (!tabmail) {
+      return [];
     }
 
-    this._mustNotify = true;
-    this._checkListening();
-
-    return promise.resolve([...this._actorByBrowser.values()]);
-  },
-
-  onOpenWindow: DevToolsUtils.makeInfallible(function(aWindow) {
-    let handleLoad = DevToolsUtils.makeInfallible(() => {
-      if (this._checkedWindows.has(appShellDOMWindowType(aWindow))) {
-        // This is one of our windows, we need to check for browser
-        // elements. Notify is enough, iterate will do the actual actor
-        // creation.
-        this._notifyListChanged();
-      }
-    });
-
-    // You can hardly do anything at all with a XUL window at this point; it
-    // doesn't even have its document yet. Wait until its document has
-    // loaded, and then see what we've got. This also avoids
-    // nsIWindowMediator enumeration from within listeners (bug 873589).
-    aWindow = aWindow.docShell.domWindow;
-    aWindow.addEventListener("load", handleLoad, {capture: false, once: true});
-  }, "TBTabList.prototype.onOpenWindow"),
-
-  onCloseWindow: DevToolsUtils.makeInfallible(function(aWindow) {
-    aWindow = aWindow.docShell.domWindow;
-
-    // Only handle our window types
-    if (this._checkedWindows.has(appShellDOMWindowType(aWindow))) {
-      return;
-    }
-
-    // nsIWindowMediator deadlocks if you call its GetEnumerator method from
-    // a nsIWindowMediatorListener's onCloseWindow hook (bug 873589), so
-    // handle the close in a different tick.
-    Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(() => {
-      let shouldNotify = false;
-
-      // Scan the whole map for browsers that were in this window.
-      for (let [browser, actor] of this._actorByBrowser) {
-        // The browser document of a closed window has no default view.
-        if (!browser.ownerGlobal) {
-          this._actorByBrowser.delete(browser);
-          actor.exit();
-          shouldNotify = true;
-        }
-      }
-
-      if (shouldNotify) {
-        this._notifyListChanged();
-      }
-      this._checkListening();
-    }, "TBTabList.prototype.onCloseWindow's delayed body"), 0);
-  }, "TBTabList.prototype.onCloseWindow"),
-
-  onWindowTitleChange() {},
-};
-
-exports.register = function(handle) {
-  handle.setRootActor(createRootActor);
-};
-
-exports.unregister = function(handle) {
-  handle.setRootActor(null);
-};
+    return tabmail.tabInfo.map(tab => tabmail.getBrowserForTab(tab)).filter(Boolean);
+  }
+}