merge mozilla-inbound to mozilal-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 09 Mar 2017 11:23:25 +0100
changeset 346696 da0ea1c722078f30c6f390627d3c680d3556a7a6
parent 346667 7ac30073bcac75a1f0217a342300233a4a2d939e (current diff)
parent 346695 3a0760865f8bc9d3dfe977e9f91997eaacbf8202 (diff)
child 346697 c40ca7a1bdd93632c6bdc5e23bd33d984d508b19
child 346732 cc4a644b213693a55f9bf545e2f9cc8288e7cd0b
child 346785 aad8e9c4a51140bbd8463e5752107a8a440be5ba
push id31474
push usercbook@mozilla.com
push dateThu, 09 Mar 2017 10:23:37 +0000
treeherdermozilla-central@da0ea1c72207 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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 mozilla-inbound to mozilal-central a=merge
layout/base/GeckoRestyleManager.cpp
layout/style/nsStyleSet.cpp
layout/tables/nsTableFrame.cpp
xpcom/ds/nsICollection.idl
xpcom/ds/nsIEnumerator.idl
xpcom/ds/nsISupportsArray.idl
xpcom/ds/nsSupportsArray.cpp
xpcom/ds/nsSupportsArray.h
xpcom/ds/nsSupportsArrayEnumerator.cpp
xpcom/ds/nsSupportsArrayEnumerator.h
xpcom/tests/gtest/TestArray.cpp
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -71,16 +71,17 @@ FormAutofillParent.prototype = {
    */
   init() {
     log.debug("init");
     let storePath = OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
     this._profileStore = new ProfileStorage(storePath);
     this._profileStore.initialize();
 
     Services.obs.addObserver(this, "advanced-pane-loaded", false);
+    Services.ppmm.addMessageListener("FormAutofill:SaveProfile", this);
 
     // Observing the pref and storage changes
     Services.prefs.addObserver(ENABLED_PREF, this, false);
     Services.obs.addObserver(this, "formautofill-storage-changed", false);
 
     // Force to trigger the onStatusChanged function for setting listeners properly
     // while initizlization
     this._setStatus(this._getStatus());
@@ -173,19 +174,28 @@ FormAutofillParent.prototype = {
    * Handles the message coming from FormAutofillContent.
    *
    * @param   {string} message.name The name of the message.
    * @param   {object} message.data The data of the message.
    * @param   {nsIFrameMessageManager} message.target Caller's message manager.
    */
   receiveMessage({name, data, target}) {
     switch (name) {
-      case "FormAutofill:GetProfiles":
+      case "FormAutofill:GetProfiles": {
         this._getProfiles(data, target);
         break;
+      }
+      case "FormAutofill:SaveProfile": {
+        if (data.guid) {
+          this.getProfileStore().update(data.guid, data.profile);
+        } else {
+          this.getProfileStore().add(data.profile);
+        }
+        break;
+      }
     }
   },
 
   /**
    * Returns the instance of ProfileStorage. To avoid syncing issues, anyone
    * who needs to access the profile should request the instance by this instead
    * of creating a new one.
    *
@@ -202,16 +212,17 @@ FormAutofillParent.prototype = {
    */
   _uninit() {
     if (this._profileStore) {
       this._profileStore._saveImmediately();
       this._profileStore = null;
     }
 
     Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
+    Services.ppmm.removeMessageListener("FormAutofill:SaveProfile", this);
     Services.obs.removeObserver(this, "advanced-pane-loaded");
     Services.prefs.removeObserver(ENABLED_PREF, this);
   },
 
   /**
    * Get the profile data from profile store and return profiles back to content process.
    *
    * @private
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -10,16 +10,21 @@
 
 this.EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
 const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
+this.log = null;
+FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 function FormAutofillPreferences() {
   this.bundle = Services.strings.createBundle(BUNDLE_URI);
 }
 
 FormAutofillPreferences.prototype = {
   /**
    * Check if Form Autofill feature is enabled.
@@ -41,16 +46,36 @@ FormAutofillPreferences.prototype = {
 
   /**
    * Create the Form Autofill preference group.
    *
    * @param   {XULDocument} document
    * @returns {XULElement}
    */
   init(document) {
+    this.createPreferenceGroup(document);
+    this.attachEventListeners();
+
+    return this.refs.formAutofillGroup;
+  },
+
+  /**
+   * Remove event listeners and the preference group.
+   */
+  uninit() {
+    this.detachEventListeners();
+    this.refs.formAutofillGroup.remove();
+  },
+
+  /**
+   * Create Form Autofill preference group
+   *
+   * @param  {XULDocument} document
+   */
+  createPreferenceGroup(document) {
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
     let formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
     let caption = document.createElementNS(XUL_NS, "caption");
     let captionLabel = document.createElementNS(XUL_NS, "label");
     let hbox = document.createElementNS(XUL_NS, "hbox");
     let enabledCheckbox = document.createElementNS(XUL_NS, "checkbox");
     let spacer = document.createElementNS(XUL_NS, "spacer");
@@ -80,28 +105,16 @@ FormAutofillPreferences.prototype = {
     spacer.flex = 1;
 
     formAutofillGroup.appendChild(caption);
     caption.appendChild(captionLabel);
     formAutofillGroup.appendChild(hbox);
     hbox.appendChild(enabledCheckbox);
     hbox.appendChild(spacer);
     hbox.appendChild(savedProfilesBtn);
-
-    this.attachEventListeners();
-
-    return formAutofillGroup;
-  },
-
-  /**
-   * Remove event listeners and the preference group.
-   */
-  uninit() {
-    this.detachEventListeners();
-    this.refs.formAutofillGroup.remove();
   },
 
   /**
    * Handle events
    *
    * @param  {DOMEvent} event
    */
   handleEvent(event) {
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -26,18 +26,18 @@ function insertStyleSheet(domWindow, url
   if (CACHED_STYLESHEETS.has(domWindow)) {
     CACHED_STYLESHEETS.get(domWindow).push(styleSheet);
   } else {
     CACHED_STYLESHEETS.set(domWindow, [styleSheet]);
   }
 }
 
 let windowListener = {
-  onOpenWindow(aWindow) {
-    let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+  onOpenWindow(window) {
+    let domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
     domWindow.addEventListener("load", function onWindowLoaded() {
       insertStyleSheet(domWindow, STYLESHEET_URI);
     }, {once: true});
   },
 };
 
 function startup() {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/editProfile.css
@@ -0,0 +1,92 @@
+/* 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/. */
+
+body {
+  font-size: 1rem;
+}
+
+form,
+label,
+div {
+  display: flex;
+}
+
+form {
+  flex-wrap: wrap;
+}
+
+label {
+  margin: 0 0 0.5em;
+}
+
+label > span {
+  flex: 0 0 8em;
+  padding-inline-end: 0.5em;
+  align-self: center;
+  text-align: end;
+}
+
+input,
+select {
+  flex: 1 0 auto;
+  width: 9em;
+}
+
+option {
+  padding: 6px;
+}
+
+textarea {
+  resize: none;
+}
+
+button {
+  padding: 3px 2em;
+}
+
+#country-container {
+  width: 15em;
+}
+
+#first-name-container,
+#middle-name-container,
+#address-level1-container,
+#postal-code-container,
+#country-container {
+  flex: 0 1 50%;
+}
+
+#last-name-container,
+#organization-container,
+#street-address-container,
+#address-level2-container,
+#email-container,
+#tel-container,
+#controls-container {
+  flex: 0 1 100%;
+}
+
+#controls-container {
+  justify-content: end;
+}
+
+#last-name,
+#organization,
+#address-level2,
+#tel{
+  flex: 0 0 auto;
+  width: calc(50% - 10em);
+}
+
+#street-address,
+#email {
+  flex: 1 0 auto;
+}
+
+#first-name-container,
+#middle-name-container,
+#last-name-container {
+  /* Hide until we support names */
+  display: none;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/editProfile.js
@@ -0,0 +1,140 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
+function EditDialog(profile) {
+  this._profile = profile;
+  window.addEventListener("DOMContentLoaded", this, {once: true});
+}
+
+EditDialog.prototype = {
+  init() {
+    this.refs = {
+      controlsContainer: document.getElementById("controls-container"),
+      cancel: document.getElementById("cancel"),
+      save: document.getElementById("save"),
+    };
+    this.attachEventListeners();
+  },
+
+  /**
+   * Asks FormAutofillParent to save or update a profile.
+   * @param  {object} data
+   *         {
+   *           {string} guid [optional]
+   *           {object} profile
+   *         }
+   */
+  saveProfile(data) {
+    Services.cpmm.sendAsyncMessage("FormAutofill:SaveProfile", data);
+  },
+
+  /**
+   * Fill the form with a profile object.
+   * @param  {object} profile
+   */
+  loadInitialValues(profile) {
+    for (let field in profile) {
+      let input = document.getElementById(field);
+      if (input) {
+        input.value = profile[field];
+      }
+    }
+  },
+
+  /**
+   * Get inputs from the form.
+   * @returns {object}
+   */
+  buildProfileObject() {
+    return Array.from(document.forms[0].elements).reduce((obj, input) => {
+      if (input.value) {
+        obj[input.id] = input.value;
+      }
+      return obj;
+    }, {});
+  },
+
+  /**
+   * Handle events
+   *
+   * @param  {DOMEvent} event
+   */
+  handleEvent(event) {
+    switch (event.type) {
+      case "DOMContentLoaded": {
+        this.init();
+        if (this._profile) {
+          this.loadInitialValues(this._profile);
+        }
+        break;
+      }
+      case "click": {
+        this.handleClick(event);
+        break;
+      }
+      case "input": {
+        // Toggle disabled attribute on the save button based on
+        // whether the form is filled or empty.
+        if (Object.keys(this.buildProfileObject()).length == 0) {
+          this.refs.save.setAttribute("disabled", true);
+        } else {
+          this.refs.save.removeAttribute("disabled");
+        }
+        break;
+      }
+    }
+  },
+
+  /**
+   * Handle click events
+   *
+   * @param  {DOMEvent} event
+   */
+  handleClick(event) {
+    if (event.target == this.refs.cancel) {
+      this.detachEventListeners();
+      window.close();
+    }
+    if (event.target == this.refs.save) {
+      if (this._profile) {
+        this.saveProfile({
+          guid: this._profile.guid,
+          profile: this.buildProfileObject(),
+        });
+      } else {
+        this.saveProfile({
+          profile: this.buildProfileObject(),
+        });
+      }
+      this.detachEventListeners();
+      window.close();
+    }
+  },
+
+  /**
+   * Attach event listener
+   */
+  attachEventListeners() {
+    this.refs.controlsContainer.addEventListener("click", this);
+    document.addEventListener("input", this);
+  },
+
+  /**
+   * Remove event listener
+   */
+  detachEventListeners() {
+    this.refs.controlsContainer.removeEventListener("click", this);
+    document.removeEventListener("input", this);
+  },
+};
+
+// Pass in argument from openDialog
+new EditDialog(window.arguments[0]);
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/editProfile.xhtml
@@ -0,0 +1,67 @@
+<?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/. -->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Profile Autofill - Edit Profile</title>
+  <link rel="stylesheet" href="chrome://formautofill/content/editProfile.css" />
+  <script src="chrome://formautofill/content/editProfile.js"></script>
+</head>
+<body>
+  <form>
+    <label id="first-name-container">
+      <span>First Name</span>
+      <input id="first-name" type="text"/>
+    </label>
+    <label id="middle-name-container">
+      <span>Middle Name</span>
+      <input id="middle-name" type="text"/>
+    </label>
+    <label id="last-name-container">
+      <span>Last Name</span>
+      <input id="last-name" type="text"/>
+    </label>
+    <label id="organization-container">
+      <span>Company</span>
+      <input id="organization" type="text"/>
+    </label>
+    <label id="street-address-container">
+      <span>Street Address</span>
+      <textarea id="street-address"/>
+    </label>
+    <label id="address-level2-container">
+      <span>City/Town</span>
+      <input id="address-level2" type="text"/>
+    </label>
+    <label id="address-level1-container">
+      <span>State/Province</span>
+      <input id="address-level1" type="text"/>
+    </label>
+    <label id="postal-code-container">
+      <span>Zip/Postal</span>
+      <input id="postal-code" type="text"/>
+    </label>
+    <label id="country-container">
+      <span>Country</span>
+      <select id="country">
+        <option/>
+        <option value="US">United States</option>
+      </select>
+    </label>
+    <label id="email-container">
+      <span>Email</span>
+      <input id="email" type="email"/>
+    </label>
+    <label id="tel-container">
+      <span>Phone</span>
+      <input id="tel" type="tel"/>
+    </label>
+  </form>
+  <div id="controls-container">
+    <button id="cancel">Cancel</button>
+    <button id="save" disabled="disabled">Save</button>
+  </div>
+</body>
+</html>
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -84,25 +84,38 @@ option(env='MOZ_AUTOMATION', help='Enabl
 set_config('MOZ_AUTOMATION', depends_if('MOZ_AUTOMATION')(lambda x: True))
 
 
 option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script')
 
 option(env='MOZ_CURRENT_PROJECT', nargs=1, help='Current build project')
 option(env='MOZCONFIG', nargs=1, help='Mozconfig location')
 
+option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
+       help='External directory containing additional build files')
+
+@depends('--with-external-source-dir')
+def external_source_dir(value):
+    if value:
+        return value[0]
+
+set_config('EXTERNAL_SOURCE_DIR', external_source_dir)
+add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir)
+
 # Read user mozconfig
 # ==============================================================
 # Note: the dependency on --help is only there to always read the mozconfig,
 # even when --help is passed. Without this dependency, the function wouldn't
 # be called when --help is passed, and the mozconfig wouldn't be read.
 @depends('MOZ_CURRENT_PROJECT', 'MOZCONFIG', 'OLD_CONFIGURE',
-         check_build_environment, '--help')
+         check_build_environment, '--with-external-source-dir',
+         '--help')
 @imports(_from='mozbuild.mozconfig', _import='MozconfigLoader')
-def mozconfig(current_project, mozconfig, old_configure, build_env, help):
+def mozconfig(current_project, mozconfig, old_configure, build_env,
+              external_source_dir, help):
     if not old_configure:
         die('The OLD_CONFIGURE environment variable must be set')
 
     # Don't read the mozconfig for the js configure (yay backwards
     # compatibility)
     # While the long term goal is that js and top-level use the same configure
     # and the same overall setup, including the possibility to use mozconfigs,
     # figuring out what we want to do wrt mozconfig vs. command line and
@@ -115,17 +128,20 @@ def mozconfig(current_project, mozconfig
     # Unfortunately, there is no direct way to tell whether the running
     # configure is the js configure. The indirect way is to look at the
     # OLD_CONFIGURE path, which points to js/src/old-configure.
     # I expect we'll have figured things out for mozconfigs well before
     # old-configure dies.
     if os.path.dirname(os.path.abspath(old_configure[0])).endswith('/js/src'):
         return {'path': None}
 
-    loader = MozconfigLoader(build_env.topsrcdir)
+    topsrcdir = build_env.topsrcdir
+    if external_source_dir:
+        topsrcdir = external_source_dir[0]
+    loader = MozconfigLoader(topsrcdir)
     current_project = current_project[0] if current_project else None
     mozconfig = mozconfig[0] if mozconfig else None
     mozconfig = loader.find_mozconfig(env={'MOZCONFIG': mozconfig})
     mozconfig = loader.read_mozconfig(mozconfig, moz_build_app=current_project)
 
     return mozconfig
 
 set_config('MOZCONFIG', depends(mozconfig)(lambda m: m['path']))
@@ -627,43 +643,32 @@ imply_option('--enable-project', applica
 def default_project(build_env, help):
     if build_env.topobjdir.endswith('/js/src'):
         return 'js'
     return 'browser'
 
 option('--enable-project', nargs=1, default=default_project,
        help='Project to build')
 
-option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
-       help='External directory containing additional build files')
-
 @depends('--enable-project', '--with-external-source-dir',
          check_build_environment, '--help')
 @imports(_from='os.path', _import='exists')
 def include_project_configure(project, external_source_dir, build_env, help):
     if not project:
         die('--enable-project is required.')
 
     base_dir = build_env.topsrcdir
     if external_source_dir:
         base_dir = os.path.join(base_dir, external_source_dir[0])
 
     path = os.path.join(base_dir, project[0], 'moz.configure')
     if not exists(path):
         die('Cannot find project %s', project[0])
     return path
 
-@depends('--with-external-source-dir')
-def external_source_dir(value):
-    if value:
-        return value[0]
-
-set_config('EXTERNAL_SOURCE_DIR', external_source_dir)
-add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir)
-
 
 @depends(include_project_configure, check_build_environment, '--help')
 def build_project(include_project_configure, build_env, help):
     ret = os.path.dirname(os.path.relpath(include_project_configure,
                                           build_env.topsrcdir))
     return ret
 
 set_config('MOZ_BUILD_APP', build_project)
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -146,16 +146,18 @@ for f in files:
     if not ipdl.checkSyncMessage(ast, syncMsgList):
         print >>sys.stderr, 'Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s' % options.syncMsgList
         sys.exit(1)
 
     if _verbosity > 2:
         log(3, '  pretty printed code:')
         ipdl.genipdl(ast, codedir)
 
+ipdl.checkFixedSyncMessages(parser)
+
 # Second pass: generate code
 for f in files:
     # Read from parser cache
     filename = normalizedFilename(f)
     ast = ipdl.parse(None, filename, includedirs=includedirs)
     ipdl.gencxx(filename, ast, headersdir, cppdir)
 
     if ast.protocol:
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -1,22 +1,23 @@
 # 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/.
 
-__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified', 'checkSyncMessage' ]
+__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified',
+            'checkSyncMessage', 'checkFixedSyncMessages' ]
 
 import os, sys
 from cStringIO import StringIO
 
 from ipdl.cgen import IPDLCodeGen
 from ipdl.lower import LowerToCxx, msgenums
 from ipdl.parser import Parser, ParseError
 from ipdl.type import TypeCheck
-from ipdl.checker import checkSyncMessage
+from ipdl.checker import checkSyncMessage, checkFixedSyncMessages
 
 from ipdl.cxx.cgen import CxxCodeGen
 
 
 def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr):
     '''Return an IPDL AST if parsing was successful.  Print errors to |errout|
     if it is not.'''
     # The file type and name are later enforced by the type checker.
--- a/ipc/ipdl/ipdl/checker.py
+++ b/ipc/ipdl/ipdl/checker.py
@@ -2,42 +2,64 @@
 # 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/.
 
 import sys
 from ipdl.ast import Visitor, ASYNC
 
 class SyncMessageChecker(Visitor):
+    syncMsgList = []
+    seenProtocols = []
+    seenSyncMessages = []
     def __init__(self, syncMsgList):
-        self.syncMsgList = syncMsgList
+        SyncMessageChecker.syncMsgList = syncMsgList
         self.errors = []
 
     def prettyMsgName(self, msg):
         return "%s::%s" % (self.currentProtocol, msg)
 
     def errorUnknownSyncMessage(self, loc, msg):
         self.errors.append('%s: error: Unknown sync IPC message %s' %
                            (str(loc), msg))
 
     def errorAsyncMessageCanRemove(self, loc, msg):
         self.errors.append('%s: error: IPC message %s is async, can be delisted' %
                            (str(loc), msg))
 
     def visitProtocol(self, p):
+        self.errors = []
         self.currentProtocol = p.name
+        SyncMessageChecker.seenProtocols.append(p.name)
         Visitor.visitProtocol(self, p)
 
     def visitMessageDecl(self, md):
         pn = self.prettyMsgName(md.name)
-        if md.sendSemantics is not ASYNC and pn not in self.syncMsgList:
-            self.errorUnknownSyncMessage(md.loc, pn)
-        if md.sendSemantics is ASYNC and pn in self.syncMsgList:
+        if md.sendSemantics is not ASYNC:
+            if pn not in SyncMessageChecker.syncMsgList:
+                self.errorUnknownSyncMessage(md.loc, pn)
+            SyncMessageChecker.seenSyncMessages.append(pn)
+        elif pn in SyncMessageChecker.syncMsgList:
             self.errorAsyncMessageCanRemove(md.loc, pn)
 
+    @staticmethod
+    def getFixedSyncMessages():
+        return set(SyncMessageChecker.syncMsgList) - set(SyncMessageChecker.seenSyncMessages)
+
 def checkSyncMessage(tu, syncMsgList, errout=sys.stderr):
     checker = SyncMessageChecker(syncMsgList)
     tu.accept(checker)
     if len(checker.errors):
         for error in checker.errors:
             print >>errout, error
         return False
     return True
+
+def checkFixedSyncMessages(config, errout=sys.stderr):
+    fixed = SyncMessageChecker.getFixedSyncMessages()
+    for item in fixed:
+        protocol = item.split('::')[0]
+        # Ignore things like sync messages in test protocols we didn't compile.
+        # Also, ignore platform-specific IPC messages.
+        if protocol in SyncMessageChecker.seenProtocols and \
+           'platform' not in config.options(item):
+            print >>errout, 'Error: Sync IPC message %s not found, it appears to be fixed.\n' \
+                            'Please remove it from sync-messages.ini.' % item
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -468,16 +468,17 @@ description =
 [PDocAccessible::AccessibleAtPoint]
 description =
 [PDocAccessible::Extents]
 description =
 [PDocAccessible::DOMNodeID]
 description =
 [PDocAccessible::GetWindowedPluginIAccessible]
 description =
+platform = win
 
 # CPOWs
 [PBrowser::RpcMessage]
 description =
 [PContent::RpcMessage]
 description =
 [PJavaScript::PreventExtensions]
 description =
@@ -532,18 +533,16 @@ description =
 [PPluginInstance::__delete__]
 description =
 [PPluginInstance::CreateChildPluginWindow]
 description =
 [PPluginInstance::NPP_SetWindow]
 description =
 [PPluginInstance::NPP_GetValue_NPPVpluginWantsAllNetworkStreams]
 description =
-[PPluginInstance::NPP_GetValue_NPPVpluginNeedsXEmbed]
-description =
 [PPluginInstance::NPP_GetValue_NPPVpluginScriptableNPObject]
 description =
 [PPluginInstance::NPP_SetValue_NPNVprivateModeBool]
 description =
 [PPluginInstance::NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId]
 description =
 [PPluginInstance::NPP_SetValue_NPNVCSSZoomFactor]
 description =
@@ -614,18 +613,16 @@ description =
 [PPluginInstance::NPN_GetValueForURL]
 description =
 [PPluginInstance::NPN_SetValueForURL]
 description =
 [PPluginInstance::NPN_GetAuthenticationInfo]
 description =
 [PPluginInstance::NPN_ConvertPoint]
 description =
-[PPluginInstance::NegotiatedCarbon]
-description =
 [PPluginInstance::GetCompositionString]
 description =
 [PPluginInstance::NPP_NewStream]
 description =
 [PPluginInstance::PPluginStream]
 description =
 [PPluginInstance::PluginFocusChange]
 description =
@@ -640,18 +637,16 @@ description =
 [PPluginModule::NP_Initialize]
 description =
 [PPluginModule::SyncNPP_New]
 description =
 [PPluginModule::NP_Shutdown]
 description =
 [PPluginModule::OptionalFunctionsSupported]
 description =
-[PPluginModule::PCrashReporter]
-description =
 [PPluginModule::ProcessSomeEvents]
 description =
 [PPluginModule::NPN_SetException]
 description =
 [PPluginModule::GetKeyState]
 description =
 [PPluginModule::NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges]
 description =
@@ -754,18 +749,16 @@ description =
 [PContent::LoadPlugin]
 description =
 [PContent::ConnectPluginBridge]
 description =
 [PContent::GetBlocklistState]
 description =
 [PContent::FindPlugins]
 description =
-[PContent::PCrashReporter]
-description =
 [PContent::NSSU2FTokenIsCompatibleVersion]
 description =
 [PContent::NSSU2FTokenIsRegistered]
 description =
 [PContent::NSSU2FTokenRegister]
 description =
 [PContent::NSSU2FTokenSign]
 description =
@@ -796,18 +789,16 @@ description =
 [PContent::GetShowPasswordSetting]
 description =
 [PContent::KeywordToURI]
 description =
 [PContent::NotifyKeywordSearchLoading]
 description =
 [PContent::AllocateLayerTreeId]
 description =
-[PContent::GetGfxInfoFeatureStatus]
-description =
 [PContent::BeginDriverCrashGuard]
 description =
 [PContent::EndDriverCrashGuard]
 description =
 [PContent::OpenAnonymousTemporaryFile]
 description =
 [PContent::KeygenProcessValue]
 description =
@@ -910,20 +901,23 @@ description =
 [PCompositorBridge::PLayerTransaction]
 description =
 [PCompositorBridge::SyncWithCompositor]
 description =
 [PCompositorBridge::PWebRenderBridge]
 description =
 [PCompositorWidget::EnterPresentLock]
 description =
+platform = win
 [PCompositorWidget::LeavePresentLock]
 description =
+platform = win
 [PCompositorWidget::ClearTransparentWindow]
 description =
+platform = win
 [PImageBridge::WillClose]
 description =
 [PImageBridge::NewCompositable]
 description =
 [PLayerTransaction::SetTestSampleTime]
 description =
 [PLayerTransaction::LeaveTestMode]
 description =
@@ -934,18 +928,16 @@ description =
 [PLayerTransaction::SetAsyncScrollOffset]
 description =
 [PLayerTransaction::SetAsyncZoom]
 description =
 [PLayerTransaction::GetAPZTestData]
 description =
 [PLayerTransaction::RequestProperty]
 description =
-[PTexture::DestroySync]
-description =
 [PUiCompositorController::Pause]
 description =
 [PUiCompositorController::Resume]
 description =
 [PUiCompositorController::ResumeAndResize]
 description =
 [PWebRenderBridge::Create]
 description =
--- a/js/public/SweepingAPI.h
+++ b/js/public/SweepingAPI.h
@@ -10,20 +10,24 @@
 #include "js/HeapAPI.h"
 
 namespace JS {
 template <typename T> class WeakCache;
 
 namespace shadow {
 JS_PUBLIC_API(void)
 RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
+JS_PUBLIC_API(void)
+RegisterWeakCache(JSRuntime* rt, JS::WeakCache<void*>* cachep);
 } // namespace shadow
 
 // A WeakCache stores the given Sweepable container and links itself into a
-// list of such caches that are swept during each GC.
+// list of such caches that are swept during each GC. A WeakCache can be
+// specific to a zone, or across a whole runtime, depending on which
+// constructor is used.
 template <typename T>
 class WeakCache : public js::MutableWrappedPtrOperations<T, WeakCache<T>>,
                   private mozilla::LinkedListElement<WeakCache<T>>
 {
     friend class mozilla::LinkedListElement<WeakCache<T>>;
     friend class mozilla::LinkedList<WeakCache<T>>;
 
     WeakCache() = delete;
@@ -38,16 +42,23 @@ class WeakCache : public js::MutableWrap
 
     template <typename U>
     WeakCache(Zone* zone, U&& initial)
       : cache(mozilla::Forward<U>(initial))
     {
         sweeper = GCPolicy<T>::sweep;
         shadow::RegisterWeakCache(zone, reinterpret_cast<WeakCache<void*>*>(this));
     }
+    template <typename U>
+    WeakCache(JSRuntime* rt, U&& initial)
+      : cache(mozilla::Forward<U>(initial))
+    {
+        sweeper = GCPolicy<T>::sweep;
+        shadow::RegisterWeakCache(rt, reinterpret_cast<WeakCache<void*>*>(this));
+    }
     WeakCache(WeakCache&& other)
       : sweeper(other.sweeper),
         cache(mozilla::Move(other.cache))
     {
     }
 
     const T& get() const { return cache; }
     T& get() { return cache; }
--- a/js/src/ds/OrderedHashTable.h
+++ b/js/src/ds/OrderedHashTable.h
@@ -528,16 +528,18 @@ class OrderedHashTable
 
     static size_t offsetOfDataLength() {
         return offsetof(OrderedHashTable, dataLength);
     }
     static size_t offsetOfData() {
         return offsetof(OrderedHashTable, data);
     }
     static constexpr size_t offsetOfDataElement() {
+        static_assert(offsetof(Data, element) == 0,
+                      "RangeFront and RangePopFront depend on offsetof(Data, element) being 0");
         return offsetof(Data, element);
     }
     static constexpr size_t sizeofData() {
         return sizeof(Data);
     }
 
   private:
     /* Logarithm base 2 of the number of buckets in the hash table initially. */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6460,31 +6460,31 @@ RangeFront(MacroAssembler&, Register, Re
 
 template <>
 void
 RangeFront<ValueMap>(MacroAssembler& masm, Register range, Register i, Register front)
 {
     masm.loadPtr(Address(range, ValueMap::Range::offsetOfHashTable()), front);
     masm.loadPtr(Address(front, ValueMap::offsetOfImplData()), front);
 
-    static_assert(ValueMap::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
+    MOZ_ASSERT(ValueMap::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
     static_assert(ValueMap::sizeofImplData() == 24, "sizeof(Data) is 24");
     masm.mulBy3(i, i);
     masm.lshiftPtr(Imm32(3), i);
     masm.addPtr(i, front);
 }
 
 template <>
 void
 RangeFront<ValueSet>(MacroAssembler& masm, Register range, Register i, Register front)
 {
     masm.loadPtr(Address(range, ValueSet::Range::offsetOfHashTable()), front);
     masm.loadPtr(Address(front, ValueSet::offsetOfImplData()), front);
 
-    static_assert(ValueSet::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
+    MOZ_ASSERT(ValueSet::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
     static_assert(ValueSet::sizeofImplData() == 16, "sizeof(Data) is 16");
     masm.lshiftPtr(Imm32(4), i);
     masm.addPtr(i, front);
 }
 
 template <class OrderedHashTable>
 static void
 RangePopFront(MacroAssembler& masm, Register range, Register front, Register dataLength,
@@ -6498,17 +6498,17 @@ RangePopFront(MacroAssembler& masm, Regi
     masm.add32(Imm32(1), i);
 
     Label done, seek;
     masm.bind(&seek);
     masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done);
 
     // We can add sizeof(Data) to |front| to select the next element, because
     // |front| and |range.ht.data[i]| point to the same location.
-    static_assert(OrderedHashTable::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
+    MOZ_ASSERT(OrderedHashTable::offsetOfImplDataElement() == 0, "offsetof(Data, element) is 0");
     masm.addPtr(Imm32(OrderedHashTable::sizeofImplData()), front);
 
     masm.branchTestMagic(Assembler::NotEqual, Address(front, OrderedHashTable::offsetOfEntryKey()),
                          JS_HASH_KEY_EMPTY, &done);
 
     masm.add32(Imm32(1), i);
     masm.jump(&seek);
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -121,16 +121,17 @@ MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0
 MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments")
 MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL,   1, JSEXN_REFERENCEERR, "can't access lexical declaration `{0}' before initialization")
 MSG_DEF(JSMSG_BAD_CONST_ASSIGN,        1, JSEXN_TYPEERR, "invalid assignment to const `{0}'")
 MSG_DEF(JSMSG_CANT_DECLARE_GLOBAL_BINDING, 2, JSEXN_TYPEERR, "cannot declare global binding `{0}': {1}")
 
 // Date
 MSG_DEF(JSMSG_INVALID_DATE,            0, JSEXN_RANGEERR, "invalid date")
 MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP,    0, JSEXN_TYPEERR, "toISOString property is not callable")
+MSG_DEF(JSMSG_DEPRECATED_TOLOCALEFORMAT, 0, JSEXN_WARN, "Date.prototype.toLocaleFormat is deprecated; consider using Intl.DateTimeFormat instead")
 
 // String
 MSG_DEF(JSMSG_BAD_URI,                 0, JSEXN_URIERR, "malformed URI sequence")
 MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM,  0, JSEXN_RANGEERR, "form must be one of 'NFC', 'NFD', 'NFKC', or 'NFKD'")
 MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
 MSG_DEF(JSMSG_NOT_A_CODEPOINT,         1, JSEXN_RANGEERR, "{0} is not a valid code point")
 MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size")
 MSG_DEF(JSMSG_DEPRECATED_STRING_METHOD, 2, JSEXN_WARN, "String.{0} is deprecated; use String.prototype.{1} instead")
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -49,16 +49,17 @@ JSCompartment::JSCompartment(Zone* zone,
     behaviors_(options.behaviors()),
     zone_(zone),
     runtime_(zone->runtimeFromAnyThread()),
     principals_(nullptr),
     isSystem_(false),
     isAtomsCompartment_(false),
     isSelfHosting(false),
     marked(true),
+    warnedAboutDateToLocaleFormat(false),
     warnedAboutExprClosure(false),
     warnedAboutForEach(false),
     warnedAboutStringGenericsMethods(0),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -401,16 +401,17 @@ struct JSCompartment
   private:
     JSPrincipals*                principals_;
     bool                         isSystem_;
     bool                         isAtomsCompartment_;
 
   public:
     bool                         isSelfHosting;
     bool                         marked;
+    bool                         warnedAboutDateToLocaleFormat;
     bool                         warnedAboutExprClosure;
     bool                         warnedAboutForEach;
     uint32_t                     warnedAboutStringGenericsMethods;
 
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
 #endif
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2817,16 +2817,27 @@ date_toLocaleTimeString(JSContext* cx, u
 }
 #endif /* !EXPOSE_INTL_API */
 
 MOZ_ALWAYS_INLINE bool
 date_toLocaleFormat_impl(JSContext* cx, const CallArgs& args)
 {
     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
 
+#if EXPOSE_INTL_API
+    if (!cx->compartment()->warnedAboutDateToLocaleFormat) {
+        if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
+                                               JSMSG_DEPRECATED_TOLOCALEFORMAT))
+        {
+            return false;
+        }
+        cx->compartment()->warnedAboutDateToLocaleFormat = true;
+    }
+#endif
+
     if (args.length() == 0) {
         /*
          * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
          * with msvc; '%#c' requests that a full year be used in the result string.
          */
         static const char format[] =
 #if defined(_WIN32) && !defined(__MWERKS__)
                                        "%#c"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2549,16 +2549,18 @@ GCRuntime::updateRuntimePointersToReloca
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&trc, grayRootTracer.data);
     }
 
     // Sweep everything to fix up weak pointers.
     WatchpointMap::sweepAll(rt);
     Debugger::sweepAll(rt->defaultFreeOp());
     jit::JitRuntime::SweepJitcodeGlobalTable(rt);
+    for (JS::WeakCache<void*>* cache : rt->weakCaches())
+        cache->sweep();
 
     // Type inference may put more blocks here to free.
     blocksToFreeAfterSweeping.ref().freeAll();
 
     // Call callbacks to get the rest of the system to fixup other untraced pointers.
     callWeakPointerZoneGroupCallbacks();
 }
 
@@ -5144,16 +5146,20 @@ GCRuntime::beginSweepingZoneGroup(AutoLo
             WatchpointMap::sweepAll(rt);
 
             // Detach unreachable debuggers and global objects from each other.
             Debugger::sweepAll(&fop);
 
             // Sweep entries containing about-to-be-finalized JitCode and
             // update relocated TypeSet::Types inside the JitcodeGlobalTable.
             jit::JitRuntime::SweepJitcodeGlobalTable(rt);
+
+            // Sweep runtime-wide weak caches.
+            for (JS::WeakCache<void*>* cache : rt->weakCaches())
+                cache->sweep();
         }
 
         {
             gcstats::AutoPhase apdc(stats(), gcstats::PHASE_SWEEP_DISCARD_CODE);
             for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
                 zone->discardJitCode(&fop);
         }
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/toLocaleFormat-deprecated-no-Intl.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!xulRuntime.shell||this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Don't warn about Date.prototype.toLocaleFormat() when Intl isn't supported.
+
+enableLastWarning();
+
+new Date().toLocaleFormat("%Y");
+
+var warning = getLastWarning();
+assertEq(warning, null, "warning shouldn't be emitted for toLocaleFormat");
+
+disableLastWarning();
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/toLocaleFormat-deprecated.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!xulRuntime.shell||!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Warn once about Date.prototype.toLocaleFormat().
+
+enableLastWarning();
+
+new Date().toLocaleFormat("%Y");
+
+var warning = getLastWarning();
+assertEq(warning !== null, true, "warning should be emitted for toLocaleFormat");
+assertEq(warning.name, "Warning");
+assertEq(warning.message.indexOf("toLocaleFormat") !== -1, true,
+         "warning should mention toLocaleFormat");
+
+clearLastWarning();
+
+new Date().toLocaleFormat("%Y");
+
+warning = getLastWarning();
+assertEq(warning, null, "warning shouldn't be emitted for 2nd call to toLocaleFormat");
+
+disableLastWarning();
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -910,8 +910,14 @@ JSRuntime::ionLazyLinkListRemove(jit::Io
 void
 JSRuntime::ionLazyLinkListAdd(jit::IonBuilder* builder)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this),
                "Should only be mutated by the active thread.");
     ionLazyLinkList().insertFront(builder);
     ionLazyLinkListSize_++;
 }
+
+JS_PUBLIC_API(void)
+JS::shadow::RegisterWeakCache(JSRuntime* rt, WeakCache<void*>* cachep)
+{
+    rt->registerWeakCache(cachep);
+}
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -556,16 +556,25 @@ struct JSRuntime : public js::MallocProv
     const js::Class* maybeWindowProxyClass() const {
         return windowProxyClass_;
     }
     void setWindowProxyClass(const js::Class* clasp) {
         windowProxyClass_ = clasp;
     }
 
   private:
+    // List of non-ephemeron weak containers to sweep during beginSweepingZoneGroup.
+    js::ActiveThreadData<mozilla::LinkedList<JS::WeakCache<void*>>> weakCaches_;
+  public:
+    mozilla::LinkedList<JS::WeakCache<void*>>& weakCaches() { return weakCaches_.ref(); }
+    void registerWeakCache(JS::WeakCache<void*>* cachep) {
+        weakCaches().insertBack(cachep);
+    }
+
+  private:
     /*
      * Head of circular list of all enabled Debuggers that have
      * onNewGlobalObject handler methods established.
      */
     js::ActiveThreadData<JSCList> onNewGlobalObjectWatchers_;
   public:
     JSCList& onNewGlobalObjectWatchers() { return onNewGlobalObjectWatchers_.ref(); }
 
--- a/js/src/wasm/WasmDebugFrame.h
+++ b/js/src/wasm/WasmDebugFrame.h
@@ -59,16 +59,27 @@ class DebugFrame
         void*   reserved1_;
     };
 
     TlsData*    tlsData_;
     Frame       frame_;
 
     explicit DebugFrame() {}
 
+    void StaticAsserts() {
+        // VS2017 doesn't consider offsetOfResults() etc. to be constexpr, so we have to use
+        // offsetof directly. These asserts can't be at class-level because the type is incomplete.
+        static_assert(offsetof(DebugFrame, resultI32_) == 0, "results shall be at offset 0");
+        static_assert(offsetof(DebugFrame, tlsData_) + sizeof(TlsData*) ==
+                      offsetof(DebugFrame, frame_),
+                      "TLS pointer must be a field just before the wasm frame");
+        static_assert(sizeof(DebugFrame) % 8 == 0 && offsetof(DebugFrame, frame_) % 8 == 0,
+                      "DebugFrame and its portion is 8-bytes aligned for AbstractFramePtr");
+    }
+
   public:
     inline uint32_t funcIndex() const { return funcIndex_; }
     inline TlsData* tlsData() const { return tlsData_; }
     inline Frame& frame() { return frame_; }
 
     Instance* instance() const;
     GlobalObject* global() const;
 
@@ -105,18 +116,12 @@ class DebugFrame
 
     static constexpr size_t offsetOfResults() { return offsetof(DebugFrame, resultI32_); }
     static constexpr size_t offsetOfFlagsWord() { return offsetof(DebugFrame, reserved1_); }
     static constexpr size_t offsetOfFuncIndex() { return offsetof(DebugFrame, funcIndex_); }
     static constexpr size_t offsetOfTlsData() { return offsetof(DebugFrame, tlsData_); }
     static constexpr size_t offsetOfFrame() { return offsetof(DebugFrame, frame_); }
 };
 
-static_assert(DebugFrame::offsetOfResults() == 0, "results shall be at offset 0");
-static_assert(DebugFrame::offsetOfTlsData() + sizeof(TlsData*) == DebugFrame::offsetOfFrame(),
-              "TLS pointer must be a field just before the wasm frame");
-static_assert(sizeof(DebugFrame) % 8 == 0 && DebugFrame::offsetOfFrame() % 8 == 0,
-              "DebugFrame and its portion is 8-bytes aligned for AbstractFramePtr");
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasmdebugframe_js_h
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -588,20 +588,16 @@ GeckoRestyleManager::UpdateOnlyAnimation
 
   transitionManager->SetInAnimationOnlyStyleUpdate(true);
 
   RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
                          ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
   tracker.Init(this);
 
   if (doCSS) {
-    // FIXME:  We should have the transition manager and animation manager
-    // add only the elements for which animations are currently throttled
-    // (i.e., animating on the compositor with main-thread style updates
-    // suppressed).
     PresContext()->EffectCompositor()->AddStyleUpdatesTo(tracker);
   }
 
   if (doSMIL) {
     animationController->AddStyleUpdatesTo(tracker);
   }
 
   ProcessRestyles(tracker);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1570,17 +1570,18 @@ nsDisplayImage::GetDestRect()
   return imageFrame->PredictedDestRect(frameContentBox);
 }
 
 LayerState
 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               const ContainerLayerParameters& aParameters)
 {
-  if (!nsDisplayItem::ForceActiveLayers()) {
+  if (!nsDisplayItem::ForceActiveLayers() &&
+      aManager->GetBackendType() != layers::LayersBackend::LAYERS_WR) {
     bool animated = false;
     if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
         mImage->GetType() != imgIContainer::TYPE_RASTER ||
         NS_FAILED(mImage->GetAnimated(&animated)) ||
         !animated) {
       if (!aManager->IsCompositingCheap() ||
           !nsLayoutUtils::GPUImageScalingEnabled()) {
         return LAYER_NONE;
--- a/layout/reftests/bugs/reftest-stylo.list
+++ b/layout/reftests/bugs/reftest-stylo.list
@@ -301,17 +301,18 @@ fails == 273681-1.html 273681-1.html
 fails == 280708-1a.html 280708-1a.html
 fails == 280708-1b.html 280708-1b.html
 == 281241-1.html 281241-1.html
 == 281241-2.xhtml 281241-2.xhtml
 == 283686-1.html 283686-1.html
 == 283686-2.html 283686-2.html
 == 283686-3.html 283686-3.html
 fails == 289384-1.xhtml 289384-1.xhtml
-skip-if(stylo) == 289480.html#top 289480.html#top # Bug 1341705
+fails == 289480.html#top 289480-ref.html
+fails == 289480-ref.html 289480-ref.html
 fails == 290129-1.html 290129-1.html
 fails == 291078-1.html 291078-1.html
 == 291078-2.html 291078-2.html
 == 291262-1.html 291262-1.html
 == 294306-1.html 294306-1.html
 == 294306-1.html 294306-1.html
 fails == 296361-1.html 296361-1.html
 == 296904-1.html 296904-1.html
@@ -1470,17 +1471,17 @@ fails random == 536061.html 536061.html
 fails == 538909-1.html 538909-1.html
 == 538935-1.html 538935-1.html
 == 539226-1.html 539226-1.html
 == 539323-1.html 539323-1.html
 == 539323-2.html 539323-2.html
 == 539323-3.html 539323-3.html
 fails == 539880-1.html 539880-1.html
 fails == 539880-1-dynamic.html 539880-1-dynamic.html
-skip-if(stylo) == 539949-1.html#test2 539949-1.html#test2 # load fails???
+fails == 539949-1.html#test2 539949-1-ref.html#test2
 fails == 541382-1.html 541382-1.html
 == 541406-1.html 541406-1.html
 fails needs-focus == 542116-1.html 542116-1.html
 fails needs-focus == 542116-2.html 542116-2.html
 needs-focus == 542116-3.html 542116-3.html
 == 542317-1.html 542317-1.html
 == 542620-1.html 542620-1.html
 fails == 545049-1.html 545049-1.html # Bug 1341785
--- a/layout/reftests/scrolling/reftest-stylo.list
+++ b/layout/reftests/scrolling/reftest-stylo.list
@@ -1,10 +1,10 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
-skip-if(stylo) HTTP == deferred-anchor.xhtml#d deferred-anchor.xhtml#d # Fails to load?
+HTTP == deferred-anchor.xhtml#d deferred-anchor-ref.xhtml#d
 fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,23) == deferred-anchor2.xhtml deferred-anchor2.xhtml
 fails HTTP == fixed-1.html fixed-1.html
 fails == fixed-table-1.html fixed-table-1.html
 fails HTTP == fixed-opacity-1.html fixed-opacity-1.html
 fails HTTP == fixed-opacity-2.html fixed-opacity-2.html
 fails == fixed-text-1.html fixed-text-1.html
 HTTP == fixed-text-2.html fixed-text-2.html
 fails == iframe-border-radius.html iframe-border-radius.html
@@ -18,17 +18,17 @@ fails == scroll-behavior-4.html scroll-b
 fails == scroll-behavior-5.html scroll-behavior-5.html
 fails == scroll-behavior-6.html scroll-behavior-6.html
 fails == scroll-behavior-7.html scroll-behavior-7.html
 fails == scroll-behavior-8.html scroll-behavior-8.html
 fails == scroll-behavior-9.html scroll-behavior-9.html
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-10.html scroll-behavior-10.html
 fails == scroll-behavior-textarea.html scroll-behavior-textarea.html
 fails HTTP == simple-1.html simple-1.html
-skip-if(stylo) HTTP == subpixel-1.html#d subpixel-1.html#d # Fails to load?
+fails HTTP == subpixel-1.html#d subpixel-1-ref.html#d
 == text-1.html text-1.html
 == text-2.html?up text-2.html?up
 == transformed-1.html transformed-1.html
 HTTP == transformed-1.html?up transformed-1.html?up
 == uncovering-1.html uncovering-1.html
 == uncovering-2.html uncovering-2.html
 fails == less-than-scrollbar-height.html less-than-scrollbar-height.html
 fails == huge-horizontal-overflow.html huge-horizontal-overflow.html
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1723,20 +1723,20 @@ nsStyleSet::ResolveStyleWithReplacement(
   if (!(aFlags & eSkipStartingAnimations) &&
       (pseudoType == CSSPseudoElementType::NotPseudo ||
        pseudoType == CSSPseudoElementType::before ||
        pseudoType == CSSPseudoElementType::after)) {
     // We want to compute a correct elementForAnimation to pass in
     // because at this point the parameter is more than just the element
     // for animation; it's also used for the SetBodyTextColor call when
     // it's the body element.
-    // However, we only want to set the flag to call CheckAnimationRule
+    // However, we only want to set the flag to call UpdateAnimations
     // if we're dealing with a replacement (such as style attribute
     // replacement) that could lead to the animation property changing,
-    // and we explicitly do NOT want to call CheckAnimationRule when
+    // and we explicitly do NOT want to call UpdateAnimations when
     // we're trying to do an animation-only update.
     if (aReplacements & ~(eRestyle_CSSTransitions | eRestyle_CSSAnimations)) {
       flags |= eDoAnimation;
     }
     elementForAnimation = aElement;
 #ifdef DEBUG
     {
       nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(elementForAnimation);
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -511,40 +511,59 @@ nsTableFrame::AdjustRowIndices(int32_t  
 }
 
 
 void
 nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
 {
   // Iterate over the row groups and adjust the row indices of all rows
   // omit the rowgroups that will be inserted later
+  mDeletedRowIndexRanges.clear();
+
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
 
-  int32_t rowIndex = 0;
   nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
   nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
   while (!excludeRowGroupsEnumerator.AtEnd()) {
     excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
+#ifdef DEBUG
+    {
+      // Check to make sure that the row indices of all rows in excluded row
+      // groups are '0' (i.e. the initial value since they haven't been added yet)
+      const nsFrameList& rowFrames =
+        excludeRowGroupsEnumerator.get()->PrincipalChildList();
+      for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
+        nsTableRowFrame* row = static_cast<nsTableRowFrame*>(rows.get());
+        MOZ_ASSERT(row->GetRowIndex() == 0,
+                   "exclusions cannot be used for rows that were already added,"
+                   "because we'd need to process mDeletedRowIndexRanges");
+      }
+    }
+#endif
     excludeRowGroupsEnumerator.Next();
   }
 
+  int32_t rowIndex = 0;
   for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
     if (!excludeRowGroups.GetEntry(rgFrame)) {
       const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
       for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
-        if (mozilla::StyleDisplay::TableRow == rows.get()->StyleDisplay()->mDisplay) {
-          ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
+        if (mozilla::StyleDisplay::TableRow ==
+            rows.get()->StyleDisplay()->mDisplay) {
+          nsTableRowFrame* row = static_cast<nsTableRowFrame*>(rows.get());
+          row->SetRowIndex(rowIndex);
           rowIndex++;
         }
       }
     }
   }
 }
+
 void
 nsTableFrame::InsertColGroups(int32_t                   aStartColIndex,
                               const nsFrameList::Slice& aColGroups)
 {
   int32_t colIndex = aStartColIndex;
   nsFrameList::Enumerator colGroups(aColGroups);
   for (; !colGroups.AtEnd(); colGroups.Next()) {
     MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame);
@@ -915,23 +934,28 @@ nsTableFrame::InsertRows(nsTableRowGroup
   printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
   Dump(true, false, true);
 #endif
 
   int32_t numColsToAdd = 0;
   nsTableCellMap* cellMap = GetCellMap();
   if (cellMap) {
     TableArea damageArea(0, 0, 0, 0);
-    bool didRecalculate = RecalculateRowIndices();
+    bool shouldRecalculateIndex = !IsDeletedRowIndexRangesEmpty();
+    if (shouldRecalculateIndex) {
+      ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
+    }
     int32_t origNumRows = cellMap->GetRowCount();
     int32_t numNewRows = aRowFrames.Length();
     cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
     MatchCellMapToColCache(cellMap);
 
-    if (!didRecalculate) {
+    // Perform row index adjustment only if row indices were not
+    // reset above
+    if (!shouldRecalculateIndex) {
       if (aRowIndex < origNumRows) {
         AdjustRowIndices(aRowIndex, numNewRows);
       }
 
       // assign the correct row indices to the new rows. If they were recalculated
       // above it may not have been done correctly because each row is constructed
       // with index 0
       for (int32_t rowB = 0; rowB < numNewRows; rowB++) {
@@ -947,42 +971,16 @@ nsTableFrame::InsertRows(nsTableRowGroup
 #ifdef DEBUG_TABLE_CELLMAP
   printf("=== insertRowsAfter \n");
   Dump(true, false, true);
 #endif
 
   return numColsToAdd;
 }
 
-bool
-nsTableFrame::RecalculateRowIndices()
-{
-  if (mDeletedRowIndexRanges.size() == 0) {
-    return false;
-  }
-  mDeletedRowIndexRanges.clear();
-
-  RowGroupArray rowGroups;
-  OrderRowGroups(rowGroups);
-
-  int32_t rowIndex = 0;
-  for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
-    nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
-    const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
-    for (nsFrameList::Enumerator rEnum(rowFrames); !rEnum.AtEnd(); rEnum.Next()) {
-      if (mozilla::StyleDisplay::TableRow == rEnum.get()->StyleDisplay()->mDisplay) {
-        nsTableRowFrame *row = static_cast<nsTableRowFrame*>(rEnum.get());
-        row->SetRowIndex(rowIndex);
-        rowIndex++;
-      }
-    }
-  }
-  return true;
-}
-
 void
 nsTableFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex)
 {
   if (mDeletedRowIndexRanges.size() == 0) {
     mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>
                                     (aDeletedRowStoredIndex,
                                      aDeletedRowStoredIndex));
     return;
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -867,22 +867,16 @@ public:
    *  rows that have already been deleted can never call this method).
    *  @param aStoredIndex - The stored index value that must be adjusted
    *  Note - 'stored' index here refers to the index that was assigned to
    *  the row before any remove row operations were performed i.e. the
    *  value of mRowIndex and not the value returned by GetRowIndex()
    */
   int32_t GetAdjustmentForStoredIndex(int32_t aStoredIndex);
 
-  /** Recalculate the row indices of all rows (if needed) and overwrite
-   *  the value of the stored index with this newly calculated index.
-   *  Returns whether recalculation was performed.
-   */
-  bool RecalculateRowIndices();
-
   /** Returns whether mDeletedRowIndexRanges is empty
    */
   bool IsDeletedRowIndexRangesEmpty() const {
     return mDeletedRowIndexRanges.empty();
   }
 
 public:
 
--- a/mfbt/Compression.cpp
+++ b/mfbt/Compression.cpp
@@ -7,16 +7,25 @@
 #include "mozilla/Compression.h"
 #include "mozilla/CheckedInt.h"
 
 // Without including <string>, MSVC 2015 complains about e.g. the impossibility
 // to convert `const void* const` to `void*` when calling memchr from
 // corecrt_memory.h.
 #include <string>
 
+// Because we wrap lz4.c in an anonymous namespace, all of its #includes
+// go in the anonymous namespace too. This would create conflicting
+// declarations for intrinsic functions that are internally defined
+// at top-level. Including intrin.h here prevents it from being included
+// later within the anonymous namespace.
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
 using namespace mozilla::Compression;
 
 namespace {
 
 #include "lz4.c"
 
 }/* anonymous namespace */
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -282,16 +282,17 @@ gvjar.sources += [geckoview_source_dir +
     'gfx/RectUtils.java',
     'gfx/RenderTask.java',
     'gfx/StackScroller.java',
     'gfx/SurfaceTextureListener.java',
     'gfx/ViewTransform.java',
     'gfx/VsyncSource.java',
     'InputConnectionListener.java',
     'InputMethods.java',
+    'NativeQueue.java',
     'NotificationListener.java',
     'NSSBridge.java',
     'permissions/PermissionBlock.java',
     'permissions/Permissions.java',
     'permissions/PermissionsHelper.java',
     'PrefsHelper.java',
     'process/GeckoProcessManager.java',
     'process/GeckoServiceChildProcess.java',
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -4,16 +4,17 @@
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.mozglue.JNIObject;
+import org.mozilla.gecko.NativeQueue.StateHolder;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -52,24 +53,34 @@ public final class EventDispatcher exten
     private final Map<String, List<BundleEventListener>> mGeckoThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_GECKO_EVENTS_COUNT);
     private final Map<String, List<BundleEventListener>> mUiThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_UI_EVENTS_COUNT);
     private final Map<String, List<BundleEventListener>> mBackgroundThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT);
 
     private boolean mAttachedToGecko;
+    private final StateHolder mStateHolder;
 
     @ReflectionTarget
     @WrapForJNI(calledFrom = "gecko")
     public static EventDispatcher getInstance() {
         return INSTANCE;
     }
 
     /* package */ EventDispatcher() {
+        mStateHolder = GeckoThread.getStateHolder();
+    }
+
+    /* package */ EventDispatcher(final NativeQueue.StateHolder stateHolder) {
+        mStateHolder = stateHolder;
+    }
+
+    private boolean isReadyForDispatchingToGecko() {
+        return mStateHolder.isReady();
     }
 
     @WrapForJNI(dispatchTo = "gecko") @Override // JNIObject
     protected native void disposeNative();
 
     @WrapForJNI private static final int DETACHED = 0;
     @WrapForJNI private static final int ATTACHED = 1;
     @WrapForJNI private static final int REATTACHING = 2;
@@ -223,17 +234,17 @@ public final class EventDispatcher exten
      *
      * @param type Event type
      * @param message Bundle message
      * @param callback Optional object for callbacks from events.
      */
     public void dispatch(final String type, final GeckoBundle message,
                          final EventCallback callback) {
         synchronized (this) {
-            if (mAttachedToGecko && hasGeckoListener(type)) {
+            if (isReadyForDispatchingToGecko() && hasGeckoListener(type)) {
                 dispatchToGecko(type, message, JavaCallbackDelegate.wrap(callback));
                 return;
             }
         }
 
         if (!dispatchToThreads(type, message, /* callback */ callback)) {
             Log.w(LOGTAG, "No listener for " + type);
         }
@@ -274,25 +285,28 @@ public final class EventDispatcher exten
             return true;
         }
 
         if (dispatchToThread(type, message, callback,
                              mBackgroundThreadListeners, ThreadUtils.getBackgroundHandler())) {
             return true;
         }
 
-        if (!GeckoThread.isRunning()) {
-            // Usually, we discard an event if there is no listeners for it by the time of
-            // the dispatch. However, if Gecko is not ready and there is no listener for
-            // this event that's possibly headed to Gecko, we make a special exception to
-            // queue this event until Gecko is ready. This way, Gecko can first register
-            // its listeners, and accept the event when it is ready.
-            GeckoThread.queueNativeCall(this, "dispatchToGecko",
-                                        String.class, type, GeckoBundle.class, message,
-                                        EventCallback.class, JavaCallbackDelegate.wrap(callback));
+        if (!isReadyForDispatchingToGecko()) {
+            // Usually, we discard an event if there is no listeners for it by
+            // the time of the dispatch. However, if Gecko(View) is not ready and
+            // there is no listener for this event that's possibly headed to
+            // Gecko, we make a special exception to queue this event until
+            // Gecko(View) is ready. This way, Gecko can first register its
+            // listeners, and accept the event when it is ready.
+            NativeQueue.queueUntil(mStateHolder,
+                mStateHolder.getReadyState(), this, "dispatchToGecko",
+                String.class, type,
+                GeckoBundle.class, message,
+                EventCallback.class, JavaCallbackDelegate.wrap(callback));
             return true;
         }
 
         return false;
     }
 
     private boolean dispatchToThread(final String type,
                                      final GeckoBundle message,
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -3,49 +3,42 @@
  * 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;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.NativeQueue.StateHolder;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Locale;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.StringTokenizer;
 
 public class GeckoThread extends Thread {
     private static final String LOGTAG = "GeckoThread";
 
-    public enum State {
+    public enum State implements NativeQueue.State {
         // After being loaded by class loader.
         @WrapForJNI INITIAL(0),
         // After launching Gecko thread
         @WrapForJNI LAUNCHED(1),
         // After loading the mozglue library.
         @WrapForJNI MOZGLUE_READY(2),
         // After loading the libxul library.
         @WrapForJNI LIBS_READY(3),
@@ -68,57 +61,40 @@ public class GeckoThread extends Thread 
          * LIBS_READY because both states have a similar amount of components available.
          */
         private final int rank;
 
         private State(int rank) {
             this.rank = rank;
         }
 
-        public boolean is(final State other) {
+        @Override
+        public boolean is(final NativeQueue.State other) {
             return this == other;
         }
 
-        public boolean isAtLeast(final State other) {
-            return this.rank >= other.rank;
+        @Override
+        public boolean isAtLeast(final NativeQueue.State other) {
+            if (other instanceof State) {
+                return this.rank >= ((State) other).rank;
+            }
+            return false;
         }
+    }
 
-        public boolean isAtMost(final State other) {
-            return this.rank <= other.rank;
-        }
+    private static final StateHolder sStateHolder =
+        new StateHolder(State.INITIAL, State.RUNNING);
 
-        // Inclusive
-        public boolean isBetween(final State min, final State max) {
-            return this.rank >= min.rank && this.rank <= max.rank;
-        }
+    /* package */ static StateHolder getStateHolder() {
+        return sStateHolder;
     }
 
     public static final State MIN_STATE = State.INITIAL;
     public static final State MAX_STATE = State.EXITED;
 
-    private static volatile State sState = State.INITIAL;
-
-    private static class QueuedCall {
-        public Method method;
-        public Object target;
-        public Object[] args;
-        public State state;
-
-        public QueuedCall(final Method method, final Object target,
-                          final Object[] args, final State state) {
-            this.method = method;
-            this.target = target;
-            this.args = args;
-            this.state = state;
-        }
-    }
-
-    private static final int QUEUED_CALLS_COUNT = 16;
-    private static final ArrayList<QueuedCall> QUEUED_CALLS = new ArrayList<>(QUEUED_CALLS_COUNT);
-
     private static final Runnable UI_THREAD_CALLBACK = new Runnable() {
         @Override
         public void run() {
             ThreadUtils.assertOnUiThread();
             long nextDelay = runUiThreadCallback();
             if (nextDelay >= 0) {
                 ThreadUtils.getUiHandler().postDelayed(this, nextDelay);
             }
@@ -254,163 +230,16 @@ public class GeckoThread extends Thread 
         return !isState(State.INITIAL);
     }
 
     @RobocopTarget
     public static boolean isRunning() {
         return isState(State.RUNNING);
     }
 
-    // Invoke the given Method and handle checked Exceptions.
-    private static void invokeMethod(final Method method, final Object obj, final Object[] args) {
-        try {
-            method.setAccessible(true);
-            method.invoke(obj, args);
-        } catch (final IllegalAccessException e) {
-            throw new IllegalStateException("Unexpected exception", e);
-        } catch (final InvocationTargetException e) {
-            throw new UnsupportedOperationException("Cannot make call", e.getCause());
-        }
-    }
-
-    // Queue a call to the given method.
-    private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
-                                              final Object obj, final Object[] args,
-                                              final State state) {
-        final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
-        final ArrayList<Object> argValues = new ArrayList<>(args.length);
-
-        for (int i = 0; i < args.length; i++) {
-            if (args[i] instanceof Class) {
-                argTypes.add((Class<?>) args[i]);
-                argValues.add(args[++i]);
-                continue;
-            }
-            Class<?> argType = args[i].getClass();
-            if (argType == Boolean.class) argType = Boolean.TYPE;
-            else if (argType == Byte.class) argType = Byte.TYPE;
-            else if (argType == Character.class) argType = Character.TYPE;
-            else if (argType == Double.class) argType = Double.TYPE;
-            else if (argType == Float.class) argType = Float.TYPE;
-            else if (argType == Integer.class) argType = Integer.TYPE;
-            else if (argType == Long.class) argType = Long.TYPE;
-            else if (argType == Short.class) argType = Short.TYPE;
-            argTypes.add(argType);
-            argValues.add(args[i]);
-        }
-        final Method method;
-        try {
-            method = cls.getDeclaredMethod(
-                    methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
-        } catch (final NoSuchMethodException e) {
-            throw new IllegalArgumentException("Cannot find method", e);
-        }
-
-        if (!Modifier.isNative(method.getModifiers())) {
-            // As a precaution, we disallow queuing non-native methods. Queuing non-native
-            // methods is dangerous because the method could end up being called on either
-            // the original thread or the Gecko thread depending on timing. Native methods
-            // usually handle this by posting an event to the Gecko thread automatically,
-            // but there is no automatic mechanism for non-native methods.
-            throw new UnsupportedOperationException("Not allowed to queue non-native methods");
-        }
-
-        if (isStateAtLeast(state)) {
-            invokeMethod(method, obj, argValues.toArray());
-            return;
-        }
-
-        QUEUED_CALLS.add(new QueuedCall(
-                method, obj, argValues.toArray(), state));
-    }
-
-    /**
-     * Queue a call to the given static method until Gecko is in the given state.
-     *
-     * @param state The Gecko state in which the native call could be executed.
-     *              Default is State.RUNNING, which means this queued call will
-     *              run when Gecko is at or after RUNNING state.
-     * @param cls Class that declares the static method.
-     * @param methodName Name of the static method.
-     * @param args Args to call the static method with; to specify a parameter type,
-     *             pass in a Class instance first, followed by the value.
-     */
-    public static void queueNativeCallUntil(final State state, final Class<?> cls,
-                                            final String methodName, final Object... args) {
-        synchronized (QUEUED_CALLS) {
-            queueNativeCallLocked(cls, methodName, null, args, state);
-        }
-    }
-
-    /**
-     * Queue a call to the given static method until Gecko is in the RUNNING state.
-     */
-    public static void queueNativeCall(final Class<?> cls, final String methodName,
-                                       final Object... args) {
-        synchronized (QUEUED_CALLS) {
-            queueNativeCallLocked(cls, methodName, null, args, State.RUNNING);
-        }
-    }
-
-    /**
-     * Queue a call to the given instance method until Gecko is in the given state.
-     *
-     * @param state The Gecko state in which the native call could be executed.
-     * @param obj Object that declares the instance method.
-     * @param methodName Name of the instance method.
-     * @param args Args to call the instance method with; to specify a parameter type,
-     *             pass in a Class instance first, followed by the value.
-     */
-    public static void queueNativeCallUntil(final State state, final Object obj,
-                                            final String methodName, final Object... args) {
-        synchronized (QUEUED_CALLS) {
-            queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
-        }
-    }
-
-    /**
-     * Queue a call to the given instance method until Gecko is in the RUNNING state.
-     */
-    public static void queueNativeCall(final Object obj, final String methodName,
-                                       final Object... args) {
-        synchronized (QUEUED_CALLS) {
-            queueNativeCallLocked(obj.getClass(), methodName, obj, args, State.RUNNING);
-        }
-    }
-
-    // Run all queued methods
-    private static void flushQueuedNativeCallsLocked(final State state) {
-        int lastSkipped = -1;
-        for (int i = 0; i < QUEUED_CALLS.size(); i++) {
-            final QueuedCall call = QUEUED_CALLS.get(i);
-            if (call == null) {
-                // We already handled the call.
-                continue;
-            }
-            if (!state.isAtLeast(call.state)) {
-                // The call is not ready yet; skip it.
-                lastSkipped = i;
-                continue;
-            }
-            // Mark as handled.
-            QUEUED_CALLS.set(i, null);
-
-            invokeMethod(call.method, call.target, call.args);
-        }
-        if (lastSkipped < 0) {
-            // We're done here; release the memory
-            QUEUED_CALLS.clear();
-            QUEUED_CALLS.trimToSize();
-        } else if (lastSkipped < QUEUED_CALLS.size() - 1) {
-            // We skipped some; free up null entries at the end,
-            // but keep all the previous entries for later.
-            QUEUED_CALLS.subList(lastSkipped + 1, QUEUED_CALLS.size()).clear();
-        }
-    }
-
     private static void loadGeckoLibs(final Context context, final String resourcePath) {
         GeckoLoader.loadSQLiteLibs(context, resourcePath);
         GeckoLoader.loadNSSLibs(context, resourcePath);
         GeckoLoader.loadGeckoLibs(context, resourcePath);
     }
 
     private static void initGeckoEnvironment() {
         final Context context = GeckoAppShell.getApplicationContext();
@@ -612,72 +441,62 @@ public class GeckoThread extends Thread 
 
     /**
      * Check that the current Gecko thread state matches the given state.
      *
      * @param state State to check
      * @return True if the current Gecko thread state matches
      */
     public static boolean isState(final State state) {
-        return sState.is(state);
+        return sStateHolder.getState().is(state);
     }
 
     /**
      * Check that the current Gecko thread state is at the given state or further along,
      * according to the order defined in the State enum.
      *
      * @param state State to check
      * @return True if the current Gecko thread state matches
      */
     public static boolean isStateAtLeast(final State state) {
-        return sState.isAtLeast(state);
+        return sStateHolder.getState().isAtLeast(state);
     }
 
     /**
      * Check that the current Gecko thread state is at the given state or prior,
      * according to the order defined in the State enum.
      *
      * @param state State to check
      * @return True if the current Gecko thread state matches
      */
     public static boolean isStateAtMost(final State state) {
-        return sState.isAtMost(state);
+        return state.isAtLeast(sStateHolder.getState());
     }
 
     /**
      * Check that the current Gecko thread state falls into an inclusive range of states,
      * according to the order defined in the State enum.
      *
      * @param minState Lower range of allowable states
      * @param maxState Upper range of allowable states
      * @return True if the current Gecko thread state matches
      */
     public static boolean isStateBetween(final State minState, final State maxState) {
-        return sState.isBetween(minState, maxState);
+        return isStateAtLeast(minState) && isStateAtMost(maxState);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private static void setState(final State newState) {
-        ThreadUtils.assertOnGeckoThread();
-        synchronized (QUEUED_CALLS) {
-            flushQueuedNativeCallsLocked(newState);
-            sState = newState;
-        }
+        sStateHolder.setState(newState);
     }
 
     @WrapForJNI(calledFrom = "gecko")
-    private static boolean checkAndSetState(final State currentState, final State newState) {
-        synchronized (QUEUED_CALLS) {
-            if (sState == currentState) {
-                flushQueuedNativeCallsLocked(newState);
-                sState = newState;
-                return true;
-            }
-        }
-        return false;
+    private static boolean checkAndSetState(final State expectedState,
+                                            final State newState) {
+        return sStateHolder.checkAndSetState(expectedState, newState);
     }
 
     @WrapForJNI(stubName = "SpeculativeConnect")
     private static native void speculativeConnectNative(String uri);
 
     public static void speculativeConnect(final String uri) {
         // This is almost always called before Gecko loads, so we don't
         // bother checking here if Gecko is actually loaded or not.
@@ -731,9 +550,41 @@ public class GeckoThread extends Thread 
 
     @WrapForJNI(calledFrom = "ui")
     /* package */ static native long runUiThreadCallback();
 
     @WrapForJNI
     private static void requestUiThreadCallback(long delay) {
         ThreadUtils.getUiHandler().postDelayed(UI_THREAD_CALLBACK, delay);
     }
+
+    /**
+     * Queue a call to the given static method until Gecko is in the RUNNING state.
+     */
+    public static void queueNativeCall(final Class<?> cls, final String methodName,
+                                       final Object... args) {
+        NativeQueue.queueUntil(getStateHolder(), State.RUNNING, cls, methodName, args);
+    }
+
+    /**
+     * Queue a call to the given instance method until Gecko is in the RUNNING state.
+     */
+    public static void queueNativeCall(final Object obj, final String methodName,
+                                       final Object... args) {
+        NativeQueue.queueUntil(getStateHolder(), State.RUNNING, obj, methodName, args);
+    }
+
+    /**
+     * Queue a call to the given instance method until Gecko is in the RUNNING state.
+     */
+    public static void queueNativeCallUntil(final State state, final Object obj, final String methodName,
+                                       final Object... args) {
+        NativeQueue.queueUntil(getStateHolder(), state, obj, methodName, args);
+    }
+
+    /**
+     * Queue a call to the given static method until Gecko is in the RUNNING state.
+     */
+    public static void queueNativeCallUntil(final State state, final Class<?> cls, final String methodName,
+                                       final Object... args) {
+        NativeQueue.queueUntil(getStateHolder(), state, cls, methodName, args);
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -7,16 +7,17 @@
 package org.mozilla.gecko;
 
 import java.util.Set;
 
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.JNIObject;
+import org.mozilla.gecko.NativeQueue.StateHolder;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.Binder;
@@ -36,17 +37,50 @@ import android.view.inputmethod.InputCon
 public class GeckoView extends LayerView
     implements ContextGetter {
 
     private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
-    private final EventDispatcher mEventDispatcher = new EventDispatcher();
+    /* package */ enum State implements NativeQueue.State {
+        @WrapForJNI INITIAL(0),
+        @WrapForJNI READY(1);
+
+        private int rank;
+
+        private State(int rank) {
+            this.rank = rank;
+        }
+
+        @Override
+        public boolean is(final NativeQueue.State other) {
+            return this == other;
+        }
+
+        @Override
+        public boolean isAtLeast(final NativeQueue.State other) {
+            if (other instanceof State) {
+                return this.rank >= ((State) other).rank;
+            }
+            return false;
+        }
+    }
+
+    private final StateHolder mStateHolder =
+        new StateHolder(State.INITIAL, State.READY);
+
+    @WrapForJNI(calledFrom = "gecko")
+    private void setState(final State newState) {
+        mStateHolder.setState(newState);
+    }
+
+    private final EventDispatcher mEventDispatcher =
+        new EventDispatcher(mStateHolder);
 
     private ChromeDelegate mChromeDelegate;
     /* package */ ContentListener mContentListener;
     /* package */ NavigationListener mNavigationListener;
     /* package */ ProgressListener mProgressListener;
     private InputConnectionListener mInputConnectionListener;
 
     private GeckoViewSettings mSettings;
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NativeQueue.java
@@ -0,0 +1,219 @@
+/* -*- 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;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+public class NativeQueue {
+    private static final String LOGTAG = "GeckoNativeQueue";
+
+    public interface State {
+        boolean is(final State other);
+        boolean isAtLeast(final State other);
+    }
+
+    public static class StateHolder {
+        private volatile State mState;
+        private final State mReadyState;
+
+        public StateHolder(final State initial, final State ready) {
+            this.mState = initial;
+            this.mReadyState = ready;
+        }
+
+        public boolean isReady() {
+            return getState().isAtLeast(mReadyState);
+        }
+
+        public State getReadyState() {
+            return mReadyState;
+        }
+
+        public State getState() {
+            return mState;
+        }
+
+        public boolean setState(final State newState) {
+            return checkAndSetState(null, newState);
+        }
+
+        public boolean checkAndSetState(final State expectedState,
+                                        final State newState) {
+            synchronized (NativeQueue.sQueue) {
+                if (expectedState != null && !mState.is(expectedState)) {
+                    return false;
+                }
+                NativeQueue.flushQueuedLocked(newState);
+                mState = newState;
+                return true;
+            }
+        }
+    }
+
+    private static class QueuedCall {
+        public Method method;
+        public Object target;
+        public Object[] args;
+        public State state;
+
+        public QueuedCall(final Method method, final Object target,
+                          final Object[] args, final State state) {
+            this.method = method;
+            this.target = target;
+            this.args = args;
+            this.state = state;
+        }
+    }
+
+    private static final int QUEUED_CALLS_COUNT = 16;
+    /* package */ static final ArrayList<QueuedCall> sQueue =
+        new ArrayList<>(QUEUED_CALLS_COUNT);
+
+    // Invoke the given Method and handle checked Exceptions.
+    private static void invokeMethod(final Method method, final Object obj,
+                                     final Object[] args) {
+        try {
+            method.setAccessible(true);
+            method.invoke(obj, args);
+        } catch (final IllegalAccessException e) {
+            throw new IllegalStateException("Unexpected exception", e);
+        } catch (final InvocationTargetException e) {
+            throw new UnsupportedOperationException("Cannot make call", e.getCause());
+        }
+    }
+
+    // Queue a call to the given method.
+    private static void queueNativeCallLocked(final StateHolder stateHolder,
+                                              final Class<?> cls,
+                                              final String methodName,
+                                              final Object obj,
+                                              final Object[] args,
+                                              final State state) {
+        final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
+        final ArrayList<Object> argValues = new ArrayList<>(args.length);
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i] instanceof Class) {
+                argTypes.add((Class<?>) args[i]);
+                argValues.add(args[++i]);
+                continue;
+            }
+            Class<?> argType = args[i].getClass();
+            if (argType == Boolean.class) argType = Boolean.TYPE;
+            else if (argType == Byte.class) argType = Byte.TYPE;
+            else if (argType == Character.class) argType = Character.TYPE;
+            else if (argType == Double.class) argType = Double.TYPE;
+            else if (argType == Float.class) argType = Float.TYPE;
+            else if (argType == Integer.class) argType = Integer.TYPE;
+            else if (argType == Long.class) argType = Long.TYPE;
+            else if (argType == Short.class) argType = Short.TYPE;
+            argTypes.add(argType);
+            argValues.add(args[i]);
+        }
+        final Method method;
+        try {
+            method = cls.getDeclaredMethod(
+                    methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalArgumentException("Cannot find method", e);
+        }
+
+        if (!Modifier.isNative(method.getModifiers())) {
+            // As a precaution, we disallow queuing non-native methods. Queuing non-native
+            // methods is dangerous because the method could end up being called on either
+            // the original thread or the Gecko thread depending on timing. Native methods
+            // usually handle this by posting an event to the Gecko thread automatically,
+            // but there is no automatic mechanism for non-native methods.
+            throw new UnsupportedOperationException("Not allowed to queue non-native methods");
+        }
+
+        if (stateHolder.getState().isAtLeast(state)) {
+            invokeMethod(method, obj, argValues.toArray());
+            return;
+        }
+
+        sQueue.add(new QueuedCall(
+                method, obj, argValues.toArray(), state));
+    }
+
+    /**
+     * Queue a call to the given instance method if the given current state does
+     * not satisfy the given state.
+     *
+     * @param stateHolder The state holder used to query the current state.
+     * @param state The state in which the native call could be executed.
+     * @param obj Object that declares the instance method.
+     * @param methodName Name of the instance method.
+     * @param args Args to call the instance method with; to specify a parameter
+     *             type, pass in a Class instance first, followed by the value.
+     */
+    public static void queueUntil(final StateHolder stateHolder,
+                                  final State state,
+                                  final Object obj,
+                                  final String methodName,
+                                  final Object... args) {
+        synchronized (sQueue) {
+            queueNativeCallLocked(stateHolder, obj.getClass(), methodName, obj,
+                                  args, state);
+        }
+    }
+
+    /**
+     * Queue a call to the given static method if the given current state does
+     * not satisfy the given state.
+     *
+     * @param stateHolder The state holder used to query the current state.
+     * @param state The state in which the native call could be executed.
+     * @param cls Class that declares the static method.
+     * @param methodName Name of the instance method.
+     * @param args Args to call the instance method with; to specify a parameter
+     *             type, pass in a Class instance first, followed by the value.
+     */
+    public static void queueUntil(final StateHolder stateHolder,
+                                  final State state,
+                                  final Class<?> cls,
+                                  final String methodName,
+                                  final Object... args) {
+        synchronized (sQueue) {
+            queueNativeCallLocked(stateHolder, cls, methodName, null, args, state);
+        }
+    }
+
+    // Run all queued methods
+    private static void flushQueuedLocked(final State state) {
+        int lastSkipped = -1;
+        for (int i = 0; i < sQueue.size(); i++) {
+            final QueuedCall call = sQueue.get(i);
+            if (call == null) {
+                // We already handled the call.
+                continue;
+            }
+            if (!state.isAtLeast(call.state)) {
+                // The call is not ready yet; skip it.
+                lastSkipped = i;
+                continue;
+            }
+            // Mark as handled.
+            sQueue.set(i, null);
+
+            invokeMethod(call.method, call.target, call.args);
+        }
+        if (lastSkipped < 0) {
+            // We're done here; release the memory
+            sQueue.clear();
+            sQueue.trimToSize();
+        } else if (lastSkipped < sQueue.size() - 1) {
+            // We skipped some; free up null entries at the end,
+            // but keep all the previous entries for later.
+            sQueue.subList(lastSkipped + 1, sQueue.size()).clear();
+        }
+    }
+}
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -2158,25 +2158,30 @@ nsHttpTransaction::Do0RTT()
      m0RTTInProgress = true;
    }
    return m0RTTInProgress;
 }
 
 nsresult
 nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
 {
+    LOG(("nsHttpTransaction::Finish0RTT %p %d %d\n", this, aRestart, aAlpnChanged));
     MOZ_ASSERT(m0RTTInProgress);
     m0RTTInProgress = false;
     if (aRestart) {
         // Reset request headers to be sent again.
         nsCOMPtr<nsISeekableStream> seekable =
             do_QueryInterface(mRequestStream);
         if (seekable) {
             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
         } else {
             return NS_ERROR_FAILURE;
         }
+    } else if (!mConnected) {
+        // this is code that was skipped in ::ReadSegments while in 0RTT
+        mConnected = true;
+        mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     }
     return NS_OK;
 }
 
 } // namespace net
 } // namespace mozilla
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -894,26 +894,29 @@ class TestInfoCommand(MachCommandBase):
         default='mozilla-central,mozilla-inbound,autoland',
         help='Report for named branches (default: mozilla-central,mozilla-inbound,autoland)')
     @CommandArgument('--start',
         default=(date.today() - timedelta(7)).strftime("%Y-%m-%d"),
         help='Start date (YYYY-MM-DD)')
     @CommandArgument('--end',
         default=date.today().strftime("%Y-%m-%d"),
         help='End date (YYYY-MM-DD)')
+    @CommandArgument('--verbose', action='store_true',
+        help='Enable debug logging.')
 
     def test_info(self, **params):
 
         import which
         from mozbuild.base import MozbuildObject
 
         self.test_name = params['test_name']
         self.branches = params['branches']
         self.start = params['start']
         self.end = params['end']
+        self.verbose = params['verbose']
 
         if len(self.test_name) < 6:
             print("'%s' is too short for a test name!" % self.test_name)
             return
 
         here = os.path.abspath(os.path.dirname(__file__))
         build_obj = MozbuildObject.from_environment(cwd=here)
 
@@ -1050,19 +1053,26 @@ class TestInfoCommand(MachCommandBase):
     def get_platform(self, record):
         platform = record['build']['platform']
         type = record['build']['type']
         e10s = "-%s" % record['run']['type'] if 'run' in record else ""
         return "%s/%s%s:" % (platform, type, e10s)
 
     def submit(self, query):
         import requests
+        import datetime
+        if self.verbose:
+            print(datetime.datetime.now())
+            print(json.dumps(query))
         response = requests.post("http://activedata.allizom.org/query",
                                  data=json.dumps(query),
                                  stream=True)
+        if self.verbose:
+            print(datetime.datetime.now())
+            print(response)
         response.raise_for_status()
         data = response.json()["data"]
         return data
 
     def report_test_results(self):
         # Report test pass/fail summary from ActiveData
         query = {
             "from": "unittest",
@@ -1087,22 +1097,39 @@ class TestInfoCommand(MachCommandBase):
                 {"lt": {"run.timestamp": {"date": self.end}}}
             ]}
         }
         print("\nTest results for %s on %s between %s and %s" %
             (self.activedata_test_name, self.branches, self.start, self.end))
         data = self.submit(query)
         if data and len(data) > 0:
             data.sort(key=self.get_platform)
+            worst_rate = 0.0
+            worst_platform = None
+            total_runs = 0
+            total_failures = 0
             for record in data:
                 platform = self.get_platform(record)
                 runs = record['count']
+                total_runs = total_runs + runs
                 failures = record['failures']
+                total_failures = total_failures + failures
+                rate = (float)(failures) / runs
+                if rate >= worst_rate:
+                    worst_rate = rate
+                    worst_platform = platform
+                    worst_failures = failures
+                    worst_runs = runs
                 print("%-30s %6d failures in %6d runs" % (
                     platform, failures, runs))
+            print("\nTotal: %d failures in %d runs or %.3f failures/run" %
+                (total_failures, total_runs, (float)(total_failures) / total_runs))
+            if worst_failures > 0:
+                print("Worst rate on %s %d failures in %d runs or %.3f failures/run" %
+                    (worst_platform, worst_failures, worst_runs, worst_rate))
         else:
             print("No test result data found.")
 
     def report_test_durations(self):
         # Report test durations summary from ActiveData
         query = {
 	    "from": "unittest",
             "format": "list",
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -144,17 +144,17 @@ addTest(function(exception_t) {
     setters.push(function(p) { Object.setPrototypeOf(this, p); });
   setters.forEach(function(protoSetter) {
     test_throws(exception_t, new TypeError, function() { protoSetter.call(C, new Object()); }, "proto setter |call| on cross-origin Window");
     test_throws(exception_t, new TypeError, function() { protoSetter.call(C.location, new Object()); }, "proto setter |call| on cross-origin Location");
   });
   if (Reflect.setPrototypeOf) {
     assert_false(Reflect.setPrototypeOf(C, new Object()),
                  "Reflect.setPrototypeOf on cross-origin Window");
-    assert_false(Reflect.setPrototypeOf(C, new Object()),
+    assert_false(Reflect.setPrototypeOf(C.location, new Object()),
                 "Reflect.setPrototypeOf on cross-origin Location");
   }
 }, "[[SetPrototypeOf]] should return false");
 
 /*
  * [[IsExtensible]]
  */
 addTest(function(exception_t) {
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -262,16 +262,17 @@ fail-if = os == "android"
 [test_syncGUID.js]
 [test_strictcompatibility.js]
 [test_targetPlatforms.js]
 [test_theme.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
 [test_types.js]
 [test_undothemeuninstall.js]
+skip-if = appname == "thunderbird"
 [test_undouninstall.js]
 [test_uninstall.js]
 [test_update.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_update_webextensions.js]
 tags = webextensions
 [test_updateCancel.js]
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -6,103 +6,81 @@
 # which doesn't use the updater binary as other applications do and are excluded
 # from running the tests in the moz.build file.
 
 [DEFAULT]
 tags = appupdate
 head = head_update.js
 
 [marSuccessComplete.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marSuccessPartial.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marFailurePartial.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marStageSuccessComplete.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marStageSuccessPartial.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marVersionDowngrade.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985 and mar signing
 [marWrongChannel.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985 and mar signing
 [marStageFailurePartial.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 [marCallbackAppSuccessComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marCallbackAppSuccessPartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marCallbackAppStageSuccessComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marCallbackAppStageSuccessPartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marAppInUseSuccessComplete.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985 and bug 1164150
 [marAppInUseStageSuccessComplete_unix.js]
 skip-if = os == 'win'
-reason = not a Windows test and bug 1164150
+reason = not a Windows test
 [marAppInUseStageFailureComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileLockedFailureComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileLockedFailurePartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileLockedStageFailureComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileLockedStageFailurePartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileInUseSuccessComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileInUseSuccessPartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marRMRFDirFileInUseSuccessComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marRMRFDirFileInUseSuccessPartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileInUseStageFailureComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marFileInUseStageFailurePartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marRMRFDirFileInUseStageFailureComplete_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marRMRFDirFileInUseStageFailurePartial_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marAppApplyDirLockedStageFailure_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marAppApplyUpdateAppBinInUseStageSuccess_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
 [marAppApplyUpdateSuccess.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985 and bug 1164150
 [marAppApplyUpdateStageSuccess.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985 and bug 1164150
 [marWrongApplyToDirFailure_win.js]
-skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
-reason = Windows only test and bug 1291985
+skip-if = os != 'win'
+reason = Windows only test
--- a/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
@@ -4,124 +4,66 @@
 
 # Tests that require the updater binary and the maintenance service.
 
 [DEFAULT]
 tags = appupdate
 head = head_update.js
 
 [bootstrapSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marSuccessCompleteSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marSuccessPartialSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFailurePartialSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marStageSuccessCompleteSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marStageSuccessPartialSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marStageFailurePartialSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marCallbackAppSuccessCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marCallbackAppSuccessPartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marCallbackAppStageSuccessCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marCallbackAppStageSuccessPartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppInUseSuccessCompleteSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppInUseStageFailureCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileLockedFailureCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileLockedFailurePartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileLockedStageFailureCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileLockedStageFailurePartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileInUseSuccessCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileInUseSuccessPartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marRMRFDirFileInUseSuccessCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marRMRFDirFileInUseSuccessPartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileInUseStageFailureCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marFileInUseStageFailurePartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marRMRFDirFileInUseStageFailureCompleteSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marRMRFDirFileInUseStageFailurePartialSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppApplyDirLockedStageFailureSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppApplyUpdateSuccessSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marAppApplyUpdateStageSuccessSvc.js]
-skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
-reason = bug 1291985
 run-sequentially = Uses the Mozilla Maintenance Service.
 [checkUpdaterSigSvc.js]
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -429,16 +429,18 @@ xul|button[type="menu"] > xul|menupopup 
   margin: 0;
   padding: 0;
   border-top: 1px solid var(--in-content-box-border-color);
   border-bottom: none;
 }
 
 /* textboxes */
 
+html|input[type="email"],
+html|input[type="tel"],
 html|input[type="text"],
 html|textarea,
 xul|textbox {
   -moz-appearance: none;
   color: var(--in-content-text-color);
   border: 1px solid var(--in-content-box-border-color);
   -moz-border-top-colors: none !important;
   -moz-border-right-colors: none !important;
@@ -457,29 +459,35 @@ xul|textbox {
 /* Create a separate rule to unset these styles on .tree-input instead of
    using :not(.tree-input) so the selector specifity doesn't change. */
 xul|textbox.tree-input {
   min-height: unset;
   padding-right: unset;
   padding-left: unset;
 }
 
+html|input[type="email"],
+html|input[type="tel"],
 html|input[type="text"],
 html|textarea {
   font-family: inherit;
   font-size: inherit;
   padding: 5px 10px;
 }
 
+html|input[type="email"]:focus,
+html|input[type="tel"]:focus,
 html|input[type="text"]:focus,
 html|textarea:focus,
 xul|textbox[focused] {
   border-color: var(--in-content-border-focus);
 }
 
+html|input[type="email"]:disabled,
+html|input[type="tel"]:disabled,
 html|input[type="text"]:disabled,
 html|textarea:disabled,
 xul|textbox[disabled="true"] {
   opacity: 0.5;
 }
 
 /* Links */
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -995,16 +995,43 @@ constexpr char GeckoThread::State::RUNNI
 auto GeckoThread::State::RUNNING() -> State::LocalRef
 {
     return mozilla::jni::Field<RUNNING_t>::Get(State::Context(), nullptr);
 }
 
 const char GeckoView::name[] =
         "org/mozilla/gecko/GeckoView";
 
+constexpr char GeckoView::SetState_t::name[];
+constexpr char GeckoView::SetState_t::signature[];
+
+auto GeckoView::SetState(mozilla::jni::Object::Param a0) const -> void
+{
+    return mozilla::jni::Method<SetState_t>::Call(GeckoView::mCtx, nullptr, a0);
+}
+
+const char GeckoView::State::name[] =
+        "org/mozilla/gecko/GeckoView$State";
+
+constexpr char GeckoView::State::INITIAL_t::name[];
+constexpr char GeckoView::State::INITIAL_t::signature[];
+
+auto GeckoView::State::INITIAL() -> State::LocalRef
+{
+    return mozilla::jni::Field<INITIAL_t>::Get(State::Context(), nullptr);
+}
+
+constexpr char GeckoView::State::READY_t::name[];
+constexpr char GeckoView::State::READY_t::signature[];
+
+auto GeckoView::State::READY() -> State::LocalRef
+{
+    return mozilla::jni::Field<READY_t>::Get(State::Context(), nullptr);
+}
+
 const char GeckoView::Window::name[] =
         "org/mozilla/gecko/GeckoView$Window";
 
 constexpr char GeckoView::Window::Close_t::name[];
 constexpr char GeckoView::Window::Close_t::signature[];
 
 constexpr char GeckoView::Window::DisposeNative_t::name[];
 constexpr char GeckoView::Window::DisposeNative_t::signature[];
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2984,25 +2984,96 @@ public:
 
 class GeckoView : public mozilla::jni::ObjectBase<GeckoView>
 {
 public:
     static const char name[];
 
     explicit GeckoView(const Context& ctx) : ObjectBase<GeckoView>(ctx) {}
 
+    class State;
     class Window;
 
+    struct SetState_t {
+        typedef GeckoView Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "setState";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/GeckoView$State;)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    auto SetState(mozilla::jni::Object::Param) const -> void;
+
     static const int32_t LOAD_DEFAULT = 0;
 
     static const int32_t LOAD_NEW_TAB = 1;
 
     static const int32_t LOAD_SWITCH_TAB = 2;
 
     static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::GECKO;
+
+};
+
+class GeckoView::State : public mozilla::jni::ObjectBase<State>
+{
+public:
+    static const char name[];
+
+    explicit State(const Context& ctx) : ObjectBase<State>(ctx) {}
+
+    struct INITIAL_t {
+        typedef State Owner;
+        typedef State::LocalRef ReturnType;
+        typedef State::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "INITIAL";
+        static constexpr char signature[] =
+                "Lorg/mozilla/gecko/GeckoView$State;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto INITIAL() -> State::LocalRef;
+
+    struct READY_t {
+        typedef State Owner;
+        typedef State::LocalRef ReturnType;
+        typedef State::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "READY";
+        static constexpr char signature[] =
+                "Lorg/mozilla/gecko/GeckoView$State;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto READY() -> State::LocalRef;
+
+    static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
 class GeckoView::Window : public mozilla::jni::ObjectBase<Window>
 {
 public:
     static const char name[];
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -504,20 +504,23 @@ nsresult
 nsAppShell::Init()
 {
     nsresult rv = nsBaseAppShell::Init();
     nsCOMPtr<nsIObserverService> obsServ =
         mozilla::services::GetObserverService();
     if (obsServ) {
         obsServ->AddObserver(this, "browser-delayed-startup-finished", false);
         obsServ->AddObserver(this, "profile-after-change", false);
-        obsServ->AddObserver(this, "chrome-document-loaded", false);
         obsServ->AddObserver(this, "tab-child-created", false);
         obsServ->AddObserver(this, "quit-application-granted", false);
         obsServ->AddObserver(this, "xpcom-shutdown", false);
+
+        if (XRE_IsParentProcess()) {
+            obsServ->AddObserver(this, "chrome-document-loaded", false);
+        }
     }
 
     if (sPowerManagerService)
         sPowerManagerService->AddWakeLockListener(sWakeLockListener);
 
     Preferences::AddStrongObservers(this, kObservedPrefs);
     mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
     return rv;
@@ -577,18 +580,28 @@ nsAppShell::Observe(nsISupports* aSubjec
 
     } else if (!strcmp(aTopic, "chrome-document-loaded")) {
         if (jni::IsAvailable()) {
             // Our first window has loaded, assume any JS initialization has run.
             java::GeckoThread::CheckAndSetState(
                     java::GeckoThread::State::PROFILE_READY(),
                     java::GeckoThread::State::RUNNING());
         }
-        removeObserver = true;
 
+        // Enable the window event dispatcher for the given GeckoView.
+        nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject);
+        MOZ_ASSERT(doc);
+        nsCOMPtr<nsIWidget> widget =
+            WidgetUtils::DOMWindowToWidget(doc->GetWindow());
+        MOZ_ASSERT(widget);
+        if (widget->WindowType() == nsWindowType::eWindowType_toplevel) {
+            // Make sure to call this only on top level nsWindow.
+            const auto window = static_cast<nsWindow*>(widget.get());
+            window->EnableEventDispatcher();
+        }
     } else if (!strcmp(aTopic, "quit-application-granted")) {
         if (jni::IsAvailable()) {
             java::GeckoThread::SetState(
                     java::GeckoThread::State::EXITING());
 
             // We are told explicitly to quit, perhaps due to
             // nsIAppStartup::Quit being called. We should release our hold on
             // nsIAppStartup and let it continue to quit.
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -232,22 +232,22 @@ public:
         : MutexAutoLock(aPtr.mImplLock)
         , mImpl(aPtr.mImpl)
     {}
 
     operator Impl*() const { return mImpl; }
     Impl* operator->() const { return mImpl; }
 };
 
-
 class nsWindow::GeckoViewSupport final
     : public GeckoView::Window::Natives<GeckoViewSupport>
     , public SupportsWeakPtr<GeckoViewSupport>
 {
     nsWindow& window;
+    GeckoView::GlobalRef mView;
 
 public:
     typedef GeckoView::Window::Natives<GeckoViewSupport> Base;
     typedef SupportsWeakPtr<GeckoViewSupport> SupportsWeakPtr;
 
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GeckoViewSupport);
 
     template<typename Functor>
@@ -263,16 +263,17 @@ public:
         nsAppShell::PostEvent(mozilla::MakeUnique<WindowEvent<Functor>>(
                 mozilla::Move(aCall)));
     }
 
     GeckoViewSupport(nsWindow* aWindow,
                      const GeckoView::Window::LocalRef& aInstance,
                      GeckoView::Param aView)
         : window(*aWindow)
+        , mView(aView)
     {
         Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
     }
 
     ~GeckoViewSupport();
 
     using Base::DisposeNative;
 
@@ -296,16 +297,18 @@ public:
     void Close();
 
     // Reattach this nsWindow to a new GeckoView.
     void Reattach(const GeckoView::Window::LocalRef& inst,
                   GeckoView::Param aView, jni::Object::Param aCompositor,
                   jni::Object::Param aDispatcher);
 
     void LoadUri(jni::String::Param aUri, int32_t aFlags);
+
+    void EnableEventDispatcher();
 };
 
 /**
  * NativePanZoomController handles its native calls on the UI thread, so make
  * it separate from GeckoViewSupport.
  */
 class nsWindow::NPZCSupport final
     : public NativePanZoomController::Natives<NPZCSupport>
@@ -1531,16 +1534,25 @@ nsWindow::RedrawAll()
 
 int64_t
 nsWindow::GetRootLayerId() const
 {
     return mCompositorSession ? mCompositorSession->RootLayerTreeId() : 0;
 }
 
 void
+nsWindow::EnableEventDispatcher()
+{
+    if (!mGeckoViewSupport) {
+        return;
+    }
+    mGeckoViewSupport->EnableEventDispatcher();
+}
+
+void
 nsWindow::SetParent(nsIWidget *aNewParent)
 {
     if ((nsIWidget*)mParent == aNewParent)
         return;
 
     // If we had a parent before, remove ourselves from its list of
     // children.
     if (mParent)
@@ -2057,16 +2069,23 @@ nsWindow::GetEventTimeStamp(int64_t aEve
     // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
     //  too. Due to same implementation, we can use this via FromSystemTime.
     int64_t tick =
         BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
     return TimeStamp::FromSystemTime(tick);
 }
 
 void
+nsWindow::GeckoViewSupport::EnableEventDispatcher()
+{
+    MOZ_ASSERT(mView);
+    mView->SetState(GeckoView::State::READY());
+}
+
+void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   if (mIdleService) {
     mIdleService->ResetIdleTimeOut(0);
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -45,16 +45,17 @@ public:
     using nsBaseWidget::GetLayerManager;
 
     nsWindow();
 
     NS_DECL_ISUPPORTS_INHERITED
 
     static void InitNatives();
     void SetScreenId(uint32_t aScreenId) { mScreenId = aScreenId; }
+    void EnableEventDispatcher();
 
 private:
     uint32_t mScreenId;
 
     // An Event subclass that guards against stale events.
     template<typename Lambda,
              bool IsStatic = Lambda::isStatic,
              typename InstanceType = typename Lambda::ThisArgType,
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -33,34 +33,16 @@
 #include "nsPipe.h"
 #include "nsScriptableBase64Encoder.h"
 
 #include "nsMemoryImpl.h"
 #include "nsDebugImpl.h"
 #include "nsTraceRefcnt.h"
 #include "nsErrorService.h"
 
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-#include "nsSupportsArray.h"
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-
 #include "nsArray.h"
 #include "nsINIParserImpl.h"
 #include "nsSupportsPrimitives.h"
 #include "nsConsoleService.h"
 
 #include "nsComponentManager.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsIServiceManager.h"
--- a/xpcom/build/XPCOMModule.inc
+++ b/xpcom/build/XPCOMModule.inc
@@ -11,17 +11,16 @@
     COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor)
     COMPONENT(SCRIPTABLEBASE64ENCODER, nsScriptableBase64EncoderConstructor)
     COMPONENT(PIPE, nsPipeConstructor)
 
     COMPONENT(PROPERTIES, nsPropertiesConstructor)
 
     COMPONENT(PERSISTENTPROPERTIES, nsPersistentProperties::Create)
 
-    COMPONENT(SUPPORTSARRAY, nsSupportsArray::Create)
     COMPONENT(ARRAY, nsArrayBase::XPCOMConstructor)
     COMPONENT(CONSOLESERVICE, nsConsoleServiceConstructor)
     COMPONENT(ATOMSERVICE, nsAtomServiceConstructor)
     COMPONENT_M(OBSERVERSERVICE, nsObserverService::Create, Module::ALLOW_IN_GPU_PROCESS)
 
     COMPONENT_M(TIMER, nsTimerConstructor, Module::ALLOW_IN_GPU_PROCESS)
 
 #define COMPONENT_SUPPORTS(TYPE, Type)                                         \
--- a/xpcom/ds/moz.build
+++ b/xpcom/ds/moz.build
@@ -4,32 +4,29 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIArray.idl',
     'nsIArrayExtensions.idl',
     'nsIAtom.idl',
     'nsIAtomService.idl',
-    'nsICollection.idl',
-    'nsIEnumerator.idl',
     'nsIHashable.idl',
     'nsIINIParser.idl',
     'nsIMutableArray.idl',
     'nsIObserver.idl',
     'nsIObserverService.idl',
     'nsIPersistentProperties2.idl',
     'nsIProperties.idl',
     'nsIProperty.idl',
     'nsIPropertyBag.idl',
     'nsIPropertyBag2.idl',
     'nsISerializable.idl',
     'nsISimpleEnumerator.idl',
     'nsIStringEnumerator.idl',
-    'nsISupportsArray.idl',
     'nsISupportsIterators.idl',
     'nsISupportsPrimitives.idl',
     'nsIVariant.idl',
     'nsIWritablePropertyBag.idl',
     'nsIWritablePropertyBag2.idl',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
@@ -64,17 +61,16 @@ EXPORTS += [
     'nsJSThingHashtable.h',
     'nsMathUtils.h',
     'nsPointerHashKeys.h',
     'nsQuickSort.h',
     'nsRefPtrHashtable.h',
     'nsStaticAtom.h',
     'nsStaticNameTable.h',
     'nsStringEnumerator.h',
-    'nsSupportsArray.h',
     'nsSupportsPrimitives.h',
     'nsTArray-inl.h',
     'nsTArray.h',
     'nsTArrayForwardDeclare.h',
     'nsTHashtable.h',
     'nsTObserverArray.h',
     'nsTPriorityQueue.h',
     'nsVariant.h',
@@ -103,18 +99,16 @@ UNIFIED_SOURCES += [
     'nsEnumeratorUtils.cpp',
     'nsHashPropertyBag.cpp',
     'nsINIParserImpl.cpp',
     'nsObserverList.cpp',
     'nsObserverService.cpp',
     'nsProperties.cpp',
     'nsQuickSort.cpp',
     'nsStringEnumerator.cpp',
-    'nsSupportsArray.cpp',
-    'nsSupportsArrayEnumerator.cpp',
     'nsSupportsPrimitives.cpp',
     'nsTArray.cpp',
     'nsTObserverArray.cpp',
     'nsVariant.cpp',
     'PLDHashTable.cpp',
     'Tokenizer.cpp',
 ]
 
deleted file mode 100644
--- a/xpcom/ds/nsICollection.idl
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "nsISerializable.idl"
-
-interface nsIEnumerator;
-
-[deprecated, scriptable, uuid(83b6019c-cbc4-11d2-8cca-0060b0fc14a3)]
-interface nsICollection : nsISerializable
-{
-
-  uint32_t Count();
-  nsISupports GetElementAt(in uint32_t index);
-  void QueryElementAt(in uint32_t index, in nsIIDRef uuid, 
-                      [iid_is(uuid),retval] out nsQIResult result);
-  void SetElementAt(in uint32_t index, in nsISupports item);
-  void AppendElement(in nsISupports item);
-  void RemoveElement(in nsISupports item);
-
-  /**
-   * This clashes with |nsISimpleEnumerator nsIArray.enumerate()| (only on the
-   * binary side), so it is renamed with a 'Deprecated' prefix in favor of the
-   * non-deprecated |nsIArray.enumerate|.
-   */
-  [binaryname(DeprecatedEnumerate)]
-  nsIEnumerator Enumerate();
-
-  void Clear();
-
-};
-
-%{C++
-
-#ifndef nsCOMPtr_h__
-#include "nsCOMPtr.h"
-#endif
-
-class MOZ_STACK_CLASS nsQueryElementAt : public nsCOMPtr_helper
-  {
-    public:
-      nsQueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr )
-          : mCollection(aCollection),
-            mIndex(aIndex),
-            mErrorPtr(aErrorPtr)
-        {
-          // nothing else to do here
-        }
-
-      virtual nsresult NS_FASTCALL operator()( const nsIID& aIID, void** )
-        const override;
-
-    private:
-      nsICollection* MOZ_NON_OWNING_REF mCollection;
-      uint32_t        mIndex;
-      nsresult*       mErrorPtr;
-  };
-
-inline
-const nsQueryElementAt
-do_QueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr = 0 )
-  {
-    return nsQueryElementAt(aCollection, aIndex, aErrorPtr);
-  }
-
-%}
deleted file mode 100644
--- a/xpcom/ds/nsIEnumerator.idl
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "nsISupports.idl"
-
-%{C++
-#define NS_ENUMERATOR_FALSE 1
-%}
-/*
- * DO NOT USE THIS INTERFACE.  IT IS HORRIBLY BROKEN, USES NS_COMFALSE
- * AND IS BASICALLY IMPOSSIBLE TO USE CORRECTLY THROUGH PROXIES OR
- * XPCONNECT.  IF YOU SEE NEW USES OF THIS INTERFACE IN CODE YOU ARE
- * REVIEWING, YOU SHOULD INSIST ON nsISimpleEnumerator.
- *
- * DON'T MAKE ME COME OVER THERE.
- */
-[deprecated, scriptable, uuid(ad385286-cbc4-11d2-8cca-0060b0fc14a3)]
-interface nsIEnumerator : nsISupports {
-  /** First will reset the list. will return NS_FAILED if no items
-   */
-  void first();
-
-  /** Next will advance the list. will return failed if already at end
-   */
-  void next();
-
-  /** CurrentItem will return the CurrentItem item it will fail if the 
-   *  list is empty
-   */
-  nsISupports currentItem();
-
-  /** return if the collection is at the end.  that is the beginning following 
-   *  a call to Prev and it is the end of the list following a call to next
-   */
-  void isDone();
-};
-
-[deprecated, uuid(75f158a0-cadd-11d2-8cca-0060b0fc14a3)]
-interface nsIBidirectionalEnumerator : nsIEnumerator {
-
-  /** Last will reset the list to the end. will return NS_FAILED if no items
-   */
-  void last();
-
-  /** Prev will decrement the list. will return failed if already at beginning
-   */
-  void prev();
-};
deleted file mode 100644
--- a/xpcom/ds/nsISupportsArray.idl
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "nsICollection.idl"
-
-/*
- * This entire interface is deprecated and should not be used.
- * See nsIArray and nsIMutableArray for the new implementations.
- *
- * http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=3D779491.3050506%40netscape.com&rnum=2
- * http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=al8412%245ab2%40ripley.netscape.com&rnum=8
- */
-
-%{C++
-
-class nsIBidirectionalEnumerator;
-class nsISupportsArray;
- 
-#define NS_SUPPORTSARRAY_CID                         \
-{ /* bda17d50-0d6b-11d3-9331-00104ba0fd40 */         \
-    0xbda17d50,                                      \
-    0x0d6b,                                          \
-    0x11d3,                                          \
-    {0x93, 0x31, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
-}
-#define NS_SUPPORTSARRAY_CONTRACTID "@mozilla.org/supports-array;1"
- 
-%}
-
-[deprecated, scriptable, uuid(241addc8-3608-4e73-8083-2fd6fa09eba2)]
-interface nsISupportsArray : nsICollection {
-
-  [notxpcom] long IndexOf([const] in nsISupports aPossibleElement);
-  
-  // xpcom-compatible versions
-  long GetIndexOf(in nsISupports aPossibleElement);
-  
-  [notxpcom] boolean InsertElementAt(in nsISupports aElement,
-                                     in unsigned long aIndex);
-  [notxpcom] boolean ReplaceElementAt(in nsISupports aElement,
-                                      in unsigned long aIndex);
-
-  [notxpcom] boolean RemoveElementAt(in unsigned long aIndex);
-  
-  // xpcom-compatible versions
-  void DeleteElementAt(in unsigned long aIndex);
-  
-  nsISupportsArray clone();
-};
-
-%{C++
-
-// Construct and return a default implementation of nsISupportsArray:
-extern MOZ_MUST_USE nsresult
-NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult);
-
-%}
deleted file mode 100644
--- a/xpcom/ds/nsSupportsArray.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 <stdint.h>
-#include <string.h>
-
-#include "nsArrayEnumerator.h"
-#include "nsIObjectInputStream.h"
-#include "nsIObjectOutputStream.h"
-#include "nsSupportsArray.h"
-#include "nsSupportsArrayEnumerator.h"
-
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-nsresult
-nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const
-{
-  nsresult status =
-    mCollection ? mCollection->QueryElementAt(mIndex, aIID, aResult) :
-                  NS_ERROR_NULL_POINTER;
-
-  if (mErrorPtr) {
-    *mErrorPtr = status;
-  }
-
-  return status;
-}
-
-nsSupportsArray::nsSupportsArray()
-{
-}
-
-nsSupportsArray::~nsSupportsArray()
-{
-  Clear();
-}
-
-nsresult
-nsSupportsArray::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
-{
-  if (aOuter) {
-    return NS_ERROR_NO_AGGREGATION;
-  }
-
-  nsCOMPtr<nsISupportsArray> it = new nsSupportsArray();
-
-  return it->QueryInterface(aIID, aResult);
-}
-
-NS_IMPL_ISUPPORTS(nsSupportsArray, nsIArray, nsISupportsArray, nsICollection,
-                  nsISerializable)
-
-NS_IMETHODIMP
-nsSupportsArray::Read(nsIObjectInputStream* aStream)
-{
-  nsresult rv;
-
-  uint32_t newArraySize;
-  rv = aStream->Read32(&newArraySize);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  uint32_t count;
-  rv = aStream->Read32(&count);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  NS_ASSERTION(count <= newArraySize, "overlarge mCount!");
-  if (count > newArraySize) {
-    count = newArraySize;
-  }
-
-  // Don't clear out our array until we know we have enough space for the new
-  // one and have successfully copied everything out of the stream.
-  nsCOMArray<nsISupports> tmp;
-  tmp.SetCapacity(newArraySize);
-  tmp.SetCount(count);
-
-  auto elems = tmp.Elements();
-  for (uint32_t i = 0; i < count; i++) {
-    rv = aStream->ReadObject(true, &elems[i]);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
-
-  // Now clear out existing refs and replace with the new array.
-  mArray.Clear();
-  mArray.SwapElements(tmp);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::Write(nsIObjectOutputStream* aStream)
-{
-  nsresult rv;
-
-  rv = aStream->Write32(mArray.Capacity());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = aStream->Write32(mArray.Length());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  for (size_t i = 0; i < mArray.Length(); i++) {
-    rv = aStream->WriteObject(mArray[i], true);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports** aOutPtr)
-{
-  nsCOMPtr<nsISupports> elm = mArray.SafeElementAt(aIndex);
-  elm.forget(aOutPtr);
-  return NS_OK;
-}
-
-NS_IMETHODIMP_(int32_t)
-nsSupportsArray::IndexOf(const nsISupports* aPossibleElement)
-{
-  // nsCOMArray takes a non-const param, but it just passes through to
-  // nsTArray which takes a const param.
-  return mArray.IndexOf(const_cast<nsISupports*>(aPossibleElement));
-}
-
-NS_IMETHODIMP_(bool)
-nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex)
-{
-  return mArray.InsertObjectAt(aElement, aIndex);
-}
-
-NS_IMETHODIMP_(bool)
-nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex)
-{
-  // nsCOMArray::ReplaceObjectAt will grow the array if necessary. Instead
-  // we do the bounds check and only replace if it's in range.
-  if (aIndex < mArray.Length()) {
-    mArray.ReplaceElementAt(aIndex, aElement);
-    return true;
-  }
-  return false;
-}
-
-NS_IMETHODIMP_(bool)
-nsSupportsArray::RemoveElementAt(uint32_t aIndex)
-{
-  return mArray.RemoveObjectAt(aIndex);
-}
-
-NS_IMETHODIMP
-nsSupportsArray::RemoveElement(nsISupports* aElement)
-{
-  return mArray.RemoveObject(aElement) ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::Clear(void)
-{
-  mArray.Clear();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::DeprecatedEnumerate(nsIEnumerator** aResult)
-{
-  RefPtr<nsSupportsArrayEnumerator> e = new nsSupportsArrayEnumerator(this);
-  e.forget(aResult);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::Clone(nsISupportsArray** aResult)
-{
-  nsCOMPtr<nsISupportsArray> newArray;
-  nsresult rv = NS_NewISupportsArray(getter_AddRefs(newArray));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  for (size_t i = 0; i < mArray.Length(); i++) {
-    // AppendElement does an odd cast of bool to nsresult, we just cast back
-    // here.
-    if (!(bool)newArray->AppendElement(mArray[i])) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  newArray.forget(aResult);
-  return NS_OK;
-}
-
-nsresult
-NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult)
-{
-  nsresult rv;
-  rv = nsSupportsArray::Create(nullptr, NS_GET_IID(nsISupportsArray),
-                               (void**)aInstancePtrResult);
-  return rv;
-}
-
-/**
- * nsIArray adapters.
- */
-NS_IMETHODIMP
-nsSupportsArray::GetLength(uint32_t* aLength) {
-  return Count(aLength);
-}
-
-NS_IMETHODIMP
-nsSupportsArray::QueryElementAt(uint32_t aIndex, const nsIID& aIID, void** aResult)
-{
-  nsISupports* element = mArray.SafeElementAt(aIndex);
-  if (element) {
-    return element->QueryInterface(aIID, aResult);
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::IndexOf(uint32_t aStartIndex, nsISupports* aElement, uint32_t* aResult)
-{
-  int32_t idx = mArray.IndexOf(aElement, aStartIndex);
-  if (idx < 0) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *aResult = static_cast<uint32_t>(idx);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArray::Enumerate(nsISimpleEnumerator** aResult)
-{
-  return NS_NewArrayEnumerator(aResult, this);
-}
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-
deleted file mode 100644
--- a/xpcom/ds/nsSupportsArray.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 nsSupportsArray_h__
-#define nsSupportsArray_h__
-
-#include "nsIArray.h"
-#include "nsCOMArray.h"
-#include "mozilla/Attributes.h"
-
-
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-#include "nsISupportsArray.h"
-
-class nsSupportsArray final : public nsISupportsArray,
-                              public nsIArray
-{
-  ~nsSupportsArray(void); // nonvirtual since we're not subclassed
-
-public:
-  nsSupportsArray(void);
-
-  static MOZ_MUST_USE nsresult
-  Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  NS_DECL_NSISERIALIZABLE
-
-  // nsICollection methods:
-  NS_IMETHOD Count(uint32_t* aResult) override
-  {
-    *aResult = mArray.Length();
-    return NS_OK;
-  }
-  NS_IMETHOD GetElementAt(uint32_t aIndex, nsISupports** aResult) override;
-  MOZ_MUST_USE NS_IMETHOD
-  SetElementAt(uint32_t aIndex, nsISupports* aValue) override
-  {
-    return ReplaceElementAt(aValue, aIndex) ? NS_OK : NS_ERROR_FAILURE;
-  }
-  MOZ_MUST_USE NS_IMETHOD AppendElement(nsISupports* aElement) override
-  {
-    // XXX Invalid cast of bool to nsresult (bug 778110)
-    return (nsresult)InsertElementAt(aElement, mArray.Length())/* ? NS_OK : NS_ERROR_FAILURE*/;
-  }
-  // XXX this is badly named - should be RemoveFirstElement
-  MOZ_MUST_USE NS_IMETHOD RemoveElement(nsISupports* aElement) override;
-  NS_IMETHOD DeprecatedEnumerate(nsIEnumerator** aResult) override;
-  NS_IMETHOD Clear(void) override;
-
-  // nsISupportsArray methods:
-  NS_IMETHOD_(int32_t) IndexOf(const nsISupports* aPossibleElement) override;
-
-  NS_IMETHOD GetIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override
-  {
-    *aResult = IndexOf(aPossibleElement);
-    return NS_OK;
-  }
-
-  MOZ_MUST_USE NS_IMETHOD_(bool)
-  InsertElementAt(nsISupports* aElement, uint32_t aIndex) override;
-
-  MOZ_MUST_USE NS_IMETHOD_(bool)
-  ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) override;
-
-  MOZ_MUST_USE NS_IMETHOD_(bool)
-  RemoveElementAt(uint32_t aIndex) override;
-
-  MOZ_MUST_USE NS_IMETHOD DeleteElementAt(uint32_t aIndex) override
-  {
-    return (RemoveElementAt(aIndex) ? NS_OK : NS_ERROR_FAILURE);
-  }
-
-  MOZ_MUST_USE NS_IMETHOD Clone(nsISupportsArray** aResult) override;
-
-  /**
-   * nsIArray adapters.
-   */
-  NS_DECL_NSIARRAY
-
-private:
-  // Copy constructors are not allowed
-  explicit nsSupportsArray(const nsISupportsArray& aOther);
-
-  nsCOMArray<nsISupports> mArray;
-};
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-
-#endif // nsSupportsArray_h__
deleted file mode 100644
--- a/xpcom/ds/nsSupportsArrayEnumerator.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 "nsSupportsArrayEnumerator.h"
-
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-#include "nsISupportsArray.h"
-
-nsSupportsArrayEnumerator::nsSupportsArrayEnumerator(nsISupportsArray* array)
-  : mArray(array)
-  , mCursor(0)
-{
-  NS_ASSERTION(array, "null array");
-}
-
-nsSupportsArrayEnumerator::~nsSupportsArrayEnumerator()
-{
-}
-
-NS_IMPL_ISUPPORTS(nsSupportsArrayEnumerator, nsIBidirectionalEnumerator,
-                  nsIEnumerator)
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::First()
-{
-  mCursor = 0;
-  uint32_t cnt;
-  nsresult rv = mArray->Count(&cnt);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  int32_t end = (int32_t)cnt;
-  if (mCursor < end) {
-    return NS_OK;
-  } else {
-    return NS_ERROR_FAILURE;
-  }
-}
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::Next()
-{
-  uint32_t cnt;
-  nsresult rv = mArray->Count(&cnt);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  int32_t end = (int32_t)cnt;
-  if (mCursor < end) { // don't count upward forever
-    mCursor++;
-  }
-  if (mCursor < end) {
-    return NS_OK;
-  } else {
-    return NS_ERROR_FAILURE;
-  }
-}
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::CurrentItem(nsISupports** aItem)
-{
-  NS_ASSERTION(aItem, "null out parameter");
-  uint32_t cnt;
-  nsresult rv = mArray->Count(&cnt);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (mCursor >= 0 && mCursor < (int32_t)cnt) {
-    return mArray->GetElementAt(mCursor, aItem);
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::IsDone()
-{
-  uint32_t cnt;
-  nsresult rv = mArray->Count(&cnt);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  // XXX This is completely incompatible with the meaning of nsresult.
-  // NS_ENUMERATOR_FALSE is defined to be 1.  (bug 778111)
-  return (mCursor >= 0 && mCursor < (int32_t)cnt)
-    ? (nsresult)NS_ENUMERATOR_FALSE : NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::Last()
-{
-  uint32_t cnt;
-  nsresult rv = mArray->Count(&cnt);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  mCursor = cnt - 1;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSupportsArrayEnumerator::Prev()
-{
-  if (mCursor >= 0) {
-    --mCursor;
-  }
-  if (mCursor >= 0) {
-    return NS_OK;
-  } else {
-    return NS_ERROR_FAILURE;
-  }
-}
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
deleted file mode 100644
--- a/xpcom/ds/nsSupportsArrayEnumerator.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 nsSupportsArrayEnumerator_h___
-#define nsSupportsArrayEnumerator_h___
-
-#include "nsCOMPtr.h"
-#include "mozilla/Attributes.h"
-
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-#include "nsIEnumerator.h"
-
-class nsISupportsArray;
-
-class nsSupportsArrayEnumerator final : public nsIBidirectionalEnumerator
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit nsSupportsArrayEnumerator(nsISupportsArray* aArray);
-
-  // nsIEnumerator methods:
-  NS_DECL_NSIENUMERATOR
-
-  // nsIBidirectionalEnumerator methods:
-  NS_DECL_NSIBIDIRECTIONALENUMERATOR
-
-private:
-  ~nsSupportsArrayEnumerator();
-
-protected:
-  nsCOMPtr<nsISupportsArray> mArray;
-  int32_t               mCursor;
-
-};
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-
-#endif // __nsSupportsArrayEnumerator_h
-
deleted file mode 100644
--- a/xpcom/tests/gtest/TestArray.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 <stdio.h>
-#include <stdlib.h>
-#include "gtest/gtest.h"
-
-// Disable deprecation warnings generated by nsISupportsArray and associated
-// classes.
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(_MSC_VER)
-#pragma warning (push)
-#pragma warning (disable : 4996)
-#endif
-
-#include "nsISupportsArray.h"
-
-// {9e70a320-be02-11d1-8031-006008159b5a}
-#define NS_IFOO_IID \
-  {0x9e70a320, 0xbe02, 0x11d1,    \
-    {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
-
-namespace TestArray {
-
-class IFoo : public nsISupports {
-public:
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
-
-  NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
-  NS_IMETHOD_(int32_t) ID() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
-
-class Foo final : public IFoo {
-public:
-
-  explicit Foo(int32_t aID);
-
-  // nsISupports implementation
-  NS_DECL_ISUPPORTS
-
-  // IFoo implementation
-  NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
-  NS_IMETHOD_(int32_t) ID() override { return mID; }
-
-  static int32_t gCount;
-
-  int32_t mID;
-
-private:
-  ~Foo();
-};
-
-int32_t Foo::gCount;
-
-Foo::Foo(int32_t aID)
-{
-  mID = aID;
-  ++gCount;
-}
-
-Foo::~Foo()
-{
-  --gCount;
-}
-
-NS_IMPL_ISUPPORTS(Foo, IFoo)
-
-void CheckArray(nsISupportsArray* aArray, int32_t aExpectedCount, int32_t aElementIDs[], int32_t aExpectedTotal)
-{
-  uint32_t cnt = 0;
-#ifdef DEBUG
-  nsresult rv =
-#endif
-    aArray->Count(&cnt);
-  NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
-  int32_t count = cnt;
-  int32_t index;
-
-  EXPECT_EQ(Foo::gCount, aExpectedTotal);
-  EXPECT_EQ(count, aExpectedCount);
-
-  for (index = 0; (index < count) && (index < aExpectedCount); index++) {
-    nsCOMPtr<IFoo> foo = do_QueryElementAt(aArray, index);
-    EXPECT_EQ(foo->ID(), aElementIDs[index]);
-  }
-}
-
-void FillArray(nsISupportsArray* aArray, int32_t aCount)
-{
-  int32_t index;
-  for (index = 0; index < aCount; index++) {
-    nsCOMPtr<IFoo> foo = new Foo(index);
-    aArray->AppendElement(foo);
-  }
-}
-
-} // namespace TestArray
-
-using namespace TestArray;
-
-TEST(Array, main)
-{
-  nsISupportsArray* array;
-  nsresult  rv;
-
-  if (NS_OK == (rv = NS_NewISupportsArray(&array))) {
-    FillArray(array, 10);
-    int32_t   fillResult[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    CheckArray(array, 10, fillResult, 10);
-
-    // test insert
-    nsCOMPtr<IFoo> foo = do_QueryElementAt(array, 3);
-    array->InsertElementAt(foo, 5);
-    int32_t   insertResult[11] = {0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
-    CheckArray(array, 11, insertResult, 10);
-    array->InsertElementAt(foo, 0);
-    int32_t   insertResult2[12] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
-    CheckArray(array, 12, insertResult2, 10);
-    array->AppendElement(foo);
-    int32_t   appendResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9, 3};
-    CheckArray(array, 13, appendResult, 10);
-
-
-    // test IndexOf
-    int32_t expectedIndex = 0;
-    int32_t index = array->IndexOf(foo);
-    EXPECT_EQ(index, expectedIndex);
-
-    // test ReplaceElementAt
-    array->ReplaceElementAt(foo, 8);
-    int32_t   replaceResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
-    CheckArray(array, 13, replaceResult, 9);
-
-    // test RemoveElementAt, RemoveElement
-    array->RemoveElementAt(0);
-    int32_t   removeResult[12] = {0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
-    CheckArray(array, 12, removeResult, 9);
-    array->RemoveElementAt(7);
-    int32_t   removeResult2[11] = {0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 3};
-    CheckArray(array, 11, removeResult2, 9);
-    array->RemoveElement(foo);
-    int32_t   removeResult3[10] = {0, 1, 2, 4, 3, 5, 7, 8, 9, 3};
-    CheckArray(array, 10, removeResult3, 9);
-
-    foo = nullptr;
-
-    // test clear
-    array->Clear();
-    FillArray(array, 4);
-    CheckArray(array, 4, fillResult, 4);
-
-    // test delete
-    NS_RELEASE(array);
-  }
-}
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning (pop)
-#endif
--- a/xpcom/tests/gtest/TestNsDeque.cpp
+++ b/xpcom/tests/gtest/TestNsDeque.cpp
@@ -4,16 +4,20 @@
  * 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 "gtest/gtest.h"
 #include "nsDeque.h"
 #include "nsCRT.h"
 #include "mozilla/TypeTraits.h"
 #include <stdio.h>
+#include <functional>
+
+using mozilla::IsSame;
+using mozilla::DeclVal;
 
 /**************************************************************
   Now define the token deallocator class...
  **************************************************************/
 namespace TestNsDeque {
 
   class _Dealloc: public nsDequeFunctor
   {
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -1,17 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
     'Helpers.cpp',
-    'TestArray.cpp',
     'TestAtoms.cpp',
     'TestAutoPtr.cpp',
     'TestAutoRef.cpp',
     'TestBase64.cpp',
     'TestCallTemplates.cpp',
     'TestCloneInputStream.cpp',
     'TestCOMPtrEq.cpp',
     'TestCRT.cpp',
--- a/xpcom/tests/unit/test_nsIMutableArray.js
+++ b/xpcom/tests/unit/test_nsIMutableArray.js
@@ -102,55 +102,16 @@ function test_enumerate()
     let str = en.getNext();
     do_check_true(str instanceof Ci.nsISupportsString);
     do_check_eq(str.data, "element " + i);
     i++;
   }
   do_check_eq(arr.length, i);
 }
 
-function test_nssupportsarray_interop() {
-  // Tests to check that an nsSupportsArray instance can behave like an
-  // nsIArray.
-  let test = Components.classes["@mozilla.org/supports-array;1"]
-             .createInstance(Ci.nsISupportsArray);
-
-  let str = new SupportsString();
-  str.data = "element";
-  test.AppendElement(str);
-
-  // Now query to an nsIArray.
-  let iarray = test.QueryInterface(Ci.nsIArray);
-  do_check_neq(iarray, null);
-
-  // Make sure |nsIArray.length| works.
-  do_check_eq(iarray.length, 1);
-
-  // Make sure |nsIArray.queryElementAt| works.
-  let elm = iarray.queryElementAt(0, Ci.nsISupportsString);
-  do_check_eq(elm.data, "element");
-
-  // Make sure |nsIArray.indexOf| works.
-  let idx = iarray.indexOf(0, str);
-  do_check_eq(idx, 0);
-
-  // Make sure |nsIArray.enumerate| works.
-  let en = iarray.enumerate();
-  do_check_neq(en, null);
-  let i = 0;
-  while (en.hasMoreElements()) {
-    let str = en.getNext();
-    do_check_true(str instanceof Ci.nsISupportsString);
-    do_check_eq(str.data, "element");
-    i++;
-  }
-
-  do_check_eq(iarray.length, i);
-}
-
 function test_nsiarrayextensions() {
   // Tests to check that the extensions that make an nsArray act like an
   // nsISupportsArray for iteration purposes works.
   // Note: we do not want to QI here, just want to make sure the magic glue
   // works as a drop-in replacement.
 
   let fake_nsisupports_array = create_n_element_array(5);
 
@@ -169,16 +130,15 @@ function test_nsiarrayextensions() {
 
 var tests = [
   test_appending_null_actually_inserts,
   test_object_gets_appended,
   test_insert_at_beginning,
   test_replace_element,
   test_clear,
   test_enumerate,
-  test_nssupportsarray_interop,
   test_nsiarrayextensions,
 ];
 
 function run_test() {
   for (var i = 0; i < tests.length; i++)
     tests[i]();
 }