Bug 1355888 - Add env var MARIONETTE to start server; r?whimboo,maja_zf,ted draft
authorAndreas Tolfsen <ato@mozilla.com>
Sat, 15 Apr 2017 01:50:29 +0100
changeset 566999 371726ecd09013c2f0583d11fbfb235f671a9456
parent 566881 73752931e273091185e1e4b5231c28beed657cc8
child 567000 7eac3b4af6a194a93b0f5c05bfd1d557637c07d7
push id55406
push userbmo:ato@mozilla.com
push dateMon, 24 Apr 2017 09:34:42 +0000
reviewerswhimboo, maja_zf, ted
bugs1355888
milestone55.0a1
Bug 1355888 - Add env var MARIONETTE to start server; r?whimboo,maja_zf,ted This patch introduces a new environment variable, MARIONETTE, which if set will start the Marionette remote control server. This is meant to be analogous to passing the -marionette flag to the Firefox binary. When the server is started, we set the environment variable to preserve Marionette's enabled state across internal restarts. When Services.startup.quit(eRestart) is called, Firefox is restarted without the original command line flags it was started with, which means we loose track of whether Marionette was enabled. By setting MARIONETTE in-process, we preserve the knowledge of this state. This approach is in line with how state is preserved across in-app restarts in toolkit/xre/nsAppRunner.cpp:4761 (XRE_PROFILE_*), and for how MOZ_APP_RESTART and XUL_APP_FILE works. MozReview-Commit-ID: Dcb34m6FoZh
testing/marionette/components/marionette.js
testing/marionette/server.js
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -6,16 +6,19 @@
 
 const {Constructor: CC, interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(
+    this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
+
 const MARIONETTE_CONTRACT_ID = "@mozilla.org/marionette;1";
 const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
 
 const PREF_ENABLED = "marionette.enabled";
 const PREF_ENABLED_FALLBACK = "marionette.defaultPrefs.enabled";
 const PREF_PORT = "marionette.port";
 const PREF_PORT_FALLBACK = "marionette.defaultPrefs.port";
 const PREF_LOG_LEVEL = "marionette.log.level";
@@ -40,29 +43,34 @@ const LOG_LEVELS = new class extends Map
     let s = new String(level).toLowerCase();
     if (!this.has(s)) {
       return DEFAULT_LOG_LEVEL;
     }
     return super.get(s);
   }
 };
 
+// Complements -marionette flag for starting the Marionette server.
+// We also set this if Marionette is running in order to start the server
+// again after a Firefox restart.
+const ENV_ENABLED = "MARIONETTE";
+
 // Besides starting based on existing prefs in a profile and a command
 // line flag, we also support inheriting prefs out of an env var, and to
 // start Marionette that way.
 //
 // This allows marionette prefs to persist when we do a restart into
 // a different profile in order to test things like Firefox refresh.
 // The environment variable itself, if present, is interpreted as a
 // JSON structure, with the keys mapping to preference names in the
 // "marionette." branch, and the values to the values of those prefs. So
 // something like {"enabled": true} would result in the marionette.enabled
 // pref being set to true, thus triggering marionette being enabled for
 // that startup.
-const ENV_PREF_VAR = "MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS";
+const ENV_PRESERVE_PREFS = "MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS";
 
 const ServerSocket = CC("@mozilla.org/network/server-socket;1",
     "nsIServerSocket",
     "initSpecialConnection");
 
 // Get preference value of |preferred|, falling back to |fallback|
 // if |preferred| is not user-modified and |fallback| exists.
 function getPref (preferred, fallback) {
@@ -107,18 +115,17 @@ const prefs = {
           Preferences.set("marionette." + prefName, prefs[prefName]);
         }
       }
     }
   },
 };
 
 function MarionetteComponent() {
-  // guards against this component
-  // being initialised multiple times
+  this.enabled = env.exists(ENV_ENABLED);
   this.running = false;
 
   // holds a reference to server.TCPListener
   this.server = null;
 
   // holds reference to ChromeWindow
   // used to run GFX sanity tests on Windows
   this.gfxWindow = null;
@@ -188,17 +195,17 @@ MarionetteComponent.prototype.observe = 
       }
       break;
 
     case "profile-after-change":
       // Using sessionstore-windows-restored as the xpcom category doesn't
       // seem to work, so we wait for that by adding an observer here.
       Services.obs.addObserver(this, "sessionstore-windows-restored");
 
-      prefs.readFromEnvironment(ENV_PREF_VAR);
+      prefs.readFromEnvironment(ENV_PRESERVE_PREFS);
 
       if (this.enabled) {
         // We want to suppress the modal dialog that's shown
         // when starting up in safe-mode to enable testing.
         if (Services.appinfo.inSafeMode) {
           Services.obs.addObserver(this, "domwindowopened");
         }
       }
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -1,41 +1,51 @@
 /* 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 {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
-const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initSpecialConnection");
+const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+    .getService(Ci.mozIJSSubScriptLoader);
+const ServerSocket = CC(
+    "@mozilla.org/network/server-socket;1",
+    "nsIServerSocket",
+    "initSpecialConnection");
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.import("chrome://marionette/content/assert.js");
 Cu.import("chrome://marionette/content/driver.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/message.js");
 
+XPCOMUtils.defineLazyServiceGetter(
+    this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
+
 // Bug 1083711: Load transport.js as an SDK module instead of subscript
 loader.loadSubScript("resource://devtools/shared/transport/transport.js");
 
 const logger = Log.repository.getLogger("Marionette");
 
 const {KeepWhenOffline, LoopbackOnly} = Ci.nsIServerSocket;
 
 this.EXPORTED_SYMBOLS = ["server"];
 this.server = {};
 
 const PROTOCOL_VERSION = 3;
 
+const ENV_ENABLED = "MARIONETTE";
+
 const PREF_CONTENT_LISTENER = "marionette.contentListener";
 const PREF_RECOMMENDED = "marionette.prefs.recommended";
 
 // Marionette sets preferences recommended for automation when it starts,
 // unless |marionette.prefs.recommended| has been set to false.
 // Where noted, some prefs should also be set in the profile passed to
 // Marionette to prevent them from affecting startup, since some of these
 // are checked before Marionette initialises.
@@ -321,32 +331,33 @@ server.TCPListener = class {
 
     const flags = KeepWhenOffline | LoopbackOnly;
     const backlog = 1;
     this.listener = new ServerSocket(this.port, flags, backlog);
     this.listener.asyncListen(this);
 
     this.alive = true;
     this._acceptConnections = true;
+    env.set(ENV_ENABLED, "1");
   }
 
   stop () {
     if (!this.alive) {
       return;
     }
 
     for (let k of this.alteredPrefs) {
       logger.debug(`Resetting recommended pref ${k}`);
       Preferences.reset(k);
     }
     this.closeListener();
 
     this.alteredPrefs.clear();
+    this._acceptConnections = false;
     this.alive = false;
-    this._acceptConnections = false;
   }
 
   closeListener () {
     this.listener.close();
     this.listener = null;
   }
 
   onSocketAccepted (serverSocket, clientSocket) {