Bug 1506523 - Adapt Marionette so it can run on Thunderbird; r=whimboo
authorGeoff Lankow <geoff@darktrojan.net>
Fri, 23 Nov 2018 10:38:19 +1300
changeset 507005 f9d8c60591142bd751ff149f34b287d456df606c
parent 507004 1cdcda217f8b0e094c218e040fda6f334d6a1b65
child 507006 8810195a2a42d9622f020cf704b8025396d0f139
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswhimboo
bugs1506523
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1506523 - Adapt Marionette so it can run on Thunderbird; r=whimboo
testing/marionette/assert.js
testing/marionette/browser.js
testing/marionette/components/marionette.js
testing/marionette/driver.js
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -24,16 +24,18 @@ XPCOMUtils.defineLazyModuleGetters(this,
   browser: "chrome://marionette/content/browser.js",
 });
 
 this.EXPORTED_SYMBOLS = ["assert"];
 
 const isFennec = () => AppConstants.platform == "android";
 const isFirefox = () =>
     Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const isThunderbird = () =>
+    Services.appinfo.ID == "{3550f703-e582-4d05-9a08-453d09bdfdc6}";
 
 /**
  * Shorthands for common assertions made in Marionette.
  *
  * @namespace
  */
 this.assert = {};
 
@@ -87,16 +89,31 @@ assert.session = function(driver, msg = 
  *     If current browser is not Firefox.
  */
 assert.firefox = function(msg = "") {
   msg = msg || "Only supported in Firefox";
   assert.that(isFirefox, msg, UnsupportedOperationError)();
 };
 
 /**
+ * Asserts that the current browser is Firefox Desktop or Thunderbird.
+ *
+ * @param {string=} msg
+ *     Custom error message.
+ *
+ * @throws {UnsupportedOperationError}
+ *     If current browser is not Firefox or Thunderbird.
+ */
+assert.desktop = function(msg = "") {
+  msg = msg || "Only supported in desktop applications";
+  assert.that(obj => isFirefox(obj) || isThunderbird(obj),
+      msg, UnsupportedOperationError)();
+};
+
+/**
  * Asserts that the current browser is Fennec, or Firefox for Android.
  *
  * @param {string=} msg
  *     Custom error message.
  *
  * @throws {UnsupportedOperationError}
  *     If current browser is not Fennec.
  */
--- a/testing/marionette/browser.js
+++ b/testing/marionette/browser.js
@@ -93,16 +93,20 @@ browser.getBrowserForTab = function(tab)
 browser.getTabBrowser = function(window) {
   // Fennec
   if ("BrowserApp" in window) {
     return window.BrowserApp;
 
   // Firefox
   } else if ("gBrowser" in window) {
     return window.gBrowser;
+
+  // Thunderbird
+  } else if (window.document.getElementById("tabmail")) {
+    return window.document.getElementById("tabmail");
   }
 
   return null;
 };
 
 /**
  * Creates a browsing context wrapper.
  *
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -319,16 +319,17 @@ class MarionetteParentProcess {
         } else {
           this.uninit();
         }
         break;
 
       case "profile-after-change":
         Services.obs.addObserver(this, "command-line-startup");
         Services.obs.addObserver(this, "sessionstore-windows-restored");
+        Services.obs.addObserver(this, "mail-startup-done");
         Services.obs.addObserver(this, "toplevel-window-ready");
 
         for (let [pref, value] of EnvironmentPrefs.from(ENV_PRESERVE_PREFS)) {
           Preferences.set(pref, value);
         }
         break;
 
       // In safe mode the command line handlers are getting parsed after the
@@ -373,16 +374,22 @@ class MarionetteParentProcess {
             let parserError = ev.target.querySelector("parsererror");
             log.fatal(parserError.textContent);
             this.uninit();
             Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
           }
         }, {once: true});
         break;
 
+      // Thunderbird only, instead of sessionstore-windows-restored.
+      case "mail-startup-done":
+        this.finalUIStartup = true;
+        this.init();
+        break;
+
       case "sessionstore-windows-restored":
         Services.obs.removeObserver(this, topic);
         Services.obs.removeObserver(this, "toplevel-window-ready");
 
         // When Firefox starts on Windows, an additional GFX sanity test
         // window may appear off-screen.  Marionette should wait for it
         // to close.
         for (let win of Services.wm.getEnumerator(null)) {
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -65,16 +65,17 @@ const {
 } = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
 XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 this.EXPORTED_SYMBOLS = ["GeckoDriver"];
 
 const APP_ID_FIREFOX = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const APP_ID_THUNDERBIRD = "{3550f703-e582-4d05-9a08-453d09bdfdc6}";
 
 const FRAME_SCRIPT = "chrome://marionette/content/listener.js";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.ClassName,
   element.Strategy.Selector,
   element.Strategy.ID,
@@ -713,17 +714,26 @@ GeckoDriver.prototype.newSession = async
   if (this.a11yChecks && accessibility.service) {
     logger.info("Preemptively starting accessibility service in Chrome");
   }
 
   let registerBrowsers = this.registerPromise();
   let browserListening = this.listeningPromise();
 
   let waitForWindow = function() {
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    let windowType;
+    switch (this.appId) {
+      case APP_ID_THUNDERBIRD:
+        windowType = "mail:3pane";
+        break;
+      default:
+        windowType = "navigator:browser";
+        break;
+    }
+    let win = Services.wm.getMostRecentWindow(windowType);
     if (!win) {
       // if the window isn't even created, just poll wait for it
       let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       checkTimer.initWithCallback(waitForWindow.bind(this), 100,
           Ci.nsITimer.TYPE_ONE_SHOT);
     } else if (win.document.readyState != "complete") {
       // otherwise, wait for it to be fully loaded before proceeding
       let listener = ev => {
@@ -3298,17 +3308,17 @@ GeckoDriver.prototype.quit = async funct
   });
 
   Services.startup.quit(mode);
 
   return {cause: await quitApplication};
 };
 
 GeckoDriver.prototype.installAddon = function(cmd) {
-  assert.firefox();
+  assert.desktop();
 
   let path = cmd.parameters.path;
   let temp = cmd.parameters.temporary || false;
   if (typeof path == "undefined" || typeof path != "string" ||
       typeof temp != "boolean") {
     throw new InvalidArgumentError();
   }