merge autoland to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 18 Oct 2017 11:42:41 +0200
changeset 386868 8b57edba9837c1f18d48470b8e2a940f5d0111ce
parent 386775 4efa95ae9141a1973b2cdc026f315691f4e3d7ea (current diff)
parent 386867 08f1d46bc342090a4c00d4cec5df83a17515bf7f (diff)
child 386869 a29052590fc6538b89641e690d11731ca8e78120
push id96311
push userarchaeopteryx@coole-files.de
push dateWed, 18 Oct 2017 09:52:02 +0000
treeherdermozilla-inbound@a8a1e8cc1980 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.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
merge autoland to mozilla-central. r=merge a=merge MozReview-Commit-ID: JvxL3r663v
dom/plugins/ipc/PluginInstanceChild.cpp
dom/webauthn/WebAuthnRequest.h
netwerk/socket/nsNamedPipeIOLayer.cpp
taskcluster/docker/desktop-build/bin/build.sh
taskcluster/docker/desktop-build/bin/checkout-sources.sh
taskcluster/docker/desktop1604-test/tc-vcs-config.yml
testing/mozharness/mozharness/base/vcs/tcvcs.py
testing/web-platform/meta/cssom-view/scrollIntoView-shadow.html.ini
testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini
testing/web-platform/meta/intersection-observer/shadow-content.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/attributes/test-006.html.ini
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -1,139 +1,188 @@
 # This file is rendered via JSON-e by
 # - mozilla-taskcluster - https://docs.taskcluster.net/reference/integrations/mozilla-taskcluster/docs/taskcluster-yml
 # - cron tasks - taskcluster/taskgraph/cron/decision.py
+# - action tasks - taskcluster/taskgraph/actions/registry.py
 version: 1
 tasks:
-  $let:
-    # sometimes the push user is just `ffxbld` or the like, but we want an email-like field..
-    ownerEmail: {$if: '"@" in push.owner', then: '${push.owner}', else: '${push.owner}@noreply.mozilla.org'}
-    # ensure there's no trailing `/` on the repo URL
-    repoUrl: {$if: 'repository.url[-1] == "/"', then: {$eval: 'repository.url[:-1]'}, else: {$eval: 'repository.url'}}
-  in:
-  - taskId: '${as_slugid("decision")}'
-    taskGroupId: '${as_slugid("decision")}' # same as tsakId; this is how automation identifies a decision tsak
-    schedulerId: 'gecko-level-${repository.level}'
-
-    created: {$fromNow: ''}
-    deadline: {$fromNow: '1 day'}
-    expires: {$fromNow: '1 year 1 second'} # 1 second so artifacts expire first, despite rounding errors
-    metadata:
-      $merge:
-        - owner: "${ownerEmail}"
-          source: "${repoUrl}/raw-file/${push.revision}/.taskcluster.yml"
-        - $if: 'tasks_for == "hg-push"'
-          then:
-            name: "Gecko Decision Task"
-            description: 'The task that creates all of the other tasks in the task graph'
-          else:
-            name: "Decision Task for cron job ${cron.job_name}"
-            description: 'Created by a [cron task](https://tools.taskcluster.net/tasks/${cron.task_id})'
-
-    provisionerId: "aws-provisioner-v1"
-    workerType: "gecko-${repository.level}-decision"
-
-    tags:
-      $if: 'tasks_for == "hg-push"'
-      then: {createdForUser: "${ownerEmail}"}
-
-    routes:
-      $if: 'tasks_for == "hg-push"'
-      then:
-        - "index.gecko.v2.${repository.project}.latest.firefox.decision"
-        - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
-        - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-        - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-        - "notify.email.${ownerEmail}.on-failed"
-        - "notify.email.${ownerEmail}.on-exception"
-      else:
-        - "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
-        - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-        - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-
-    scopes:
-      $if: 'tasks_for == "hg-push"'
-      then:
-        - 'assume:repo:${repoUrl[8:]}:*'
-        - 'queue:route:notify.email.${ownerEmail}.*'
-      else:
-        - 'assume:repo:${repoUrl[8:]}:cron:${cron.job_name}'
-
-    dependencies: []
-    requires: all-completed
-
-    priority: lowest
-    retries: 5
+  - $let:
+      # sometimes the push user is just `ffxbld` or the like, but we want an email-like field..
+      ownerEmail: {$if: '"@" in push.owner', then: '${push.owner}', else: '${push.owner}@noreply.mozilla.org'}
+      # ensure there's no trailing `/` on the repo URL
+      repoUrl: {$if: 'repository.url[-1] == "/"', then: {$eval: 'repository.url[:-1]'}, else: {$eval: 'repository.url'}}
+    in:
+      taskId: {$if: 'tasks_for != "action"', then: '${as_slugid("decision")}'}
+      taskGroupId:
+        $if: 'tasks_for == "action"'
+        then:
+          '${action.taskGroupId}'
+        else:
+          '${as_slugid("decision")}' # same as taskId; this is how automation identifies a decision tsak
+      schedulerId: 'gecko-level-${repository.level}'
 
-    payload:
-      env:
-        # checkout-gecko uses these to check out the source; the inputs
-        # to `mach taskgraph decision` are all on the command line.
-        GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
-        GECKO_HEAD_REPOSITORY: '${repoUrl}'
-        GECKO_HEAD_REF: '${push.revision}'
-        GECKO_HEAD_REV: '${push.revision}'
-        GECKO_COMMIT_MSG: '${push.comment}'
-        HG_STORE_PATH: /builds/worker/checkouts/hg-store
-        TASKCLUSTER_CACHES: /builds/worker/checkouts
-
-      cache:
-        level-${repository.level}-checkouts-sparse-v1: /builds/worker/checkouts
-
-      features:
-        taskclusterProxy: true
-        chainOfTrust: true
-
-      # Note: This task is built server side without the context or tooling that
-      # exist in tree so we must hard code the hash
-      # XXX Changing this will break Chain of Trust without an associated puppet and
-      # scriptworker patch!
-      image: 'taskcluster/decision:2.0.0@sha256:4039fd878e5700b326d4a636e28c595c053fbcb53909c1db84ad1f513cf644ef'
-
-      maxRunTime: 1800
-
-      # TODO use mozilla-unified for the base repository once the tc-vcs
-      # tar.gz archives are created or tc-vcs isn't being used.
-      command:
-        - /builds/worker/bin/run-task
-        - '--vcs-checkout=/builds/worker/checkouts/gecko'
-        - '--sparse-profile=build/sparse-profiles/taskgraph'
-        - '--'
-        - bash
-        - -cx
-        - $let:
-            extraArgs: {$if: 'tasks_for == "hg-push"', then: '', else: '${cron.quoted_args}'}
-          # NOTE: the explicit reference to mozilla-central below is required because android-stuff
-          # still uses tc-vcs, which does not support mozilla-unified
-          # https://bugzilla.mozilla.org/show_bug.cgi?id=1383973
-          in: >
-            cd /builds/worker/checkouts/gecko &&
-            ln -s /builds/worker/artifacts artifacts &&
-            ./mach --log-no-times taskgraph decision
-            --pushlog-id='${push.pushlog_id}'
-            --pushdate='${push.pushdate}'
-            --project='${repository.project}'
-            --message="$GECKO_COMMIT_MSG"
-            --owner='${ownerEmail}'
-            --level='${repository.level}'
-            --base-repository='https://hg.mozilla.org/mozilla-central'
-            --head-repository="$GECKO_HEAD_REPOSITORY"
-            --head-ref="$GECKO_HEAD_REF"
-            --head-rev="$GECKO_HEAD_REV"
-            ${extraArgs}
-
-      artifacts:
-        'public':
-          type: 'directory'
-          path: '/builds/worker/artifacts'
-          expires: {$fromNow: '1 year'}
-
-    extra:
-      treeherder:
+      created: {$fromNow: ''}
+      deadline: {$fromNow: '1 day'}
+      expires: {$fromNow: '1 year 1 second'} # 1 second so artifacts expire first, despite rounding errors
+      metadata:
         $merge:
-          - machine:
-              platform: gecko-decision
+          - owner: "${ownerEmail}"
+            source: "${repoUrl}/raw-file/${push.revision}/.taskcluster.yml"
           - $if: 'tasks_for == "hg-push"'
             then:
-              symbol: D
+              name: "Gecko Decision Task"
+              description: 'The task that creates all of the other tasks in the task graph'
             else:
-              groupSymbol: cron
-              symbol: "${cron.job_symbol}"
+              $if: 'tasks_for == "action"'
+              then:
+                name: "Action: ${action.title}"
+                description: '${action.description}'
+              else:
+                name: "Decision Task for cron job ${cron.job_name}"
+                description: 'Created by a [cron task](https://tools.taskcluster.net/tasks/${cron.task_id})'
+
+      provisionerId: "aws-provisioner-v1"
+      workerType: "gecko-${repository.level}-decision"
+
+      tags:
+        $if: 'tasks_for == "hg-push"'
+        then: {createdForUser: "${ownerEmail}"}
+        else:
+          $if: 'tasks_for == "action"'
+          then:
+            createdForUser: '${ownerEmail}'
+            kind: 'action-callback'
+
+      routes:
+        $if: 'tasks_for == "hg-push"'
+        then:
+          - "index.gecko.v2.${repository.project}.latest.firefox.decision"
+          - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
+          - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+          - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+          - "notify.email.${ownerEmail}.on-failed"
+          - "notify.email.${ownerEmail}.on-exception"
+        else:
+          - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+          - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+          - $if: 'tasks_for == "action"'
+            then: "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.$ownTaskId"
+            else: "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
+
+      scopes:
+        $if: 'tasks_for == "hg-push"'
+        then:
+          - 'assume:repo:${repoUrl[8:]}:*'
+          - 'queue:route:notify.email.${ownerEmail}.*'
+        else:
+          $if: 'tasks_for == "action"'
+          then:
+            - '${action.repo_scope}'
+          else:
+            - 'assume:repo:${repoUrl[8:]}:cron:${cron.job_name}'
+
+      dependencies: []
+      requires: all-completed
+
+      priority: lowest
+      retries: 5
+
+      payload:
+        env:
+          # checkout-gecko uses these to check out the source; the inputs
+          # to `mach taskgraph decision` are all on the command line.
+          $merge:
+            - GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
+              GECKO_HEAD_REPOSITORY: '${repoUrl}'
+              GECKO_HEAD_REF: '${push.revision}'
+              GECKO_HEAD_REV: '${push.revision}'
+              GECKO_COMMIT_MSG: {$if: 'tasks_for != "action"', then: '${push.comment}'}
+              HG_STORE_PATH: /builds/worker/checkouts/hg-store
+              TASKCLUSTER_CACHES: /builds/worker/checkouts
+            - $if: 'tasks_for == "action"'
+              then:
+                ACTION_TASK_GROUP_ID: '${action.taskGroupId}'
+                ACTION_TASK_ID: {$json: {$eval: 'taskId'}}
+                ACTION_TASK: {$json: {$eval: 'task'}}
+                ACTION_INPUT: {$json: {$eval: 'input'}}
+                ACTION_CALLBACK: '${action.cb_name}'
+                ACTION_PARAMETERS: {$json: {$eval: 'parameters'}}
+
+        cache:
+          level-${repository.level}-checkouts-sparse-v1: /builds/worker/checkouts
+
+        features:
+          taskclusterProxy: true
+          chainOfTrust: true
+
+        # Note: This task is built server side without the context or tooling that
+        # exist in tree so we must hard code the hash
+        # XXX Changing this will break Chain of Trust without an associated puppet and
+        # scriptworker patch!
+        image: 'taskcluster/decision:2.0.0@sha256:4039fd878e5700b326d4a636e28c595c053fbcb53909c1db84ad1f513cf644ef'
+
+        maxRunTime: 1800
+
+        command:
+          - /builds/worker/bin/run-task
+          - '--vcs-checkout=/builds/worker/checkouts/gecko'
+          - '--sparse-profile=build/sparse-profiles/taskgraph'
+          - '--'
+          - bash
+          - -cx
+          - $let:
+              extraArgs: {$if: 'tasks_for == "cron"', then: '${cron.quoted_args}', else: ''}
+            in:
+              $if: 'tasks_for == "action"'
+              then: >
+                cd /builds/worker/checkouts/gecko &&
+                ln -s /builds/worker/artifacts artifacts &&
+                ./mach --log-no-times taskgraph action-callback
+              else: >
+                cd /builds/worker/checkouts/gecko &&
+                ln -s /builds/worker/artifacts artifacts &&
+                ./mach --log-no-times taskgraph decision
+                --pushlog-id='${push.pushlog_id}'
+                --pushdate='${push.pushdate}'
+                --project='${repository.project}'
+                --message="$GECKO_COMMIT_MSG"
+                --owner='${ownerEmail}'
+                --level='${repository.level}'
+                --base-repository="$GECKO_BASE_REPOSITORY"
+                --head-repository="$GECKO_HEAD_REPOSITORY"
+                --head-ref="$GECKO_HEAD_REF"
+                --head-rev="$GECKO_HEAD_REV"
+                ${extraArgs}
+
+        artifacts:
+          'public':
+            type: 'directory'
+            path: '/builds/worker/artifacts'
+            expires: {$fromNow: '1 year'}
+
+      extra:
+        $merge:
+          - treeherder:
+              $merge:
+                - machine:
+                    platform: gecko-decision
+                - $if: 'tasks_for == "hg-push"'
+                  then:
+                    symbol: D
+                  else:
+                    $if: 'tasks_for == "action"'
+                    then:
+                      groupName: 'action-callback'
+                      groupSymbol: AC
+                      symbol: "${action.symbol}"
+                    else:
+                      groupSymbol: cron
+                      symbol: "${cron.job_symbol}"
+          - $if: 'tasks_for == "action"'
+            then:
+              parent: '${action.taskGroupId}'
+              action:
+                name: '${action.name}'
+                context:
+                  taskGroupId: '${action.taskGroupId}'
+                  taskId: {$eval: 'taskId'}
+                  input: {$eval: 'input'}
+                  parameters: {$eval: 'parameters'}
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -10,17 +10,16 @@ support-files =
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
 [test_bug1189277.html]
 [test_bug1276857.html]
-skip-if = stylo # Stylo doesn't support shadow DOM yet, bug 1293844
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_general.html]
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -305,17 +305,19 @@ pref("browser.urlbar.filter.javascript",
 // the maximum number of results to show in autocomplete when doing richResults
 pref("browser.urlbar.maxRichResults", 10);
 // The amount of time (ms) to wait after the user has stopped typing
 // before starting to perform autocomplete.  50 is the default set in
 // autocomplete.xml.
 pref("browser.urlbar.delay", 50);
 
 // The maximum number of historical search results to show.
-pref("browser.urlbar.maxHistoricalSearchSuggestions", 1);
+pref("browser.urlbar.maxHistoricalSearchSuggestions", 0);
+// The awesomebar result composition.
+pref("browser.urlbar.matchBuckets", "suggestion:4,general:5");
 
 // The default behavior for the urlbar can be configured to use any combination
 // of the match filters with each additional filter adding more results (union).
 pref("browser.urlbar.suggest.history",              true);
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
 pref("browser.urlbar.suggest.searches",             true);
 
@@ -415,16 +417,23 @@ pref("browser.search.widget.inNavBar", f
 pref("browser.search.reset.enabled", true);
 #endif
 
 pref("browser.sessionhistory.max_entries", 50);
 
 // Built-in default permissions.
 pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");
 
+// Set default fallback values for site permissions we want
+// the user to be able to globally change.
+pref("permissions.default.camera", 0);
+pref("permissions.default.microphone", 0);
+pref("permissions.default.geo", 0);
+pref("permissions.default.desktop-notification", 0);
+
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
 
 // handle external links (i.e. links opened from a different application)
 // default: use browser.link.open_newwindow
 // 1-3: see browser.link.open_newwindow for interpretation
 pref("browser.link.open_newwindow.override.external", -1);
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -29,22 +29,16 @@
                           accesskey="&newNavigatorCmd.accesskey;"
                           key="key_newNavigator"
                           command="cmd_newNavigator"/>
                 <menuitem id="menu_newPrivateWindow"
                           label="&newPrivateWindow.label;"
                           accesskey="&newPrivateWindow.accesskey;"
                           command="Tools:PrivateBrowsing"
                           key="key_privatebrowsing"/>
-#ifdef E10S_TESTING_ONLY
-                <menuitem id="menu_newNonRemoteWindow"
-                          label="&newNonRemoteWindow.label;"
-                          hidden="true"
-                          command="Tools:NonRemoteWindow"/>
-#endif
 #ifdef MAC_NON_BROWSER_WINDOW
                 <menuitem id="menu_openLocation"
                           label="&openLocationCmd.label;"
                           command="Browser:OpenLocation"
                           key="focusURLBar"/>
 #endif
                 <menuitem id="menu_openFile"
                           label="&openFileCmd.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -97,20 +97,16 @@
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});"/>
-#ifdef E10S_TESTING_ONLY
-    <command id="Tools:NonRemoteWindow"
-      oncommand="OpenBrowserWindow({remote: false});"/>
-#endif
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"/>
     <command id="Browser:ShowAllHistory"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1461,19 +1461,16 @@ var gBrowserInit = {
     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete");
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
 
     BrowserOffline.init();
     IndexedDBPromptHelper.init();
     CanvasPermissionPromptHelper.init();
 
-    if (AppConstants.E10S_TESTING_ONLY)
-      gRemoteTabsUI.init();
-
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
 
     UpdateUrlbarSearchSplitterState();
 
@@ -1977,19 +1974,16 @@ if (AppConstants.platform == "macosx") {
     this._delayedStartupTimeoutId = null;
 
     // initialise the offline listener
     BrowserOffline.init();
 
     // initialize the private browsing UI
     gPrivateBrowsingUI.init();
 
-    if (AppConstants.E10S_TESTING_ONLY) {
-      gRemoteTabsUI.init();
-    }
   };
 
   gBrowserInit.nonBrowserWindowShutdown = function() {
     let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
                       .getService(Ci.nsIMacDockSupport);
     dockSupport.dockMenu = null;
 
     // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
@@ -8278,37 +8272,16 @@ var gPrivateBrowsingUI = {
     }
     if (!urlBarSearchParam.includes("private-window")) {
       urlBarSearchParam += " private-window";
     }
     gURLBar.setAttribute("autocompletesearchparam", urlBarSearchParam);
   }
 };
 
-var gRemoteTabsUI = {
-  init() {
-    if (window.location.href != getBrowserURL() &&
-        // Also check hidden window for the Mac no-window case
-        window.location.href != "chrome://browser/content/hiddenWindow.xul") {
-      return;
-    }
-
-    if (AppConstants.platform == "macosx" &&
-        Services.prefs.getBoolPref("layers.acceleration.disabled")) {
-      // On OS X, "Disable Hardware Acceleration" also disables OMTC and forces
-      // a fallback to Basic Layers. This is incompatible with e10s.
-      return;
-    }
-
-    let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
-    let autostart = Services.appinfo.browserTabsRemoteAutostart;
-    newNonRemoteWindow.hidden = !autostart;
-  }
-};
-
 /**
  * Switch to a tab that has a given URI, and focuses its browser window.
  * If a matching tab is in this window, it will be switched to. Otherwise, other
  * windows will be searched.
  *
  * @param aURI
  *        URI to search for
  * @param aOpenNew
@@ -8556,26 +8529,16 @@ var TabContextMenu = {
 
     var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
     if (this.contextTab.hasAttribute("customizemode"))
       document.getElementById("context_openTabInWindow").disabled = true;
 
-    if (AppConstants.E10S_TESTING_ONLY) {
-      menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
-      for (let menuItem of menuItems) {
-        menuItem.hidden = !gMultiProcessBrowser;
-        if (menuItem.id == "context_openNonRemoteWindow") {
-          menuItem.disabled = !!parseInt(this.contextTab.getAttribute("usercontextid"));
-        }
-      }
-    }
-
     disabled = gBrowser.visibleTabs.length == 1;
     menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
     // Session store
     document.getElementById("context_undoCloseTab").disabled =
       SessionStore.getClosedTabCount(window) == 0;
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -71,24 +71,24 @@ function initRow(aPartId) {
     return;
   }
 
   createRow(aPartId);
 
   var checkbox = document.getElementById(aPartId + "Def");
   var command  = document.getElementById("cmd_" + aPartId + "Toggle");
   var {state} = SitePermissions.get(gPermURI, aPartId);
+  let defaultState = SitePermissions.getDefault(aPartId);
 
-  if (state != SitePermissions.UNKNOWN) {
+  if (state != defaultState) {
     checkbox.checked = false;
     command.removeAttribute("disabled");
   } else {
     checkbox.checked = true;
     command.setAttribute("disabled", "true");
-    state = SitePermissions.getDefault(aPartId);
   }
   setRadioState(aPartId, state);
 
   if (aPartId == "indexedDB") {
     initIndexedDBRow();
   }
 }
 
@@ -164,17 +164,17 @@ function onCheckboxClick(aPartId) {
 
 function onPluginRadioClick(aEvent) {
   onRadioClick(aEvent.originalTarget.getAttribute("id").split("#")[0]);
 }
 
 function onRadioClick(aPartId) {
   var radioGroup = document.getElementById(aPartId + "RadioGroup");
   var id = radioGroup.selectedItem.id;
-  var permission = id.split("#")[1];
+  var permission = parseInt(id.split("#")[1]);
   SitePermissions.set(gPermURI, aPartId, permission);
 }
 
 function setRadioState(aPartId, aValue) {
   var radio = document.getElementById(aPartId + "#" + aValue);
   if (radio) {
     radio.radioGroup.selectedItem = radio;
   }
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -541,16 +541,17 @@ support-files =
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_unloaddialogs.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_utilityOverlay.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_viewSourceInTabOnViewSource.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleFindSelection.js]
+skip-if = true # Bug 1409184 disabled because interactive find next is not automating properly
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs_bookmarkAllPages.js]
 skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs_bookmarkAllTabs.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -24,16 +24,17 @@ support-files =
   plugin_clickToPlayDeny.html
   plugin_favorfallback.html
   plugin_hidden_to_visible.html
   plugin_iframe.html
   plugin_outsideScrollArea.html
   plugin_overlayed.html
   plugin_positioned.html
   plugin_simple_blank.swf
+  plugin_shouldShowOverlay.html
   plugin_small.html
   plugin_small_2.html
   plugin_syncRemoved.html
   plugin_test.html
   plugin_test2.html
   plugin_test3.html
   plugin_two_types.html
   plugin_unknown.html
@@ -71,16 +72,17 @@ tags = blocklist
 [browser_CTP_notificationBar.js]
 tags = blocklist
 [browser_CTP_outsideScrollArea.js]
 tags = blocklist
 [browser_CTP_remove_navigate.js]
 tags = blocklist
 [browser_CTP_resize.js]
 tags = blocklist
+[browser_CTP_shouldShowOverlay.js]
 [browser_CTP_zoom.js]
 tags = blocklist
 [browser_blocking.js]
 tags = blocklist
 [browser_iterate_hidden_plugins.js]
 [browser_plugins_added_dynamically.js]
 tags = blocklist
 [browser_pluginnotification.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
@@ -0,0 +1,56 @@
+/* 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";
+
+/* This test ensures that the click-to-play "Activate Plugin" overlay
+ * is shown when expected.
+ * All testcases are in the plugin_shouldShowOverlay.html file.
+ */
+
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+var gTestBrowser = null;
+
+add_task(async function() {
+  registerCleanupFunction(function() {
+    clearAllPluginPermissions();
+    setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+    gBrowser.removeCurrentTab();
+    gTestBrowser = null;
+  });
+});
+
+add_task(async function() {
+  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+  gTestBrowser = gBrowser.selectedBrowser;
+
+  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
+
+  let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(!popupNotification, "Sanity check, should not have a click-to-play notification");
+
+  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_shouldShowOverlay.html");
+
+  // Work around for delayed PluginBindingAttached
+  await promiseUpdatePluginBindings(gTestBrowser);
+
+  await ContentTask.spawn(gTestBrowser, null, async function() {
+    let doc = content.document;
+    let testcases = doc.querySelectorAll(".testcase");
+
+    for (let testcase of testcases) {
+      let plugin = testcase.querySelector("object");
+      Assert.ok(plugin, `plugin exists in ${testcase.id}`);
+
+      let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+      Assert.ok(overlay, `overlay exists in ${testcase.id}`);
+
+      let expectedVisibility = (testcase.getAttribute("shouldshow") == "true");
+      Assert.ok(overlay.classList.contains("visible") == expectedVisibility,
+                `The expected visibility is correct in ${testcase.id}`);
+    }
+  })
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_shouldShowOverlay.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+object {
+  width: 200px;
+  height: 200px;
+}
+
+.testcase {
+  position: relative;
+  margin-bottom: 5px;
+}
+
+.cover {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background-color: green;
+}
+</style>
+</head>
+<body>
+
+  <div id="testcase1" class="testcase" shouldshow="true"
+       style="top: -100px">
+    <!-- Should show overlay even though the top part is outside
+       of the page. -->
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase2" class="testcase" shouldshow="true"
+       style="left: -100px">
+    <!-- Should show overlay even though the left part is outside
+       of the page. -->
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase3" class="testcase" shouldshow="false"
+       style="left: -210px">
+    <!-- The object is entirely outside of the page, so the overlay
+         should NOT show. -->
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase4" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though the top-left corner is covered. -->
+    <div class="cover" style="top: 0; left: 0"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase5" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though the top-right corner is covered. -->
+    <div class="cover" style="top: 0; left: 180px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase6" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though the bottom-left corner is covered. -->
+    <div class="cover" style="top: 180px; left: 0"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+
+  <div id="testcase7" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though the bottom-right corner is covered. -->
+    <div class="cover" style="top: 180px; left: 180px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase8" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though the center is covered. -->
+    <div class="cover" style="top: 90px; left: 90px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase9" class="testcase" shouldshow="true">
+    <!-- Should show overlay even though multiple points are covered,
+       but not all of them. -->
+    <div class="cover" style="top: 0; left: 0"></div>
+    <div class="cover" style="top: 0; left: 180px"></div>
+    <div class="cover" style="top: 180px; left: 0"></div>
+    <div class="cover" style="top: 180px; left: 180px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase10" class="testcase" shouldshow="true">
+    <!-- Another case where 4 are covered, but not all. -->
+    <div class="cover" style="top: 90px; left: 90px"></div>
+    <div class="cover" style="top: 0; left: 180px"></div>
+    <div class="cover" style="top: 180px; left: 0"></div>
+    <div class="cover" style="top: 180px; left: 180px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase11" class="testcase" shouldshow="false">
+    <!-- All corners and center are covered here, so in this
+         case the overlay should NOT show. -->
+    <div class="cover" style="top: 0; left: 0"></div>
+    <div class="cover" style="top: 0; left: 180px"></div>
+    <div class="cover" style="top: 180px; left: 0"></div>
+    <div class="cover" style="top: 180px; left: 180px"></div>
+    <div class="cover" style="top: 90px; left: 90px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+  <div id="testcase12" class="testcase" shouldshow="false">
+        <!-- All corners and center are covered here, by a single
+        element. In this case the overlay should NOT show. -->
+    <div class="cover" style="width: 200px; height:200px"></div>
+    <object type="application/x-test"></object>
+  </div>
+
+</body>
+</html>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -2881,16 +2881,22 @@ file, You can obtain one at http://mozil
           this._secondaryButton.label = gNavigatorBundle.getString(button2.label);
           this._secondaryButton.accessKey = gNavigatorBundle.getString(button2.accesskey);
           this._secondaryButton.setAttribute("action", button2.action);
           if (button1.default) {
             this._primaryButton.setAttribute("default", "true");
           } else if (button2.default) {
             this._secondaryButton.setAttribute("default", "true");
           }
+
+          if (this._primaryButton.hidden) {
+            this._secondaryButton.setAttribute("alone", "true");
+          } else if (this._secondaryButton.hidden) {
+            this._primaryButton.setAttribute("alone", "true");
+          }
         ]]></body>
       </method>
       <method name="_setupDescription">
         <parameter name="baseString" />
         <parameter name="pluginName" /> <!-- null for the multiple-plugin case -->
         <parameter name="prePath" />
         <body><![CDATA[
           var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
--- a/browser/branding/aurora/pref/firefox-branding.js
+++ b/browser/branding/aurora/pref/firefox-branding.js
@@ -1,15 +1,15 @@
 
 
 /* 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/. */
 
-pref("startup.homepage_override_url", "");
+pref("startup.homepage_override_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%a2/whatsnew/");
 pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%a2/firstrun/");
 pref("startup.homepage_welcome_url.additional", "");
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 28800); // 8 hours
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 // 0 means "download everything at once"
 pref("app.update.download.backgroundInterval", 0);
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -54,17 +54,17 @@ const kSubviewEvents = [
   "ViewShowing",
   "ViewHiding"
 ];
 
 /**
  * The current version. We can use this to auto-add new default widgets as necessary.
  * (would be const but isn't because of testing purposes)
  */
-var kVersion = 12;
+var kVersion = 13;
 
 /**
  * Buttons removed from built-ins by version they were removed. kVersion must be
  * bumped any time a new id is added to this. Use the button id as key, and
  * version the button is removed in as the value.  e.g. "pocket-button": 5
  */
 var ObsoleteBuiltinButtons = {
 };
@@ -340,17 +340,16 @@ var CustomizableUIInternal = {
         "save-page-button",
         "print-button",
         "history-panelmenu",
         "fullscreen-button",
         "find-button",
         "preferences-button",
         "add-ons-button",
         "sync-button",
-        "e10s-button",
       ];
 
       if (!AppConstants.MOZ_DEV_EDITION) {
         defaultPlacements.splice(-1, 0, "developer-button");
       }
 
       let showCharacterEncoding = Services.prefs.getComplexValue(
         "browser.menu.showCharacterEncoding",
@@ -447,16 +446,27 @@ var CustomizableUIInternal = {
         for (let button of removedButtons) {
           let buttonIndex = placements.indexOf(button);
           if (buttonIndex != -1) {
             placements.splice(buttonIndex, 1);
           }
         }
       }
     }
+
+    // Remove the old placements from the now-gone Nightly-only
+    // "New non-e10s window" button.
+    if (currentVersion < 13 && gSavedState.placements) {
+      for (let placements of Object.values(gSavedState.placements)) {
+        let buttonIndex = placements.indexOf("e10s-button");
+        if (buttonIndex != -1) {
+          placements.splice(buttonIndex, 1);
+        }
+      }
+    }
   },
 
   /**
    * _markObsoleteBuiltinButtonsSeen
    * when upgrading, ensure obsoleted buttons are in seen state.
    */
   _markObsoleteBuiltinButtonsSeen() {
     if (!gSavedState)
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -992,26 +992,8 @@ if (Services.prefs.getBoolPref("privacy.
       forgetButton.addEventListener("command", this);
     },
     onViewHiding(aEvent) {
       let forgetButton = aEvent.target.querySelector("#PanelUI-panic-view-button");
       forgetButton.removeEventListener("command", this);
     },
   });
 }
-
-if (AppConstants.E10S_TESTING_ONLY) {
-  if (Services.appinfo.browserTabsRemoteAutostart) {
-    CustomizableWidgets.push({
-      id: "e10s-button",
-      defaultArea: CustomizableUI.AREA_PANEL,
-      onBuild(aDocument) {
-        let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
-        node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
-        node.setAttribute("tooltiptext", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
-      },
-      onCommand(aEvent) {
-        let win = aEvent.view;
-        win.OpenBrowserWindow({remote: false});
-      },
-    });
-  }
-}
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -466,16 +466,20 @@ const PanelUI = {
         viewNode.removeEventListener("ViewShown", listener);
         panelRemover();
         return;
       }
 
       CustomizableUI.addPanelCloseListeners(tempPanel);
       tempPanel.addEventListener("popuphidden", panelRemover);
 
+      if (aAnchor.parentNode.id == "PersonalToolbar") {
+        tempPanel.classList.add("bookmarks-toolbar");
+      }
+
       let anchor = this._getPanelAnchor(aAnchor);
 
       if (aAnchor != anchor && aAnchor.id) {
         anchor.setAttribute("consumeanchor", aAnchor.id);
       }
 
       tempPanel.openPopup(anchor, {
         position: "bottomcenter topright",
@@ -842,16 +846,18 @@ const PanelUI = {
 
   _getPopupId(notification) { return "appMenu-" + notification.id + "-notification"; },
 
   _getBadgeStatus(notification) { return notification.id; },
 
   _getPanelAnchor(candidate) {
     let iconAnchor =
       document.getAnonymousElementByAttribute(candidate, "class",
+                                              "toolbarbutton-badge-stack") ||
+      document.getAnonymousElementByAttribute(candidate, "class",
                                               "toolbarbutton-icon");
     return iconAnchor || candidate;
   },
 
   _addedShortcuts: false,
   _ensureShortcutsShown() {
     if (this._addedShortcuts) {
       return;
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -12,21 +12,16 @@ Cu.import("resource://gre/modules/AppCon
 var {Promise, CustomizableUI, AppConstants} = tmp;
 
 var EventUtils = {};
 Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
 Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
 registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
 
-// Remove temporary e10s related new window options in customize ui,
-// they break a lot of tests.
-CustomizableUI.destroyWidget("e10s-button");
-CustomizableUI.removeWidgetFromArea("e10s-button");
-
 var {synthesizeDragStart, synthesizeDrop} = EventUtils;
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kTabEventFailureTimeoutInMs = 20000;
 
 const kForceOverflowWidthPx = 300;
 
 function createDummyXULButton(id, label, win = window) {
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -553,16 +553,19 @@ var DownloadsPanel = {
         return;
       }
 
       if (!anchor) {
         DownloadsCommon.error("Downloads button cannot be found.");
         return;
       }
 
+      let onBookmarksToolbar = !!anchor.closest("#PersonalToolbar");
+      this.panel.classList.toggle("bookmarks-toolbar", onBookmarksToolbar);
+
       // When the panel is opened, we check if the target files of visible items
       // still exist, and update the allowed items interactions accordingly.  We
       // do these checks on a background thread, and don't prevent the panel to
       // be displayed while these checks are being performed.
       for (let viewItem of DownloadsView._visibleViewItems.values()) {
         viewItem.download.refresh().catch(Cu.reportError);
       }
 
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -621,19 +621,22 @@ const DownloadsIndicatorView = {
     }
 
     return this._indicator = indicator;
   },
 
   get indicatorAnchor() {
     let widgetGroup = CustomizableUI.getWidget("downloads-button");
     if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
-      return widgetGroup.forWindow(window).anchor;
+      let overflowIcon = widgetGroup.forWindow(window).anchor;
+      return document.getAnonymousElementByAttribute(overflowIcon, "class", "toolbarbutton-icon");
     }
-    return document.getElementById("downloads-indicator-anchor");
+
+    return document.getAnonymousElementByAttribute(this.indicator, "class",
+                                                   "toolbarbutton-badge-stack");
   },
 
   get _progressIcon() {
     return this.__progressIcon ||
       (this.__progressIcon = document.getElementById("downloads-indicator-progress-inner"));
   },
 
   get notifier() {
--- a/browser/components/downloads/test/browser/browser_overflow_anchor.js
+++ b/browser/components/downloads/test/browser/browser_overflow_anchor.js
@@ -7,17 +7,17 @@ const kForceOverflowWidthPx = 200;
 registerCleanupFunction(async function() {
   // Clean up when the test finishes.
   await task_resetState();
 });
 
 /**
  * Make sure the downloads button and indicator overflows into the nav-bar
  * chevron properly, and then when those buttons are clicked in the overflow
- * panel that the downloads panel anchors to the chevron.
+ * panel that the downloads panel anchors to the chevron`s icon.
  */
 add_task(async function test_overflow_anchor() {
   await SpecialPowers.pushPrefEnv({set: [["browser.download.autohideButton", false]]});
   // Ensure that state is reset in case previous tests didn't finish.
   await task_resetState();
 
   // The downloads button should not be overflowed to begin with.
   let button = CustomizableUI.getWidget("downloads-button")
@@ -29,24 +29,28 @@ add_task(async function test_overflow_an
 
   let promise = promisePanelOpened();
   EventUtils.sendMouseEvent({ type: "mousedown", button: 0 }, button.node);
   info("waiting for panel to open");
   await promise;
 
   let panel = DownloadsPanel.panel;
   let chevron = document.getElementById("nav-bar-overflow-button");
-  is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
+  let chevronIcon = document.getAnonymousElementByAttribute(chevron,
+                                                            "class", "toolbarbutton-icon");
+  is(panel.anchorNode, chevronIcon, "Panel should be anchored to the chevron`s icon.");
 
   DownloadsPanel.hidePanel();
 
   gCustomizeMode.addToToolbar(button.node);
 
   // Now try opening the panel again.
   promise = promisePanelOpened();
   EventUtils.sendMouseEvent({ type: "mousedown", button: 0 }, button.node);
   await promise;
 
-  is(panel.anchorNode.id, "downloads-indicator-anchor");
+  let downloadsAnchor = document.getAnonymousElementByAttribute(button.node, "class",
+                                                               "toolbarbutton-badge-stack");
+  is(panel.anchorNode, downloadsAnchor);
 
   DownloadsPanel.hidePanel();
 });
 
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -92,21 +92,16 @@ const ICON_URL_APP = AppConstants.platfo
 
 // For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL
 // was set by us to a custom handler icon and CSS should not try to override it.
 const APP_ICON_ATTR_NAME = "appHandlerIcon";
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
   "resource://gre/modules/osfile.jsm");
 
-if (AppConstants.E10S_TESTING_ONLY) {
-  XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-    "resource://gre/modules/UpdateUtils.jsm");
-}
-
 if (AppConstants.MOZ_DEV_EDITION) {
   XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
     "resource://gre/modules/FxAccounts.jsm");
 }
 
 // A promise that resolves when the list of application handlers is loaded.
 // We store this in a global so tests can await it.
 var promiseLoadHandlersList;
@@ -314,36 +309,16 @@ var gMainPane = {
       row.removeAttribute("hidden");
       // Showing attribution only for Bing Translator.
       Components.utils.import("resource:///modules/translation/Translation.jsm");
       if (Translation.translationEngine == "bing") {
         document.getElementById("bingAttribution").removeAttribute("hidden");
       }
     }
 
-    if (AppConstants.E10S_TESTING_ONLY) {
-      setEventListener("e10sAutoStart", "command",
-        gMainPane.enableE10SChange);
-      let e10sCheckbox = document.getElementById("e10sAutoStart");
-
-      let e10sPref = document.getElementById("browser.tabs.remote.autostart");
-      let e10sTempPref = document.getElementById("e10sTempPref");
-      let e10sForceEnable = document.getElementById("e10sForceEnable");
-
-      let preffedOn = e10sPref.value || e10sTempPref.value || e10sForceEnable.value;
-
-      if (preffedOn) {
-        // The checkbox is checked if e10s is preffed on and enabled.
-        e10sCheckbox.checked = Services.appinfo.browserTabsRemoteAutostart;
-
-        // but if it's force disabled, then the checkbox is disabled.
-        e10sCheckbox.disabled = !Services.appinfo.browserTabsRemoteAutostart;
-      }
-    }
-
     if (AppConstants.MOZ_DEV_EDITION) {
       let uAppData = OS.Constants.Path.userApplicationDataDir;
       let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
 
       setEventListener("separateProfileMode", "command", gMainPane.separateProfileModeChange);
       let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
       setEventListener("getStarted", "click", gMainPane.onGetStarted);
 
@@ -541,64 +516,16 @@ var gMainPane = {
 
     const link = document.getElementById("browserContainersLearnMore");
     link.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "containers";
 
     document.getElementById("browserContainersbox").hidden = false;
     this.readBrowserContainersCheckbox();
   },
 
-  isE10SEnabled() {
-    let e10sEnabled;
-    try {
-      let e10sStatus = Components.classes["@mozilla.org/supports-PRUint64;1"]
-        .createInstance(Ci.nsISupportsPRUint64);
-      let appinfo = Services.appinfo.QueryInterface(Ci.nsIObserver);
-      appinfo.observe(e10sStatus, "getE10SBlocked", "");
-      e10sEnabled = e10sStatus.data < 2;
-    } catch (e) {
-      e10sEnabled = false;
-    }
-
-    return e10sEnabled;
-  },
-
-  enableE10SChange() {
-    if (AppConstants.E10S_TESTING_ONLY) {
-      let e10sCheckbox = document.getElementById("e10sAutoStart");
-      let e10sPref = document.getElementById("browser.tabs.remote.autostart");
-      let e10sTempPref = document.getElementById("e10sTempPref");
-
-      let prefsToChange;
-      if (e10sCheckbox.checked) {
-        // Enabling e10s autostart
-        prefsToChange = [e10sPref];
-      } else {
-        // Disabling e10s autostart
-        prefsToChange = [e10sPref];
-        if (e10sTempPref.value) {
-          prefsToChange.push(e10sTempPref);
-        }
-      }
-
-      let buttonIndex = confirmRestartPrompt(e10sCheckbox.checked, 0,
-        true, false);
-      if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
-        for (let prefToChange of prefsToChange) {
-          prefToChange.value = e10sCheckbox.checked;
-        }
-
-        Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
-      }
-
-      // Revert the checkbox in case we didn't quit
-      e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
-    }
-  },
-
   separateProfileModeChange() {
     if (AppConstants.MOZ_DEV_EDITION) {
       function quitApp() {
         Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestartNotSameProfile);
       }
       function revertCheckbox(error) {
         separateProfileModeCheckbox.checked = !separateProfileModeCheckbox.checked;
         if (error) {
@@ -1280,17 +1207,17 @@ var gMainPane = {
         processCountPref.value == processCountPref.defaultValue) {
         processCountPref.value = e10sRolloutProcessCountPref.value;
       }
       performanceSettings.hidden = false;
     }
   },
 
   buildContentProcessCountMenuList() {
-    if (gMainPane.isE10SEnabled()) {
+    if (Services.appinfo.browserTabsRemoteAutostart) {
       let processCountPref = document.getElementById("dom.ipc.processCount");
       let e10sRolloutProcessCountPref =
         document.getElementById("dom.ipc.processCount.web");
       let defaultProcessCount =
         e10sRolloutProcessCountPref.value || processCountPref.defaultValue;
       let bundlePreferences = document.getElementById("bundlePreferences");
       let label = bundlePreferences.getFormattedString("defaultContentProcessCount",
         [defaultProcessCount]);
--- a/browser/components/preferences/sitePermissions.js
+++ b/browser/components/preferences/sitePermissions.js
@@ -155,18 +155,25 @@ var gSitePermissionsManager = {
     let menulist = document.createElement("menulist");
     let menupopup = document.createElement("menupopup");
     menulist.setAttribute("flex", "1");
     menulist.setAttribute("width", "50");
     menulist.setAttribute("class", "website-status");
     menulist.appendChild(menupopup);
     let states = SitePermissions.getAvailableStates(permission.type);
     for (let state of states) {
-      if (state == SitePermissions.UNKNOWN)
+      // Work around the (rare) edge case when a user has changed their
+      // default permission type back to UNKNOWN while still having a
+      // PROMPT permission set for an origin.
+      if (state == SitePermissions.UNKNOWN &&
+          permission.capability == SitePermissions.PROMPT) {
+        state = SitePermissions.PROMPT;
+      } else if (state == SitePermissions.UNKNOWN) {
         continue;
+      }
       let m = document.createElement("menuitem");
       m.setAttribute("label", this._getCapabilityString(state));
       m.setAttribute("value", state);
       menupopup.appendChild(m);
     }
     menulist.value = permission.capability;
 
     menulist.addEventListener("select", () => {
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -289,17 +289,16 @@ These should match what Safari and other
 <!ENTITY fileMenu.accesskey       "F">
 <!ENTITY newUserContext.label             "New Container Tab">
 <!ENTITY newUserContext.accesskey         "B">
 <!ENTITY newNavigatorCmd.label        "New Window">
 <!ENTITY newNavigatorCmd.key        "N">
 <!ENTITY newNavigatorCmd.accesskey      "N">
 <!ENTITY newPrivateWindow.label     "New Private Window">
 <!ENTITY newPrivateWindow.accesskey "W">
-<!ENTITY newNonRemoteWindow.label   "New Non-e10s Window">
 
 <!ENTITY editMenu.label         "Edit">
 <!ENTITY editMenu.accesskey       "E">
 <!ENTITY undoCmd.label            "Undo">
 <!ENTITY undoCmd.key            "Z">
 <!ENTITY undoCmd.accesskey          "U">
 <!ENTITY redoCmd.label            "Redo">
 <!ENTITY redoCmd.key            "Y">
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -96,14 +96,11 @@ panic-button.label = Forget
 panic-button.tooltiptext = Forget about some browsing history
 
 # LOCALIZATION NOTE(devtools-webide-button.label, devtools-webide-button.tooltiptext):
 # widget is only visible after WebIDE has been started once (Tools > Web Developers > WebIDE)
 # %S is the keyboard shortcut
 devtools-webide-button2.label = WebIDE
 devtools-webide-button2.tooltiptext = Open WebIDE (%S)
 
-e10s-button.label = New Non-e10s Window
-e10s-button.tooltiptext = Open a new Non-e10s Window
-
 toolbarspring.label = Flexible Space
 toolbarseparator.label = Separator
 toolbarspacer.label = Space
--- a/browser/locales/en-US/chrome/browser/preferences/main.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/main.dtd
@@ -39,10 +39,8 @@
 <!ENTITY setAsMyDefaultBrowser3.accesskey "D">
 <!ENTITY isDefault.label                  "&brandShortName; is currently your default browser">
 <!ENTITY isNotDefault.label               "&brandShortName; is not your default browser">
 
 <!ENTITY separateProfileMode.label        "Allow &brandShortName; and Firefox to run at the same time">
 <!ENTITY useFirefoxSync.label             "Tip: This uses separate profiles. Use Sync to share data between them.">
 <!ENTITY getStarted.notloggedin.label     "Sign in to &syncBrand.shortName.label;…">
 <!ENTITY getStarted.configured.label      "Open &syncBrand.shortName.label; preferences">
-
-<!ENTITY e10sEnabled.label                "Enable multi-process &brandShortName;">
--- a/browser/locales/en-US/chrome/browser/sitePermissions.properties
+++ b/browser/locales/en-US/chrome/browser/sitePermissions.properties
@@ -9,16 +9,17 @@
 #                    state.current.blocked):
 # This label is used to display active permission states in the site
 # identity popup (which does not have a lot of screen space).
 state.current.allowed = Allowed
 state.current.allowedForSession = Allowed for Session
 state.current.allowedTemporarily = Allowed Temporarily
 state.current.blockedTemporarily = Blocked Temporarily
 state.current.blocked = Blocked
+state.current.prompt = Always Ask
 
 # LOCALIZATION NOTE (state.multichoice.alwaysAsk,
 #                    state.multichoice.allow,
 #                    state.multichoice.allowForSession,
 #                    state.multichoice.block):
 # Used to label permission state checkboxes in the page info dialog.
 state.multichoice.alwaysAsk = Always Ask
 state.multichoice.allow = Allow
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -325,32 +325,31 @@ PluginContent.prototype = {
     let centerX = left + (right - left) / 2;
     let centerY = top + (bottom - top) / 2;
     let points = [[left, top],
                    [left, bottom],
                    [right, top],
                    [right, bottom],
                    [centerX, centerY]];
 
-    if (right <= 0 || top <= 0) {
-      return false;
-    }
-
     let contentWindow = plugin.ownerGlobal;
     let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
 
     for (let [x, y] of points) {
+      if (x < 0 || y < 0) {
+        continue;
+      }
       let el = cwu.elementFromPoint(x, y, true, true);
-      if (el !== plugin) {
-        return false;
+      if (el === plugin) {
+        return true;
       }
     }
 
-    return true;
+    return false;
   },
 
   addLinkClickCallback(linkNode, callbackName /* callbackArgs...*/) {
     // XXX just doing (callback)(arg) was giving a same-origin error. bug?
     let self = this;
     let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
     linkNode.addEventListener("click",
                               function(evt) {
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -136,24 +136,27 @@ const TemporaryBlockedPermissions = {
  * Some methods have the side effect of dispatching a "PermissionStateChange"
  * event on changes to temporary permissions, as mentioned in the respective docs.
  */
 this.SitePermissions = {
   // Permission states.
   UNKNOWN: Services.perms.UNKNOWN_ACTION,
   ALLOW: Services.perms.ALLOW_ACTION,
   BLOCK: Services.perms.DENY_ACTION,
+  PROMPT: Services.perms.PROMPT_ACTION,
   ALLOW_COOKIES_FOR_SESSION: Components.interfaces.nsICookiePermission.ACCESS_SESSION,
 
   // Permission scopes.
   SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
   SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}",
   SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}",
   SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}",
 
+  _defaultPrefBranch: Services.prefs.getBranch("permissions.default."),
+
   /**
    * Gets all custom permissions for a given URI.
    * Install addon permission is excluded, check bug 1303108.
    *
    * @return {Array} a list of objects with the keys:
    *          - id: the permissionId of the permission
    *          - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY)
    *          - state: a constant representing the current permission state
@@ -273,36 +276,42 @@ this.SitePermissions = {
    *
    * @return {Array<SitePermissions state>} an array of all permission states.
    */
   getAvailableStates(permissionID) {
     if (permissionID in gPermissionObject &&
         gPermissionObject[permissionID].states)
       return gPermissionObject[permissionID].states;
 
+    /* Since the permissions we are dealing with have adopted the convention
+     * of treating UNKNOWN == PROMPT, we only include one of either UNKNOWN
+     * or PROMPT in this list, to avoid duplicating states. */
     if (this.getDefault(permissionID) == this.UNKNOWN)
       return [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ];
 
-    return [ SitePermissions.ALLOW, SitePermissions.BLOCK ];
+    return [ SitePermissions.PROMPT, SitePermissions.ALLOW, SitePermissions.BLOCK ];
   },
 
   /**
    * Returns the default state of a particular permission.
    *
    * @param {string} permissionID
    *        The ID to get the default for.
    *
    * @return {SitePermissions.state} the default state.
    */
   getDefault(permissionID) {
+    // If the permission has custom logic for getting its default value,
+    // try that first.
     if (permissionID in gPermissionObject &&
         gPermissionObject[permissionID].getDefault)
       return gPermissionObject[permissionID].getDefault();
 
-    return this.UNKNOWN;
+    // Otherwise try to get the default preference for that permission.
+    return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN);
   },
 
   /**
    * Returns the state and scope of a particular permission for a given URI.
    *
    * This method will NOT dispatch a "PermissionStateChange" event on the specified
    * browser if a temporary permission was removed because it has expired.
    *
@@ -315,17 +324,18 @@ this.SitePermissions = {
    *
    * @return {Object} an object with the keys:
    *           - state: The current state of the permission
    *             (e.g. SitePermissions.ALLOW)
    *           - scope: The scope of the permission
    *             (e.g. SitePermissions.SCOPE_PERSISTENT)
    */
   get(uri, permissionID, browser) {
-    let result = { state: this.UNKNOWN, scope: this.SCOPE_PERSISTENT };
+    let defaultState = this.getDefault(permissionID);
+    let result = { state: defaultState, scope: this.SCOPE_PERSISTENT };
     if (this.isSupportedURI(uri)) {
       let permission = null;
       if (permissionID in gPermissionObject &&
         gPermissionObject[permissionID].exactHostMatch) {
         permission = Services.perms.getPermissionObjectForURI(uri, permissionID, true);
       } else {
         permission = Services.perms.getPermissionObjectForURI(uri, permissionID, false);
       }
@@ -333,17 +343,17 @@ this.SitePermissions = {
       if (permission) {
         result.state = permission.capability;
         if (permission.expireType == Services.perms.EXPIRE_SESSION) {
           result.scope = this.SCOPE_SESSION;
         }
       }
     }
 
-    if (!result.state) {
+    if (result.state == defaultState) {
       // If there's no persistent permission saved, check if we have something
       // set temporarily.
       let value = TemporaryBlockedPermissions.get(browser, permissionID);
 
       if (value) {
         result.state = value.state;
         result.scope = this.SCOPE_TEMPORARY;
       }
@@ -366,17 +376,17 @@ this.SitePermissions = {
    *        The state of the permission.
    * @param {SitePermissions scope} scope (optional)
    *        The scope of the permission. Defaults to SCOPE_PERSISTENT.
    * @param {Browser} browser (optional)
    *        The browser object to set temporary permissions on.
    *        This needs to be provided if the scope is SCOPE_TEMPORARY!
    */
   set(uri, permissionID, state, scope = this.SCOPE_PERSISTENT, browser = null) {
-    if (state == this.UNKNOWN) {
+    if (state == this.UNKNOWN || state == this.getDefault(permissionID)) {
       this.remove(uri, permissionID, browser);
       return;
     }
 
     if (state == this.ALLOW_COOKIES_FOR_SESSION && permissionID != "cookie") {
       throw "ALLOW_COOKIES_FOR_SESSION can only be set on the cookie permission";
     }
 
@@ -483,16 +493,17 @@ this.SitePermissions = {
    *        The state to get the label for.
    *
    * @return {String|null} the localized label or null if an
    *         unknown state was passed.
    */
   getMultichoiceStateLabel(state) {
     switch (state) {
       case this.UNKNOWN:
+      case this.PROMPT:
         return gStringBundle.GetStringFromName("state.multichoice.alwaysAsk");
       case this.ALLOW:
         return gStringBundle.GetStringFromName("state.multichoice.allow");
       case this.ALLOW_COOKIES_FOR_SESSION:
         return gStringBundle.GetStringFromName("state.multichoice.allowForSession");
       case this.BLOCK:
         return gStringBundle.GetStringFromName("state.multichoice.block");
       default:
@@ -508,16 +519,18 @@ this.SitePermissions = {
    * @param {SitePermissions scope} scope (optional)
    *        The scope to get the label for.
    *
    * @return {String|null} the localized label or null if an
    *         unknown state was passed.
    */
   getCurrentStateLabel(state, scope = null) {
     switch (state) {
+      case this.PROMPT:
+        return gStringBundle.GetStringFromName("state.current.prompt");
       case this.ALLOW:
         if (scope && scope != this.SCOPE_PERSISTENT)
           return gStringBundle.GetStringFromName("state.current.allowedTemporarily");
         return gStringBundle.GetStringFromName("state.current.allowed");
       case this.ALLOW_COOKIES_FOR_SESSION:
         return gStringBundle.GetStringFromName("state.current.allowedForSession");
       case this.BLOCK:
         if (scope && scope != this.SCOPE_PERSISTENT)
@@ -548,20 +561,17 @@ var gPermissionObject = {
    *    e.g. "desktop-notification2" to use permission.desktop-notification2.label
    *
    *  - states
    *    Array of permission states to be exposed to the user.
    *    Defaults to ALLOW, BLOCK and the default state (see getDefault).
    */
 
   "image": {
-    getDefault() {
-      return Services.prefs.getIntPref("permissions.default.image") == 2 ?
-               SitePermissions.BLOCK : SitePermissions.ALLOW;
-    }
+    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "cookie": {
     states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ],
     getDefault() {
       if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == 2)
         return SitePermissions.BLOCK;
 
@@ -589,24 +599,26 @@ var gPermissionObject = {
     exactHostMatch: true,
     states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
   },
 
   "popup": {
     getDefault() {
       return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
                SitePermissions.BLOCK : SitePermissions.ALLOW;
-    }
+    },
+    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "install": {
     getDefault() {
       return Services.prefs.getBoolPref("xpinstall.whitelist.required") ?
                SitePermissions.BLOCK : SitePermissions.ALLOW;
-    }
+    },
+    states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "geo": {
     exactHostMatch: true
   },
 
   "indexedDB": {},
 
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -102,16 +102,19 @@ add_task(async function setup() {
   // Clear history so that history added by previous tests doesn't mess up this
   // test when it selects results in the urlbar.
   await PlacesTestUtils.clearHistory();
 
   // Clear historical search suggestions to avoid interference from previous
   // tests.
   await SpecialPowers.pushPrefEnv({"set": [["browser.urlbar.maxHistoricalSearchSuggestions", 0]]});
 
+  // Use the default matching bucket configuration.
+  await SpecialPowers.pushPrefEnv({"set": [["browser.urlbar.matchBuckets", "general:5,suggestion:4"]]});
+
   // Make sure to restore the engine once we're done.
   registerCleanupFunction(async function() {
     Services.telemetry.canRecordExtended = oldCanRecord;
     Services.search.currentEngine = originalEngine;
     Services.search.removeEngine(engine);
     Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, suggestionsEnabled);
     Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
     await PlacesTestUtils.clearHistory();
--- a/browser/modules/test/unit/test_SitePermissions.js
+++ b/browser/modules/test/unit/test_SitePermissions.js
@@ -61,16 +61,24 @@ add_task(async function testGetAllByURI(
 });
 
 add_task(async function testGetAvailableStates() {
   Assert.deepEqual(SitePermissions.getAvailableStates("camera"),
                    [ SitePermissions.UNKNOWN,
                      SitePermissions.ALLOW,
                      SitePermissions.BLOCK ]);
 
+  // Test available states with a default permission set.
+  Services.prefs.setIntPref("permissions.default.camera", SitePermissions.ALLOW);
+  Assert.deepEqual(SitePermissions.getAvailableStates("camera"),
+                   [ SitePermissions.PROMPT,
+                     SitePermissions.ALLOW,
+                     SitePermissions.BLOCK ]);
+  Services.prefs.clearUserPref("permissions.default.camera");
+
   Assert.deepEqual(SitePermissions.getAvailableStates("cookie"),
                    [ SitePermissions.ALLOW,
                      SitePermissions.ALLOW_COOKIES_FOR_SESSION,
                      SitePermissions.BLOCK ]);
 
   Assert.deepEqual(SitePermissions.getAvailableStates("popup"),
                    [ SitePermissions.ALLOW,
                      SitePermissions.BLOCK ]);
@@ -91,26 +99,80 @@ add_task(async function testExactHostMat
   let nonExactHostMatched = ["image", "cookie", "popup", "install", "indexedDB"];
 
   let permissions = SitePermissions.listPermissions();
   for (let permission of permissions) {
     SitePermissions.set(uri, permission, SitePermissions.ALLOW);
 
     if (exactHostMatched.includes(permission)) {
       // Check that the sub-origin does not inherit the permission from its parent.
-      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.UNKNOWN);
+      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.UNKNOWN,
+        `${permission} should exact-host match`);
     } else if (nonExactHostMatched.includes(permission)) {
       // Check that the sub-origin does inherit the permission from its parent.
-      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.ALLOW);
+      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.ALLOW,
+        `${permission} should not exact-host match`);
     } else {
       Assert.ok(false, `Found an unknown permission ${permission} in exact host match test.` +
                        "Please add new permissions from SitePermissions.jsm to this test.");
     }
 
     // Check that the permission can be made specific to the sub-origin.
-    SitePermissions.set(subUri, permission, SitePermissions.BLOCK);
-    Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.BLOCK);
+    SitePermissions.set(subUri, permission, SitePermissions.PROMPT);
+    Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.PROMPT);
     Assert.equal(SitePermissions.get(uri, permission).state, SitePermissions.ALLOW);
 
     SitePermissions.remove(subUri, permission);
     SitePermissions.remove(uri, permission);
   }
 });
+
+add_task(function* testDefaultPrefs() {
+  let uri = Services.io.newURI("https://example.com")
+
+  // Check that without a pref the default return value is UNKNOWN.
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.UNKNOWN,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // Check that the default return value changed after setting the pref.
+  Services.prefs.setIntPref("permissions.default.camera", SitePermissions.BLOCK);
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.BLOCK,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // Check that other permissions still return UNKNOWN.
+  Assert.deepEqual(SitePermissions.get(uri, "microphone"), {
+    state: SitePermissions.UNKNOWN,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // Check that the default return value changed after changing the pref.
+  Services.prefs.setIntPref("permissions.default.camera", SitePermissions.ALLOW);
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.ALLOW,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // Check that the preference is ignored if there is a value.
+  SitePermissions.set(uri, "camera", SitePermissions.BLOCK);
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.BLOCK,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // The preference should be honored again, after resetting the permissions.
+  SitePermissions.remove(uri, "camera");
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.ALLOW,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+
+  // Should be UNKNOWN after clearing the pref.
+  Services.prefs.clearUserPref("permissions.default.camera");
+  Assert.deepEqual(SitePermissions.get(uri, "camera"), {
+    state: SitePermissions.UNKNOWN,
+    scope: SitePermissions.SCOPE_PERSISTENT,
+  });
+});
+
--- a/browser/themes/osx/controlcenter/panel.css
+++ b/browser/themes/osx/controlcenter/panel.css
@@ -1,19 +1,15 @@
 /* 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/. */
 
 %include ../shared.inc
 %include ../../shared/controlcenter/panel.inc.css
 
-#identity-popup {
-  margin-top: 1px;
-}
-
 .identity-popup-expander:-moz-focusring,
 .identity-popup-permission-remove-button:-moz-focusring {
   box-shadow: var(--focus-ring-box-shadow);
 }
 
 #identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
   border-bottom-right-radius: 3.5px;
 }
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -1,20 +1,16 @@
 /* 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/. */
 
 %include ../../shared/downloads/downloads.inc.css
 
 /*** Panel and outer controls ***/
 
-#downloadsPanel {
-  margin-top: -1px;
-}
-
 @keyfocus@ #downloadsSummary:focus,
 @keyfocus@ .downloadsPanelFooterButton:focus {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
 /*** List items and similar elements in the summary ***/
 
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -194,16 +194,68 @@
   transform: translateX(-@menuPanelWidth@);
 }
 
 panelview {
   -moz-box-orient: vertical;
   -moz-box-flex: 1;
 }
 
+/* This section is to anchor all the drop down panels at the same height, shift the
+  panel`s top margin according to its positioning and the uidensity of the window. */
+#downloadsPanel,
+#widget-overflow,
+#appMenu-popup,
+#customizationui-widget-panel {
+  margin-top: -6px;
+}
+
+/* The difference between the toolbar buttons` padding and the urlbar-icons` padding,
+   then subtract 6px. */
+#pageActionActivatedActionPanel,
+#pageActionPanel,
+#editBookmarkPanel,
+.browser-extension-panel {
+  margin-top: calc(var(--toolbarbutton-inner-padding) - var(--urlbar-icon-padding) - 6px)
+}
+
+/* The identity popup does not have any padding of its own,
+   otherwise would use the same formula as above. */
+#identity-popup {
+  margin-top: calc(var(--toolbarbutton-inner-padding) - 6px);
+}
+
+/* The bookmarks toolbar is too thin to have the panels overlap 6px. */
+#downloadsPanel.bookmarks-toolbar,
+#widget-overflow.bookmarks-toolbar,
+#appMenu-popup.bookmarks-toolbar,
+#customizationui-widget-panel.bookmarks-toolbar {
+  margin-top: -1px;
+}
+
+/* The BMB_bookmarksPopup is unique because it is built into the
+   bookmarks-menu-button, resulting in many edge cases. */
+#BMB_bookmarksPopup {
+  margin-top: -8px;
+}
+
+:root:not([uidensity]) #nav-bar #BMB_bookmarksPopup {
+  margin-top: -11px;
+}
+
+:root[uidensity=touch] #nav-bar #BMB_bookmarksPopup,
+:root[uidensity=touch] #TabsToolbar #BMB_bookmarksPopup,
+:root[uidensity=compact] #BMB_bookmarksPopup {
+  margin-top: -9px;
+}
+
+:root[uidensity=compact] #TabsToolbar #BMB_bookmarksPopup {
+  margin-top: -7px;
+}
+
 .panel-subview-body {
   overflow-y: auto;
   overflow-x: hidden;
   -moz-box-flex: 1;
 }
 
 .panel-view-body-unscrollable {
   overflow: hidden;
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -224,24 +224,16 @@ toolbar[brighttext] {
 #characterencoding-button {
   list-style-image: url("chrome://browser/skin/characterEncoding.svg");
 }
 
 #new-window-button {
   list-style-image: url("chrome://browser/skin/new-window.svg");
 }
 
-#e10s-button {
-  list-style-image: url("chrome://browser/skin/new-window.svg");
-}
-
-#e10s-button > .toolbarbutton-icon {
-  transform: scaleY(-1);
-}
-
 #new-tab-button {
   list-style-image: url("chrome://browser/skin/new-tab.svg");
 }
 
 #privatebrowsing-button {
   list-style-image: url("chrome://browser/skin/privateBrowsing.svg");
 }
 
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -107,20 +107,16 @@ toolbar .toolbarbutton-1 {
   padding-inline-start: 5px;
   padding-inline-end: 5px;
 }
 
 toolbar .toolbarbutton-1 > menupopup {
   margin-top: -3px;
 }
 
-toolbar .toolbarbutton-1 > menupopup.cui-widget-panel {
-  margin-top: -8px;
-}
-
 .findbar-button > .toolbarbutton-text,
 toolbarbutton.bookmark-item:not(.subviewbutton),
 toolbar .toolbarbutton-1 > .toolbarbutton-icon,
 toolbar .toolbarbutton-1 > .toolbarbutton-text,
 toolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
   padding: var(--toolbarbutton-inner-padding);
   border-radius: var(--toolbarbutton-border-radius);
 }
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -1,14 +1,29 @@
 %if 0
 /* 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/. */
 %endif
 
+:root {
+  /* 28x28 box - 16x16 image = 12x12 padding, 6 on each side */
+  --urlbar-icon-padding: 6px;
+}
+
+:root[uidensity=compact] {
+  /* 24x24 box - 16x16 image = 8x8 padding, 4 on each side */
+  --urlbar-icon-padding: 4px;
+}
+
+:root[uidensity=touch] {
+  /* 30x30 box - 16x16 image = 14x14 padding, 7 on each side */
+  --urlbar-icon-padding: 7px;
+}
+
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   background-clip: content-box;
   border: 1px solid hsla(240,5%,5%,.25);
   border-radius: var(--toolbarbutton-border-radius);
   box-shadow: 0 1px 4px rgba(0,0,0,.05);
   padding: 0;
@@ -164,36 +179,31 @@
 #urlbar-zoom-button {
   margin-left: 6px;
   margin-right: 6px;
 }
 
 .urlbar-icon {
   width: 28px;
   height: 28px;
-  /* 28x28 box - 16x16 image = 12x12 padding, 6 on each side */
-  padding: 6px;
+  padding: var(--urlbar-icon-padding);
   -moz-context-properties: fill, fill-opacity;
   fill: currentColor;
   fill-opacity: 0.6;
   color: inherit;
 }
 
 :root[uidensity=compact] .urlbar-icon {
   width: 24px;
   height: 24px;
-  /* 24x24 box - 16x16 image = 8x8 padding, 4 on each side */
-  padding: 4px;
 }
 
 :root[uidensity=touch] .urlbar-icon {
   width: 30px;
   height: 30px;
-  /* 30x30 box - 16x16 image = 14x14 padding, 7 on each side */
-  padding: 7px;
 }
 
 .urlbar-icon:hover,
 .urlbar-icon-wrapper:hover {
   background-color: hsla(0,0%,80%,.4);
 }
 
 .urlbar-icon[open],
--- a/browser/themes/windows/customizableui/panelUI.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -78,8 +78,13 @@ menu.subviewbutton > .menu-right:-moz-lo
   #edit-controls@inAnyPanel@,
   #zoom-controls@inAnyPanel@,
   #edit-controls@inAnyPanel@ > toolbarbutton,
   #zoom-controls@inAnyPanel@ > toolbarbutton {
     border-radius: 0;
   }
 }
 
+/* Anchor the bookmark menu drop down panel at the same height as other
+   panels when in the #toolbar-menubar */
+#toolbar-menubar #BMB_bookmarksPopup {
+  margin-top: -6px;
+}
--- a/build.gradle
+++ b/build.gradle
@@ -49,23 +49,23 @@ buildscript {
         }
         // For in tree plugins.
         maven {
             url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
         }
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.1.3'
+        classpath 'com.android.tools.build:gradle:2.3.3'
         classpath('com.stanfy.spoon:spoon-gradle-plugin:1.0.4') {
             // Without these, we get errors linting.
             exclude module: 'guava'
         }
         // Provided in tree.
-        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.1'
+        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.3'
     }
 }
 
 task generateCodeAndResources(type:Exec) {
     workingDir "${topobjdir}"
 
     commandLine mozconfig.substs.GMAKE
     args '-C'
--- a/build/sparse-profiles/taskgraph
+++ b/build/sparse-profiles/taskgraph
@@ -31,8 +31,11 @@ glob:**/*.mozbuild
 
 # Tooltool manifests also need to be opened. Assume they
 # are all somewhere in "tooltool-manifests" directories.
 glob:**/tooltool-manifests/**
 
 # For scheduling android-gradle-dependencies.
 path:mobile/android/config/
 glob:**/*.gradle
+
+# for action-task building
+path:.taskcluster.yml
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -119,16 +119,17 @@ Converter.prototype = {
     this.listener.onStartRequest(request, context);
 
     // Initialize stuff.
     let win = NetworkHelper.getWindowForRequest(request);
     this.data = exportData(win, request);
     win.addEventListener("DOMContentLoaded", event => {
       win.addEventListener("contentMessage", onContentMessage, false, true);
     }, {once: true});
+    keepThemeUpdated(win);
 
     // Send the initial HTML code.
     let bytes = encoder.encode(initialHTML(win.document));
     this.convertAndSendBuffer(request, context, bytes.buffer);
 
     // Create an array to store data until the encoding is determined.
     this.encodingArray = [];
   },
@@ -317,16 +318,28 @@ function initialHTML(doc) {
       "dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
     }) +
     head.outerHTML +
     startTag("body") +
     startTag("div", {"id": "content"}) +
     startTag("plaintext", {"id": "json"});
 }
 
+function keepThemeUpdated(win) {
+  let listener = function () {
+    let theme = Services.prefs.getCharPref("devtools.theme");
+    win.document.documentElement.className = "theme-" + theme;
+  };
+  Services.prefs.addObserver("devtools.theme", listener);
+  win.addEventListener("unload", function (event) {
+    Services.prefs.removeObserver("devtools.theme", listener);
+    win = null;
+  }, {once: true});
+}
+
 // Chrome <-> Content communication
 function onContentMessage(e) {
   // Do not handle events from different documents.
   let win = this;
   if (win != e.target) {
     return;
   }
 
--- a/devtools/client/jsonview/test/browser.ini
+++ b/devtools/client/jsonview/test/browser.ini
@@ -38,11 +38,12 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_jsonview_invalid_json.js]
 [browser_jsonview_manifest.js]
 [browser_jsonview_nojs.js]
 [browser_jsonview_nul.js]
 [browser_jsonview_object-type.js]
 [browser_jsonview_save_json.js]
 support-files =
   !/toolkit/content/tests/browser/common/mockTransfer.js
+[browser_jsonview_theme.js]
 [browser_jsonview_slash.js]
 [browser_jsonview_valid_json.js]
 [browser_json_refresh.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/browser_jsonview_theme.js
@@ -0,0 +1,34 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_JSON_URL = URL_ROOT + "valid_json.json";
+
+add_task(function* () {
+  info("Test JSON theme started.");
+
+  let oldPref = SpecialPowers.getCharPref("devtools.theme");
+  SpecialPowers.setCharPref("devtools.theme", "light");
+
+  yield addJsonViewTab(TEST_JSON_URL);
+
+  is(yield getTheme(), "theme-light", "The initial theme is light");
+
+  SpecialPowers.setCharPref("devtools.theme", "dark");
+  is(yield getTheme(), "theme-dark", "Theme changed to dark");
+
+  SpecialPowers.setCharPref("devtools.theme", "firebug");
+  is(yield getTheme(), "theme-firebug", "Theme changed to firebug");
+
+  SpecialPowers.setCharPref("devtools.theme", "light");
+  is(yield getTheme(), "theme-light", "Theme changed to light");
+
+  SpecialPowers.setCharPref("devtools.theme", oldPref);
+});
+
+function getTheme() {
+  return getElementAttr(":root", "class");
+}
--- a/devtools/client/jsonview/test/doc_frame_script.js
+++ b/devtools/client/jsonview/test/doc_frame_script.js
@@ -53,16 +53,23 @@ addMessageListener("Test:JsonView:GetEle
 
 addMessageListener("Test:JsonView:GetElementVisibleText", function (msg) {
   let {selector} = msg.data;
   let element = content.document.querySelector(selector);
   let text = element ? element.innerText : null;
   sendAsyncMessage(msg.name, {text: text});
 });
 
+addMessageListener("Test:JsonView:GetElementAttr", function (msg) {
+  let {selector, attr} = msg.data;
+  let element = content.document.querySelector(selector);
+  let text = element ? element.getAttribute(attr) : null;
+  sendAsyncMessage(msg.name, {text: text});
+});
+
 addMessageListener("Test:JsonView:FocusElement", function (msg) {
   let {selector} = msg.data;
   let element = content.document.querySelector(selector);
   if (element) {
     element.focus();
   }
   sendAsyncMessage(msg.name);
 });
--- a/devtools/client/jsonview/test/head.js
+++ b/devtools/client/jsonview/test/head.js
@@ -118,16 +118,24 @@ function getElementText(selector) {
   };
 
   return executeInContent("Test:JsonView:GetElementText", data)
   .then(result => {
     return result.text;
   });
 }
 
+function getElementAttr(selector, attr) {
+  info("Get attribute '" + attr + "' for element '" + selector + "'");
+
+  let data = {selector, attr};
+  return executeInContent("Test:JsonView:GetElementAttr", data)
+  .then(result => result.text);
+}
+
 function focusElement(selector) {
   info("Focus element: '" + selector + "'");
 
   let data = {
     selector: selector
   };
 
   return executeInContent("Test:JsonView:FocusElement", data);
--- a/devtools/client/locales/en-US/performance.dtd
+++ b/devtools/client/locales/en-US/performance.dtd
@@ -10,21 +10,16 @@
   - You want to make that choice consistent across the developer tools.
   - A good criteria is the language in which you'd find the best
   - documentation on web development on the web. -->
 
 <!-- LOCALIZATION NOTE (performanceUI.bufferStatusTooltip): This string
   -  is displayed as the tooltip for the buffer capacity during a recording. -->
 <!ENTITY performanceUI.bufferStatusTooltip "The profiler stores samples in a circular buffer, and once the buffer reaches the limit for a recording, newer samples begin to overwrite samples at the beginning of the recording.">
 
-<!-- LOCALIZATION NOTE (performanceUI.disabledRealTime.nonE10SBuild): This string
-  -  is displayed as a message for why the real time overview graph is disabled
-  -  when running on a non-multiprocess build. -->
-<!ENTITY performanceUI.disabledRealTime.nonE10SBuild "Realtime recording data disabled on non-multiprocess Firefox.">
-
 <!-- LOCALIZATION NOTE (performanceUI.disabledRealTime.disabledE10S): This string
   -  is displayed as a message for why the real time overview graph is disabled
   -  when running on a build that can run multiprocess Firefox, but just is not enabled. -->
 <!ENTITY performanceUI.disabledRealTime.disabledE10S "Enable multiprocess Firefox in preferences for rendering recording data in realtime.">
 
 <!-- LOCALIZATION NOTE (performanceUI.bufferStatusFull): This string
   -  is displayed when the profiler's circular buffer has started to overlap. -->
 <!ENTITY performanceUI.bufferStatusFull "The buffer is full. Older samples are now being overwritten.">
--- a/devtools/client/netmonitor/src/components/request-list-header.js
+++ b/devtools/client/netmonitor/src/components/request-list-header.js
@@ -82,19 +82,19 @@ const RequestListHeader = createClass({
     this.background.draw(props);
   },
 
   resizeWaterfall() {
     let waterfallHeader = this.refs.waterfallHeader;
     if (waterfallHeader) {
       // Measure its width and update the 'waterfallWidth' property in the store.
       // The 'waterfallWidth' will be further updated on every window resize.
-      setTimeout(() => {
-        this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width);
-      }, 500);
+      window.cancelIdleCallback(this._resizeTimerId);
+      this._resizeTimerId = window.requestIdleCallback(() =>
+        this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width));
     }
   },
 
   render() {
     let { columns, scale, sort, sortBy, waterfallWidth } = this.props;
 
     return (
       div({ className: "devtools-toolbar requests-list-headers" },
--- a/devtools/client/performance/performance-controller.js
+++ b/devtools/client/performance/performance-controller.js
@@ -512,28 +512,27 @@ var PerformanceController = {
   /**
    * Returns an object with `supported` and `enabled` properties indicating
    * whether or not the platform is capable of turning on e10s and whether or not
    * it's already enabled, respectively.
    *
    * @return {object}
    */
   getMultiprocessStatus: function () {
-    // If testing, set both supported and enabled to true so we
-    // have realtime rendering tests in non-e10s. This function is
-    // overridden wholesale in tests when we want to test multiprocess support
+    // If testing, set enabled to true so we have realtime rendering tests
+    // in non-e10s. This function is overridden wholesale in tests
+    // when we want to test multiprocess support
     // specifically.
     if (flags.testing) {
-      return { supported: true, enabled: true };
+      return { enabled: true };
     }
-    let supported = system.constants.E10S_TESTING_ONLY;
     // This is only checked on tool startup -- requires a restart if
     // e10s subsequently enabled.
     let enabled = this._e10s;
-    return { supported, enabled };
+    return { enabled };
   },
 
   /**
    * Takes a PerformanceRecording and a state, and waits for
    * the event to be emitted from the front for that recording.
    *
    * @param {PerformanceRecordingFront} recording
    * @param {string} expectedState
@@ -551,22 +550,19 @@ var PerformanceController = {
   }),
 
   /**
    * Called on init, sets an `e10s` attribute on the main view container with
    * "disabled" if e10s is possible on the platform and just not on, or "unsupported"
    * if e10s is not possible on the platform. If e10s is on, no attribute is set.
    */
   _setMultiprocessAttributes: function () {
-    let { enabled, supported } = this.getMultiprocessStatus();
-    if (!enabled && supported) {
+    let { enabled } = this.getMultiprocessStatus();
+    if (!enabled) {
       $("#performance-view").setAttribute("e10s", "disabled");
-    } else if (!enabled && !supported) {
-      // Could be a chance where the directive goes away yet e10s is still on
-      $("#performance-view").setAttribute("e10s", "unsupported");
     }
   },
 
   /**
    * Pipes an event from some source to the PerformanceController.
    */
   _pipe: function (eventName, ...data) {
     this.emit(eventName, ...data);
--- a/devtools/client/performance/performance.xul
+++ b/devtools/client/performance/performance.xul
@@ -206,18 +206,16 @@
             <vbox id="recording-notice"
                   class="notice-container"
                   align="center"
                   pack="center"
                   flex="1">
               <hbox pack="center">
                 <html:div class='recording-button-mount'/>
               </hbox>
-              <label class="realtime-disabled-message"
-                     value="&performanceUI.disabledRealTime.nonE10SBuild;"/>
               <label class="realtime-disabled-on-e10s-message"
                      value="&performanceUI.disabledRealTime.disabledE10S;"/>
               <label class="buffer-status-message"
                      tooltiptext="&performanceUI.bufferStatusTooltip;"/>
               <label class="buffer-status-message-full"
                      value="&performanceUI.bufferStatusFull;"/>
             </vbox>
 
@@ -232,18 +230,16 @@
                 <label class="console-profile-command" />
                 <label value="&performanceUI.console.recordingNoticeEnd;" />
               </hbox>
               <hbox class="console-profile-stop-notice">
                 <label value="&performanceUI.console.stopCommandStart;" />
                 <label class="console-profile-command" />
                 <label value="&performanceUI.console.stopCommandEnd;" />
               </hbox>
-              <label class="realtime-disabled-message"
-                     value="&performanceUI.disabledRealTime.nonE10SBuild;"/>
               <label class="realtime-disabled-on-e10s-message"
                      value="&performanceUI.disabledRealTime.disabledE10S;"/>
               <label class="buffer-status-message"
                      tooltiptext="&performanceUI.bufferStatusTooltip;"/>
               <label class="buffer-status-message-full"
                      value="&performanceUI.bufferStatusFull;"/>
             </vbox>
 
--- a/devtools/client/performance/test/browser_perf-recording-notices-05.js
+++ b/devtools/client/performance/test/browser_perf-recording-notices-05.js
@@ -15,40 +15,26 @@ add_task(function* () {
     win: window
   });
 
   let { gFront, $, PerformanceController } = panel.panelWin;
 
   // Set a fast profiler-status update interval
   yield gFront.setProfilerStatusInterval(10);
 
-  let supported = false;
   let enabled = false;
 
   PerformanceController.getMultiprocessStatus = () => {
-    return { supported, enabled };
+    return { enabled };
   };
 
   PerformanceController._setMultiprocessAttributes();
-  ok($("#performance-view").getAttribute("e10s"), "unsupported",
-    "When e10s is disabled and no option to turn on, container has [e10s=unsupported].");
+  ok($("#performance-view").getAttribute("e10s"), "disabled",
+    "When e10s is disabled, container has [e10s=disabled].");
 
-  supported = true;
-  enabled = false;
-  PerformanceController._setMultiprocessAttributes();
-  ok($("#performance-view").getAttribute("e10s"), "disabled",
-    "When e10s is disabled and but is supported, container has [e10s=disabled].");
+  enabled = true;
 
-  supported = false;
-  enabled = true;
   PerformanceController._setMultiprocessAttributes();
   ok($("#performance-view").getAttribute("e10s"), "",
-    "When e10s is enabled, but not supported, this probably means we no longer have " +
-    "E10S_TESTING_ONLY, and we have no e10s attribute.");
-
-  supported = true;
-  enabled = true;
-  PerformanceController._setMultiprocessAttributes();
-  ok($("#performance-view").getAttribute("e10s"), "",
-    "When e10s is enabled and supported, there should be no e10s attribute.");
+    "When e10s is enabled, there should be no e10s attribute.");
 
   yield teardownToolboxAndRemoveTab(panel);
 });
--- a/devtools/client/shared/test/browser_num-l10n.js
+++ b/devtools/client/shared/test/browser_num-l10n.js
@@ -17,11 +17,17 @@ function test() {
   is(l10n.numberWithDecimals(1.0001, 2), "1",
     "The third number was properly localized.");
   is(l10n.numberWithDecimals(NaN, 2), "0",
     "NaN was properly localized.");
   is(l10n.numberWithDecimals(null, 2), "0",
     "`null` was properly localized.");
   is(l10n.numberWithDecimals(undefined, 2), "0",
     "`undefined` was properly localized.");
+  is(l10n.numberWithDecimals(-1234.56789, 2), "-1,234.57",
+    "Negative number was properly localized.");
+  is(l10n.numberWithDecimals(1234.56789, 0), "1,235",
+    "Number was properly localized with decimals set 0.");
+  is(l10n.numberWithDecimals(-1234.56789, 0), "-1,235",
+    "Negative number was properly localized with decimals set 0.");
 
   finish();
 }
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -242,23 +242,21 @@
   overflow: hidden;
 }
 
 .console-profile-command {
   font-family: monospace;
   margin: 3px 2px;
 }
 
-.realtime-disabled-message,
 .realtime-disabled-on-e10s-message {
   display: none;
 }
 
-#performance-view[e10s="disabled"] .realtime-disabled-on-e10s-message,
-#performance-view[e10s="unsupported"] .realtime-disabled-message {
+#performance-view[e10s="disabled"] .realtime-disabled-on-e10s-message {
   display: initial;
   opacity: 0.5;
 }
 
 .buffer-status-message,
 .buffer-status-message-full {
   display: none;
 }
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -198,17 +198,16 @@ skip-if = true #	Bug 1403188
 [browser_jsterm_copy_command.js]
 skip-if = true
 subsuite = clipboard
 # old console skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsterm_dollar.js]
 [browser_jsterm_history_persist.js]
 [browser_jsterm_inspect.js]
 [browser_jsterm_no_autocompletion_on_defined_variables.js]
-skip-if = true # Bug 1408872
 [browser_jsterm_no_input_and_tab_key_pressed.js]
 [browser_jsterm_no_input_change_and_tab_key_pressed.js]
 [browser_netmonitor_shows_reqs_in_webconsole.js]
 [browser_webconsole.js]
 skip-if = true #	Bug 1404829
 [browser_webconsole_add_edited_input_to_history.js]
 skip-if = true # Bug 1408915
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_no_autocompletion_on_defined_variables.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_no_autocompletion_on_defined_variables.js
@@ -6,20 +6,17 @@
 // Tests for bug 704295
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-console.html";
 
 add_task(function* () {
-  yield loadTab(TEST_URI);
-
-  let hud = yield openConsole();
-
+  let hud = yield openNewTabAndConsole(TEST_URI);
   testCompletion(hud);
 });
 
 function testCompletion(hud) {
   let jsterm = hud.jsterm;
   let input = jsterm.inputNode;
 
   // Test typing 'var d = 5;' and press RETURN
--- a/devtools/shared/l10n.js
+++ b/devtools/shared/l10n.js
@@ -30,16 +30,32 @@ const reqShared = require.context("raw!d
                                   true, /^.*\.properties$/);
 const reqClient = require.context("raw!devtools/client/locales/",
                                   true, /^.*\.properties$/);
 const reqShim = require.context("raw!devtools/shim/locales/",
                                   true, /^.*\.properties$/);
 const reqGlobal = require.context("raw!toolkit/locales/",
                                   true, /^.*\.properties$/);
 
+// Map used to memoize Number formatters.
+const numberFormatters = new Map();
+const getNumberFormatter = function (decimals) {
+  let formatter = numberFormatters.get(decimals);
+  if (!formatter) {
+    // Create and memoize a formatter for the provided decimals
+    formatter = Intl.NumberFormat(undefined, {
+      maximumFractionDigits: decimals,
+      minimumFractionDigits: decimals
+    });
+    numberFormatters.set(decimals, formatter);
+  }
+
+  return formatter;
+};
+
 /**
  * Memoized getter for properties files that ensures a given url is only required and
  * parsed once.
  *
  * @param {String} url
  *        The URL of the properties file to parse.
  * @return {Object} parsed properties mapped in an object.
  */
@@ -139,28 +155,28 @@ LocalizationHelper.prototype = {
     if (number === (number|0)) {
       return number;
     }
     // If this isn't a number (and yes, `isNaN(null)` is false), return zero.
     if (isNaN(number) || number === null) {
       return "0";
     }
 
-    let localized = number.toLocaleString();
+    // Localize the number using a memoized Intl.NumberFormat formatter.
+    let localized = getNumberFormatter(decimals).format(number);
 
-    // If no grouping or decimal separators are available, bail out, because
-    // padding with zeros at the end of the string won't make sense anymore.
-    if (!localized.match(/[^\d]/)) {
-      return localized;
+    // Convert the localized number to a number again.
+    let localizedNumber = localized * 1;
+    // Check if this number is now equal to an integer.
+    if (localizedNumber === (localizedNumber|0)) {
+    // If it is, remove the fraction part.
+      return getNumberFormatter(0).format(localizedNumber);
     }
 
-    return number.toLocaleString(undefined, {
-      maximumFractionDigits: decimals,
-      minimumFractionDigits: decimals
-    });
+    return localized;
   }
 };
 
 function getPropertiesForNode(node) {
   let bundleEl = node.closest("[data-localization-bundle]");
   if (!bundleEl) {
     return null;
   }
--- a/devtools/shared/sprintfjs/UPGRADING.md
+++ b/devtools/shared/sprintfjs/UPGRADING.md
@@ -1,12 +1,17 @@
 SPRINTF JS UPGRADING
 
 Original library at https://github.com/alexei/sprintf.js
+
+This library should no longer be upgraded from upstream. We added performance improvements
+in https://bugzilla.mozilla.org/show_bug.cgi?id=1406311. Most importantly removing the
+usage of the get_type() method as well as prioritizing the %S use case.
+
+If for some reason, updating from upstream becomes necessary, please refer to the bug
+mentioned above to reimplement the performance fixes in the new version.
+
 By default the library only supports string placeholders using %s (lowercase) while we use
-%S (uppercase). The library has to be manually patched in order to support it.
+%S (uppercase). The library also has to be manually patched in order to support it.
 
 - grab the unminified version at https://github.com/alexei/sprintf.js/blob/master/src/sprintf.js
 - update the re.placeholder regexp to allow "S" as well as "s"
 - update the switch statement in the format() method to make case "S" equivalent to case "s"
-
-The original changeset adding support for "%S" can be found on this fork:
-- https://github.com/juliandescottes/sprintf.js/commit/a60ea5d7c4cd9a006002ba9f0afc1e2689107eec
\ No newline at end of file
--- a/devtools/shared/sprintfjs/sprintf.js
+++ b/devtools/shared/sprintfjs/sprintf.js
@@ -21,16 +21,17 @@
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
 
+/* eslint-disable */
 /* globals window, exports, define */
 
 (function(window) {
     'use strict'
 
     var re = {
         not_string: /[^s]/,
         not_bool: /[^t]/,
@@ -55,21 +56,25 @@
             cache[key] = sprintf.parse(key)
         }
         return sprintf.format.call(null, cache[key], arguments)
     }
 
     sprintf.format = function(parse_tree, argv) {
         var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ''
         for (i = 0; i < tree_length; i++) {
-            node_type = get_type(parse_tree[i])
+            node_type = typeof parse_tree[i]
+            // The items of parse tree are either strings or results of a match() call.
             if (node_type === 'string') {
+                // this is not a placeholder, this is just a string.
                 output[output.length] = parse_tree[i]
             }
-            else if (node_type === 'array') {
+            else {
+                // this is a placeholder, need to identify its type, options and replace
+                // it with the appropriate argument.
                 match = parse_tree[i] // convenience purposes only
                 if (match[2]) { // keyword argument
                     arg = argv[cursor]
                     for (k = 0; k < match[2].length; k++) {
                         if (!arg.hasOwnProperty(match[2][k])) {
                             throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]))
                         }
                         arg = arg[match[2][k]]
@@ -77,22 +82,37 @@
                 }
                 else if (match[1]) { // positional argument (explicit)
                     arg = argv[match[1]]
                 }
                 else { // positional argument (implicit)
                     arg = argv[cursor++]
                 }
 
-                if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && get_type(arg) == 'function') {
+                // The most commonly used placeholder in DevTools is the string (%S or %s).
+                // We check it first to avoid unnecessary verifications.
+                let hasPadding = match[6];
+                let patternType = match[8];
+                if (!hasPadding && (patternType === "S" || patternType === "s")) {
+                    if (typeof arg === "function") {
+                        arg = arg();
+                    }
+                    if (typeof arg !== "string") {
+                        arg = String(arg);
+                    }
+                    output[output.length] = match[7] ? arg.substring(0, match[7]) : arg;
+                    continue;
+                }
+
+                if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && typeof arg == 'function') {
                     arg = arg()
                 }
 
-                if (re.numeric_arg.test(match[8]) && (get_type(arg) != 'number' && isNaN(arg))) {
-                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
+                if (re.numeric_arg.test(match[8]) && (typeof arg != 'number' && isNaN(arg))) {
+                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", typeof arg))
                 }
 
                 if (re.number.test(match[8])) {
                     is_positive = arg >= 0
                 }
 
                 switch (match[8]) {
                     case 'b':
@@ -125,17 +145,17 @@
                         arg = String(arg)
                         arg = (match[7] ? arg.substring(0, match[7]) : arg)
                     break
                     case 't':
                         arg = String(!!arg)
                         arg = (match[7] ? arg.substring(0, match[7]) : arg)
                     break
                     case 'T':
-                        arg = get_type(arg)
+                        arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase()
                         arg = (match[7] ? arg.substring(0, match[7]) : arg)
                     break
                     case 'u':
                         arg = parseInt(arg, 10) >>> 0
                     break
                     case 'v':
                         arg = arg.valueOf()
                         arg = (match[7] ? arg.substring(0, match[7]) : arg)
@@ -222,27 +242,16 @@
         _argv = (argv || []).slice(0)
         _argv.splice(0, 0, fmt)
         return sprintf.apply(null, _argv)
     }
 
     /**
      * helpers
      */
-    function get_type(variable) {
-        if (typeof variable === 'number') {
-            return 'number'
-        }
-        else if (typeof variable === 'string') {
-            return 'string'
-        }
-        else {
-            return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
-        }
-    }
 
     var preformattedPadding = {
         '0': ['', '0', '00', '000', '0000', '00000', '000000', '0000000'],
         ' ': ['', ' ', '  ', '   ', '    ', '     ', '      ', '       '],
         '_': ['', '_', '__', '___', '____', '_____', '______', '_______'],
     }
     function str_repeat(input, multiplier) {
         if (multiplier >= 0 && multiplier <= 7 && preformattedPadding[input]) {
new file mode 100644
--- /dev/null
+++ b/devtools/shared/tests/unit/test_sprintfjs.js
@@ -0,0 +1,113 @@
+/* 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";
+
+/**
+ * This unit test checks that our string formatter works with different patterns and
+ * arguments.
+ * Initially copied from unit tests at https://github.com/alexei/sprintf.js
+ */
+
+const {sprintf} = require("devtools/shared/sprintfjs/sprintf");
+const PI = 3.141592653589793;
+
+function run_test() {
+  // Simple patterns
+  equal("%", sprintf("%%"));
+  equal("10", sprintf("%b", 2));
+  equal("A", sprintf("%c", 65));
+  equal("2", sprintf("%d", 2));
+  equal("2", sprintf("%i", 2));
+  equal("2", sprintf("%d", "2"));
+  equal("2", sprintf("%i", "2"));
+  equal("{\"foo\":\"bar\"}", sprintf("%j", {foo: "bar"}));
+  equal("[\"foo\",\"bar\"]", sprintf("%j", ["foo", "bar"]));
+  equal("2e+0", sprintf("%e", 2));
+  equal("2", sprintf("%u", 2));
+  equal("4294967294", sprintf("%u", -2));
+  equal("2.2", sprintf("%f", 2.2));
+  equal("3.141592653589793", sprintf("%g", PI));
+  equal("10", sprintf("%o", 8));
+  equal("%s", sprintf("%s", "%s"));
+  equal("ff", sprintf("%x", 255));
+  equal("FF", sprintf("%X", 255));
+  equal("Polly wants a cracker",
+    sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants"));
+  equal("Hello world!", sprintf("Hello %(who)s!", {who: "world"}));
+  equal("true", sprintf("%t", true));
+  equal("t", sprintf("%.1t", true));
+  equal("true", sprintf("%t", "true"));
+  equal("true", sprintf("%t", 1));
+  equal("false", sprintf("%t", false));
+  equal("f", sprintf("%.1t", false));
+  equal("false", sprintf("%t", ""));
+  equal("false", sprintf("%t", 0));
+
+  equal("undefined", sprintf("%T", undefined));
+  equal("null", sprintf("%T", null));
+  equal("boolean", sprintf("%T", true));
+  equal("number", sprintf("%T", 42));
+  equal("string", sprintf("%T", "This is a string"));
+  equal("function", sprintf("%T", Math.log));
+  equal("array", sprintf("%T", [1, 2, 3]));
+  equal("object", sprintf("%T", {foo: "bar"}));
+
+  equal("regexp", sprintf("%T", /<("[^"]*"|"[^"]*"|[^"">])*>/));
+
+  equal("true", sprintf("%v", true));
+  equal("42", sprintf("%v", 42));
+  equal("This is a string", sprintf("%v", "This is a string"));
+  equal("1,2,3", sprintf("%v", [1, 2, 3]));
+  equal("[object Object]", sprintf("%v", {foo: "bar"}));
+  equal("/<(\"[^\"]*\"|'[^']*'|[^'\">])*>/",
+    sprintf("%v", /<("[^"]*"|'[^']*'|[^'">])*>/));
+
+  // sign
+  equal("2", sprintf("%d", 2));
+  equal("-2", sprintf("%d", -2));
+  equal("+2", sprintf("%+d", 2));
+  equal("-2", sprintf("%+d", -2));
+  equal("2", sprintf("%i", 2));
+  equal("-2", sprintf("%i", -2));
+  equal("+2", sprintf("%+i", 2));
+  equal("-2", sprintf("%+i", -2));
+  equal("2.2", sprintf("%f", 2.2));
+  equal("-2.2", sprintf("%f", -2.2));
+  equal("+2.2", sprintf("%+f", 2.2));
+  equal("-2.2", sprintf("%+f", -2.2));
+  equal("-2.3", sprintf("%+.1f", -2.34));
+  equal("-0.0", sprintf("%+.1f", -0.01));
+  equal("3.14159", sprintf("%.6g", PI));
+  equal("3.14", sprintf("%.3g", PI));
+  equal("3", sprintf("%.1g", PI));
+  equal("-000000123", sprintf("%+010d", -123));
+  equal("______-123", sprintf("%+'_10d", -123));
+  equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2));
+
+  // padding
+  equal("-0002", sprintf("%05d", -2));
+  equal("-0002", sprintf("%05i", -2));
+  equal("    <", sprintf("%5s", "<"));
+  equal("0000<", sprintf("%05s", "<"));
+  equal("____<", sprintf("%'_5s", "<"));
+  equal(">    ", sprintf("%-5s", ">"));
+  equal(">0000", sprintf("%0-5s", ">"));
+  equal(">____", sprintf("%'_-5s", ">"));
+  equal("xxxxxx", sprintf("%5s", "xxxxxx"));
+  equal("1234", sprintf("%02u", 1234));
+  equal(" -10.235", sprintf("%8.3f", -10.23456));
+  equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx"));
+  equal("{\n  \"foo\": \"bar\"\n}", sprintf("%2j", {foo: "bar"}));
+  equal("[\n  \"foo\",\n  \"bar\"\n]", sprintf("%2j", ["foo", "bar"]));
+
+  // precision
+  equal("2.3", sprintf("%.1f", 2.345));
+  equal("xxxxx", sprintf("%5.5s", "xxxxxx"));
+  equal("    x", sprintf("%5.1s", "xxxxxx"));
+
+  equal("foobar", sprintf("%s", function () {
+    return "foobar";
+  }));
+}
--- a/devtools/shared/tests/unit/xpcshell.ini
+++ b/devtools/shared/tests/unit/xpcshell.ini
@@ -32,13 +32,14 @@ run-if = nightly_build
 [test_async-utils.js]
 [test_console_filtering.js]
 [test_pluralForm-english.js]
 [test_pluralForm-makeGetter.js]
 [test_prettifyCSS.js]
 [test_require_lazy.js]
 [test_require_raw.js]
 [test_require.js]
+[test_sprintfjs.js]
 [test_stack.js]
 [test_defer.js]
 [test_executeSoon.js]
 [test_wasm-source-map.js]
 [test_protocol_index.js]
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6939,19 +6939,20 @@ nsContentUtils::FlushLayoutForTree(nsPID
     doc->FlushPendingNotifications(FlushType::Layout);
   }
 
   if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
     int32_t i = 0, i_end;
     docShell->GetChildCount(&i_end);
     for (; i < i_end; ++i) {
       nsCOMPtr<nsIDocShellTreeItem> item;
-      docShell->GetChildAt(i, getter_AddRefs(item));
-      if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
-        FlushLayoutForTree(win);
+      if (docShell->GetChildAt(i, getter_AddRefs(item)) == NS_OK && item) {
+        if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
+          FlushLayoutForTree(win);
+        }
       }
     }
   }
 }
 
 void nsContentUtils::RemoveNewlines(nsString &aString)
 {
   aString.StripCRLF();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6431,55 +6431,16 @@ nsDocument::CustomElementConstructor(JSC
 
   // The prototype setup happens in Element::WrapObject().
   nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
   NS_ENSURE_SUCCESS(rv, true);
 
   return true;
 }
 
-bool
-nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
-{
-  if (!nsContentUtils::IsWebComponentsEnabled()) {
-    return false;
-  }
-
-  JS::Rooted<JSObject*> obj(aCx, aObject);
-
-  JSAutoCompartment ac(aCx, obj);
-  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
-  nsCOMPtr<nsPIDOMWindowInner> window =
-    do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
-
-  nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
-  if (doc && doc->IsStyledByServo()) {
-    NS_WARNING("stylo: Web Components not supported yet");
-    return false;
-  }
-
-  return true;
-}
-
-bool
-nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo)
-{
-  if (!nsContentUtils::IsWebComponentsEnabled()) {
-    return false;
-  }
-
-  nsIDocument* doc = aNodeInfo->GetDocument();
-  if (doc->IsStyledByServo()) {
-    NS_WARNING("stylo: Web Components not supported yet");
-    return false;
-  }
-
-  return true;
-}
-
 void
 nsDocument::ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
 {
   mLazySVGPresElements.PutEntry(aSVG);
 }
 
 void
 nsDocument::UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1186,22 +1186,16 @@ protected:
 private:
   void UpdatePossiblyStaleDocumentState();
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
 public:
   virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
     GetCustomElementRegistry() override;
 
-  // Check whether web components are enabled for the global of aObject.
-  static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
-  // Check whether web components are enabled for the global of the document
-  // this nodeinfo comes from.
-  static bool IsWebComponentsEnabled(mozilla::dom::NodeInfo* aNodeInfo);
-
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
   RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -27,17 +27,17 @@ NS_NewHTMLContentElement(already_AddRefe
                          mozilla::dom::FromParser aFromParser)
 {
   // When this check is removed, remove the nsDocument.h and
   // HTMLUnknownElement.h includes.  Also remove nsINode::IsHTMLContentElement.
   //
   // We have to jump through some hoops to be able to produce both NodeInfo* and
   // already_AddRefed<NodeInfo>& for our callees.
   RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
-  if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
+  if (!nsContentUtils::IsWebComponentsEnabled()) {
     already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
     return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
   }
 
   already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
   return new mozilla::dom::HTMLContentElement(nodeInfoArg);
 }
 
--- a/dom/html/HTMLSlotElement.cpp
+++ b/dom/html/HTMLSlotElement.cpp
@@ -11,17 +11,17 @@
 #include "nsDocument.h"
 
 nsGenericHTMLElement*
 NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                       mozilla::dom::FromParser aFromParser)
 {
   RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
   /* Disabled for now
-  if (nsDocument::IsWebComponentsEnabled(nodeInfo)) {
+  if (nsContentUtils::IsWebComponentsEnabled()) {
     already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
     return new mozilla::dom::HTMLSlotElement(nodeInfoArg);
   }
   */
 
   already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
   return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
 }
@@ -59,9 +59,9 @@ HTMLSlotElement::AssignedNodes(const Ass
 
 JSObject*
 HTMLSlotElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLSlotElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
-} // namespace mozilla
\ No newline at end of file
+} // namespace mozilla
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3060,19 +3060,21 @@ ContentChild::GetBrowserOrId(TabChild* a
 mozilla::ipc::IPCResult
 ContentChild::RecvUpdateWindow(const uintptr_t& aChildId)
 {
 #if defined(XP_WIN)
   NS_ASSERTION(aChildId, "Expected child hwnd value for remote plugin instance.");
   mozilla::plugins::PluginInstanceParent* parentInstance =
   mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(aChildId);
   if (parentInstance) {
-  // sync! update call to the plugin instance that forces the
-  // plugin to paint its child window.
-  parentInstance->CallUpdateWindow();
+    // sync! update call to the plugin instance that forces the
+    // plugin to paint its child window.
+    if(!parentInstance->CallUpdateWindow()) {
+      return IPC_FAIL_NO_REASON(this);
+    }
   }
   return IPC_OK();
 #else
   MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
   return IPC_FAIL_NO_REASON(this);
 #endif
 }
 
--- a/dom/media/platforms/agnostic/AOMDecoder.cpp
+++ b/dom/media/platforms/agnostic/AOMDecoder.cpp
@@ -188,18 +188,18 @@ AOMDecoder::ProcessDecode(MediaRawData* 
   aom_codec_iter_t iter = nullptr;
   aom_image_t *img;
   UniquePtr<aom_image_t, AomImageFree> img8;
   DecodedData results;
 
   while ((img = aom_codec_get_frame(&mCodec, &iter))) {
     // Track whether the underlying buffer is 8 or 16 bits per channel.
     bool highbd = bool(img->fmt & AOM_IMG_FMT_HIGHBITDEPTH);
-    if (img->bit_depth > 8) {
-      // Downsample images with more than 8 significant bits per channel.
+    if (highbd) {
+      // Downsample images with more than 8 bits per channel.
       aom_img_fmt_t fmt8 = static_cast<aom_img_fmt_t>(img->fmt ^ AOM_IMG_FMT_HIGHBITDEPTH);
       img8.reset(aom_img_alloc(NULL, fmt8, img->d_w, img->d_h, 16));
       if (img8 == nullptr) {
         LOG("Couldn't allocate bitdepth reduction target!");
         return DecodePromise::CreateAndReject(
           MediaResult(NS_ERROR_OUT_OF_MEMORY,
                       RESULT_DETAIL("Couldn't allocate conversion buffer for AV1 frame")),
                       __func__);
--- a/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -93,22 +93,22 @@
       helper.drawColor(canvas, upleft, {offsetX: 0, offsetY: 0});
       helper.drawColor(canvas, upright, {offsetX: 10, offsetY: 0});
       helper.drawColor(canvas, downleft, {offsetX: 0, offsetY: 10});
       helper.drawColor(canvas, downright, {offsetX: 10, offsetY: 10});
     };
     let helper = new CaptureStreamTestHelper2D(1, 1);
 
     await new Promise((resolve, reject) => {
-      document.onfullscreenchange = resolve;
-      document.onfullscreenerror = () => reject(new Error("fullscreenerror"));
+      SpecialPowers.wrap(document).onfullscreenchange = resolve;
+      SpecialPowers.wrap(document).onfullscreenerror = () => reject(new Error("fullscreenerror"));
 
       // Note that going fullscreen requires the tab (and window) to be in the
       // foreground and having focus.
-      canvas.requestFullscreen();
+      SpecialPowers.wrap(canvas).requestFullscreen();
     });
 
     info("Testing screenshare without constraints");
     let stream = await getUserMedia({
       video: {mediaSource: "screen"},
       fake: false,
     });
     draw(helper.red, helper.blue,
@@ -169,14 +169,14 @@
          helper.blue, helper.red);
     await playback.verifyPlaying(); // still playing
     await verifyScreenshare(testVideo, helper,
                             helper.grey, helper.green,
                             helper.blue, helper.red);
     await playback.deprecatedStopStreamInMediaPlayback();
     playback.detachFromMediaElement();
 
-    document.exitFullscreen();
+    SpecialPowers.wrap(document).exitFullscreen();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/plugins/base/npapi.h
+++ b/dom/plugins/base/npapi.h
@@ -559,17 +559,17 @@ typedef EventRecord NPEvent;
 #endif
 #elif defined(XP_SYMBIAN)
 typedef QEvent NPEvent;
 #elif defined(XP_WIN)
 typedef struct _NPEvent
 {
   uint16_t event;
   uintptr_t wParam;
-  uintptr_t lParam;
+  intptr_t lParam;
 } NPEvent;
 #elif defined(XP_UNIX) && defined(MOZ_X11)
 typedef XEvent NPEvent;
 #else
 typedef void*  NPEvent;
 #endif
 
 #if defined(XP_MACOSX)
old mode 100644
new mode 100755
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3304,20 +3304,24 @@ PluginInstanceChild::UpdateWindowAttribu
 #endif // XP_MACOSX
 
 #ifdef XP_WIN
     // Windowless plugins on Windows need a WM_WINDOWPOSCHANGED event to update
     // their location... or at least Flash does: Silverlight uses the
     // window.x/y passed to NPP_SetWindow
 
     if (mPluginIface->event) {
+        // width and height are stored as units, but narrow to ints here
+        MOZ_RELEASE_ASSERT(mWindow.width <= INT_MAX);
+        MOZ_RELEASE_ASSERT(mWindow.height <= INT_MAX);
+
         WINDOWPOS winpos = {
             0, 0,
             mWindow.x, mWindow.y,
-            mWindow.width, mWindow.height,
+            (int32_t)mWindow.width, (int32_t)mWindow.height,
             0
         };
         NPEvent pluginEvent = {
             WM_WINDOWPOSCHANGED, 0,
             (LPARAM) &winpos
         };
         mPluginIface->event(&mData, &pluginEvent);
     }
@@ -3374,17 +3378,17 @@ PluginInstanceChild::PaintRectToPlatform
         mWindow.x + aRect.x,
         mWindow.y + aRect.y,
         mWindow.x + aRect.XMost(),
         mWindow.y + aRect.YMost()
     };
     NPEvent paintEvent = {
         WM_PAINT,
         uintptr_t(mWindow.window),
-        uintptr_t(&rect)
+        intptr_t(&rect)
     };
 
     ::SetViewportOrgEx((HDC) mWindow.window, -mWindow.x, -mWindow.y, nullptr);
     ::SelectClipRgn((HDC) mWindow.window, nullptr);
     ::IntersectClipRect((HDC) mWindow.window, rect.left, rect.top, rect.right, rect.bottom);
     mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
 #else
     MOZ_CRASH("Surface type not implemented.");
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -535,17 +535,17 @@ PluginModuleChromeParent::OnProcessLaunc
     }
 #endif
 
 #if defined(XP_WIN) && defined(_X86_)
     // Protected mode only applies to Windows and only to x86.
     if (!mIsBlocklisted && mIsFlashPlugin &&
         (Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false) ||
          mSandboxLevel >= 2)) {
-        SendDisableFlashProtectedMode();
+        Unused << SendDisableFlashProtectedMode();
     }
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
     Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 #endif
 }
 
@@ -2430,17 +2430,20 @@ PluginModuleParent::NPP_NewInternal(NPMI
 #ifdef XP_WIN
         bool supportsAsyncRender =
           Preferences::GetBool("dom.ipc.plugins.asyncdrawing.enabled", false);
         bool supportsForceDirect =
           Preferences::GetBool("dom.ipc.plugins.forcedirect.enabled", false);
         if (supportsAsyncRender) {
           // Prefs indicates we want async plugin rendering, make sure
           // the flash module has support.
-          CallModuleSupportsAsyncRender(&supportsAsyncRender);
+          if(!CallModuleSupportsAsyncRender(&supportsAsyncRender)) {
+            *error = NPERR_GENERIC_ERROR;
+            return NS_ERROR_FAILURE;
+          }
         }
 #ifdef _WIN64
         // For 64-bit builds force windowless if the flash library doesn't support
         // async rendering regardless of sandbox level.
         if (!supportsAsyncRender) {
 #else
         // For 32-bit builds force windowless if the flash library doesn't support
         // async rendering and the sandbox level is 2 or greater.
--- a/dom/plugins/ipc/PluginUtilsWin.cpp
+++ b/dom/plugins/ipc/PluginUtilsWin.cpp
@@ -30,17 +30,19 @@ public:
   NS_IMETHOD Run() override
   {
     StaticMutexAutoLock lock(sMutex);
     PLUGIN_LOG_DEBUG(("Notifying %d plugins of audio device change.",
                                             mAudioNotificationSet->Count()));
 
     for (auto iter = mAudioNotificationSet->ConstIter(); !iter.Done(); iter.Next()) {
       PluginModuleParent* pluginModule = iter.Get()->GetKey();
-      pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails);
+      if(!pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails)) {
+        return NS_ERROR_FAILURE;
+      }
     }
     return NS_OK;
   }
 
 protected:
   NPAudioDeviceChangeDetailsIPC mChangeDetails;
   const PluginModuleSet* mAudioNotificationSet;
 };
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -409,17 +409,17 @@ var interfaceNamesInGlobalScope =
     "HTMLBRElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLButtonElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLCanvasElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLCollection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "HTMLContentElement", stylo: false},
+    "HTMLContentElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLDataElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLDataListElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLDetailsElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "HTMLDialogElement", disabled: true},
@@ -507,17 +507,17 @@ var interfaceNamesInGlobalScope =
     "HTMLProgressElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLQuoteElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLScriptElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSelectElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "HTMLSlotElement", stylo: false},
+    "HTMLSlotElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSourceElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSpanElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLStyleElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLTableCaptionElement",
@@ -839,17 +839,17 @@ var interfaceNamesInGlobalScope =
     "ServiceWorkerContainer",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerRegistration",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ScopedCredential", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ScopedCredentialInfo", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ShadowRoot", stylo: false}, // Bogus, but the test harness forces it on.  See bug 1159768.
+    "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SharedWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleGestureEvent", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleTest", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SourceBuffer",
@@ -1295,17 +1295,16 @@ function createInterfaceMap(isXBLScope) 
   var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
   var isNightly = version.endsWith("a1");
   var isRelease = !version.includes("a");
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isMac = /Mac OS/.test(navigator.oscpu);
   var isWindows = /Windows/.test(navigator.oscpu);
   var isAndroid = navigator.userAgent.includes("Android");
   var isLinux = /Linux/.test(navigator.oscpu) && !isAndroid;
-  var isStylo = SpecialPowers.DOMWindowUtils.isStyledByServo;
   var isSecureContext = window.isSecureContext;
 
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
@@ -1315,17 +1314,16 @@ function createInterfaceMap(isXBLScope) 
         if ((entry.nightly === !isNightly) ||
             (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
             (entry.xbl === !isXBLScope) ||
             (entry.desktop === !isDesktop) ||
             (entry.windows === !isWindows) ||
             (entry.mac === !isMac) ||
             (entry.linux === !isLinux) ||
             (entry.android === !isAndroid && !entry.nightlyAndroid) ||
-            (entry.stylo === !isStylo) ||
             (entry.release === !isRelease) ||
             (entry.releaseNonWindowsAndMac === !(isRelease && !isWindows && !isMac)) ||
             (entry.isSecureContext === !isSecureContext) ||
             entry.disabled) {
           interfaceMap[entry.name] = false;
         } else {
           interfaceMap[entry.name] = true;
         }
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,73 +1,57 @@
 [DEFAULT]
 support-files =
   inert_style.css
   dummy_page.html
 
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_bug1176757.html]
-skip-if = stylo # bug 1293844
 [test_bug1276240.html]
 [test_content_element.html]
-skip-if = stylo # bug 1293844
 [test_custom_element_adopt_callbacks.html]
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_clone_callbacks_extended.html]
 [test_custom_element_htmlconstructor.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
 [test_custom_element_import_node_created_callback.html]
 [test_custom_element_in_shadow.html]
-skip-if = true || stylo # disabled - See bug 1390396 and 1293844
+skip-if = true # disabled - See bug 1390396
 [test_custom_element_register_invalid_callbacks.html]
 [test_custom_element_get.html]
 [test_custom_element_when_defined.html]
 [test_custom_element_upgrade.html]
 support-files =
   test_upgrade_page.html
   upgrade_tests.js
 [test_nested_content_element.html]
-skip-if = stylo # bug 1293844
 [test_dest_insertion_points.html]
-skip-if = stylo # bug 1293844
 [test_fallback_dest_insertion_points.html]
-skip-if = stylo # bug 1293844
 [test_detached_style.html]
-skip-if = stylo # bug 1293844
 [test_dynamic_content_element_matching.html]
-skip-if = stylo # bug 1293844
 [test_document_adoptnode.html]
-skip-if = stylo # bug 1293844
 [test_document_importnode.html]
-skip-if = stylo # bug 1293844
 [test_document_register.html]
 [test_document_register_base_queue.html]
 [test_document_register_lifecycle.html]
 skip-if = true # disabled - See bug 1390396
 [test_document_register_parser.html]
 [test_document_register_stack.html]
 skip-if = true # disabled - See bug 1390396
 [test_document_shared_registry.html]
 [test_event_retarget.html]
-skip-if = stylo # bug 1293844
 [test_event_stopping.html]
-skip-if = stylo # bug 1293844
 [test_template.html]
 [test_template_xhtml.html]
 [test_template_custom_elements.html]
 [test_shadowroot.html]
-skip-if = stylo # bug 1293844
 [test_shadowroot_inert_element.html]
-skip-if = stylo # bug 1293844
 [test_shadowroot_style.html]
-skip-if = stylo # bug 1293844
 [test_shadowroot_style_order.html]
-skip-if = stylo # bug 1293844
 [test_style_fallback_content.html]
-skip-if = stylo # bug 1293844
+skip-if = stylo # bug 1409088
 [test_unresolved_pseudo_class.html]
 [test_link_prefetch.html]
 [test_bug1269155.html]
-skip-if = stylo # bug 1293844
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthnCoseIdentifiers.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_WebAuthnCoseIdentifiers_h
+#define mozilla_dom_WebAuthnCoseIdentifiers_h
+
+#include "mozilla/dom/WebCryptoCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+// From https://www.iana.org/assignments/cose/cose.xhtml#algorithms
+enum class CoseAlgorithmIdentifier : int32_t {
+  ES256 = -7
+};
+
+static nsresult
+CoseAlgorithmToWebCryptoId(const int32_t& aId, /* out */ nsString& aName)
+{
+  switch(static_cast<CoseAlgorithmIdentifier>(aId)) {
+    case CoseAlgorithmIdentifier::ES256:
+      aName.AssignLiteral(JWK_ALG_ECDSA_P_256);
+      break;
+    default:
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebAuthnCoseIdentifiers_h
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -3,26 +3,26 @@
 /* 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/. */
 
 #include "hasht.h"
 #include "nsICryptoHash.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
+#include "WebAuthnCoseIdentifiers.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/AuthenticatorAttestationResponse.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PWebAuthnTransaction.h"
 #include "mozilla/dom/U2FUtil.h"
 #include "mozilla/dom/WebAuthnCBORUtil.h"
 #include "mozilla/dom/WebAuthnManager.h"
 #include "mozilla/dom/WebAuthnTransactionChild.h"
 #include "mozilla/dom/WebAuthnUtil.h"
-#include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
@@ -48,38 +48,16 @@ NS_NAMED_LITERAL_STRING(kVisibilityChang
 
 NS_IMPL_ISUPPORTS(WebAuthnManager, nsIIPCBackgroundChildCreateCallback,
                   nsIDOMEventListener);
 
 /***********************************************************************
  * Utility Functions
  **********************************************************************/
 
-template<class OOS>
-static nsresult
-GetAlgorithmName(const OOS& aAlgorithm,
-                 /* out */ nsString& aName)
-{
-  if (aAlgorithm.IsString()) {
-    // If string, then treat as algorithm name
-    aName.Assign(aAlgorithm.GetAsString());
-  } else {
-    // TODO: Coerce to string and extract name. See WebCryptoTask.cpp
-  }
-
-  // Only ES256 is currently supported
-  if (NORMALIZED_EQUALS(aName, JWK_ALG_ECDSA_P_256)) {
-    aName.AssignLiteral(JWK_ALG_ECDSA_P_256);
-  } else {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  return NS_OK;
-}
-
 static nsresult
 AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
                    /* out */ nsACString& aJsonOut)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString challengeBase64;
   nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
@@ -360,85 +338,50 @@ WebAuthnManager::MakeCredential(nsPIDOMW
   }
 
   srv = HashCString(hashService, rpId, rpIdHash);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
-  // Process each element of cryptoParameters using the following steps, to
-  // produce a new sequence normalizedParameters.
-  nsTArray<PublicKeyCredentialParameters> normalizedParams;
+
+  // TODO: Move this logic into U2FTokenManager in Bug 1409220.
+
+  // Process each element of mPubKeyCredParams using the following steps, to
+  // produce a new sequence acceptableParams.
+  nsTArray<PublicKeyCredentialParameters> acceptableParams;
   for (size_t a = 0; a < aOptions.mPubKeyCredParams.Length(); ++a) {
     // Let current be the currently selected element of
-    // cryptoParameters.
+    // mPubKeyCredParams.
 
     // If current.type does not contain a PublicKeyCredentialType
     // supported by this implementation, then stop processing current and move
-    // on to the next element in cryptoParameters.
+    // on to the next element in mPubKeyCredParams.
     if (aOptions.mPubKeyCredParams[a].mType != PublicKeyCredentialType::Public_key) {
       continue;
     }
 
-    // Let normalizedAlgorithm be the result of normalizing an algorithm using
-    // the procedure defined in [WebCryptoAPI], with alg set to
-    // current.algorithm and op set to 'generateKey'. If an error occurs during
-    // this procedure, then stop processing current and move on to the next
-    // element in cryptoParameters.
-
     nsString algName;
-    if (NS_FAILED(GetAlgorithmName(aOptions.mPubKeyCredParams[a].mAlg,
-                                   algName))) {
+    if (NS_FAILED(CoseAlgorithmToWebCryptoId(aOptions.mPubKeyCredParams[a].mAlg,
+                                             algName))) {
       continue;
     }
 
-    // Add a new object of type PublicKeyCredentialParameters to
-    // normalizedParameters, with type set to current.type and algorithm set to
-    // normalizedAlgorithm.
-    PublicKeyCredentialParameters normalizedObj;
-    normalizedObj.mType = aOptions.mPubKeyCredParams[a].mType;
-    normalizedObj.mAlg.SetAsString().Assign(algName);
-
-    if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
+    if (!acceptableParams.AppendElement(aOptions.mPubKeyCredParams[a],
+                                        mozilla::fallible)){
       promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
       return promise.forget();
     }
   }
 
-  // If normalizedAlgorithm is empty and cryptoParameters was not empty, cancel
+  // If acceptableParams is empty and mPubKeyCredParams was not empty, cancel
   // the timer started in step 2, reject promise with a DOMException whose name
   // is "NotSupportedError", and terminate this algorithm.
-  if (normalizedParams.IsEmpty() && !aOptions.mPubKeyCredParams.IsEmpty()) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return promise.forget();
-  }
-
-  // TODO: The following check should not be here. This is checking for
-  // parameters specific to the soft key, and should be put in the soft key
-  // manager in the parent process. Still need to serialize
-  // PublicKeyCredentialParameters first.
-
-  // Check if at least one of the specified combinations of
-  // PublicKeyCredentialParameters and cryptographic parameters is supported. If
-  // not, return an error code equivalent to NotSupportedError and terminate the
-  // operation.
-
-  bool isValidCombination = false;
-
-  for (size_t a = 0; a < normalizedParams.Length(); ++a) {
-    if (normalizedParams[a].mType == PublicKeyCredentialType::Public_key &&
-        normalizedParams[a].mAlg.IsString() &&
-        normalizedParams[a].mAlg.GetAsString().EqualsLiteral(
-          JWK_ALG_ECDSA_P_256)) {
-      isValidCombination = true;
-      break;
-    }
-  }
-  if (!isValidCombination) {
+  if (acceptableParams.IsEmpty() && !aOptions.mPubKeyCredParams.IsEmpty()) {
     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return promise.forget();
   }
 
   // If excludeList is undefined, set it to the empty list.
   //
   // If extensions was specified, process any extensions supported by this
   // client platform, to produce the extension data that needs to be sent to the
deleted file mode 100644
--- a/dom/webauthn/WebAuthnRequest.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef mozilla_dom_WebAuthnAsync_h
-#define mozilla_dom_WebAuthnAsync_h
-
-#include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/SharedThreadPool.h"
-#include "mozilla/TimeStamp.h"
-
-namespace mozilla {
-namespace dom {
-
-//extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
-
-// WebAuthnRequest tracks the completion of a single WebAuthn request that
-// may run on multiple kinds of authenticators, and be subject to a deadline.
-template<class Success>
-class WebAuthnRequest {
-public:
-  WebAuthnRequest()
-    : mCancelled(false)
-    , mSuccess(false)
-    , mCountTokens(0)
-    , mTokensFailed(0)
-    , mReentrantMonitor("WebAuthnRequest")
-  {}
-
-  void AddActiveToken(const char* aCallSite)
-  {
-    // MOZ_LOG(gWebauthLog, LogLevel::Debug,
-    //        ("WebAuthnRequest is tracking a new token, called from [%s]",
-    //         aCallSite));
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    mCountTokens += 1;
-  }
-
-  bool IsComplete()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    return mCancelled || mSuccess ||
-      (mCountTokens > 0 && mTokensFailed == mCountTokens);
-  }
-
-  void CancelNow()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause CancelNow to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mCancelled = true;
-    mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-  }
-
-  void SetFailure(nsresult aError)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause SetFailure to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mTokensFailed += 1;
-    MOZ_ASSERT(mTokensFailed <= mCountTokens);
-
-    if (mTokensFailed == mCountTokens) {
-      // Provide the final error as being indicitive of the whole set.
-      mPromise.Reject(aError, __func__);
-    }
-  }
-
-  void SetSuccess(const Success& aResult)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause multiple calls to SetSuccess
-    // in succession. We will only select the earliest.
-    if (IsComplete()) {
-      return;
-    }
-
-    mSuccess = true;
-    mPromise.Resolve(aResult, __func__);
-  }
-
-  void SetDeadline(TimeDuration aDeadline)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    // TODO: Monitor the deadline and stop with a timeout error if it expires.
-  }
-
-  RefPtr<MozPromise<Success, nsresult, false>> Ensure()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    return mPromise.Ensure(__func__);
-  }
-
-  void CompleteTask()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    if (mCountTokens == 0) {
-      // Special case for there being no tasks to complete
-      mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-    }
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
-
-private:
-  ~WebAuthnRequest() {};
-
-  bool mCancelled;
-  bool mSuccess;
-  int mCountTokens;
-  int mTokensFailed;
-  ReentrantMonitor mReentrantMonitor;
-  MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthnAsync_h
--- a/dom/webauthn/moz.build
+++ b/dom/webauthn/moz.build
@@ -17,17 +17,16 @@ EXPORTS.mozilla.dom += [
     'AuthenticatorResponse.h',
     'PublicKeyCredential.h',
     'U2FHIDTokenManager.h',
     'U2FSoftTokenManager.h',
     'U2FTokenManager.h',
     'U2FTokenTransport.h',
     'WebAuthnCBORUtil.h',
     'WebAuthnManager.h',
-    'WebAuthnRequest.h',
     'WebAuthnTransactionChild.h',
     'WebAuthnTransactionParent.h',
     'WebAuthnUtil.h',
 ]
 
 UNIFIED_SOURCES += [
     'AuthenticatorAssertionResponse.cpp',
     'AuthenticatorAttestationResponse.cpp',
--- a/dom/webauthn/tests/browser/tab_webauthn_success.html
+++ b/dom/webauthn/tests/browser/tab_webauthn_success.html
@@ -33,17 +33,17 @@ function signalCompletion(aText) {
 }
 
 let gState = {};
 let makeCredentialOptions = {
   rp: {id: document.domain, name: "none", icon: "none"},
   user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
   challenge: gCredentialChallenge,
   timeout: 5000, // the minimum timeout is actually 15 seconds
-  pubKeyCredParams: [{type: "public-key", alg: "ES256"}],
+  pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
 };
 
 navigator.credentials.create({publicKey: makeCredentialOptions})
 .then(function (aNewCredentialInfo) {
   gState.credential = aNewCredentialInfo;
 
   return webAuthnDecodeCBORAttestation(aNewCredentialInfo.response.attestationObject);
 })
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -133,17 +133,17 @@ function() {
       console.log(aPublicKey, aSignedData, aAssertion.response.signature);
       return verifySignature(aPublicKey, aSignedData, aAssertion.response.signature);
     })
   }
 
   function testMakeCredential() {
     let rp = {id: document.domain, name: "none", icon: "none"};
     let user = {name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", alg: "ES256"};
+    let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
     let makeCredentialOptions = {
       rp: rp,
       user: user,
       challenge: gCredentialChallenge,
       pubKeyCredParams: [param]
     };
     credm.create({publicKey: makeCredentialOptions})
     .then(decodeCreatedCredential)
@@ -152,17 +152,17 @@ function() {
       ok(false, aReason);
       SimpleTest.finish();
     });
   }
 
   function testMakeDuplicate(aCredInfo) {
     let rp = {id: document.domain, name: "none", icon: "none"};
     let user = {name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", alg: "ES256"};
+    let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
     let makeCredentialOptions = {
       rp: rp,
       user: user,
       challenge: gCredentialChallenge,
       pubKeyCredParams: [param],
       excludeCredentials: [{type: "public-key", id: new Uint8Array(aCredInfo.rawId),
                      transports: ["usb"]}]
     };
--- a/dom/webauthn/tests/test_webauthn_make_credential.html
+++ b/dom/webauthn/tests/test_webauthn_make_credential.html
@@ -57,18 +57,18 @@
 
       let credm = navigator.credentials;
 
       let gCredentialChallenge = new Uint8Array(16);
       window.crypto.getRandomValues(gCredentialChallenge);
 
       let rp = {id: document.domain, name: "none", icon: "none"};
       let user = {id: new Uint8Array(64), name: "none", icon: "none", displayName: "none"};
-      let param = {type: "public-key", alg: "es256"};
-      let unsupportedParam = {type: "public-key", alg: "3DES"};
+      let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+      let unsupportedParam = {type: "public-key", alg: cose_alg_ECDSA_w_SHA512};
       let badParam = {type: "SimplePassword", alg: "MaxLength=2"};
 
       var testFuncs = [
         // Test basic good call
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
@@ -82,24 +82,25 @@
           let makeCredentialOptions = {
             challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
-        // Test without a parameter
+        // Test without any parameters; this is acceptable meaning the RP ID is
+        // happy to take any credential type
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: []
           };
           return credm.create({publicKey: makeCredentialOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectNotSupportedError);
+                      .then(arrivingHereIsGood)
+                      .catch(arrivingHereIsBad);
         },
 
         // Test without a parameter array at all
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge
           };
           return credm.create({publicKey: makeCredentialOptions})
--- a/dom/webauthn/tests/test_webauthn_no_token.html
+++ b/dom/webauthn/tests/test_webauthn_no_token.html
@@ -40,17 +40,17 @@ function() {
   let credentialId = new Uint8Array(128);
   window.crypto.getRandomValues(credentialId);
 
   testMakeCredential();
 
   function testMakeCredential() {
     let rp = {id: document.domain, name: "none", icon: "none"};
     let user = {name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", alg: "es256"};
+    let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
     let makeCredentialOptions = {
       rp: rp, user: user, challenge: credentialChallenge, pubKeyCredParams: [param]
     };
     credm.create({publicKey: makeCredentialOptions})
     .then(function(aResult) {
       ok(false, "Should have failed.");
       testAssertion();
     })
--- a/dom/webauthn/tests/test_webauthn_sameorigin.html
+++ b/dom/webauthn/tests/test_webauthn_sameorigin.html
@@ -56,17 +56,17 @@
       isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
       let credm = navigator.credentials;
 
       let chall = new Uint8Array(16);
       window.crypto.getRandomValues(chall);
 
       let user = {id: new Uint8Array(16), name: "none", icon: "none", displayName: "none"};
-      let param = {type: "public-key", alg: "Es256"};
+      let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
 
       var testFuncs = [
         function() {
           // Test basic good call
           let rp = {id: document.domain};
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
--- a/dom/webauthn/tests/u2futil.js
+++ b/dom/webauthn/tests/u2futil.js
@@ -1,16 +1,19 @@
 // Used by local_addTest() / local_completeTest()
 var _countCompletions = 0;
 var _expectedCompletions = 0;
 
 const flag_TUP = 0x01;
 const flag_UV = 0x04;
 const flag_AT = 0x40;
 
+const cose_alg_ECDSA_w_SHA256 = -7;
+const cose_alg_ECDSA_w_SHA512 = -36;
+
 function handleEventMessage(event) {
   if ("test" in event.data) {
     let summary = event.data.test + ": " + event.data.msg;
     log(event.data.status + ": " + summary);
     ok(event.data.status, summary);
   } else if ("done" in event.data) {
     SimpleTest.finish();
   } else {
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -232,21 +232,21 @@ partial interface Element {
   [Throws, Pure]
   Element?  querySelector(DOMString selectors);
   [Throws, Pure]
   NodeList  querySelectorAll(DOMString selectors);
 };
 
 // http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-element-interface
 partial interface Element {
-  [Throws,Func="nsDocument::IsWebComponentsEnabled"]
+  [Throws,Pref="dom.webcomponents.enabled"]
   ShadowRoot createShadowRoot();
-  [Func="nsDocument::IsWebComponentsEnabled"]
+  [Pref="dom.webcomponents.enabled"]
   NodeList getDestinationInsertionPoints();
-  [Func="nsDocument::IsWebComponentsEnabled"]
+  [Pref="dom.webcomponents.enabled"]
   readonly attribute ShadowRoot? shadowRoot;
 };
 
 Element implements ChildNode;
 Element implements NonDocumentTypeChildNode;
 Element implements ParentNode;
 Element implements Animatable;
 Element implements GeometryUtils;
--- a/dom/webidl/HTMLContentElement.webidl
+++ b/dom/webidl/HTMLContentElement.webidl
@@ -6,15 +6,15 @@
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[Func="nsDocument::IsWebComponentsEnabled"]
+[Pref="dom.webcomponents.enabled"]
 interface HTMLContentElement : HTMLElement
 {
   attribute DOMString select;
   NodeList getDistributedNodes();
 };
 
--- a/dom/webidl/HTMLSlotElement.webidl
+++ b/dom/webidl/HTMLSlotElement.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is
  * https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[Func="nsDocument::IsWebComponentsEnabled", Exposed=Window, HTMLConstructor]
+[Pref="dom.webcomponents.enabled", Exposed=Window, HTMLConstructor]
 interface HTMLSlotElement : HTMLElement {
   [CEReactions, SetterThrows] attribute DOMString name;
   sequence<Node> assignedNodes(optional AssignedNodesOptions options);
 };
 
 dictionary AssignedNodesOptions {
   boolean flatten = false;
-};
\ No newline at end of file
+};
--- a/dom/webidl/ShadowRoot.webidl
+++ b/dom/webidl/ShadowRoot.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Func="nsDocument::IsWebComponentsEnabled"]
+[Pref="dom.webcomponents.enabled"]
 interface ShadowRoot : DocumentFragment
 {
   Element? getElementById(DOMString elementId);
   HTMLCollection getElementsByTagName(DOMString localName);
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   HTMLCollection getElementsByClassName(DOMString classNames);
   [CEReactions, SetterThrows, TreatNullAs=EmptyString]
   attribute DOMString innerHTML;
--- a/dom/webidl/WebAuthentication.webidl
+++ b/dom/webidl/WebAuthentication.webidl
@@ -36,17 +36,17 @@ interface AuthenticatorAttestationRespon
 interface AuthenticatorAssertionResponse : AuthenticatorResponse {
     [SameObject] readonly attribute ArrayBuffer      authenticatorData;
     [SameObject] readonly attribute ArrayBuffer      signature;
     readonly attribute DOMString                     userId;
 };
 
 dictionary PublicKeyCredentialParameters {
     required PublicKeyCredentialType  type;
-    required WebAuthnAlgorithmID      alg; // Switch to COSE in Bug 1381190
+    required COSEAlgorithmIdentifier  alg;
 };
 
 dictionary MakePublicKeyCredentialOptions {
     required PublicKeyCredentialRpEntity   rp;
     required PublicKeyCredentialUserEntity user;
 
     required BufferSource                            challenge;
     required sequence<PublicKeyCredentialParameters> pubKeyCredParams;
@@ -119,11 +119,9 @@ enum AuthenticatorTransport {
     "nfc",
     "ble"
 };
 
 typedef long COSEAlgorithmIdentifier;
 
 typedef sequence<AAGUID>      AuthenticatorSelectionList;
 
-typedef BufferSource      AAGUID;
-
-typedef (boolean or DOMString) WebAuthnAlgorithmID; // Switch to COSE in Bug 1381190
+typedef BufferSource      AAGUID;
\ No newline at end of file
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -107,19 +107,24 @@ nsXBLPrototypeResources::FlushSkinSheets
     else {
       newSheet = oldSheet;
     }
 
     mStyleSheetList.AppendElement(newSheet);
   }
 
   if (doc->IsStyledByServo()) {
-    MOZ_ASSERT(doc->GetShell());
-    MOZ_ASSERT(doc->GetShell()->GetPresContext());
-    ComputeServoStyleSet(doc->GetShell()->GetPresContext());
+    // There may be no shell during unlink.
+    //
+    // FIXME(emilio): We shouldn't skip shadow root style updates just because?
+    // Though during unlink is fine I guess...
+    if (auto* shell = doc->GetShell()) {
+      MOZ_ASSERT(shell->GetPresContext());
+      ComputeServoStyleSet(shell->GetPresContext());
+    }
   } else {
     GatherRuleProcessor();
   }
 
   return NS_OK;
 }
 
 nsresult
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -124,16 +124,41 @@ static const char* kPreloadPermissions[]
   "csp_report",
   "xslt",
   "beacon",
   "fetch",
   "image",
   "manifest"
 };
 
+// A list of permissions that can have a fallback default permission
+// set under the permissions.default.* pref.
+static const char* kPermissionsWithDefaults[] = {
+  "camera",
+  "microphone",
+  "geo",
+  "desktop-notification"
+};
+
+// NOTE: nullptr can be passed as aType - if it is this function will return
+// "false" unconditionally.
+bool
+HasDefaultPref(const char* aType)
+{
+  if (aType) {
+    for (const char* perm : kPermissionsWithDefaults) {
+      if (!strcmp(aType, perm)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 // NOTE: nullptr can be passed as aType - if it is this function will return
 // "false" unconditionally.
 bool
 IsPreloadPermission(const char* aType)
 {
   if (aType) {
     for (uint32_t i = 0; i < mozilla::ArrayLength(kPreloadPermissions); ++i) {
       if (!strcmp(aType, kPreloadPermissions[i])) {
@@ -928,16 +953,23 @@ nsPermissionManager::GetXPCOMSingleton()
 
 nsresult
 nsPermissionManager::Init()
 {
   // If the 'permissions.memory_only' pref is set to true, then don't write any
   // permission settings to disk, but keep them in a memory-only database.
   mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
 
+  nsresult rv;
+  nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = prefService->GetBranch("permissions.default.", getter_AddRefs(mDefaultPrefBranch));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (IsChildProcess()) {
     // Stop here; we don't need the DB in the child process. Instead we will be
     // sent permissions as we need them by our parent process.
     return NS_OK;
   }
 
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
@@ -2227,16 +2259,27 @@ nsPermissionManager::CommonTestPermissio
   if (aPrincipal && nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     *aPermission = nsIPermissionManager::ALLOW_ACTION;
     return NS_OK;
   }
 
   // Set the default.
   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
 
+  // For some permissions, query the default from a pref. We want to avoid
+  // doing this for all permissions so that permissions can opt into having
+  // the pref lookup overhead on each call.
+  if (HasDefaultPref(aType)) {
+    int32_t defaultPermission = nsIPermissionManager::UNKNOWN_ACTION;
+    nsresult rv = mDefaultPrefBranch->GetIntPref(aType, &defaultPermission);
+    if (NS_SUCCEEDED(rv)) {
+      *aPermission = defaultPermission;
+    }
+  }
+
   // For expanded principals, we want to iterate over the whitelist and see
   // if the permission is granted for any of them.
   auto* basePrin = BasePrincipal::Cast(aPrincipal);
   if (basePrin && basePrin->Is<ExpandedPrincipal>()) {
     auto ep = basePrin->As<ExpandedPrincipal>();
     for (auto& prin : ep->WhiteList()) {
       uint32_t perm;
       nsresult rv = CommonTestPermission(prin, aType, &perm,
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -11,16 +11,17 @@
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsPermission.h"
+#include "nsIPrefBranch.h"
 #include "nsHashKeys.h"
 #include "nsCOMArray.h"
 #include "nsDataHashtable.h"
 #include "nsIRunnable.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/MozPromise.h"
 
 namespace mozilla {
@@ -384,16 +385,18 @@ private:
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.
   bool mIsShuttingDown;
 
+  nsCOMPtr<nsIPrefBranch> mDefaultPrefBranch;
+
   friend class DeleteFromMozHostListener;
   friend class CloseDatabaseListener;
 };
 
 // {4F6B5E00-0C36-11d5-A535-0010A401EB10}
 #define NS_PERMISSIONMANAGER_CID \
 { 0x4f6b5e00, 0xc36, 0x11d5, { 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 } }
 
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_default_pref.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+  let uri = Services.io.newURI("https://example.org");
+
+  // Check that without a pref the default return value is UNKNOWN.
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.UNKNOWN_ACTION);
+
+  // Check that the default return value changed after setting the pref.
+  Services.prefs.setIntPref("permissions.default.camera", Services.perms.DENY_ACTION);
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.DENY_ACTION);
+
+  // Check that functions that do not directly return a permission value still
+  // consider the permission as being set to its default.
+  do_check_null(Services.perms.getPermissionObjectForURI(uri, "camera", false));
+
+  // Check that other permissions still return UNKNOWN.
+  do_check_eq(Services.perms.testPermission(uri, "geo"), Services.perms.UNKNOWN_ACTION);
+
+  // Check that the default return value changed after changing the pref.
+  Services.prefs.setIntPref("permissions.default.camera", Services.perms.ALLOW_ACTION);
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.ALLOW_ACTION);
+
+  // Check that the preference is ignored if there is a value.
+  Services.perms.add(uri, "camera", Services.perms.DENY_ACTION);
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.DENY_ACTION);
+  do_check_true(Services.perms.getPermissionObjectForURI(uri, "camera", false) != null);
+
+  // The preference should be honored again, after resetting the permissions.
+  Services.perms.removeAll();
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.ALLOW_ACTION);
+
+  // Should be UNKNOWN after clearing the pref.
+  Services.prefs.clearUserPref("permissions.default.camera");
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.UNKNOWN_ACTION);
+}
--- a/extensions/cookie/test/unit/xpcshell.ini
+++ b/extensions/cookie/test/unit/xpcshell.ini
@@ -11,16 +11,17 @@ skip-if = true # Bug 863738
 [test_cookies_privatebrowsing.js]
 [test_cookies_profile_close.js]
 [test_cookies_read.js]
 [test_cookies_sync_failure.js]
 [test_cookies_thirdparty.js]
 [test_cookies_thirdparty_session.js]
 [test_domain_eviction.js]
 [test_eviction.js]
+[test_permmanager_default_pref.js]
 [test_permmanager_defaults.js]
 [test_permmanager_expiration.js]
 [test_permmanager_getAllForURI.js]
 [test_permmanager_getPermissionObject.js]
 [test_permmanager_notifications.js]
 [test_permmanager_removeall.js]
 [test_permmanager_removesince.js]
 [test_permmanager_removeforapp.js]
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -684,17 +684,17 @@ DisableAdvancedLayers(FeatureStatus aSta
   if (!al.IsEnabled()) {
     return;
   }
 
   al.SetFailed(aStatus, aMessage.get(), aFailureId);
 
   FeatureFailure info(aStatus, aMessage, aFailureId);
   if (GPUParent* gpu = GPUParent::GetSingleton()) {
-    gpu->SendUpdateFeature(Feature::ADVANCED_LAYERS, info);
+    Unused << gpu->SendUpdateFeature(Feature::ADVANCED_LAYERS, info);
   }
 
   if (aFailureId.Length()) {
     nsString failureId = NS_ConvertUTF8toUTF16(aFailureId.get());
     Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_ADVANCED_LAYERS_FAILURE_ID, failureId, 1);
   }
 
   // Notify TelemetryEnvironment.jsm.
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,3 @@
 org.gradle.parallel=true
 org.gradle.daemon=true
+org.gradle.jvmargs=-Xmx2560M
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
 #Fri Sep 16 15:41:50 PDT 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
-distributionSha256Sum=88a910cdf2e03ebbb5fe90f7ecf534fc9ac22e12112dc9a2fee810c598a76091
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
+distributionSha256Sum=ed7e9c8bb41bd10d4c9339c95b2f8b122f5bf13188bd90504a26e0f00b123b0d
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -579,17 +579,17 @@ asserts(543) load 1015844.html # bug 574
 pref(font.size.inflation.minTwips,200) load 1032450.html
 load 1032613-1.svg
 load 1032613-2.html
 load 1037903.html
 load 1039454-1.html
 load 1042489.html
 load 1054010-1.html
 load 1058954-1.html
-asserts-if(!stylo,0-2) pref(dom.webcomponents.enabled,true) load 1059138-1.html # bug 1389936
+asserts-if(!stylo,0-2) skip-if(stylo) pref(dom.webcomponents.enabled,true) load 1059138-1.html # bug 1389936 for non-stylo, bug 1409136 for stylo.
 load 1134531.html
 load 1134667.html
 load 1137723-1.html
 load 1137723-2.html
 load 1140268-1.html
 load 1145768.html
 load 1145931.html
 load 1146103.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1852,17 +1852,17 @@ fuzzy-if(skiaContent,1,24000) == 1025914
 test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.html
 == 1059167-1.html 1059167-1-ref.html
 == 1059498-1.html 1059498-1-ref.html
 == 1059498-2.html 1059498-1-ref.html
 == 1059498-3.html 1059498-1-ref.html
 == 1062108-1.html 1062108-1-ref.html
 == 1062792-1.html 1062792-1-ref.html
 == 1062963-floatmanager-reflow.html 1062963-floatmanager-reflow-ref.html
-test-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == 1066554-1.html 1066554-1-ref.html
+test-pref(dom.webcomponents.enabled,true) == 1066554-1.html 1066554-1-ref.html
 == 1069716-1.html 1069716-1-ref.html
 random-if(webrender) == 1078262-1.html about:blank
 test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html
 == 1081185-1.html 1081185-1-ref.html
 == 1097437-1.html 1097437-1-ref.html
 == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test
 == 1105137-1.html 1105137-1-ref.html
 fuzzy-if(d2d,36,304) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&d2d,127,701) HTTP(..) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
--- a/layout/reftests/css-display/reftest.list
+++ b/layout/reftests/css-display/reftest.list
@@ -12,17 +12,17 @@ fails-if(styloVsGecko||stylo) pref(layou
 fuzzy-if(winWidget,12,100) skip-if(styloVsGecko||stylo) pref(layout.css.scoped-style.enabled,true) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
 == display-contents-tables.xhtml display-contents-tables-ref.xhtml
 == display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
 == display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
 == display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
 == display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
 == display-contents-495385-2d.html display-contents-495385-2d-ref.html
 fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl-ref.html
-fuzzy-if(Android,7,1186) fails-if(stylo||styloVsGecko) pref(dom.webcomponents.enabled,true) pref(layout.css.scoped-style.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
+fuzzy-if(Android,7,1186) skip-if(stylo||styloVsGecko) pref(dom.webcomponents.enabled,true) pref(layout.css.scoped-style.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html # stylo: bug 1409086
 fails-if(styloVsGecko) == display-contents-xbl-2.xul display-contents-xbl-2-ref.xul # bug 1408235
 fails-if(styloVsGecko) == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul # bug 1408235
 skip == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
 asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
 fails-if(styloVsGecko) == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul # bug 1408235
 fails-if(!stylo) == display-contents-xbl-6.xhtml display-contents-xbl-6-ref.html # bug 1345809
 == display-contents-xbl-7.xhtml display-contents-xbl-7-ref.html
 == display-contents-list-item-child.html display-contents-list-item-child-ref.html
--- a/layout/reftests/forms/legend/reftest.list
+++ b/layout/reftests/forms/legend/reftest.list
@@ -1,4 +1,4 @@
 == legend.html legend-ref.html
-fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == shadow-dom.html shadow-dom-ref.html
+fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == shadow-dom.html shadow-dom-ref.html # stylo: bug 1409086
 == 1273433.html 1273433-ref.html
 fails-if(styloVsGecko||stylo) == 1339287.html 1339287-ref.html
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -362,17 +362,17 @@ fuzzy-if(OSX,1,100) fuzzy-if(skiaContent
 == mfrac-C-2.html mfrac-C-2-ref.html
 == mfrac-C-3.html mfrac-C-3-ref.html
 == mfrac-C-4.html mfrac-C-4-ref.html
 fuzzy-if(OSX,1,100) fuzzy-if(skiaContent,1,14) == mfrac-D-1.html mfrac-D-1-ref.html
 == mfrac-D-2.html mfrac-D-2-ref.html
 == mfrac-D-3.html mfrac-D-3-ref.html
 == mfrac-D-4.html mfrac-D-4-ref.html
 == mfrac-E-1.html mfrac-E-1-ref.html
-test-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == shadow-dom-1.html shadow-dom-1-ref.html
+test-pref(dom.webcomponents.enabled,true) == shadow-dom-1.html shadow-dom-1-ref.html
 pref(dom.meta-viewport.enabled,true) pref(font.size.inflation.emPerLine,25) == font-inflation-1.html font-inflation-1-ref.html
 test-pref(font.minimum-size.x-math,40) == default-font.html default-font-ref.html
 != radicalbar-1.html about:blank
 != radicalbar-1a.html about:blank
 != radicalbar-1b.html about:blank
 != radicalbar-1c.html about:blank
 fails-if(webrender) != radicalbar-1d.html about:blank
 != radicalbar-2.html about:blank
--- a/layout/reftests/webcomponents/reftest.list
+++ b/layout/reftests/webcomponents/reftest.list
@@ -1,15 +1,15 @@
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-shadow-1.html basic-shadow-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-shadow-2.html basic-shadow-2-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-shadow-3.html basic-shadow-3-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-shadow-4.html basic-shadow-4-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == basic-insertion-point-2.html basic-insertion-point-2-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == fallback-content-1.html fallback-content-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
-pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) fails-if(stylo||styloVsGecko) == input-transition-1.html input-transition-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+pref(dom.webcomponents.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
+pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
+pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
+pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
+pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
+pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
+pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-2-ref.html
+pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == fallback-content-1.html fallback-content-1-ref.html # stylo: bug 1409088
+pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
+pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
+pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
+pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
+pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
+pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html # bug 1409136
+pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -105,17 +105,17 @@ load 894245-1.html
 load 915440.html
 load 927734-1.html
 load 930270-1.html
 load 930270-2.html
 load 945048-1.html
 load 972199-1.html
 load 989965-1.html
 load 992333-1.html
-pref(dom.webcomponents.enabled,true) load 1017798-1.html # bug 1323689
+pref(dom.webcomponents.enabled,true) skip-if(stylo) load 1017798-1.html # bug 1409086
 load 1028514-1.html
 load 1066089-1.html
 load 1074651-1.html
 load 1135534.html
 pref(dom.webcomponents.enabled,true) load 1089463-1.html
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
 load 1153693-1.html
new file mode 100644
--- /dev/null
+++ b/layout/tools/reftest/globals.jsm
@@ -0,0 +1,160 @@
+/* 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";
+
+this.EXPORTED_SYMBOLS = [];
+
+for (let [key, val] of Object.entries({
+  /* Constants */
+  XHTML_NS: "http://www.w3.org/1999/xhtml",
+  XUL_NS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+
+  NS_LOCAL_FILE_CONTRACTID: "@mozilla.org/file/local;1",
+  NS_GFXINFO_CONTRACTID: "@mozilla.org/gfx/info;1",
+  IO_SERVICE_CONTRACTID: "@mozilla.org/network/io-service;1",
+  DEBUG_CONTRACTID: "@mozilla.org/xpcom/debug;1",
+  NS_DIRECTORY_SERVICE_CONTRACTID: "@mozilla.org/file/directory_service;1",
+  NS_OBSERVER_SERVICE_CONTRACTID: "@mozilla.org/observer-service;1",
+
+  TYPE_REFTEST_EQUAL: '==',
+  TYPE_REFTEST_NOTEQUAL: '!=',
+  TYPE_LOAD: 'load',     // test without a reference (just test that it does
+                         // not assert, crash, hang, or leak)
+  TYPE_SCRIPT: 'script', // test contains individual test results
+  TYPE_PRINT: 'print',   // test and reference will be printed to PDF's and
+                         // compared structurally
+
+  // The order of these constants matters, since when we have a status
+  // listed for a *manifest*, we combine the status with the status for
+  // the test by using the *larger*.
+  // FIXME: In the future, we may also want to use this rule for combining
+  // statuses that are on the same line (rather than making the last one
+  // win).
+  EXPECTED_PASS: 0,
+  EXPECTED_FAIL: 1,
+  EXPECTED_RANDOM: 2,
+  EXPECTED_DEATH: 3,     // test must be skipped to avoid e.g. crash/hang
+  EXPECTED_FUZZY: 4,
+
+  // types of preference value we might want to set for a specific test
+  PREF_BOOLEAN: 0,
+  PREF_STRING: 1,
+  PREF_INTEGER: 2,
+
+  FOCUS_FILTER_ALL_TESTS: "all",
+  FOCUS_FILTER_NEEDS_FOCUS_TESTS: "needs-focus",
+  FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS: "non-needs-focus",
+
+  // "<!--CLEAR-->"
+  BLANK_URL_FOR_CLEARING: "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E",
+
+  /* Globals */
+  g: {
+    loadTimeout: 0,
+    timeoutHook: null,
+    remote: false,
+    ignoreWindowSize: false,
+    shuffle: false,
+    repeat: null,
+    runUntilFailure: false,
+    cleanupPendingCrashes: false,
+    totalChunks: 0,
+    thisChunk: 0,
+    containingWindow: null,
+    urlFilterRegex: {},
+    contentGfxInfo: null,
+    focusFilterMode: "all",
+    compareStyloToGecko: false,
+
+    browser: undefined,
+    // Are we testing web content loaded in a separate process?
+    browserIsRemote: undefined,        // bool
+    // Are we using <iframe mozbrowser>?
+    browserIsIframe: undefined,        // bool
+    browserMessageManager: undefined,  // bool
+    canvas1: undefined,
+    canvas2: undefined,
+    // gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next
+    // RecordResult.
+    currentCanvas: null,
+    urls: undefined,
+    // Map from URI spec to the number of times it remains to be used
+    uriUseCounts: undefined,
+    // Map from URI spec to the canvas rendered for that URI
+    uriCanvases: undefined,
+    testResults: {
+      // Successful...
+      Pass: 0,
+      LoadOnly: 0,
+      // Unexpected...
+      Exception: 0,
+      FailedLoad: 0,
+      UnexpectedFail: 0,
+      UnexpectedPass: 0,
+      AssertionUnexpected: 0,
+      AssertionUnexpectedFixed: 0,
+      // Known problems...
+      KnownFail : 0,
+      AssertionKnown: 0,
+      Random : 0,
+      Skip: 0,
+      Slow: 0,
+    },
+    totalTests: 0,
+    state: undefined,
+    currentURL: undefined,
+    testLog: [],
+    logLevel: undefined,
+    logFile: null,
+    logger: undefined,
+    server: undefined,
+    count: 0,
+    assertionCount: 0,
+
+    ioService: undefined,
+    debug: undefined,
+    windowUtils: undefined,
+
+    slowestTestTime: 0,
+    slowestTestURL: undefined,
+    failedUseWidgetLayers: false,
+
+    drawWindowFlags: undefined,
+
+    expectingProcessCrash: false,
+    expectedCrashDumpFiles: [],
+    unexpectedCrashDumpFiles: {},
+    crashDumpDir: undefined,
+    pendingCrashDumpDir: undefined,
+    failedNoPaint: false,
+    failedOpaqueLayer: false,
+    failedOpaqueLayerMessages: [],
+    failedAssignedLayer: false,
+    failedAssignedLayerMessages: [],
+
+    startAfter: undefined,
+    suiteStarted: false,
+
+    // The enabled-state of the test-plugins, stored so they can be reset later
+    testPluginEnabledStates: null,
+    prefsToRestore: [],
+    httpServerPort: -1,
+
+    // whether to run slow tests or not
+    runSlowTests: true,
+
+    // whether we should skip caching canvases
+    noCanvasCache: false,
+    recycledCanvases: new Array(),
+    testPrintOutput: null,
+
+    manifestsLoaded: {},
+    // Only dump the sandbox once, because it doesn't depend on the
+    // manifest URL (yet!).
+    dumpedConditionSandbox: false,
+  }
+})) {
+  this[key] = val;
+  this.EXPORTED_SYMBOLS.push(key);
+}
--- a/layout/tools/reftest/jar.mn
+++ b/layout/tools/reftest/jar.mn
@@ -1,8 +1,10 @@
 reftest.jar:
 % content reftest %content/
+  content/globals.jsm (globals.jsm)
   content/reftest-content.js (reftest-content.js)
   content/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
   content/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
   content/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
+*  content/manifest.jsm (manifest.jsm)
 *  content/reftest.jsm (reftest.jsm)
   content/reftest.xul (reftest.xul)
new file mode 100644
--- /dev/null
+++ b/layout/tools/reftest/manifest.jsm
@@ -0,0 +1,717 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- /
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* 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";
+
+this.EXPORTED_SYMBOLS = ["ReadTopManifest"];
+
+var CC = Components.classes;
+const CI = Components.interfaces;
+const CU = Components.utils;
+
+CU.import("chrome://reftest/content/globals.jsm", this);
+CU.import("chrome://reftest/content/reftest.jsm", this);
+CU.import("resource://gre/modules/Services.jsm");
+CU.import("resource://gre/modules/NetUtil.jsm");
+
+const NS_SCRIPTSECURITYMANAGER_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1";
+const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = "@mozilla.org/network/protocol;1?name=";
+const NS_XREAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
+
+const RE_PROTOCOL = /^\w+:/;
+const RE_PREF_ITEM = /^(|test-|ref-)pref\((.+?),(.*)\)$/;
+
+
+function ReadTopManifest(aFileURL, aFilter)
+{
+    var url = g.ioService.newURI(aFileURL);
+    if (!url)
+        throw "Expected a file or http URL for the manifest.";
+
+    g.manifestsLoaded = {};
+    ReadManifest(url, EXPECTED_PASS, aFilter);
+}
+
+// Note: If you materially change the reftest manifest parsing,
+// please keep the parser in print-manifest-dirs.py in sync.
+function ReadManifest(aURL, inherited_status, aFilter)
+{
+    // Ensure each manifest is only read once. This assumes that manifests that are
+    // included with an unusual inherited_status or filters will be read via their
+    // include before they are read directly in the case of a duplicate
+    if (g.manifestsLoaded.hasOwnProperty(aURL.spec)) {
+        if (g.manifestsLoaded[aURL.spec] === null)
+            return;
+        else
+            aFilter = [aFilter[0], aFilter[1], true];
+    }
+    g.manifestsLoaded[aURL.spec] = aFilter[1];
+
+    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
+                     .getService(CI.nsIScriptSecurityManager);
+
+    var listURL = aURL;
+    var channel = NetUtil.newChannel({uri: aURL, loadUsingSystemPrincipal: true});
+    var inputStream = channel.open2();
+    if (channel instanceof Components.interfaces.nsIHttpChannel
+        && channel.responseStatus != 200) {
+      g.logger.error("HTTP ERROR : " + channel.responseStatus);
+    }
+    var streamBuf = getStreamContent(inputStream);
+    inputStream.close();
+    var lines = streamBuf.split(/\n|\r|\r\n/);
+
+    // Build the sandbox for fails-if(), etc., condition evaluation.
+    var sandbox = BuildConditionSandbox(aURL);
+    var lineNo = 0;
+    var urlprefix = "";
+    var defaultTestPrefSettings = [], defaultRefPrefSettings = [];
+    if (g.compareStyloToGecko) {
+        AddStyloTestPrefs(sandbox, defaultTestPrefSettings,
+                          defaultRefPrefSettings);
+    }
+    for (var str of lines) {
+        ++lineNo;
+        if (str.charAt(0) == "#")
+            continue; // entire line was a comment
+        var i = str.search(/\s+#/);
+        if (i >= 0)
+            str = str.substring(0, i);
+        // strip leading and trailing whitespace
+        str = str.replace(/^\s*/, '').replace(/\s*$/, '');
+        if (!str || str == "")
+            continue;
+        var items = str.split(/\s+/); // split on whitespace
+
+        if (items[0] == "url-prefix") {
+            if (items.length != 2)
+                throw "url-prefix requires one url in manifest file " + aURL.spec + " line " + lineNo;
+            urlprefix = items[1];
+            continue;
+        }
+
+        if (items[0] == "default-preferences") {
+            var m;
+            var item;
+            defaultTestPrefSettings = [];
+            defaultRefPrefSettings = [];
+            items.shift();
+            while ((item = items.shift())) {
+                if (!(m = item.match(RE_PREF_ITEM))) {
+                    throw "Unexpected item in default-preferences list in manifest file " + aURL.spec + " line " + lineNo;
+                }
+                if (!AddPrefSettings(m[1], m[2], m[3], sandbox, defaultTestPrefSettings, defaultRefPrefSettings)) {
+                    throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo;
+                }
+            }
+            if (g.compareStyloToGecko) {
+                AddStyloTestPrefs(sandbox, defaultTestPrefSettings,
+                                  defaultRefPrefSettings);
+            }
+            continue;
+        }
+
+        var expected_status = EXPECTED_PASS;
+        var allow_silent_fail = false;
+        var minAsserts = 0;
+        var maxAsserts = 0;
+        var needs_focus = false;
+        var slow = false;
+        var testPrefSettings = defaultTestPrefSettings.concat();
+        var refPrefSettings = defaultRefPrefSettings.concat();
+        var fuzzy_delta = { min: 0, max: 2 };
+        var fuzzy_pixels = { min: 0, max: 1 };
+        var chaosMode = false;
+
+        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode)/)) {
+            var item = items.shift();
+            var stat;
+            var cond;
+            var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
+            if (m) {
+                stat = m[1];
+                // Note: m[2] contains the parentheses, and we want them.
+                cond = Components.utils.evalInSandbox(m[2], sandbox);
+            } else if (item.match(/^(fails|random|skip)$/)) {
+                stat = item;
+                cond = true;
+            } else if (item == "needs-focus") {
+                needs_focus = true;
+                cond = false;
+            } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
+                cond = false;
+                minAsserts = Number(m[1]);
+                maxAsserts = (m[2] == undefined) ? minAsserts
+                                                 : Number(m[2].substring(1));
+            } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
+                cond = false;
+                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
+                    minAsserts = Number(m[2]);
+                    maxAsserts =
+                      (m[3] == undefined) ? minAsserts
+                                          : Number(m[3].substring(1));
+                }
+            } else if (item == "slow") {
+                cond = false;
+                slow = true;
+            } else if ((m = item.match(/^require-or\((.*?)\)$/))) {
+                var args = m[1].split(/,/);
+                if (args.length != 2) {
+                    throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": wrong number of args to require-or";
+                }
+                var [precondition_str, fallback_action] = args;
+                var preconditions = precondition_str.split(/&&/);
+                cond = false;
+                for (var precondition of preconditions) {
+                    if (precondition === "debugMode") {
+                        // Currently unimplemented. Requires asynchronous
+                        // JSD call + getting an event while no JS is running
+                        stat = fallback_action;
+                        cond = true;
+                        break;
+                    } else if (precondition === "true") {
+                        // For testing
+                    } else {
+                        // Unknown precondition. Assume it is unimplemented.
+                        stat = fallback_action;
+                        cond = true;
+                        break;
+                    }
+                }
+            } else if ((m = item.match(/^slow-if\((.*?)\)$/))) {
+                cond = false;
+                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox))
+                    slow = true;
+            } else if (item == "silentfail") {
+                cond = false;
+                allow_silent_fail = true;
+            } else if ((m = item.match(RE_PREF_ITEM))) {
+                cond = false;
+                if (!AddPrefSettings(m[1], m[2], m[3], sandbox, testPrefSettings, refPrefSettings)) {
+                    throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo;
+                }
+            } else if ((m = item.match(/^fuzzy\((\d+)(-\d+)?,(\d+)(-\d+)?\)$/))) {
+              cond = false;
+              expected_status = EXPECTED_FUZZY;
+              fuzzy_delta = ExtractRange(m, 1);
+              fuzzy_pixels = ExtractRange(m, 3);
+            } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+)(-\d+)?,(\d+)(-\d+)?\)$/))) {
+              cond = false;
+              if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
+                expected_status = EXPECTED_FUZZY;
+                fuzzy_delta = ExtractRange(m, 2);
+                fuzzy_pixels = ExtractRange(m, 4);
+              }
+            } else if (item == "chaos-mode") {
+                cond = false;
+                chaosMode = true;
+            } else {
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unexpected item " + item;
+            }
+
+            if (cond) {
+                if (stat == "fails") {
+                    expected_status = EXPECTED_FAIL;
+                } else if (stat == "random") {
+                    expected_status = EXPECTED_RANDOM;
+                } else if (stat == "skip") {
+                    expected_status = EXPECTED_DEATH;
+                } else if (stat == "silentfail") {
+                    allow_silent_fail = true;
+                }
+            }
+        }
+
+        expected_status = Math.max(expected_status, inherited_status);
+
+        if (minAsserts > maxAsserts) {
+            throw "Bad range in manifest file " + aURL.spec + " line " + lineNo;
+        }
+
+        var runHttp = false;
+        var httpDepth;
+        if (items[0] == "HTTP") {
+            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
+                                               // for non-local reftests.
+            httpDepth = 0;
+            items.shift();
+        } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) {
+            // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc.
+            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
+                                               // for non-local reftests.
+            httpDepth = (items[0].length - 5) / 3;
+            items.shift();
+        }
+
+        // do not prefix the url for include commands or urls specifying
+        // a protocol
+        if (urlprefix && items[0] != "include") {
+            if (items.length > 1 && !items[1].match(RE_PROTOCOL)) {
+                items[1] = urlprefix + items[1];
+            }
+            if (items.length > 2 && !items[2].match(RE_PROTOCOL)) {
+                items[2] = urlprefix + items[2];
+            }
+        }
+
+        var principal = secMan.createCodebasePrincipal(aURL, {});
+
+        if (items[0] == "include") {
+            if (items.length != 2)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to include";
+            if (runHttp)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": use of include with http";
+            var incURI = g.ioService.newURI(items[1], null, listURL);
+            secMan.checkLoadURIWithPrincipal(principal, incURI,
+                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+            ReadManifest(incURI, expected_status, aFilter);
+        } else if (items[0] == TYPE_LOAD) {
+            if (items.length != 2)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to load";
+            if (expected_status != EXPECTED_PASS &&
+                expected_status != EXPECTED_DEATH)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect known failure type for load test";
+            var [testURI] = runHttp
+                            ? ServeFiles(principal, httpDepth,
+                                         listURL, [items[1]])
+                            : [g.ioService.newURI(items[1], null, listURL)];
+            var prettyPath = runHttp
+                           ? g.ioService.newURI(items[1], null, listURL).spec
+                           : testURI.spec;
+            secMan.checkLoadURIWithPrincipal(principal, testURI,
+                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+            AddTestItem({ type: TYPE_LOAD,
+                          expected: expected_status,
+                          allowSilentFail: allow_silent_fail,
+                          prettyPath: prettyPath,
+                          minAsserts: minAsserts,
+                          maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
+                          slow: slow,
+                          prefSettings1: testPrefSettings,
+                          prefSettings2: refPrefSettings,
+                          fuzzyMinDelta: fuzzy_delta.min,
+                          fuzzyMaxDelta: fuzzy_delta.max,
+                          fuzzyMinPixels: fuzzy_pixels.min,
+                          fuzzyMaxPixels: fuzzy_pixels.max,
+                          url1: testURI,
+                          url2: null,
+                          chaosMode: chaosMode }, aFilter);
+        } else if (items[0] == TYPE_SCRIPT) {
+            if (items.length != 2)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to script";
+            var [testURI] = runHttp
+                            ? ServeFiles(principal, httpDepth,
+                                         listURL, [items[1]])
+                            : [g.ioService.newURI(items[1], null, listURL)];
+            var prettyPath = runHttp
+                           ? g.ioService.newURI(items[1], null, listURL).spec
+                           : testURI.spec;
+            secMan.checkLoadURIWithPrincipal(principal, testURI,
+                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+            AddTestItem({ type: TYPE_SCRIPT,
+                          expected: expected_status,
+                          allowSilentFail: allow_silent_fail,
+                          prettyPath: prettyPath,
+                          minAsserts: minAsserts,
+                          maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
+                          slow: slow,
+                          prefSettings1: testPrefSettings,
+                          prefSettings2: refPrefSettings,
+                          fuzzyMinDelta: fuzzy_delta.min,
+                          fuzzyMaxDelta: fuzzy_delta.max,
+                          fuzzyMinPixels: fuzzy_pixels.min,
+                          fuzzyMaxPixels: fuzzy_pixels.max,
+                          url1: testURI,
+                          url2: null,
+                          chaosMode: chaosMode }, aFilter);
+        } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL || items[0] == TYPE_PRINT) {
+            if (items.length != 3)
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + items[0];
+
+            if (items[0] == TYPE_REFTEST_NOTEQUAL &&
+                expected_status == EXPECTED_FUZZY &&
+                (fuzzy_delta.min > 0 || fuzzy_pixels.min > 0)) {
+                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": minimum fuzz must be zero for tests of type " + items[0];
+            }
+
+            var [testURI, refURI] = runHttp
+                                  ? ServeFiles(principal, httpDepth,
+                                               listURL, [items[1], items[2]])
+                                  : [g.ioService.newURI(items[1], null, listURL),
+                                     g.ioService.newURI(items[2], null, listURL)];
+            var prettyPath = runHttp
+                           ? g.ioService.newURI(items[1], null, listURL).spec
+                           : testURI.spec;
+            secMan.checkLoadURIWithPrincipal(principal, testURI,
+                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+            secMan.checkLoadURIWithPrincipal(principal, refURI,
+                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+            var type = items[0];
+            if (g.compareStyloToGecko) {
+                type = TYPE_REFTEST_EQUAL;
+                refURI = testURI;
+
+                // We expect twice as many assertion failures when running in
+                // styloVsGecko mode because we run each test twice: once in
+                // Stylo mode and once in Gecko mode.
+                minAsserts *= 2;
+                maxAsserts *= 2;
+
+                // Skip the test if it is expected to fail in both Stylo and
+                // Gecko modes. It would unexpectedly "pass" in styloVsGecko
+                // mode when comparing the two failures, which is not a useful
+                // result.
+                if (expected_status === EXPECTED_FAIL ||
+                    expected_status === EXPECTED_RANDOM) {
+                    expected_status = EXPECTED_DEATH;
+                }
+            }
+
+            AddTestItem({ type: type,
+                          expected: expected_status,
+                          allowSilentFail: allow_silent_fail,
+                          prettyPath: prettyPath,
+                          minAsserts: minAsserts,
+                          maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
+                          slow: slow,
+                          prefSettings1: testPrefSettings,
+                          prefSettings2: refPrefSettings,
+                          fuzzyMinDelta: fuzzy_delta.min,
+                          fuzzyMaxDelta: fuzzy_delta.max,
+                          fuzzyMinPixels: fuzzy_pixels.min,
+                          fuzzyMaxPixels: fuzzy_pixels.max,
+                          url1: testURI,
+                          url2: refURI,
+                          chaosMode: chaosMode }, aFilter);
+        } else {
+            throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unknown test type " + items[0];
+        }
+    }
+}
+
+// Read all available data from an input stream and return it
+// as a string.
+function getStreamContent(inputStream)
+{
+    var streamBuf = "";
+    var sis = CC["@mozilla.org/scriptableinputstream;1"].
+                  createInstance(CI.nsIScriptableInputStream);
+    sis.init(inputStream);
+
+    var available;
+    while ((available = sis.available()) != 0) {
+        streamBuf += sis.read(available);
+    }
+
+    return streamBuf;
+}
+
+// Build the sandbox for fails-if(), etc., condition evaluation.
+function BuildConditionSandbox(aURL) {
+    var sandbox = new Components.utils.Sandbox(aURL.spec);
+    var xr = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULRuntime);
+    var appInfo = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULAppInfo);
+    sandbox.isDebugBuild = g.debug.isDebugBuild;
+    var prefs = CC["@mozilla.org/preferences-service;1"].
+                getService(CI.nsIPrefBranch);
+    var env = CC["@mozilla.org/process/environment;1"].
+                getService(CI.nsIEnvironment);
+
+    sandbox.xulRuntime = CU.cloneInto({widgetToolkit: xr.widgetToolkit, OS: xr.OS, XPCOMABI: xr.XPCOMABI}, sandbox);
+
+    var testRect = g.browser.getBoundingClientRect();
+    sandbox.smallScreen = false;
+    if (g.containingWindow.innerWidth < 800 || g.containingWindow.innerHeight < 1000) {
+        sandbox.smallScreen = true;
+    }
+
+    var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
+    let readGfxInfo = function (obj, key) {
+      if (g.contentGfxInfo && (key in g.contentGfxInfo)) {
+        return g.contentGfxInfo[key];
+      }
+      return obj[key];
+    }
+
+    try {
+      sandbox.d2d = readGfxInfo(gfxInfo, "D2DEnabled");
+      sandbox.dwrite = readGfxInfo(gfxInfo, "DWriteEnabled");
+    } catch (e) {
+      sandbox.d2d = false;
+      sandbox.dwrite = false;
+    }
+
+    var info = gfxInfo.getInfo();
+    var canvasBackend = readGfxInfo(info, "AzureCanvasBackend");
+    var contentBackend = readGfxInfo(info, "AzureContentBackend");
+    var canvasAccelerated = readGfxInfo(info, "AzureCanvasAccelerated");
+
+    sandbox.gpuProcess = gfxInfo.usingGPUProcess;
+    sandbox.azureCairo = canvasBackend == "cairo";
+    sandbox.azureSkia = canvasBackend == "skia";
+    sandbox.skiaContent = contentBackend == "skia";
+    sandbox.azureSkiaGL = canvasAccelerated; // FIXME: assumes GL right now
+    // true if we are using the same Azure backend for rendering canvas and content
+    sandbox.contentSameGfxBackendAsCanvas = contentBackend == canvasBackend
+                                            || (contentBackend == "none" && canvasBackend == "cairo");
+
+    sandbox.layersGPUAccelerated =
+      g.windowUtils.layerManagerType != "Basic";
+    sandbox.d3d11 =
+      g.windowUtils.layerManagerType == "Direct3D 11";
+    sandbox.d3d9 =
+      g.windowUtils.layerManagerType == "Direct3D 9";
+    sandbox.layersOpenGL =
+      g.windowUtils.layerManagerType == "OpenGL";
+    sandbox.webrender =
+      g.windowUtils.layerManagerType == "WebRender";
+    sandbox.layersOMTC =
+      g.windowUtils.layerManagerRemote == true;
+    sandbox.advancedLayers =
+      g.windowUtils.usingAdvancedLayers == true;
+    sandbox.layerChecksEnabled = !sandbox.webrender;
+
+    // Shortcuts for widget toolkits.
+    sandbox.Android = xr.OS == "Android";
+    sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";
+    sandbox.gtkWidget = xr.widgetToolkit == "gtk2"
+                        || xr.widgetToolkit == "gtk3";
+    sandbox.qtWidget = xr.widgetToolkit == "qt";
+    sandbox.winWidget = xr.widgetToolkit == "windows";
+
+    // Scrollbars that are semi-transparent. See bug 1169666.
+    sandbox.transparentScrollbars = xr.widgetToolkit == "gtk3";
+
+    if (sandbox.Android) {
+        var sysInfo = CC["@mozilla.org/system-info;1"].getService(CI.nsIPropertyBag2);
+
+        // This is currently used to distinguish Android 4.0.3 (SDK version 15)
+        // and later from Android 2.x
+        sandbox.AndroidVersion = sysInfo.getPropertyAsInt32("version");
+    }
+
+#if MOZ_ASAN
+    sandbox.AddressSanitizer = true;
+#else
+    sandbox.AddressSanitizer = false;
+#endif
+
+#if MOZ_WEBRTC
+    sandbox.webrtc = true;
+#else
+    sandbox.webrtc = false;
+#endif
+
+#ifdef MOZ_STYLO
+    let styloEnabled = false;
+    // Perhaps a bit redundant in places, but this is easier to compare with the
+    // the real check in `nsLayoutUtils.cpp` to ensure they test the same way.
+    if (env.get("STYLO_FORCE_ENABLED")) {
+        styloEnabled = true;
+    } else if (env.get("STYLO_FORCE_DISABLED")) {
+        styloEnabled = false;
+    } else {
+        styloEnabled = prefs.getBoolPref("layout.css.servo.enabled", false);
+    }
+    sandbox.stylo = styloEnabled && !g.compareStyloToGecko;
+    sandbox.styloVsGecko = g.compareStyloToGecko;
+#else
+    sandbox.stylo = false;
+    sandbox.styloVsGecko = false;
+#endif
+
+// Printing via Skia PDF is only supported on Mac for now.
+#ifdef XP_MACOSX && MOZ_ENABLE_SKIA_PDF
+    sandbox.skiaPdf = true;
+#else
+    sandbox.skiaPdf = false;
+#endif
+
+#ifdef RELEASE_OR_BETA
+    sandbox.release_or_beta = true;
+#else
+    sandbox.release_or_beta = false;
+#endif
+
+    var hh = CC[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].
+                 getService(CI.nsIHttpProtocolHandler);
+    var httpProps = ["userAgent", "appName", "appVersion", "vendor",
+                     "vendorSub", "product", "productSub", "platform",
+                     "oscpu", "language", "misc"];
+    sandbox.http = new sandbox.Object();
+    httpProps.forEach((x) => sandbox.http[x] = hh[x]);
+
+    // Set OSX to be the Mac OS X version, as an integer, or undefined
+    // for other platforms.  The integer is formed by 100 times the
+    // major version plus the minor version, so 1006 for 10.6, 1010 for
+    // 10.10, etc.
+    var osxmatch = /Mac OS X (\d+).(\d+)$/.exec(hh.oscpu);
+    sandbox.OSX = osxmatch ? parseInt(osxmatch[1]) * 100 + parseInt(osxmatch[2]) : undefined;
+
+    // see if we have the test plugin available,
+    // and set a sandox prop accordingly
+    sandbox.haveTestPlugin = !sandbox.Android && !!getTestPlugin("Test Plug-in");
+
+    // Set a flag on sandbox if the windows default theme is active
+    sandbox.windowsDefaultTheme = g.containingWindow.matchMedia("(-moz-windows-default-theme)").matches;
+
+    try {
+        sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme");
+    } catch (e) {
+        sandbox.nativeThemePref = true;
+    }
+    sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled", false);
+
+    sandbox.prefs = CU.cloneInto({
+        getBoolPref: function(p) { return prefs.getBoolPref(p); },
+        getIntPref:  function(p) { return prefs.getIntPref(p); }
+    }, sandbox, { cloneFunctions: true });
+
+    // Tests shouldn't care about this except for when they need to
+    // crash the content process
+    sandbox.browserIsRemote = g.browserIsRemote;
+
+    try {
+        sandbox.asyncPan = g.containingWindow.document.docShell.asyncPanZoomEnabled;
+    } catch (e) {
+        sandbox.asyncPan = false;
+    }
+
+    // Graphics features
+    sandbox.usesRepeatResampling = sandbox.d2d;
+
+    if (!g.dumpedConditionSandbox) {
+        g.logger.info("Dumping JSON representation of sandbox");
+        g.logger.info(JSON.stringify(CU.waiveXrays(sandbox)));
+        g.dumpedConditionSandbox = true;
+    }
+
+    return sandbox;
+}
+
+function AddStyloTestPrefs(aSandbox, aTestPrefSettings, aRefPrefSettings) {
+    AddPrefSettings("test-", "layout.css.servo.enabled", "true", aSandbox,
+                    aTestPrefSettings, aRefPrefSettings);
+    AddPrefSettings("ref-", "layout.css.servo.enabled", "false", aSandbox,
+                    aTestPrefSettings, aRefPrefSettings);
+}
+
+function AddPrefSettings(aWhere, aPrefName, aPrefValExpression, aSandbox, aTestPrefSettings, aRefPrefSettings) {
+    var prefVal = Components.utils.evalInSandbox("(" + aPrefValExpression + ")", aSandbox);
+    var prefType;
+    var valType = typeof(prefVal);
+    if (valType == "boolean") {
+        prefType = PREF_BOOLEAN;
+    } else if (valType == "string") {
+        prefType = PREF_STRING;
+    } else if (valType == "number" && (parseInt(prefVal) == prefVal)) {
+        prefType = PREF_INTEGER;
+    } else {
+        return false;
+    }
+    var setting = { name: aPrefName,
+                    type: prefType,
+                    value: prefVal };
+
+    if (g.compareStyloToGecko && aPrefName != "layout.css.servo.enabled") {
+        // ref-pref() is ignored, test-pref() and pref() are added to both
+        if (aWhere != "ref-") {
+            aTestPrefSettings.push(setting);
+            aRefPrefSettings.push(setting);
+        }
+    } else {
+        if (aWhere != "ref-") {
+            aTestPrefSettings.push(setting);
+        }
+        if (aWhere != "test-") {
+            aRefPrefSettings.push(setting);
+        }
+    }
+    return true;
+}
+
+function ExtractRange(matches, startIndex, defaultMin = 0) {
+    if (matches[startIndex + 1] === undefined) {
+        return {
+            min: defaultMin,
+            max: Number(matches[startIndex])
+        };
+    }
+    return {
+        min: Number(matches[startIndex]),
+        max: Number(matches[startIndex + 1].substring(1))
+    };
+}
+
+function ServeFiles(manifestPrincipal, depth, aURL, files) {
+    var listURL = aURL.QueryInterface(CI.nsIFileURL);
+    var directory = listURL.file.parent;
+
+    // Allow serving a tree that's an ancestor of the directory containing
+    // the files so that they can use resources in ../ (etc.).
+    var dirPath = "/";
+    while (depth > 0) {
+        dirPath = "/" + directory.leafName + dirPath;
+        directory = directory.parent;
+        --depth;
+    }
+
+    g.count++;
+    var path = "/" + Date.now() + "/" + g.count;
+    g.server.registerDirectory(path + "/", directory);
+
+    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
+                     .getService(CI.nsIScriptSecurityManager);
+
+    var testbase = g.ioService.newURI("http://localhost:" + g.httpServerPort +
+                                     path + dirPath);
+
+    // Give the testbase URI access to XUL and XBL
+    Services.perms.add(testbase, "allowXULXBL", Services.perms.ALLOW_ACTION);
+
+    function FileToURI(file)
+    {
+        // Only serve relative URIs via the HTTP server, not absolute
+        // ones like about:blank.
+        var testURI = g.ioService.newURI(file, null, testbase);
+
+        // XXX necessary?  manifestURL guaranteed to be file, others always HTTP
+        secMan.checkLoadURIWithPrincipal(manifestPrincipal, testURI,
+                                         CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+
+        return testURI;
+    }
+
+    return files.map(FileToURI);
+}
+
+function AddTestItem(aTest, aFilter) {
+    if (!aFilter)
+        aFilter = [null, [], false];
+
+    var globalFilter = aFilter[0];
+    var manifestFilter = aFilter[1];
+    var invertManifest = aFilter[2];
+    if ((globalFilter && !globalFilter.test(aTest.url1.spec)) ||
+        (manifestFilter &&
+         !(invertManifest ^ manifestFilter.test(aTest.url1.spec))))
+        return;
+    if (g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS &&
+        !aTest.needsFocus)
+        return;
+    if (g.focusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS &&
+        aTest.needsFocus)
+        return;
+
+    if (aTest.url2 !== null)
+        aTest.identifier = [aTest.prettyPath, aTest.type, aTest.url2.spec];
+    else
+        aTest.identifier = aTest.prettyPath;
+
+    g.urls.push(aTest);
+}
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -1,46 +1,30 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- /
 /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["OnRefTestLoad", "OnRefTestUnload"];
+this.EXPORTED_SYMBOLS = [
+    "OnRefTestLoad",
+    "OnRefTestUnload",
+    "getTestPlugin"
+];
 
 var CC = Components.classes;
 const CI = Components.interfaces;
 const CR = Components.results;
 const CU = Components.utils;
 
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
-const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
-const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
-const DEBUG_CONTRACTID = "@mozilla.org/xpcom/debug;1";
-const NS_LOCALFILEINPUTSTREAM_CONTRACTID =
-          "@mozilla.org/network/file-input-stream;1";
-const NS_SCRIPTSECURITYMANAGER_CONTRACTID =
-          "@mozilla.org/scriptsecuritymanager;1";
-const NS_REFTESTHELPER_CONTRACTID =
-          "@mozilla.org/reftest-helper;1";
-const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX =
-          "@mozilla.org/network/protocol;1?name=";
-const NS_XREAPPINFO_CONTRACTID =
-          "@mozilla.org/xre/app-info;1";
-const NS_DIRECTORY_SERVICE_CONTRACTID =
-          "@mozilla.org/file/directory_service;1";
-const NS_OBSERVER_SERVICE_CONTRACTID =
-          "@mozilla.org/observer-service;1";
-
 CU.import("resource://gre/modules/FileUtils.jsm");
+CU.import("chrome://reftest/content/globals.jsm", this);
 CU.import("chrome://reftest/content/httpd.jsm", this);
+CU.import("chrome://reftest/content/manifest.jsm", this);
 CU.import("chrome://reftest/content/StructuredLog.jsm", this);
 CU.import("resource://gre/modules/Services.jsm");
 CU.import("resource://gre/modules/NetUtil.jsm");
 CU.import('resource://gre/modules/XPCOMUtils.jsm');
 
 XPCOMUtils.defineLazyGetter(this, "OS", function() {
     const { OS } = CU.import("resource://gre/modules/osfile.jsm");
     return OS;
@@ -49,225 +33,87 @@ XPCOMUtils.defineLazyGetter(this, "OS", 
 XPCOMUtils.defineLazyGetter(this, "PDFJS", function() {
     const { require } = CU.import("resource://gre/modules/commonjs/toolkit/require.js", {});
     return {
         main: require('resource://pdf.js/build/pdf.js'),
         worker: require('resource://pdf.js/build/pdf.worker.js')
     };
 });
 
-var gLoadTimeout = 0;
-var gTimeoutHook = null;
-var gRemote = false;
-var gIgnoreWindowSize = false;
-var gShuffle = false;
-var gRepeat = null;
-var gRunUntilFailure = false;
-var gCleanupPendingCrashes = false;
-var gTotalChunks = 0;
-var gThisChunk = 0;
-var gContainingWindow = null;
-var gURLFilterRegex = {};
-var gContentGfxInfo = null;
-const FOCUS_FILTER_ALL_TESTS = "all";
-const FOCUS_FILTER_NEEDS_FOCUS_TESTS = "needs-focus";
-const FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS = "non-needs-focus";
-var gFocusFilterMode = FOCUS_FILTER_ALL_TESTS;
-var gCompareStyloToGecko = false;
-
-// "<!--CLEAR-->"
-const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
-
-var gBrowser;
-// Are we testing web content loaded in a separate process?
-var gBrowserIsRemote;           // bool
-// Are we using <iframe mozbrowser>?
-var gBrowserIsIframe;           // bool
-var gBrowserMessageManager;
-var gCanvas1, gCanvas2;
-// gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next
-// RecordResult.
-var gCurrentCanvas = null;
-var gURLs;
-var gManifestsLoaded = {};
-// Map from URI spec to the number of times it remains to be used
-var gURIUseCounts;
-// Map from URI spec to the canvas rendered for that URI
-var gURICanvases;
-var gTestResults = {
-  // Successful...
-  Pass: 0,
-  LoadOnly: 0,
-  // Unexpected...
-  Exception: 0,
-  FailedLoad: 0,
-  UnexpectedFail: 0,
-  UnexpectedPass: 0,
-  AssertionUnexpected: 0,
-  AssertionUnexpectedFixed: 0,
-  // Known problems...
-  KnownFail : 0,
-  AssertionKnown: 0,
-  Random : 0,
-  Skip: 0,
-  Slow: 0,
-};
-var gTotalTests = 0;
-var gState;
-var gCurrentURL;
-var gTestLog = [];
-var gLogLevel;
-var gServer;
-var gCount = 0;
-var gAssertionCount = 0;
-
-var gIOService;
-var gDebug;
-var gWindowUtils;
-
-var gSlowestTestTime = 0;
-var gSlowestTestURL;
-var gFailedUseWidgetLayers = false;
-
-var gDrawWindowFlags;
-
-var gExpectingProcessCrash = false;
-var gExpectedCrashDumpFiles = [];
-var gUnexpectedCrashDumpFiles = { };
-var gCrashDumpDir;
-var gPendingCrashDumpDir;
-var gFailedNoPaint = false;
-var gFailedOpaqueLayer = false;
-var gFailedOpaqueLayerMessages = [];
-var gFailedAssignedLayer = false;
-var gFailedAssignedLayerMessages = [];
-
-var gStartAfter = undefined;
-var gSuiteStarted = false
-
-// The enabled-state of the test-plugins, stored so they can be reset later
-var gTestPluginEnabledStates = null;
-
-const TYPE_REFTEST_EQUAL = '==';
-const TYPE_REFTEST_NOTEQUAL = '!=';
-const TYPE_LOAD = 'load';     // test without a reference (just test that it does
-                              // not assert, crash, hang, or leak)
-const TYPE_SCRIPT = 'script'; // test contains individual test results
-const TYPE_PRINT = 'print'; // test and reference will be printed to PDF's and
-                            // compared structurally
-
-// The order of these constants matters, since when we have a status
-// listed for a *manifest*, we combine the status with the status for
-// the test by using the *larger*.
-// FIXME: In the future, we may also want to use this rule for combining
-// statuses that are on the same line (rather than making the last one
-// win).
-const EXPECTED_PASS = 0;
-const EXPECTED_FAIL = 1;
-const EXPECTED_RANDOM = 2;
-const EXPECTED_DEATH = 3;  // test must be skipped to avoid e.g. crash/hang
-const EXPECTED_FUZZY = 4;
-
-// types of preference value we might want to set for a specific test
-const PREF_BOOLEAN = 0;
-const PREF_STRING  = 1;
-const PREF_INTEGER = 2;
-
-var gPrefsToRestore = [];
-
-const gProtocolRE = /^\w+:/;
-const gPrefItemRE = /^(|test-|ref-)pref\((.+?),(.*)\)$/;
-
-var gHttpServerPort = -1;
-
-// whether to run slow tests or not
-var gRunSlowTests = true;
-
-// whether we should skip caching canvases
-var gNoCanvasCache = false;
-
-var gRecycledCanvases = new Array();
-
-// Only dump the sandbox once, because it doesn't depend on the
-// manifest URL (yet!).
-var gDumpedConditionSandbox = false;
-
-var gTestPrintOutput = null;
-
 function HasUnexpectedResult()
 {
-    return gTestResults.Exception > 0 ||
-           gTestResults.FailedLoad > 0 ||
-           gTestResults.UnexpectedFail > 0 ||
-           gTestResults.UnexpectedPass > 0 ||
-           gTestResults.AssertionUnexpected > 0 ||
-           gTestResults.AssertionUnexpectedFixed > 0;
+    return g.testResults.Exception > 0 ||
+           g.testResults.FailedLoad > 0 ||
+           g.testResults.UnexpectedFail > 0 ||
+           g.testResults.UnexpectedPass > 0 ||
+           g.testResults.AssertionUnexpected > 0 ||
+           g.testResults.AssertionUnexpectedFixed > 0;
 }
 
 // By default we just log to stdout
-var gLogFile = null;
 var gDumpFn = function(line) {
   dump(line);
-  if (gLogFile) {
-    gLogFile.write(line, line.length);
+  if (g.logFile) {
+    g.logFile.write(line, line.length);
   }
 }
 var gDumpRawLog = function(record) {
   // Dump JSON representation of data on a single line
   var line = "\n" + JSON.stringify(record) + "\n";
   dump(line);
 
-  if (gLogFile) {
-    gLogFile.write(line, line.length);
+  if (g.logFile) {
+    g.logFile.write(line, line.length);
   }
 }
-var logger = new StructuredLogger('reftest', gDumpRawLog);
+g.logger = new StructuredLogger('reftest', gDumpRawLog);
+var logger = g.logger;
 
 function TestBuffer(str)
 {
   logger.debug(str);
-  gTestLog.push(str);
+  g.testLog.push(str);
 }
 
 function FlushTestBuffer()
 {
   // In debug mode, we've dumped all these messages already.
-  if (gLogLevel !== 'debug') {
-    for (var i = 0; i < gTestLog.length; ++i) {
-      logger.info("Saved log: " + gTestLog[i]);
+  if (g.logLevel !== 'debug') {
+    for (var i = 0; i < g.testLog.length; ++i) {
+      logger.info("Saved log: " + g.testLog[i]);
     }
   }
-  gTestLog = [];
+  g.testLog = [];
 }
 
 function LogWidgetLayersFailure()
 {
   logger.error("USE_WIDGET_LAYERS disabled because the screen resolution is too low. This falls back to an alternate rendering path (that may not be representative) and is not implemented with e10s enabled.");
   logger.error("Consider increasing your screen resolution, or adding '--disable-e10s' to your './mach reftest' command");
 }
 
 function AllocateCanvas()
 {
-    if (gRecycledCanvases.length > 0) {
-        return gRecycledCanvases.shift();
+    if (g.recycledCanvases.length > 0) {
+        return g.recycledCanvases.shift();
     }
 
-    var canvas = gContainingWindow.document.createElementNS(XHTML_NS, "canvas");
-    var r = gBrowser.getBoundingClientRect();
+    var canvas = g.containingWindow.document.createElementNS(XHTML_NS, "canvas");
+    var r = g.browser.getBoundingClientRect();
     canvas.setAttribute("width", Math.ceil(r.width));
     canvas.setAttribute("height", Math.ceil(r.height));
 
     return canvas;
 }
 
 function ReleaseCanvas(canvas)
 {
     // store a maximum of 2 canvases, if we're not caching
-    if (!gNoCanvasCache || gRecycledCanvases.length < 2) {
-        gRecycledCanvases.push(canvas);
+    if (!g.noCanvasCache || g.recycledCanvases.length < 2) {
+        g.recycledCanvases.push(canvas);
     }
 }
 
 function IDForEventTarget(event)
 {
     try {
         return "'" + event.target.getAttribute('id') + "'";
     } catch (ex) {
@@ -286,82 +132,82 @@ function getTestPlugin(aName) {
   }
 
   logger.warning("Failed to find the test-plugin.");
   return null;
 }
 
 function OnRefTestLoad(win)
 {
-    gCrashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID]
+    g.crashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID]
                     .getService(CI.nsIProperties)
                     .get("ProfD", CI.nsIFile);
-    gCrashDumpDir.append("minidumps");
+    g.crashDumpDir.append("minidumps");
 
-    gPendingCrashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID]
+    g.pendingCrashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID]
                     .getService(CI.nsIProperties)
                     .get("UAppData", CI.nsIFile);
-    gPendingCrashDumpDir.append("Crash Reports");
-    gPendingCrashDumpDir.append("pending");
+    g.pendingCrashDumpDir.append("Crash Reports");
+    g.pendingCrashDumpDir.append("pending");
 
     var env = CC["@mozilla.org/process/environment;1"].
               getService(CI.nsIEnvironment);
 
     var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                 getService(Components.interfaces.nsIPrefBranch);
-    gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart", false);
+    g.browserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart", false);
 
-    gBrowserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled", false);
+    g.browserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled", false);
 
-    gLogLevel = prefs.getCharPref("reftest.logLevel", "info");
+    g.logLevel = prefs.getCharPref("reftest.logLevel", "info");
 
     if (win === undefined || win == null) {
       win = window;
     }
-    if (gContainingWindow == null && win != null) {
-      gContainingWindow = win;
+    if (g.containingWindow == null && win != null) {
+      g.containingWindow = win;
     }
 
-    if (gBrowserIsIframe) {
-      gBrowser = gContainingWindow.document.createElementNS(XHTML_NS, "iframe");
-      gBrowser.setAttribute("mozbrowser", "");
+    if (g.browserIsIframe) {
+      g.browser = g.containingWindow.document.createElementNS(XHTML_NS, "iframe");
+      g.browser.setAttribute("mozbrowser", "");
     } else {
-      gBrowser = gContainingWindow.document.createElementNS(XUL_NS, "xul:browser");
-      gBrowser.setAttribute("class", "lightweight");
+      g.browser = g.containingWindow.document.createElementNS(XUL_NS, "xul:browser");
+      g.browser.setAttribute("class", "lightweight");
     }
-    gBrowser.setAttribute("id", "browser");
-    gBrowser.setAttribute("type", "content");
-    gBrowser.setAttribute("primary", "true");
-    gBrowser.setAttribute("remote", gBrowserIsRemote ? "true" : "false");
+    g.browser.setAttribute("id", "browser");
+    g.browser.setAttribute("type", "content");
+    g.browser.setAttribute("primary", "true");
+    g.browser.setAttribute("remote", g.browserIsRemote ? "true" : "false");
     // Make sure the browser element is exactly 800x1000, no matter
     // what size our window is
-    gBrowser.setAttribute("style", "padding: 0px; margin: 0px; border:none; min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px");
+    g.browser.setAttribute("style", "padding: 0px; margin: 0px; border:none; min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px");
 
     if (Services.appinfo.OS == "Android") {
-      let doc = gContainingWindow.document.getElementById('main-window');
+      let doc = g.containingWindow.document.getElementById('main-window');
       while (doc.hasChildNodes()) {
         doc.firstChild.remove();
       }
-      doc.appendChild(gBrowser);
+      doc.appendChild(g.browser);
     } else {
-      document.getElementById("reftest-window").appendChild(gBrowser);
+      document.getElementById("reftest-window").appendChild(g.browser);
     }
 
     // reftests should have the test plugins enabled, not click-to-play
     let plugin1 = getTestPlugin("Test Plug-in");
     let plugin2 = getTestPlugin("Second Test Plug-in");
     if (plugin1 && plugin2) {
-      gTestPluginEnabledStates = [plugin1.enabledState, plugin2.enabledState];
+      g.testPluginEnabledStates = [plugin1.enabledState, plugin2.enabledState];
       plugin1.enabledState = CI.nsIPluginTag.STATE_ENABLED;
       plugin2.enabledState = CI.nsIPluginTag.STATE_ENABLED;
     } else {
       logger.warning("Could not get test plugin tags.");
     }
 
-    gBrowserMessageManager = gBrowser.frameLoader.messageManager;
+    g.browserMessageManager = g.browser.frameLoader.messageManager;
     // The content script waits for the initial onload, then notifies
     // us.
     RegisterMessageListenersAndLoadContentScript();
 }
 
 function InitAndStartRefTests()
 {
     /* These prefs are optional, so we don't need to spit an error to the log */
@@ -371,172 +217,171 @@ function InitAndStartRefTests()
     } catch(e) {
         logger.error("EXCEPTION: " + e);
     }
 
     try {
       prefs.setBoolPref("android.widget_paints_background", false);
     } catch (e) {}
 
-    /* set the gLoadTimeout */
+    /* set the g.loadTimeout */
     try {
-        gLoadTimeout = prefs.getIntPref("reftest.timeout");
+        g.loadTimeout = prefs.getIntPref("reftest.timeout");
     } catch(e) {
-        gLoadTimeout = 5 * 60 * 1000; //5 minutes as per bug 479518
+        g.loadTimeout = 5 * 60 * 1000; //5 minutes as per bug 479518
     }
 
     /* Get the logfile for android tests */
     try {
         var logFile = prefs.getCharPref("reftest.logFile");
         if (logFile) {
             var f = FileUtils.File(logFile);
-            gLogFile = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
+            g.logFile = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
         }
     } catch(e) {}
 
-    gRemote = prefs.getBoolPref("reftest.remote", false);
+    g.remote = prefs.getBoolPref("reftest.remote", false);
 
-    gIgnoreWindowSize = prefs.getBoolPref("reftest.ignoreWindowSize", false);
+    g.ignoreWindowSize = prefs.getBoolPref("reftest.ignoreWindowSize", false);
 
     /* Support for running a chunk (subset) of tests.  In separate try as this is optional */
     try {
-        gTotalChunks = prefs.getIntPref("reftest.totalChunks");
-        gThisChunk = prefs.getIntPref("reftest.thisChunk");
+        g.totalChunks = prefs.getIntPref("reftest.totalChunks");
+        g.thisChunk = prefs.getIntPref("reftest.thisChunk");
     }
     catch(e) {
-        gTotalChunks = 0;
-        gThisChunk = 0;
+        g.totalChunks = 0;
+        g.thisChunk = 0;
     }
 
     try {
-        gFocusFilterMode = prefs.getCharPref("reftest.focusFilterMode");
+        g.focusFilterMode = prefs.getCharPref("reftest.focusFilterMode");
     } catch(e) {}
 
     try {
-        gStartAfter = prefs.getCharPref("reftest.startAfter");
+        g.startAfter = prefs.getCharPref("reftest.startAfter");
     } catch(e) {
-        gStartAfter = undefined;
+        g.startAfter = undefined;
     }
 
 #ifdef MOZ_STYLO
     try {
-        gCompareStyloToGecko = prefs.getBoolPref("reftest.compareStyloToGecko");
+        g.compareStyloToGecko = prefs.getBoolPref("reftest.compareStyloToGecko");
     } catch(e) {}
 #endif
 
 #ifdef MOZ_ENABLE_SKIA_PDF
     try {
         // We have to disable printing via parent or else silent print operations
         // (the type that we use here) would be treated as non-silent -- in other
         // words, a print dialog would appear for each print operation, which
         // would interrupt the test run.
         // See http://searchfox.org/mozilla-central/rev/bd39b6170f04afeefc751a23bb04e18bbd10352b/layout/printing/nsPrintEngine.cpp#617
         prefs.setBoolPref("print.print_via_parent", false);
     } catch (e) {
         /* uh oh, print reftests may not work... */
     }
 #endif
 
-    gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
-    if (!gWindowUtils || !gWindowUtils.compareCanvases)
+    g.windowUtils = g.containingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
+    if (!g.windowUtils || !g.windowUtils.compareCanvases)
         throw "nsIDOMWindowUtils inteface missing";
 
-    gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
-    gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
+    g.ioService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
+    g.debug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
 
     RegisterProcessCrashObservers();
 
-    if (gRemote) {
-        gServer = null;
+    if (g.remote) {
+        g.server = null;
     } else {
-        gServer = new HttpServer();
+        g.server = new HttpServer();
     }
     try {
-        if (gServer)
+        if (g.server)
             StartHTTPServer();
     } catch (ex) {
-        //gBrowser.loadURI('data:text/plain,' + ex);
-        ++gTestResults.Exception;
+        //g.browser.loadURI('data:text/plain,' + ex);
+        ++g.testResults.Exception;
         logger.error("EXCEPTION: " + ex);
         DoneTests();
     }
 
     // Focus the content browser.
-    if (gFocusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
-        gBrowser.addEventListener("focus", StartTests, true);
-        gBrowser.focus();
+    if (g.focusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
+        g.browser.addEventListener("focus", StartTests, true);
+        g.browser.focus();
     } else {
         StartTests();
     }
 }
 
 function StartHTTPServer()
 {
-    gServer.registerContentType("sjs", "sjs");
-    gServer.start(-1);
-    gHttpServerPort = gServer.identity.primaryPort;
+    g.server.registerContentType("sjs", "sjs");
+    g.server.start(-1);
+    g.httpServerPort = g.server.identity.primaryPort;
 }
 
 // Perform a Fisher-Yates shuffle of the array.
 function Shuffle(array)
 {
     for (var i = array.length - 1; i > 0; i--) {
         var j = Math.floor(Math.random() * (i + 1));
         var temp = array[i];
         array[i] = array[j];
         array[j] = temp;
     }
 }
 
 function StartTests()
 {
-    if (gFocusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
-        gBrowser.removeEventListener("focus", StartTests, true);
+    if (g.focusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
+        g.browser.removeEventListener("focus", StartTests, true);
     }
 
     var manifests;
     /* These prefs are optional, so we don't need to spit an error to the log */
     try {
         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                     getService(Components.interfaces.nsIPrefBranch);
     } catch(e) {
         logger.error("EXCEPTION: " + e);
     }
 
-    gNoCanvasCache = prefs.getIntPref("reftest.nocache", false);
+    g.noCanvasCache = prefs.getIntPref("reftest.nocache", false);
 
-    gShuffle = prefs.getBoolPref("reftest.shuffle", false);
+    g.shuffle = prefs.getBoolPref("reftest.shuffle", false);
 
-    gRunUntilFailure = prefs.getBoolPref("reftest.runUntilFailure", false);
+    g.runUntilFailure = prefs.getBoolPref("reftest.runUntilFailure", false);
 
-    gCleanupPendingCrashes = prefs.getBoolPref("reftest.cleanupPendingCrashes", false);
+    g.cleanupPendingCrashes = prefs.getBoolPref("reftest.cleanupPendingCrashes", false);
 
     // Check if there are any crash dump files from the startup procedure, before
     // we start running the first test. Otherwise the first test might get
     // blamed for producing a crash dump file when that was not the case.
     CleanUpCrashDumpFiles();
 
     // When we repeat this function is called again, so really only want to set
-    // gRepeat once.
-    if (gRepeat == null) {
-      gRepeat = prefs.getIntPref("reftest.repeat", 0);
+    // g.repeat once.
+    if (g.repeat == null) {
+      g.repeat = prefs.getIntPref("reftest.repeat", 0);
     }
 
-    gRunSlowTests = prefs.getIntPref("reftest.skipslowtests", false);
+    g.runSlowTests = prefs.getIntPref("reftest.skipslowtests", false);
 
-    if (gShuffle) {
-        gNoCanvasCache = true;
+    if (g.shuffle) {
+        g.noCanvasCache = true;
     }
 
-    gURLs = [];
-    gManifestsLoaded = {};
+    g.urls = [];
 
     try {
         var manifests = JSON.parse(prefs.getCharPref("reftest.manifests"));
-        gURLFilterRegex = manifests[null];
+        g.urlsFilterRegex = manifests[null];
     } catch(e) {
         logger.error("Unable to find reftest.manifests pref.  Please ensure your profile is setup properly");
         DoneTests();
     }
 
     try {
         var globalFilter = manifests.hasOwnProperty("") ? new RegExp(manifests[""]) : null;
         var manifestURLs = Object.keys(manifests);
@@ -549,919 +394,225 @@ function StartTests()
             var filter = manifests[manifestURL] ? new RegExp(manifests[manifestURL]) : null;
             ReadTopManifest(manifestURL, [globalFilter, filter, false]);
         });
         BuildUseCounts();
 
         // Filter tests which will be skipped to get a more even distribution when chunking
         // tURLs is a temporary array containing all active tests
         var tURLs = new Array();
-        for (var i = 0; i < gURLs.length; ++i) {
-            if (gURLs[i].expected == EXPECTED_DEATH)
+        for (var i = 0; i < g.urls.length; ++i) {
+            if (g.urls[i].expected == EXPECTED_DEATH)
                 continue;
 
-            if (gURLs[i].needsFocus && !Focus())
+            if (g.urls[i].needsFocus && !Focus())
                 continue;
 
-            if (gURLs[i].slow && !gRunSlowTests)
+            if (g.urls[i].slow && !g.runSlowTests)
                 continue;
 
-            tURLs.push(gURLs[i]);
+            tURLs.push(g.urls[i]);
         }
 
         var numActiveTests = tURLs.length;
 
-        if (gTotalChunks > 0 && gThisChunk > 0) {
+        if (g.totalChunks > 0 && g.thisChunk > 0) {
             // Calculate start and end indices of this chunk if tURLs array were
             // divided evenly
-            var testsPerChunk = tURLs.length / gTotalChunks;
-            var start = Math.round((gThisChunk-1) * testsPerChunk);
-            var end = Math.round(gThisChunk * testsPerChunk);
+            var testsPerChunk = tURLs.length / g.totalChunks;
+            var start = Math.round((g.thisChunk-1) * testsPerChunk);
+            var end = Math.round(g.thisChunk * testsPerChunk);
             numActiveTests = end - start;
 
-            // Map these indices onto the gURLs array. This avoids modifying the
-            // gURLs array which prevents skipped tests from showing up in the log
-            start = gThisChunk == 1 ? 0 : gURLs.indexOf(tURLs[start]);
-            end = gThisChunk == gTotalChunks ? gURLs.length : gURLs.indexOf(tURLs[end + 1]) - 1;
+            // Map these indices onto the g.urls array. This avoids modifying the
+            // g.urls array which prevents skipped tests from showing up in the log
+            start = g.thisChunk == 1 ? 0 : g.urls.indexOf(tURLs[start]);
+            end = g.thisChunk == g.totalChunks ? g.urls.length : g.urls.indexOf(tURLs[end + 1]) - 1;
 
-            logger.info("Running chunk " + gThisChunk + " out of " + gTotalChunks + " chunks.  " +
-                "tests " + (start+1) + "-" + end + "/" + gURLs.length);
+            logger.info("Running chunk " + g.thisChunk + " out of " + g.totalChunks + " chunks.  " +
+                "tests " + (start+1) + "-" + end + "/" + g.urls.length);
 
-            gURLs = gURLs.slice(start, end);
+            g.urls = g.urls.slice(start, end);
         }
 
-        if (gStartAfter === undefined && !gSuiteStarted) {
-            var ids = gURLs.map(function(obj) {
+        if (g.startAfter === undefined && !g.suiteStarted) {
+            var ids = g.urls.map(function(obj) {
                 return obj.identifier;
             });
-            logger.suiteStart(ids, {"skipped": gURLs.length - numActiveTests});
-            gSuiteStarted = true
+            logger.suiteStart(ids, {"skipped": g.urls.length - numActiveTests});
+            g.suiteStarted = true
         }
 
-        if (gShuffle) {
-            if (gStartAfter !== undefined) {
+        if (g.shuffle) {
+            if (g.startAfter !== undefined) {
                 logger.error("Can't resume from a crashed test when " +
                              "--shuffle is enabled, continue by shuffling " +
                              "all the tests");
                 DoneTests();
                 return;
             }
-            Shuffle(gURLs);
-        } else if (gStartAfter !== undefined) {
+            Shuffle(g.urls);
+        } else if (g.startAfter !== undefined) {
             // Skip through previously crashed test
             // We have to do this after chunking so we don't break the numbers
-            var crash_idx = gURLs.map(function(url) {
+            var crash_idx = g.urls.map(function(url) {
                 return url['url1']['spec'];
-            }).indexOf(gStartAfter);
+            }).indexOf(g.startAfter);
             if (crash_idx == -1) {
                 throw "Can't find the previously crashed test";
             }
-            gURLs = gURLs.slice(crash_idx + 1);
+            g.urls = g.urls.slice(crash_idx + 1);
         }
 
-        gTotalTests = gURLs.length;
+        g.totalTests = g.urls.length;
 
-        if (!gTotalTests)
+        if (!g.totalTests)
             throw "No tests to run";
 
-        gURICanvases = {};
+        g.uriCanvases = {};
         StartCurrentTest();
     } catch (ex) {
-        //gBrowser.loadURI('data:text/plain,' + ex);
-        ++gTestResults.Exception;
+        //g.browser.loadURI('data:text/plain,' + ex);
+        ++g.testResults.Exception;
         logger.error("EXCEPTION: " + ex);
         DoneTests();
     }
 }
 
 function OnRefTestUnload()
 {
   let plugin1 = getTestPlugin("Test Plug-in");
   let plugin2 = getTestPlugin("Second Test Plug-in");
   if (plugin1 && plugin2) {
-    plugin1.enabledState = gTestPluginEnabledStates[0];
-    plugin2.enabledState = gTestPluginEnabledStates[1];
+    plugin1.enabledState = g.testPluginEnabledStates[0];
+    plugin2.enabledState = g.testPluginEnabledStates[1];
   } else {
     logger.warning("Failed to get test plugin tags.");
   }
 }
 
-// Read all available data from an input stream and return it
-// as a string.
-function getStreamContent(inputStream)
-{
-    var streamBuf = "";
-    var sis = CC["@mozilla.org/scriptableinputstream;1"].
-                  createInstance(CI.nsIScriptableInputStream);
-    sis.init(inputStream);
-
-    var available;
-    while ((available = sis.available()) != 0) {
-        streamBuf += sis.read(available);
-    }
-
-    return streamBuf;
-}
-
-// Build the sandbox for fails-if(), etc., condition evaluation.
-function BuildConditionSandbox(aURL) {
-    var sandbox = new Components.utils.Sandbox(aURL.spec);
-    var xr = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULRuntime);
-    var appInfo = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULAppInfo);
-    sandbox.isDebugBuild = gDebug.isDebugBuild;
-    var prefs = CC["@mozilla.org/preferences-service;1"].
-                getService(CI.nsIPrefBranch);
-    var env = CC["@mozilla.org/process/environment;1"].
-                getService(CI.nsIEnvironment);
-
-    sandbox.xulRuntime = CU.cloneInto({widgetToolkit: xr.widgetToolkit, OS: xr.OS, XPCOMABI: xr.XPCOMABI}, sandbox);
-
-    var testRect = gBrowser.getBoundingClientRect();
-    sandbox.smallScreen = false;
-    if (gContainingWindow.innerWidth < 800 || gContainingWindow.innerHeight < 1000) {
-        sandbox.smallScreen = true;
-    }
-
-    var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
-    let readGfxInfo = function (obj, key) {
-      if (gContentGfxInfo && (key in gContentGfxInfo)) {
-        return gContentGfxInfo[key];
-      }
-      return obj[key];
-    }
-
-    try {
-      sandbox.d2d = readGfxInfo(gfxInfo, "D2DEnabled");
-      sandbox.dwrite = readGfxInfo(gfxInfo, "DWriteEnabled");
-    } catch (e) {
-      sandbox.d2d = false;
-      sandbox.dwrite = false;
-    }
-
-    var info = gfxInfo.getInfo();
-    var canvasBackend = readGfxInfo(info, "AzureCanvasBackend");
-    var contentBackend = readGfxInfo(info, "AzureContentBackend");
-    var canvasAccelerated = readGfxInfo(info, "AzureCanvasAccelerated");
-
-    sandbox.gpuProcess = gfxInfo.usingGPUProcess;
-    sandbox.azureCairo = canvasBackend == "cairo";
-    sandbox.azureSkia = canvasBackend == "skia";
-    sandbox.skiaContent = contentBackend == "skia";
-    sandbox.azureSkiaGL = canvasAccelerated; // FIXME: assumes GL right now
-    // true if we are using the same Azure backend for rendering canvas and content
-    sandbox.contentSameGfxBackendAsCanvas = contentBackend == canvasBackend
-                                            || (contentBackend == "none" && canvasBackend == "cairo");
-
-    sandbox.layersGPUAccelerated =
-      gWindowUtils.layerManagerType != "Basic";
-    sandbox.d3d11 =
-      gWindowUtils.layerManagerType == "Direct3D 11";
-    sandbox.d3d9 =
-      gWindowUtils.layerManagerType == "Direct3D 9";
-    sandbox.layersOpenGL =
-      gWindowUtils.layerManagerType == "OpenGL";
-    sandbox.webrender =
-      gWindowUtils.layerManagerType == "WebRender";
-    sandbox.layersOMTC =
-      gWindowUtils.layerManagerRemote == true;
-    sandbox.advancedLayers =
-      gWindowUtils.usingAdvancedLayers == true;
-    sandbox.layerChecksEnabled = !sandbox.webrender;
-
-    // Shortcuts for widget toolkits.
-    sandbox.Android = xr.OS == "Android";
-    sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";
-    sandbox.gtkWidget = xr.widgetToolkit == "gtk2"
-                        || xr.widgetToolkit == "gtk3";
-    sandbox.qtWidget = xr.widgetToolkit == "qt";
-    sandbox.winWidget = xr.widgetToolkit == "windows";
-
-    // Scrollbars that are semi-transparent. See bug 1169666.
-    sandbox.transparentScrollbars = xr.widgetToolkit == "gtk3";
-
-    if (sandbox.Android) {
-        var sysInfo = CC["@mozilla.org/system-info;1"].getService(CI.nsIPropertyBag2);
-
-        // This is currently used to distinguish Android 4.0.3 (SDK version 15)
-        // and later from Android 2.x
-        sandbox.AndroidVersion = sysInfo.getPropertyAsInt32("version");
-    }
-
-#if MOZ_ASAN
-    sandbox.AddressSanitizer = true;
-#else
-    sandbox.AddressSanitizer = false;
-#endif
-
-#if MOZ_WEBRTC
-    sandbox.webrtc = true;
-#else
-    sandbox.webrtc = false;
-#endif
-
-#ifdef MOZ_STYLO
-    let styloEnabled = false;
-    // Perhaps a bit redundant in places, but this is easier to compare with the
-    // the real check in `nsLayoutUtils.cpp` to ensure they test the same way.
-    if (env.get("STYLO_FORCE_ENABLED")) {
-        styloEnabled = true;
-    } else if (env.get("STYLO_FORCE_DISABLED")) {
-        styloEnabled = false;
-    } else {
-        styloEnabled = prefs.getBoolPref("layout.css.servo.enabled", false);
-    }
-    sandbox.stylo = styloEnabled && !gCompareStyloToGecko;
-    sandbox.styloVsGecko = gCompareStyloToGecko;
-#else
-    sandbox.stylo = false;
-    sandbox.styloVsGecko = false;
-#endif
-
-// Printing via Skia PDF is only supported on Mac for now.
-#ifdef XP_MACOSX && MOZ_ENABLE_SKIA_PDF
-    sandbox.skiaPdf = true;
-#else
-    sandbox.skiaPdf = false;
-#endif
-
-#ifdef RELEASE_OR_BETA
-    sandbox.release_or_beta = true;
-#else
-    sandbox.release_or_beta = false;
-#endif
-
-    var hh = CC[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].
-                 getService(CI.nsIHttpProtocolHandler);
-    var httpProps = ["userAgent", "appName", "appVersion", "vendor",
-                     "vendorSub", "product", "productSub", "platform",
-                     "oscpu", "language", "misc"];
-    sandbox.http = new sandbox.Object();
-    httpProps.forEach((x) => sandbox.http[x] = hh[x]);
-
-    // Set OSX to be the Mac OS X version, as an integer, or undefined
-    // for other platforms.  The integer is formed by 100 times the
-    // major version plus the minor version, so 1006 for 10.6, 1010 for
-    // 10.10, etc.
-    var osxmatch = /Mac OS X (\d+).(\d+)$/.exec(hh.oscpu);
-    sandbox.OSX = osxmatch ? parseInt(osxmatch[1]) * 100 + parseInt(osxmatch[2]) : undefined;
-
-    // see if we have the test plugin available,
-    // and set a sandox prop accordingly
-    sandbox.haveTestPlugin = !sandbox.Android && !!getTestPlugin("Test Plug-in");
-
-    // Set a flag on sandbox if the windows default theme is active
-    sandbox.windowsDefaultTheme = gContainingWindow.matchMedia("(-moz-windows-default-theme)").matches;
-
-    try {
-        sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme");
-    } catch (e) {
-        sandbox.nativeThemePref = true;
-    }
-    sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled", false);
-
-    sandbox.prefs = CU.cloneInto({
-        getBoolPref: function(p) { return prefs.getBoolPref(p); },
-        getIntPref:  function(p) { return prefs.getIntPref(p); }
-    }, sandbox, { cloneFunctions: true });
-
-    // Tests shouldn't care about this except for when they need to
-    // crash the content process
-    sandbox.browserIsRemote = gBrowserIsRemote;
-
-    try {
-        sandbox.asyncPan = gContainingWindow.document.docShell.asyncPanZoomEnabled;
-    } catch (e) {
-        sandbox.asyncPan = false;
-    }
-
-    // Graphics features
-    sandbox.usesRepeatResampling = sandbox.d2d;
-
-    if (!gDumpedConditionSandbox) {
-        logger.info("Dumping JSON representation of sandbox");
-        logger.info(JSON.stringify(CU.waiveXrays(sandbox)));
-        gDumpedConditionSandbox = true;
-    }
-
-    return sandbox;
-}
-
-function AddPrefSettings(aWhere, aPrefName, aPrefValExpression, aSandbox, aTestPrefSettings, aRefPrefSettings)
-{
-    var prefVal = Components.utils.evalInSandbox("(" + aPrefValExpression + ")", aSandbox);
-    var prefType;
-    var valType = typeof(prefVal);
-    if (valType == "boolean") {
-        prefType = PREF_BOOLEAN;
-    } else if (valType == "string") {
-        prefType = PREF_STRING;
-    } else if (valType == "number" && (parseInt(prefVal) == prefVal)) {
-        prefType = PREF_INTEGER;
-    } else {
-        return false;
-    }
-    var setting = { name: aPrefName,
-                    type: prefType,
-                    value: prefVal };
-
-    if (gCompareStyloToGecko && aPrefName != "layout.css.servo.enabled") {
-        // ref-pref() is ignored, test-pref() and pref() are added to both
-        if (aWhere != "ref-") {
-            aTestPrefSettings.push(setting);
-            aRefPrefSettings.push(setting);
-        }
-    } else {
-        if (aWhere != "ref-") {
-            aTestPrefSettings.push(setting);
-        }
-        if (aWhere != "test-") {
-            aRefPrefSettings.push(setting);
-        }
-    }
-    return true;
-}
-
-function ReadTopManifest(aFileURL, aFilter)
-{
-    var url = gIOService.newURI(aFileURL);
-    if (!url)
-        throw "Expected a file or http URL for the manifest.";
-    ReadManifest(url, EXPECTED_PASS, aFilter);
-}
-
-function AddTestItem(aTest, aFilter)
-{
-    if (!aFilter)
-        aFilter = [null, [], false];
-
-    var globalFilter = aFilter[0];
-    var manifestFilter = aFilter[1];
-    var invertManifest = aFilter[2];
-    if ((globalFilter && !globalFilter.test(aTest.url1.spec)) ||
-        (manifestFilter &&
-         !(invertManifest ^ manifestFilter.test(aTest.url1.spec))))
-        return;
-    if (gFocusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS &&
-        !aTest.needsFocus)
-        return;
-    if (gFocusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS &&
-        aTest.needsFocus)
-        return;
-
-    if (aTest.url2 !== null)
-        aTest.identifier = [aTest.prettyPath, aTest.type, aTest.url2.spec];
-    else
-        aTest.identifier = aTest.prettyPath;
-
-    gURLs.push(aTest);
-}
-
-function AddStyloTestPrefs(aSandbox, aTestPrefSettings, aRefPrefSettings)
-{
-    AddPrefSettings("test-", "layout.css.servo.enabled", "true", aSandbox,
-                    aTestPrefSettings, aRefPrefSettings);
-    AddPrefSettings("ref-", "layout.css.servo.enabled", "false", aSandbox,
-                    aTestPrefSettings, aRefPrefSettings);
-}
-
-function ExtractRange(matches, startIndex, defaultMin = 0) {
-    if (matches[startIndex + 1] === undefined) {
-        return {
-            min: defaultMin,
-            max: Number(matches[startIndex])
-        };
-    }
-    return {
-        min: Number(matches[startIndex]),
-        max: Number(matches[startIndex + 1].substring(1))
-    };
-}
-
-// Note: If you materially change the reftest manifest parsing,
-// please keep the parser in print-manifest-dirs.py in sync.
-function ReadManifest(aURL, inherited_status, aFilter)
-{
-    // Ensure each manifest is only read once. This assumes that manifests that are
-    // included with an unusual inherited_status or filters will be read via their
-    // include before they are read directly in the case of a duplicate
-    if (gManifestsLoaded.hasOwnProperty(aURL.spec)) {
-        if (gManifestsLoaded[aURL.spec] === null)
-            return;
-        else
-            aFilter = [aFilter[0], aFilter[1], true];
-    }
-    gManifestsLoaded[aURL.spec] = aFilter[1];
-
-    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
-                     .getService(CI.nsIScriptSecurityManager);
-
-    var listURL = aURL;
-    var channel = NetUtil.newChannel({uri: aURL, loadUsingSystemPrincipal: true});
-    var inputStream = channel.open2();
-    if (channel instanceof Components.interfaces.nsIHttpChannel
-        && channel.responseStatus != 200) {
-      logger.error("HTTP ERROR : " + channel.responseStatus);
-    }
-    var streamBuf = getStreamContent(inputStream);
-    inputStream.close();
-    var lines = streamBuf.split(/\n|\r|\r\n/);
-
-    // Build the sandbox for fails-if(), etc., condition evaluation.
-    var sandbox = BuildConditionSandbox(aURL);
-    var lineNo = 0;
-    var urlprefix = "";
-    var defaultTestPrefSettings = [], defaultRefPrefSettings = [];
-    if (gCompareStyloToGecko) {
-        AddStyloTestPrefs(sandbox, defaultTestPrefSettings,
-                          defaultRefPrefSettings);
-    }
-    for (var str of lines) {
-        ++lineNo;
-        if (str.charAt(0) == "#")
-            continue; // entire line was a comment
-        var i = str.search(/\s+#/);
-        if (i >= 0)
-            str = str.substring(0, i);
-        // strip leading and trailing whitespace
-        str = str.replace(/^\s*/, '').replace(/\s*$/, '');
-        if (!str || str == "")
-            continue;
-        var items = str.split(/\s+/); // split on whitespace
-
-        if (items[0] == "url-prefix") {
-            if (items.length != 2)
-                throw "url-prefix requires one url in manifest file " + aURL.spec + " line " + lineNo;
-            urlprefix = items[1];
-            continue;
-        }
-
-        if (items[0] == "default-preferences") {
-            var m;
-            var item;
-            defaultTestPrefSettings = [];
-            defaultRefPrefSettings = [];
-            items.shift();
-            while ((item = items.shift())) {
-                if (!(m = item.match(gPrefItemRE))) {
-                    throw "Unexpected item in default-preferences list in manifest file " + aURL.spec + " line " + lineNo;
-                }
-                if (!AddPrefSettings(m[1], m[2], m[3], sandbox, defaultTestPrefSettings, defaultRefPrefSettings)) {
-                    throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo;
-                }
-            }
-            if (gCompareStyloToGecko) {
-                AddStyloTestPrefs(sandbox, defaultTestPrefSettings,
-                                  defaultRefPrefSettings);
-            }
-            continue;
-        }
-
-        var expected_status = EXPECTED_PASS;
-        var allow_silent_fail = false;
-        var minAsserts = 0;
-        var maxAsserts = 0;
-        var needs_focus = false;
-        var slow = false;
-        var testPrefSettings = defaultTestPrefSettings.concat();
-        var refPrefSettings = defaultRefPrefSettings.concat();
-        var fuzzy_delta = { min: 0, max: 2 };
-        var fuzzy_pixels = { min: 0, max: 1 };
-        var chaosMode = false;
-
-        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode)/)) {
-            var item = items.shift();
-            var stat;
-            var cond;
-            var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
-            if (m) {
-                stat = m[1];
-                // Note: m[2] contains the parentheses, and we want them.
-                cond = Components.utils.evalInSandbox(m[2], sandbox);
-            } else if (item.match(/^(fails|random|skip)$/)) {
-                stat = item;
-                cond = true;
-            } else if (item == "needs-focus") {
-                needs_focus = true;
-                cond = false;
-            } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
-                cond = false;
-                minAsserts = Number(m[1]);
-                maxAsserts = (m[2] == undefined) ? minAsserts
-                                                 : Number(m[2].substring(1));
-            } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
-                cond = false;
-                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
-                    minAsserts = Number(m[2]);
-                    maxAsserts =
-                      (m[3] == undefined) ? minAsserts
-                                          : Number(m[3].substring(1));
-                }
-            } else if (item == "slow") {
-                cond = false;
-                slow = true;
-            } else if ((m = item.match(/^require-or\((.*?)\)$/))) {
-                var args = m[1].split(/,/);
-                if (args.length != 2) {
-                    throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": wrong number of args to require-or";
-                }
-                var [precondition_str, fallback_action] = args;
-                var preconditions = precondition_str.split(/&&/);
-                cond = false;
-                for (var precondition of preconditions) {
-                    if (precondition === "debugMode") {
-                        // Currently unimplemented. Requires asynchronous
-                        // JSD call + getting an event while no JS is running
-                        stat = fallback_action;
-                        cond = true;
-                        break;
-                    } else if (precondition === "true") {
-                        // For testing
-                    } else {
-                        // Unknown precondition. Assume it is unimplemented.
-                        stat = fallback_action;
-                        cond = true;
-                        break;
-                    }
-                }
-            } else if ((m = item.match(/^slow-if\((.*?)\)$/))) {
-                cond = false;
-                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox))
-                    slow = true;
-            } else if (item == "silentfail") {
-                cond = false;
-                allow_silent_fail = true;
-            } else if ((m = item.match(gPrefItemRE))) {
-                cond = false;
-                if (!AddPrefSettings(m[1], m[2], m[3], sandbox, testPrefSettings, refPrefSettings)) {
-                    throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo;
-                }
-            } else if ((m = item.match(/^fuzzy\((\d+)(-\d+)?,(\d+)(-\d+)?\)$/))) {
-              cond = false;
-              expected_status = EXPECTED_FUZZY;
-              fuzzy_delta = ExtractRange(m, 1);
-              fuzzy_pixels = ExtractRange(m, 3);
-            } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+)(-\d+)?,(\d+)(-\d+)?\)$/))) {
-              cond = false;
-              if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
-                expected_status = EXPECTED_FUZZY;
-                fuzzy_delta = ExtractRange(m, 2);
-                fuzzy_pixels = ExtractRange(m, 4);
-              }
-            } else if (item == "chaos-mode") {
-                cond = false;
-                chaosMode = true;
-            } else {
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unexpected item " + item;
-            }
-
-            if (cond) {
-                if (stat == "fails") {
-                    expected_status = EXPECTED_FAIL;
-                } else if (stat == "random") {
-                    expected_status = EXPECTED_RANDOM;
-                } else if (stat == "skip") {
-                    expected_status = EXPECTED_DEATH;
-                } else if (stat == "silentfail") {
-                    allow_silent_fail = true;
-                }
-            }
-        }
-
-        expected_status = Math.max(expected_status, inherited_status);
-
-        if (minAsserts > maxAsserts) {
-            throw "Bad range in manifest file " + aURL.spec + " line " + lineNo;
-        }
-
-        var runHttp = false;
-        var httpDepth;
-        if (items[0] == "HTTP") {
-            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
-                                               // for non-local reftests.
-            httpDepth = 0;
-            items.shift();
-        } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) {
-            // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc.
-            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
-                                               // for non-local reftests.
-            httpDepth = (items[0].length - 5) / 3;
-            items.shift();
-        }
-
-        // do not prefix the url for include commands or urls specifying
-        // a protocol
-        if (urlprefix && items[0] != "include") {
-            if (items.length > 1 && !items[1].match(gProtocolRE)) {
-                items[1] = urlprefix + items[1];
-            }
-            if (items.length > 2 && !items[2].match(gProtocolRE)) {
-                items[2] = urlprefix + items[2];
-            }
-        }
-
-        var principal = secMan.createCodebasePrincipal(aURL, {});
-
-        if (items[0] == "include") {
-            if (items.length != 2)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to include";
-            if (runHttp)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": use of include with http";
-            var incURI = gIOService.newURI(items[1], null, listURL);
-            secMan.checkLoadURIWithPrincipal(principal, incURI,
-                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            ReadManifest(incURI, expected_status, aFilter);
-        } else if (items[0] == TYPE_LOAD) {
-            if (items.length != 2)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to load";
-            if (expected_status != EXPECTED_PASS &&
-                expected_status != EXPECTED_DEATH)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect known failure type for load test";
-            var [testURI] = runHttp
-                            ? ServeFiles(principal, httpDepth,
-                                         listURL, [items[1]])
-                            : [gIOService.newURI(items[1], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURIWithPrincipal(principal, testURI,
-                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            AddTestItem({ type: TYPE_LOAD,
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          needsFocus: needs_focus,
-                          slow: slow,
-                          prefSettings1: testPrefSettings,
-                          prefSettings2: refPrefSettings,
-                          fuzzyMinDelta: fuzzy_delta.min,
-                          fuzzyMaxDelta: fuzzy_delta.max,
-                          fuzzyMinPixels: fuzzy_pixels.min,
-                          fuzzyMaxPixels: fuzzy_pixels.max,
-                          url1: testURI,
-                          url2: null,
-                          chaosMode: chaosMode }, aFilter);
-        } else if (items[0] == TYPE_SCRIPT) {
-            if (items.length != 2)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to script";
-            var [testURI] = runHttp
-                            ? ServeFiles(principal, httpDepth,
-                                         listURL, [items[1]])
-                            : [gIOService.newURI(items[1], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURIWithPrincipal(principal, testURI,
-                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            AddTestItem({ type: TYPE_SCRIPT,
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          needsFocus: needs_focus,
-                          slow: slow,
-                          prefSettings1: testPrefSettings,
-                          prefSettings2: refPrefSettings,
-                          fuzzyMinDelta: fuzzy_delta.min,
-                          fuzzyMaxDelta: fuzzy_delta.max,
-                          fuzzyMinPixels: fuzzy_pixels.min,
-                          fuzzyMaxPixels: fuzzy_pixels.max,
-                          url1: testURI,
-                          url2: null,
-                          chaosMode: chaosMode }, aFilter);
-        } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL || items[0] == TYPE_PRINT) {
-            if (items.length != 3)
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + items[0];
-
-            if (items[0] == TYPE_REFTEST_NOTEQUAL &&
-                expected_status == EXPECTED_FUZZY &&
-                (fuzzy_delta.min > 0 || fuzzy_pixels.min > 0)) {
-                throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": minimum fuzz must be zero for tests of type " + items[0];
-            }
-
-            var [testURI, refURI] = runHttp
-                                  ? ServeFiles(principal, httpDepth,
-                                               listURL, [items[1], items[2]])
-                                  : [gIOService.newURI(items[1], null, listURL),
-                                     gIOService.newURI(items[2], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURIWithPrincipal(principal, testURI,
-                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            secMan.checkLoadURIWithPrincipal(principal, refURI,
-                                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            var type = items[0];
-            if (gCompareStyloToGecko) {
-                type = TYPE_REFTEST_EQUAL;
-                refURI = testURI;
-
-                // We expect twice as many assertion failures when running in
-                // styloVsGecko mode because we run each test twice: once in
-                // Stylo mode and once in Gecko mode.
-                minAsserts *= 2;
-                maxAsserts *= 2;
-
-                // Skip the test if it is expected to fail in both Stylo and
-                // Gecko modes. It would unexpectedly "pass" in styloVsGecko
-                // mode when comparing the two failures, which is not a useful
-                // result.
-                if (expected_status === EXPECTED_FAIL ||
-                    expected_status === EXPECTED_RANDOM) {
-                    expected_status = EXPECTED_DEATH;
-                }
-            }
-
-            AddTestItem({ type: type,
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          needsFocus: needs_focus,
-                          slow: slow,
-                          prefSettings1: testPrefSettings,
-                          prefSettings2: refPrefSettings,
-                          fuzzyMinDelta: fuzzy_delta.min,
-                          fuzzyMaxDelta: fuzzy_delta.max,
-                          fuzzyMinPixels: fuzzy_pixels.min,
-                          fuzzyMaxPixels: fuzzy_pixels.max,
-                          url1: testURI,
-                          url2: refURI,
-                          chaosMode: chaosMode }, aFilter);
-        } else {
-            throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unknown test type " + items[0];
-        }
-    }
-}
-
 function AddURIUseCount(uri)
 {
     if (uri == null)
         return;
 
     var spec = uri.spec;
-    if (spec in gURIUseCounts) {
-        gURIUseCounts[spec]++;
+    if (spec in g.uriUseCounts) {
+        g.uriUseCounts[spec]++;
     } else {
-        gURIUseCounts[spec] = 1;
+        g.uriUseCounts[spec] = 1;
     }
 }
 
 function BuildUseCounts()
 {
-    if (gNoCanvasCache) {
+    if (g.noCanvasCache) {
         return;
     }
 
-    gURIUseCounts = {};
-    for (var i = 0; i < gURLs.length; ++i) {
-        var url = gURLs[i];
+    g.uriUseCounts = {};
+    for (var i = 0; i < g.urls.length; ++i) {
+        var url = g.urls[i];
         if (url.expected != EXPECTED_DEATH &&
             (url.type == TYPE_REFTEST_EQUAL ||
              url.type == TYPE_REFTEST_NOTEQUAL)) {
             if (url.prefSettings1.length == 0) {
-                AddURIUseCount(gURLs[i].url1);
+                AddURIUseCount(g.urls[i].url1);
             }
             if (url.prefSettings2.length == 0) {
-                AddURIUseCount(gURLs[i].url2);
+                AddURIUseCount(g.urls[i].url2);
             }
         }
     }
 }
 
-function ServeFiles(manifestPrincipal, depth, aURL, files)
-{
-    var listURL = aURL.QueryInterface(CI.nsIFileURL);
-    var directory = listURL.file.parent;
-
-    // Allow serving a tree that's an ancestor of the directory containing
-    // the files so that they can use resources in ../ (etc.).
-    var dirPath = "/";
-    while (depth > 0) {
-        dirPath = "/" + directory.leafName + dirPath;
-        directory = directory.parent;
-        --depth;
-    }
-
-    gCount++;
-    var path = "/" + Date.now() + "/" + gCount;
-    gServer.registerDirectory(path + "/", directory);
-
-    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
-                     .getService(CI.nsIScriptSecurityManager);
-
-    var testbase = gIOService.newURI("http://localhost:" + gHttpServerPort +
-                                     path + dirPath);
-
-    // Give the testbase URI access to XUL and XBL
-    Services.perms.add(testbase, "allowXULXBL", Services.perms.ALLOW_ACTION);
-
-    function FileToURI(file)
-    {
-        // Only serve relative URIs via the HTTP server, not absolute
-        // ones like about:blank.
-        var testURI = gIOService.newURI(file, null, testbase);
-
-        // XXX necessary?  manifestURL guaranteed to be file, others always HTTP
-        secMan.checkLoadURIWithPrincipal(manifestPrincipal, testURI,
-                                         CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-
-        return testURI;
-    }
-
-    return files.map(FileToURI);
-}
-
 // Return true iff this window is focused when this function returns.
 function Focus()
 {
     var fm = CC["@mozilla.org/focus-manager;1"].getService(CI.nsIFocusManager);
-    fm.focusedWindow = gContainingWindow;
+    fm.focusedWindow = g.containingWindow;
 #ifdef XP_MACOSX
     try {
         var dock = CC["@mozilla.org/widget/macdocksupport;1"].getService(CI.nsIMacDockSupport);
         dock.activateApplication(true);
     } catch(ex) {
     }
 #endif // XP_MACOSX
     return true;
 }
 
 function Blur()
 {
     // On non-remote reftests, this will transfer focus to the dummy window
     // we created to hold focus for non-needs-focus tests.  Buggy tests
     // (ones which require focus but don't request needs-focus) will then
     // fail.
-    gContainingWindow.blur();
+    g.containingWindow.blur();
 }
 
 function StartCurrentTest()
 {
-    gTestLog = [];
+    g.testLog = [];
 
     // make sure we don't run tests that are expected to kill the browser
-    while (gURLs.length > 0) {
-        var test = gURLs[0];
+    while (g.urls.length > 0) {
+        var test = g.urls[0];
         logger.testStart(test.identifier);
         if (test.expected == EXPECTED_DEATH) {
-            ++gTestResults.Skip;
+            ++g.testResults.Skip;
             logger.testEnd(test.identifier, "SKIP");
-            gURLs.shift();
+            g.urls.shift();
         } else if (test.needsFocus && !Focus()) {
             // FIXME: Marking this as a known fail is dangerous!  What
             // if it starts failing all the time?
-            ++gTestResults.Skip;
+            ++g.testResults.Skip;
             logger.testEnd(test.identifier, "SKIP", null, "(COULDN'T GET FOCUS)");
-            gURLs.shift();
-        } else if (test.slow && !gRunSlowTests) {
-            ++gTestResults.Slow;
+            g.urls.shift();
+        } else if (test.slow && !g.runSlowTests) {
+            ++g.testResults.Slow;
             logger.testEnd(test.identifier, "SKIP", null, "(SLOW)");
-            gURLs.shift();
+            g.urls.shift();
         } else {
             break;
         }
     }
 
-    if ((gURLs.length == 0 && gRepeat == 0) ||
-        (gRunUntilFailure && HasUnexpectedResult())) {
+    if ((g.urls.length == 0 && g.repeat == 0) ||
+        (g.runUntilFailure && HasUnexpectedResult())) {
         RestoreChangedPreferences();
         DoneTests();
-    } else if (gURLs.length == 0 && gRepeat > 0) {
+    } else if (g.urls.length == 0 && g.repeat > 0) {
         // Repeat
-        gRepeat--;
+        g.repeat--;
         StartTests();
     } else {
-        if (gURLs[0].chaosMode) {
-            gWindowUtils.enterChaosMode();
+        if (g.urls[0].chaosMode) {
+            g.windowUtils.enterChaosMode();
         }
-        if (!gURLs[0].needsFocus) {
+        if (!g.urls[0].needsFocus) {
             Blur();
         }
-        var currentTest = gTotalTests - gURLs.length;
-        gContainingWindow.document.title = "reftest: " + currentTest + " / " + gTotalTests +
-            " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)";
+        var currentTest = g.totalTests - g.urls.length;
+        g.containingWindow.document.title = "reftest: " + currentTest + " / " + g.totalTests +
+            " (" + Math.floor(100 * (currentTest / g.totalTests)) + "%)";
         StartCurrentURI(1);
     }
 }
 
 function StartCurrentURI(aState)
 {
-    gState = aState;
-    gCurrentURL = gURLs[0]["url" + aState].spec;
+    g.state = aState;
+    g.currentURL = g.urls[0]["url" + aState].spec;
 
     RestoreChangedPreferences();
 
     var prefs = Components.classes["@mozilla.org/preferences-service;1"].
         getService(Components.interfaces.nsIPrefBranch);
 
-    var prefSettings = gURLs[0]["prefSettings" + aState];
+    var prefSettings = g.urls[0]["prefSettings" + aState];
     if (prefSettings.length > 0) {
         var badPref = undefined;
         try {
             prefSettings.forEach(function(ps) {
                 var oldVal;
                 if (ps.type == PREF_BOOLEAN) {
                     try {
                         oldVal = prefs.getBoolPref(ps.name);
@@ -1482,231 +633,231 @@ function StartCurrentURI(aState)
                     } catch (e) {
                         badPref = "integer preference '" + ps.name + "'";
                         throw "bad pref";
                     }
                 } else {
                     throw "internal error - unknown preference type";
                 }
                 if (oldVal != ps.value) {
-                    gPrefsToRestore.push( { name: ps.name,
+                    g.prefsToRestore.push( { name: ps.name,
                                             type: ps.type,
                                             value: oldVal } );
                     var value = ps.value;
                     if (ps.type == PREF_BOOLEAN) {
                         prefs.setBoolPref(ps.name, value);
                     } else if (ps.type == PREF_STRING) {
                         prefs.setCharPref(ps.name, value);
                         value = '"' + value + '"';
                     } else if (ps.type == PREF_INTEGER) {
                         prefs.setIntPref(ps.name, value);
                     }
                     logger.info("SET PREFERENCE pref(" + ps.name + "," + value + ")");
                 }
             });
         } catch (e) {
             if (e == "bad pref") {
-                var test = gURLs[0];
+                var test = g.urls[0];
                 if (test.expected == EXPECTED_FAIL) {
                     logger.testEnd(test.identifier, "FAIL", "FAIL",
                                    "(SKIPPED; " + badPref + " not known or wrong type)");
-                    ++gTestResults.Skip;
+                    ++g.testResults.Skip;
                 } else {
                     logger.testEnd(test.identifier, "FAIL", "PASS",
                                    badPref + " not known or wrong type");
-                    ++gTestResults.UnexpectedFail;
+                    ++g.testResults.UnexpectedFail;
                 }
 
                 // skip the test that had a bad preference
-                gURLs.shift();
+                g.urls.shift();
                 StartCurrentTest();
                 return;
             } else {
                 throw e;
             }
         }
     }
 
     if (prefSettings.length == 0 &&
-        gURICanvases[gCurrentURL] &&
-        (gURLs[0].type == TYPE_REFTEST_EQUAL ||
-         gURLs[0].type == TYPE_REFTEST_NOTEQUAL) &&
-        gURLs[0].maxAsserts == 0) {
+        g.uriCanvases[g.currentURL] &&
+        (g.urls[0].type == TYPE_REFTEST_EQUAL ||
+         g.urls[0].type == TYPE_REFTEST_NOTEQUAL) &&
+        g.urls[0].maxAsserts == 0) {
         // Pretend the document loaded --- RecordResult will notice
         // there's already a canvas for this URL
-        gContainingWindow.setTimeout(RecordResult, 0);
+        g.containingWindow.setTimeout(RecordResult, 0);
     } else {
-        var currentTest = gTotalTests - gURLs.length;
+        var currentTest = g.totalTests - g.urls.length;
         // Log this to preserve the same overall log format,
         // should be removed if the format is updated
-        gDumpFn("REFTEST TEST-LOAD | " + gCurrentURL + " | " + currentTest + " / " + gTotalTests +
-                " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)\n");
-        TestBuffer("START " + gCurrentURL);
-        var type = gURLs[0].type
+        gDumpFn("REFTEST TEST-LOAD | " + g.currentURL + " | " + currentTest + " / " + g.totalTests +
+                " (" + Math.floor(100 * (currentTest / g.totalTests)) + "%)\n");
+        TestBuffer("START " + g.currentURL);
+        var type = g.urls[0].type
         if (TYPE_SCRIPT == type) {
-            SendLoadScriptTest(gCurrentURL, gLoadTimeout);
+            SendLoadScriptTest(g.currentURL, g.loadTimeout);
         } else if (TYPE_PRINT == type) {
-            SendLoadPrintTest(gCurrentURL, gLoadTimeout);
+            SendLoadPrintTest(g.currentURL, g.loadTimeout);
         } else {
-            SendLoadTest(type, gCurrentURL, gLoadTimeout);
+            SendLoadTest(type, g.currentURL, g.loadTimeout);
         }
     }
 }
 
 function DoneTests()
 {
-    logger.suiteEnd({'results': gTestResults});
-    gSuiteStarted = false
-    logger.info("Slowest test took " + gSlowestTestTime + "ms (" + gSlowestTestURL + ")");
-    logger.info("Total canvas count = " + gRecycledCanvases.length);
-    if (gFailedUseWidgetLayers) {
+    logger.suiteEnd({'results': g.testResults});
+    g.suiteStarted = false
+    logger.info("Slowest test took " + g.slowestTestTime + "ms (" + g.slowestTestURL + ")");
+    logger.info("Total canvas count = " + g.recycledCanvases.length);
+    if (g.failedUseWidgetLayers) {
         LogWidgetLayersFailure();
     }
 
     function onStopped() {
         let appStartup = CC["@mozilla.org/toolkit/app-startup;1"].getService(CI.nsIAppStartup);
         appStartup.quit(CI.nsIAppStartup.eForceQuit);
     }
-    if (gServer) {
-        gServer.stop(onStopped);
+    if (g.server) {
+        g.server.stop(onStopped);
     }
     else {
         onStopped();
     }
 }
 
 function UpdateCanvasCache(url, canvas)
 {
     var spec = url.spec;
 
-    --gURIUseCounts[spec];
+    --g.uriUseCounts[spec];
 
-    if (gURIUseCounts[spec] == 0) {
+    if (g.uriUseCounts[spec] == 0) {
         ReleaseCanvas(canvas);
-        delete gURICanvases[spec];
-    } else if (gURIUseCounts[spec] > 0) {
-        gURICanvases[spec] = canvas;
+        delete g.uriCanvases[spec];
+    } else if (g.uriUseCounts[spec] > 0) {
+        g.uriCanvases[spec] = canvas;
     } else {
         throw "Use counts were computed incorrectly";
     }
 }
 
 // Recompute drawWindow flags for every drawWindow operation.
 // We have to do this every time since our window can be
 // asynchronously resized (e.g. by the window manager, to make
 // it fit on screen) at unpredictable times.
 // Fortunately this is pretty cheap.
 function DoDrawWindow(ctx, x, y, w, h)
 {
     var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW;
-    var testRect = gBrowser.getBoundingClientRect();
-    if (gIgnoreWindowSize ||
+    var testRect = g.browser.getBoundingClientRect();
+    if (g.ignoreWindowSize ||
         (0 <= testRect.left &&
          0 <= testRect.top &&
-         gContainingWindow.innerWidth >= testRect.right &&
-         gContainingWindow.innerHeight >= testRect.bottom)) {
+         g.containingWindow.innerWidth >= testRect.right &&
+         g.containingWindow.innerHeight >= testRect.bottom)) {
         // We can use the window's retained layer manager
         // because the window is big enough to display the entire
         // browser element
         flags |= ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
-    } else if (gBrowserIsRemote) {
-        logger.error(gCurrentURL + " | can't drawWindow remote content");
-        ++gTestResults.Exception;
+    } else if (g.browserIsRemote) {
+        logger.error(g.currentURL + " | can't drawWindow remote content");
+        ++g.testResults.Exception;
     }
 
-    if (gDrawWindowFlags != flags) {
+    if (g.drawWindowFlags != flags) {
         // Every time the flags change, dump the new state.
-        gDrawWindowFlags = flags;
+        g.drawWindowFlags = flags;
         var flagsStr = "DRAWWINDOW_DRAW_CARET | DRAWWINDOW_DRAW_VIEW";
         if (flags & ctx.DRAWWINDOW_USE_WIDGET_LAYERS) {
             flagsStr += " | DRAWWINDOW_USE_WIDGET_LAYERS";
         } else {
             // Output a special warning because we need to be able to detect
             // this whenever it happens.
             LogWidgetLayersFailure();
-            gFailedUseWidgetLayers = true;
+            g.failedUseWidgetLayers = true;
         }
         logger.info("drawWindow flags = " + flagsStr +
-                    "; window size = " + gContainingWindow.innerWidth + "," + gContainingWindow.innerHeight +
+                    "; window size = " + g.containingWindow.innerWidth + "," + g.containingWindow.innerHeight +
                     "; test browser size = " + testRect.width + "," + testRect.height);
     }
 
     TestBuffer("DoDrawWindow " + x + "," + y + "," + w + "," + h);
-    ctx.drawWindow(gContainingWindow, x, y, w, h, "rgb(255,255,255)",
-                   gDrawWindowFlags);
+    ctx.drawWindow(g.containingWindow, x, y, w, h, "rgb(255,255,255)",
+                   g.drawWindowFlags);
 }
 
 function InitCurrentCanvasWithSnapshot()
 {
     TestBuffer("Initializing canvas snapshot");
 
-    if (gURLs[0].type == TYPE_LOAD || gURLs[0].type == TYPE_SCRIPT || gURLs[0].type == TYPE_PRINT) {
+    if (g.urls[0].type == TYPE_LOAD || g.urls[0].type == TYPE_SCRIPT || g.urls[0].type == TYPE_PRINT) {
         // We don't want to snapshot this kind of test
         return false;
     }
 
-    if (!gCurrentCanvas) {
-        gCurrentCanvas = AllocateCanvas();
+    if (!g.currentCanvas) {
+        g.currentCanvas = AllocateCanvas();
     }
 
-    var ctx = gCurrentCanvas.getContext("2d");
-    DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
+    var ctx = g.currentCanvas.getContext("2d");
+    DoDrawWindow(ctx, 0, 0, g.currentCanvas.width, g.currentCanvas.height);
     return true;
 }
 
 function UpdateCurrentCanvasForInvalidation(rects)
 {
     TestBuffer("Updating canvas for invalidation");
 
-    if (!gCurrentCanvas) {
+    if (!g.currentCanvas) {
         return;
     }
 
-    var ctx = gCurrentCanvas.getContext("2d");
+    var ctx = g.currentCanvas.getContext("2d");
     for (var i = 0; i < rects.length; ++i) {
         var r = rects[i];
         // Set left/top/right/bottom to pixel boundaries
         var left = Math.floor(r.left);
         var top = Math.floor(r.top);
         var right = Math.ceil(r.right);
         var bottom = Math.ceil(r.bottom);
 
         // Clamp the values to the canvas size
-        left = Math.max(0, Math.min(left, gCurrentCanvas.width));
-        top = Math.max(0, Math.min(top, gCurrentCanvas.height));
-        right = Math.max(0, Math.min(right, gCurrentCanvas.width));
-        bottom = Math.max(0, Math.min(bottom, gCurrentCanvas.height));
+        left = Math.max(0, Math.min(left, g.currentCanvas.width));
+        top = Math.max(0, Math.min(top, g.currentCanvas.height));
+        right = Math.max(0, Math.min(right, g.currentCanvas.width));
+        bottom = Math.max(0, Math.min(bottom, g.currentCanvas.height));
 
         ctx.save();
         ctx.translate(left, top);
         DoDrawWindow(ctx, left, top, right - left, bottom - top);
         ctx.restore();
     }
 }
 
 function UpdateWholeCurrentCanvasForInvalidation()
 {
     TestBuffer("Updating entire canvas for invalidation");
 
-    if (!gCurrentCanvas) {
+    if (!g.currentCanvas) {
         return;
     }
 
-    var ctx = gCurrentCanvas.getContext("2d");
-    DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
+    var ctx = g.currentCanvas.getContext("2d");
+    DoDrawWindow(ctx, 0, 0, g.currentCanvas.width, g.currentCanvas.height);
 }
 
 function RecordResult(testRunTime, errorMsg, typeSpecificResults)
 {
     TestBuffer("RecordResult fired");
 
     // Keep track of which test was slowest, and how long it took.
-    if (testRunTime > gSlowestTestTime) {
-        gSlowestTestTime = testRunTime;
-        gSlowestTestURL  = gCurrentURL;
+    if (testRunTime > g.slowestTestTime) {
+        g.slowestTestTime = testRunTime;
+        g.slowestTestURL  = g.currentURL;
     }
 
     // Not 'const ...' because of 'EXPECTED_*' value dependency.
     var outputs = {};
     outputs[EXPECTED_PASS] = {
         true:  {s: ["PASS", "PASS"], n: "Pass"},
         false: {s: ["FAIL", "PASS"], n: "UnexpectedFail"}
     };
@@ -1719,45 +870,45 @@ function RecordResult(testRunTime, error
         false: {s: ["FAIL", "FAIL"], n: "Random"}
     };
     // for EXPECTED_FUZZY we need special handling because we can have
     // Pass, UnexpectedPass, or UnexpectedFail
 
     var output;
     var extra;
 
-    if (gURLs[0].type == TYPE_LOAD) {
-        ++gTestResults.LoadOnly;
-        logger.testStatus(gURLs[0].identifier, "(LOAD ONLY)", "PASS", "PASS");
-        gCurrentCanvas = null;
+    if (g.urls[0].type == TYPE_LOAD) {
+        ++g.testResults.LoadOnly;
+        logger.testStatus(g.urls[0].identifier, "(LOAD ONLY)", "PASS", "PASS");
+        g.currentCanvas = null;
         FinishTestItem();
         return;
     }
-    if (gURLs[0].type == TYPE_PRINT) {
-        switch (gState) {
+    if (g.urls[0].type == TYPE_PRINT) {
+        switch (g.state) {
         case 1:
             // First document has been loaded.
-            gTestPrintOutput = typeSpecificResults;
+            g.testPrintOutput = typeSpecificResults;
             // Proceed to load the second document.
             CleanUpCrashDumpFiles();
             StartCurrentURI(2);
             break;
         case 2:
-            let pathToTestPdf = gTestPrintOutput;
+            let pathToTestPdf = g.testPrintOutput;
             let pathToRefPdf = typeSpecificResults;
             comparePdfs(pathToTestPdf, pathToRefPdf, function(error, results) {
-                let expected = gURLs[0].expected;
+                let expected = g.urls[0].expected;
                 // TODO: We should complain here if results is empty!
                 // (If it's empty, we'll spuriously succeed, regardless of
                 // our expectations)
                 if (error) {
                     output = outputs[expected][false];
                     extra = { status_msg: output.n };
-                    ++gTestResults[output.n];
-                    logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1],
+                    ++g.testResults[output.n];
+                    logger.testEnd(g.urls[0].identifier, output.s[0], output.s[1],
                                    error.message, null, extra);
                 } else {
                     let outputPair = outputs[expected];
                     if (expected === EXPECTED_FAIL) {
                        let failureResults = results.filter(function (result) { return !result.passed });
                        if (failureResults.length > 0) {
                          // We got an expected failure. Let's get rid of the
                          // passes from the results so we don't trigger
@@ -1765,50 +916,50 @@ function RecordResult(testRunTime, error
                          results = failureResults;
                        }
                        // (else, we expected a failure but got none!
                        // Leave results untouched so we can log them.)
                     }
                     results.forEach(function(result) {
                         output = outputPair[result.passed];
                         let extra = { status_msg: output.n };
-                        ++gTestResults[output.n];
-                        logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1],
+                        ++g.testResults[output.n];
+                        logger.testEnd(g.urls[0].identifier, output.s[0], output.s[1],
                                        result.description, null, extra);
                     });
                 }
                 FinishTestItem();
             });
             break;
         default:
             throw "Unexpected state.";
         }
         return;
     }
-    if (gURLs[0].type == TYPE_SCRIPT) {
-        var expected = gURLs[0].expected;
+    if (g.urls[0].type == TYPE_SCRIPT) {
+        var expected = g.urls[0].expected;
 
         if (errorMsg) {
             // Force an unexpected failure to alert the test author to fix the test.
             expected = EXPECTED_PASS;
         } else if (typeSpecificResults.length == 0) {
              // This failure may be due to a JavaScript Engine bug causing
              // early termination of the test. If we do not allow silent
              // failure, report an error.
-             if (!gURLs[0].allowSilentFail)
+             if (!g.urls[0].allowSilentFail)
                  errorMsg = "No test results reported. (SCRIPT)\n";
              else
                  logger.info("An expected silent failure occurred");
         }
 
         if (errorMsg) {
             output = outputs[expected][false];
             extra = { status_msg: output.n };
-            ++gTestResults[output.n];
-            logger.testStatus(gURLs[0].identifier, errorMsg, output.s[0], output.s[1], null, null, extra);
+            ++g.testResults[output.n];
+            logger.testStatus(g.urls[0].identifier, errorMsg, output.s[0], output.s[1], null, null, extra);
             FinishTestItem();
             return;
         }
 
         var anyFailed = typeSpecificResults.some(function(result) { return !result.passed; });
         var outputPair;
         if (anyFailed && expected == EXPECTED_FAIL) {
             // If we're marked as expected to fail, and some (but not all) tests
@@ -1820,47 +971,47 @@ function RecordResult(testRunTime, error
         } else {
             outputPair = outputs[expected];
         }
         var index = 0;
         typeSpecificResults.forEach(function(result) {
                 var output = outputPair[result.passed];
                 var extra = { status_msg: output.n };
 
-                ++gTestResults[output.n];
-                logger.testStatus(gURLs[0].identifier, result.description + " item " + (++index),
+                ++g.testResults[output.n];
+                logger.testStatus(g.urls[0].identifier, result.description + " item " + (++index),
                                   output.s[0], output.s[1], null, null, extra);
             });
 
         if (anyFailed && expected == EXPECTED_PASS) {
             FlushTestBuffer();
         }
 
         FinishTestItem();
         return;
     }
 
-    if (gURLs[0]["prefSettings" + gState].length == 0 &&
-        gURICanvases[gCurrentURL]) {
-        gCurrentCanvas = gURICanvases[gCurrentURL];
+    if (g.urls[0]["prefSettings" + g.state].length == 0 &&
+        g.uriCanvases[g.currentURL]) {
+        g.currentCanvas = g.uriCanvases[g.currentURL];
     }
-    if (gCurrentCanvas == null) {
-        logger.error(gCurrentURL, "program error managing snapshots");
-        ++gTestResults.Exception;
+    if (g.currentCanvas == null) {
+        logger.error(g.currentURL, "program error managing snapshots");
+        ++g.testResults.Exception;
     }
-    if (gState == 1) {
-        gCanvas1 = gCurrentCanvas;
+    if (g.state == 1) {
+        g.canvas1 = g.currentCanvas;
     } else {
-        gCanvas2 = gCurrentCanvas;
+        g.canvas2 = g.currentCanvas;
     }
-    gCurrentCanvas = null;
+    g.currentCanvas = null;
 
     ResetRenderingState();
 
-    switch (gState) {
+    switch (g.state) {
         case 1:
             // First document has been loaded.
             // Proceed to load the second document.
 
             CleanUpCrashDumpFiles();
             StartCurrentURI(2);
             break;
         case 2:
@@ -1872,48 +1023,48 @@ function RecordResult(testRunTime, error
             var differences;
             // whether the two renderings match:
             var equal;
             var maxDifference = {};
             // whether the allowed fuzziness from the annotations is exceeded
             // by the actual comparison results
             var fuzz_exceeded = false;
 
-            differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference);
+            differences = g.windowUtils.compareCanvases(g.canvas1, g.canvas2, maxDifference);
             equal = (differences == 0);
 
             if (maxDifference.value > 0 && equal) {
                 throw "Inconsistent result from compareCanvases.";
             }
 
             // what is expected on this platform (PASS, FAIL, or RANDOM)
-            var expected = gURLs[0].expected;
+            var expected = g.urls[0].expected;
 
             if (expected == EXPECTED_FUZZY) {
                 logger.info(`REFTEST fuzzy test ` +
-                            `(${gURLs[0].fuzzyMinDelta}, ${gURLs[0].fuzzyMinPixels}) <= ` +
+                            `(${g.urls[0].fuzzyMinDelta}, ${g.urls[0].fuzzyMinPixels}) <= ` +
                             `(${maxDifference.value}, ${differences}) <= ` +
-                            `(${gURLs[0].fuzzyMaxDelta}, ${gURLs[0].fuzzyMaxPixels})`);
-                fuzz_exceeded = maxDifference.value > gURLs[0].fuzzyMaxDelta ||
-                                differences > gURLs[0].fuzzyMaxPixels;
+                            `(${g.urls[0].fuzzyMaxDelta}, ${g.urls[0].fuzzyMaxPixels})`);
+                fuzz_exceeded = maxDifference.value > g.urls[0].fuzzyMaxDelta ||
+                                differences > g.urls[0].fuzzyMaxPixels;
                 equal = !fuzz_exceeded &&
-                        maxDifference.value >= gURLs[0].fuzzyMinDelta &&
-                        differences >= gURLs[0].fuzzyMinPixels;
+                        maxDifference.value >= g.urls[0].fuzzyMinDelta &&
+                        differences >= g.urls[0].fuzzyMinPixels;
             }
 
-            var failedExtraCheck = gFailedNoPaint || gFailedOpaqueLayer || gFailedAssignedLayer;
+            var failedExtraCheck = g.failedNoPaint || g.failedOpaqueLayer || g.failedAssignedLayer;
 
             // whether the comparison result matches what is in the manifest
-            var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)) && !failedExtraCheck;
+            var test_passed = (equal == (g.urls[0].type == TYPE_REFTEST_EQUAL)) && !failedExtraCheck;
 
             if (expected != EXPECTED_FUZZY) {
                 output = outputs[expected][test_passed];
             } else if (test_passed) {
                 output = {s: ["PASS", "PASS"], n: "Pass"};
-            } else if (gURLs[0].type == TYPE_REFTEST_EQUAL &&
+            } else if (g.urls[0].type == TYPE_REFTEST_EQUAL &&
                        !failedExtraCheck &&
                        !fuzz_exceeded) {
                 // If we get here, that means we had an '==' type test where
                 // at least one of the actual difference values was below the
                 // allowed range, but nothing else was wrong. So let's produce
                 // UNEXPECTED-PASS in this scenario. Also, if we enter this
                 // branch, 'equal' must be false so let's assert that to guard
                 // against logic errors.
@@ -1922,74 +1073,74 @@ function RecordResult(testRunTime, error
                 }
                 output = {s: ["PASS", "FAIL"], n: "UnexpectedPass"};
             } else {
                 // In all other cases we fail the test
                 output = {s: ["FAIL", "PASS"], n: "UnexpectedFail"};
             }
             extra = { status_msg: output.n };
 
-            ++gTestResults[output.n];
+            ++g.testResults[output.n];
 
             // It's possible that we failed both an "extra check" and the normal comparison, but we don't
             // have a way to annotate these separately, so just print an error for the extra check failures.
             if (failedExtraCheck) {
                 var failures = [];
-                if (gFailedNoPaint) {
+                if (g.failedNoPaint) {
                     failures.push("failed reftest-no-paint");
                 }
-                // The gFailed*Messages arrays will contain messages from both the test and the reference.
-                if (gFailedOpaqueLayer) {
-                    failures.push("failed reftest-opaque-layer: " + gFailedOpaqueLayerMessages.join(", "));
+                // The g.failed*Messages arrays will contain messages from both the test and the reference.
+                if (g.failedOpaqueLayer) {
+                    failures.push("failed reftest-opaque-layer: " + g.failedOpaqueLayerMessages.join(", "));
                 }
-                if (gFailedAssignedLayer) {
-                    failures.push("failed reftest-assigned-layer: " + gFailedAssignedLayerMessages.join(", "));
+                if (g.failedAssignedLayer) {
+                    failures.push("failed reftest-assigned-layer: " + g.failedAssignedLayerMessages.join(", "));
                 }
                 var failureString = failures.join(", ");
-                logger.testStatus(gURLs[0].identifier, failureString, output.s[0], output.s[1], null, null, extra);
+                logger.testStatus(g.urls[0].identifier, failureString, output.s[0], output.s[1], null, null, extra);
             } else {
                 var message = "image comparison, max difference: " + maxDifference.value +
                               ", number of differing pixels: " + differences;
                 if (!test_passed && expected == EXPECTED_PASS ||
                     !test_passed && expected == EXPECTED_FUZZY ||
                     test_passed && expected == EXPECTED_FAIL) {
                     if (!equal) {
                         extra.max_difference = maxDifference.value;
                         extra.differences = differences;
-                        var image1 = gCanvas1.toDataURL();
-                        var image2 = gCanvas2.toDataURL();
+                        var image1 = g.canvas1.toDataURL();
+                        var image2 = g.canvas2.toDataURL();
                         extra.reftest_screenshots = [
-                            {url:gURLs[0].identifier[0],
+                            {url:g.urls[0].identifier[0],
                              screenshot: image1.slice(image1.indexOf(",") + 1)},
-                            gURLs[0].identifier[1],
-                            {url:gURLs[0].identifier[2],
+                            g.urls[0].identifier[1],
+                            {url:g.urls[0].identifier[2],
                              screenshot: image2.slice(image2.indexOf(",") + 1)}
                         ];
                         extra.image1 = image1;
                         extra.image2 = image2;
                     } else {
-                        var image1 = gCanvas1.toDataURL();
+                        var image1 = g.canvas1.toDataURL();
                         extra.reftest_screenshots = [
-                            {url:gURLs[0].identifier[0],
+                            {url:g.urls[0].identifier[0],
                              screenshot: image1.slice(image1.indexOf(",") + 1)}
                         ];
                         extra.image1 = image1;
                     }
                 }
-                logger.testStatus(gURLs[0].identifier, message, output.s[0], output.s[1], null, null, extra);
+                logger.testStatus(g.urls[0].identifier, message, output.s[0], output.s[1], null, null, extra);
 
-                if (gNoCanvasCache) {
-                    ReleaseCanvas(gCanvas1);
-                    ReleaseCanvas(gCanvas2);
+                if (g.noCanvasCache) {
+                    ReleaseCanvas(g.canvas1);
+                    ReleaseCanvas(g.canvas2);
                 } else {
-                    if (gURLs[0].prefSettings1.length == 0) {
-                        UpdateCanvasCache(gURLs[0].url1, gCanvas1);
+                    if (g.urls[0].prefSettings1.length == 0) {
+                        UpdateCanvasCache(g.urls[0].url1, g.canvas1);
                     }
-                    if (gURLs[0].prefSettings2.length == 0) {
-                        UpdateCanvasCache(gURLs[0].url2, gCanvas2);
+                    if (g.urls[0].prefSettings2.length == 0) {
+                        UpdateCanvasCache(g.urls[0].url2, g.canvas2);
                     }
                 }
             }
 
             if ((!test_passed && expected == EXPECTED_PASS) || (test_passed && expected == EXPECTED_FAIL)) {
                 FlushTestBuffer();
             }
 
@@ -1998,304 +1149,304 @@ function RecordResult(testRunTime, error
             break;
         default:
             throw "Unexpected state.";
     }
 }
 
 function LoadFailed(why)
 {
-    ++gTestResults.FailedLoad;
+    ++g.testResults.FailedLoad;
     // Once bug 896840 is fixed, this can go away, but for now it will give log
     // output that is TBPL starable for bug 789751 and bug 720452.
     if (!why) {
         logger.error("load failed with unknown reason");
     }
-    logger.testStatus(gURLs[0].identifier, "load failed: " + why, "FAIL", "PASS");
+    logger.testStatus(g.urls[0].identifier, "load failed: " + why, "FAIL", "PASS");
     FlushTestBuffer();
     FinishTestItem();
 }
 
 function RemoveExpectedCrashDumpFiles()
 {
-    if (gExpectingProcessCrash) {
-        for (let crashFilename of gExpectedCrashDumpFiles) {
-            let file = gCrashDumpDir.clone();
+    if (g.expectingProcessCrash) {
+        for (let crashFilename of g.expectedCrashDumpFiles) {
+            let file = g.crashDumpDir.clone();
             file.append(crashFilename);
             if (file.exists()) {
                 file.remove(false);
             }
         }
     }
-    gExpectedCrashDumpFiles.length = 0;
+    g.expectedCrashDumpFiles.length = 0;
 }
 
 function FindUnexpectedCrashDumpFiles()
 {
-    if (!gCrashDumpDir.exists()) {
+    if (!g.crashDumpDir.exists()) {
         return;
     }
 
-    let entries = gCrashDumpDir.directoryEntries;
+    let entries = g.crashDumpDir.directoryEntries;
     if (!entries) {
         return;
     }
 
     let foundCrashDumpFile = false;
     while (entries.hasMoreElements()) {
         let file = entries.getNext().QueryInterface(CI.nsIFile);
         let path = String(file.path);
-        if (path.match(/\.(dmp|extra)$/) && !gUnexpectedCrashDumpFiles[path]) {
+        if (path.match(/\.(dmp|extra)$/) && !g.unexpectedCrashDumpFiles[path]) {
             if (!foundCrashDumpFile) {
-                ++gTestResults.UnexpectedFail;
+                ++g.testResults.UnexpectedFail;
                 foundCrashDumpFile = true;
-                if (gCurrentURL) {
-                    logger.testStatus(gURLs[0].identifier, "crash-check", "FAIL", "PASS", "This test left crash dumps behind, but we weren't expecting it to!");
+                if (g.currentURL) {
+                    logger.testStatus(g.urls[0].identifier, "crash-check", "FAIL", "PASS", "This test left crash dumps behind, but we weren't expecting it to!");
                 } else {
                     logger.error("Harness startup left crash dumps behind, but we weren't expecting it to!");
                 }
             }
             logger.info("Found unexpected crash dump file " + path);
-            gUnexpectedCrashDumpFiles[path] = true;
+            g.unexpectedCrashDumpFiles[path] = true;
         }
     }
 }
 
 function RemovePendingCrashDumpFiles()
 {
-    if (!gPendingCrashDumpDir.exists()) {
+    if (!g.pendingCrashDumpDir.exists()) {
         return;
     }
 
-    let entries = gPendingCrashDumpDir.directoryEntries;
+    let entries = g.pendingCrashDumpDir.directoryEntries;
     while (entries.hasMoreElements()) {
         let file = entries.getNext().QueryInterface(CI.nsIFile);
         if (file.isFile()) {
           file.remove(false);
           logger.info("This test left pending crash dumps; deleted "+file.path);
         }
     }
 }
 
 function CleanUpCrashDumpFiles()
 {
     RemoveExpectedCrashDumpFiles();
     FindUnexpectedCrashDumpFiles();
-    if (gCleanupPendingCrashes) {
+    if (g.cleanupPendingCrashes) {
       RemovePendingCrashDumpFiles();
     }
-    gExpectingProcessCrash = false;
+    g.expectingProcessCrash = false;
 }
 
 function FinishTestItem()
 {
-    logger.testEnd(gURLs[0].identifier, "OK");
+    logger.testEnd(g.urls[0].identifier, "OK");
 
     // Replace document with BLANK_URL_FOR_CLEARING in case there are
     // assertions when unloading.
     logger.debug("Loading a blank page");
     // After clearing, content will notify us of the assertion count
     // and tests will continue.
     SendClear();
-    gFailedNoPaint = false;
-    gFailedOpaqueLayer = false;
-    gFailedOpaqueLayerMessages = [];
-    gFailedAssignedLayer = false;
-    gFailedAssignedLayerMessages = [];
+    g.failedNoPaint = false;
+    g.failedOpaqueLayer = false;
+    g.failedOpaqueLayerMessages = [];
+    g.failedAssignedLayer = false;
+    g.failedAssignedLayerMessages = [];
 }
 
 function DoAssertionCheck(numAsserts)
 {
-    if (gDebug.isDebugBuild) {
-        if (gBrowserIsRemote) {
+    if (g.debug.isDebugBuild) {
+        if (g.browserIsRemote) {
             // Count chrome-process asserts too when content is out of
             // process.
-            var newAssertionCount = gDebug.assertionCount;
-            var numLocalAsserts = newAssertionCount - gAssertionCount;
-            gAssertionCount = newAssertionCount;
+            var newAssertionCount = g.debug.assertionCount;
+            var numLocalAsserts = newAssertionCount - g.assertionCount;
+            g.assertionCount = newAssertionCount;
 
             numAsserts += numLocalAsserts;
         }
 
-        var minAsserts = gURLs[0].minAsserts;
-        var maxAsserts = gURLs[0].maxAsserts;
+        var minAsserts = g.urls[0].minAsserts;
+        var maxAsserts = g.urls[0].maxAsserts;
 
-        logger.assertionCount(gURLs[0].identifier, numAsserts, minAsserts, maxAsserts);
+        logger.assertionCount(g.urls[0].identifier, numAsserts, minAsserts, maxAsserts);
     }
 
-    if (gURLs[0].chaosMode) {
-        gWindowUtils.leaveChaosMode();
+    if (g.urls[0].chaosMode) {
+        g.windowUtils.leaveChaosMode();
     }
 
     // And start the next test.
-    gURLs.shift();
+    g.urls.shift();
     StartCurrentTest();
 }
 
 function ResetRenderingState()
 {
     SendResetRenderingState();
     // We would want to clear any viewconfig here, if we add support for it
 }
 
 function RestoreChangedPreferences()
 {
-    if (gPrefsToRestore.length > 0) {
+    if (g.prefsToRestore.length > 0) {
         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                     getService(Components.interfaces.nsIPrefBranch);
-        gPrefsToRestore.reverse();
-        gPrefsToRestore.forEach(function(ps) {
+        g.prefsToRestore.reverse();
+        g.prefsToRestore.forEach(function(ps) {
             var value = ps.value;
             if (ps.type == PREF_BOOLEAN) {
                 prefs.setBoolPref(ps.name, value);
             } else if (ps.type == PREF_STRING) {
                 prefs.setCharPref(ps.name, value);
                 value = '"' + value + '"';
             } else if (ps.type == PREF_INTEGER) {
                 prefs.setIntPref(ps.name, value);
             }
             logger.info("RESTORE PREFERENCE pref(" + ps.name + "," + value + ")");
         });
-        gPrefsToRestore = [];
+        g.prefsToRestore = [];
     }
 }
 
 function RegisterMessageListenersAndLoadContentScript()
 {
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:AssertionCount",
         function (m) { RecvAssertionCount(m.json.count); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:ContentReady",
         function (m) { return RecvContentReady(m.data); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:Exception",
         function (m) { RecvException(m.json.what) }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:FailedLoad",
         function (m) { RecvFailedLoad(m.json.why); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:FailedNoPaint",
         function (m) { RecvFailedNoPaint(); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:FailedOpaqueLayer",
         function (m) { RecvFailedOpaqueLayer(m.json.why); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:FailedAssignedLayer",
         function (m) { RecvFailedAssignedLayer(m.json.why); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:InitCanvasWithSnapshot",
         function (m) { return RecvInitCanvasWithSnapshot(); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:Log",
         function (m) { RecvLog(m.json.type, m.json.msg); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:ScriptResults",
         function (m) { RecvScriptResults(m.json.runtimeMs, m.json.error, m.json.results); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:PrintResult",
         function (m) { RecvPrintResult(m.json.runtimeMs, m.json.status, m.json.fileName); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:TestDone",
         function (m) { RecvTestDone(m.json.runtimeMs); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:UpdateCanvasForInvalidation",
         function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:UpdateWholeCanvasForInvalidation",
         function (m) { RecvUpdateWholeCanvasForInvalidation(); }
     );
-    gBrowserMessageManager.addMessageListener(
+    g.browserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
 
-    gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true, true);
+    g.browserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true, true);
 }
 
 function RecvAssertionCount(count)
 {
     DoAssertionCheck(count);
 }
 
 function RecvContentReady(info)
 {
-    gContentGfxInfo = info.gfx;
+    g.contentGfxInfo = info.gfx;
     InitAndStartRefTests();
-    return { remote: gBrowserIsRemote };
+    return { remote: g.browserIsRemote };
 }
 
 function RecvException(what)
 {
-    logger.error(gCurrentURL + " | " + what);
-    ++gTestResults.Exception;
+    logger.error(g.currentURL + " | " + what);
+    ++g.testResults.Exception;
 }
 
 function RecvFailedLoad(why)
 {
     LoadFailed(why);
 }
 
 function RecvFailedNoPaint()
 {
-    gFailedNoPaint = true;
+    g.failedNoPaint = true;
 }
 
 function RecvFailedOpaqueLayer(why) {
-    gFailedOpaqueLayer = true;
-    gFailedOpaqueLayerMessages.push(why);
+    g.failedOpaqueLayer = true;
+    g.failedOpaqueLayerMessages.push(why);
 }
 
 function RecvFailedAssignedLayer(why) {
-    gFailedAssignedLayer = true;
-    gFailedAssignedLayerMessages.push(why);
+    g.failedAssignedLayer = true;
+    g.failedAssignedLayerMessages.push(why);
 }
 
 function RecvInitCanvasWithSnapshot()
 {
     var painted = InitCurrentCanvasWithSnapshot();
     return { painted: painted };
 }
 
 function RecvLog(type, msg)
 {
     msg = "[CONTENT] " + msg;
     if (type == "info") {
         TestBuffer(msg);
     } else if (type == "warning") {
         logger.warning(msg);
     } else {
-        logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | unknown log type " + type + "\n");
-        ++gTestResults.Exception;
+        logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + g.currentURL + " | unknown log type " + type + "\n");
+        ++g.testResults.Exception;
     }
 }
 
 function RecvScriptResults(runtimeMs, error, results)
 {
     RecordResult(runtimeMs, error, results);
 }
 
 function RecvPrintResult(runtimeMs, status, fileName)
 {
     if (!Components.isSuccessCode(status)) {
-      logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | error during printing\n");
-      ++gTestResults.Exception;
+      logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + g.currentURL + " | error during printing\n");
+      ++g.testResults.Exception;
     }
     RecordResult(runtimeMs, '', fileName);
 }
 
 function RecvTestDone(runtimeMs)
 {
     RecordResult(runtimeMs, '', [ ]);
 }
@@ -2315,61 +1466,61 @@ function OnProcessCrashed(subject, topic
     var id;
     subject = subject.QueryInterface(CI.nsIPropertyBag2);
     if (topic == "plugin-crashed") {
         id = subject.getPropertyAsAString("pluginDumpID");
     } else if (topic == "ipc:content-shutdown") {
         id = subject.getPropertyAsAString("dumpID");
     }
     if (id) {
-        gExpectedCrashDumpFiles.push(id + ".dmp");
-        gExpectedCrashDumpFiles.push(id + ".extra");
+        g.expectedCrashDumpFiles.push(id + ".dmp");
+        g.expectedCrashDumpFiles.push(id + ".extra");
     }
 }
 
 function RegisterProcessCrashObservers()
 {
     var os = CC[NS_OBSERVER_SERVICE_CONTRACTID]
              .getService(CI.nsIObserverService);
     os.addObserver(OnProcessCrashed, "plugin-crashed");
     os.addObserver(OnProcessCrashed, "ipc:content-shutdown");
 }
 
 function RecvExpectProcessCrash()
 {
-    gExpectingProcessCrash = true;
+    g.expectingProcessCrash = true;
 }
 
 function SendClear()
 {
-    gBrowserMessageManager.sendAsyncMessage("reftest:Clear");
+    g.browserMessageManager.sendAsyncMessage("reftest:Clear");
 }
 
 function SendLoadScriptTest(uri, timeout)
 {
-    gBrowserMessageManager.sendAsyncMessage("reftest:LoadScriptTest",
+    g.browserMessageManager.sendAsyncMessage("reftest:LoadScriptTest",
                                             { uri: uri, timeout: timeout });
 }
 
 function SendLoadPrintTest(uri, timeout)
 {
-    gBrowserMessageManager.sendAsyncMessage("reftest:LoadPrintTest",
+    g.browserMessageManager.sendAsyncMessage("reftest:LoadPrintTest",
                                             { uri: uri, timeout: timeout });
 }
 
 function SendLoadTest(type, uri, timeout)
 {
-    gBrowserMessageManager.sendAsyncMessage("reftest:LoadTest",
+    g.browserMessageManager.sendAsyncMessage("reftest:LoadTest",
                                             { type: type, uri: uri, timeout: timeout }
     );
 }
 
 function SendResetRenderingState()
 {
-    gBrowserMessageManager.sendAsyncMessage("reftest:ResetRenderingState");
+    g.browserMessageManager.sendAsyncMessage("reftest:ResetRenderingState");
 }
 
 function readPdf(path, callback) {
     OS.File.open(path, { read: true }).then(function (file) {
         file.flush().then(function() {
             file.read().then(function (data) {
                 let fakePort = new PDFJS.main.LoopbackPort(true);
                 PDFJS.worker.WorkerMessageHandler.initializeFromPort(fakePort);
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -481,17 +481,17 @@ android.applicationVariants.all { varian
         source = variant.javaCompile.source
         classpath = variant.javaCompile.classpath
 
         excludeFilter = file("findbugs-exclude.xml")
         dependsOn "assemble${variant.name.capitalize()}"
 
         reports {
             html.enabled = true // HTML reports for humans.
-            html.destination = "$project.buildDir/outputs/findbugs/findbugs-${variant.name}-output.html"
+            html.destination = "$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.html"
             xml.enabled = false
         }
     }
 
     task("findbugsXml${variant.name.capitalize()}", type: FindBugs) {
         // TODO: figure out how to share the shared configuration.
         description "Analyze ${variant.name} code with findbugs (XML report)"
         group "Verification"
@@ -505,17 +505,17 @@ android.applicationVariants.all { varian
         source = variant.javaCompile.source
         classpath = variant.javaCompile.classpath
 
         excludeFilter = file("findbugs-exclude.xml")
         dependsOn "assemble${variant.name.capitalize()}"
 
         reports {
             xml.enabled = true // XML reports for machines.
-            xml.destination = "$project.buildDir/outputs/findbugs/findbugs-${variant.name}-output.xml"
+            xml.destination = "$project.buildDir/reports/findbugs/findbugs-${variant.name}-output.xml"
             html.enabled = false
         }
     }
 }
 
 // Bug 1353055 - Strip 'vars' debugging information to agree with moz.build.
 apply from: "${topsrcdir}/mobile/android/gradle/debug_level.gradle"
 android.applicationVariants.all configureVariantDebugLevel
--- a/mobile/android/app/lint.xml
+++ b/mobile/android/app/lint.xml
@@ -35,16 +35,19 @@
     <issue id="MissingPermission" severity="warning" />
     <issue id="OnClick" severity="warning" />
     <issue id="ReferenceType" severity="warning" />
     <issue id="ResourceAsColor" severity="warning" />
     <issue id="ResourceType" severity="warning" />
     <issue id="ValidFragment" severity="warning" />
     <issue id="WrongConstant" severity="warning" />
 
+    <!-- New Android-Gradle lint integration regressed this check. -->
+    <issue id="MissingRegistered" severity="warning" />
+
     <!-- Fixes are in progress but we would like to block future candidates.
          The ** in the path are wildcards. We need these wildcards to not change our code structure.
          See: http://stackoverflow.com/questions/43994420/what-path-is-the-issue-ignore-path-element-in-lint-xml-relative-to -->
     <issue id="NewApi" severity="error">
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStreamPreference.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadContentTelemetry.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/preferences/LocaleListPreference.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java"/>
@@ -62,16 +65,40 @@
         <ignore path="src/photon/res/values/styles.xml"/>
     </issue>
 
     <!-- We fixed all "Registered" lint errors. However the current gradle plugin has a bug where
          it ignores @SuppressLint annotations for this check. See CrashReporter class and
          https://code.google.com/p/android/issues/detail?id=204846 -->
     <issue id="Registered" severity="warning" />
 
+    <issue id="ObjectAnimatorBinding" severity="error">
+        <!-- Two animated properties are provided by the underlying
+             View implementation. -->
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/firstrun/FirstrunPager.java"/>
+    </issue>
+
+    <!-- Tracked by Bug 1409550. -->
+    <issue id="AppCompatCustomView" severity="error">
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/firstrun/FirstrunPager.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/home/PanelHeaderView.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/menu/MenuItemDefault.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/tabs/TabPanelBackButton.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/AllCapsTextView.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/ClickableWhenDisabledEditText.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/EllipsisTextView.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/SquaredImageView.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/themed/ThemedEditText.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/themed/ThemedImageButton.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/themed/ThemedImageView.java"/>
+        <ignore path="**/mobile/android/base/java/org/mozilla/gecko/widget/themed/ThemedTextView.java"/>
+        <ignore path="**/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java"/>
+        <ignore path="**/mobile/android/search/java/org/mozilla/search/ui/FacetBar.java"/>
+    </issue>
+
     <!-- WHEN YOU FIX A LINT WARNING, ADD IT TO THIS LIST.
 
          We want all lint warnings to be fatal errors.
          This is the list of checks that we've explicitly
          set as errors. Ideally, once we have no more warnings,
          we switch to the `warningsAsErrors` lint option
          (bug 1253737) rather than listing everything explicitly. -->
     <issue id="AaptCrash" severity="error" />
@@ -146,17 +173,16 @@
     <issue id="ManifestTypo" severity="error" />
     <issue id="MenuTitle" severity="error" />
     <issue id="MergeRootFrame" severity="error" />
     <issue id="MipmapIcons" severity="error" />
     <issue id="MissingApplicationIcon" severity="error" />
     <issue id="MissingId" severity="error" />
     <issue id="MissingPrefix" severity="error" />
     <issue id="MissingQuantity" severity="error" />
-    <issue id="MissingRegistered" severity="error" />
     <issue id="MissingSuperCall" severity="error" />
     <issue id="MissingTranslation" severity="error" />
     <issue id="MissingVersion" severity="error" />
     <issue id="MockLocation" severity="error" />
     <issue id="MultipleUsesSdk" severity="error" />
     <issue id="NamespaceTypo" severity="error" />
     <issue id="NestedScrolling" severity="error" />
     <issue id="NfcTechWhitespace" severity="error" />
--- a/mobile/android/app/src/main/res/values-v21/integers.xml
+++ b/mobile/android/app/src/main/res/values-v21/integers.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
 
-    <integer name="search_assist_launch_res">@drawable/search_launcher</integer>
+    <integer name="search_assist_launch_res" tools:ignore="ReferenceType">@drawable/search_launcher</integer>
 
 </resources>
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -56,16 +56,17 @@ import org.mozilla.gecko.prompts.PromptS
 import org.mozilla.gecko.text.TextSelection;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.ColorUtil;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
 import org.mozilla.gecko.util.PackageUtil;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.webapps.WebApps;
 import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.GeckoPopupMenu;
 
 import java.util.List;
 
 public class CustomTabsActivity extends AppCompatActivity
                                 implements ActionModePresenter,
                                            GeckoMenu.Callback,
@@ -648,72 +649,29 @@ public class CustomTabsActivity extends 
         }
     }
 
     @Override
     public void onContextMenu(GeckoView view, int screenX, int screenY,
                               final String uri, final String elementSrc) {
 
         final String content = uri != null ? uri : elementSrc != null ? elementSrc : "";
-        final Uri validUri = getValidURL(content);
+        final Uri validUri = WebApps.getValidURL(content);
         if (validUri == null) {
             return;
         }
 
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                openInFennec(validUri, CustomTabsActivity.this);
+                WebApps.openInFennec(validUri, CustomTabsActivity.this);
             }
         });
     }
 
-    void openInFennec(final Uri uri, final Context context) {
-        ThreadUtils.assertOnUiThread();
-
-        final Prompt prompt = new Prompt(context, new Prompt.PromptCallback() {
-            @Override
-            public void onPromptFinished(final GeckoBundle result) {
-
-                final int itemId = result.getInt("button", -1);
-
-                if (itemId == -1) {
-                    // this is the error case, we shouldn't have this situation.
-                    return;
-                }
-                Intent intent = new Intent(context, BrowserApp.class);
-                // BrowserApp's onNewIntent will check action so below is required
-                intent.setAction(Intent.ACTION_VIEW);
-                intent.setData(uri);
-                intent.setPackage(context.getPackageName());
-                context.startActivity(intent);
-
-            }
-        });
-
-        final PromptListItem[] items = new PromptListItem[1];
-        items[0] = new PromptListItem(context.getResources().getString(R.string.overlay_share_open_browser_btn_label));
-        prompt.show("", "", items, ListView.CHOICE_MODE_NONE);
-
-    }
-
-    @Nullable
-    Uri getValidURL(@NonNull String urlString) {
-        final Uri uri = Uri.parse(urlString);
-        if (uri == null) {
-            return null;
-        }
-        final String scheme = uri.getScheme();
-        // currently we only support http and https to open in Firefox
-        if (scheme.equals("http") || scheme.equals("https")) {
-            return uri;
-        } else {
-            return null;
-        }
-    }
 
     @Override // ActionModePresenter
     public void startActionMode(final ActionMode.Callback callback) {
         endActionMode();
         mActionMode = startSupportActionMode(callback);
     }
 
     @Override // ActionModePresenter
--- a/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenuInflater.java
+++ b/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenuInflater.java
@@ -1,24 +1,24 @@
 /* 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/. */
 
 package org.mozilla.gecko.menu;
 
 import java.io.IOException;
 
-import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.R;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.support.annotation.XmlRes;
 import android.util.AttributeSet;
 import android.util.Xml;
 import android.view.InflateException;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.SubMenu;
 
@@ -45,17 +45,17 @@ public class GeckoMenuInflater extends M
     }
 
     public GeckoMenuInflater(Context context) {
         super(context);
         mContext = context;
     }
 
     @Override
-    public void inflate(int menuRes, Menu menu) {
+    public void inflate(@XmlRes int menuRes, Menu menu) {
 
         // This does not check for a well-formed XML.
 
         XmlResourceParser parser = null;
         try {
             parser = mContext.getResources().getXml(menuRes);
             AttributeSet attrs = Xml.asAttributeSet(parser);
 
--- a/mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java
@@ -142,18 +142,18 @@ public class UpdateService extends Inten
     @Override
     public void onCreate () {
         mCrashHandler = CrashHandler.createDefaultCrashHandler(getApplicationContext());
 
         super.onCreate();
 
         mPrefs = getSharedPreferences(PREFS_NAME, 0);
         mNotificationManager = NotificationManagerCompat.from(this);
-        mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWifiLock = ((WifiManager)getSystemService(Context.WIFI_SERVICE))
+        mConnectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
                     .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, PREFS_NAME);
         mCancelDownload = false;
     }
 
     @Override
     public void onDestroy() {
         mCrashHandler.unregister();
         mCrashHandler = null;
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -88,17 +88,30 @@ public class WebAppActivity extends AppC
 
         super.onCreate(savedInstanceState);
 
         mGeckoView = new GeckoView(this);
         mGeckoView.setNavigationListener(this);
         mGeckoView.setContentListener(new GeckoView.ContentListener() {
             public void onTitleChange(GeckoView view, String title) {}
             public void onContextMenu(GeckoView view, int screenX, int screenY,
-                               String uri, String elementSrc) {}
+                               String uri, String elementSrc) {
+                final String content = uri != null ? uri : elementSrc != null ? elementSrc : "";
+                final Uri validUri = WebApps.getValidURL(content);
+                if (validUri == null) {
+                    return;
+                }
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        WebApps.openInFennec(validUri, WebAppActivity.this);
+                    }
+                });
+            }
             public void onFullScreen(GeckoView view, boolean fullScreen) {
                 updateFullScreenContent(fullScreen);
             }
         });
 
         mPromptService = new PromptService(this, mGeckoView.getEventDispatcher());
         mDoorHangerPopup = new DoorHangerPopup(this, mGeckoView.getEventDispatcher());
 
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebApps.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebApps.java
@@ -1,15 +1,29 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.webapps;
 
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.ListView;
+
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.prompts.Prompt;
+import org.mozilla.gecko.prompts.PromptListItem;
+import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.ThreadUtils;
+
 /**
  * 10 predefined slots for homescreen webapps, in LauncherActivity
  * launched webapps will be given an index (via WebAppIndexer) that
  * points to one of these class names
  **/
 
 public final class WebApps {
     public static class WebApp0 extends WebAppActivity { }
@@ -17,9 +31,53 @@ public final class WebApps {
     public static class WebApp2 extends WebAppActivity { }
     public static class WebApp3 extends WebAppActivity { }
     public static class WebApp4 extends WebAppActivity { }
     public static class WebApp5 extends WebAppActivity { }
     public static class WebApp6 extends WebAppActivity { }
     public static class WebApp7 extends WebAppActivity { }
     public static class WebApp8 extends WebAppActivity { }
     public static class WebApp9 extends WebAppActivity { }
+
+    public static void openInFennec(final Uri uri, final Context context) {
+        ThreadUtils.assertOnUiThread();
+
+        final Prompt prompt = new Prompt(context, new Prompt.PromptCallback() {
+            @Override
+            public void onPromptFinished(final GeckoBundle result) {
+
+                final int itemId = result.getInt("button", -1);
+
+                if (itemId == -1) {
+                    // this is the error case, we shouldn't have this situation.
+                    return;
+                }
+                Intent intent = new Intent(context, BrowserApp.class);
+                // BrowserApp's onNewIntent will check action so below is required
+                intent.setAction(Intent.ACTION_VIEW);
+                intent.setData(uri);
+                intent.setPackage(context.getPackageName());
+                context.startActivity(intent);
+
+            }
+        });
+
+        final PromptListItem[] items = new PromptListItem[1];
+        items[0] = new PromptListItem(context.getResources().getString(R.string.overlay_share_open_browser_btn_label));
+        prompt.show("", "", items, ListView.CHOICE_MODE_NONE);
+
+    }
+
+    @Nullable
+    public static Uri getValidURL(@NonNull String urlString) {
+        final Uri uri = Uri.parse(urlString);
+        if (uri == null) {
+            return null;
+        }
+        final String scheme = uri.getScheme();
+        // currently we only support http and https to open in Firefox
+        if (scheme.equals("http") || scheme.equals("https")) {
+            return uri;
+        } else {
+            return null;
+        }
+    }
 }
--- a/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java
+++ b/mobile/android/bouncer/java/org/mozilla/bouncer/BouncerService.java
@@ -90,22 +90,18 @@ public class BouncerService extends Inte
                 getFiles(path + "/" + file, acc);
             }
         } else {
             // We're a file -- accumulate.
             acc.add(path);
         }
     }
 
-    private String getDataDir() {
-        return getApplicationInfo().dataDir;
-    }
-
     private File getDataFile(final String path) {
-        File outFile = new File(getDataDir(), path);
+        File outFile = new File(getApplicationInfo().dataDir, path);
         File dir = outFile.getParentFile();
 
         if (dir != null && !dir.exists()) {
             Log.d(LOGTAG, "Creating " + dir.getAbsolutePath());
             if (!dir.mkdirs()) {
                 Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath());
                 return null;
             }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -23,31 +23,33 @@ import org.mozilla.gecko.util.GeckoBundl
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
 
 public class GeckoView extends LayerView {
 
     private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
@@ -304,16 +306,17 @@ public class GeckoView extends LayerView
      * @param delegate PermissionDelegate instance or null to use the default delegate.
      */
     public void setPermissionDelegate(final PermissionDelegate delegate) {
         mPermissionHandler.setListener(delegate, this);
     }
 
     private PromptDelegate mPromptDelegate;
     private InputConnectionListener mInputConnectionListener;
+    private boolean mIsResettingFocus;
 
     private GeckoViewSettings mSettings;
 
     protected String mChromeUri;
     protected int mScreenId = 0; // default to the primary screen
 
     @WrapForJNI(dispatchTo = "proxy")
     protected static final class Window extends JNIObject {
@@ -702,16 +705,55 @@ public class GeckoView extends LayerView
         mEventDispatcher.dispatch("GeckoView:SetActive", msg);
     }
 
     public GeckoViewSettings getSettings() {
         return mSettings;
     }
 
     @Override
+    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+
+        if (gainFocus && !mIsResettingFocus) {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!isFocused()) {
+                        return;
+                    }
+
+                    final InputMethodManager imm = InputMethods.getInputMethodManager(getContext());
+                    // Bug 1404111:
+                    // Through View#onFocusChanged, the InputMethodManager queues up a checkFocus
+                    // call for the next spin of the message loop, so by posting this Runnable after
+                    // super#onFocusChanged, the IMM should have completed its focus change handling
+                    // at this point and we should be the active view for input handling.
+
+                    // If however onViewDetachedFromWindow for the previously active view gets
+                    // called *after* onFocusChanged, but *before* the focus change has been fully
+                    // processed by the IMM with the help of checkFocus, the IMM will lose track of
+                    // the currently active view, which means that we can't interact with the IME.
+                    if (!imm.isActive(GeckoView.this)) {
+                        // If that happens, we bring the IMM's internal state back into sync by
+                        // clearing and resetting our focus.
+                        mIsResettingFocus = true;
+                        clearFocus();
+                        // After calling clearFocus we might regain focus automatically, but we
+                        // explicitly request it again in case this doesn't happen.
+                        // If we've already got the focus back, this will then be a no-op anyway.
+                        requestFocus();
+                        mIsResettingFocus = false;
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
     public Handler getHandler() {
         if (mInputConnectionListener != null) {
             return mInputConnectionListener.getHandler(super.getHandler());
         }
         return super.getHandler();
     }
 
     @Override
@@ -1661,34 +1703,34 @@ public class GeckoView extends LayerView
              * @param id ID of the selected item.
              */
             void confirm(String id);
 
             /**
              * Called by the prompt implementation when the multiple-choice list is
              * dismissed by the user.
              *
-             * @param id IDs of the selected items.
+             * @param ids IDs of the selected items.
              */
             void confirm(String[] ids);
 
             /**
              * Called by the prompt implementation when the menu or single-choice list is
              * dismissed by the user.
              *
              * @param item Bundle representing the selected item; must be an original
              *             GeckoBundle object that was passed to the implementation.
              */
             void confirm(GeckoBundle item);
 
             /**
              * Called by the prompt implementation when the multiple-choice list is
              * dismissed by the user.
              *
-             * @param item Bundle array representing the selected items; must be original
+             * @param items Bundle array representing the selected items; must be original
              *             GeckoBundle objects that were passed to the implementation.
              */
             void confirm(GeckoBundle[] items);
         }
 
         /**
          * Display choices in a menu that dismisses as soon as an item is chosen.
          */
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -32,17 +32,17 @@
 // That arrangement labels them nicely in IntelliJ.  See the comment in the
 // :omnijar project for more context.
 evaluationDependsOn(':omnijar')
 
 task buildOmnijar(type:Exec) {
     dependsOn rootProject.generateCodeAndResources
 
     // See comment in :omnijar project regarding interface mismatches here.
-    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs
+    inputs.file(project(':omnijar').sourceSets.main.resources.srcDirs).skipWhenEmpty() 
 
     // Produce a single output file.
     outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
 
     workingDir "${topobjdir}"
 
     commandLine mozconfig.substs.GMAKE
     args '-C'
@@ -66,30 +66,34 @@ task syncOmnijarFromDistDir(type: Sync) 
     into("${project.buildDir}/generated/omnijar")
     from("${topobjdir}/dist/${omnijar_dir}/omni.ja",
          "${topobjdir}/dist/${omnijar_dir}/assets/omni.ja") {
         // Throw an exception if we find multiple, potentially conflicting omni.ja files.
         duplicatesStrategy 'fail'
     }
 }
 
-task checkLibsExistInDistDir<< {
-    if (syncLibsFromDistDir.source.empty) {
-        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
+task checkLibsExistInDistDir {
+    doLast {
+        if (syncLibsFromDistDir.source.empty) {
+            throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
+        }
     }
 }
 
 task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
     into("${project.buildDir}/generated/jniLibs")
     from("${topobjdir}/dist/fennec/lib")
 }
 
-task checkAssetsExistInDistDir<< {
-    if (syncAssetsFromDistDir.source.empty) {
-        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
+task checkAssetsExistInDistDir {
+    doLast {
+        if (syncAssetsFromDistDir.source.empty) {
+            throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
+        }
     }
 }
 
 task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
     into("${project.buildDir}/generated/assets")
     from("${topobjdir}/dist/fennec/assets") {
         exclude 'omni.ja'
     }
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -130,21 +130,21 @@ class MachCommands(MachCommandBase):
         # Android Lint produces both HTML and XML reports.  Visit the
         # XML report(s) to report errors and link to the HTML
         # report(s) for human consumption.
         import xml.etree.ElementTree as ET
 
         if 'TASK_ID' in os.environ and 'RUN_ID' in os.environ:
             root_url = "https://queue.taskcluster.net/v1/task/{}/runs/{}/artifacts/public/android/lint".format(os.environ['TASK_ID'], os.environ['RUN_ID'])
         else:
-            root_url = os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/outputs')
+            root_url = os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/reports')
 
         reports = ('officialPhotonDebug',)
         for report in reports:
-            f = open(os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/outputs/lint-results-{}.xml'.format(report)), 'rt')
+            f = open(os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/reports/lint-results-{}.xml'.format(report)), 'rt')
             tree = ET.parse(f)
             root = tree.getroot()
 
             print('SUITE-START | android-lint | {}'.format(report))
             for issue in root.findall("issue[@severity='Error']"):
                 # There's no particular advantage to formatting the
                 # error, so for now let's just output the <issue> XML
                 # tag.
@@ -225,22 +225,22 @@ class MachCommands(MachCommandBase):
         # Findbug produces both HTML and XML reports.  Visit the
         # XML report(s) to report errors and link to the HTML
         # report(s) for human consumption.
         import xml.etree.ElementTree as ET
 
         if 'TASK_ID' in os.environ and 'RUN_ID' in os.environ:
             root_url = "https://queue.taskcluster.net/v1/task/{}/runs/{}/artifacts/public/artifacts/findbugs".format(os.environ['TASK_ID'], os.environ['RUN_ID'])
         else:
-            root_url = os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/outputs/findbugs')
+            root_url = os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/reports/findbugs')
 
         reports = ('findbugs-officialPhotonDebug-output.xml',)
         for report in reports:
             try:
-                f = open(os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/outputs/findbugs', report), 'rt')
+                f = open(os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/reports/findbugs', report), 'rt')
             except IOError:
                 continue
 
             tree = ET.parse(f)
             root = tree.getroot()
 
             print('SUITE-START | android-findbugs | {}'.format(report))
             for error in root.findall('./BugInstance'):
@@ -311,17 +311,17 @@ class MachCommands(MachCommandBase):
         # filter strings.xml, which is really UTF-8; the ellipsis character is
         # replaced with ??? in some encodings (including ASCII).  It's not yet
         # possible to filter with encodings in Gradle
         # (https://github.com/gradle/gradle/pull/520) and it's challenging to
         # do our filtering with Gradle's Ant support.  Moreover, all of the
         # Android tools expect UTF-8: see
         # http://tools.android.com/knownissues/encoding.  See
         # http://stackoverflow.com/a/21267635 for discussion of this approach.
-        return self.run_process([self.substs['GRADLE']] + gradle_flags + args,
+        return self.run_process([self.substs['GRADLE']] + gradle_flags + ['--console=plain'] + args,
             append_env={
                 'GRADLE_OPTS': '-Dfile.encoding=utf-8',
                 'JAVA_HOME': java_home,
             },
             pass_thru=True, # Allow user to run gradle interactively.
             ensure_exit_code=False, # Don't throw on non-zero exit code.
             cwd=mozpath.join(self.topsrcdir))
 
--- a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
@@ -105,16 +105,17 @@ public class PostSearchFragment extends 
         private boolean networkError;
 
         @Override
         public void onPageStarted(WebView view, final String url, Bitmap favicon) {
             // Reset the error state.
             networkError = false;
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             // Ignore about:blank URL loads and the first results page we try to load.
             if (TextUtils.equals(url, Constants.ABOUT_BLANK) || resultsPageHost == null) {
                 return false;
             }
 
             String host = null;
--- a/mobile/android/stumbler/moz.build
+++ b/mobile/android/stumbler/moz.build
@@ -7,9 +7,11 @@
 with Files('**'):
     BUG_COMPONENT = ('Android Background Services', 'Geolocation')
 
 include('stumbler_sources.mozbuild')
 
 stumbler_jar = add_java_jar('stumbler')
 stumbler_jar.sources += stumbler_sources
 stumbler_jar.extra_jars += [CONFIG['ANDROID_SUPPORT_V4_AAR_LIB']]
-stumbler_jar.javac_flags += ['-Xlint:all']
+# Android has deprecated most of the GPS interfaces stumbler uses, so
+# we need -deprecation.
+stumbler_jar.javac_flags += ['-Xlint:all,-deprecation']
--- a/mobile/android/tests/browser/junit3/moz.build
+++ b/mobile/android/tests/browser/junit3/moz.build
@@ -26,17 +26,20 @@ jar.sources += [
     'src/org/mozilla/tests/browser/junit3/TestGeckoProfilesProvider.java',
     'src/org/mozilla/tests/browser/junit3/TestGeckoSharedPrefs.java',
     'src/org/mozilla/tests/browser/junit3/TestImageDownloader.java',
     'src/org/mozilla/tests/browser/junit3/TestJarReader.java',
     'src/org/mozilla/tests/browser/junit3/TestRawResource.java',
     'src/org/mozilla/tests/browser/junit3/TestSuggestedSites.java',
 ]
 jar.generated_sources = [] # None yet -- try to keep it this way.
-jar.javac_flags += ['-Xlint:all']
+# MockResources has been deprecated, so we need -deprecation.  See
+# https://github.com/Countly/countly-sdk-android/blob/ae89490a5646e7db09c855f62b35c861819300cd/sdk/src/androidTest/java/ly/count/android/sdk/DeviceInfoTests.java#L80
+# for a Mockito-based approach.
+jar.javac_flags += ['-Xlint:all,-deprecation']
 
 jar.extra_jars += [
     CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
     CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB'],
     TOPOBJDIR + '/mobile/android/base/constants.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-R.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-browser.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-mozglue.jar',
--- a/moz.configure
+++ b/moz.configure
@@ -9,27 +9,16 @@ include('build/moz.configure/init.config
 # Note:
 # - Gecko-specific options and rules should go in toolkit/moz.configure.
 # - Firefox-specific options and rules should go in browser/moz.configure.
 # - Fennec-specific options and rules should go in
 #   mobile/android/moz.configure.
 # - Spidermonkey-specific options and rules should go in js/moz.configure.
 # - etc.
 
-# Multiprocess Firefox Testing UI - Nightly and Aurora
-# To be removed in Bug 1003313
-@depends(milestone)
-def e10s_testing_only(milestone):
-    if not milestone.is_release_or_beta:
-        return True
-
-set_config('E10S_TESTING_ONLY', e10s_testing_only)
-set_define('E10S_TESTING_ONLY', e10s_testing_only)
-
-
 option('--enable-artifact-builds', env='MOZ_ARTIFACT_BUILDS',
        help='Download and use prebuilt binary artifacts.')
 
 @depends('--enable-artifact-builds')
 def artifact_builds(value):
     if value:
         return True
 
--- a/netwerk/base/nsIPermissionManager.idl
+++ b/netwerk/base/nsIPermissionManager.idl
@@ -158,61 +158,73 @@ interface nsIPermissionManager : nsISupp
 
   /**
    * Clear all permission information added since the specified time.
    */
   void removeAllSince(in int64_t since);
 
   /**
    * Test whether a website has permission to perform the given action.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    * @param uri     the uri to be tested
    * @param type    a case-sensitive ASCII string, identifying the consumer
    * @param return  see add(), param permission. returns UNKNOWN_ACTION when
    *                there is no stored permission for this uri and / or type.
    */
   uint32_t testPermission(in nsIURI uri,
                           in string type);
 
   /**
    * Test whether the principal has the permission to perform a given action.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testPermissionFromPrincipal(in nsIPrincipal principal,
                                        in string type);
 
   /**
    * Test whether the principal associated with the window's document has the
    * permission to perform a given action.  System principals will always
    * have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testPermissionFromWindow(in mozIDOMWindow window,
                                     in string type);
 
   /**
    * Test whether a website has permission to perform the given action.
    * This requires an exact hostname match, subdomains are not a match.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    * @param uri     the uri to be tested
    * @param type    a case-sensitive ASCII string, identifying the consumer
    * @param return  see add(), param permission. returns UNKNOWN_ACTION when
    *                there is no stored permission for this uri and / or type.
    */
   uint32_t testExactPermission(in nsIURI uri,
                                in string type);
 
   /**
    * See testExactPermission() above.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testExactPermissionFromPrincipal(in nsIPrincipal principal,
                                             in string type);
 
   /**
    * Test whether a website has permission to perform the given action
    * ignoring active sessions.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    *
    * @param principal the principal
    * @param type      a case-sensitive ASCII string, identifying the consumer
    * @param return    see add(), param permission. returns UNKNOWN_ACTION when
    *                  there is no stored permission for this uri and / or type.
    */
   uint32_t testExactPermanentPermission(in nsIPrincipal principal,
                                         in string type);
old mode 100644
new mode 100755
--- a/netwerk/socket/nsNamedPipeIOLayer.cpp
+++ b/netwerk/socket/nsNamedPipeIOLayer.cpp
@@ -293,32 +293,33 @@ NamedPipeInfo::Connect(const nsACString&
 }
 
 nsresult
 NamedPipeInfo::Disconnect()
 {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 
   nsresult rv = mNamedPipeService->RemoveDataObserver(mPipe, this);
-  NS_WARN_IF(NS_FAILED(rv));
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
   mPipe = nullptr;
 
   if (mReadOverlapped.hEvent &&
       mReadOverlapped.hEvent != INVALID_HANDLE_VALUE) {
     CloseHandle(mReadOverlapped.hEvent);
     mReadOverlapped.hEvent = nullptr;
   }
 
   if (mWriteOverlapped.hEvent &&
       mWriteOverlapped.hEvent != INVALID_HANDLE_VALUE) {
     CloseHandle(mWriteOverlapped.hEvent);
     mWriteOverlapped.hEvent = nullptr;
   }
 
-  return NS_OK;
+  return rv;
 }
 
 int32_t
 NamedPipeInfo::Read(void* aBuffer, int32_t aSize)
 {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 
   int32_t bytesRead = Peek(aBuffer, aSize);
--- a/old-configure.in
+++ b/old-configure.in
@@ -2215,17 +2215,17 @@ AC_SUBST(MOZ_MULET)
 
 dnl ========================================================
 dnl Ensure Android SDK and build-tools versions depending on
 dnl mobile target.
 dnl ========================================================
 
 case "$MOZ_BUILD_APP" in
 mobile/android)
-    MOZ_ANDROID_SDK(23, 23, 23.0.3 23.0.1, 26.0.0 26.0.0-dev 25.3.2 25.3.1)
+    MOZ_ANDROID_SDK(23, 23, 25.0.3, 26.0.0 26.0.0-dev 25.3.2 25.3.1)
     ;;
 esac
 
 dnl ========================================================
 dnl =
 dnl = Toolkit Options
 dnl =
 dnl ========================================================
--- a/python/mozboot/mozboot/android-packages.txt
+++ b/python/mozboot/mozboot/android-packages.txt
@@ -1,5 +1,5 @@
 platform-tools
-build-tools;23.0.3
+build-tools;25.0.3
 platforms;android-23
 extras;android;m2repository
 extras;google;m2repository
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -254,17 +254,25 @@ class BaseBootstrapper(object):
     def ensure_stylo_packages(self, state_dir, checkout_root):
         '''
         Install any necessary packages needed for Stylo development.
         '''
         raise NotImplementedError(
             '%s does not yet implement ensure_stylo_packages()'
             % __name__)
 
-    def install_tooltool_clang_package(self, state_dir, checkout_root, toolchain_job):
+    def ensure_proguard_packages(self, state_dir, checkout_root):
+        '''
+        Install any necessary packages that provide the Proguard JAR.
+
+        Only required to build mobile/android.
+        '''
+        self.install_toolchain_artifact(state_dir, checkout_root, 'proguard-jar')
+
+    def install_toolchain_artifact(self, state_dir, checkout_root, toolchain_job):
         mach_binary = os.path.join(checkout_root, 'mach')
         mach_binary = os.path.abspath(mach_binary)
         if not os.path.exists(mach_binary):
             raise ValueError("mach not found at %s" % mach_binary)
 
         # If Python can't figure out what its own executable is, there's little
         # chance we're going to be able to execute mach on its own, particularly
         # on Windows.
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -365,16 +365,19 @@ class Bootstrapper(object):
 
             if not have_clone:
                 print(STYLO_REQUIRES_CLONE)
                 sys.exit(1)
 
             self.instance.state_dir = state_dir
             self.instance.ensure_stylo_packages(state_dir, checkout_root)
 
+            if 'mobile_android' in application:
+                self.instance.ensure_proguard_packages(state_dir, checkout_root)
+
         print(self.finished % name)
         if not (self.instance.which('rustc') and self.instance._parse_version('rustc') >= MODERN_RUST_VERSION):
             print("To build %s, please restart the shell (Start a new terminal window)" % name)
 
         # Like 'suggest_browser_mozconfig' or 'suggest_mobile_android_mozconfig'.
         getattr(self.instance, 'suggest_%s_mozconfig' % application)()
 
 
--- a/python/mozboot/mozboot/linux_common.py
+++ b/python/mozboot/mozboot/linux_common.py
@@ -6,9 +6,9 @@
 # needed to install Stylo dependencies.  This class must come before
 # BaseBootstrapper in the inheritance list.
 class StyloInstall(object):
     def __init__(self, **kwargs):
         pass
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         import stylo
-        self.install_tooltool_clang_package(state_dir, checkout_root, stylo.LINUX)
+        self.install_toolchain_artifact(state_dir, checkout_root, stylo.LINUX)
--- a/python/mozboot/mozboot/mozillabuild.py
+++ b/python/mozboot/mozboot/mozillabuild.py
@@ -40,17 +40,17 @@ class MozillaBuildBootstrapper(BaseBoots
     def install_mobile_android_packages(self):
         pass
 
     def install_mobile_android_artifact_mode_packages(self):
         pass
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         import stylo
-        self.install_tooltool_clang_package(state_dir, checkout_root, stylo.WINDOWS)
+        self.install_toolchain_artifact(state_dir, checkout_root, stylo.WINDOWS)
 
     def _update_package_manager(self):
         pass
 
     def run(self, command):
         subprocess.check_call(command, stdin=sys.stdin)
 
     def pip_install(self, *packages):
--- a/python/mozboot/mozboot/windows.py
+++ b/python/mozboot/mozboot/windows.py
@@ -65,17 +65,17 @@ class WindowsBootstrapper(BaseBootstrapp
     def install_mobile_android_packages(self):
         raise NotImplementedError('We do not support building Android on Windows. Sorry!')
 
     def install_mobile_android_artifact_mode_packages(self):
         raise NotImplementedError('We do not support building Android on Windows. Sorry!')
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         import stylo
-        self.install_tooltool_clang_package(state_dir, checkout_root, stylo.WINDOWS)
+        self.install_toolchain_artifact(state_dir, checkout_root, stylo.WINDOWS)
 
     def _update_package_manager(self):
         self.pacman_update()
 
     def run(self, command):
         subprocess.check_call(command, stdin=sys.stdin)
 
     def pacman_update(self):
--- a/security/manager/ssl/PKCS11ModuleDB.cpp
+++ b/security/manager/ssl/PKCS11ModuleDB.cpp
@@ -109,16 +109,23 @@ PKCS11ModuleDB::AddModule(const nsAStrin
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (aModuleName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  // There appears to be a deadlock if we try to load modules concurrently, so
+  // just wait until the loadable roots module has been loaded.
+  nsresult rv = BlockUntilLoadableRootsLoaded();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   NS_ConvertUTF16toUTF8 moduleName(aModuleName);
   nsCString fullPath;
   // NSS doesn't support Unicode path.  Use native charset
   NS_CopyUnicodeToNative(aLibraryFullPath, fullPath);
   uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
   uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
   SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(),
                                       mechFlags, cipherFlags);
--- a/security/manager/ssl/security-prefs.js
+++ b/security/manager/ssl/security-prefs.js
@@ -39,17 +39,17 @@ pref("security.ask_for_password",       
 pref("security.password_lifetime",       30);
 
 // If true, use the modern sqlite-backed certificate and key databases in NSS.
 // If false, use the default format. Currently the default in NSS is the old
 // BerkeleyDB format, but this will change in bug 1377940.
 // Changing this requires a restart to take effect.
 // Note that the environment variable MOZPSM_NSSDBDIR_OVERRIDE can override both
 // the behavior of this preference and the NSS default.
-pref("security.use_sqldb", false);
+pref("security.use_sqldb", true);
 
 // The supported values of this pref are:
 // 0: disable detecting Family Safety mode and importing the root
 // 1: only attempt to detect Family Safety mode (don't import the root)
 // 2: detect Family Safety mode and import the root
 // (This is only relevant to Windows 8.1)
 pref("security.family_safety.mode", 2);
 
copy from security/manager/ssl/tests/unit/test_sdr_preexisting.js
copy to security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
--- a/security/manager/ssl/tests/unit/test_sdr_preexisting.js
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
@@ -1,187 +1,68 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 // 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";
 
 // Tests that the SDR implementation is able to decrypt strings encrypted using
-// a preexisting NSS key database. Creating the database is straight-forward:
-// simply run Firefox (or xpcshell) and encrypt something using
-// nsISecretDecoderRing (e.g. by saving a password or directly using the
-// interface). The resulting key3.db file (in the profile directory) now
-// contains the private key used to encrypt the data.
-// "Upgrading" a key3.db with certutil to use on Android appears not to work.
-// Because the keys have to be the same for this test to work the way it does,
-// the key from key3.db must be extracted and added to a new key4.db. This can
-// be done with NSS' PK11_* APIs like so (although note that the following code
-// is not guaranteed to compile or work, but is more of a guideline for how to
-// do this in the future if necessary):
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-//
-// void printPRError(const char* message) {
-//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-//   if (NSS_Initialize(".", "", "", "", NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT)
-//         != SECSuccess) {
-//     printPRError("NSS_Initialize failed");
-//     return 1;
-//   }
-//
-//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-//   if (!slot) {
-//     printPRError("PK11_GetInternalKeySlot failed");
-//     return 1;
-//   }
-//
-//   // Create a key to wrap the SDR key to export it.
-//   unsigned char wrappingKeyIDBytes[] = { 0 };
-//   SECItem wrappingKeyID = {
-//     siBuffer,
-//     wrappingKeyIDBytes,
-//     sizeof(wrappingKeyIDBytes)
-//   };
-//   PK11SymKey* wrappingKey = PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0,
-//                                              &wrappingKeyID, PR_FALSE, NULL);
-//   if (!wrappingKey) {
-//     printPRError("PK11_TokenKeyGen failed");
-//     return 1;
-//   }
-//
-//   // This is the magic identifier NSS uses for the SDR key.
-//   unsigned char sdrKeyIDBytes[] = {
-//     0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
-//   };
-//   SECItem sdrKeyID = { siBuffer, sdrKeyIDBytes, sizeof(sdrKeyIDBytes) };
-//   PK11SymKey* sdrKey = PK11_FindFixedKey(slot, CKM_DES3_CBC, &sdrKeyID,
-//                                          NULL);
-//   if (!sdrKey) {
-//     printPRError("PK11_FindFixedKey failed");
-//     return 1;
-//   }
-//
-//   // Wrap the SDR key.
-//   unsigned char wrappedKeyBuf[1024];
-//   SECItem wrapped = { siBuffer, wrappedKeyBuf, sizeof(wrappedKeyBuf) };
-//   if (PK11_WrapSymKey(CKM_DES3_ECB, NULL, wrappingKey, sdrKey, &wrapped)
-//         != SECSuccess) {
-//     printPRError("PK11_WrapSymKey failed");
-//     return 1;
-//   }
-//
-//   // Unwrap the SDR key (NSS considers the SDR key "sensitive" and so won't
-//   // just export it as raw key material - we have to export it and then
-//   // re-import it as non-sensitive to get that data.
-//   PK11SymKey* unwrapped = PK11_UnwrapSymKey(wrappingKey, CKM_DES3_ECB, NULL,
-//                                             &wrapped, CKM_DES3_CBC,
-//                                             CKA_ENCRYPT, 0);
-//   if (!unwrapped) {
-//     printPRError("PK11_UnwrapSymKey failed");
-//     return 1;
-//   }
-//   if (PK11_ExtractKeyValue(unwrapped) != SECSuccess) {
-//     printPRError("PK11_ExtractKeyValue failed");
-//     return 1;
-//   }
-//   SECItem* keyData = PK11_GetKeyData(unwrapped);
-//   if (!keyData) {
-//     printPRError("PK11_GetKeyData failed");
-//     return 1;
-//   }
-//   for (int i = 0; i < keyData->len; i++) {
-//     printf("0x%02hhx, ", keyData->data[i]);
-//   }
-//   printf("\n");
-//
-//   PK11_FreeSymKey(unwrapped);
-//   PK11_FreeSymKey(sdrKey);
-//   PK11_FreeSymKey(wrappingKey);
-//   PK11_FreeSlot(slot);
-//
-//   if (NSS_Shutdown() != SECSuccess) {
-//     printPRError("NSS_Shutdown failed");
-//     return 1;
-//   }
-//   return 0;
-// }
-//
-// The output of compiling and running the above should be the bytes of the SDR
-// key. Given that, create a key4.db with an empty password using
-// `certutil -N -d sql:.` and then compile and run the following:
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-// #include "secmod.h"
-//
-// void printPRError(const char* message) {
-//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-//   if (NSS_Initialize("sql:.", "", "", "",
-//                      NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT) != SECSuccess) {
-//     printPRError("NSS_Initialize failed");
-//     return 1;
-//   }
-//
-//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-//   if (!slot) {
-//     printPRError("PK11_GetInternalKeySlot failed");
-//     return 1;
-//   }
-//
-//   // These are the bytes of the SDR key from the previous step:
-//   unsigned char keyBytes[] = {
-//     0x70, 0xab, 0xea, 0x1f, 0x8f, 0xe3, 0x4a, 0x7a, 0xb5, 0xb0, 0x43, 0xe5,
-//     0x51, 0x83, 0x86, 0xe5, 0xb3, 0x43, 0xa8, 0x1f, 0xc1, 0x57, 0x86, 0x46
-//   };
-//   SECItem keyItem = { siBuffer, keyBytes, sizeof(keyBytes) };
-//   PK11SymKey* key = PK11_ImportSymKey(slot, CKM_DES3_CBC, PK11_OriginUnwrap,
-//                                       CKA_ENCRYPT, &keyItem, NULL);
-//   if (!key) {
-//     printPRError("PK11_ImportSymKey failed");
-//     return 1;
-//   }
-//
-//   PK11_FreeSymKey(key);
-//   PK11_FreeSlot(slot);
-//
-//   if (NSS_Shutdown() != SECSuccess) {
-//     printPRError("NSS_Shutdown failed");
-//     return 1;
-//   }
-//   return 0;
-// }
-//
-// This should create a key4.db file with the SDR key. (Note however that this
-// does not set the magic key ID for the SDR key. Currently this is not a
-// problem because the NSS implementation that backs the SDR simply tries all
-// applicable keys it has when decrypting, so this still works.)
+// a preexisting NSS key database that a) has a password and b) is in the old
+// dbm format.
+// To create such a database, run a version Firefox (or xpcshell) where the
+// default database format is the old dbm format (i.e. pre-bug 783994), set a
+// master password, and then encrypt something using nsISecretDecoderRing.
+// This does not apply to Android as the dbm implementation was never enabled on
+// that platform.
+
+var gMockPrompter = {
+  passwordToTry: "password",
+  numPrompts: 0,
+
+  // This intentionally does not use arrow function syntax to avoid an issue
+  // where in the context of the arrow function, |this != gMockPrompter| due to
+  // how objects get wrapped when going across xpcom boundaries.
+  promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+    this.numPrompts++;
+    if (this.numPrompts > 1) { // don't keep retrying a bad password
+      return false;
+    }
+    equal(text,
+          "Please enter your master password.",
+          "password prompt text should be as expected");
+    equal(checkMsg, null, "checkMsg should be null");
+    ok(this.passwordToTry, "passwordToTry should be non-null");
+    password.value = this.passwordToTry;
+    return true;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+  getNewPrompter: () => gMockPrompter,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
+};
 
 function run_test() {
-  const isAndroid = AppConstants.platform == "android";
-  const keyDBName = isAndroid ? "key4.db" : "key3.db";
+  let windowWatcherCID =
+    MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+                           gWindowWatcher);
+  do_register_cleanup(() => {
+    MockRegistrar.unregister(windowWatcherCID);
+  });
+
+  Services.prefs.setBoolPref("security.use_sqldb", true);
+
   let profile = do_get_profile();
-  let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
-  keyDBFile.copyTo(profile, keyDBName);
+  let keyDBFile = do_get_file("test_sdr_preexisting_with_password/key3.db");
+  keyDBFile.copyTo(profile, "key3.db");
 
   let sdr = Cc["@mozilla.org/security/sdr;1"]
               .getService(Ci.nsISecretDecoderRing);
 
   let testcases = [
     // a full padding block
     { ciphertext: "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
       plaintext: "password" },
@@ -206,9 +87,11 @@ function run_test() {
       plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks" },
   ];
 
   for (let testcase of testcases) {
     let decrypted = sdr.decryptString(testcase.ciphertext);
     equal(decrypted, testcase.plaintext,
           "decrypted ciphertext should match expected plaintext");
   }
+  equal(gMockPrompter.numPrompts, 1,
+        "Should have been prompted for a password once");
 }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cac0808ac32c35752b08d886eef4a3d362e56acd
GIT binary patch
literal 16384
zc%1Fn&r8!`90%~v^ZkAsjxC;{3lwj|!`d*up@JQBOv=&_Y72uXXsP*YY_bU(N~E9`
zu_0({6tN%~9)=bbtq~nc^dLbtgw&JZNxSS)Zt$(S+z$2!=n&p7c=LI~b9kQX^C8tK
z2N4NGQj$oQtE44jln<2@icpy1{@gzz>0b}I?rl+?P{@%hVa2!=00000007|lP*H}b
z`3`^2V?4~qbIAe#000000Kn0hotAtJ7j=e)wV4`M?8;b^W_6|8>AvG|R(l=JK~c}n
z-5B|+9c@kxDU-p4rAVW4?XitCrin`vyC<3U1QRV!%hu=o_GsrLtNiV<Wq82h_4?eN
z&gzbyu8w}Oz02e6b-M&g@mt)UOR+Z*000000002Tlt_%^(SU4J9sHQf6_Vb5Hn}~#
zZt8o!ee+}H)v{b#)s_($lWzCcD<dtOCEms^)c^Jet*yGo2UE$V`BZD<s(JFRrXsFC
z6ZqP5-?*`8_~bu-J^IG@<Hc*WIuv~7QZ%RIKPy8|BFe^Rv#;)#vDVhVS(<43Q2sru
efB5Cy%YnIY`u`WQKgHg3000000002Mq1yvX%-vf6
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -25,16 +25,17 @@ support-files =
   test_keysize_ev/**
   test_missing_intermediate/**
   test_name_constraints/**
   test_ocsp_fetch_method/**
   test_ocsp_url/**
   test_onecrl/**
   test_pinning_dynamic/**
   test_sdr_preexisting/**
+  test_sdr_preexisting_with_password/**
   test_signed_apps/**
   test_signed_dir/**
   test_startcom_wosign/**
   test_validity/**
   tlsserver/**
 
 [test_add_preexisting_cert.js]
 [test_baseline_requirements_subject_common_name.js]
@@ -137,16 +138,19 @@ run-sequentially = hardcoded ports
 run-sequentially = hardcoded ports
 # This test can take longer than 300 seconds on B2G emulator debug builds, so
 # give it enough time to finish. See bug 1081128.
 requesttimeoutfactor = 2
 [test_pinning_dynamic.js]
 [test_pinning_header_parsing.js]
 [test_sdr.js]
 [test_sdr_preexisting.js]
+[test_sdr_preexisting_with_password.js]
+# Not relevant to Android. See the comment in the test.
+skip-if = toolkit == 'android'
 [test_session_resumption.js]
 run-sequentially = hardcoded ports
 [test_signed_apps.js]
 [test_signed_dir.js]
 tags = addons psm
 [test_sss_enumerate.js]
 [test_sss_eviction.js]
 [test_sss_originAttributes.js]
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -470,16 +470,17 @@ dependencies = [
 name = "compositing"
 version = "0.0.1"
 dependencies = [
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "image 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "nonzero 0.0.1",
  "profile_traits 0.0.1",
  "script_traits 0.0.1",
  "servo_config 0.0.1",
  "servo_geometry 0.0.1",
@@ -1562,16 +1563,17 @@ dependencies = [
  "gfx 0.0.1",
  "gfx_traits 0.0.1",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "layout 0.0.1",
  "layout_traits 0.0.1",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "metrics 0.0.1",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "nonzero 0.0.1",
  "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "profile_traits 0.0.1",
  "range 0.0.1",
--- a/servo/components/compositing/Cargo.toml
+++ b/servo/components/compositing/Cargo.toml
@@ -10,16 +10,17 @@ name = "compositing"
 path = "lib.rs"
 
 [dependencies]
 euclid = "0.15"
 gfx_traits = {path = "../gfx_traits"}
 gleam = "0.4"
 image = "0.16"
 ipc-channel = "0.9"
+libc = "0.2"
 log = "0.3.5"
 msg = {path = "../msg"}
 net_traits = {path = "../net_traits"}
 nonzero = {path = "../nonzero"}
 profile_traits = {path = "../profile_traits"}
 script_traits = {path = "../script_traits"}
 servo_config = {path = "../config"}
 servo_geometry = {path = "../geometry"}
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -1,45 +1,46 @@
 /* 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 CompositionPipeline;
 use SendableFrameTree;
 use compositor_thread::{CompositorProxy, CompositorReceiver};
 use compositor_thread::{InitialCompositorState, Msg, RenderListener};
-use euclid::{Point2D, TypedPoint2D, TypedVector2D, ScaleFactor};
+use euclid::{TypedPoint2D, TypedVector2D, ScaleFactor};
 use gfx_traits::Epoch;
 use gleam::gl;
 use image::{DynamicImage, ImageFormat, RgbImage};
 use ipc_channel::ipc::{self, IpcSharedMemory};
+use libc::c_void;
 use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
 use net_traits::image::base::{Image, PixelFormat};
 use nonzero::NonZero;
 use profile_traits::time::{self, ProfilerCategory, profile};
-use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg};
-use script_traits::{ConstellationMsg, LayoutControlMsg, MouseButton};
-use script_traits::{MouseEventType, ScrollState};
-use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType};
-use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
+use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
+use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId};
+use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
+use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
 use servo_config::opts;
 use servo_config::prefs::PREFS;
 use servo_geometry::DeviceIndependentPixel;
 use std::collections::HashMap;
 use std::fs::File;
 use std::rc::Rc;
 use std::sync::mpsc::Sender;
 use std::time::{Duration, Instant};
 use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
+use style_traits::cursor::Cursor;
 use style_traits::viewport::ViewportConstraints;
 use time::{precise_time_ns, precise_time_s};
 use touch::{TouchHandler, TouchAction};
 use webrender;
-use webrender_api::{self, ClipId, DeviceUintRect, DeviceUintSize, LayoutPoint, LayoutVector2D};
-use webrender_api::{ScrollEventPhase, ScrollLocation, ScrollClamping};
+use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult};
+use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation};
 use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
 
 #[derive(Debug, PartialEq)]
 enum UnableToComposite {
     WindowUnprepared,
     NotReadyToPaintImage(NotReadyToPaint),
 }
 
@@ -459,21 +460,16 @@ impl<Window: WindowMethods> IOCompositor
             }
 
             (Msg::SetFrameTree(frame_tree),
              ShutdownState::NotShuttingDown) => {
                 self.set_frame_tree(&frame_tree);
                 self.send_viewport_rects();
             }
 
-            (Msg::ScrollFragmentPoint(scroll_root_id, point, _),
-             ShutdownState::NotShuttingDown) => {
-                self.scroll_fragment_to_point(scroll_root_id, point);
-            }
-
             (Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => {
                 self.composition_request = CompositionRequest::CompositeNow(reason)
             }
 
 
             (Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
                 self.touch_handler.on_event_processed(result);
             }
@@ -651,23 +647,16 @@ impl<Window: WindowMethods> IOCompositor
         };
         let msg = ConstellationMsg::WindowSize(top_level_browsing_context_id, data, size_type);
 
         if let Err(e) = self.constellation_chan.send(msg) {
             warn!("Sending window resize to constellation failed ({}).", e);
         }
     }
 
-    fn scroll_fragment_to_point(&mut self, id: ClipId, point: Point2D<f32>) {
-        self.webrender_api.scroll_node_with_id(self.webrender_document,
-                                               LayoutPoint::from_untyped(&point),
-                                               id,
-                                               ScrollClamping::ToContentBounds);
-    }
-
     pub fn on_resize_window_event(&mut self, new_size: DeviceUintSize) {
         debug!("compositor resizing to {:?}", new_size.to_untyped());
 
         // A size change could also mean a resolution change.
         let new_scale_factor = self.window.hidpi_factor();
         if self.scale_factor != new_scale_factor {
             self.scale_factor = new_scale_factor;
             self.update_zoom_transform();
@@ -702,42 +691,57 @@ impl<Window: WindowMethods> IOCompositor
 
     fn dispatch_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) {
         let point = match mouse_window_event {
             MouseWindowEvent::Click(_, p) => p,
             MouseWindowEvent::MouseDown(_, p) => p,
             MouseWindowEvent::MouseUp(_, p) => p,
         };
 
-        let root_pipeline_id = match self.get_root_pipeline_id() {
-            Some(root_pipeline_id) => root_pipeline_id,
+        let results = self.hit_test_at_point(point);
+        let result = match results.items.first() {
+            Some(result) => result,
             None => return,
         };
 
-        if let Some(pipeline) = self.pipeline(root_pipeline_id) {
-            let dppx = self.page_zoom * self.hidpi_factor();
-            let translated_point = (point / dppx).to_untyped();
-            let event_to_send = match mouse_window_event {
-                MouseWindowEvent::Click(button, _) => {
-                    MouseButtonEvent(MouseEventType::Click, button, translated_point)
-                }
-                MouseWindowEvent::MouseDown(button, _) => {
-                    MouseButtonEvent(MouseEventType::MouseDown, button, translated_point)
-                }
-                MouseWindowEvent::MouseUp(button, _) => {
-                    MouseButtonEvent(MouseEventType::MouseUp, button, translated_point)
-                }
-            };
-            let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
-            if let Err(e) = pipeline.script_chan.send(msg) {
-                warn!("Sending control event to script failed ({}).", e);
+        let point = result.point_in_viewport.to_untyped();
+        let node_address = Some(UntrustedNodeAddress(result.tag.0 as *const c_void));
+        let event_to_send = match mouse_window_event {
+            MouseWindowEvent::Click(button, _) => {
+                MouseButtonEvent(MouseEventType::Click, button, point, node_address)
+            }
+            MouseWindowEvent::MouseDown(button, _) => {
+                MouseButtonEvent(MouseEventType::MouseDown, button, point, node_address)
+            }
+            MouseWindowEvent::MouseUp(button, _) => {
+                MouseButtonEvent(MouseEventType::MouseUp, button, point, node_address)
             }
+        };
+
+        let pipeline_id = PipelineId::from_webrender(result.pipeline);
+        let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
+        if let Err(e) = self.constellation_chan.send(msg) {
+            warn!("Sending event to constellation failed ({}).", e);
         }
     }
 
+    fn hit_test_at_point(&self, point: TypedPoint2D<f32, DevicePixel>) -> HitTestResult {
+        let dppx = self.page_zoom * self.hidpi_factor();
+        let scaled_point = (point / dppx).to_untyped();
+
+        let world_cursor = webrender_api::WorldPoint::from_untyped(&scaled_point);
+        self.webrender_api.hit_test(
+            self.webrender_document,
+            None,
+            world_cursor,
+            HitTestFlags::empty()
+        )
+
+    }
+
     pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>) {
         if opts::get().convert_mouse_to_touch {
             self.on_touch_move(TouchId(0), cursor);
             return
         }
 
         self.dispatch_mouse_window_move_event_class(cursor);
     }
@@ -746,36 +750,53 @@ impl<Window: WindowMethods> IOCompositor
         let root_pipeline_id = match self.get_root_pipeline_id() {
             Some(root_pipeline_id) => root_pipeline_id,
             None => return,
         };
         if self.pipeline(root_pipeline_id).is_none() {
             return;
         }
 
-        let dppx = self.page_zoom * self.hidpi_factor();
-        let event_to_send = MouseMoveEvent(Some((cursor / dppx).to_untyped()));
-        let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
-        if let Some(pipeline) = self.pipeline(root_pipeline_id) {
-            if let Err(e) = pipeline.script_chan.send(msg) {
-                warn!("Sending mouse control event to script failed ({}).", e);
+        let results = self.hit_test_at_point(cursor);
+        if let Some(item) = results.items.first() {
+            let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
+            let event = MouseMoveEvent(Some(item.point_in_viewport.to_untyped()), node_address);
+            let pipeline_id = PipelineId::from_webrender(item.pipeline);
+            let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
+            if let Err(e) = self.constellation_chan.send(msg) {
+                warn!("Sending event to constellation failed ({}).", e);
+            }
+
+            if let Some(cursor) =  Cursor::from_u8(item.tag.1).ok() {
+                let msg = ConstellationMsg::SetCursor(cursor);
+                if let Err(e) = self.constellation_chan.send(msg) {
+                    warn!("Sending event to constellation failed ({}).", e);
+                }
             }
         }
     }
 
-    fn send_event_to_root_pipeline(&self, event: CompositorEvent) {
-        let root_pipeline_id = match self.get_root_pipeline_id() {
-            Some(root_pipeline_id) => root_pipeline_id,
-            None => return,
-        };
-
-        if let Some(pipeline) = self.pipeline(root_pipeline_id) {
-            let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event);
-            if let Err(e) = pipeline.script_chan.send(msg) {
-                warn!("Sending control event to script failed ({}).", e);
+    fn send_touch_event(
+        &self,
+        event_type: TouchEventType,
+        identifier: TouchId,
+        point: TypedPoint2D<f32, DevicePixel>)
+    {
+        let results = self.hit_test_at_point(point);
+        if let Some(item) = results.items.first() {
+            let event = TouchEvent(
+                event_type,
+                identifier,
+                item.point_in_viewport.to_untyped(),
+                Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
+            );
+            let pipeline_id = PipelineId::from_webrender(item.pipeline);
+            let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
+            if let Err(e) = self.constellation_chan.send(msg) {
+                warn!("Sending event to constellation failed ({}).", e);
             }
         }
     }
 
     pub fn on_touch_event(&mut self,
                           event_type: TouchEventType,
                           identifier: TouchId,
                           location: TypedPoint2D<f32, DevicePixel>) {
@@ -784,21 +805,17 @@ impl<Window: WindowMethods> IOCompositor
             TouchEventType::Move => self.on_touch_move(identifier, location),
             TouchEventType::Up => self.on_touch_up(identifier, location),
             TouchEventType::Cancel => self.on_touch_cancel(identifier, location),
         }
     }
 
     fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
         self.touch_handler.on_touch_down(identifier, point);
-        let dppx = self.page_zoom * self.hidpi_factor();
-        let translated_point = (point / dppx).to_untyped();
-        self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Down,
-                                                    identifier,
-                                                    translated_point));
+        self.send_touch_event(TouchEventType::Down, identifier, point);
     }
 
     fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
         match self.touch_handler.on_touch_move(identifier, point) {
             TouchAction::Scroll(delta) => {
                 match point.cast() {
                     Some(point) => self.on_scroll_window_event(
                         ScrollLocation::Delta(
@@ -816,61 +833,62 @@ impl<Window: WindowMethods> IOCompositor
                     scroll_location: ScrollLocation::Delta(webrender_api::LayoutVector2D::from_untyped(
                                                            &scroll_delta.to_untyped())),
                     cursor: cursor,
                     phase: ScrollEventPhase::Move(true),
                     event_count: 1,
                 });
             }
             TouchAction::DispatchEvent => {
-                let dppx = self.page_zoom * self.hidpi_factor();
-                let translated_point = (point / dppx).to_untyped();
-                self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Move,
-                                                            identifier,
-