merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 05 Apr 2017 14:33:35 +0200
changeset 556265 3854bcf837a729374b4d345910c274fed12cffe4
parent 556255 c7e37c13ea9e0442eca9c22a1c7d450abd0d3f64 (current diff)
parent 556264 abdb79a72b8177377404ddbc6d9d06c0ffa8f07c (diff)
child 556266 166c25775ac3aac8abf4760dc03e36e27bd3010c
child 556270 dab8199bdfe67b21fb277abbb372a822af4e5f2d
child 556271 f74042696b59485c638c1aaf5608690912f5b837
child 556274 84f5d6eeaae341d2aa5cd9781a72b1f64a5a19a3
child 556279 aac1a94af9de00f43e7432de801862ad1721c037
child 556291 6fb7492b2de2ac9aa59374f0db5a2b3468e2ff74
child 556293 ec30080ed82e7835bb950b23dba2463e0ec6e227
child 556294 d47e927c1dcc19339749f3662c8c24e1c1f848d9
child 556325 973b034006402423d7e04ceceae26c8ee182c9f0
child 556334 1be490239c3f555bc6a2fdd114fa5a497c8116b2
child 556335 cf8b7f685e045a10cb7ffcd06f90d86fd8654a66
child 556342 d0c9f6b5d1c3fa16f4c025a492ced525994b3588
child 556347 abbd903025fe0d21b0a76937eca8e3f85f026082
child 556354 4991ca36d1a6404fd87ce18bb1130f27f289df45
child 556355 e24cf4c36134b1e69e20782bf260a25af67a68da
child 556371 f0a6eb8781115c7f27f45ee3bfadcd322254ea80
child 556388 7d360fe12f6d6b61ce4306c1f82b40791e46052c
child 556453 6de450a4e1aa9b5e76efdf21a3c478df32964bb6
child 556976 9ec2c72c0555a0b53871c3eac401b4ee2a0ea345
child 556983 4189dec6749dac96c6f55531860470ae3c56bdc9
child 556984 d0cf8acc05b359144d44f5c454839e8852514e5e
child 557005 876a289995a984fe6b09c1c78464514bd7235b1e
child 557272 57b66a17605545a6a727aa6eb29d77e9bc7ffb49
child 557408 e637fb9ef40bce69f42c8a9c7290d245faa29e77
child 558371 7610da714354a888bc356a832d4c7ad5d8552fd7
child 558524 edaebd1988b9ea75794c0231b9aaf2f8379a6028
child 558526 fdfccc3528f51c1c91b7c8108b5b34709fe8ddb8
child 558854 ff2a5dc648b0e0a69ac50522e9730c7e4d08862b
child 558883 bed4ac7fffe9e2c5b95803fcbc620d0ffedd3e0d
child 559173 54299d3e164b7d13bbf4b755fe87747f9d27cb59
child 560733 aa3ac55f2c32b08ce5f76b5cebf521cfb5d6c6a2
child 564197 8c65ed8eab81999937485d0ff89999c20b3a2f92
child 564200 8e40c4e9db845a608bd1c63bde797e11d5d1227e
child 564600 5f2217208b672269cc4723b381bb06833c15f0bb
child 565777 ee3682dfd564c144f640b9e8b5c7991404261837
push id52480
push userbmo:standard8@mozilla.com
push dateWed, 05 Apr 2017 14:31:04 +0000
reviewersmerge
milestone55.0a1
merge mozilla-inbound to mozilla-central a=merge
modules/libpref/init/all.js
testing/web-platform/meta/dom/events/CustomEvent.html.ini
testing/web-platform/meta/dom/events/Event-initEvent.html.ini
testing/web-platform/meta/html/webappapis/scripting/events/messageevent-constructor.https.html.ini
toolkit/components/telemetry/Histograms.json
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -784,18 +784,26 @@ pref("browser.rights.3.shown", false);
 pref("browser.rights.override", true);
 #endif
 
 pref("browser.selfsupport.url", "https://self-repair.mozilla.org/%LOCALE%/repair");
 
 pref("browser.sessionstore.resume_from_crash", true);
 pref("browser.sessionstore.resume_session_once", false);
 
-// minimal interval between two save operations in milliseconds
-pref("browser.sessionstore.interval", 15000);
+// Minimal interval between two save operations in milliseconds (while the user is active).
+pref("browser.sessionstore.interval", 15000); // 15 seconds
+
+// Minimal interval between two save operations in milliseconds (while the user is idle).
+pref("browser.sessionstore.interval.idle", 3600000); // 1h
+
+// Time (ms) before we assume that the user is idle and that we don't need to
+// collect/save the session quite as often.
+pref("browser.sessionstore.idleDelay", 180000); // 3 minutes
+
 // on which sites to save text data, POSTDATA and cookies
 // 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
 pref("browser.sessionstore.privacy_level", 0);
 // how many tabs can be reopened (per window)
 pref("browser.sessionstore.max_tabs_undo", 10);
 // how many windows can be reopened (per session) - on non-OS X platforms this
 // pref may be ignored when dealing with pop-up windows to ensure proper startup
 pref("browser.sessionstore.max_windows_undo", 3);
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -242,18 +242,16 @@ var whitelist = new Set([
   // Bug 1351089
   {file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"},
   // Bug 1351091
   {file: "resource://gre/modules/Profiler.jsm"},
   // Bug 1351658
   {file: "resource://gre/modules/PropertyListUtils.jsm", platforms: ["linux", "win"]},
   // Bug 1351093
   {file: "resource://gre/modules/Sntp.jsm"},
-  // Bug 1351980
-  {file: "resource://gre/modules/UserAgentOverrides.jsm"},
   // Bug 1351097
   {file: "resource://gre/modules/accessibility/AccessFu.jsm"},
   // Bug 1351099
   {file: "resource://gre/modules/addons/AddonLogging.jsm"},
   // Bug 1351637
   {file: "resource://gre/modules/sdk/bootstrap.js"},
   // Bug 1351657
   {file: "resource://gre/res/langGroups.properties", platforms: ["macosx"]},
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -29,17 +29,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
           LightweightThemeManager:false, LoginHelper:false, LoginManagerParent:false,
           NetUtil:false, NewTabMessages:false, NewTabUtils:false, OS:false,
           PageThumbs:false, PdfJs:false, PermissionUI:false, PlacesBackups:false,
           PlacesUtils:false, PluralForm:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, ReaderParent:false, RecentWindow:false,
           RemotePrompt:false, SelfSupportBackend:false, SessionStore:false,
           ShellService:false, SimpleServiceDiscovery:false, TabCrashHandler:false,
           Task:false, UITour:false, WebChannel:false,
-          WindowsRegistry:false, webrtcUI:false */
+          WindowsRegistry:false, webrtcUI:false, UserAgentOverrides: false */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
  */
 
 [
   ["AboutHome", "resource:///modules/AboutHome.jsm"],
@@ -84,16 +84,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
   ["ShellService", "resource:///modules/ShellService.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["WebChannel", "resource://gre/modules/WebChannel.jsm"],
   ["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
+  ["UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm"],
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                     "resource:///modules/ContentCrashHandlers.jsm");
   XPCOMUtils.defineLazyModuleGetter(this, "UnsubmittedCrashHandler",
                                     "resource:///modules/ContentCrashHandlers.jsm");
   XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
@@ -426,16 +427,18 @@ BrowserGlue.prototype = {
     this._flashHangCount = 0;
     this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
 
     if (AppConstants.platform == "macosx") {
       // Handles prompting to inform about incompatibilites when accessibility
       // and e10s are active together.
       E10SAccessibilityCheck.init();
     }
+
+    UserAgentOverrides.init();
   },
 
   // cleanup (called on application shutdown)
   _dispose: function BG__dispose() {
     let os = Services.obs;
     os.removeObserver(this, "notifications-open-settings");
     os.removeObserver(this, "prefservice:after-app-defaults");
     os.removeObserver(this, "final-ui-startup");
@@ -465,16 +468,18 @@ BrowserGlue.prototype = {
     os.removeObserver(this, "handle-xul-text-link");
     os.removeObserver(this, "profile-before-change");
     if (AppConstants.MOZ_TELEMETRY_REPORTING) {
       os.removeObserver(this, "keyword-search");
     }
     os.removeObserver(this, "browser-search-engine-modified");
     os.removeObserver(this, "flash-plugin-hang");
     os.removeObserver(this, "xpi-signature-changed");
+
+    UserAgentOverrides.uninit();
   },
 
   _onAppDefaults: function BG__onAppDefaults() {
     // apply distribution customizations (prefs)
     // other customizations are applied in _finalUIStartup()
     this._distributionCustomizer.applyPrefDefaults();
   },
 
--- a/browser/components/sessionstore/SessionSaver.jsm
+++ b/browser/components/sessionstore/SessionSaver.jsm
@@ -25,32 +25,37 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/sessionstore/RunState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
   "resource:///modules/sessionstore/SessionStore.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-// Minimal interval between two save operations (in milliseconds).
-XPCOMUtils.defineLazyGetter(this, "gInterval", function() {
-  const PREF = "browser.sessionstore.interval";
-
-  // Observer that updates the cached value when the preference changes.
-  Services.prefs.addObserver(PREF, () => {
-    this.gInterval = Services.prefs.getIntPref(PREF);
-
-    // Cancel any pending runs and call runDelayed() with
-    // zero to apply the newly configured interval.
-    SessionSaverInternal.cancel();
-    SessionSaverInternal.runDelayed(0);
-  }, false);
-
-  return Services.prefs.getIntPref(PREF);
-});
+/*
+ * Minimal interval between two save operations (in milliseconds).
+ *
+ * To save system resources, we generally do not save changes immediately when
+ * a change is detected. Rather, we wait a little to see if this change is
+ * followed by other changes, in which case only the last write is necessary.
+ * This delay is defined by "browser.sessionstore.interval".
+ *
+ * Furthermore, when the user is not actively using the computer, webpages
+ * may still perform changes that require (re)writing to sessionstore, e.g.
+ * updating Session Cookies or DOM Session Storage, or refreshing, etc. We
+ * expect that these changes are much less critical to the user and do not
+ * need to be saved as often. In such cases, we increase the delay to
+ *  "browser.sessionstore.interval.idle".
+ *
+ * When the user returns to the computer, if a save is pending, we reschedule
+ * it to happen soon, with "browser.sessionstore.interval".
+ */
+const PREF_INTERVAL_ACTIVE = "browser.sessionstore.interval";
+const PREF_INTERVAL_IDLE = "browser.sessionstore.interval.idle";
+const PREF_IDLE_DELAY = "browser.sessionstore.idleDelay";
 
 // Notify observers about a given topic with a given subject.
 function notify(subject, topic) {
   Services.obs.notifyObservers(subject, topic, "");
 }
 
 // TelemetryStopwatch helper functions.
 function stopWatch(method) {
@@ -114,16 +119,46 @@ var SessionSaverInternal = {
   /**
    * A timestamp that keeps track of when we saved the session last. We will
    * this to determine the correct interval between delayed saves to not deceed
    * the configured session write interval.
    */
   _lastSaveTime: 0,
 
   /**
+   * `true` if the user has been idle for at least
+   * `SessionSaverInternal._intervalWhileIdle` ms. Idleness is computed
+   * with `nsIIdleService`.
+   */
+  _isIdle: false,
+
+  /**
+   * `true` if the user was idle when we last scheduled a delayed save.
+   * See `_isIdle` for details on idleness.
+   */
+  _wasIdle: false,
+
+  /**
+   * Minimal interval between two save operations (in ms), while the user
+   * is active.
+   */
+  _intervalWhileActive: null,
+
+  /**
+   * Minimal interval between two save operations (in ms), while the user
+   * is idle.
+   */
+  _intervalWhileIdle: null,
+
+  /**
+   * How long before we assume that the user is idle (ms).
+   */
+  _idleDelay: null,
+
+  /**
    * Immediately saves the current session to disk.
    */
   run() {
     return this._saveState(true /* force-update all windows */);
   },
 
   /**
    * Saves the current session to disk delayed by a given amount of time. Should
@@ -136,19 +171,21 @@ var SessionSaverInternal = {
    */
   runDelayed(delay = 2000) {
     // Bail out if there's a pending run.
     if (this._timeoutID) {
       return;
     }
 
     // Interval until the next disk operation is allowed.
-    delay = Math.max(this._lastSaveTime + gInterval - Date.now(), delay, 0);
+    let interval = this._isIdle ? this._intervalWhileIdle : this._intervalWhileActive;
+    delay = Math.max(this._lastSaveTime + interval - Date.now(), delay, 0);
 
     // Schedule a state save.
+    this._wasIdle = this._isIdle;
     this._timeoutID = setTimeout(() => this._saveStateAsync(), delay);
   },
 
   /**
    * Sets the last save time to the current time. This will cause us to wait for
    * at least the configured interval when runDelayed() is called next.
    */
   updateLastSaveTime() {
@@ -159,16 +196,39 @@ var SessionSaverInternal = {
    * Cancels all pending session saves.
    */
   cancel() {
     clearTimeout(this._timeoutID);
     this._timeoutID = null;
   },
 
   /**
+   * Observe idle/ active notifications.
+   */
+  observe(subject, topic, data) {
+    switch (topic) {
+      case "idle":
+        this._isIdle = true;
+        break;
+      case "active":
+        this._isIdle = false;
+        if (this._timeoutID && this._wasIdle) {
+          // A state save has been scheduled while we were idle.
+          // Replace it by an active save.
+          clearTimeout(this._timeoutID);
+          this._timeoutID = null;
+          this.runDelayed();
+        }
+        break;
+      default:
+        throw new Error(`Unexpected change value ${topic}`);
+    }
+  },
+
+  /**
    * Saves the current session state. Collects data and writes to disk.
    *
    * @param forceUpdateAllWindows (optional)
    *        Forces us to recollect data for all windows and will bypass and
    *        update the corresponding caches.
    */
   _saveState(forceUpdateAllWindows = false) {
     // Cancel any pending timeouts.
@@ -263,8 +323,38 @@ var SessionSaverInternal = {
     // file is successfully updated, save the time stamp of the last save and
     // notify the observers.
     return SessionFile.write(state).then(() => {
       this.updateLastSaveTime();
       notify(null, "sessionstore-state-write-complete");
     }, console.error);
   },
 };
+
+XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_intervalWhileActive", PREF_INTERVAL_ACTIVE,
+  15000 /* 15 seconds */, () => {
+  // Cancel any pending runs and call runDelayed() with
+  // zero to apply the newly configured interval.
+  SessionSaverInternal.cancel();
+  SessionSaverInternal.runDelayed(0);
+});
+
+XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_intervalWhileIdle", PREF_INTERVAL_IDLE,
+  3600000 /* 1 h */);
+
+XPCOMUtils.defineLazyPreferenceGetter(SessionSaverInternal, "_idleDelay", PREF_IDLE_DELAY,
+  180000 /* 3 minutes */, (key, previous, latest) => {
+  // Update the idle observer for the new `PREF_IDLE_DELAY` value. Here we need
+  // to re-fetch the service instead of the original one in use; This is for a
+  // case that the Mock service in the unit test needs to be fetched to
+  // replace the original one.
+  var idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
+  if (previous != undefined) {
+    idleService.removeIdleObserver(SessionSaverInternal, previous);
+  }
+  if (latest != undefined) {
+    idleService.addIdleObserver(SessionSaverInternal, latest);
+  }
+});
+
+var idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
+idleService.addIdleObserver(SessionSaverInternal, SessionSaverInternal._idleDelay);
+
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -205,16 +205,17 @@ skip-if = true # Needs to be rewritten a
 [browser_694378.js]
 [browser_701377.js]
 [browser_705597.js]
 [browser_707862.js]
 [browser_739531.js]
 [browser_739805.js]
 [browser_819510_perwindowpb.js]
 skip-if = (os == 'win' && bits == 64) # Bug 1284312
+[browser_not_collect_when_idle.js]
 
 # Disabled for frequent intermittent failures
 [browser_464620_a.js]
 skip-if = true
 [browser_464620_b.js]
 skip-if = true
 
 # Disabled on OS X:
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_not_collect_when_idle.js
@@ -0,0 +1,122 @@
+/** Test for Bug 1305950 **/
+
+Cu.import("resource://testing-common/MockRegistrar.jsm", this);
+
+// The mock idle service.
+var idleService = {
+  _observers: new Set(),
+  _activity: {
+    addCalls: [],
+    removeCalls: [],
+    observerFires: [],
+  },
+
+  _reset() {
+    this._observers.clear();
+    this._activity.addCalls = [];
+    this._activity.removeCalls = [];
+    this._activity.observerFires = [];
+  },
+
+  _fireObservers(state) {
+    for (let observer of this._observers.values()) {
+      observer.observe(observer, state, null);
+      this._activity.observerFires.push(state);
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIdleService]),
+  idleTime: 19999,
+
+  addIdleObserver(observer, time) {
+    this._observers.add(observer);
+    this._activity.addCalls.push(time);
+  },
+
+  removeIdleObserver(observer, time) {
+    this._observers.delete(observer);
+    this._activity.removeCalls.push(time);
+  }
+};
+
+add_task(async function testIntervalChanges() {
+  const PREF_SS_INTERVAL = 2000;
+
+  // We speed up the interval between session saves to ensure that the test
+  // runs quickly.
+  Services.prefs.setIntPref("browser.sessionstore.interval", PREF_SS_INTERVAL);
+
+  // Increase `idleDelay` to 1 day to update the pre-registered idle observer
+  // in "real" idle service to avoid possible interference, especially for the
+  // CI server environment.
+  Services.prefs.setIntPref("browser.sessionstore.idleDelay", 86400000);
+
+  // Mock an idle service.
+  let fakeIdleService = MockRegistrar.register("@mozilla.org/widget/idleservice;1", idleService);
+  idleService._reset();
+
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.sessionstore.interval");
+    MockRegistrar.unregister(fakeIdleService);
+  });
+
+  // Hook idle/active observer to mock idle service by changing pref `idleDelay`
+  // to a whatever value, which will not be used.
+  Services.prefs.setIntPref("browser.sessionstore.idleDelay", 5000000);
+
+  // Wait a `sessionstore-state-write-complete` event from any previous
+  // scheduled state write. This is needed since the `_lastSaveTime` in
+  // runDelayed() should be set at least once, or the `_isIdle` flag will not
+  // become effective.
+  // We wait at most 30 sec which is about as long as the timeout for the
+  // regular whole test case, and we don't expect the write event comes fast
+  // enough for an immediate `waitForSaveState()` from now.
+  await new Promise(function(resolve, reject) {
+    waitForTopic("sessionstore-state-write-complete", 30 * 1000, function(isSuccessful) {
+      if (!isSuccessful) {
+        reject(new Error("Timeout: didn't get any `sessionstore-state-write-complete` event"));
+      } else {
+        resolve();
+      }
+    });
+  });
+
+  info("Got the state write event, start to test idle mode...");
+
+  // Enter the "idle mode" (raise the `_isIdle` flag) by firing idle
+  // observer of mock idle service.
+  idleService._fireObservers("idle");
+
+  // Cancel any possible state save, which is not related with this test to
+  // avoid interference.
+  SessionSaver.cancel();
+
+  let p1 = promiseSaveState();
+
+  // Schedule a state write, which is expeced to be postponed after about
+  // `browser.sessionstore.interval.idle` ms, since the idle flag was just set.
+  SessionSaver.runDelayed(0);
+
+  // We expect `p1` hits the timeout.
+  await Assert.rejects(p1, null, "[Test 1A] No state write during idle.");
+
+  // Test again for better reliability. Same, we expect following promise hits
+  // the timeout.
+  await Assert.rejects(promiseSaveState(), null, "[Test 1B] Again: No state write during idle.");
+
+  // Back to the active mode.
+  info("Start to test active mode...");
+  idleService._fireObservers("active");
+
+  await new Promise(function(resolve, reject) {
+    waitForTopic("sessionstore-state-write-complete", PREF_SS_INTERVAL + 10000, function(isSuccessful) {
+      if (!isSuccessful) {
+        reject(new Error("Timeout: didn't get any `sessionstore-state-write-complete` event"));
+      } else {
+        resolve();
+      }
+    });
+  });
+  info("[Test 2] Has state write during active.");
+});
+
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -242,23 +242,23 @@ function waitForTopic(aTopic, aTimeout, 
  * otherwise, it is passed |false|.
  */
 function waitForSaveState(aCallback) {
   let timeout = 100 +
     Services.prefs.getIntPref("browser.sessionstore.interval");
   return waitForTopic("sessionstore-state-write-complete", timeout, aCallback);
 }
 function promiseSaveState() {
-  return new Promise(resolve => {
+  return new Promise((resolve, reject) => {
     waitForSaveState(isSuccessful => {
       if (!isSuccessful) {
-        throw new Error("timeout");
+        reject(new Error("Save state timeout"));
+      } else {
+        resolve();
       }
-
-      resolve();
     });
   });
 }
 function forceSaveState() {
   return SessionSaver.run();
 }
 
 function promiseRecoveryFileContents() {
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -428,10 +428,11 @@ function enableServiceWorkerDebugging() 
       // Enable service workers.
       ["dom.serviceWorkers.enabled", true],
       // Accept workers from mochitest's http.
       ["dom.serviceWorkers.testing.enabled", true],
       // Force single content process.
       ["dom.ipc.processCount", 1],
     ]};
     SpecialPowers.pushPrefEnv(options, done);
+    Services.ppmm.releaseCachedProcesses();
   });
 }
--- a/docshell/shistory/nsSHEntryShared.h
+++ b/docshell/shistory/nsSHEntryShared.h
@@ -41,24 +41,24 @@ public:
   static void Shutdown();
 
   nsSHEntryShared();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMUTATIONOBSERVER
   NS_DECL_NSIBFCACHEENTRY
 
+  nsExpirationState *GetExpirationState() { return &mExpirationState; }
+
 private:
   ~nsSHEntryShared();
 
   friend class nsSHEntry;
 
   friend class HistoryTracker;
-  friend class nsExpirationTracker<nsSHEntryShared, 3>;
-  nsExpirationState *GetExpirationState() { return &mExpirationState; }
 
   static already_AddRefed<nsSHEntryShared> Duplicate(nsSHEntryShared* aEntry);
 
   void RemoveFromExpirationTracker();
   void Expire();
   nsresult SyncPresentationState();
   void DropPresentationState();
 
--- a/dom/base/nsContentSink.h
+++ b/dom/base/nsContentSink.h
@@ -51,21 +51,21 @@ extern mozilla::LazyLogModule gContentSi
 
 #define SINK_TRACE_CALLS              0x1
 #define SINK_TRACE_REFLOW             0x2
 #define SINK_ALWAYS_REFLOW            0x4
 
 #define SINK_LOG_TEST(_lm, _bit) (int((_lm)->Level()) & (_bit))
 
 #define SINK_TRACE(_lm, _bit, _args) \
-  PR_BEGIN_MACRO                     \
+  do {                     \
     if (SINK_LOG_TEST(_lm, _bit)) {  \
-      PR_LogPrint _args;             \
+      printf_stderr _args;             \
     }                                \
-  PR_END_MACRO
+  } while(0)
 
 #else
 #define SINK_TRACE(_lm, _bit, _args)
 #endif
 
 #undef SINK_NO_INCREMENTAL
 
 //----------------------------------------------------------------------
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -310,17 +310,19 @@ nsDOMWindowUtils::Redraw(uint32_t aCount
 
     if (rootFrame) {
       PRIntervalTime iStart = PR_IntervalNow();
 
       for (uint32_t i = 0; i < aCount; i++)
         rootFrame->InvalidateFrame();
 
 #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK)
-      XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), False);
+      if (!gfxPlatform::IsHeadless()) {
+        XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), False);
+      }
 #endif
 
       *aDurationOut = PR_IntervalToMilliseconds(PR_IntervalNow() - iStart);
 
       return NS_OK;
     }
   }
   return NS_ERROR_FAILURE;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1198,18 +1198,19 @@ nsDOMStyleSheetSetList::EnsureFresh()
     sheet->GetTitle(title);
     if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
       return;
     }
   }
 }
 
 // ==================================================================
-nsIDocument::SelectorCache::SelectorCache()
-  : nsExpirationTracker<SelectorCacheKey, 4>(1000, "nsIDocument::SelectorCache")
+nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
+  : nsExpirationTracker<SelectorCacheKey, 4>(
+      1000, "nsIDocument::SelectorCache", aEventTarget)
 { }
 
 nsIDocument::SelectorCache::~SelectorCache()
 {
   AgeAllGenerations();
 }
 
 // CacheList takes ownership of aSelectorList.
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1063,17 +1063,17 @@ private:
 
   class SelectorCacheKeyDeleter;
 
 public:
   class SelectorCache final
     : public nsExpirationTracker<SelectorCacheKey, 4>
   {
     public:
-      SelectorCache();
+      explicit SelectorCache(nsIEventTarget* aEventTarget);
 
       // CacheList takes ownership of aSelectorList.
       void CacheList(const nsAString& aSelector, nsCSSSelectorList* aSelectorList);
 
       virtual void NotifyExpired(SelectorCacheKey* aSelector) override;
 
       // We do not call MarkUsed because it would just slow down lookups and
       // because we're OK expiring things after a few seconds even if they're
@@ -1086,19 +1086,22 @@ public:
       }
 
       ~SelectorCache();
 
     private:
       nsClassHashtable<nsStringHashKey, nsCSSSelectorList> mTable;
   };
 
-  SelectorCache& GetSelectorCache()
-  {
-    return mSelectorCache;
+  SelectorCache& GetSelectorCache() {
+    if (!mSelectorCache) {
+      mSelectorCache =
+        new SelectorCache(EventTargetFor(mozilla::TaskCategory::Other));
+    }
+    return *mSelectorCache;
   }
   // Get the root <html> element, or return null if there isn't one (e.g.
   // if the root isn't <html>)
   Element* GetHtmlElement() const;
   // Returns the first child of GetHtmlContent which has the given tag,
   // or nullptr if that doesn't exist.
   Element* GetHtmlChildElement(nsIAtom* aTag);
   // Get the canonical <body> element, or return null if there isn't one (e.g.
@@ -2911,17 +2914,18 @@ protected:
   bool GetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mChildDocumentUseCounters[aUseCounter];
   }
 
 private:
   mutable std::bitset<eDeprecatedOperationCount> mDeprecationWarnedAbout;
   mutable std::bitset<eDocumentWarningCount> mDocWarningWarnedAbout;
-  SelectorCache mSelectorCache;
+  // Lazy-initialization to have mDocGroup initialized in prior to mSelectorCache.
+  nsAutoPtr<SelectorCache> mSelectorCache;
 
 protected:
   ~nsIDocument();
   nsPropertyTable* GetExtraPropertyTable(uint16_t aCategory);
 
   // Never ever call this. Only call GetWindow!
   virtual nsPIDOMWindowOuter* GetWindowInternal() const = 0;
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2220,35 +2220,57 @@ ScopedLazyBind::UnwrapImpl()
 {
     if (mTarget) {
         mGL->fBindBuffer(mTarget, 0);
     }
 }
 
 ////////////////////////////////////////
 
-void
-Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
-          uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
-          uint32_t* const out_intSize)
+bool
+Intersect(const int32_t srcSize, const int32_t read0, const int32_t readSize,
+          int32_t* const out_intRead0, int32_t* const out_intWrite0,
+          int32_t* const out_intSize)
 {
-    // Only >0 if dstStartInSrc is >0:
-    // 0  3          // src coords
-    // |  [========] // dst box
-    // ^--^
-    *out_intStartInSrc = std::max<int32_t>(0, dstStartInSrc);
-
-    // Only >0 if dstStartInSrc is <0:
-    //-6     0       // src coords
-    // [=====|==]    // dst box
-    // ^-----^
-    *out_intStartInDst = std::max<int32_t>(0, 0 - dstStartInSrc);
-
-    int32_t intEndInSrc = std::min<int32_t>(srcSize, dstStartInSrc + dstSize);
-    *out_intSize = std::max<int32_t>(0, intEndInSrc - *out_intStartInSrc);
+    MOZ_ASSERT(srcSize >= 0);
+    MOZ_ASSERT(readSize >= 0);
+    const auto read1 = int64_t(read0) + readSize;
+
+    int32_t intRead0 = read0; // Clearly doesn't need validation.
+    int64_t intWrite0 = 0;
+    int64_t intSize = readSize;
+
+    if (read1 <= 0 || read0 >= srcSize) {
+        // Disjoint ranges.
+        intSize = 0;
+    } else {
+        if (read0 < 0) {
+            const auto diff = int64_t(0) - read0;
+            MOZ_ASSERT(diff >= 0);
+            intRead0 = 0;
+            intWrite0 = diff;
+            intSize -= diff;
+        }
+        if (read1 > srcSize) {
+            const auto diff = int64_t(read1) - srcSize;
+            MOZ_ASSERT(diff >= 0);
+            intSize -= diff;
+        }
+
+        if (!CheckedInt<int32_t>(intWrite0).isValid() ||
+            !CheckedInt<int32_t>(intSize).isValid())
+        {
+            return false;
+        }
+    }
+
+    *out_intRead0 = intRead0;
+    *out_intWrite0 = intWrite0;
+    *out_intSize = intSize;
+    return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 CheckedUint32
 WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
                             uint32_t depth, uint8_t bytesPerPixel)
 {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -2159,20 +2159,19 @@ public:
     ScopedLazyBind(gl::GLContext* gl, GLenum target, const WebGLBuffer* buf);
 
 private:
     void UnwrapImpl();
 };
 
 ////
 
-void
-Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
-          uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
-          uint32_t* const out_intSize);
+bool
+Intersect(int32_t srcSize, int32_t read0, int32_t readSize, int32_t* out_intRead0,
+          int32_t* out_intWrite0, int32_t* out_intSize);
 
 ////
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags = 0);
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1551,28 +1551,42 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
         return;
     }
 
     if (bytesNeeded > dataLen) {
         ErrorInvalidOperation("readPixels: buffer too small");
         return;
     }
 
+    ////
+
+    int32_t readX, readY;
+    int32_t writeX, writeY;
+    int32_t rwWidth, rwHeight;
+    if (!Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth) ||
+        !Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight))
+    {
+        ErrorOutOfMemory("readPixels: Bad subrect selection.");
+        return;
+    }
+
     ////////////////
     // Now that the errors are out of the way, on to actually reading!
 
     OnBeforeReadCall();
 
-    uint32_t readX, readY;
-    uint32_t writeX, writeY;
-    uint32_t rwWidth, rwHeight;
-    Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
-    Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
-
-    if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
+    if (!rwWidth || !rwHeight) {
+        // Disjoint rects, so we're done already.
+        DummyReadFramebufferOperation("readPixels");
+        return;
+    }
+
+    if (uint32_t(rwWidth) == width &&
+        uint32_t(rwHeight) == height)
+    {
         DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
                                packType, dest, dataLen, rowStride);
         return;
     }
 
     // Read request contains out-of-bounds pixels. Unfortunately:
     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
     // "If any of these pixels lies outside of the window allocated to the current GL
@@ -1581,22 +1595,16 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
 
     // This is a slow-path, so warn people away!
     GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
                     " may be slow.");
 
     ////////////////////////////////////
     // Read only the in-bounds pixels.
 
-    if (!rwWidth || !rwHeight) {
-        // There aren't any, so we're 'done'.
-        DummyReadFramebufferOperation("readPixels");
-        return;
-    }
-
     if (IsWebGL2()) {
         if (!mPixelStore_PackRowLength) {
             gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
                              mPixelStore_PackSkipPixels + width);
         }
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
 
@@ -1606,17 +1614,17 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
         gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     } else {
         // I *did* say "hilariously slow".
 
         uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel;
         row += writeY * rowStride;
-        for (uint32_t j = 0; j < rwHeight; j++) {
+        for (uint32_t j = 0; j < uint32_t(rwHeight); j++) {
             DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
                                    packFormat, packType, row, dataLen, rowStride);
             row += rowStride;
         }
     }
 }
 
 void
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -2006,34 +2006,38 @@ DoCopyTexOrSubImage(WebGLContext* webgl,
                     GLint xOffset, GLint yOffset, GLint zOffset,
                     uint32_t dstWidth, uint32_t dstHeight,
                     const webgl::FormatUsageInfo* dstUsage)
 {
     const auto& gl = webgl->gl;
 
     ////
 
-    uint32_t readX, readY;
-    uint32_t writeX, writeY;
-    uint32_t rwWidth, rwHeight;
-    Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX, &rwWidth);
-    Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY, &rwHeight);
+    int32_t readX, readY;
+    int32_t writeX, writeY;
+    int32_t rwWidth, rwHeight;
+    if (!Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX, &rwWidth) ||
+        !Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY, &rwHeight))
+    {
+        webgl->ErrorOutOfMemory("%s: Bad subrect selection.", funcName);
+        return false;
+    }
 
     writeX += xOffset;
     writeY += yOffset;
 
     ////
 
     GLenum error = 0;
     do {
         const auto& idealUnpack = dstUsage->idealUnpack;
         if (!isSubImage) {
             UniqueBuffer buffer;
 
-            if (rwWidth != dstWidth || rwHeight != dstHeight) {
+            if (uint32_t(rwWidth) != dstWidth || uint32_t(rwHeight) != dstHeight) {
                 const auto& pi = idealUnpack->ToPacking();
                 CheckedUint32 byteCount = BytesPerPixel(pi);
                 byteCount *= dstWidth;
                 byteCount *= dstHeight;
 
                 if (byteCount.isValid()) {
                     buffer = calloc(1, byteCount.value());
                 }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -741,16 +741,20 @@ ContentParent::GetMaxProcessCount(const 
 ContentParent::IsMaxProcessCountReached(const nsAString& aContentProcessType)
 {
   return GetPoolSize(aContentProcessType) >= GetMaxProcessCount(aContentProcessType);
 }
 
 /*static*/ void
 ContentParent::ReleaseCachedProcesses()
 {
+  if (!GetPoolSize(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
+    return;
+  }
+
   // We might want to extend this for other process types as well in the future...
   nsTArray<ContentParent*>& contentParents = GetOrCreatePool(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   nsTArray<ContentParent*> toRelease;
 
   // Shuting down these processes will change the array so let's use another array for the removal.
   for (auto* cp : contentParents) {
     nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(cp->mChildID);
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -9,26 +9,21 @@
 #include <inttypes.h>
 
 #include "nsAutoPtr.h"
 #include "VideoUtils.h"
 #include "TimeUnits.h"
 #include "prenv.h"
 #include "mozilla/SizePrintfMacros.h"
 
-#ifdef PR_LOGGING
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define ADTSLOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("ADTSDemuxer " msg, ##__VA_ARGS__))
 #define ADTSLOGV(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("ADTSDemuxer " msg, ##__VA_ARGS__))
-#else
-#define ADTSLOG(msg, ...)  do {} while (false)
-#define ADTSLOGV(msg, ...) do {} while (false)
-#endif
 
 namespace mozilla {
 namespace adts {
 
 // adts::FrameHeader - Holds the ADTS frame header and its parsing
 // state.
 //
 // ADTS Frame Structure
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -12,26 +12,21 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "nsAutoPtr.h"
 #include "VideoUtils.h"
 #include "TimeUnits.h"
 #include "prenv.h"
 
-#ifdef PR_LOGGING
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define MP3LOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
 #define MP3LOGV(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
-#else
-#define MP3LOG(msg, ...)
-#define MP3LOGV(msg, ...)
-#endif
 
 using mozilla::media::TimeUnit;
 using mozilla::media::TimeInterval;
 using mozilla::media::TimeIntervals;
 using mp4_demuxer::ByteReader;
 
 namespace mozilla {
 namespace mp3 {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -123,19 +123,17 @@ public:
     if (decoders.IsEmpty()) {
       sUniqueInstance = nullptr;
     }
   }
 };
 
 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
 
-#if defined(PR_LOGGING)
 LazyLogModule gMediaTimerLog("MediaTimer");
-#endif
 
 void
 MediaDecoder::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -10,26 +10,21 @@
 #include "mozilla/SizePrintfMacros.h"
 #include "mp4_demuxer/BitReader.h"
 #include "nsAutoPtr.h"
 #include "prenv.h"
 #include "FlacFrameParser.h"
 #include "VideoUtils.h"
 #include "TimeUnits.h"
 
-#ifdef PR_LOGGING
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define LOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("FlacDemuxer " msg, ##__VA_ARGS__))
 #define LOGV(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("FlacDemuxer " msg, ##__VA_ARGS__))
-#else
-#define LOG(msg, ...)  do {} while (false)
-#define LOGV(msg, ...) do {} while (false)
-#endif
 
 using namespace mozilla::media;
 
 namespace mozilla {
 namespace flac {
 
 // flac::FrameHeader - Holds the flac frame header and its parsing
 // state.
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -43,26 +43,28 @@ namespace camera {
 // - the main thread for some setups, and occassionally for video capture setup
 //   calls that don't work correctly elsewhere.
 // - the IPC thread on which PBackground is running and which receives and
 //   sends messages
 // - a thread which will execute the actual (possibly slow) camera access
 //   called "VideoCapture". On Windows this is a thread with an event loop
 //   suitable for UI access.
 
+// InputObserver is owned by CamerasParent, and it has a ref to CamerasParent
 void InputObserver::OnDeviceChange() {
   LOG((__PRETTY_FUNCTION__));
   MOZ_ASSERT(mParent);
 
+  RefPtr<InputObserver> self(this);
   RefPtr<nsIRunnable> ipc_runnable =
-    media::NewRunnableFrom([this]() -> nsresult {
-      if (mParent->IsShuttingDown()) {
+    media::NewRunnableFrom([self]() -> nsresult {
+      if (self->mParent->IsShuttingDown()) {
         return NS_ERROR_FAILURE;
       }
-      Unused << mParent->SendDeviceChange();
+      Unused << self->mParent->SendDeviceChange();
       return NS_OK;
     });
 
   nsIThread* thread = mParent->GetBackgroundThread();
   MOZ_ASSERT(thread != nullptr);
   thread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
 };
 
@@ -349,21 +351,21 @@ CamerasParent::SetupEngine(CaptureEngine
   config->Set<webrtc::CaptureDeviceInfo>(captureDeviceInfo);
   *engine = mozilla::camera::VideoEngine::Create(UniquePtr<const webrtc::Config>(config.release()));
 
   if (!engine->get()) {
     LOG(("VideoEngine::Create failed"));
     return false;
   }
 
-  InputObserver** observer = mObservers.AppendElement(new InputObserver(this));
+  RefPtr<InputObserver>* observer = mObservers.AppendElement(new InputObserver(this));
   auto device_info = engine->get()->GetOrCreateVideoCaptureDeviceInfo();
   MOZ_ASSERT(device_info);
   if (device_info) {
-    device_info->RegisterVideoInputFeedBack(**observer);
+    device_info->RegisterVideoInputFeedBack(*(observer->get()));
   }
 
   return true;
 }
 
 void
 CamerasParent::CloseEngines()
 {
@@ -393,19 +395,17 @@ CamerasParent::CloseEngines()
       if (device_info) {
         device_info->DeRegisterVideoInputFeedBack();
       }
       mozilla::camera::VideoEngine::Delete(engine);
       mEngines[i] = nullptr;
     }
   }
 
-  for (InputObserver* observer : mObservers) {
-    delete observer;
-  }
+  // the observers hold references to us
   mObservers.Clear();
 
   mWebRTCAlive = false;
 }
 
 VideoEngine *
 CamerasParent::EnsureInitialized(int aEngine)
 {
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -64,23 +64,27 @@ private:
   CaptureEngine mCapEngine;
   uint32_t mStreamId;
   CamerasParent *mParent;
 };
 
 class InputObserver :  public webrtc::VideoInputFeedBack
 {
 public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputObserver)
+
   explicit InputObserver(CamerasParent* aParent)
     : mParent(aParent) {};
   virtual void OnDeviceChange();
 
   friend CamerasParent;
 
 private:
+  ~InputObserver() {}
+
   RefPtr<CamerasParent> mParent;
 };
 
 class CamerasParent :  public PCamerasParent,
                        public nsIObserver
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -156,17 +160,17 @@ protected:
   base::Thread* mVideoCaptureThread;
 
   // Shutdown handling
   bool mChildIsAlive;
   bool mDestroyed;
   // Above 2 are PBackground only, but this is potentially
   // read cross-thread.
   mozilla::Atomic<bool> mWebRTCAlive;
-  nsTArray<InputObserver*> mObservers;
+  nsTArray<RefPtr<InputObserver>> mObservers;
 };
 
 PCamerasParent* CreateCamerasParent();
 
 } // namespace camera
 } // namespace mozilla
 
 #endif  // mozilla_CameraParent_h
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -146,21 +146,19 @@ static const char *kPrefUnloadPluginTime
 static const uint32_t kDefaultPluginUnloadingTimeout = 30;
 
 static const char *kPluginRegistryVersion = "0.18";
 
 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
 
 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
 
-#ifdef PLUGIN_LOGGING
 LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
 LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
 LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME);
-#endif
 
 // #defines for plugin cache and prefs
 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
 // Raise this from '10' to '50' to work around a bug in Apple's current Java
 // plugins on OS X Lion and SnowLeopard.  See bug 705931.
 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
 
 nsIFile *nsPluginHost::sPluginTempDir;
--- a/dom/plugins/base/nsPluginLogging.h
+++ b/dom/plugins/base/nsPluginLogging.h
@@ -5,20 +5,16 @@
 
 /* Plugin Module Logging usage instructions and includes */
 ////////////////////////////////////////////////////////////////////////////////
 #ifndef nsPluginLogging_h__
 #define nsPluginLogging_h__
 
 #include "mozilla/Logging.h"
 
-#ifndef PLUGIN_LOGGING    // allow external override
-#define PLUGIN_LOGGING 1  // master compile-time switch for pluging logging
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 // Basic Plugin Logging Usage Instructions
 //
 // 1. Set this environment variable: MOZ_LOG=<name>:<level>
 
 // Choose the <name> and <level> from this list (no quotes):
 
 // Log Names            <name>
@@ -38,53 +34,23 @@
 // 3. Instead of output going to the console, you can log to a file. Additionally, set the
 //    MOZ_LOG_FILE environment variable to point to the full path of a file.
 //    My favorite Win32 Example: SET MOZ_LOG_FILE=c:\temp\pluginLog.txt
 
 // 4. For complete information see the Gecko Developer guide:
 // https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Gecko_Logging
 
 
-#ifdef PLUGIN_LOGGING
-
 class nsPluginLogging
 {
 public:
   static mozilla::LazyLogModule gNPNLog;  // 4.x NP API, calls into navigator
   static mozilla::LazyLogModule gNPPLog;  // 4.x NP API, calls into plugin
   static mozilla::LazyLogModule gPluginLog;  // general plugin log
 };
 
-#endif   // PLUGIN_LOGGING
-
 // Quick-use macros
-#ifdef PLUGIN_LOGGING
- #define NPN_PLUGIN_LOG(a, b)                              \
-   PR_BEGIN_MACRO                                        \
-   MOZ_LOG(nsPluginLogging::gNPNLog, a, b); \
-   PR_LogFlush();                                                    \
-   PR_END_MACRO
-#else
- #define NPN_PLUGIN_LOG(a, b)
-#endif
-
-#ifdef PLUGIN_LOGGING
- #define NPP_PLUGIN_LOG(a, b)                              \
-   PR_BEGIN_MACRO                                         \
-   MOZ_LOG(nsPluginLogging::gNPPLog, a, b); \
-   PR_LogFlush();                                                    \
-   PR_END_MACRO
-#else
- #define NPP_PLUGIN_LOG(a, b)
-#endif
-
-#ifdef PLUGIN_LOGGING
- #define PLUGIN_LOG(a, b)                              \
-   PR_BEGIN_MACRO                                         \
-   MOZ_LOG(nsPluginLogging::gPluginLog, a, b); \
-   PR_LogFlush();                                                    \
-   PR_END_MACRO
-#else
- #define PLUGIN_LOG(a, b)
-#endif
+#define NPN_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPNLog, a, b)
+#define NPP_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPPLog, a, b)
+#define PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gPluginLog, a, b)
 
 #endif   // nsPluginLogging_h__
 
--- a/dom/webidl/CommandEvent.webidl
+++ b/dom/webidl/CommandEvent.webidl
@@ -3,12 +3,12 @@
  * 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/.
  */
 
 interface CommandEvent : Event {
   readonly attribute DOMString? command;
 
   void initCommandEvent(DOMString type,
-                        boolean canBubble,
-                        boolean cancelable,
-                        DOMString? command);
+                        optional boolean canBubble = false,
+                        optional boolean cancelable = false,
+                        optional DOMString? command = null);
 };
--- a/dom/webidl/CompositionEvent.webidl
+++ b/dom/webidl/CompositionEvent.webidl
@@ -26,14 +26,14 @@ interface CompositionEvent : UIEvent
 
 dictionary CompositionEventInit : UIEventInit {
   DOMString data = "";
 };
 
 partial interface CompositionEvent
 {
   void initCompositionEvent(DOMString typeArg,
-                            boolean canBubbleArg,
-                            boolean cancelableArg,
-                            Window? viewArg,
-                            DOMString? dataArg,
-                            DOMString localeArg);
+                            optional boolean canBubbleArg = false,
+                            optional boolean cancelableArg = false,
+                            optional Window? viewArg = null,
+                            optional DOMString? dataArg = null,
+                            optional DOMString localeArg = "");
 };
--- a/dom/webidl/CustomEvent.webidl
+++ b/dom/webidl/CustomEvent.webidl
@@ -14,17 +14,17 @@
  Exposed=(Window, Worker)]
 interface CustomEvent : Event
 {
   readonly attribute any detail;
 
   // initCustomEvent is a Gecko specific deprecated method.
   [Throws]
   void initCustomEvent(DOMString type,
-                       boolean canBubble,
-                       boolean cancelable,
-                       any detail);
+                       optional boolean canBubble = false,
+                       optional boolean cancelable = false,
+                       optional any detail = null);
 };
 
 dictionary CustomEventInit : EventInit
 {
   any detail = null;
 };
--- a/dom/webidl/DeviceMotionEvent.webidl
+++ b/dom/webidl/DeviceMotionEvent.webidl
@@ -43,15 +43,15 @@ dictionary DeviceMotionEventInit : Event
   DeviceAccelerationInit accelerationIncludingGravity;
   DeviceRotationRateInit rotationRate;
   double? interval = null;
 };
 
 // Mozilla extensions.
 partial interface DeviceMotionEvent {
   void initDeviceMotionEvent(DOMString type,
-                             boolean canBubble,
-                             boolean cancelable,
-                             DeviceAccelerationInit acceleration,
-                             DeviceAccelerationInit accelerationIncludingGravity,
-                             DeviceRotationRateInit rotationRate,
-                             double? interval);
+                             optional boolean canBubble = false,
+                             optional boolean cancelable = false,
+                             optional DeviceAccelerationInit acceleration,
+                             optional DeviceAccelerationInit accelerationIncludingGravity,
+                             optional DeviceRotationRateInit rotationRate,
+                             optional double? interval = null);
 };
--- a/dom/webidl/DeviceOrientationEvent.webidl
+++ b/dom/webidl/DeviceOrientationEvent.webidl
@@ -9,22 +9,22 @@ interface DeviceOrientationEvent : Event
 {
   readonly attribute double? alpha;
   readonly attribute double? beta;
   readonly attribute double? gamma;
   readonly attribute boolean absolute;
 
   // initDeviceOrientationEvent is a Gecko specific deprecated method.
   void initDeviceOrientationEvent(DOMString type,
-                                  boolean canBubble,
-                                  boolean cancelable,
-                                  double? alpha,
-                                  double? beta,
-                                  double? gamma,
-                                  boolean absolute);
+                                  optional boolean canBubble = false,
+                                  optional boolean cancelable = false,
+                                  optional double? alpha = null,
+                                  optional double? beta = null,
+                                  optional double? gamma = null,
+                                  optional boolean absolute = false);
 };
 
 dictionary DeviceOrientationEventInit : EventInit
 {
   double? alpha = null;
   double? beta = null;
   double? gamma = null;
   boolean absolute = false;
--- a/dom/webidl/DragEvent.webidl
+++ b/dom/webidl/DragEvent.webidl
@@ -5,29 +5,29 @@
  */
 
 [Constructor(DOMString type, optional DragEventInit eventInitDict)]
 interface DragEvent : MouseEvent
 {
   readonly attribute DataTransfer? dataTransfer;
 
   void initDragEvent(DOMString type,
-                     boolean canBubble,
-                     boolean cancelable,
-                     Window? aView,
-                     long aDetail,
-                     long aScreenX,
-                     long aScreenY,
-                     long aClientX,
-                     long aClientY,
-                     boolean aCtrlKey,
-                     boolean aAltKey,
-                     boolean aShiftKey,
-                     boolean aMetaKey,
-                     unsigned short aButton,
-                     EventTarget? aRelatedTarget,
-                     DataTransfer? aDataTransfer);
+                     optional boolean canBubble = false,
+                     optional boolean cancelable = false,
+                     optional Window? aView = null,
+                     optional long aDetail = 0,
+                     optional long aScreenX = 0,
+                     optional long aScreenY = 0,
+                     optional long aClientX = 0,
+                     optional long aClientY = 0,
+                     optional boolean aCtrlKey = false,
+                     optional boolean aAltKey = false,
+                     optional boolean aShiftKey = false,
+                     optional boolean aMetaKey = false,
+                     optional unsigned short aButton = 0,
+                     optional EventTarget? aRelatedTarget = null,
+                     optional DataTransfer? aDataTransfer = null);
 };
 
 dictionary DragEventInit : MouseEventInit
 {
   DataTransfer? dataTransfer = null;
 };
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -45,17 +45,19 @@ interface Event {
   [Pure]
   readonly attribute boolean composed;
 
   [Unforgeable, Pure]
   readonly attribute boolean isTrusted;
   [Pure]
   readonly attribute DOMHighResTimeStamp timeStamp;
 
-  void initEvent(DOMString type, boolean bubbles, boolean cancelable);
+  void initEvent(DOMString type,
+                 optional boolean bubbles = false,
+                 optional boolean cancelable = false);
   attribute boolean cancelBubble;
 };
 
 // Mozilla specific legacy stuff.
 partial interface Event {
   const long ALT_MASK     = 0x00000001;
   const long CONTROL_MASK = 0x00000002;
   const long SHIFT_MASK   = 0x00000004;
--- a/dom/webidl/HashChangeEvent.webidl
+++ b/dom/webidl/HashChangeEvent.webidl
@@ -6,19 +6,19 @@
 
 [Constructor(DOMString type, optional HashChangeEventInit eventInitDict), LegacyEventInit]
 interface HashChangeEvent : Event
 {
   readonly attribute DOMString oldURL;
   readonly attribute DOMString newURL;
 
   void initHashChangeEvent(DOMString typeArg,
-                           boolean canBubbleArg,
-                           boolean cancelableArg,
-                           DOMString oldURLArg,
-                           DOMString newURLArg);
+                           optional boolean canBubbleArg = false,
+                           optional boolean cancelableArg = false,
+                           optional DOMString oldURLArg = "",
+                           optional DOMString newURLArg = "");
 };
 
 dictionary HashChangeEventInit : EventInit
 {
   DOMString oldURL = "";
   DOMString newURL = "";
 };
--- a/dom/webidl/KeyEvent.webidl
+++ b/dom/webidl/KeyEvent.webidl
@@ -220,18 +220,18 @@ interface KeyEvent
   const unsigned long DOM_VK_ZOOM           = 0xFB;
   const unsigned long DOM_VK_PA1            = 0xFD;
 
   // OEM specific virtual keyCode of Windows should pass through DOM keyCode
   // for compatibility with the other web browsers on Windows.
   const unsigned long DOM_VK_WIN_OEM_CLEAR  = 0xFE;
 
   void initKeyEvent(DOMString type,
-                    boolean canBubble,
-                    boolean cancelable,
-                    Window? view,
-                    boolean ctrlKey,
-                    boolean altKey,
-                    boolean shiftKey,
-                    boolean metaKey,
-                    unsigned long keyCode,
-                    unsigned long charCode);
+                    optional boolean canBubble = false,
+                    optional boolean cancelable = false,
+                    optional Window? view = null,
+                    optional boolean ctrlKey = false,
+                    optional boolean altKey = false,
+                    optional boolean shiftKey = false,
+                    optional boolean metaKey = false,
+                    optional unsigned long keyCode = 0,
+                    optional unsigned long charCode = 0);
 };
--- a/dom/webidl/MessageEvent.webidl
+++ b/dom/webidl/MessageEvent.webidl
@@ -38,20 +38,24 @@ interface MessageEvent : Event {
   /**
    * Initializes this event with the given data, in a manner analogous to
    * the similarly-named method on the nsIDOMEvent interface, also setting the
    * data, origin, source, and lastEventId attributes of this appropriately.
    */
   [Pure, Cached, Frozen]
   readonly attribute sequence<MessagePort> ports;
 
-  void initMessageEvent(DOMString type, boolean bubbles, boolean cancelable,
-                        any data, DOMString origin, DOMString lastEventId,
-                        MessageEventSource? source,
-                        sequence<MessagePort> ports);
+  void initMessageEvent(DOMString type,
+                        optional boolean bubbles = false,
+                        optional boolean cancelable = false,
+                        optional any data = null,
+                        optional DOMString origin = "",
+                        optional DOMString lastEventId = "",
+                        optional MessageEventSource? source = null,
+                        optional sequence<MessagePort> ports = []);
 };
 
 dictionary MessageEventInit : EventInit {
   any data = null;
   DOMString origin = "";
   DOMString lastEventId = "";
   MessageEventSource? source = null;
   sequence<MessagePort> ports = [];
--- a/dom/webidl/MouseEvent.webidl
+++ b/dom/webidl/MouseEvent.webidl
@@ -33,31 +33,31 @@ interface MouseEvent : UIEvent {
   readonly attribute EventTarget?   relatedTarget;
   readonly attribute DOMString?     region;
 
   // Pointer Lock
   readonly attribute long           movementX;
   readonly attribute long           movementY;
 
   // Deprecated in DOM Level 3:
-  void                              initMouseEvent(DOMString typeArg, 
-                                                   boolean canBubbleArg, 
-                                                   boolean cancelableArg, 
-                                                   Window? viewArg,
-                                                   long detailArg, 
-                                                   long screenXArg, 
-                                                   long screenYArg, 
-                                                   long clientXArg, 
-                                                   long clientYArg, 
-                                                   boolean ctrlKeyArg, 
-                                                   boolean altKeyArg, 
-                                                   boolean shiftKeyArg, 
-                                                   boolean metaKeyArg, 
-                                                   short buttonArg,
-                                                   EventTarget? relatedTargetArg);
+void initMouseEvent(DOMString typeArg,
+                    optional boolean canBubbleArg = false,
+                    optional boolean cancelableArg = false,
+                    optional Window? viewArg = null,
+                    optional long detailArg = 0,
+                    optional long screenXArg = 0,
+                    optional long screenYArg = 0,
+                    optional long clientXArg = 0,
+                    optional long clientYArg = 0,
+                    optional boolean ctrlKeyArg = false,
+                    optional boolean altKeyArg = false,
+                    optional boolean shiftKeyArg = false,
+                    optional boolean metaKeyArg = false,
+                    optional short buttonArg = 0,
+                    optional EventTarget? relatedTargetArg = null);
   // Introduced in DOM Level 3:
   boolean                           getModifierState(DOMString keyArg);
 };
 
 // Suggested initMouseEvent replacement initializer:
 dictionary MouseEventInit : EventModifierInit {
   // Attributes for MouseEvent:
   long           screenX       = 0;
@@ -86,30 +86,30 @@ partial interface MouseEvent
   const unsigned short    MOZ_SOURCE_PEN        = 2;
   const unsigned short    MOZ_SOURCE_ERASER     = 3;
   const unsigned short    MOZ_SOURCE_CURSOR     = 4;
   const unsigned short    MOZ_SOURCE_TOUCH      = 5;
   const unsigned short    MOZ_SOURCE_KEYBOARD   = 6;
 
   readonly attribute unsigned short mozInputSource;
 
-  void                initNSMouseEvent(DOMString typeArg,
-                                       boolean canBubbleArg,
-                                       boolean cancelableArg,
-                                       Window? viewArg,
-                                       long detailArg,
-                                       long screenXArg,
-                                       long screenYArg,
-                                       long clientXArg,
-                                       long clientYArg,
-                                       boolean ctrlKeyArg,
-                                       boolean altKeyArg,
-                                       boolean shiftKeyArg,
-                                       boolean metaKeyArg,
-                                       short buttonArg,
-                                       EventTarget? relatedTargetArg,
-                                       float pressure,
-                                       unsigned short inputSourceArg);
+  void initNSMouseEvent(DOMString typeArg,
+                        optional boolean canBubbleArg = false,
+                        optional boolean cancelableArg = false,
+                        optional Window? viewArg = null,
+                        optional long detailArg = 0,
+                        optional long screenXArg = 0,
+                        optional long screenYArg = 0,
+                        optional long clientXArg = 0,
+                        optional long clientYArg = 0,
+                        optional boolean ctrlKeyArg = false,
+                        optional boolean altKeyArg = false,
+                        optional boolean shiftKeyArg = false,
+                        optional boolean metaKeyArg = false,
+                        optional short buttonArg = 0,
+                        optional EventTarget? relatedTargetArg = null,
+                        optional float pressure = 0,
+                        optional unsigned short inputSourceArg = 0);
   [ChromeOnly]
   readonly attribute boolean hitCluster; // True when touch occurs in a cluster of links
 
 };
 
--- a/dom/webidl/MouseScrollEvent.webidl
+++ b/dom/webidl/MouseScrollEvent.webidl
@@ -7,24 +7,24 @@
 interface MouseScrollEvent : MouseEvent
 {
   const long HORIZONTAL_AXIS = 1;
   const long VERTICAL_AXIS = 2;
 
   readonly attribute long axis;
 
   void initMouseScrollEvent(DOMString type,
-                            boolean canBubble,
-                            boolean cancelable,
-                            Window? view,
-                            long detail,
-                            long screenX,
-                            long screenY,
-                            long clientX,
-                            long clientY,
-                            boolean ctrlKey,
-                            boolean altKey,
-                            boolean shiftKey,
-                            boolean metaKey,
-                            unsigned short button,
-                            EventTarget? relatedTarget,
-                            long axis);
+                            optional boolean canBubble = false,
+                            optional boolean cancelable = false,
+                            optional Window? view = null,
+                            optional long detail = 0,
+                            optional long screenX = 0,
+                            optional long screenY = 0,
+                            optional long clientX = 0,
+                            optional long clientY = 0,
+                            optional boolean ctrlKey = false,
+                            optional boolean altKey = false,
+                            optional boolean shiftKey = false,
+                            optional boolean metaKey = false,
+                            optional short button = 0,
+                            optional EventTarget? relatedTarget = null,
+                            optional long axis = 0);
 };
--- a/dom/webidl/MutationEvent.webidl
+++ b/dom/webidl/MutationEvent.webidl
@@ -18,16 +18,16 @@ interface MutationEvent : Event
   readonly attribute Node?          relatedNode;
   readonly attribute DOMString      prevValue;
   readonly attribute DOMString      newValue;
   readonly attribute DOMString      attrName;
   readonly attribute unsigned short attrChange;
 
   [Throws]
   void initMutationEvent(DOMString type,
-                         boolean canBubble,
-                         boolean cancelable,
-                         Node? relatedNode,
-                         DOMString prevValue,
-                         DOMString newValue,
-                         DOMString attrName,
-                         unsigned short attrChange);
+                         optional boolean canBubble = false,
+                         optional boolean cancelable = false,
+                         optional Node? relatedNode = null,
+                         optional DOMString prevValue = "",
+                         optional DOMString newValue = "",
+                         optional DOMString attrName = "",
+                         optional unsigned short attrChange = 0);
 };
--- a/dom/webidl/ScrollAreaEvent.webidl
+++ b/dom/webidl/ScrollAreaEvent.webidl
@@ -7,17 +7,17 @@
 interface ScrollAreaEvent : UIEvent
 {
   readonly attribute float x;
   readonly attribute float y;
   readonly attribute float width;
   readonly attribute float height;
 
   void initScrollAreaEvent(DOMString type,
-                           boolean canBubble,
-                           boolean cancelable,
-                           Window? view,
-                           long detail,
-                           float x,
-                           float y,
-                           float width,
-                           float height);
+                           optional boolean canBubble = false,
+                           optional boolean cancelable = false,
+                           optional Window? view = null,
+                           optional long detail = 0,
+                           optional float x = 0,
+                           optional float y = 0,
+                           optional float width = 0,
+                           optional float height = 0);
 };
--- a/dom/webidl/SimpleGestureEvent.webidl
+++ b/dom/webidl/SimpleGestureEvent.webidl
@@ -20,27 +20,27 @@ interface SimpleGestureEvent : MouseEven
 
   readonly attribute unsigned long direction;
 
   readonly attribute double delta;
 
   readonly attribute unsigned long clickCount;
 
   void initSimpleGestureEvent(DOMString typeArg,
-                              boolean canBubbleArg,
-                              boolean cancelableArg,
-                              Window? viewArg,
-                              long detailArg,
-                              long screenXArg,
-                              long screenYArg,
-                              long clientXArg,
-                              long clientYArg,
-                              boolean ctrlKeyArg,
-                              boolean altKeyArg,
-                              boolean shiftKeyArg,
-                              boolean metaKeyArg,
-                              unsigned short buttonArg,
-                              EventTarget? relatedTargetArg,
-                              unsigned long allowedDirectionsArg,
-                              unsigned long directionArg,
-                              double deltaArg,
-                              unsigned long clickCount);
+                              optional boolean canBubbleArg = false,
+                              optional boolean cancelableArg = false,
+                              optional Window? viewArg = null,
+                              optional long detailArg = 0,
+                              optional long screenXArg = 0,
+                              optional long screenYArg = 0,
+                              optional long clientXArg = 0,
+                              optional long clientYArg = 0,
+                              optional boolean ctrlKeyArg = false,
+                              optional boolean altKeyArg = false,
+                              optional boolean shiftKeyArg = false,
+                              optional boolean metaKeyArg = false,
+                              optional short buttonArg = 0,
+                              optional EventTarget? relatedTargetArg = null,
+                              optional unsigned long allowedDirectionsArg = 0,
+                              optional unsigned long directionArg = 0,
+                              optional double deltaArg = 0,
+                              optional unsigned long clickCount = 0);
 };
--- a/dom/webidl/StorageEvent.webidl
+++ b/dom/webidl/StorageEvent.webidl
@@ -16,23 +16,23 @@ interface StorageEvent : Event
   readonly attribute DOMString? key;
   readonly attribute DOMString? oldValue;
   readonly attribute DOMString? newValue;
   readonly attribute DOMString? url;
   readonly attribute Storage? storageArea;
 
   // Bug 1016053 - This is not spec compliant.
   void initStorageEvent(DOMString type,
-                        boolean canBubble,
-                        boolean cancelable,
-                        DOMString? key,
-                        DOMString? oldValue,
-                        DOMString? newValue,
-                        DOMString? url,
-                        Storage? storageArea);
+                        optional boolean canBubble = false,
+                        optional boolean cancelable = false,
+                        optional DOMString? key = null,
+                        optional DOMString? oldValue = null,
+                        optional DOMString? newValue = null,
+                        optional DOMString? url = null,
+                        optional Storage? storageArea = null);
 };
 
 dictionary StorageEventInit : EventInit
 {
   DOMString? key = null;
   DOMString? oldValue = null;
   DOMString? newValue = null;
   DOMString url = "";
--- a/dom/webidl/TimeEvent.webidl
+++ b/dom/webidl/TimeEvent.webidl
@@ -10,11 +10,11 @@
  * liability, trademark and document use rules apply.
  */
 
 interface TimeEvent : Event
 {
   readonly attribute long         detail;
   readonly attribute WindowProxy? view;
   void initTimeEvent(DOMString aType,
-                     Window? aView,
-                     long aDetail);
+                     optional Window? aView = null,
+                     optional long aDetail = 0);
 };
--- a/dom/webidl/TouchEvent.webidl
+++ b/dom/webidl/TouchEvent.webidl
@@ -18,20 +18,20 @@ interface TouchEvent : UIEvent {
   readonly attribute TouchList changedTouches;
 
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
 
   void initTouchEvent(DOMString type,
-                      boolean canBubble,
-                      boolean cancelable,
-                      Window? view,
-                      long detail,
-                      boolean ctrlKey,
-                      boolean altKey,
-                      boolean shiftKey,
-                      boolean metaKey,
-                      TouchList? touches,
-                      TouchList? targetTouches,
-                      TouchList? changedTouches);
+                      optional boolean canBubble = false,
+                      optional boolean cancelable = false,
+                      optional Window? view = null,
+                      optional long detail = 0,
+                      optional boolean ctrlKey = false,
+                      optional boolean altKey = false,
+                      optional boolean shiftKey = false,
+                      optional boolean metaKey = false,
+                      optional TouchList? touches = null,
+                      optional TouchList? targetTouches = null,
+                      optional TouchList? changedTouches = null);
 };
--- a/dom/webidl/UIEvent.webidl
+++ b/dom/webidl/UIEvent.webidl
@@ -11,20 +11,20 @@
  */
 
 [Constructor(DOMString type, optional UIEventInit eventInitDict)]
 interface UIEvent : Event
 {
   readonly attribute WindowProxy? view;
   readonly attribute long         detail;
   void initUIEvent(DOMString aType,
-                   boolean aCanBubble,
-                   boolean aCancelable,
-                   Window? aView,
-                   long aDetail);
+                   optional boolean aCanBubble = false,
+                   optional boolean aCancelable = false,
+                   optional Window? aView = null,
+                   optional long aDetail = 0);
 };
 
 // Additional DOM0 properties.
 partial interface UIEvent {
   const long SCROLL_PAGE_UP = -32768;
   const long SCROLL_PAGE_DOWN = 32768;
 
   readonly attribute long          layerX;
--- a/dom/webidl/XULCommandEvent.webidl
+++ b/dom/webidl/XULCommandEvent.webidl
@@ -10,18 +10,18 @@ interface XULCommandEvent : UIEvent
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
 
   readonly attribute Event? sourceEvent;
 
   void initCommandEvent(DOMString type,
-                        boolean canBubble,
-                        boolean cancelable,
-                        Window? view,
-                        long detail,
-                        boolean ctrlKey,
-                        boolean altKey,
-                        boolean shiftKey,
-                        boolean metaKey,
-                        Event? sourceEvent);
+                        optional boolean canBubble = false,
+                        optional boolean cancelable = false,
+                        optional Window? view = null,
+                        optional long detail = 0,
+                        optional boolean ctrlKey = false,
+                        optional boolean altKey = false,
+                        optional boolean shiftKey = false,
+                        optional boolean metaKey = false,
+                        optional Event? sourceEvent = null);
 };
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -175,17 +175,17 @@ struct BasicLogger
     // make the corresponding change in the ShouldOutputMessage method
     // above.
     if (LoggingPrefs::sGfxLogLevel >= aLevel) {
 #if defined(MOZ_WIDGET_ANDROID)
       printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
 #else
 #if defined(MOZ_LOGGING)
       if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
-        PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+        MOZ_LOG(GetGFX2DLog(), PRLogLevelForLevel(aLevel), ("%s%s", aString.c_str(), aNoNewline ? "" : "\n"));
       } else
 #endif
       if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
                  (aLevel < LOG_DEBUG)) {
         printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
       }
 #endif
     }
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -6,16 +6,17 @@
 #include <stdint.h>                     // for uint8_t, uint32_t
 #include "BasicLayers.h"                // for BasicLayerManager
 #include "ImageContainer.h"             // for PlanarYCbCrImage, etc
 #include "ImageTypes.h"                 // for ImageFormat, etc
 #include "cairo.h"                      // for cairo_user_data_key_t
 #include "gfxASurface.h"                // for gfxASurface, etc
 #include "gfxPlatform.h"                // for gfxPlatform, gfxImageFormat
 #include "gfxUtils.h"                   // for gfxUtils
+#include "mozilla/CheckedInt.h"
 #include "mozilla/mozalloc.h"           // for operator delete[], etc
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAutoRef.h"                  // for nsCountedRef
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ERROR, NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Image::Release, etc
 #include "nsThreadUtils.h"              // for NS_IsMainThread
@@ -106,17 +107,23 @@ BasicPlanarYCbCrImage::CopyData(const Da
   if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
       size.height > PlanarYCbCrImage::MAX_DIMENSION) {
     NS_ERROR("Illegal image dest width or height");
     return false;
   }
 
   gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format);
   mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width);
-  mDecodedBuffer = AllocateBuffer(size.height * mStride);
+  mozilla::CheckedInt32 requiredBytes =
+    mozilla::CheckedInt32(size.height) * mozilla::CheckedInt32(mStride);
+  if (!requiredBytes.isValid()) {
+    // invalid size
+    return false;
+  }
+  mDecodedBuffer = AllocateBuffer(requiredBytes.value());
   if (!mDecodedBuffer) {
     // out of memory
     return false;
   }
 
   gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride);
   SetOffscreenFormat(iFormat);
   mSize = size;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -497,19 +497,19 @@ LayerTransactionParent::RecvUpdate(const
     TimeDuration latency = TimeStamp::Now() - aInfo.transactionStart();
     if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) {
       float severity = (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)).ToMilliseconds() /
                          (4 * visualWarningTrigger);
       if (severity > 1.f) {
         severity = 1.f;
       }
       mLayerManager->VisualFrameWarning(severity);
-      PR_LogPrint("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms",
-                  OtherPid(),
-                  latency.ToMilliseconds());
+      printf_stderr("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms",
+                    OtherPid(),
+                    latency.ToMilliseconds());
     }
   }
 
   return IPC_OK();
 }
 
 bool
 LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp)
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -770,17 +770,17 @@ PreparePattern(FcPattern* aPattern, bool
     // pick up dynamic changes.
     if(aIsPrinterFont) {
        cairo_font_options_t *options = cairo_font_options_create();
        cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
        cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
        cairo_ft_font_options_substitute(options, aPattern);
        cairo_font_options_destroy(options);
        FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
-    } else {
+    } else if (!gfxPlatform::IsHeadless()) {
 #ifdef MOZ_WIDGET_GTK
         ApplyGdkScreenFontOptions(aPattern);
 
 #ifdef MOZ_X11
         FcValue value;
         int lcdfilter;
         if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
             GdkDisplay* dpy = gdk_display_get_default();
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -831,16 +831,22 @@ gfxPlatform::InitMoz2DLogging()
     mozilla::gfx::Config cfg;
     cfg.mLogForwarder = fwd;
     cfg.mMaxTextureSize = gfxPrefs::MaxTextureSize();
     cfg.mMaxAllocSize = gfxPrefs::MaxAllocSize();
 
     gfx::Factory::Init(cfg);
 }
 
+/* static */ bool
+gfxPlatform::IsHeadless()
+{
+    return PR_GetEnv("MOZ_HEADLESS");
+}
+
 static bool sLayersIPCIsUp = false;
 
 /* static */ void
 gfxPlatform::InitNullMetadata()
 {
   ScrollMetadata::sNullMetadata = new ScrollMetadata();
   ClearOnShutdown(&ScrollMetadata::sNullMetadata);
 }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -180,16 +180,18 @@ public:
 
     /**
      * Initialize ScrollMetadata statics. Does not depend on gfxPlatform.
      */
     static void InitNullMetadata();
 
     static void InitMoz2DLogging();
 
+    static bool IsHeadless();
+
     /**
      * Create an offscreen surface of the given dimensions
      * and image format.
      */
     virtual already_AddRefed<gfxASurface>
       CreateOffscreenSurface(const IntSize& aSize,
                              gfxImageFormat aFormat) = 0;
 
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -68,22 +68,24 @@ using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 
 #if (MOZ_WIDGET_GTK == 2)
 static cairo_user_data_key_t cairo_gdk_drawable_key;
 #endif
 
 gfxPlatformGtk::gfxPlatformGtk()
 {
-    gtk_init(nullptr, nullptr);
+    if (!gfxPlatform::IsHeadless()) {
+        gtk_init(nullptr, nullptr);
+    }
 
     mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
 
 #ifdef MOZ_X11
-    if (XRE_IsParentProcess()) {
+    if (!gfxPlatform::IsHeadless() && XRE_IsParentProcess()) {
       if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
           mozilla::Preferences::GetBool("gfx.xrender.enabled"))
       {
           gfxVars::SetUseXRender(true);
       }
     }
 #endif
 
@@ -92,17 +94,17 @@ gfxPlatformGtk::gfxPlatformGtk()
 #ifdef USE_SKIA
     canvasMask |= BackendTypeBit(BackendType::SKIA);
     contentMask |= BackendTypeBit(BackendType::SKIA);
 #endif
     InitBackendPrefs(canvasMask, BackendType::CAIRO,
                      contentMask, BackendType::CAIRO);
 
 #ifdef MOZ_X11
-    if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+    if (gfxPlatform::IsHeadless() && GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
       mCompositorDisplay = XOpenDisplay(nullptr);
       MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
     } else {
       mCompositorDisplay = nullptr;
     }
 #endif // MOZ_X11
 }
 
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -418,30 +418,32 @@ private:
 
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() { RegisterWeakMemoryReporter(this); }
 
   InsertOutcome Insert(NotNull<ISurfaceProvider*> aProvider,
-                       bool                       aSetAvailable)
+                       bool                       aSetAvailable,
+                       const StaticMutexAutoLock& aAutoLock)
   {
     // If this is a duplicate surface, refuse to replace the original.
     // XXX(seth): Calling Lookup() and then RemoveEntry() does the lookup
     // twice. We'll make this more efficient in bug 1185137.
     LookupResult result = Lookup(aProvider->GetImageKey(),
                                  aProvider->GetSurfaceKey(),
+                                 aAutoLock,
                                  /* aMarkUsed = */ false);
     if (MOZ_UNLIKELY(result)) {
       return InsertOutcome::FAILURE_ALREADY_PRESENT;
     }
 
     if (result.Type() == MatchType::PENDING) {
-      RemoveEntry(aProvider->GetImageKey(), aProvider->GetSurfaceKey());
+      RemoveEntry(aProvider->GetImageKey(), aProvider->GetSurfaceKey(), aAutoLock);
     }
 
     MOZ_ASSERT(result.Type() == MatchType::NOT_FOUND ||
                result.Type() == MatchType::PENDING,
                "A LookupResult with no surface should be NOT_FOUND or PENDING");
 
     // If this is bigger than we can hold after discarding everything we can,
     // refuse to cache it.
@@ -451,17 +453,17 @@ public:
       return InsertOutcome::FAILURE;
     }
 
     // Remove elements in order of cost until we can fit this in the cache. Note
     // that locked surfaces aren't in mCosts, so we never remove them here.
     while (cost > mAvailableCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(),
                  "Removed everything and it still won't fit");
-      Remove(mCosts.LastElement().Surface());
+      Remove(mCosts.LastElement().Surface(), aAutoLock);
     }
 
     // Locate the appropriate per-image cache. If there's not an existing cache
     // for this image, create it.
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aProvider->GetImageKey());
     if (!cache) {
       cache = new ImageSurfaceCache;
       mImageCaches.Put(aProvider->GetImageKey(), cache);
@@ -483,75 +485,78 @@ public:
       if (!surface->IsLocked()) {
         return InsertOutcome::FAILURE;
       }
     }
 
     // Insert.
     MOZ_ASSERT(cost <= mAvailableCost, "Inserting despite too large a cost");
     cache->Insert(surface);
-    StartTracking(surface);
+    StartTracking(surface, aAutoLock);
 
     return InsertOutcome::SUCCESS;
   }
 
-  void Remove(NotNull<CachedSurface*> aSurface)
+  void Remove(NotNull<CachedSurface*> aSurface,
+              const StaticMutexAutoLock& aAutoLock)
   {
     ImageKey imageKey = aSurface->GetImageKey();
 
     RefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
     MOZ_ASSERT(cache, "Shouldn't try to remove a surface with no image cache");
 
     // If the surface was not a placeholder, tell its image that we discarded it.
     if (!aSurface->IsPlaceholder()) {
       static_cast<Image*>(imageKey)->OnSurfaceDiscarded(aSurface->GetSurfaceKey());
     }
 
-    StopTracking(aSurface);
+    StopTracking(aSurface, aAutoLock);
     cache->Remove(aSurface);
 
     // Remove the per-image cache if it's unneeded now. (Keep it if the image is
     // locked, since the per-image cache is where we store that state.)
     if (cache->IsEmpty() && !cache->IsLocked()) {
       mImageCaches.Remove(imageKey);
     }
   }
 
-  void StartTracking(NotNull<CachedSurface*> aSurface)
+  void StartTracking(NotNull<CachedSurface*> aSurface,
+                     const StaticMutexAutoLock& aAutoLock)
   {
     CostEntry costEntry = aSurface->GetCostEntry();
     MOZ_ASSERT(costEntry.GetCost() <= mAvailableCost,
                "Cost too large and the caller didn't catch it");
 
     mAvailableCost -= costEntry.GetCost();
 
     if (aSurface->IsLocked()) {
       mLockedCost += costEntry.GetCost();
       MOZ_ASSERT(mLockedCost <= mMaxCost, "Locked more than we can hold?");
     } else {
       mCosts.InsertElementSorted(costEntry);
       // This may fail during XPCOM shutdown, so we need to ensure the object is
       // tracked before calling RemoveObject in StopTracking.
-      mExpirationTracker.AddObject(aSurface);
+      mExpirationTracker.AddObjectLocked(aSurface, aAutoLock);
     }
   }
 
-  void StopTracking(NotNull<CachedSurface*> aSurface)
+  void StopTracking(NotNull<CachedSurface*> aSurface,
+                    const StaticMutexAutoLock& aAutoLock)
   {
     CostEntry costEntry = aSurface->GetCostEntry();
 
     if (aSurface->IsLocked()) {
       MOZ_ASSERT(mLockedCost >= costEntry.GetCost(), "Costs don't balance");
       mLockedCost -= costEntry.GetCost();
       // XXX(seth): It'd be nice to use an O(log n) lookup here. This is O(n).
       MOZ_ASSERT(!mCosts.Contains(costEntry),
                  "Shouldn't have a cost entry for a locked surface");
     } else {
       if (MOZ_LIKELY(aSurface->GetExpirationState()->IsTracked())) {
-        mExpirationTracker.RemoveObject(aSurface);
+        mExpirationTracker.RemoveObjectLocked(aSurface, aAutoLock);
       } else {
         // Our call to AddObject must have failed in StartTracking; most likely
         // we're in XPCOM shutdown right now.
         NS_ASSERTION(ShutdownTracker::ShutdownHasStarted(),
                      "Not expiration-tracking an unlocked surface!");
       }
 
       DebugOnly<bool> foundInCosts = mCosts.RemoveElementSorted(costEntry);
@@ -560,16 +565,17 @@ public:
 
     mAvailableCost += costEntry.GetCost();
     MOZ_ASSERT(mAvailableCost <= mMaxCost,
                "More available cost than we started with");
   }
 
   LookupResult Lookup(const ImageKey    aImageKey,
                       const SurfaceKey& aSurfaceKey,
+                      const StaticMutexAutoLock& aAutoLock,
                       bool aMarkUsed = true)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       // No cached surfaces for this image.
       return LookupResult(MatchType::NOT_FOUND);
     }
 
@@ -582,31 +588,32 @@ public:
     if (surface->IsPlaceholder()) {
       return LookupResult(MatchType::PENDING);
     }
 
     DrawableSurface drawableSurface = surface->GetDrawableSurface();
     if (!drawableSurface) {
       // The surface was released by the operating system. Remove the cache
       // entry as well.
-      Remove(WrapNotNull(surface));
+      Remove(WrapNotNull(surface), aAutoLock);
       return LookupResult(MatchType::NOT_FOUND);
     }
 
     if (aMarkUsed) {
-      MarkUsed(WrapNotNull(surface), WrapNotNull(cache));
+      MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock);
     }
 
     MOZ_ASSERT(surface->GetSurfaceKey() == aSurfaceKey,
                "Lookup() not returning an exact match?");
     return LookupResult(Move(drawableSurface), MatchType::EXACT);
   }
 
   LookupResult LookupBestMatch(const ImageKey         aImageKey,
-                               const SurfaceKey&      aSurfaceKey)
+                               const SurfaceKey&      aSurfaceKey,
+                               const StaticMutexAutoLock& aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       // No cached surfaces for this image.
       return LookupResult(MatchType::NOT_FOUND);
     }
 
     // Repeatedly look up the best match, trying again if the resulting surface
@@ -627,166 +634,168 @@ public:
 
       drawableSurface = surface->GetDrawableSurface();
       if (drawableSurface) {
         break;
       }
 
       // The surface was released by the operating system. Remove the cache
       // entry as well.
-      Remove(WrapNotNull(surface));
+      Remove(WrapNotNull(surface), aAutoLock);
     }
 
     MOZ_ASSERT_IF(matchType == MatchType::EXACT,
                   surface->GetSurfaceKey() == aSurfaceKey);
     MOZ_ASSERT_IF(matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
                   matchType == MatchType::SUBSTITUTE_BECAUSE_PENDING,
       surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() &&
       surface->GetSurfaceKey().Playback() == aSurfaceKey.Playback() &&
       surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
 
     if (matchType == MatchType::EXACT) {
-      MarkUsed(WrapNotNull(surface), WrapNotNull(cache));
+      MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock);
     }
 
     return LookupResult(Move(drawableSurface), matchType);
   }
 
   bool CanHold(const Cost aCost) const
   {
     return aCost <= mMaxCost;
   }
 
   size_t MaximumCapacity() const
   {
     return size_t(mMaxCost);
   }
 
-  void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider)
+  void SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider,
+                        const StaticMutexAutoLock& aAutoLock)
   {
     if (!aProvider->Availability().IsPlaceholder()) {
       MOZ_ASSERT_UNREACHABLE("Calling SurfaceAvailable on non-placeholder");
       return;
     }
 
     // Reinsert the provider, requesting that Insert() mark it available. This
     // may or may not succeed, depending on whether some other decoder has
     // beaten us to the punch and inserted a non-placeholder version of this
     // surface first, but it's fine either way.
     // XXX(seth): This could be implemented more efficiently; we should be able
     // to just update our data structures without reinserting.
-    Insert(aProvider, /* aSetAvailable = */ true);
+    Insert(aProvider, /* aSetAvailable = */ true, aAutoLock);
   }
 
   void LockImage(const ImageKey aImageKey)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       cache = new ImageSurfaceCache;
       mImageCaches.Put(aImageKey, cache);
     }
 
     cache->SetLocked(true);
 
     // We don't relock this image's existing surfaces right away; instead, the
     // image should arrange for Lookup() to touch them if they are still useful.
   }
 
-  void UnlockImage(const ImageKey aImageKey)
+  void UnlockImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache || !cache->IsLocked()) {
       return;  // Already unlocked.
     }
 
     cache->SetLocked(false);
-    DoUnlockSurfaces(WrapNotNull(cache), /* aStaticOnly = */ false);
+    DoUnlockSurfaces(WrapNotNull(cache), /* aStaticOnly = */ false, aAutoLock);
   }
 
-  void UnlockEntries(const ImageKey aImageKey)
+  void UnlockEntries(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache || !cache->IsLocked()) {
       return;  // Already unlocked.
     }
 
     // (Note that we *don't* unlock the per-image cache here; that's the
     // difference between this and UnlockImage.)
     DoUnlockSurfaces(WrapNotNull(cache),
-      /* aStaticOnly = */ !gfxPrefs::ImageMemAnimatedDiscardable());
+      /* aStaticOnly = */ !gfxPrefs::ImageMemAnimatedDiscardable(), aAutoLock);
   }
 
-  void RemoveImage(const ImageKey aImageKey)
+  void RemoveImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No cached surfaces for this image, so nothing to do.
     }
 
     // Discard all of the cached surfaces for this image.
     // XXX(seth): This is O(n^2) since for each item in the cache we are
     // removing an element from the costs array. Since n is expected to be
     // small, performance should be good, but if usage patterns change we should
     // change the data structure used for mCosts.
     for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
-      StopTracking(WrapNotNull(iter.UserData()));
+      StopTracking(WrapNotNull(iter.UserData()), aAutoLock);
     }
 
     // The per-image cache isn't needed anymore, so remove it as well.
     // This implicitly unlocks the image if it was locked.
     mImageCaches.Remove(aImageKey);
   }
 
-  void DiscardAll()
+  void DiscardAll(const StaticMutexAutoLock& aAutoLock)
   {
     // Remove in order of cost because mCosts is an array and the other data
     // structures are all hash tables. Note that locked surfaces are not
     // removed, since they aren't present in mCosts.
     while (!mCosts.IsEmpty()) {
-      Remove(mCosts.LastElement().Surface());
+      Remove(mCosts.LastElement().Surface(), aAutoLock);
     }
   }
 
-  void DiscardForMemoryPressure()
+  void DiscardForMemoryPressure(const StaticMutexAutoLock& aAutoLock)
   {
     // Compute our discardable cost. Since locked surfaces aren't discardable,
     // we exclude them.
     const Cost discardableCost = (mMaxCost - mAvailableCost) - mLockedCost;
     MOZ_ASSERT(discardableCost <= mMaxCost, "Discardable cost doesn't add up");
 
     // Our target is to raise our available cost by (1 / mDiscardFactor) of our
     // discardable cost - in other words, we want to end up with about
     // (discardableCost / mDiscardFactor) fewer bytes stored in the surface
     // cache after we're done.
     const Cost targetCost = mAvailableCost + (discardableCost / mDiscardFactor);
 
     if (targetCost > mMaxCost - mLockedCost) {
       MOZ_ASSERT_UNREACHABLE("Target cost is more than we can discard");
-      DiscardAll();
+      DiscardAll(aAutoLock);
       return;
     }
 
     // Discard surfaces until we've reduced our cost to our target cost.
     while (mAvailableCost < targetCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and still not done");
-      Remove(mCosts.LastElement().Surface());
+      Remove(mCosts.LastElement().Surface(), aAutoLock);
     }
   }
 
-  void LockSurface(NotNull<CachedSurface*> aSurface)
+  void LockSurface(NotNull<CachedSurface*> aSurface,
+                   const StaticMutexAutoLock& aAutoLock)
   {
     if (aSurface->IsPlaceholder() || aSurface->IsLocked()) {
       return;
     }
 
-    StopTracking(aSurface);
+    StopTracking(aSurface, aAutoLock);
 
     // Lock the surface. This can fail.
     aSurface->SetLocked(true);
-    StartTracking(aSurface);
+    StartTracking(aSurface, aAutoLock);
   }
 
   NS_IMETHOD
   CollectReports(nsIHandleReportCallback* aHandleReport,
                  nsISupports*             aData,
                  bool                     aAnonymize) override
   {
     StaticMutexAutoLock lock(sInstanceMutex);
@@ -843,86 +852,95 @@ private:
   // means that the result would be meaningless: another thread could insert a
   // surface or lock an image at any time.
   bool CanHoldAfterDiscarding(const Cost aCost) const
   {
     return aCost <= mMaxCost - mLockedCost;
   }
 
   void MarkUsed(NotNull<CachedSurface*> aSurface,
-                NotNull<ImageSurfaceCache*> aCache)
+                NotNull<ImageSurfaceCache*> aCache,
+                const StaticMutexAutoLock& aAutoLock)
   {
     if (aCache->IsLocked()) {
-      LockSurface(aSurface);
+      LockSurface(aSurface, aAutoLock);
     } else {
-      mExpirationTracker.MarkUsed(aSurface);
+      mExpirationTracker.MarkUsedLocked(aSurface, aAutoLock);
     }
   }
 
-  void DoUnlockSurfaces(NotNull<ImageSurfaceCache*> aCache, bool aStaticOnly)
+  void DoUnlockSurfaces(NotNull<ImageSurfaceCache*> aCache, bool aStaticOnly,
+                        const StaticMutexAutoLock& aAutoLock)
   {
     // Unlock all the surfaces the per-image cache is holding.
     for (auto iter = aCache->ConstIter(); !iter.Done(); iter.Next()) {
       NotNull<CachedSurface*> surface = WrapNotNull(iter.UserData());
       if (surface->IsPlaceholder() || !surface->IsLocked()) {
         continue;
       }
       if (aStaticOnly && surface->GetSurfaceKey().Playback() != PlaybackType::eStatic) {
         continue;
       }
-      StopTracking(surface);
+      StopTracking(surface, aAutoLock);
       surface->SetLocked(false);
-      StartTracking(surface);
+      StartTracking(surface, aAutoLock);
     }
   }
 
   void RemoveEntry(const ImageKey    aImageKey,
-                   const SurfaceKey& aSurfaceKey)
+                   const SurfaceKey& aSurfaceKey,
+                   const StaticMutexAutoLock& aAutoLock)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No cached surfaces for this image.
     }
 
     RefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
     if (!surface) {
       return;  // Lookup in the per-image cache missed.
     }
 
-    Remove(WrapNotNull(surface));
+    Remove(WrapNotNull(surface), aAutoLock);
   }
 
-  struct SurfaceTracker : public nsExpirationTracker<CachedSurface, 2>
+  struct SurfaceTracker : public ExpirationTrackerImpl<CachedSurface, 2,
+                                                       StaticMutex,
+                                                       StaticMutexAutoLock>
   {
     explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
-      : nsExpirationTracker<CachedSurface, 2>(aSurfaceCacheExpirationTimeMS,
-                                              "SurfaceTracker")
+      : ExpirationTrackerImpl<CachedSurface, 2,
+                              StaticMutex, StaticMutexAutoLock>(
+          aSurfaceCacheExpirationTimeMS, "SurfaceTracker")
     { }
 
   protected:
-    virtual void NotifyExpired(CachedSurface* aSurface) override
+    void NotifyExpiredLocked(CachedSurface* aSurface,
+                             const StaticMutexAutoLock& aAutoLock) override
     {
-      StaticMutexAutoLock lock(sInstanceMutex);
-      if (sInstance) {
-        sInstance->Remove(WrapNotNull(aSurface));
-      }
+      sInstance->Remove(WrapNotNull(aSurface), aAutoLock);
+    }
+
+    StaticMutex& GetMutex() override
+    {
+      return sInstanceMutex;
     }
   };
 
   struct MemoryPressureObserver : public nsIObserver
   {
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD Observe(nsISupports*,
                        const char* aTopic,
                        const char16_t*) override
     {
       StaticMutexAutoLock lock(sInstanceMutex);
       if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
-        sInstance->DiscardForMemoryPressure();
+        sInstance->DiscardForMemoryPressure(lock);
       }
       return NS_OK;
     }
 
   private:
     virtual ~MemoryPressureObserver() { }
   };
 
@@ -1014,40 +1032,40 @@ SurfaceCache::Shutdown()
 SurfaceCache::Lookup(const ImageKey         aImageKey,
                      const SurfaceKey&      aSurfaceKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return LookupResult(MatchType::NOT_FOUND);
   }
 
-  return sInstance->Lookup(aImageKey, aSurfaceKey);
+  return sInstance->Lookup(aImageKey, aSurfaceKey, lock);
 }
 
 /* static */ LookupResult
 SurfaceCache::LookupBestMatch(const ImageKey         aImageKey,
                               const SurfaceKey&      aSurfaceKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return LookupResult(MatchType::NOT_FOUND);
   }
 
-  return sInstance->LookupBestMatch(aImageKey, aSurfaceKey);
+  return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, lock);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return InsertOutcome::FAILURE;
   }
 
-  return sInstance->Insert(aProvider, /* aSetAvailable = */ false);
+  return sInstance->Insert(aProvider, /* aSetAvailable = */ false, lock);
 }
 
 /* static */ bool
 SurfaceCache::CanHold(const IntSize& aSize, uint32_t aBytesPerPixel /* = 4 */)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return false;
@@ -1071,61 +1089,61 @@ SurfaceCache::CanHold(size_t aSize)
 /* static */ void
 SurfaceCache::SurfaceAvailable(NotNull<ISurfaceProvider*> aProvider)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (!sInstance) {
     return;
   }
 
-  sInstance->SurfaceAvailable(aProvider);
+  sInstance->SurfaceAvailable(aProvider, lock);
 }
 
 /* static */ void
 SurfaceCache::LockImage(const ImageKey aImageKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (sInstance) {
     return sInstance->LockImage(aImageKey);
   }
 }
 
 /* static */ void
 SurfaceCache::UnlockImage(const ImageKey aImageKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (sInstance) {
-    return sInstance->UnlockImage(aImageKey);
+    return sInstance->UnlockImage(aImageKey, lock);
   }
 }
 
 /* static */ void
 SurfaceCache::UnlockEntries(const ImageKey aImageKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (sInstance) {
-    return sInstance->UnlockEntries(aImageKey);
+    return sInstance->UnlockEntries(aImageKey, lock);
   }
 }
 
 /* static */ void
 SurfaceCache::RemoveImage(const ImageKey aImageKey)
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (sInstance) {
-    sInstance->RemoveImage(aImageKey);
+    sInstance->RemoveImage(aImageKey, lock);
   }
 }
 
 /* static */ void
 SurfaceCache::DiscardAll()
 {
   StaticMutexAutoLock lock(sInstanceMutex);
   if (sInstance) {
-    sInstance->DiscardAll();
+    sInstance->DiscardAll(lock);
   }
 }
 
 /* static */ void
 SurfaceCache::CollectSizeOfSurfaces(const ImageKey                  aImageKey,
                                     nsTArray<SurfaceMemoryCounter>& aCounters,
                                     MallocSizeOf                    aMallocSizeOf)
 {
--- a/image/decoders/icon/gtk/nsIconChannel.cpp
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -25,16 +25,17 @@
 
 #include "nsNetUtil.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIStringStream.h"
 #include "nsServiceManagerUtils.h"
 #include "NullPrincipal.h"
 #include "nsIURL.h"
 #include "prlink.h"
+#include "gfxPlatform.h"
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIRequest,
                   nsIChannel)
 
 static nsresult
 moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI* aURI,
                           nsIChannel** aChannel)
@@ -303,16 +304,20 @@ nsIconChannel::InitWithGIO(nsIMozIconURI
 #endif // MOZ_ENABLE_GIO
 
 nsresult
 nsIconChannel::Init(nsIURI* aURI)
 {
   nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
   NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
 
+  if (gfxPlatform::IsHeadless()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsAutoCString stockIcon;
   iconURI->GetStockIcon(stockIcon);
   if (stockIcon.IsEmpty()) {
 #ifdef MOZ_ENABLE_GIO
     return InitWithGIO(iconURI);
 #else
     return NS_ERROR_NOT_AVAILABLE;
 #endif
--- a/ipc/chromium/src/base/sys_info_posix.cc
+++ b/ipc/chromium/src/base/sys_info_posix.cc
@@ -116,27 +116,27 @@ std::wstring SysInfo::GetEnvVar(const wc
     return L"";
   } else {
     return UTF8ToWide(value);
   }
 }
 
 // static
 std::string SysInfo::OperatingSystemName() {
-  utsname info;
+  struct utsname info;
   if (uname(&info) < 0) {
     NOTREACHED();
     return "";
   }
   return std::string(info.sysname);
 }
 
 // static
 std::string SysInfo::CPUArchitecture() {
-  utsname info;
+  struct utsname info;
   if (uname(&info) < 0) {
     NOTREACHED();
     return "";
   }
   return std::string(info.machine);
 }
 
 // static
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -3,16 +3,17 @@
 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "chrome/common/ipc_channel_posix.h"
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #if defined(OS_MACOSX)
 #include <sched.h>
 #endif
 #include <stddef.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -34,18 +35,24 @@
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/UniquePtr.h"
 
 #ifdef FUZZING
 #include "mozilla/ipc/Faulty.h"
 #endif
 
-// Work around possible OS limitations.
+// Use OS specific iovec array limit where it's possible.
+#if defined(IOV_MAX)
+static const size_t kMaxIOVecSize = IOV_MAX;
+#elif defined(ANDROID)
 static const size_t kMaxIOVecSize = 256;
+#else
+static const size_t kMaxIOVecSize = 16;
+#endif
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracerImpl.h"
 using namespace mozilla::tasktracer;
 #endif
 
 namespace IPC {
 
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -13,30 +13,34 @@
 #include "jspubtd.h"
 
 #include "vm/ArrayObject.h"
 
 namespace js {
 /* 2^32-2, inclusive */
 const uint32_t MAX_ARRAY_INDEX = 4294967294u;
 
-inline bool
+MOZ_ALWAYS_INLINE bool
 IdIsIndex(jsid id, uint32_t* indexp)
 {
     if (JSID_IS_INT(id)) {
         int32_t i = JSID_TO_INT(id);
         MOZ_ASSERT(i >= 0);
         *indexp = (uint32_t)i;
         return true;
     }
 
     if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
         return false;
 
-    return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp);
+    JSAtom* atom = JSID_TO_ATOM(id);
+    if (atom->length() == 0 || !JS7_ISDEC(atom->latin1OrTwoByteChar(0)))
+        return false;
+
+    return js::StringIsArrayIndex(atom, indexp);
 }
 
 // The methods below only create dense boxed arrays.
 
 /* Create a dense array with no capacity allocated, length set to 0. */
 extern ArrayObject * JS_FASTCALL
 NewDenseEmptyArray(JSContext* cx, HandleObject proto = nullptr,
                    NewObjectKind newKind = GenericObject);
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -451,16 +451,68 @@ NativeObject::create(JSContext* cx, js::
     else
         nobj = SetNewObjectMetadata(cx, nobj);
 
     js::gc::TraceCreateObject(nobj);
 
     return nobj;
 }
 
+MOZ_ALWAYS_INLINE bool
+NativeObject::updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan)
+{
+    MOZ_ASSERT(oldSpan != newSpan);
+
+    size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan, getClass());
+    size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan, getClass());
+
+    if (oldSpan < newSpan) {
+        if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
+            return false;
+
+        if (newSpan == oldSpan + 1)
+            initSlotUnchecked(oldSpan, UndefinedValue());
+        else
+            initializeSlotRange(oldSpan, newSpan - oldSpan);
+    } else {
+        /* Trigger write barriers on the old slots before reallocating. */
+        prepareSlotRangeForOverwrite(newSpan, oldSpan);
+        invalidateSlotRange(newSpan, oldSpan - newSpan);
+
+        if (oldCount > newCount)
+            shrinkSlots(cx, oldCount, newCount);
+    }
+
+    return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+NativeObject::setLastProperty(JSContext* cx, Shape* shape)
+{
+    MOZ_ASSERT(!inDictionaryMode());
+    MOZ_ASSERT(!shape->inDictionary());
+    MOZ_ASSERT(shape->zone() == zone());
+    MOZ_ASSERT(shape->numFixedSlots() == numFixedSlots());
+    MOZ_ASSERT(shape->getObjectClass() == getClass());
+
+    size_t oldSpan = lastProperty()->slotSpan();
+    size_t newSpan = shape->slotSpan();
+
+    if (oldSpan == newSpan) {
+        shape_ = shape;
+        return true;
+    }
+
+    if (MOZ_UNLIKELY(!updateSlotsForSpan(cx, oldSpan, newSpan)))
+        return false;
+
+    shape_ = shape;
+    return true;
+}
+
 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
 static inline PlainObject*
 CopyInitializerObject(JSContext* cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
 {
     MOZ_ASSERT(!baseobj->inDictionaryMode());
 
     gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
     allocKind = gc::GetBackgroundAllocKind(allocKind);
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -294,68 +294,16 @@ js::NativeObject::dynamicSlotsCount(uint
     if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN)
         return SLOT_CAPACITY_MIN;
 
     uint32_t slots = mozilla::RoundUpPow2(span);
     MOZ_ASSERT(slots >= span);
     return slots;
 }
 
-inline bool
-NativeObject::updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan)
-{
-    MOZ_ASSERT(oldSpan != newSpan);
-
-    size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan, getClass());
-    size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan, getClass());
-
-    if (oldSpan < newSpan) {
-        if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
-            return false;
-
-        if (newSpan == oldSpan + 1)
-            initSlotUnchecked(oldSpan, UndefinedValue());
-        else
-            initializeSlotRange(oldSpan, newSpan - oldSpan);
-    } else {
-        /* Trigger write barriers on the old slots before reallocating. */
-        prepareSlotRangeForOverwrite(newSpan, oldSpan);
-        invalidateSlotRange(newSpan, oldSpan - newSpan);
-
-        if (oldCount > newCount)
-            shrinkSlots(cx, oldCount, newCount);
-    }
-
-    return true;
-}
-
-bool
-NativeObject::setLastProperty(JSContext* cx, Shape* shape)
-{
-    MOZ_ASSERT(!inDictionaryMode());
-    MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->zone() == zone());
-    MOZ_ASSERT(shape->numFixedSlots() == numFixedSlots());
-    MOZ_ASSERT(shape->getObjectClass() == getClass());
-
-    size_t oldSpan = lastProperty()->slotSpan();
-    size_t newSpan = shape->slotSpan();
-
-    if (oldSpan == newSpan) {
-        shape_ = shape;
-        return true;
-    }
-
-    if (!updateSlotsForSpan(cx, oldSpan, newSpan))
-        return false;
-
-    shape_ = shape;
-    return true;
-}
-
 void
 NativeObject::setLastPropertyShrinkFixedSlots(Shape* shape)
 {
     MOZ_ASSERT(!inDictionaryMode());
     MOZ_ASSERT(!shape->inDictionary());
     MOZ_ASSERT(shape->zone() == zone());
     MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
     MOZ_ASSERT(shape->getObjectClass() == getClass());
@@ -1086,46 +1034,46 @@ js::NativeLookupOwnProperty<CanGC>(JSCon
                                    MutableHandle<PropertyResult> propp);
 
 template bool
 js::NativeLookupOwnProperty<NoGC>(JSContext* cx, NativeObject* const& obj, const jsid& id,
                                   FakeMutableHandle<PropertyResult> propp);
 
 /*** [[DefineOwnProperty]] ***********************************************************************/
 
-static inline bool
-CallAddPropertyHook(JSContext* cx, HandleNativeObject obj, HandleShape shape,
-                    HandleValue value)
+static MOZ_ALWAYS_INLINE bool
+CallAddPropertyHook(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value)
 {
-    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
+    JSAddPropertyOp addProperty = obj->getClass()->getAddProperty();
+    if (MOZ_UNLIKELY(addProperty)) {
         MOZ_ASSERT(!cx->helperThread());
 
-        RootedId id(cx, shape->propid());
         if (!CallJSAddPropertyOp(cx, addProperty, obj, id, value)) {
-            NativeObject::removeProperty(cx, obj, shape->propid());
+            NativeObject::removeProperty(cx, obj, id);
             return false;
         }
     }
     return true;
 }
 
-static inline bool
+static MOZ_ALWAYS_INLINE bool
 CallAddPropertyHookDense(JSContext* cx, HandleNativeObject obj, uint32_t index,
                          HandleValue value)
 {
     // Inline addProperty for array objects.
     if (obj->is<ArrayObject>()) {
         ArrayObject* arr = &obj->as<ArrayObject>();
         uint32_t length = arr->length();
         if (index >= length)
             arr->setLength(cx, index + 1);
         return true;
     }
 
-    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
+    JSAddPropertyOp addProperty = obj->getClass()->getAddProperty();
+    if (MOZ_UNLIKELY(addProperty)) {
         MOZ_ASSERT(!cx->helperThread());
 
         if (!obj->maybeCopyElementsForWrite(cx))
             return false;
 
         RootedId id(cx, INT_TO_JSID(index));
         if (!CallJSAddPropertyOp(cx, addProperty, obj, id, value)) {
             obj->setDenseElementHole(cx, index);
@@ -1223,17 +1171,20 @@ PurgeEnvironmentChainHelper(JSContext* c
 static inline bool
 PurgeEnvironmentChain(JSContext* cx, HandleObject obj, HandleId id)
 {
     if (obj->isDelegate() && obj->isNative())
         return PurgeEnvironmentChainHelper(cx, obj, id);
     return true;
 }
 
-static bool
+enum class IsAddOrChange { Add, AddOrChange };
+
+template <IsAddOrChange AddOrChange>
+static MOZ_ALWAYS_INLINE bool
 AddOrChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                     Handle<PropertyDescriptor> desc)
 {
     desc.assertComplete();
 
     if (!PurgeEnvironmentChain(cx, obj, id))
         return false;
 
@@ -1252,18 +1203,26 @@ AddOrChangeProperty(JSContext* cx, Handl
         if (edResult == DenseElementResult::Success) {
             obj->setDenseElementWithType(cx, index, desc.value());
             if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
                 return false;
             return true;
         }
     }
 
-    RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
-                                                    SHAPE_INVALID_SLOT, desc.attributes(), 0));
+    // If we know this is a new property we can call addProperty instead of
+    // the slower putProperty.
+    Shape* shape;
+    if (AddOrChange == IsAddOrChange::Add) {
+        shape = NativeObject::addProperty(cx, obj, id, desc.getter(), desc.setter(),
+                                          SHAPE_INVALID_SLOT, desc.attributes(), 0);
+    } else {
+        shape = NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
+                                          SHAPE_INVALID_SLOT, desc.attributes(), 0);
+    }
     if (!shape)
         return false;
 
     UpdateShapeTypeAndValue(cx, obj, shape, id, desc.value());
 
     // Clear any existing dense index after adding a sparse indexed property,
     // and investigate converting the object to dense indexes.
     if (JSID_IS_INT(id)) {
@@ -1277,17 +1236,17 @@ AddOrChangeProperty(JSContext* cx, Handl
         if (edResult == DenseElementResult::Failure)
             return false;
         if (edResult == DenseElementResult::Success) {
             MOZ_ASSERT(!desc.setter());
             return CallAddPropertyHookDense(cx, obj, index, desc.value());
         }
     }
 
-    return CallAddPropertyHook(cx, obj, shape, desc.value());
+    return CallAddPropertyHook(cx, obj, id, desc.value());
 }
 
 static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
 static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
 static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
 
 static bool IsAccessorDescriptor(unsigned attrs) {
     return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
@@ -1476,17 +1435,17 @@ js::NativeDefineProperty(JSContext* cx, 
     // Step 2.
     if (!prop) {
         if (!obj->nonProxyIsExtensible())
             return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
 
         // Fill in missing desc fields with defaults.
         CompletePropertyDescriptor(&desc);
 
-        if (!AddOrChangeProperty(cx, obj, id, desc))
+        if (!AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc))
             return false;
         return result.succeed();
     }
 
     // Steps 3-4. (Step 3 is a special case of step 4.) We use shapeAttrs as a
     // stand-in for shape in many places below, since shape might not be a
     // pointer to a real Shape (see IsImplicitDenseOrTypedArrayElement).
     unsigned shapeAttrs = GetPropertyAttributes(obj, prop);
@@ -1622,17 +1581,17 @@ js::NativeDefineProperty(JSContext* cx, 
             }
         } else {
             // Fill in desc.[[Get]] from shape.
             desc.setGetterObject(prop.shape()->getterObject());
         }
     }
 
     // Step 10.
-    if (!AddOrChangeProperty(cx, obj, id, desc))
+    if (!AddOrChangeProperty<IsAddOrChange::AddOrChange>(cx, obj, id, desc))
         return false;
     return result.succeed();
 }
 
 bool
 js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                          HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
                          ObjectOpResult& result)
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -444,17 +444,17 @@ class NativeObject : public ShapedObject
     }
 
     bool isSharedMemory() const {
         return getElementsHeader()->isSharedMemory();
     }
 
     // Update the last property, keeping the number of allocated slots in sync
     // with the object's new slot span.
-    bool setLastProperty(JSContext* cx, Shape* shape);
+    MOZ_ALWAYS_INLINE bool setLastProperty(JSContext* cx, Shape* shape);
 
     // As for setLastProperty(), but allows the number of fixed slots to
     // change. This can only be used when fixed slots are being erased from the
     // object, and only when the object will not require dynamic slots to cover
     // the new properties.
     void setLastPropertyShrinkFixedSlots(Shape* shape);
 
     // As for setLastProperty(), but changes the class associated with the
@@ -730,18 +730,16 @@ class NativeObject : public ShapedObject
      * FIXME: bug 593129 -- slot allocation should be done by object methods
      * after calling object-parameter-free shape methods, avoiding coupling
      * logic across the object vs. shape module wall.
      */
     static bool allocSlot(JSContext* cx, HandleNativeObject obj, uint32_t* slotp);
     void freeSlot(JSContext* cx, uint32_t slot);
 
   private:
-    static Shape* getChildPropertyOnDictionary(JSContext* cx, HandleNativeObject obj,
-                                               HandleShape parent, MutableHandle<StackShape> child);
     static Shape* getChildProperty(JSContext* cx, HandleNativeObject obj,
                                    HandleShape parent, MutableHandle<StackShape> child);
 
   public:
     /* Add a property whose id is not yet in this scope. */
     static Shape* addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                               JSGetterOp getter, JSSetterOp setter,
                               uint32_t slot, unsigned attrs, unsigned flags,
@@ -876,17 +874,17 @@ class NativeObject : public ShapedObject
         getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
     }
 
     // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
     // size classes will give an object.
     static const uint32_t MAX_FIXED_SLOTS = 16;
 
   protected:
-    inline bool updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan);
+    MOZ_ALWAYS_INLINE bool updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan);
 
   private:
     void prepareElementRangeForOverwrite(size_t start, size_t end) {
         MOZ_ASSERT(end <= getDenseInitializedLength());
         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
         for (size_t i = start; i < end; i++)
             elements_[i].HeapSlot::~HeapSlot();
     }
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -286,18 +286,19 @@ Shape::replaceLastProperty(JSContext* cx
 }
 
 /*
  * Get or create a property-tree or dictionary child property of |parent|,
  * which must be lastProperty() if inDictionaryMode(), else parent must be
  * one of lastProperty() or lastProperty()->parent.
  */
 /* static */ Shape*
-NativeObject::getChildPropertyOnDictionary(JSContext* cx, HandleNativeObject obj,
-                                           HandleShape parent, MutableHandle<StackShape> child)
+NativeObject::getChildProperty(JSContext* cx,
+                               HandleNativeObject obj, HandleShape parent,
+                               MutableHandle<StackShape> child)
 {
     /*
      * Shared properties have no slot, but slot_ will reflect that of parent.
      * Unshared properties allocate a slot here but may lose it due to a
      * JS_ClearScope call.
      */
     if (!child.hasSlot()) {
         child.setSlot(parent->maybeSlot());
@@ -320,52 +321,38 @@ NativeObject::getChildPropertyOnDictiona
             MOZ_ASSERT(obj->inDictionaryMode() ||
                        parent->hasMissingSlot() ||
                        child.slot() == parent->maybeSlot() + 1 ||
                        (parent->maybeSlot() + 1 < JSSLOT_FREE(obj->getClass()) &&
                         child.slot() == JSSLOT_FREE(obj->getClass())));
         }
     }
 
-    RootedShape shape(cx);
-
     if (obj->inDictionaryMode()) {
         MOZ_ASSERT(parent == obj->lastProperty());
-        shape = child.isAccessorShape() ? Allocate<AccessorShape>(cx) : Allocate<Shape>(cx);
+        Shape* shape = child.isAccessorShape() ? Allocate<AccessorShape>(cx) : Allocate<Shape>(cx);
         if (!shape)
             return nullptr;
         if (child.hasSlot() && child.slot() >= obj->lastProperty()->base()->slotSpan()) {
             if (!obj->setSlotSpan(cx, child.slot() + 1)) {
                 new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
                 return nullptr;
             }
         }
         shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
+        return shape;
     }
 
-    return shape;
-}
-
-/* static */ Shape*
-NativeObject::getChildProperty(JSContext* cx,
-                               HandleNativeObject obj, HandleShape parent,
-                               MutableHandle<StackShape> child)
-{
-    Shape* shape = getChildPropertyOnDictionary(cx, obj, parent, child);
-
-    if (!obj->inDictionaryMode()) {
-        shape = cx->zone()->propertyTree().getChild(cx, parent, child);
-        if (!shape)
-            return nullptr;
-        //MOZ_ASSERT(shape->parent == parent);
-        //MOZ_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
-        if (!obj->setLastProperty(cx, shape))
-            return nullptr;
-    }
-
+    Shape* shape = cx->zone()->propertyTree().getChild(cx, parent, child);
+    if (!shape)
+        return nullptr;
+    //MOZ_ASSERT(shape->parent == parent);
+    //MOZ_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
+    if (!obj->setLastProperty(cx, shape))
+        return nullptr;
     return shape;
 }
 
 /* static */ bool
 js::NativeObject::toDictionaryMode(JSContext* cx, HandleNativeObject obj)
 {
     MOZ_ASSERT(!obj->inDictionaryMode());
     MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
@@ -426,16 +413,17 @@ js::NativeObject::toDictionaryMode(JSCon
 NativeObject::addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                           GetterOp getter, SetterOp setter, uint32_t slot, unsigned attrs,
                           unsigned flags, bool allowDictionary)
 {
     MOZ_ASSERT(!JSID_IS_VOID(id));
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
     MOZ_ASSERT(obj->nonProxyIsExtensible());
+    MOZ_ASSERT(!obj->containsPure(id));
 
     AutoKeepShapeTables keep(cx);
     ShapeTable::Entry* entry = nullptr;
     if (obj->inDictionaryMode()) {
         ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
         if (!table)
             return nullptr;
         entry = &table->search<MaybeAdding::Adding>(id, keep);
--- a/js/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -291,33 +291,49 @@ this.XPCOMUtils = {
    * @param aObject
    *        The object to define the lazy getter on.
    * @param aName
    *        The name of the getter property to define on aObject.
    * @param aPreference
    *        The name of the preference to read.
    * @param aDefaultValue
    *        The default value to use, if the preference is not defined.
+   * @param aOnUpdate
+   *        A function to call upon update. Receives as arguments
+   *         `(aPreference, previousValue, newValue)`
    */
   defineLazyPreferenceGetter: function XPCU_defineLazyPreferenceGetter(
-                                   aObject, aName, aPreference, aDefaultValue = null)
+                                   aObject, aName, aPreference,
+                                   aDefaultValue = null,
+                                   aOnUpdate = null)
   {
     // Note: We need to keep a reference to this observer alive as long
     // as aObject is alive. This means that all of our getters need to
     // explicitly close over the variable that holds the object, and we
     // cannot define a value in place of a getter after we read the
     // preference.
     let observer = {
       QueryInterface: this.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
       value: undefined,
 
       observe(subject, topic, data) {
         if (data == aPreference) {
-          this.value = undefined;
+          if (aOnUpdate) {
+            let previous = this.value;
+
+            // Fetch and cache value.
+            this.value = undefined;
+            let latest = lazyGetter();
+            aOnUpdate(data, previous, latest);
+          } else {
+
+            // Empty cache, next call to the getter will cause refetch.
+            this.value = undefined;
+          }
         }
       },
     }
 
     let defineGetter = get => {
       Object.defineProperty(aObject, aName, {
         configurable: true,
         enumerable: true,
--- a/layout/base/FramePropertyTable.cpp
+++ b/layout/base/FramePropertyTable.cpp
@@ -8,25 +8,26 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoStyleSet.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 void
 FramePropertyTable::SetInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, void* aValue)
+  nsIFrame* aFrame, UntypedDescriptor aProperty, void* aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(aFrame, "Null frame?");
   NS_ASSERTION(aProperty, "Null property?");
 
   if (mLastFrame != aFrame || !mLastEntry) {
     mLastFrame = aFrame;
     mLastEntry = mEntries.PutEntry(aFrame);
+    aFrame->AddStateBits(NS_FRAME_HAS_PROPERTIES);
   }
   Entry* entry = mLastEntry;
 
   if (!entry->mProp.IsArray()) {
     if (!entry->mProp.mProperty) {
       // Empty entry, so we can just store our property in the empty slot
       entry->mProp.mProperty = aProperty;
       entry->mProp.mValue = aValue;
@@ -58,35 +59,42 @@ FramePropertyTable::SetInternal(
     return;
   }
 
   array->AppendElement(PropertyValue(aProperty, aValue));
 }
 
 void*
 FramePropertyTable::GetInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
+  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck,
+  bool* aFoundResult)
 {
   NS_ASSERTION(aFrame, "Null frame?");
   NS_ASSERTION(aProperty, "Null property?");
 
   if (aFoundResult) {
     *aFoundResult = false;
   }
 
+  if (!aSkipBitCheck && !(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
+    return nullptr;
+  }
+
   // We can end up here during parallel style traversal, in which case the main
   // thread is blocked. Reading from the cache is fine on any thread, but we
   // only want to write to it in the main-thread case.
   bool cacheHit = mLastFrame == aFrame;
   Entry* entry = cacheHit ? mLastEntry : mEntries.GetEntry(aFrame);
   if (!cacheHit && !ServoStyleSet::IsInServoTraversal()) {
     mLastFrame = aFrame;
     mLastEntry = entry;
   }
 
+  MOZ_ASSERT(entry || aSkipBitCheck,
+             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
   if (!entry)
     return nullptr;
 
   if (entry->mProp.mProperty == aProperty) {
     if (aFoundResult) {
       *aFoundResult = true;
     }
     return entry->mProp.mValue;
@@ -106,41 +114,49 @@ FramePropertyTable::GetInternal(
     *aFoundResult = true;
   }
 
   return array->ElementAt(index).mValue;
 }
 
 void*
 FramePropertyTable::RemoveInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
+  nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck,
+  bool* aFoundResult)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(aFrame, "Null frame?");
   NS_ASSERTION(aProperty, "Null property?");
 
   if (aFoundResult) {
     *aFoundResult = false;
   }
 
+  if (!aSkipBitCheck && !(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
+    return nullptr;
+  }
+
   if (mLastFrame != aFrame) {
     mLastFrame = aFrame;
     mLastEntry = mEntries.GetEntry(aFrame);
   }
   Entry* entry = mLastEntry;
+  MOZ_ASSERT(entry || aSkipBitCheck,
+             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
   if (!entry)
     return nullptr;
 
   if (entry->mProp.mProperty == aProperty) {
     // There's only one entry and it's the one we want
     void* value = entry->mProp.mValue;
 
     // Here it's ok to use RemoveEntry() -- which may resize mEntries --
     // because we null mLastEntry at the same time.
     mEntries.RemoveEntry(entry);
+    aFrame->RemoveStateBits(NS_FRAME_HAS_PROPERTIES);
     mLastEntry = nullptr;
     if (aFoundResult) {
       *aFoundResult = true;
     }
     return value;
   }
   if (!entry->mProp.IsArray()) {
     // There's just one property and it's not the one we want, bail
@@ -171,24 +187,24 @@ FramePropertyTable::RemoveInternal(
     entry->mProp = pv;
   }
   
   return result;
 }
 
 void
 FramePropertyTable::DeleteInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty)
+  nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(aFrame, "Null frame?");
   NS_ASSERTION(aProperty, "Null property?");
 
   bool found;
-  void* v = RemoveInternal(aFrame, aProperty, &found);
+  void* v = RemoveInternal(aFrame, aProperty, aSkipBitCheck, &found);
   if (found) {
     PropertyValue pv(aProperty, v);
     pv.DestroyValueFor(aFrame);
   }
 }
 
 /* static */ void
 FramePropertyTable::DeleteAllForEntry(Entry* aEntry)
@@ -201,36 +217,44 @@ FramePropertyTable::DeleteAllForEntry(En
   nsTArray<PropertyValue>* array = aEntry->mProp.ToArray();
   for (uint32_t i = 0; i < array->Length(); ++i) {
     array->ElementAt(i).DestroyValueFor(aEntry->GetKey());
   }
   array->~nsTArray<PropertyValue>();
 }
 
 void
-FramePropertyTable::DeleteAllFor(const nsIFrame* aFrame)
+FramePropertyTable::DeleteAllFor(nsIFrame* aFrame)
 {
   NS_ASSERTION(aFrame, "Null frame?");
 
+  if (!(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
+    return;
+  }
+
   Entry* entry = mEntries.GetEntry(aFrame);
+  MOZ_ASSERT(entry,
+             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
   if (!entry)
     return;
 
   if (mLastFrame == aFrame) {
     // Flush cache. We assume DeleteAllForEntry will be called before
     // a frame is destroyed.
     mLastFrame = nullptr;
     mLastEntry = nullptr;
   }
 
   DeleteAllForEntry(entry);
 
   // mLastEntry points into mEntries, so we use RawRemoveEntry() which will not
   // resize mEntries.
   mEntries.RawRemoveEntry(entry);
+
+  // Don't bother unsetting NS_FRAME_HAS_PROPERTIES, since aFrame is going away
 }
 
 void
 FramePropertyTable::DeleteAll()
 {
   mLastFrame = nullptr;
   mLastEntry = nullptr;
 
--- a/layout/base/FramePropertyTable.h
+++ b/layout/base/FramePropertyTable.h
@@ -160,17 +160,17 @@ public:
 
   /**
    * Set a property value on a frame. This requires one hashtable
    * lookup (using the frame as the key) and a linear search through
    * the properties of that frame. Any existing value for the property
    * is destroyed.
    */
   template<typename T>
-  void Set(const nsIFrame* aFrame, Descriptor<T> aProperty,
+  void Set(nsIFrame* aFrame, Descriptor<T> aProperty,
            PropertyType<T> aValue)
   {
     void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
     SetInternal(aFrame, aProperty, ptr);
   }
 
   /**
    * @return true if @aProperty is set for @aFrame. This requires one hashtable
@@ -183,22 +183,35 @@ public:
    * property is set. Legitimate non-assertion uses include:
    *
    *   - Checking if a frame property is set in cases where that's all we want
    *     to know (i.e., we don't intend to read the actual value or remove the
    *     property).
    *
    *   - Calling Has() before Set() in cases where we don't want to overwrite
    *     an existing value for the frame property.
+   *
+   * The HasSkippingBitCheck variant doesn't test NS_FRAME_HAS_PROPERTIES
+   * on aFrame, so it is safe to call after aFrame has been destroyed as
+   * long as, since that destruction happened, it isn't possible for a
+   * new frame to have been created and the same property added.
    */
   template<typename T>
   bool Has(const nsIFrame* aFrame, Descriptor<T> aProperty)
   {
     bool foundResult = false;
-    mozilla::Unused << GetInternal(aFrame, aProperty, &foundResult);
+    mozilla::Unused << GetInternal(aFrame, aProperty, false, &foundResult);
+    return foundResult;
+  }
+
+  template<typename T>
+  bool HasSkippingBitCheck(const nsIFrame* aFrame, Descriptor<T> aProperty)
+  {
+    bool foundResult = false;
+    mozilla::Unused << GetInternal(aFrame, aProperty, true, &foundResult);
     return foundResult;
   }
 
   /**
    * Get a property value for a frame. This requires one hashtable
    * lookup (using the frame as the key) and a linear search through
    * the properties of that frame. If the frame has no such property,
    * returns zero-filled result, which means null for pointers and
@@ -207,72 +220,85 @@ public:
    * the frame has a value for the property. This lets callers
    * disambiguate a null result, which can mean 'no such property' or
    * 'property value is null'.
    */
   template<typename T>
   PropertyType<T> Get(const nsIFrame* aFrame, Descriptor<T> aProperty,
                       bool* aFoundResult = nullptr)
   {
-    void* ptr = GetInternal(aFrame, aProperty, aFoundResult);
+    void* ptr = GetInternal(aFrame, aProperty, false, aFoundResult);
     return ReinterpretHelper<T>::FromPointer(ptr);
   }
   /**
    * Remove a property value for a frame. This requires one hashtable
    * lookup (using the frame as the key) and a linear search through
    * the properties of that frame. The old property value is returned
    * (and not destroyed). If the frame has no such property,
    * returns zero-filled result, which means null for pointers and
    * zero for integers and floating point types.
    * @param aFoundResult if non-null, receives a value 'true' iff
    * the frame had a value for the property. This lets callers
    * disambiguate a null result, which can mean 'no such property' or
    * 'property value is null'.
    */
   template<typename T>
-  PropertyType<T> Remove(const nsIFrame* aFrame, Descriptor<T> aProperty,
+  PropertyType<T> Remove(nsIFrame* aFrame, Descriptor<T> aProperty,
                          bool* aFoundResult = nullptr)
   {
-    void* ptr = RemoveInternal(aFrame, aProperty, aFoundResult);
+    void* ptr = RemoveInternal(aFrame, aProperty, false, aFoundResult);
     return ReinterpretHelper<T>::FromPointer(ptr);
   }
   /**
    * Remove and destroy a property value for a frame. This requires one
    * hashtable lookup (using the frame as the key) and a linear search
    * through the properties of that frame. If the frame has no such
    * property, nothing happens.
+   *
+   * The DeleteSkippingBitCheck variant doesn't test
+   * NS_FRAME_HAS_PROPERTIES on aFrame, so it is safe to call after
+   * aFrame has been destroyed as long as, since that destruction
+   * happened, it isn't possible for a new frame to have been created
+   * and the same property added.
    */
   template<typename T>
-  void Delete(const nsIFrame* aFrame, Descriptor<T> aProperty)
+  void Delete(nsIFrame* aFrame, Descriptor<T> aProperty)
   {
-    DeleteInternal(aFrame, aProperty);
+    DeleteInternal(aFrame, aProperty, false);
+  }
+
+  template<typename T>
+  void DeleteSkippingBitCheck(nsIFrame* aFrame, Descriptor<T> aProperty)
+  {
+    DeleteInternal(aFrame, aProperty, true);
   }
   /**
    * Remove and destroy all property values for a frame. This requires one
    * hashtable lookup (using the frame as the key).
    */
-  void DeleteAllFor(const nsIFrame* aFrame);
+  void DeleteAllFor(nsIFrame* aFrame);
   /**
    * Remove and destroy all property values for all frames.
    */
   void DeleteAll();
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
-  void SetInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
+  void SetInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
                    void* aValue);
 
   void* GetInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                    bool* aFoundResult);
+                    bool aSkipBitCheck, bool* aFoundResult);
 
-  void* RemoveInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                       bool* aFoundResult);
+  void* RemoveInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
+                       bool aSkipBitCheck, bool* aFoundResult);
 
-  void DeleteInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty);
+  void DeleteInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
+                      bool aSkipBitCheck);
 
   template<typename T>
   struct ReinterpretHelper
   {
     static_assert(sizeof(PropertyType<T>) <= sizeof(void*),
                   "size of the value must never be larger than a pointer");
 
     static void* ToPointer(PropertyType<T> aValue)
@@ -387,56 +413,83 @@ protected:
   // not triggering a resize of mEntries, e.g. use RawRemoveEntry() instead of
   // RemoveEntry() in some places.
   nsTHashtable<Entry> mEntries;
   const nsIFrame* mLastFrame;
   Entry* mLastEntry;
 };
 
 /**
- * This class encapsulates the properties of a frame.
+ * The FrameProperties/ConstFrameProperties class encapsulates the
+ * properties of a frame.
+ *
+ * However, since frame properties are like member variables, we have
+ * different versions for whether the frame is |const|, sharing a common
+ * base class.
+ *
+ * CVnsIFrame is either |nsIFrame| or |const nsIFrame|.
  */
-class FrameProperties {
+template<class CVnsIFrame>
+class FramePropertiesBase {
 public:
   template<typename T> using Descriptor = FramePropertyTable::Descriptor<T>;
   template<typename T> using PropertyType = FramePropertyTable::PropertyType<T>;
 
-  FrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame)
-    : mTable(aTable), mFrame(aFrame) {}
-
-  template<typename T>
-  void Set(Descriptor<T> aProperty, PropertyType<T> aValue) const
-  {
-    mTable->Set(mFrame, aProperty, aValue);
-  }
-
   template<typename T>
   bool Has(Descriptor<T> aProperty) const
   {
     return mTable->Has(mFrame, aProperty);
   }
 
   template<typename T>
   PropertyType<T> Get(Descriptor<T> aProperty,
                       bool* aFoundResult = nullptr) const
   {
     return mTable->Get(mFrame, aProperty, aFoundResult);
   }
+
+protected:
+  FramePropertiesBase(FramePropertyTable* aTable, CVnsIFrame* aFrame)
+    : mTable(aTable), mFrame(aFrame) {}
+
+  FramePropertyTable* const mTable;
+  CVnsIFrame* const mFrame;
+};
+
+class ConstFrameProperties : public FramePropertiesBase<const nsIFrame> {
+public:
+  ConstFrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame)
+    : FramePropertiesBase(aTable, aFrame)
+  {
+  }
+};
+
+class FrameProperties : public FramePropertiesBase<nsIFrame> {
+public:
+  FrameProperties(FramePropertyTable* aTable, nsIFrame* aFrame)
+    : FramePropertiesBase(aTable, aFrame)
+  {
+  }
+
+  template<typename T>
+  void Set(Descriptor<T> aProperty, PropertyType<T> aValue) const
+  {
+    mTable->Set(mFrame, aProperty, aValue);
+  }
+
   template<typename T>
   PropertyType<T> Remove(Descriptor<T> aProperty,
                          bool* aFoundResult = nullptr) const
   {
     return mTable->Remove(mFrame, aProperty, aFoundResult);
   }
+
   template<typename T>
-  void Delete(Descriptor<T> aProperty)
+  void Delete(Descriptor<T> aProperty) const
   {
     mTable->Delete(mFrame, aProperty);
   }
 
-private:
-  FramePropertyTable* mTable;
-  const nsIFrame* mFrame;
 };
 
 } // namespace mozilla
 
 #endif /* FRAMEPROPERTYTABLE_H_ */
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -5998,18 +5998,16 @@ PresShell::MarkFramesInSubtreeApproximat
     }
     return;
   }
 
   nsRect rect = aRect;
 
   nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
   if (scrollFrame) {
-    scrollFrame->NotifyApproximateFrameVisibilityUpdate();
-
     bool ignoreDisplayPort = false;
     if (nsLayoutUtils::IsMissingDisplayPortBaseRect(aFrame->GetContent())) {
       // We can properly set the base rect for root scroll frames on top level
       // and root content documents. Otherwise the base rect we compute might
       // be way too big without the limiting that
       // ScrollFrameHelper::DecideScrollableLayer does, so we just ignore the
       // displayport in that case.
       nsPresContext* pc = aFrame->PresContext();
@@ -6018,16 +6016,18 @@ PresShell::MarkFramesInSubtreeApproximat
         nsRect baseRect =
           nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame));
         nsLayoutUtils::SetDisplayPortBase(aFrame->GetContent(), baseRect);
       } else {
         ignoreDisplayPort = true;
       }
     }
 
+    scrollFrame->NotifyApproximateFrameVisibilityUpdate(ignoreDisplayPort);
+
     nsRect displayPort;
     bool usingDisplayport = !ignoreDisplayPort &&
       nsLayoutUtils::GetDisplayPortForVisibilityTesting(
         aFrame->GetContent(), &displayPort, RelativeTo::ScrollFrame);
     if (usingDisplayport) {
       rect = displayPort;
     } else {
       rect = rect.Intersect(scrollFrame->GetScrollPortRect());
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1391,28 +1391,33 @@ RestyleManager::ProcessRestyledFrames(ns
     for (size_t j = lazyRangeStart; j < i; ++j) {
       MOZ_ASSERT_IF(aChangeList[j].mContent->GetPrimaryFrame(),
                     !aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
     }
     if (i == aChangeList.Length()) {
       break;
     }
 
-    const nsStyleChangeData& data = aChangeList[i];
+    nsStyleChangeData& mutable_data = aChangeList[i];
+    const nsStyleChangeData& data = mutable_data;
     nsIFrame* frame = data.mFrame;
     nsIContent* content = data.mContent;
     nsChangeHint hint = data.mHint;
     bool didReflowThisFrame = false;
 
     NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
                  (hint & nsChangeHint_NeedReflow),
                  "Reflow hint bits set without actually asking for a reflow");
 
     // skip any frame that has been destroyed due to a ripple effect
-    if (frame && !propTable->Get(frame, ChangeListProperty())) {
+    if (frame && !propTable->HasSkippingBitCheck(frame, ChangeListProperty())) {
+      // Null out the pointer since the frame was already destroyed.
+      // This is important so we don't try to delete its
+      // ChangeListProperty() below.
+      mutable_data.mFrame = nullptr;
       continue;
     }
 
     if (frame && frame->GetContent() != content) {
       // XXXbz this is due to image maps messing with the primary frame of
       // <area>s.  See bug 135040.  Remove this block once that's fixed.
       frame = nullptr;
       if (!(hint & nsChangeHint_ReconstructFrame)) {
@@ -1470,16 +1475,21 @@ RestyleManager::ProcessRestyledFrames(ns
         }
         // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
         // transformed by other means. It's OK to have the bit even if it's
         // not needed.
       }
     }
 
     if (hint & nsChangeHint_ReconstructFrame) {
+      // We're about to destroy data.mFrame, so null out the pointer.
+      // This is important so we don't try to delete its
+      // ChangeListProperty() below.
+      mutable_data.mFrame = nullptr;
+
       // If we ever start passing true here, be careful of restyles
       // that involve a reframe and animations.  In particular, if the
       // restyle we're processing here is an animation restyle, but
       // the style resolution we will do for the frame construction
       // happens async when we're not in an animation restyle already,
       // problems could arise.
       // We could also have problems with triggering of CSS transitions
       // on elements whose frames are reconstructed, since we depend on
@@ -1659,17 +1669,17 @@ RestyleManager::ProcessRestyledFrames(ns
 
   frameConstructor->EndUpdate();
 
   // cleanup references and verify the style tree.  Note that the latter needs
   // to happen once we've processed the whole list, since until then the tree
   // is not in fact in a consistent state.
   for (const nsStyleChangeData& data : aChangeList) {
     if (data.mFrame) {
-      propTable->Delete(data.mFrame, ChangeListProperty());
+      propTable->DeleteSkippingBitCheck(data.mFrame, ChangeListProperty());
     }
 
 #ifdef DEBUG
     // reget frame from content since it may have been regenerated...
     if (data.mContent) {
       nsIFrame* frame = data.mContent->GetPrimaryFrame();
       if (frame) {
         DebugVerifyStyleTree(frame);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1697,17 +1697,17 @@ nsIFrame::GetCrossDocChildLists(nsTArray
 Visibility
 nsIFrame::GetVisibility() const
 {
   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
     return Visibility::UNTRACKED;
   }
 
   bool isSet = false;
-  FrameProperties props = Properties();
+  ConstFrameProperties props = Properties();
   uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
   return visibleCount > 0
        ? Visibility::APPROXIMATELY_VISIBLE
        : Visibility::APPROXIMATELY_NONVISIBLE;
@@ -9977,18 +9977,19 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
 
     nsIFrame *parentFrame = GetParent();
     nsFrameState savedState = parentFrame->GetStateBits();
     WritingMode parentWM = parentFrame->GetWritingMode();
     ReflowInput
       parentReflowInput(aPresContext, parentFrame, aRenderingContext,
                         LogicalSize(parentWM, parentSize),
                         ReflowInput::DUMMY_PARENT_REFLOW_STATE);
-    parentFrame->RemoveStateBits(~nsFrameState(0));
-    parentFrame->AddStateBits(savedState);
+    const nsFrameState bitsToLeaveUntouched = NS_FRAME_HAS_PROPERTIES;
+    parentFrame->RemoveStateBits(~bitsToLeaveUntouched);
+    parentFrame->AddStateBits(savedState & ~bitsToLeaveUntouched);
 
     // This may not do very much useful, but it's probably worth trying.
     if (parentSize.width != NS_INTRINSICSIZE)
       parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
     if (parentSize.height != NS_INTRINSICSIZE)
       parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
     parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
     // XXX use box methods
@@ -10538,27 +10539,27 @@ GetTagName(nsFrame* aFrame, nsIContent* 
 }
 
 void
 nsFrame::Trace(const char* aMethod, bool aEnter)
 {
   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
     char tagbuf[40];
     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
-    PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
+    printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
   }
 }
 
 void
 nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
 {
   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
     char tagbuf[40];
     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
-    PR_LogPrint("%s: %s %s, status=%scomplete%s",
+    printf_stderr("%s: %s %s, status=%scomplete%s",
                 tagbuf, aEnter ? "enter" : "exit", aMethod,
                 aStatus.IsIncomplete() ? "not" : "",
                 (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
   }
 }
 
 void
 nsFrame::TraceMsg(const char* aFormatString, ...)
@@ -10568,17 +10569,17 @@ nsFrame::TraceMsg(const char* aFormatStr
     char argbuf[200];
     va_list ap;
     va_start(ap, aFormatString);
     VsprintfLiteral(argbuf, aFormatString, ap);
     va_end(ap);
 
     char tagbuf[40];
     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
-    PR_LogPrint("%s: %s", tagbuf, argbuf);
+    printf_stderr("%s: %s", tagbuf, argbuf);
   }
 }
 
 void
 nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList)
 {
   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
     NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -19,29 +19,32 @@
 #include "nsHTMLParts.h"
 #include "nsISelectionDisplay.h"
 
 /**
  * nsFrame logging constants. We redefine the nspr
  * PRLogModuleInfo.level field to be a bitfield.  Each bit controls a
  * specific type of logging. Each logging operation has associated
  * inline methods defined below.
+ *
+ * Due to the redefinition of the level field we cannot use MOZ_LOG directly
+ * as that will cause assertions due to invalid log levels.
  */
 #define NS_FRAME_TRACE_CALLS        0x1
 #define NS_FRAME_TRACE_PUSH_PULL    0x2
 #define NS_FRAME_TRACE_CHILD_REFLOW 0x4
 #define NS_FRAME_TRACE_NEW_FRAMES   0x8
 
 #define NS_FRAME_LOG_TEST(_lm,_bit) (int(((mozilla::LogModule*)_lm)->Level()) & (_bit))
 
 #ifdef DEBUG
 #define NS_FRAME_LOG(_bit,_args)                                \
   PR_BEGIN_MACRO                                                \
     if (NS_FRAME_LOG_TEST(nsFrame::sFrameLogModule,_bit)) {  \
-      PR_LogPrint _args;                                        \
+      printf_stderr _args; \
     }                                                           \
   PR_END_MACRO
 #else
 #define NS_FRAME_LOG(_bit,_args)
 #endif
 
 // XXX Need to rework this so that logging is free when it's off
 #ifdef DEBUG
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -265,16 +265,19 @@ FRAME_STATE_BIT(Generic, 53, NS_FRAME_IS
 
 // Frame has a LayerActivityProperty property
 FRAME_STATE_BIT(Generic, 54, NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)
 
 // Frame owns anonymous boxes whose style contexts it will need to update during
 // a stylo tree traversal.
 FRAME_STATE_BIT(Generic, 55, NS_FRAME_OWNS_ANON_BOXES)
 
+// Frame has properties in the nsIFrame::Properties() hash.
+FRAME_STATE_BIT(Generic, 56, NS_FRAME_HAS_PROPERTIES)
+
 // Set for all descendants of MathML sub/supscript elements (other than the
 // base frame) to indicate that the SSTY font feature should be used.
 FRAME_STATE_BIT(Generic, 58, NS_FRAME_MATHML_SCRIPT_DESCENDANT)
 
 // This state bit is set on frames within token MathML elements if the
 // token represents an <mi> tag whose inner HTML consists of a single
 // non-whitespace character to allow special rendering behaviour.
 FRAME_STATE_BIT(Generic, 59, NS_FRAME_IS_IN_SINGLE_CHAR_MI)
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2712,22 +2712,27 @@ ScrollFrameHelper::ScheduleSyntheticMous
       return;
   }
 
   mScrollActivityTimer->InitWithFuncCallback(
         ScrollActivityCallback, this, 100, nsITimer::TYPE_ONE_SHOT);
 }
 
 void
-ScrollFrameHelper::NotifyApproximateFrameVisibilityUpdate()
+ScrollFrameHelper::NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort)
 {
   mLastUpdateFramesPos = GetScrollPosition();
-  mHadDisplayPortAtLastFrameUpdate =
-    nsLayoutUtils::GetDisplayPort(mOuter->GetContent(),
-                                  &mDisplayPortAtLastFrameUpdate);
+  if (aIgnoreDisplayPort) {
+    mHadDisplayPortAtLastFrameUpdate = false;
+    mDisplayPortAtLastFrameUpdate = nsRect();
+  } else {
+    mHadDisplayPortAtLastFrameUpdate =
+      nsLayoutUtils::GetDisplayPort(mOuter->GetContent(),
+                                    &mDisplayPortAtLastFrameUpdate);
+  }
 }
 
 bool
 ScrollFrameHelper::GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort)
 {
   if (mHadDisplayPortAtLastFrameUpdate) {
     *aDisplayPort = mDisplayPortAtLastFrameUpdate;
   }
@@ -3233,17 +3238,17 @@ ClipListsExceptCaret(nsDisplayListCollec
 }
 
 void
 ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   if (aBuilder->IsForFrameVisibility()) {
-    NotifyApproximateFrameVisibilityUpdate();
+    NotifyApproximateFrameVisibilityUpdate(false);
   }
 
   mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   if (aBuilder->IsPaintingToWindow()) {
     mScrollPosAtLastPaint = GetScrollPosition();
     if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
       MarkNotRecentlyScrolled();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -400,17 +400,17 @@ public:
 
   bool UsesContainerScrolling() const;
 
   ScrollSnapInfo GetScrollSnapInfo() const;
 
   bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                              nsRect* aDirtyRect,
                              bool aAllowCreateDisplayPort);
-  void NotifyApproximateFrameVisibilityUpdate();
+  void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort);
   bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort);
 
   bool AllowDisplayPortExpiration();
   void TriggerDisplayPortExpiration();
   void ResetDisplayPortExpiryTimer();
 
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
@@ -938,18 +938,18 @@ public:
   virtual bool UsesContainerScrolling() const override {
     return mHelper.UsesContainerScrolling();
   }
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aDirtyRect,
                                      bool aAllowCreateDisplayPort) override {
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
-  virtual void NotifyApproximateFrameVisibilityUpdate() override {
-    mHelper.NotifyApproximateFrameVisibilityUpdate();
+  virtual void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort) override {
+    mHelper.NotifyApproximateFrameVisibilityUpdate(aIgnoreDisplayPort);
   }
   virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
     return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
   }
   void TriggerDisplayPortExpiration() override {
     mHelper.TriggerDisplayPortExpiration();
   }
 
@@ -1458,18 +1458,18 @@ public:
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aDirtyRect,
                                      bool aAllowCreateDisplayPort) override {
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
-  virtual void NotifyApproximateFrameVisibilityUpdate() override {
-    mHelper.NotifyApproximateFrameVisibilityUpdate();
+  virtual void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort) override {
+    mHelper.NotifyApproximateFrameVisibilityUpdate(aIgnoreDisplayPort);
   }
   virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
     return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
   }
   void TriggerDisplayPortExpiration() override {
     mHelper.TriggerDisplayPortExpiration();
   }
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -572,16 +572,17 @@ public:
   using OnNonvisible = mozilla::OnNonvisible;
   template<typename T=void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
 
   typedef mozilla::FrameProperties FrameProperties;
+  typedef mozilla::ConstFrameProperties ConstFrameProperties;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layout::FrameChildList ChildList;
   typedef mozilla::layout::FrameChildListID ChildListID;
   typedef mozilla::layout::FrameChildListIDs ChildListIDs;
   typedef mozilla::layout::FrameChildListIterator ChildListIterator;
   typedef mozilla::layout::FrameChildListArrayIterator ChildListArrayIterator;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Matrix Matrix;
@@ -3246,20 +3247,24 @@ public:
    *
    * @param aParentContent the content node corresponding to the parent frame
    * @return whether the frame is a pseudo frame
    */
   bool IsPseudoFrame(const nsIContent* aParentContent) {
     return mContent == aParentContent;
   }
 
-  FrameProperties Properties() const {
+  FrameProperties Properties() {
     return FrameProperties(PresContext()->PropertyTable(), this);
   }
 
+  ConstFrameProperties Properties() const {
+    return ConstFrameProperties(PresContext()->PropertyTable(), this);
+  }
+
   /**
    * Return true if and only if this frame obeys visibility:hidden.
    * if it does not, then nsContainerFrame will hide its view even though
    * this means children can't be made visible again.
    */
   virtual bool SupportsVisibilityHidden() { return true; }
 
   /**
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -447,18 +447,20 @@ public:
    * has been a call with it set to true before on the same paint.
    */
   virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                                      nsRect* aDirtyRect,
                                      bool aAllowCreateDisplayPort) = 0;
 
   /**
    * Notification that this scroll frame is getting its frame visibility updated.
+   * aIgnoreDisplayPort indicates that the display port was ignored (because
+   * there was no suitable base rect)
    */
-  virtual void NotifyApproximateFrameVisibilityUpdate() = 0;
+  virtual void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort) = 0;
 
   /**
    * Returns true if this scroll frame had a display port at the last frame
    * visibility update and fills in aDisplayPort with that displayport. Returns
    * false otherwise, and doesn't touch aDisplayPort.
    */
   virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) = 0;
 
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -58,19 +58,16 @@
 #include "ReadbackLayer.h"
 #include "ImageContainer.h"
 
 // accessibility support
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
-#ifdef MOZ_LOGGING
-#define FORCE_PR_LOG 1 /* Allow logging in the release build */
-#endif /* MOZ_LOGGING */
 #include "mozilla/Logging.h"
 
 #ifdef XP_MACOSX
 #include "gfxQuartzNativeDrawing.h"
 #include "mozilla/gfx/QuartzSupport.h"
 #endif
 
 #ifdef MOZ_X11
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -3199,17 +3199,17 @@ protected:
   }
 
   const RefPtr<gfxTextRun>        mTextRun;
   mutable gfxFontGroup*           mFontGroup;
   mutable RefPtr<nsFontMetrics>   mFontMetrics;
   const nsStyleText*              mTextStyle;
   const nsTextFragment*           mFrag;
   const nsIFrame*                 mLineContainer;
-  const nsTextFrame*              mFrame;
+  nsTextFrame*                    mFrame;
   gfxSkipCharsIterator            mStart;  // Offset in original and transformed string
   const gfxSkipCharsIterator      mTempIterator;
 
   // Either null, or pointing to the frame's TabWidthProperty.
   mutable TabWidthStore*          mTabWidths;
   // How far we've done tab-width calculation; this is ONLY valid when
   // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead).
   // It's a DOM offset relative to the current frame's offset.
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -162,17 +162,17 @@ AttributeToProperty(nsIAtom* aAttribute)
 static nsTArray<int8_t>*
 FindCellProperty(const nsIFrame* aCellFrame,
                  const FramePropertyDescriptor<nsTArray<int8_t>>* aFrameProperty)
 {
   const nsIFrame* currentFrame = aCellFrame;
   nsTArray<int8_t>* propertyData = nullptr;
 
   while (currentFrame) {
-    FrameProperties props = currentFrame->Properties();
+    ConstFrameProperties props = currentFrame->Properties();
     propertyData = props.Get(aFrameProperty);
     bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame);
 
     if (propertyData || frameIsTable)
       currentFrame = nullptr; // A null frame pointer exits the loop
     else
       currentFrame = currentFrame->GetParent(); // Go to the parent frame
   }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2301,16 +2301,22 @@ nsDisplayItem* nsDisplayList::RemoveBott
   }
   item->mAbove = nullptr;
   return item;
 }
 
 void nsDisplayList::DeleteAll() {
   nsDisplayItem* item;
   while ((item = RemoveBottom()) != nullptr) {
+#ifdef NIGHTLY_BUILD
+    if (XRE_IsContentProcess()) {
+      mozilla::Telemetry::Accumulate(mozilla::Telemetry::DISPLAY_ITEM_USAGE_COUNT,
+                                     item->GetType());
+    }
+#endif
     item->~nsDisplayItem();
   }
 }
 
 static bool
 GetMouseThrough(const nsIFrame* aFrame)
 {
   if (!aFrame->IsXULBoxFrame())
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -2789,21 +2789,27 @@ nsTableFrame::GetUsedMargin() const
   // The margin is inherited to the table wrapper frame via
   // the ::-moz-table-wrapper rule in ua.css.
   return nsMargin(0, 0, 0, 0);
 }
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCProperty, BCPropertyData)
 
 BCPropertyData*
-nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const
+nsTableFrame::GetBCProperty() const
+{
+  return Properties().Get(TableBCProperty());
+}
+
+BCPropertyData*
+nsTableFrame::GetOrCreateBCProperty()
 {
   FrameProperties props = Properties();
   BCPropertyData* value = props.Get(TableBCProperty());
-  if (!value && aCreateIfNecessary) {
+  if (!value) {
     value = new BCPropertyData();
     props.Set(TableBCProperty(), value);
   }
 
   return value;
 }
 
 static void
@@ -4242,17 +4248,17 @@ nsTableFrame::AddBCDamageArea(const Tabl
 {
   NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
 #ifdef DEBUG
   VerifyDamageRect(aValue);
 #endif
 
   SetNeedToCalcBCBorders(true);
   // Get the property
-  BCPropertyData* value = GetBCProperty(true);
+  BCPropertyData* value = GetOrCreateBCProperty();
   if (value) {
 #ifdef DEBUG
     VerifyNonNegativeDamageRect(value->mDamageArea);
 #endif
     // Clamp the old damage area to the current table area in case it shrunk.
     int32_t cols = GetColCount();
     if (value->mDamageArea.EndCol() > cols) {
       if (value->mDamageArea.StartCol() > cols) {
@@ -4282,17 +4288,17 @@ nsTableFrame::AddBCDamageArea(const Tabl
 
 void
 nsTableFrame::SetFullBCDamageArea()
 {
   NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
 
   SetNeedToCalcBCBorders(true);
 
-  BCPropertyData* value = GetBCProperty(true);
+  BCPropertyData* value = GetOrCreateBCProperty();
   if (value) {
     value->mDamageArea = TableArea(0, 0, GetColCount(), GetRowCount());
   }
 }
 
 
 /* BCCellBorder represents a border segment which can be either an inline-dir
  * or a block-dir segment. For each segment we need to know the color, width,
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -802,17 +802,18 @@ public:
 
   nsTArray<nsTableColFrame*>& GetColCache();
 
 
 protected:
 
   void SetBorderCollapse(bool aValue);
 
-  BCPropertyData* GetBCProperty(bool aCreateIfNecessary = false) const;
+  BCPropertyData* GetBCProperty() const;
+  BCPropertyData* GetOrCreateBCProperty();
   void SetFullBCDamageArea();
   void CalcBCBorders();
 
   void ExpandBCDamageArea(mozilla::TableArea& aRect) const;
 
   void SetColumnDimensions(nscoord aHeight, WritingMode aWM,
                            const LogicalMargin& aBorderPadding,
                            const nsSize& aContainerSize);
--- a/media/mtransport/logging.h
+++ b/media/mtransport/logging.h
@@ -19,28 +19,41 @@
 #define ML_DEBUG            mozilla::LogLevel::Verbose
 
 #ifdef MOZILLA_INTERNAL_API
 #define MOZ_MTLOG_MODULE(n) \
   static mozilla::LogModule* getLogModule() {   \
     static mozilla::LazyLogModule log(n);       \
     return static_cast<mozilla::LogModule*>(log);      \
   }
+
+#define MOZ_MTLOG(level, b)                                       \
+  do {                                                            \
+    if (MOZ_LOG_TEST(getLogModule(), level)) {                    \
+      std::stringstream str;                                      \
+      str << b;                                                   \
+      MOZ_LOG(getLogModule(), level, ("%s", str.str().c_str()));  \
+    }                                                             \
+  } while(0)
 #else
+// When building mtransport outside of XUL, for example in stand-alone gtests,
+// PR_Logging needs to be used instead of mozilla logging.
+
+#include "prlog.h"
+
 #define MOZ_MTLOG_MODULE(n) \
   static PRLogModuleInfo* getLogModule() {      \
     static PRLogModuleInfo* log;                \
     if (!log)                                   \
       log = PR_NewLogModule(n);                 \
     return log;                                 \
   }
-#endif
 
-#define MOZ_MTLOG(level, b) \
-  do {                                                                  \
-    if (MOZ_LOG_TEST(getLogModule(), level)) {                           \
-      std::stringstream str;                                            \
-      str << b;                                                         \
-      MOZ_LOG(getLogModule(), level, ("%s", str.str().c_str()));         \
-    }                                                                   \
+#define MOZ_MTLOG(level, b)                                                         \
+  do {                                                                              \
+    if (PR_LOG_TEST(getLogModule(), (PRLogModuleLevel)level)) {                     \
+      std::stringstream str;                                                        \
+      str << b;                                                                     \
+      PR_LOG(getLogModule(), (PRLogModuleLevel)level, ("%s", str.str().c_str()));   \
+    }                                                                               \
   } while(0)
-
+#endif // MOZILLA_INTERNAL_API
 #endif // logging_h__
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -1,20 +1,21 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 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;
 
@@ -50,41 +51,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 volatile StateHolder mStateHolder;
+    private final NativeQueue mNativeQueue;
 
     @ReflectionTarget
     @WrapForJNI(calledFrom = "gecko")
     public static EventDispatcher getInstance() {
         return INSTANCE;
     }
 
     /* package */ EventDispatcher() {
-        mStateHolder = GeckoThread.getStateHolder();
+        mNativeQueue = GeckoThread.getNativeQueue();
     }
 
-    /* package */ EventDispatcher(final NativeQueue.StateHolder stateHolder) {
-        mStateHolder = stateHolder;
-    }
-
-    /* package */ void setStateHolder(final NativeQueue.StateHolder stateHolder) {
-        mStateHolder = stateHolder;
-        // Force queue flushing.
-        final NativeQueue.State state = mStateHolder.getState();
-        mStateHolder.checkAndSetState(state, state);
+    /* package */ EventDispatcher(final NativeQueue queue) {
+        mNativeQueue = queue;
     }
 
     private boolean isReadyForDispatchingToGecko() {
-        return mStateHolder.isReady();
+        return mNativeQueue.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;
@@ -296,18 +290,17 @@ public final class EventDispatcher exten
 
         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",
+            mNativeQueue.queueUntilReady(this, "dispatchToGecko",
                 String.class, type,
                 GeckoBundle.class, message,
                 EventCallback.class, JavaCallbackDelegate.wrap(callback));
             return true;
         }
 
         return false;
     }
--- 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,17 +3,16 @@
  * 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 android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
@@ -75,21 +74,21 @@ public class GeckoThread extends Thread 
         public boolean isAtLeast(final NativeQueue.State other) {
             if (other instanceof State) {
                 return this.rank >= ((State) other).rank;
             }
             return false;
         }
     }
 
-    private static final StateHolder sStateHolder =
-        new StateHolder(State.INITIAL, State.RUNNING);
+    private static final NativeQueue sNativeQueue =
+        new NativeQueue(State.INITIAL, State.RUNNING);
 
-    /* package */ static StateHolder getStateHolder() {
-        return sStateHolder;
+    /* package */ static NativeQueue getNativeQueue() {
+        return sNativeQueue;
     }
 
     public static final State MIN_STATE = State.INITIAL;
     public static final State MAX_STATE = State.EXITED;
 
     private static final Runnable UI_THREAD_CALLBACK = new Runnable() {
         @Override
         public void run() {
@@ -441,62 +440,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 sStateHolder.getState().is(state);
+        return sNativeQueue.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 sStateHolder.getState().isAtLeast(state);
+        return sNativeQueue.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 state.isAtLeast(sStateHolder.getState());
+        return state.isAtLeast(sNativeQueue.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 isStateAtLeast(minState) && isStateAtMost(maxState);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private static void setState(final State newState) {
-        sStateHolder.setState(newState);
+        sNativeQueue.setState(newState);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private static boolean checkAndSetState(final State expectedState,
                                             final State newState) {
-        return sStateHolder.checkAndSetState(expectedState, newState);
+        return sNativeQueue.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.
@@ -556,35 +555,35 @@ public class GeckoThread extends Thread 
         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);
+        sNativeQueue.queueUntilReady(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);
+        sNativeQueue.queueUntilReady(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);
+        sNativeQueue.queueUntil(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);
+        sNativeQueue.queueUntil(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,17 +7,16 @@
 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.net.Uri;
@@ -42,80 +41,79 @@ public class GeckoView extends LayerView
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
     /* package */ enum State implements NativeQueue.State {
         @WrapForJNI INITIAL(0),
         @WrapForJNI READY(1);
 
-        private int mRank;
+        private final int mRank;
 
         private State(int rank) {
             mRank = 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 mRank >= ((State) other).mRank;
-            }
-            return false;
+            return (other instanceof State) &&
+                   mRank >= ((State) other).mRank;
         }
     }
 
-    private static final StateHolder sDummyStateHolder =
-        new StateHolder(State.INITIAL, State.READY);
+    private final NativeQueue mNativeQueue =
+        new NativeQueue(State.INITIAL, State.READY);
 
     private final EventDispatcher mEventDispatcher =
-        new EventDispatcher(sDummyStateHolder);
+        new EventDispatcher(mNativeQueue);
 
     private ChromeDelegate mChromeDelegate;
     /* package */ ContentListener mContentListener;
     /* package */ NavigationListener mNavigationListener;
     /* package */ ProgressListener mProgressListener;
     private InputConnectionListener mInputConnectionListener;
 
     private GeckoViewSettings mSettings;
 
     protected boolean mOnAttachedToWindowCalled;
     protected String mChromeUri;
     protected int mScreenId = 0; // default to the primary screen
 
     @WrapForJNI(dispatchTo = "proxy")
     protected static final class Window extends JNIObject {
         @WrapForJNI(skip = true)
-        /* package */ final StateHolder mStateHolder =
-            new StateHolder(State.INITIAL, State.READY);
+        /* package */ NativeQueue mNativeQueue;
 
         @WrapForJNI(skip = true)
-        /* package */ Window() {}
+        /* package */ Window(final NativeQueue queue) {
+            mNativeQueue = queue;
+        }
 
         static native void open(Window instance, GeckoView view,
                                 Object compositor, EventDispatcher dispatcher,
                                 String chromeUri, GeckoBundle settings,
                                 int screenId);
 
         @Override protected native void disposeNative();
 
         native void close();
 
         native void reattach(GeckoView view, Object compositor,
                              EventDispatcher dispatcher);
 
         native void loadUri(String uri, int flags);
 
         @WrapForJNI(calledFrom = "gecko")
-        private void setState(final State newState) {
-            mStateHolder.setState(newState);
+        private synchronized void setState(final State newState) {
+            mNativeQueue.setState(newState);
         }
     }
 
     // Object to hold onto our nsWindow connection when GeckoView gets destroyed.
     private static class StateBinder extends Binder implements Parcelable {
         public final Parcelable superState;
         public final Window window;
 
@@ -282,35 +280,39 @@ public class GeckoView extends LayerView
                 EventDispatcher.class, mEventDispatcher,
                 String.class, mChromeUri,
                 GeckoBundle.class, mSettings.asBundle(),
                 mScreenId);
         }
     }
 
     protected void reattachWindow() {
-        mEventDispatcher.setStateHolder(mWindow.mStateHolder);
+        synchronized (mWindow) {
+            if (mNativeQueue != mWindow.mNativeQueue) {
+                mNativeQueue.setState(mWindow.mNativeQueue.getState());
+                mWindow.mNativeQueue = mNativeQueue;
+            }
+        }
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             mWindow.reattach(this, getCompositor(), mEventDispatcher);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "reattach", GeckoView.class, this,
                     Object.class, getCompositor(), EventDispatcher.class, mEventDispatcher);
         }
     }
 
     @Override
     public void onAttachedToWindow() {
         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
 
         if (mWindow == null) {
             // Open a new nsWindow if we didn't have one from before.
-            mWindow = new Window();
-            mEventDispatcher.setStateHolder(mWindow.mStateHolder);
+            mWindow = new Window(mNativeQueue);
             openWindow();
         } else {
             reattachWindow();
         }
 
         super.onAttachedToWindow();
 
         mOnAttachedToWindowCalled = true;
@@ -331,17 +333,16 @@ public class GeckoView extends LayerView
             mWindow.disposeNative();
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "close");
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "disposeNative");
         }
 
-        mEventDispatcher.setStateHolder(sDummyStateHolder);
         mOnAttachedToWindowCalled = false;
     }
 
     @WrapForJNI public static final int LOAD_DEFAULT = 0;
     @WrapForJNI public static final int LOAD_NEW_TAB = 1;
     @WrapForJNI public static final int LOAD_SWITCH_TAB = 2;
 
     /**
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NativeQueue.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NativeQueue.java
@@ -1,9 +1,10 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.util.Log;
 
@@ -15,52 +16,44 @@ 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;
+    private volatile State mState;
+    private final State mReadyState;
 
-        public StateHolder(final State initial, final State ready) {
-            this.mState = initial;
-            this.mReadyState = ready;
-        }
+    public NativeQueue(final State initial, final State ready) {
+        mState = initial;
+        mReadyState = ready;
+    }
 
-        public boolean isReady() {
-            return getState().isAtLeast(mReadyState);
-        }
-
-        public State getReadyState() {
-            return mReadyState;
-        }
+    public boolean isReady() {
+        return getState().isAtLeast(mReadyState);
+    }
 
-        public State getState() {
-            return mState;
-        }
+    public State getState() {
+        return mState;
+    }
 
-        public boolean setState(final State newState) {
-            return checkAndSetState(null, newState);
-        }
+    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;
-            }
+    public synchronized boolean checkAndSetState(final State expectedState,
+                                                 final State newState) {
+        if (expectedState != null && !mState.is(expectedState)) {
+            return false;
         }
+        flushQueuedLocked(newState);
+        mState = newState;
+        return true;
     }
 
     private static class QueuedCall {
         public Method method;
         public Object target;
         public Object[] args;
         public State state;
 
@@ -69,39 +62,38 @@ public class NativeQueue {
             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 =
+    /* package */ final ArrayList<QueuedCall> mQueue =
         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) {
+    private 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;
@@ -130,90 +122,109 @@ public class NativeQueue {
             // 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)) {
+        if (getState().isAtLeast(state)) {
             invokeMethod(method, obj, argValues.toArray());
             return;
         }
 
-        sQueue.add(new QueuedCall(
+        mQueue.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 isReady condition.
+     *
+     * @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 synchronized void queueUntilReady(final Object obj,
+                                             final String methodName,
+                                             final Object... args) {
+        queueNativeCallLocked(obj.getClass(), methodName, obj, args, mReadyState);
+    }
+
+    /**
+     * Queue a call to the given static method if the given current state does
+     * not satisfy the isReady condition.
+     *
+     * @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 synchronized void queueUntilReady(final Class<?> cls,
+                                             final String methodName,
+                                             final Object... args) {
+        queueNativeCallLocked(cls, methodName, null, args, mReadyState);
+    }
+
+    /**
+     * 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);
-        }
+    public synchronized void queueUntil(final State state, final Object obj,
+                                        final String methodName,
+                                        final Object... args) {
+        queueNativeCallLocked(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);
-        }
+    public synchronized void queueUntil(final State state, final Class<?> cls,
+                                        final String methodName,
+                                        final Object... args) {
+        queueNativeCallLocked(cls, methodName, null, args, state);
     }
 
     // Run all queued methods
-    private static void flushQueuedLocked(final State state) {
+    private void flushQueuedLocked(final State state) {
         int lastSkipped = -1;
-        for (int i = 0; i < sQueue.size(); i++) {
-            final QueuedCall call = sQueue.get(i);
+        for (int i = 0; i < mQueue.size(); i++) {
+            final QueuedCall call = mQueue.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);
+            mQueue.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) {
+            mQueue.clear();
+            mQueue.trimToSize();
+        } else if (lastSkipped < mQueue.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();
+            mQueue.subList(lastSkipped + 1, mQueue.size()).clear();
         }
     }
 }
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -565,17 +565,17 @@ nsZipArchive::FindInit(const char * aPat
         break;
 
       case VALID_SXP:
         regExp = true;
         break;
 
       default:
         // undocumented return value from RegExpValid!
-        PR_ASSERT(false);
+        MOZ_ASSERT(false);
         return NS_ERROR_ILLEGAL_VALUE;
     }
 
     pattern = PL_strdup(aPattern);
     if (!pattern)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -12,16 +12,17 @@
 
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefBranchInternal.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsWeakReference.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/MemoryReporting.h"
 
 class nsIFile;
 class nsAdoptingString;
 class nsAdoptingCString;
 
 #ifndef have_PrefChangedFunc_typedef
 typedef void (*PrefChangedFunc)(const char *, void *);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1594,16 +1594,19 @@ pref("network.http.spdy.default-hpack-bu
 // alt-svc allows separation of transport routing from
 // the origin host without using a proxy.
 pref("network.http.altsvc.enabled", true);
 pref("network.http.altsvc.oe", true);
 
 // Turn on 0RTT data for TLS 1.3
 pref("security.tls.enable_0rtt_data", true);
 
+// the origin extension impacts h2 coalescing
+pref("network.http.originextension", true);
+
 pref("network.http.diagnostics", false);
 
 pref("network.http.pacing.requests.enabled", true);
 pref("network.http.pacing.requests.min-parallelism", 6);
 pref("network.http.pacing.requests.hz", 80);
 pref("network.http.pacing.requests.burst", 10);
 
 // TCP Keepalive config for HTTP connections.
--- a/netwerk/base/ThrottlingService.cpp
+++ b/netwerk/base/ThrottlingService.cpp
@@ -126,25 +126,20 @@ ThrottlingService::AddChannel(nsIHttpCha
   // We don't check mEnabled, because we always want to put channels in the hash
   // to avoid potential inconsistencies in the case where the user changes the
   // enabled pref at run-time.
 
   if (IsNeckoChild()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsAutoCString strKey;
-  nsresult rv = channel->GetChannelId(strKey);
+  uint64_t key;
+  nsresult rv = channel->GetChannelId(&key);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsID key;
-  if (!key.Parse(strKey.get())) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
   if (mChannelHash.Get(key, nullptr)) {
     // We already have this channel under our control, not adding it again.
     MOZ_ASSERT(false, "Trying to throttle an already-throttled channel");
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (!mIteratingHash) {
     // This should be the common case, and as such is easy to handle
@@ -173,25 +168,20 @@ ThrottlingService::RemoveChannel(nsIHttp
 
   // Just like above, don't worry about mEnabled to avoid inconsistencies when
   // the pref changes at run-time
 
   if (IsNeckoChild()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsAutoCString strKey;
-  nsresult rv = channel->GetChannelId(strKey);
+  uint64_t key;
+  nsresult rv = channel->GetChannelId(&key);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsID key;
-  if (!key.Parse(strKey.get())) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
   if (!mChannelHash.Get(key, nullptr)) {
     // TODO - warn?
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (!mIteratingHash) {
     // This should be the common case, and easy to handle.
     mChannelHash.Remove(key);
--- a/netwerk/base/ThrottlingService.h
+++ b/netwerk/base/ThrottlingService.h
@@ -45,17 +45,17 @@ private:
 
   bool mEnabled;
   bool mInitCalled;
   bool mSuspended;
   uint32_t mPressureCount;
   uint32_t mSuspendPeriod; // How long we should Suspend() channels for
   uint32_t mResumePeriod; // How long we should Resume() channels for
   nsCOMPtr<nsITimer> mTimer;
-  typedef nsInterfaceHashtable<nsIDHashKey, nsIHttpChannel> ChannelHash;
+  typedef nsInterfaceHashtable<nsUint64HashKey, nsIHttpChannel> ChannelHash;
   ChannelHash mChannelHash;
 
   // Used to avoid inconsistencies in the hash and the suspend/resume count of
   // channels. See comments in AddChannel and RemoveChannel for details.
   void IterateHash(void (* callback)(ChannelHash::Iterator &iter));
   bool mIteratingHash;
   nsCOMArray<nsIHttpChannel> mChannelsToAddRemove;
   nsTArray<bool> mChannelIsAdd;
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -123,17 +123,17 @@ struct HttpChannelOpenArgs
   uint32_t                    cacheKey;
   uint64_t                    requestContextID;
   OptionalCorsPreflightArgs   preflightArgs;
   uint32_t                    initialRwin;
   bool                        blockAuthPrompt;
   bool                        suspendAfterSynthesizeResponse;
   bool                        allowStaleCacheContent;
   nsCString                   contentTypeHint;
-  nsCString                   channelId;
+  uint64_t                    channelId;
   uint64_t                    contentWindowId;
   nsCString                   preferredAlternativeType;
   uint64_t                    topLevelOuterContentWindowId;
 };
 
 struct HttpChannelConnectArgs
 {
   uint32_t registrarId;
--- a/netwerk/protocol/http/ASpdySession.h
+++ b/netwerk/protocol/http/ASpdySession.h
@@ -27,16 +27,19 @@ public:
   virtual bool RoomForMoreStreams() = 0;
   virtual PRIntervalTime IdleTime() = 0;
   virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
   virtual void DontReuse() = 0;
   virtual uint32_t SpdyVersion() = 0;
 
   static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *, bool);
 
+  virtual bool TestJoinConnection(const nsACString &hostname, int32_t port) = 0;
+  virtual bool JoinConnection(const nsACString &hostname, int32_t port) = 0;
+
   // MaybeReTunnel() is called by the connection manager when it cannot
   // dispatch a tunneled transaction. That might be because the tunnels it
   // expects to see are dead (and we may or may not be able to make more),
   // or it might just need to wait longer for one of them to become free.
   //
   // return true if the session takes back ownership of the transaction from
   // the connection manager.
   virtual bool MaybeReTunnel(nsAHttpTransaction *) = 0;
--- a/netwerk/protocol/http/AlternateServices.cpp
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -665,19 +665,19 @@ public:
     LOG(("WellKnownChecker dtor %p\n", this));
   }
 
 private:
   nsresult
   MakeChannel(nsHttpChannel *chan, TransactionObserver *obs, nsHttpConnectionInfo *ci,
               nsIURI *uri, uint32_t caps, nsILoadInfo *loadInfo)
   {
-    nsID channelId;
+    uint64_t channelId;
     nsLoadFlags flags;
-    if (NS_FAILED(gHttpHandler->NewChannelId(&channelId)) ||
+    if (NS_FAILED(gHttpHandler->NewChannelId(channelId)) ||
         NS_FAILED(chan->Init(uri, caps, nullptr, 0, nullptr, channelId)) ||
         NS_FAILED(chan->SetAllowAltSvc(false)) ||
         NS_FAILED(chan->SetRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_ERROR)) ||
         NS_FAILED(chan->SetLoadInfo(loadInfo)) ||
         NS_FAILED(chan->GetLoadFlags(&flags))) {
       return NS_ERROR_FAILURE;
     }
     flags |= HttpBaseChannel::LOAD_BYPASS_CACHE;
--- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
@@ -62,18 +62,17 @@ nsHttpConnectionMgr::OnMsgPrintDiagnosti
     mLogData.AppendPrintf("   Active Conns Length = %" PRIuSIZE "\n",
                           ent->mActiveConns.Length());
     mLogData.AppendPrintf("   Idle Conns Length = %" PRIuSIZE "\n",
                           ent->mIdleConns.Length());
     mLogData.AppendPrintf("   Half Opens Length = %" PRIuSIZE "\n",
                           ent->mHalfOpens.Length());
     mLogData.AppendPrintf("   Coalescing Keys Length = %" PRIuSIZE "\n",
                           ent->mCoalescingKeys.Length());
-    mLogData.AppendPrintf("   Spdy using = %d, preferred = %d\n",
-                          ent->mUsingSpdy, ent->mInPreferredHash);
+    mLogData.AppendPrintf("   Spdy using = %d\n", ent->mUsingSpdy);
 
     uint32_t i;
     for (i = 0; i < ent->mActiveConns.Length(); ++i) {
       mLogData.AppendPrintf("   :: Active Connection #%u\n", i);
       ent->mActiveConns[i]->PrintDiagnostics(mLogData);
     }
     for (i = 0; i < ent->mIdleConns.Length(); ++i) {
       mLogData.AppendPrintf("   :: Idle Connection #%u\n", i);
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -106,16 +106,17 @@ Http2Session::Http2Session(nsISocketTran
   , mOutputQueueSent(0)
   , mLastReadEpoch(PR_IntervalNow())
   , mPingSentEpoch(0)
   , mPreviousUsed(false)
   , mWaitingForSettingsAck(false)
   , mGoAwayOnPush(false)
   , mUseH2Deps(false)
   , mAttemptingEarlyData(attemptingEarlyData)
+  , mOriginFrameActivated(false)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   static uint64_t sSerial;
   mSerial = ++sSerial;
 
   LOG3(("Http2Session::Http2Session %p serial=0x%" PRIX64 "\n", this, mSerial));
 
@@ -219,17 +220,18 @@ static Http2ControlFx sControlFunctions[
   Http2Session::RecvPriority,
   Http2Session::RecvRstStream,
   Http2Session::RecvSettings,
   Http2Session::RecvPushPromise,
   Http2Session::RecvPing,
   Http2Session::RecvGoAway,
   Http2Session::RecvWindowUpdate,
   Http2Session::RecvContinuation,
-  Http2Session::RecvAltSvc // extension for type 0x0A
+  Http2Session::RecvAltSvc, // extension for type 0x0A
+  Http2Session::RecvOrigin  // extension for type 0x0B
 };
 
 bool
 Http2Session::RoomForMoreConcurrent()
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   return (mConcurrent < mMaxConcurrent);
 }
@@ -1793,36 +1795,33 @@ Http2Session::RecvPushPromise(Http2Sessi
   nsAutoCString key;
   if (!pushedStream->GetHashKey(key)) {
     LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
     self->ResetDownstreamState();
     return NS_OK;
   }
 
-  RefPtr<nsStandardURL> associatedURL, pushedURL;
-  rv = Http2Stream::MakeOriginURL(associatedStream->Origin(), associatedURL);
-  if (NS_SUCCEEDED(rv)) {
-    rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
-  }
-  LOG3(("Http2Session::RecvPushPromise %p checking %s == %s", self,
-        associatedStream->Origin().get(), pushedStream->Origin().get()));
-  bool match = false;
+  // does the pushed origin belong on this connection?
+  LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
+        pushedStream->Origin().get()));
+  RefPtr<nsStandardURL> pushedURL;
+  rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
+  nsAutoCString pushedHostName;
+  int32_t pushedPort = -1;
   if (NS_SUCCEEDED(rv)) {
-    rv = associatedURL->Equals(pushedURL, &match);
+    rv = pushedURL->GetHost(pushedHostName);
+  }
+  if (NS_SUCCEEDED(rv)) {
+    rv = pushedURL->GetPort(&pushedPort);
   }
-  if (NS_FAILED(rv)) {
-    // Fallback to string equality of origins. This won't be guaranteed to be as
-    // liberal as we want it to be, but it will at least be safe
-    match = associatedStream->Origin().Equals(pushedStream->Origin());
-  }
-  if (!match) {
-    LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin "
-          "associated origin %s .. pushed origin %s\n", self,
-          associatedStream->Origin().get(), pushedStream->Origin().get()));
+  if (NS_FAILED(rv) ||
+      !self->TestJoinConnection(pushedHostName, pushedPort)) {
+    LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin %s\n",
+          self, pushedStream->Origin().get()));
     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
     self->ResetDownstreamState();
     return NS_OK;
   }
 
   if (pushedStream->TryOnPush()) {
     LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener "
           "stream %p will not be placed into session cache.\n", self, pushedStream));
@@ -2286,16 +2285,121 @@ Http2Session::RecvAltSvc(Http2Session *s
 
   RefPtr<UpdateAltSvcEvent> event =
     new UpdateAltSvcEvent(altSvcFieldValue, origin, ci, irCallbacks);
   NS_DispatchToMainThread(event);
   self->ResetDownstreamState();
   return NS_OK;
 }
 
+void
+Http2Session::Received421(nsHttpConnectionInfo *ci)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::Recevied421 %p %d\n", this, mOriginFrameActivated));
+  if (!mOriginFrameActivated || !ci) {
+    return;
+  }
+
+  nsAutoCString key(ci->GetOrigin());
+  key.Append(':');
+  key.AppendInt(ci->OriginPort());
+  mOriginFrame.Remove(key);
+  LOG3(("Http2Session::Received421 %p key %s removed\n", this, key.get()));
+}
+
+// defined as an http2 extension - origin
+// defines receipt of frame type 0x0b.. http://httpwg.org/http-extensions/origin-frame.html
+// as this is an extension, never generate protocol error - just ignore problems
+nsresult
+Http2Session::RecvOrigin(Http2Session *self)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ORIGIN);
+  LOG3(("Http2Session::RecvOrigin %p Flags 0x%X id 0x%X\n", self,
+        self->mInputFrameFlags, self->mInputFrameID));
+
+  if (self->mInputFrameFlags & 0x0F) {
+    LOG3(("Http2Session::RecvOrigin %p leading flags must be 0", self));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  if (self->mInputFrameID) {
+    LOG3(("Http2Session::RecvOrigin %p not stream 0", self));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  if (self->ConnectionInfo()->UsingProxy()) {
+    LOG3(("Http2Session::RecvOrigin %p must not use proxy", self));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  if (!gHttpHandler->AllowOriginExtension()) {
+    LOG3(("Http2Session::RecvOrigin %p origin extension pref'd off", self));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  uint32_t offset = 0;
+  self->mOriginFrameActivated = true;
+
+  while (self->mInputFrameDataSize >= (offset + 2U)) {
+
+    uint16_t originLen = NetworkEndian::readUint16(
+      self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset);
+    LOG3(("Http2Session::RecvOrigin %p origin extension defined as %d bytes\n", self, originLen));
+    if (originLen + 2U + offset > self->mInputFrameDataSize) {
+      LOG3(("Http2Session::RecvOrigin %p origin len too big for frame", self));
+      break;
+    }
+
+    nsAutoCString originString;
+    RefPtr<nsStandardURL> originURL;
+    originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
+    offset += originLen + 2;
+    if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){
+      LOG3(("Http2Session::RecvOrigin %p origin frame string %s failed to parse\n", self, originString.get()));
+      continue;
+    }
+
+    LOG3(("Http2Session::RecvOrigin %p origin frame string %s parsed OK\n", self, originString.get()));
+    bool isHttps = false;
+    if (NS_FAILED(originURL->SchemeIs("https", &isHttps)) || !isHttps) {
+      LOG3(("Http2Session::RecvOrigin %p origin frame not https\n", self));
+      continue;
+    }
+
+    int32_t port = -1;
+    originURL->GetPort(&port);
+    if (port == -1) {
+      port = 443;
+    }
+    // dont use ->GetHostPort because we want explicit 443
+    nsAutoCString host;
+    originURL->GetHost(host);
+    nsAutoCString key(host);
+    key.Append(':');
+    key.AppendInt(port);
+    if (!self->mOriginFrame.Get(key)) {
+      self->mOriginFrame.Put(key, true);
+      RefPtr<nsHttpConnection> conn(self->HttpConnection());
+      MOZ_ASSERT(conn.get());
+      gHttpHandler->ConnMgr()->RegisterOriginCoalescingKey(conn, host, port);
+    } else {
+      LOG3(("Http2Session::RecvOrigin %p origin frame already in set\n", self));
+    }
+  }
+
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
 void
 Http2Session::OnTransportStatus(nsITransport* aTransport,
                                 nsresult aStatus, int64_t aProgress)
@@ -3843,16 +3947,25 @@ Http2Session::TakeTransport(nsISocketTra
 
 already_AddRefed<nsHttpConnection>
 Http2Session::TakeHttpConnection()
 {
   MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
   return nullptr;
 }
 
+already_AddRefed<nsHttpConnection>
+Http2Session::HttpConnection()
+{
+  if (mConnection) {
+    return mConnection->HttpConnection();
+  }
+  return nullptr;
+}
+
 void
 Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
 {
   *aOut = nullptr;
 }
 
 //-----------------------------------------------------------------------------
 // unused methods of nsAHttpTransaction
@@ -4000,10 +4113,113 @@ Http2Session::SendPing()
     mPreviousPingThreshold = mPingThreshold;
     mPreviousUsed = true;
     mPingThreshold = gHttpHandler->NetworkChangedTimeout();
   }
   GeneratePing(false);
   Unused << ResumeRecv();
 }
 
+bool
+Http2Session::TestOriginFrame(const nsACString &hostname, int32_t port)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(mOriginFrameActivated);
+
+  nsAutoCString key(hostname);
+  key.Append (':');
+  key.AppendInt(port);
+  bool rv = mOriginFrame.Get(key);
+  LOG3(("TestOriginFrame() hash.get %p %s %d\n", this, key.get(), rv));
+  if (!rv && ConnectionInfo()) {
+    // the SNI is also implicitly in this list, so consult that too
+    nsHttpConnectionInfo *ci = ConnectionInfo();
+    rv = nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort());
+    LOG3(("TestOriginFrame() %p sni test %d\n", this, rv));
+  }
+  return rv;
+}
+
+bool
+Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
+{
+  if (!mConnection || mClosed || mShouldGoAway) {
+    return false;
+  }
+
+  if (mOriginFrameActivated) {
+    bool originFrameResult = TestOriginFrame(hostname, port);
+    if (!originFrameResult) {
+      return false;
+    }
+  } else {
+    LOG3(("TestJoinConnection %p no origin frame check used.\n", this));
+  }
+
+  nsresult rv;
+  bool isJoined = false;
+
+  nsCOMPtr<nsISupports> securityInfo;
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl;
+
+  mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+  sslSocketControl = do_QueryInterface(securityInfo, &rv);
+  if (NS_FAILED(rv) || !sslSocketControl) {
+    return false;
+  }
+
+  // try all the coalescable versions we support.
+  const SpdyInformation *info = gHttpHandler->SpdyInfo();
+  for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
+    if (info->ProtocolEnabled(index - 1)) {
+      rv = sslSocketControl->TestJoinConnection(info->VersionString[index - 1],
+                                                hostname, port, &isJoined);
+      if (NS_SUCCEEDED(rv) && isJoined) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool
+Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
+{
+  if (!mConnection || mClosed || mShouldGoAway) {
+    return false;
+  }
+  if (mOriginFrameActivated) {
+    bool originFrameResult = TestOriginFrame(hostname, port);
+    if (!originFrameResult) {
+      return false;
+    }
+  } else {
+    LOG3(("JoinConnection %p no origin frame check used.\n", this));
+  }
+
+  nsresult rv;
+  bool isJoined = false;
+
+  nsCOMPtr<nsISupports> securityInfo;
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl;
+
+  mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+  sslSocketControl = do_QueryInterface(securityInfo, &rv);
+  if (NS_FAILED(rv) || !sslSocketControl) {
+    return false;
+  }
+
+  // try all the coalescable versions we support.
+  const SpdyInformation *info = gHttpHandler->SpdyInfo();
+  for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
+    if (info->ProtocolEnabled(index - 1)) {
+      rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
+                                            hostname, port, &isJoined);
+      if (NS_SUCCEEDED(rv) && isJoined) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -45,16 +45,18 @@ public:
 
  Http2Session(nsISocketTransport *, uint32_t version, bool attemptingEarlyData);
 
   MOZ_MUST_USE bool AddStream(nsAHttpTransaction *, int32_t,
                               bool, nsIInterfaceRequestor *) override;
   bool CanReuse() override { return !mShouldGoAway && !mClosed; }
   bool RoomForMoreStreams() override;
   uint32_t SpdyVersion() override;
+  bool TestJoinConnection(const nsACString &hostname, int32_t port) override;
+  bool JoinConnection(const nsACString &hostname, int32_t port) override;
 
   // When the connection is active this is called up to once every 1 second
   // return the interval (in seconds) that the connection next wants to
   // have this invoked. It might happen sooner depending on the needs of
   // other connections.
   uint32_t  ReadTimeoutTick(PRIntervalTime now) override;
 
   // Idle time represents time since "goodput".. e.g. a data or header frame
@@ -84,17 +86,18 @@ public:
     FRAME_TYPE_RST_STREAM    = 0x3,
     FRAME_TYPE_SETTINGS      = 0x4,
     FRAME_TYPE_PUSH_PROMISE  = 0x5,
     FRAME_TYPE_PING          = 0x6,
     FRAME_TYPE_GOAWAY        = 0x7,
     FRAME_TYPE_WINDOW_UPDATE = 0x8,
     FRAME_TYPE_CONTINUATION  = 0x9,
     FRAME_TYPE_ALTSVC        = 0xA,
-    FRAME_TYPE_LAST          = 0xB
+    FRAME_TYPE_ORIGIN        = 0xB,
+    FRAME_TYPE_LAST          = 0xC
   };
 
   // NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
   // code NO_ERROR to be NO_HTTP_ERROR
   enum errorType {
     NO_HTTP_ERROR = 0,
     PROTOCOL_ERROR = 1,
     INTERNAL_ERROR = 2,
@@ -180,16 +183,17 @@ public:
   static nsresult RecvRstStream(Http2Session *);
   static nsresult RecvSettings(Http2Session *);
   static nsresult RecvPushPromise(Http2Session *);
   static nsresult RecvPing(Http2Session *);
   static nsresult RecvGoAway(Http2Session *);
   static nsresult RecvWindowUpdate(Http2Session *);
   static nsresult RecvContinuation(Http2Session *);
   static nsresult RecvAltSvc(Http2Session *);
+  static nsresult RecvOrigin(Http2Session *);
 
   char       *EnsureOutputBuffer(uint32_t needed);
 
   template<typename charType>
   void CreateFrameHeader(charType dest, uint16_t frameLength,
                          uint8_t frameType, uint8_t frameFlags,
                          uint32_t streamID);
 
@@ -238,16 +242,19 @@ public:
   bool UseH2Deps() { return mUseH2Deps; }
 
   // overload of nsAHttpTransaction
   MOZ_MUST_USE nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
   MOZ_MUST_USE nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
   MOZ_MUST_USE bool Do0RTT() override final { return true; }
   MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) override final;
 
+  // For use by an HTTP2Stream
+  void Received421(nsHttpConnectionInfo *ci);
+
 private:
 
   // These internal states do not correspond to the states of the HTTP/2 specification
   enum internalStateType {
     BUFFERING_OPENING_SETTINGS,
     BUFFERING_FRAME_HEADER,
     BUFFERING_CONTROL_FRAME,
     PROCESSING_DATA_FRAME_PADDING_CONTROL,
@@ -503,16 +510,20 @@ private:
   bool mGoAwayOnPush;
 
   bool mUseH2Deps;
 
   bool mAttemptingEarlyData;
   // The ID(s) of the stream(s) that we are getting 0RTT data from.
   nsTArray<uint32_t> m0RTTStreams;
 
+  bool TestOriginFrame(const nsACString &name, int32_t port);
+  bool mOriginFrameActivated;
+  nsDataHashtable<nsCStringHashKey, bool> mOriginFrame;
+
 private:
 /// connect tunnels
   void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
   void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
   void RegisterTunnel(Http2Stream *);
   void UnRegisterTunnel(Http2Stream *);
   uint32_t FindTunnelCount(nsHttpConnectionInfo *);
   nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -1017,29 +1017,35 @@ Http2Stream::ConvertResponseHeaders(Http
   decompressor->GetStatus(statusString);
   if (statusString.IsEmpty()) {
     LOG3(("Http2Stream::ConvertResponseHeaders %p Error - no status\n", this));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   nsresult errcode;
   httpResponseCode = statusString.ToInteger(&errcode);
+  LOG3(("Http2Stream::ConvertResponseHeaders %p response code %d\n", this, httpResponseCode));
   if (mIsTunnel) {
     LOG3(("Http2Stream %p Tunnel Response code %d", this, httpResponseCode));
     if ((httpResponseCode / 100) != 2) {
       MapStreamToPlainText();
     }
   }
 
   if (httpResponseCode == 101) {
     // 8.1.1 of h2 disallows 101.. throw PROTOCOL_ERROR on stream
     LOG3(("Http2Stream::ConvertResponseHeaders %p Error - status == 101\n", this));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
+  if (httpResponseCode == 421) {
+    // Origin Frame requires 421 to remove this origin from the origin set
+    mSession->Received421(mTransaction->ConnectionInfo());
+  }
+
   if (aHeadersIn.Length() && aHeadersOut.Length()) {
     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
     uint32_t ratio =
       aHeadersIn.Length() * 100 / aHeadersOut.Length();
     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
   }
 
   // The decoding went ok. Now we can customize and clean up.
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -259,17 +259,17 @@ HttpBaseChannel::ReleaseMainThreadOnlyRe
 }
 
 nsresult
 HttpBaseChannel::Init(nsIURI *aURI,
                       uint32_t aCaps,
                       nsProxyInfo *aProxyInfo,
                       uint32_t aProxyResolveFlags,
                       nsIURI *aProxyURI,
-                      const nsID& aChannelId)
+                      uint64_t aChannelId)
 {
   LOG(("HttpBaseChannel::Init [this=%p]\n", this));
 
   NS_PRECONDITION(aURI, "null uri");
 
   mURI = aURI;
   mOriginalURI = aURI;
   mDocumentURI = nullptr;
@@ -1328,35 +1328,28 @@ HttpBaseChannel::nsContentEncodings::Pre
 }
 
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-HttpBaseChannel::GetChannelId(nsACString& aChannelId)
+HttpBaseChannel::GetChannelId(uint64_t *aChannelId)
 {
-  char id[NSID_LENGTH];
-  mChannelId.ToProvidedString(id);
-  aChannelId.AssignASCII(id);
+  NS_ENSURE_ARG_POINTER(aChannelId);
+  *aChannelId = mChannelId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::SetChannelId(const nsACString& aChannelId)
+HttpBaseChannel::SetChannelId(uint64_t aChannelId)
 {
-  nsID newId;
-  nsAutoCString idStr(aChannelId);
-  if (newId.Parse(idStr.get())) {
-    mChannelId = newId;
-    return NS_OK;
-  }
-
-  return NS_ERROR_FAILURE;
+  mChannelId = aChannelId;
+  return NS_OK;
 }
 
 NS_IMETHODIMP HttpBaseChannel::GetTopLevelContentWindowId(uint64_t *aWindowId)
 {
   if (!mContentWindowId) {
     nsCOMPtr<nsILoadContext> loadContext;
     GetCallback(loadContext);
     if (loadContext) {
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -110,17 +110,17 @@ public:
   NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_BASE_CHANNEL_IID)
 
   HttpBaseChannel();
 
   virtual MOZ_MUST_USE nsresult Init(nsIURI *aURI, uint32_t aCaps,
                                      nsProxyInfo *aProxyInfo,
                                      uint32_t aProxyResolveFlags,
                                      nsIURI *aProxyURI,
-                                     const nsID& aChannelId);
+                                     uint64_t aChannelId);
 
   // nsIRequest
   NS_IMETHOD GetName(nsACString& aName) override;
   NS_IMETHOD IsPending(bool *aIsPending) override;
   NS_IMETHOD GetStatus(nsresult *aStatus) override;
   NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup) override;
   NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup) override;
   NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) override;
@@ -197,18 +197,18 @@ public:
   NS_IMETHOD GetRequestContextID(uint64_t *aRCID) override;
   NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
   NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
   NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SetRequestContextID(uint64_t aRCID) override;
   NS_IMETHOD GetIsMainDocumentChannel(bool* aValue) override;
   NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
   NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
-  NS_IMETHOD GetChannelId(nsACString& aChannelId) override;
-  NS_IMETHOD SetChannelId(const nsACString& aChannelId) override;
+  NS_IMETHOD GetChannelId(uint64_t *aChannelId) override;
+  NS_IMETHOD SetChannelId(uint64_t aChannelId) override;
   NS_IMETHOD GetTopLevelContentWindowId(uint64_t *aContentWindowId) override;
   NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
   NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t *aWindowId) override;
   NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
   NS_IMETHOD GetIsTrackingResource(bool* aIsTrackingResource) override;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
@@ -606,17 +606,17 @@ protected:
   nsCString mPreferredCachedAltDataType;
   // Holds the name of the alternative data type the channel returned.
   nsCString mAvailableCachedAltDataType;
   int64_t   mAltDataLength;
 
   bool mForceMainDocumentChannel;
   bool mIsTrackingResource;
 
-  nsID mChannelId;
+  uint64_t mChannelId;
 
   nsString mIntegrityMetadata;
 
   // Classified channel's matched information
   nsCString mMatchedList;
   nsCString mMatchedProvider;
   nsCString mMatchedPrefix;
 };
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1416,17 +1416,17 @@ class Redirect1Event : public ChannelEve
 {
  public:
   Redirect1Event(HttpChannelChild* child,
                  const uint32_t& registrarId,
                  const URIParams& newURI,
                  const uint32_t& redirectFlags,
                  const nsHttpResponseHead& responseHead,
                  const nsACString& securityInfoSerialization,
-                 const nsACString& channelId)
+                 const uint64_t& channelId)
   : mChild(child)
   , mRegistrarId(registrarId)
   , mNewURI(newURI)
   , mRedirectFlags(redirectFlags)
   , mResponseHead(responseHead)
   , mSecurityInfoSerialization(securityInfoSerialization)
   , mChannelId(channelId) {}
 
@@ -1445,26 +1445,26 @@ class Redirect1Event : public ChannelEve
   }
  private:
   HttpChannelChild*   mChild;
   uint32_t            mRegistrarId;
   URIParams           mNewURI;
   uint32_t            mRedirectFlags;
   nsHttpResponseHead  mResponseHead;
   nsCString           mSecurityInfoSerialization;
-  nsCString           mChannelId;
+  uint64_t            mChannelId;
 };
 
 mozilla::ipc::IPCResult
 HttpChannelChild::RecvRedirect1Begin(const uint32_t& registrarId,
                                      const URIParams& newUri,
                                      const uint32_t& redirectFlags,
                                      const nsHttpResponseHead& responseHead,
                                      const nsCString& securityInfoSerialization,
-                                     const nsCString& channelId)
+                                     const uint64_t& channelId)
 {
   // TODO: handle security info
   LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
   mEventQ->RunOrEnqueue(new Redirect1Event(this, registrarId, newUri,
                                            redirectFlags, responseHead,
                                            securityInfoSerialization,
                                            channelId));
   return IPC_OK();
@@ -1532,17 +1532,17 @@ HttpChannelChild::SetupRedirect(nsIURI* 
 }
 
 void
 HttpChannelChild::Redirect1Begin(const uint32_t& registrarId,
                                  const URIParams& newUri,
                                  const uint32_t& redirectFlags,
                                  const nsHttpResponseHead& responseHead,
                                  const nsACString& securityInfoSerialization,
-                                 const nsACString& channelId)
+                                 const uint64_t& channelId)
 {
   LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
 
   nsCOMPtr<nsIURI> uri = DeserializeURI(newUri);
 
   if (!securityInfoSerialization.IsEmpty()) {
     NS_DeserializeObject(securityInfoSerialization,
                          getter_AddRefs(mSecurityInfo));
@@ -2413,19 +2413,17 @@ HttpChannelChild::ContinueAsyncOpen()
   openArgs.contentTypeHint() = mContentTypeHint;
 
   nsresult rv = mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &openArgs.loadInfo());
   NS_ENSURE_SUCCESS(rv, rv);
 
   EnsureRequestContextID();
   openArgs.requestContextID() = mRequestContextID;
 
-  char chid[NSID_LENGTH];
-  mChannelId.ToProvidedString(chid);
-  openArgs.channelId().AssignASCII(chid);
+  openArgs.channelId() = mChannelId;
 
   openArgs.contentWindowId() = contentWindowId;
   openArgs.topLevelOuterContentWindowId() = mTopLevelOuterContentWindowId;
 
   if (tabChild && !tabChild->IPCOpen()) {
     return NS_ERROR_FAILURE;
   }
 
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -140,17 +140,17 @@ protected:
   mozilla::ipc::IPCResult RecvOnProgress(const int64_t& progress, const int64_t& progressMax) override;
   mozilla::ipc::IPCResult RecvOnStatus(const nsresult& status) override;
   mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& status) override;
   mozilla::ipc::IPCResult RecvRedirect1Begin(const uint32_t& registrarId,
                                              const URIParams& newURI,
                                              const uint32_t& redirectFlags,
                                              const nsHttpResponseHead& responseHead,
                                              const nsCString& securityInfoSerialization,
-                                             const nsCString& channelId) override;
+                                             const uint64_t& channelId) override;
   mozilla::ipc::IPCResult RecvRedirect3Complete() override;
   mozilla::ipc::IPCResult RecvAssociateApplicationCache(const nsCString& groupID,
                                                         const nsCString& clientID) override;
   mozilla::ipc::IPCResult RecvFlushedForDiversion() override;
   mozilla::ipc::IPCResult RecvDivertMessages() override;
   mozilla::ipc::IPCResult RecvDeleteSelf() override;
   mozilla::ipc::IPCResult RecvFinishInterceptedRedirect() override;
 
@@ -356,17 +356,17 @@ private:
   void OnStatus(const nsresult& status);
   void FailedAsyncOpen(const nsresult& status);
   void HandleAsyncAbort();
   void Redirect1Begin(const uint32_t& registrarId,
                       const URIParams& newUri,
                       const uint32_t& redirectFlags,
                       const nsHttpResponseHead& responseHead,
                       const nsACString& securityInfoSerialization,
-                      const nsACString& channelId);
+                      const uint64_t& channelId);
   bool Redirect3Complete(OverrideRunnable* aRunnable);
   void DeleteSelf();
 
   // Create a a new channel to be used in a redirection, based on the provided
   // response headers.
   MOZ_MUST_USE nsresult SetupRedirect(nsIURI* uri,
                                       const nsHttpResponseHead* responseHead,
                                       const uint32_t& redirectFlags,
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -323,17 +323,17 @@ HttpChannelParent::DoAsyncOpen(  const U
                                  const uint32_t&            aCacheKey,
                                  const uint64_t&            aRequestContextID,
                                  const OptionalCorsPreflightArgs& aCorsPreflightArgs,
                                  const uint32_t&            aInitialRwin,
                                  const bool&                aBlockAuthPrompt,
                                  const bool&                aSuspendAfterSynthesizeResponse,
                                  const bool&                aAllowStaleCacheContent,
                                  const nsCString&           aContentTypeHint,
-                                 const nsCString&           aChannelId,
+                                 const uint64_t&            aChannelId,
                                  const uint64_t&            aContentWindowId,
                                  const nsCString&           aPreferredAlternativeType,
                                  const uint64_t&            aTopLevelOuterContentWindowId)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
@@ -1439,20 +1439,20 @@ HttpChannelParent::StartRedirect(uint32_
   SerializeURI(newURI, uriParams);
 
   nsCString secInfoSerialization;
   UpdateAndSerializeSecurityInfo(secInfoSerialization);
 
   // If the channel is a HTTP channel, we also want to inform the child
   // about the parent's channelId attribute, so that both parent and child
   // share the same ID. Useful for monitoring channel activity in devtools.
-  nsAutoCString channelId;
+  uint64_t channelId;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (httpChannel) {
-    nsresult rv = httpChannel->GetChannelId(channelId);
+    nsresult rv = httpChannel->GetChannelId(&channelId);
     NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
   }
 
   nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
   bool result = false;
   if (!mIPCClosed) {
     result = SendRedirect1Begin(registrarId, uriParams, redirectFlags,
                                 responseHead ? *responseHead
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -141,17 +141,17 @@ protected:
               const uint32_t&            aCacheKey,
               const uint64_t&            aRequestContextID,
               const OptionalCorsPreflightArgs& aCorsPreflightArgs,
               const uint32_t&            aInitialRwin,
               const bool&                aBlockAuthPrompt,
               const bool&                aSuspendAfterSynthesizeResponse,
               const bool&                aAllowStaleCacheContent,
               const nsCString&           aContentTypeHint,
-              const nsCString&           aChannelId,
+              const uint64_t&            aChannelId,
               const uint64_t&            aContentWindowId,
               const nsCString&           aPreferredAlternativeType,
               const uint64_t&            aTopLevelOuterContentWindowId);
 
   virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
   virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;
   virtual mozilla::ipc::IPCResult RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual mozilla::ipc::IPCResult RecvSuspend() override;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -53,23 +53,23 @@ NullHttpChannel::Init(nsIURI *aURI,
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // NullHttpChannel::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-NullHttpChannel::GetChannelId(nsACString& aChannelId)
+NullHttpChannel::GetChannelId(uint64_t *aChannelId)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-NullHttpChannel::SetChannelId(const nsACString& aChannelId)
+NullHttpChannel::SetChannelId(uint64_t aChannelId)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetTopLevelContentWindowId(uint64_t *aWindowId)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -131,17 +131,17 @@ child:
 
   // Called to initiate content channel redirect, starts talking to sinks
   // on the content process and reports result via Redirect2Verify above
   async Redirect1Begin(uint32_t           registrarId,
                        URIParams          newUri,
                        uint32_t           redirectFlags,
                        nsHttpResponseHead responseHead,
                        nsCString          securityInfoSerialization,
-                       nsCString          channelId);
+                       uint64_t           channelId);
 
   // Called if redirect successful so that child can complete setup.
   async Redirect3Complete();
 
   // Associate the child with an application ids
   async AssociateApplicationCache(nsCString groupID,
                                   nsCString clientID);
 
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -124,16 +124,19 @@ public:
     // the same connection and work around buggy servers.
     virtual bool LastTransactionExpectedNoContent() = 0;
     virtual void   SetLastTransactionExpectedNoContent(bool) = 0;
 
     // Transfer the base http connection object along with a
     // reference to it to the caller.
     virtual already_AddRefed<nsHttpConnection> TakeHttpConnection() = 0;
 
+    // Like TakeHttpConnection() but do not drop our own ref
+    virtual already_AddRefed<nsHttpConnection> HttpConnection() = 0;
+
     // Get the nsISocketTransport used by the connection without changing
     //  references or ownership.
     virtual nsISocketTransport *Transport() = 0;
 
     // The number of transaction bytes written out on this HTTP Connection, does
     // not count CONNECT tunnel setup
     virtual int64_t BytesWritten() = 0;
 
@@ -156,17 +159,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpCon
     MOZ_MUST_USE nsresult TakeTransport(nsISocketTransport **,    \
                                         nsIAsyncInputStream **,   \
                                         nsIAsyncOutputStream **) override; \
     bool IsPersistent() override;                         \
     bool IsReused() override;                             \
     void DontReuse() override;                            \
     MOZ_MUST_USE nsresult PushBack(const char *, uint32_t) override; \
     already_AddRefed<nsHttpConnection> TakeHttpConnection() override; \
-    /*                                                    \
+    already_AddRefed<nsHttpConnection> HttpConnection() override; \
+    /*                                                                  \
        Thes methods below have automatic definitions that just forward the \
        function to a lower level connection object        \
     */                                                    \
     void GetConnectionInfo(nsHttpConnectionInfo **result) \
       override                                            \
     {                                                     \
       if (!(fwdObject)) {                                 \
           *result = nullptr;                              \
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -330,17 +330,17 @@ nsHttpChannel::ReleaseMainThreadOnlyRefe
 }
 
 nsresult
 nsHttpChannel::Init(nsIURI *uri,
                     uint32_t caps,
                     nsProxyInfo *proxyInfo,
                     uint32_t proxyResolveFlags,
                     nsIURI *proxyURI,
-                    const nsID& channelId)
+                    uint64_t channelId)
 {
     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo,
                                         proxyResolveFlags, proxyURI, channelId);
     if (NS_FAILED(rv))
         return rv;
 
     LOG(("nsHttpChannel::Init [this=%p]\n", this));
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -127,17 +127,17 @@ public:
     NS_IMETHOD GetRequestMethod(nsACString& aMethod) override;
 
     nsHttpChannel();
 
     virtual MOZ_MUST_USE nsresult Init(nsIURI *aURI, uint32_t aCaps,
                                        nsProxyInfo *aProxyInfo,
                                        uint32_t aProxyResolveFlags,
                                        nsIURI *aProxyURI,
-                                       const nsID& aChannelId) override;
+                                       uint64_t aChannelId) override;
 
     MOZ_MUST_USE nsresult OnPush(const nsACString &uri,
                                  Http2PushedStream *pushedStream);
 
     static bool IsRedirectStatus(uint32_t status);
     static bool WillRedirect(nsHttpResponseHead * response);
 
 
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -879,16 +879,34 @@ nsHttpConnection::DontReuse()
     mKeepAlive = false;
     mDontReuse = true;
     mIdleTimeout = 0;
     if (mSpdySession)
         mSpdySession->DontReuse();
 }
 
 bool
+nsHttpConnection::TestJoinConnection(const nsACString &hostname, int32_t port)
+{
+    if (mSpdySession && CanDirectlyActivate()) {
+        return mSpdySession->TestJoinConnection(hostname, port);
+    }
+    return false;
+}
+
+bool
+nsHttpConnection::JoinConnection(const nsACString &hostname, int32_t port)
+{
+    if (mSpdySession && CanDirectlyActivate()) {
+        return mSpdySession->JoinConnection(hostname, port);
+    }
+    return false;
+}
+
+bool
 nsHttpConnection::CanReuse()
 {
     if (mDontReuse || !mRemainingConnectionUses) {
         return false;
     }
 
     if ((mTransaction ? (mTransaction->IsDone() ? 0U : 1U) : 0U) >=
         mRemainingConnectionUses) {
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -217,16 +217,19 @@ public:
     // connection since CheckForTraffic() was called.
     bool NoTraffic() {
         return mTrafficStamp &&
             (mTrafficCount == (mTotalBytesWritten + mTotalBytesRead));
     }
     // override of nsAHttpConnection
     virtual uint32_t Version();
 
+    bool TestJoinConnection(const nsACString &hostname, int32_t port);
+    bool JoinConnection(const nsACString &hostname, int32_t port);
+
 private:
     // Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
     enum TCPKeepaliveConfig {
       kTCPKeepaliveDisabled = 0,
       kTCPKeepaliveShortLivedConfig,
       kTCPKeepaliveLongLivedConfig
     };
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -95,16 +95,17 @@ nsHttpConnectionMgr::nsHttpConnectionMgr
     , mCurrentTopLevelOuterContentWindowId(0)
 {
     LOG(("Creating nsHttpConnectionMgr @%p\n", this));
 }
 
 nsHttpConnectionMgr::~nsHttpConnectionMgr()
 {
     LOG(("Destroying nsHttpConnectionMgr @%p\n", this));
+    MOZ_ASSERT(mCoalescingHash.Count() == 0);
     if (mTimeoutTick)
         mTimeoutTick->Cancel();
 }
 
 nsresult
 nsHttpConnectionMgr::EnsureSocketThreadTarget()
 {
     nsresult rv;
@@ -554,123 +555,28 @@ nsHttpConnectionMgr::ClearConnectionHist
             ent->PendingQLength()       == 0) {
             iter.Remove();
         }
     }
 
     return NS_OK;
 }
 
-
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::LookupPreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
-    nsConnectionEntry *preferred = nullptr;
-    uint32_t len = ent->mCoalescingKeys.Length();
-    for (uint32_t i = 0; !preferred && (i < len); ++i) {
-        preferred = mSpdyPreferredHash.Get(ent->mCoalescingKeys[i]);
-    }
-    return preferred;
-}
-
-void
-nsHttpConnectionMgr::StorePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
-    if (ent->mCoalescingKeys.IsEmpty()) {
-        return;
-    }
-
-    ent->mInPreferredHash = true;
-    uint32_t len = ent->mCoalescingKeys.Length();
-    for (uint32_t i = 0; i < len; ++i) {
-        mSpdyPreferredHash.Put(ent->mCoalescingKeys[i], ent);
-    }
-}
-
-void
-nsHttpConnectionMgr::RemovePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
-    if (!ent->mInPreferredHash || ent->mCoalescingKeys.IsEmpty()) {
-        return;
-    }
-
-    ent->mInPreferredHash = false;
-    uint32_t len = ent->mCoalescingKeys.Length();
-    for (uint32_t i = 0; i < len; ++i) {
-        mSpdyPreferredHash.Remove(ent->mCoalescingKeys[i]);
-    }
-}
-
-// Given a nsHttpConnectionInfo find the connection entry object that
-// contains either the nshttpconnection or nshttptransaction parameter.
-// Normally this is done by the hashkey lookup of connectioninfo,
-// but if spdy coalescing is in play it might be found in a redirected
-// entry
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
-                                           nsHttpConnection *conn,
-                                           nsHttpTransaction *trans)
-{
-    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-    if (!ci)
-        return nullptr;
-
-    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
-
-    // If there is no sign of coalescing (or it is disabled) then just
-    // return the primary hash lookup
-    if (!ent || !ent->mUsingSpdy || ent->mCoalescingKeys.IsEmpty())
-        return ent;
-
-    // If there is no preferred coalescing entry for this host (or the
-    // preferred entry is the one that matched the mCT hash lookup) then
-    // there is only option
-    nsConnectionEntry *preferred = LookupPreferredHash(ent);
-    if (!preferred || (preferred == ent))
-        return ent;
-
-    if (conn) {
-        // The connection could be either in preferred or ent. It is most
-        // likely the only active connection in preferred - so start with that.
-        if (preferred->mActiveConns.Contains(conn))
-            return preferred;
-        if (preferred->mIdleConns.Contains(conn))
-            return preferred;
-    }
-
-    if (trans) {
-        if (preferred->mUrgentStartQ.Contains(trans, PendingComparator())) {
-            return preferred;
-        }
-
-        nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
-        if (preferred->mPendingTransactionTable.Get(
-            trans->TopLevelOuterContentWindowId(), &infoArray)) {
-            if (infoArray->Contains(trans, PendingComparator())) {
-                return preferred;
-            }
-        }
-    }
-
-    // Neither conn nor trans found in preferred, use the default entry
-    return ent;
-}
-
 nsresult
 nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
          this, conn));
 
-    if (!conn->ConnectionInfo())
+    if (!conn->ConnectionInfo()) {
         return NS_ERROR_UNEXPECTED;
-
-    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
-                                                   conn, nullptr);
+    }
+
+    nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
 
     RefPtr<nsHttpConnection> deleteProtector(conn);
     if (!ent || !ent->mIdleConns.RemoveElement(conn))
         return NS_ERROR_UNEXPECTED;
 
     conn->Close(NS_ERROR_ABORT);
     mNumIdleConns--;
     ConditionallyStopPruneDeadConnectionsTimer();
@@ -684,237 +590,244 @@ nsHttpConnectionMgr::RemoveIdleConnectio
 
     LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p",
          this, conn));
 
     if (!conn->ConnectionInfo()) {
         return NS_ERROR_UNEXPECTED;
     }
 
-    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
-                                                   conn, nullptr);
+    nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
 
     if (!ent || !ent->mIdleConns.RemoveElement(conn)) {
         return NS_ERROR_UNEXPECTED;
     }
 
     mNumIdleConns--;
     return NS_OK;
 }
 
+nsHttpConnection *
+nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(nsConnectionEntry *ent,
+                                                        const nsCString &key,
+                                                        bool justKidding)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    MOZ_ASSERT(ent->mConnInfo);
+    nsHttpConnectionInfo *ci = ent->mConnInfo;
+
+    nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(key);
+    if (!listOfWeakConns) {
+        return nullptr;
+    }
+
+    uint32_t listLen = listOfWeakConns->Length();
+    for (uint32_t j = 0; j < listLen; ) {
+
+        RefPtr<nsHttpConnection> potentialMatch = do_QueryReferent(listOfWeakConns->ElementAt(j));
+        if (!potentialMatch) {
+            // This is a connection that needs to be removed from the list
+            LOG(("FindCoalescableConnectionByHashKey() found old conn %p that has null weak ptr - removing\n",
+                 listOfWeakConns->ElementAt(j).get()));
+            if (j != listLen - 1) {
+                listOfWeakConns->Elements()[j] = listOfWeakConns->Elements()[listLen - 1];
+            }
+            listOfWeakConns->RemoveElementAt(listLen - 1);
+            MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
+            listLen--;
+            continue; // without adjusting iterator
+        }
+
+        bool couldJoin;
+        if (justKidding) {
+            couldJoin = potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
+        } else {
+            couldJoin = potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
+        }
+        if (couldJoin) {
+            LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join ok\n",
+                 potentialMatch.get(), key.get(), ci->HashKey().get()));
+            return potentialMatch.get();
+        } else {
+            LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join failed\n",
+                 potentialMatch.get(), key.get(), ci->HashKey().get()));
+        }
+        ++j; // bypassed by continue when weakptr fails
+    }
+
+    if (!listLen) { // shrunk to 0 while iterating
+        LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
+        mCoalescingHash.Remove(key);
+    }
+    return nullptr;
+}
+
+static void
+BuildOriginFrameHashKey(nsACString &newKey, nsHttpConnectionInfo *ci,
+                        const nsACString &host, int32_t port)
+{
+    newKey.Assign(host);
+    if (ci->GetAnonymous()) {
+        newKey.AppendLiteral("~A:");
+    } else {
+        newKey.AppendLiteral("~.:");
+    }
+    newKey.AppendInt(port);
+    newKey.AppendLiteral("/[");
+    nsAutoCString suffix;
+    ci->GetOriginAttributes().CreateSuffix(suffix);
+    newKey.Append(suffix);
+    newKey.AppendLiteral("]viaORIGIN.FRAME");
+}
+
+nsHttpConnection *
+nsHttpConnectionMgr::FindCoalescableConnection(nsConnectionEntry *ent,
+                                               bool justKidding)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    MOZ_ASSERT(ent->mConnInfo);
+    nsHttpConnectionInfo *ci = ent->mConnInfo;
+    LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));
+    // First try and look it up by origin frame
+    nsCString newKey;
+    BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort());
+    nsHttpConnection *conn =
+        FindCoalescableConnectionByHashKey(ent, newKey, justKidding);
+    if (conn) {
+        LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
+             ci->HashKey().get(), conn, newKey.get()));
+        return conn;
+    }
+
+    // now check for DNS based keys
+    // deleted conns (null weak pointers) are removed from list
+    uint32_t keyLen = ent->mCoalescingKeys.Length();
+    for (uint32_t i = 0; i < keyLen; ++i) {
+        conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i], justKidding);
+        if (conn) {
+            LOG(("FindCoalescableConnection(%s) match conn %p on dns key %s\n",
+                 ci->HashKey().get(), conn, ent->mCoalescingKeys[i].get()));
+            return conn;
+        }
+    }
+
+    LOG(("FindCoalescableConnection(%s) no matching conn\n", ci->HashKey().get()));
+    return nullptr;
+}
+
+void
+nsHttpConnectionMgr::UpdateCoalescingForNewConn(nsHttpConnection *conn,
+                                                nsConnectionEntry *ent)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    MOZ_ASSERT(conn);
+    MOZ_ASSERT(conn->ConnectionInfo());
+    MOZ_ASSERT(ent);
+    MOZ_ASSERT(mCT.Get(conn->ConnectionInfo()->HashKey()) == ent);
+
+    nsHttpConnection *existingConn = FindCoalescableConnection(ent, true);
+    if (existingConn) {
+        LOG(("UpdateCoalescingForNewConn() found active conn that could have served newConn\n"
+             "graceful close of conn=%p to migrate to %p\n", conn, existingConn));
+        conn->DontReuse();
+        return;
+    }
+
+    // This connection might go into the mCoalescingHash for new transactions to be coalesced onto
+    // if it can accept new transactions
+    if (!conn->CanDirectlyActivate()) {
+        return;
+    }
+
+    uint32_t keyLen = ent->mCoalescingKeys.Length();
+    for (uint32_t i = 0;i < keyLen; ++i) {
+        LOG(("UpdateCoalescingForNewConn() registering conn %p %s under key %s\n",
+             conn, conn->ConnectionInfo()->HashKey().get(), ent->mCoalescingKeys[i].get()));
+        nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(ent->mCoalescingKeys[i]);
+        if (!listOfWeakConns) {
+            LOG(("UpdateCoalescingForNewConn() need new list element\n"));
+            listOfWeakConns = new nsTArray<nsWeakPtr>(1);
+            mCoalescingHash.Put(ent->mCoalescingKeys[i], listOfWeakConns);
+        }
+        listOfWeakConns->AppendElement(
+            do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
+    }
+
+    // Cancel any other pending connections - their associated transactions
+    // are in the pending queue and will be dispatched onto this new connection
+    for (int32_t index = ent->mHalfOpens.Length() - 1; index >= 0; --index) {
+        LOG(("UpdateCoalescingForNewConn() forcing halfopen abandon %p\n",
+             ent->mHalfOpens[index]));
+        ent->mHalfOpens[index]->Abandon();
+    }
+
+    if (ent->mActiveConns.Length() > 1) {
+        // this is a new connection that can be coalesced onto. hooray!
+        // if there are other connection to this entry (e.g.
+        // some could still be handshaking, shutting down, etc..) then close
+        // them down after any transactions that are on them are complete.
+        // This probably happened due to the parallel connection algorithm
+        // that is used only before the host is known to speak h2.
+        for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
+            nsHttpConnection *otherConn = ent->mActiveConns[index];
+            if (otherConn != conn) {
+                LOG(("UpdateCoalescingForNewConn() shutting down connection (%p) because new "
+                     "spdy connection (%p) takes precedence\n", otherConn, conn));
+                otherConn->DontReuse();
+            }
+        }
+    }
+}
+
 // This function lets a connection, after completing the NPN phase,
 // report whether or not it is using spdy through the usingSpdy
 // argument. It would not be necessary if NPN were driven out of
 // the connection manager. The connection entry associated with the
 // connection is then updated to indicate whether or not we want to use
-// spdy with that host and update the preliminary preferred host
+// spdy with that host and update the coalescing hash
 // entries used for de-sharding hostsnames.
 void
 nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
                                           bool usingSpdy)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
-    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
-                                                   conn, nullptr);
-
-    if (!ent)
+    if (!conn->ConnectionInfo()) {
         return;
-
-    if (!usingSpdy)
+    }
+    nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
+    if (!ent || !usingSpdy) {
         return;
+    }
 
     ent->mUsingSpdy = true;
     mNumSpdyActiveConns++;
 
+    // adjust timeout timer
     uint32_t ttl = conn->TimeToLive();
     uint64_t timeOfExpire = NowInSeconds() + ttl;
-    if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
+    if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
         PruneDeadConnectionsAfter(ttl);
-
-    // Lookup preferred directly from the hash instead of using
-    // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
-    // check at this point because the cert is never part of the hash
-    // lookup. Filtering on that has to be done at the time of use
-    // rather than the time of registration (i.e. now).
-    nsConnectionEntry *joinedConnection;
-    nsConnectionEntry *preferred = LookupPreferredHash(ent);
-
-    LOG(("ReportSpdyConnection %p,%s conn %p prefers %p,%s\n",
-         ent, ent->mConnInfo->Origin(), conn, preferred,
-         preferred ? preferred->mConnInfo->Origin() : ""));
-
-    if (!preferred) {
-        // this becomes the preferred entry
-        StorePreferredHash(ent);
-        preferred = ent;
-    } else if ((preferred != ent) &&
-               (joinedConnection = GetSpdyPreferredEnt(ent)) &&
-               (joinedConnection != ent)) {
-        //
-        // A connection entry (e.g. made with a different hostname) with
-        // the same IP address is preferred for future transactions over this
-        // connection entry. Gracefully close down the connection to help
-        // new transactions migrate over.
-
-        LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to "
-                 "migrate to preferred (desharding)\n", conn, ent));
-        conn->DontReuse();
-    } else if (preferred != ent) {
-        LOG (("ReportSpdyConnection preferred host may be in false start or "
-              "may have insufficient cert. Leave mapping in place but do not "
-              "abandon this connection yet."));
     }
 
-    if ((preferred == ent) && conn->CanDirectlyActivate()) {
-        // this is a new spdy connection to the preferred entry
-
-        // Cancel any other pending connections - their associated transactions
-        // are in the pending queue and will be dispatched onto this connection
-        for (int32_t index = ent->mHalfOpens.Length() - 1;
-             index >= 0; --index) {
-            LOG(("ReportSpdyConnection forcing halfopen abandon %p\n",
-                 ent->mHalfOpens[index]));
-            ent->mHalfOpens[index]->Abandon();
-        }
-
-        if (ent->mActiveConns.Length() > 1) {
-            // this is a new connection to an established preferred spdy host.
-            // if there is more than 1 live and established spdy connection (e.g.
-            // some could still be handshaking, shutting down, etc..) then close
-            // this one down after any transactions that are on it are complete.
-            // This probably happened due to the parallel connection algorithm
-            // that is used only before the host is known to speak spdy.
-            for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
-                nsHttpConnection *otherConn = ent->mActiveConns[index];
-                if (otherConn != conn) {
-                    LOG(("ReportSpdyConnection shutting down connection (%p) because new "
-                         "spdy connection (%p) takes precedence\n", otherConn, conn));
-                    otherConn->DontReuse();
-                }
-            }
-        }
-    }
+    UpdateCoalescingForNewConn(conn, ent);
 
     nsresult rv = ProcessPendingQ(ent->mConnInfo);
     if (NS_FAILED(rv)) {
         LOG(("ReportSpdyConnection conn=%p ent=%p "
              "failed to process pending queue (%08x)\n", conn, ent,
              static_cast<uint32_t>(rv)));
     }
     rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
     if (NS_FAILED(rv)) {
         LOG(("ReportSpdyConnection conn=%p ent=%p "
              "failed to post event (%08x)\n", conn, ent,
              static_cast<uint32_t>(rv)));
     }
 }
 
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
-{
-    if (!gHttpHandler->IsSpdyEnabled() ||
-        !gHttpHandler->CoalesceSpdy() ||
-        aOriginalEntry->mConnInfo->GetNoSpdy() ||
-        aOriginalEntry->mCoalescingKeys.IsEmpty()) {
-        return nullptr;
-    }
-
-    nsConnectionEntry *preferred = LookupPreferredHash(aOriginalEntry);
-
-    // if there is no redirection no cert validation is required
-    if (preferred == aOriginalEntry)
-        return aOriginalEntry;
-
-    // if there is no preferred host or it is no longer using spdy
-    // then skip pooling
-    if (!preferred || !preferred->mUsingSpdy)
-        return nullptr;
-
-    // if there is not an active spdy session in this entry then
-    // we cannot pool because the cert upon activation may not
-    // be the same as the old one. Active sessions are prohibited
-    // from changing certs.
-
-    nsHttpConnection *activeSpdy = nullptr;
-
-    for (uint32_t index = 0; index < preferred->mActiveConns.Length(); ++index) {
-        if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
-            activeSpdy = preferred->mActiveConns[index];
-            break;
-        }
-    }
-
-    if (!activeSpdy) {
-        // remove the preferred status of this entry if it cannot be
-        // used for pooling.
-        RemovePreferredHash(preferred);
-        LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
-             "preferred host mapping %s to %s removed due to inactivity.\n",
-             aOriginalEntry->mConnInfo->Origin(),
-             preferred->mConnInfo->Origin()));
-
-        return nullptr;
-    }
-
-    // Check that the server cert supports redirection
-    nsresult rv;
-    bool isJoined = false;
-
-    nsCOMPtr<nsISupports> securityInfo;
-    nsCOMPtr<nsISSLSocketControl> sslSocketControl;
-    nsAutoCString negotiatedNPN;
-
-    activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
-    if (!securityInfo) {
-        NS_WARNING("cannot obtain spdy security info");
-        return nullptr;
-    }
-
-    sslSocketControl = do_QueryInterface(securityInfo, &rv);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("sslSocketControl QI Failed");
-        return nullptr;
-    }
-
-    // try all the spdy versions we support.
-    const SpdyInformation *info = gHttpHandler->SpdyInfo();
-    for (uint32_t index = SpdyInformation::kCount;
-         NS_SUCCEEDED(rv) && index > 0; --index) {
-        if (info->ProtocolEnabled(index - 1)) {
-            rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
-                                                  aOriginalEntry->mConnInfo->GetOrigin(),
-                                                  aOriginalEntry->mConnInfo->OriginPort(),
-                                                  &isJoined);
-            if (NS_SUCCEEDED(rv) && isJoined) {
-                break;
-            }
-        }
-    }
-
-    if (NS_FAILED(rv) || !isJoined) {
-        LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
-             "Host %s cannot be confirmed to be joined "
-             "with %s connections. rv=%" PRIx32 " isJoined=%d",
-             preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
-             static_cast<uint32_t>(rv), isJoined));
-        Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, false);
-        return nullptr;
-    }
-
-    // IP pooling confirmed
-    LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
-         "Host %s has cert valid for %s connections, "
-         "so %s will be coalesced with %s",
-         preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
-         aOriginalEntry->mConnInfo->Origin(), preferred->mConnInfo->Origin()));
-    Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
-    return preferred;
-}
-
 //-----------------------------------------------------------------------------
 bool
 nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
                                       nsConnectionEntry *ent,
                                       bool considerAll)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
@@ -1042,16 +955,19 @@ nsHttpConnectionMgr::ProcessPendingQForE
 
     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
          "[ci=%s ent=%p active=%" PRIuSIZE " idle=%" PRIuSIZE " urgent-start queue=%" PRIuSIZE
          "queued=%" PRIuSIZE "]\n",
          ent->mConnInfo->HashKey().get(), ent, ent->mActiveConns.Length(),
          ent->mIdleConns.Length(), ent->mUrgentStartQ.Length(),
          ent->PendingQLength()));
 
+    if (!ent->mUrgentStartQ.Length() && !ent->mPendingTransactionTable.Count()) {
+        return false;
+    }
     ProcessSpdyPendingQ(ent);
 
     bool dispatchedSuccessfully = false;
 
     if (!ent->mUrgentStartQ.IsEmpty()) {
         dispatchedSuccessfully = DispatchPendingQ(ent->mUrgentStartQ,
                                                   ent,
                                                   considerAll);
@@ -1453,17 +1369,17 @@ nsHttpConnectionMgr::TryDispatchTransact
 
     RefPtr<nsHttpConnection> unusedSpdyPersistentConnection;
 
     // step 0
     // look for existing spdy connection - that's always best because it is
     // essentially pipelining without head of line blocking
 
     if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
-        RefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
+        RefPtr<nsHttpConnection> conn = GetSpdyActiveConn(ent);
         if (conn) {
             if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
                 LOG(("   dispatch to spdy: [conn=%p]\n", conn.get()));
                 trans->RemoveDispatchedAsBlocking();  /* just in case */
                 nsresult rv = DispatchTransaction(ent, trans, conn);
                 NS_ENSURE_SUCCESS(rv, rv);
                 return NS_OK;
             }
@@ -1771,27 +1687,16 @@ nsHttpConnectionMgr::ProcessNewTransacti
 
     nsresult rv = NS_OK;
     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
     MOZ_ASSERT(ci);
 
     nsConnectionEntry *ent =
         GetOrCreateConnectionEntry(ci, !!trans->TunnelProvider());
 
-    // SPDY coalescing of hostnames means we might redirect from this
-    // connection entry onto the preferred one.
-    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
-    if (preferredEntry && (preferredEntry != ent)) {
-        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
-             "redirected via coalescing from %s to %s\n", trans,
-             ent->mConnInfo->Origin(), preferredEntry->mConnInfo->Origin()));
-
-        ent = preferredEntry;
-    }
-
     ReportProxyTelemetry(ent);
 
     // Check if the transaction already has a sticky reference to a connection.
     // If so, then we can just use it directly by transferring its reference
     // to the new connection variable instead of searching for a new one
 
     nsAHttpConnection *wrappedConnection = trans->Connection();
     RefPtr<nsHttpConnection> conn;
@@ -1997,24 +1902,23 @@ nsHttpConnectionMgr::DispatchSpdyPending
     // Put the leftovers back in the pending queue and get rid of the
     // transactions we dispatched
     leftovers.SwapElements(pendingQ);
     leftovers.Clear();
 }
 
 // This function tries to dispatch the pending spdy transactions on
 // the connection entry sent in as an argument. It will do so on the
-// active spdy connection either in that same entry or in the
-// redirected 'preferred' entry for the same coalescing hash key if
-// coalescing is enabled.
+// active spdy connection either in that same entry or from the
+// coalescing hash table
 
 void
 nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
 {
-    nsHttpConnection *conn = GetSpdyPreferredConn(ent);
+    nsHttpConnection *conn = GetSpdyActiveConn(ent);
     if (!conn || !conn->CanDirectlyActivate()) {
         return;
     }
 
     DispatchSpdyPendingQ(ent->mUrgentStartQ, ent, conn);
     if (!conn->CanDirectlyActivate()) {
         return;
     }
@@ -2035,70 +1939,78 @@ nsHttpConnectionMgr::OnMsgProcessAllSpdy
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
         ProcessSpdyPendingQ(iter.Data());
     }
 }
 
+// Given a connection entry, return an active h2 connection
+// that can be directly activated or null
 nsHttpConnection *
-nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
+nsHttpConnectionMgr::GetSpdyActiveConn(nsConnectionEntry *ent)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(ent);
 
-    nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
-    // this entry is spdy-enabled if it is involved in a redirect
-    if (preferred) {
-        // all new connections for this entry will use spdy too
-        ent->mUsingSpdy = true;
-    } else {
-        preferred = ent;
-    }
-
-    if (!preferred->mUsingSpdy) {
-        return nullptr;
-    }
-
-    nsHttpConnection *rv = nullptr;
-    uint32_t activeLen = preferred->mActiveConns.Length();
+    nsHttpConnection *experienced = nullptr;
+    nsHttpConnection *noExperience = nullptr;
+    uint32_t activeLen = ent->mActiveConns.Length();
+    nsHttpConnectionInfo *ci = ent->mConnInfo;
     uint32_t index;
 
     // activeLen should generally be 1.. this is a setup race being resolved
     // take a conn who can activate and is experienced
     for (index = 0; index < activeLen; ++index) {
-        nsHttpConnection *tmp = preferred->mActiveConns[index];
-        if (tmp->CanDirectlyActivate() && tmp->IsExperienced()) {
-            rv = tmp;
-            break;
+        nsHttpConnection *tmp = ent->mActiveConns[index];
+        if (tmp->CanDirectlyActivate()) {
+            if (tmp->IsExperienced()) {
+                experienced = tmp;
+                break;
+            }
+            noExperience = tmp; // keep looking for a better option
         }
     }
 
-    // if that worked, cleanup anything else
-    if (rv) {
+    // if that worked, cleanup anything else and exit
+    if (experienced) {
         for (index = 0; index < activeLen; ++index) {
-            nsHttpConnection *tmp = preferred->mActiveConns[index];
+            nsHttpConnection *tmp = ent->mActiveConns[index];
             // in the case where there is a functional h2 session, drop the others
-            if (tmp != rv) {
+            if (tmp != experienced) {
                 tmp->DontReuse();
             }
         }
-        return rv;
+        LOG(("GetSpdyActiveConn() request for ent %p %s "
+             "found an active experienced connection %p in native connection entry\n",
+             ent, ci->HashKey().get(), experienced));
+        return experienced;
+    }
+
+    if (noExperience) {
+        LOG(("GetSpdyActiveConn() request for ent %p %s "
+             "found an active but inexperienced connection %p in native connection entry\n",
+             ent, ci->HashKey().get(), noExperience));
+        return noExperience;
     }
 
-    // take a conn who can activate and leave the rest alone
-    for (index = 0; index < activeLen; ++index) {
-        nsHttpConnection *tmp = preferred->mActiveConns[index];
-        if (tmp->CanDirectlyActivate()) {
-            rv = tmp;
-            break;
-        }
+    // there was no active spdy connection in the connection entry, but
+    // there might be one in the hash table for coalescing
+    nsHttpConnection *existingConn = FindCoalescableConnection(ent, false);
+    if (existingConn) {
+        LOG(("GetSpdyActiveConn() request for ent %p %s "
+             "found an active connection %p in the coalescing hashtable\n",
+             ent, ci->HashKey().get(), existingConn));
+        return existingConn;
     }
-    return rv;
+
+    LOG(("GetSpdyActiveConn() request for ent %p %s "
+         "did not find an active connection\n", ent, ci->HashKey().get()));
+    return nullptr;
 }
 
 //-----------------------------------------------------------------------------
 
 void
 nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2169,16 +2081,17 @@ nsHttpConnectionMgr::OnMsgShutdown(int32
     if (mTimer) {
       mTimer->Cancel();
       mTimer = nullptr;
     }
     if (mTrafficTimer) {
       mTrafficTimer->Cancel();
       mTrafficTimer = nullptr;
     }
+    mCoalescingHash.Clear();
 
     // signal shutdown complete
     nsCOMPtr<nsIRunnable> runnable =
         new ConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
                       0, param);
     NS_DispatchToMainThread(runnable);
 }
 
@@ -2208,18 +2121,20 @@ void
 nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
 
     RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction *>(param);
     trans->SetPriority(priority);
 
-    nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
-                                                   nullptr, trans);
+    if (!trans->ConnectionInfo()) {
+        return;
+    }
+    nsConnectionEntry *ent = mCT.Get(trans->ConnectionInfo()->HashKey());
 
     if (ent) {
         int32_t caps = trans->Caps();
         nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
         if (caps & NS_HTTP_URGENT_START) {
             pendingQ = &(ent->mUrgentStartQ);
         } else {
             pendingQ =
@@ -2253,19 +2168,20 @@ nsHttpConnectionMgr::OnMsgCancelTransact
     // if the transaction owns a connection and the transaction is not done,
     // then ask the connection to close the transaction.  otherwise, close the
     // transaction directly (removing it from the pending queue first).
     //
     RefPtr<nsAHttpConnection> conn(trans->Connection());
     if (conn && !trans->IsDone()) {
         conn->CloseTransaction(trans, closeCode);
     } else {
-        nsConnectionEntry *ent =
-            LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
-
+        nsConnectionEntry *ent = nullptr;
+        if (trans->ConnectionInfo()) {
+            ent = mCT.Get(trans->ConnectionInfo()->HashKey());
+        }
         if (ent) {
             uint32_t caps = trans->Caps();
             int32_t transIndex;
             // We will abandon all half-open sockets belonging to the given
             // transaction.
             nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
             RefPtr<PendingTransactionInfo> pendingTransInfo;
             if (caps & NS_HTTP_URGENT_START) {
@@ -2597,18 +2513,19 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
     nsHttpConnection *conn = static_cast<nsHttpConnection *>(param);
 
     //
     // 1) remove the connection from the active list
     // 2) if keep-alive, add connection to idle list
     // 3) post event to process the pending transaction queue
     //
 
-    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
-                                                   conn, nullptr);
+    nsConnectionEntry *ent = conn->ConnectionInfo() ?
+        mCT.Get(conn->ConnectionInfo()->HashKey()) : nullptr;
+
     if (!ent) {
         // this can happen if the connection is made outside of the
         // connection manager and is being "reclaimed" for use with
         // future transactions. HTTP/2 tunnels work like this.
         ent = GetOrCreateConnectionEntry(conn->ConnectionInfo(), true);
         LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection conn %p "
              "forced new hash entry %s\n",
              conn, conn->ConnectionInfo()->HashKey().get()));
@@ -2735,17 +2652,16 @@ nsHttpConnectionMgr::OnMsgUpdateParam(in
         NS_NOTREACHED("unexpected parameter name");
     }
 }
 
 // nsHttpConnectionMgr::nsConnectionEntry
 nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
 {
     MOZ_COUNT_DTOR(nsConnectionEntry);
-    gHttpHandler->ConnMgr()->RemovePreferredHash(this);
 }
 
 // Read Timeout Tick handlers
 
 void
 nsHttpConnectionMgr::ActivateTimeoutTick()
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2962,23 +2878,16 @@ nsHttpConnectionMgr::OnMsgSpeculativeCon
     SpeculativeConnectArgs *args = static_cast<SpeculativeConnectArgs *>(param);
 
     LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
          args->mTrans->ConnectionInfo()->HashKey().get()));
 
     nsConnectionEntry *ent =
         GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo(), false);
 
-    // If spdy has previously made a preferred entry for this host via
-    // the ip pooling rules. If so, connect to the preferred host instead of
-    // the one directly passed in here.
-    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
-    if (preferredEntry)
-        ent = preferredEntry;
-
     uint32_t parallelSpeculativeConnectLimit =
         gHttpHandler->ParallelSpeculativeConnectLimit();
     bool ignoreIdle = false;
     bool isFromPredictor = false;
     bool allow1918 = false;
 
     if (args->mOverridesOK) {
         parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
@@ -3568,16 +3477,43 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
                 }
             }
         }
     }
 
     return rv;
 }
 
+// register a connection to receive CanJoinConnection() for particular
+// origin keys
+void
+nsHttpConnectionMgr::RegisterOriginCoalescingKey(nsHttpConnection *conn,
+                                                 const nsACString &host,
+                                                 int32_t port)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    nsHttpConnectionInfo *ci = conn ? conn->ConnectionInfo() : nullptr;
+    if (!ci || !conn->CanDirectlyActivate()) {
+        return;
+    }
+
+    nsCString newKey;
+    BuildOriginFrameHashKey(newKey, ci, host, port);
+    nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(newKey);
+    if (!listOfWeakConns) {
+        listOfWeakConns = new nsTArray<nsWeakPtr>(1);
+        mCoalescingHash.Put(newKey, listOfWeakConns);
+    }
+    listOfWeakConns->AppendElement(do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
+
+    LOG(("nsHttpConnectionMgr::RegisterOriginCoalescingKey "
+         "Established New Coalescing Key %s to %p %s\n",
+         newKey.get(), conn, ci->HashKey().get()));
+}
+
 // method for nsITransportEventSink
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
                                                          nsresult status,
                                                          int64_t progress,
                                                          int64_t progressMax)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -3645,17 +3581,17 @@ nsHttpConnectionMgr::nsHalfOpenSocket::O
                 } else {
                     newKey->AppendLiteral("~.:");
                 }
                 newKey->AppendInt(mEnt->mConnInfo->OriginPort());
                 newKey->AppendLiteral("/[");
                 nsAutoCString suffix;
                 mEnt->mConnInfo->GetOriginAttributes().CreateSuffix(suffix);
                 newKey->Append(suffix);
-                newKey->Append(']');
+                newKey->AppendLiteral("]viaDNS");
                 LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
                      "STATUS_CONNECTING_TO Established New Coalescing Key # %d for host "
                      "%s [%s]", i, mEnt->mConnInfo->Origin(), newKey->get()));
             }
             gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
         }
     }
 
@@ -3746,40 +3682,45 @@ already_AddRefed<nsHttpConnection>
 ConnectionHandle::TakeHttpConnection()
 {
     // return our connection object to the caller and clear it internally
     // do not drop our reference - the caller now owns it.
     MOZ_ASSERT(mConn);
     return mConn.forget();
 }
 
+already_AddRefed<nsHttpConnection>
+ConnectionHandle::HttpConnection()
+{
+    RefPtr<nsHttpConnection> rv(mConn);
+    return rv.forget();
+}
 
 // nsConnectionEntry
 
 nsHttpConnectionMgr::
 nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
     : mConnInfo(ci)
     , mUsingSpdy(false)
-    , mInPreferredHash(false)
     , mPreferIPv4(false)
     , mPreferIPv6(false)
     , mUsedForConnection(false)
 {
     MOZ_COUNT_CTOR(nsConnectionEntry);
 }
 
 bool
 nsHttpConnectionMgr::nsConnectionEntry::AvailableForDispatchNow()
 {
     if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) {
         return true;
     }
 
     return gHttpHandler->ConnMgr()->
-        GetSpdyPreferredConn(this) ? true : false;
+        GetSpdyActiveConn(this) ? true : false;
 }
 
 bool
 nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams> *aArg)
 {
     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
 
@@ -3823,19 +3764,20 @@ nsHttpConnectionMgr::GetConnectionData(n
 
     return true;
 }
 
 void
 nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-    nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr);
-    if (ent)
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (ent) {
         ent->ResetIPFamilyPreference();
+    }
 }
 
 uint32_t
 nsHttpConnectionMgr::
 nsConnectionEntry::UnconnectedHalfOpens()
 {
     uint32_t unconnectedHalfOpens = 0;
     for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
@@ -4016,17 +3958,17 @@ nsHttpConnectionMgr::MoveToWildCardConnE
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(specificCI->UsingHttpsProxy());
 
     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p has requested to "
          "change CI from %s to %s\n", proxyConn, specificCI->HashKey().get(),
          wildCardCI->HashKey().get()));
 
-    nsConnectionEntry *ent = LookupConnectionEntry(specificCI, proxyConn, nullptr);
+    nsConnectionEntry *ent = mCT.Get(specificCI->HashKey());
     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p using ent %p (spdy %d)\n",
          proxyConn, ent, ent ? ent->mUsingSpdy : 0));
 
     if (!ent || !ent->mUsingSpdy) {
         return;
     }
 
     nsConnectionEntry *wcEnt = GetOrCreateConnectionEntry(wildCardCI, true);
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -239,32 +239,26 @@ private:
         // connection complete
         uint32_t UnconnectedHalfOpens();
 
         // Remove a particular half open socket from the mHalfOpens array
         void RemoveHalfOpen(nsHalfOpenSocket *);
 
         // Spdy sometimes resolves the address in the socket manager in order
         // to re-coalesce sharded HTTP hosts. The dotted decimal address is
-        // combined with the Anonymous flag from the connection information
+        // combined with the Anonymous flag and OA from the connection information
         // to build the hash key for hosts in the same ip pool.
         //
-        // When a set of hosts are coalesced together one of them is marked
-        // mSpdyPreferred. The mapping is maintained in the connection mananger
-        // mSpdyPreferred hash.
-        //
         nsTArray<nsCString> mCoalescingKeys;
 
         // To have the UsingSpdy flag means some host with the same connection
         // entry has done NPN=spdy/* at some point. It does not mean every
         // connection is currently using spdy.
         bool mUsingSpdy : 1;
 
-        bool mInPreferredHash : 1;
-
         // Flags to remember our happy-eyeballs decision.
         // Reset only by Ctrl-F5 reload.
         // True when we've first connected an IPv4 server for this host,
         // initially false.
         bool mPreferIPv4 : 1;
         // True when we've first connected an IPv6 server for this host,
         // initially false.
         bool mPreferIPv6 : 1;
@@ -302,16 +296,17 @@ private:
             uint32_t maxCount = 0);
 
         // Remove the empty pendingQ in |mPendingTransactionTable|.
         void RemoveEmptyPendingQ();
     };
 
 public:
     static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped);
+    void RegisterOriginCoalescingKey(nsHttpConnection *, const nsACString &host, int32_t port);
 
 private:
 
     // nsHalfOpenSocket is used to hold the state of an opening TCP socket
     // while we wait for it to establish and bind it to a connection
 
     class nsHalfOpenSocket final : public nsIOutputStreamCallback,
                                    public nsITransportEventSink,
@@ -513,26 +508,24 @@ private:
                                  PendingTransactionInfo *pendingTransInfo);
 
     nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
                                                   bool allowWildCard);
 
     MOZ_MUST_USE nsresult MakeNewConnection(nsConnectionEntry *ent,
                                             PendingTransactionInfo *pendingTransInfo);
 
-    // Manage the preferred spdy connection entry for this address
-    nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
-    nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent);
-    void               StorePreferredHash(nsConnectionEntry *ent);
-    void               RemovePreferredHash(nsConnectionEntry *ent);
-    nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
-    nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
-    nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
-                                             nsHttpConnection *conn,
-                                             nsHttpTransaction *trans);
+    // Manage h2 connection coalescing
+    // The hashtable contains arrays of weak pointers to nsHttpConnections
+    nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > mCoalescingHash;
+
+    nsHttpConnection *FindCoalescableConnection(nsConnectionEntry *ent, bool justKidding);
+    nsHttpConnection *FindCoalescableConnectionByHashKey(nsConnectionEntry *ent, const nsCString &key, bool justKidding);
+    void UpdateCoalescingForNewConn(nsHttpConnection *conn, nsConnectionEntry *ent);
+    nsHttpConnection *GetSpdyActiveConn(nsConnectionEntry *ent);
 
     void               ProcessSpdyPendingQ(nsConnectionEntry *ent);
     void               DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                                             nsConnectionEntry *ent,
                                             nsHttpConnection *conn);
     // used to marshall events to the socket transport thread.
     MOZ_MUST_USE nsresult PostEvent(nsConnEventHandler  handler,
                                     int32_t             iparam = 0,
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -46,19 +46,19 @@
 #include "nsIParentalControlsService.h"
 #include "nsPIDOMWindow.h"
 #include "nsINetworkLinkService.h"
 #include "nsHttpChannelAuthProvider.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSocketTransportService2.h"
 #include "nsIOService.h"
-#include "nsIUUIDGenerator.h"
 #include "nsIThrottlingService.h"
 #include "nsISupportsPrimitives.h"
+#include "nsIXULRuntime.h"
 
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "mozilla/BasePrincipal.h"
 
@@ -210,16 +210,17 @@ nsHttpHandler::nsHttpHandler()
     , mHttp2Enabled(true)
     , mUseH2Deps(true)
     , mEnforceHttp2TlsProfile(true)
     , mCoalesceSpdy(true)
     , mSpdyPersistentSettings(false)
     , mAllowPush(true)
     , mEnableAltSvc(false)
     , mEnableAltSvcOE(false)
+    , mEnableOriginExtension(false)
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPushAllowance(32768)
     , mSpdyPullAllowance(ASpdySession::kInitialRwin)
     , mDefaultSpdyConcurrent(ASpdySession::kDefaultMaxConcurrent)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
@@ -234,21 +235,27 @@ nsHttpHandler::nsHttpHandler()
     , mTCPKeepaliveShortLivedIdleTimeS(10)
     , mTCPKeepaliveLongLivedEnabled(false)
     , mTCPKeepaliveLongLivedIdleTimeS(600)
     , mEnforceH1Framing(FRAMECHECK_BARELY)
     , mKeepEmptyResponseHeadersAsEmtpyString(false)
     , mDefaultHpackBuffer(4096)
     , mMaxHttpResponseHeaderSize(393216)
     , mFocusedWindowTransactionRatio(0.9f)
+    , mProcessId(0)
+    , mNextChannelId(1)
 {
     LOG(("Creating nsHttpHandler [this=%p].\n", this));
 
     MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!");
     gHttpHandler = this;
+    nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+    if (runtime) {
+        runtime->GetProcessID(&mProcessId);
+    }
 }
 
 nsHttpHandler::~nsHttpHandler()
 {
     LOG(("Deleting nsHttpHandler [this=%p]\n", this));
 
     // make sure the connection manager is shutdown
     if (mConnMgr) {
@@ -1370,16 +1377,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 
     if (PREF_CHANGED(HTTP_PREF("altsvc.oe"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("altsvc.oe"),
                                 &cVar);
         if (NS_SUCCEEDED(rv))
             mEnableAltSvcOE = cVar;
     }
 
+    if (PREF_CHANGED(HTTP_PREF("originextension"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("originextension"),
+                                &cVar);
+        if (NS_SUCCEEDED(rv))
+            mEnableOriginExtension = cVar;
+    }
+
     if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
         if (NS_SUCCEEDED(rv)) {
             mSpdyPushAllowance =
                 static_cast<uint32_t>
                 (clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
         }
     }
@@ -1963,18 +1977,18 @@ nsHttpHandler::NewProxiedChannel2(nsIURI
 
     uint32_t caps = mCapabilities;
 
     if (!IsNeckoChild()) {
         // HACK: make sure PSM gets initialized on the main thread.
         net_EnsurePSMInit();
     }
 
-    nsID channelId;
-    rv = NewChannelId(&channelId);
+    uint64_t channelId;
+    rv = NewChannelId(channelId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI, channelId);
     if (NS_FAILED(rv))
         return rv;
 
     // set the loadInfo on the new channel
     rv = httpChannel->SetLoadInfo(aLoadInfo);
@@ -2458,22 +2472,17 @@ nsHttpHandler::ShutdownConnectionManager
         if (NS_FAILED(rv)) {
             LOG(("nsHttpHandler::ShutdownConnectionManager\n"
                  "    failed to shutdown connection manager\n"));
         }
     }
 }
 
 nsresult
-nsHttpHandler::NewChannelId(nsID *channelId)
+nsHttpHandler::NewChannelId(uint64_t& channelId)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (!mUUIDGen) {
-    nsresult rv;
-    mUUIDGen = do_GetService("@mozilla.org/uuid-generator;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return mUUIDGen->GenerateUUIDInPlace(channelId);
+  channelId = ((static_cast<uint64_t>(mProcessId) << 32) & 0xFFFFFFFF00000000LL) | mNextChannelId++;
+  return NS_OK;
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -23,17 +23,16 @@ class nsIHttpChannel;
 class nsIPrefBranch;
 class nsICancelable;
 class nsICookieService;
 class nsIIOService;
 class nsIRequestContextService;
 class nsISiteSecurityService;
 class nsIStreamConverterService;
 class nsIThrottlingService;
-class nsIUUIDGenerator;
 
 
 namespace mozilla {
 namespace net {
 
 extern Atomic<PRThread*, Relaxed> gSocketThread;
 
 class ATokenBucketEvent;
@@ -119,16 +118,17 @@ public:
     uint32_t       SpdyPushAllowance()       { return mSpdyPushAllowance; }
     uint32_t       SpdyPullAllowance()       { return mSpdyPullAllowance; }
     uint32_t       DefaultSpdyConcurrent()   { return mDefaultSpdyConcurrent; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     bool           AllowPush()   { return mAllowPush; }
     bool           AllowAltSvc() { return mEnableAltSvc; }
     bool           AllowAltSvcOE() { return mEnableAltSvcOE; }
+    bool           AllowOriginExtension() { return mEnableOriginExtension; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
     bool           CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
     bool           UseH2Deps() { return mUseH2Deps; }
 
     uint32_t       MaxConnectionsPerOrigin() { return mMaxPersistentConnectionsPerServer; }
     bool           UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
     uint16_t       RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
@@ -498,16 +498,17 @@ private:
     uint32_t           mHttp2Enabled : 1;
     uint32_t           mUseH2Deps : 1;
     uint32_t           mEnforceHttp2TlsProfile : 1;
     uint32_t           mCoalesceSpdy : 1;
     uint32_t           mSpdyPersistentSettings : 1;
     uint32_t           mAllowPush : 1;
     uint32_t           mEnableAltSvc : 1;
     uint32_t           mEnableAltSvcOE : 1;
+    uint32_t           mEnableOriginExtension : 1;
 
     // Try to use SPDY features instead of HTTP/1.1 over SSL
     SpdyInformation    mSpdyInfo;
 
     uint32_t       mSpdySendingChunkSize;
     uint32_t       mSpdySendBufferSize;
     uint32_t       mSpdyPushAllowance;
     uint32_t       mSpdyPullAllowance;
@@ -614,21 +615,22 @@ private:
 
 private:
     MOZ_MUST_USE nsresult
     SpeculativeConnectInternal(nsIURI *aURI,
                                nsIPrincipal *aPrincipal,
                                nsIInterfaceRequestor *aCallbacks,
                                bool anonymous);
 
-    // UUID generator for channelIds
-    nsCOMPtr<nsIUUIDGenerator> mUUIDGen;
+    // State for generating channelIds
+    uint32_t mProcessId;
+    uint32_t mNextChannelId;
 
 public:
-    MOZ_MUST_USE nsresult NewChannelId(nsID *channelId);
+    MOZ_MUST_USE nsresult NewChannelId(uint64_t& channelId);
 };
 
 extern nsHttpHandler *gHttpHandler;
 
 //-----------------------------------------------------------------------------
 // nsHttpsHandler - thin wrapper to distinguish the HTTP handler from the
 //                  HTTPS handler (even though they share the same impl).
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -449,17 +449,17 @@ interface nsIHttpChannel : nsIChannel
      */
     [noscript, must_use] attribute uint64_t requestContextID;
 
     /**
      * Unique ID of the channel, shared between parent and child. Needed if
      * the channel activity needs to be monitored across process boundaries,
      * like in devtools net monitor. See bug 1274556.
      */
-    [must_use] attribute ACString channelId;
+    [must_use] attribute uint64_t channelId;
 
     /**
      * ID of the top-level document's inner window.  Identifies the content
      * this channels is being load in.
      */
     [must_use] attribute uint64_t topLevelContentWindowId;
 
     /**
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -707,24 +707,25 @@ nsViewSourceChannel::OnDataAvailable(nsI
 
 
 // nsIHttpChannel methods
 
 // We want to forward most of nsIHttpChannel over to mHttpChannel, but we want
 // to override GetRequestHeader and VisitHeaders. The reason is that we don't
 // want various headers like Link: and Refresh: applying to view-source.
 NS_IMETHODIMP
-nsViewSourceChannel::GetChannelId(nsACString& aChannelId)
+nsViewSourceChannel::GetChannelId(uint64_t *aChannelId)
 {
+    NS_ENSURE_ARG_POINTER(aChannelId);
   return !mHttpChannel ? NS_ERROR_NULL_POINTER :
       mHttpChannel->GetChannelId(aChannelId);
 }
 
 NS_IMETHODIMP
-nsViewSourceChannel::SetChannelId(const nsACString& aChannelId)
+nsViewSourceChannel::SetChannelId(uint64_t aChannelId)
 {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER :
       mHttpChannel->SetChannelId(aChannelId);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetTopLevelContentWindowId(uint64_t *aWindowId)
 {
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -57,20 +57,28 @@ interface nsISSLSocketControl : nsISuppo
 
     /* When 0RTT is performed, PR_Write will not drive the handshake forward.
      * It must be forced by calling this function.
      */
     void driveHandshake();
 
     /* Determine if a potential SSL connection to hostname:port with
      * a desired NPN negotiated protocol of npnProtocol can use the socket
-     * associated with this object instead of making a new one.
+     * associated with this object instead of making a new one. And if so, combine
+     * them.
      */
     boolean joinConnection(
-      in ACString npnProtocol, /* e.g. "spdy/2" */
+      in ACString npnProtocol, /* e.g. "h2" */
+      in ACString hostname,
+      in long port);
+
+    /* just like JoinConnection() except do not mark a successful test as joined.
+     */
+    boolean testJoinConnection(
+      in ACString npnProtocol, /* e.g. "h2" */
       in ACString hostname,
       in long port);
 
     /* Determine if existing connection should be trusted to convey information about
      * a hostname.
      */
     boolean isAcceptableForHost(in ACString hostname);
 
--- a/netwerk/test/mochitests/test_user_agent_overrides.html
+++ b/netwerk/test/mochitests/test_user_agent_overrides.html
@@ -226,21 +226,16 @@ function testPriority(callback) {
 function testOverrides(callback) {
   SpecialPowers.pushPrefEnv({
     set: [[PREF_OVERRIDES_ENABLED, true]]
   }, function nextTest() {
     testUA(tests.shift(), function() { tests.length ? nextTest() : callback() });
   });
 }
 
-SpecialPowers.loadChromeScript(_ => {
-  Components.utils.import("resource://gre/modules/UserAgentOverrides.jsm");
-  UserAgentOverrides.init();
-});
-
 SimpleTest.waitForExplicitFinish();
 SimpleTest.requestCompleteLog();
 SimpleTest.requestLongerTimeout(5);
 
 testOverrides(function() {
   testInactive(function() {
     testPriority(SimpleTest.finish)
   });
--- a/netwerk/test/mochitests/test_user_agent_updates.html
+++ b/netwerk/test/mochitests/test_user_agent_updates.html
@@ -195,41 +195,34 @@ function testBadUpdate(callback) {
 SimpleTest.waitForExplicitFinish();
 SimpleTest.requestFlakyTimeout("Test sets timeouts to wait for updates to happen.");
 
 SpecialPowers.pushPrefEnv({
   set: [
     [PREF_APP_UPDATE_TIMERMINIMUMDELAY, 0]
   ]
 }, function () {
-  chromeScript.sendSyncMessage("UAO-uninit");
-
   // Sets the OVERRIDES var in the chrome script.
   // We do this to avoid code duplication.
   chromeScript.sendSyncMessage("set-overrides", OVERRIDES);
 
   // testProfileLoad, testDownload, and testProfileSave must run in this order
-  //  because testDownload depends on testProfileLoad to call UAO.init()
-  //  and testProfileSave depends on testDownload to save overrides to the profile
+  // because testDownload depends on testProfileLoad and testProfileSave depends
+  // on testDownload to save overrides to the profile
   chromeScript.sendAsyncMessage("testProfileLoad", location.hostname);
 });
 
 
 const chromeScript = SpecialPowers.loadChromeScript(_ => {
   // Enter update timer manager test mode
   Components.classes["@mozilla.org/updates/timer-manager;1"].getService(
     Components.interfaces.nsIObserver).observe(null, "utm-test-init", "");
 
-  Components.utils.import("resource://gre/modules/UserAgentOverrides.jsm");
-
   var _notifyOnUpdate = false;
 
-  var UAO = UserAgentOverrides;
-  UAO.uninit();
-
   Components.utils.import("resource://gre/modules/FileUtils.jsm");
   var FU = FileUtils;
 
   const { TextDecoder, TextEncoder, OS } = Components.utils.import("resource://gre/modules/osfile.jsm");
   var OSF = OS.File;
 
   const KEY_PREFDIR = "PrefD";
   const KEY_APPDIR = "XCurProcD";
@@ -313,29 +306,24 @@ const chromeScript = SpecialPowers.loadC
       }
     );
   }
 
 
   addMessageListener("testProfileSave", testProfileSave);
   addMessageListener("testProfileLoad", testProfileLoad);
   addMessageListener("set-overrides", function(overrides) { OVERRIDES = overrides});
-  addMessageListener("UAO-init", function() { UAO.init(); });
-  addMessageListener("UAO-uninit", function() { UAO.uninit(); });
   addMessageListener("notify-on-update", () => { _notifyOnUpdate = true });
 });
 
 chromeScript.addMessageListener("testProfileSaveDone", SimpleTest.finish);
 chromeScript.addMessageListener("testProfileLoadDone", function() {
   SpecialPowers.pushPrefEnv({
     set: [[PREF_UPDATES_ENABLED, true]]
   }, function () {
-    // initialize UserAgentOverrides.jsm and
-    // UserAgentUpdates.jsm and load saved file
-    chromeScript.sendSyncMessage("UAO-init");
     (function waitForLoad() {
       var ifr = document.createElement('IFRAME');
       ifr.src = location.origin;
 
       ifr.addEventListener('load', function() {
         var nav = ifr.contentWindow.navigator;
         if (nav.userAgent !== UA_ALT_OVERRIDE) {
           setTimeout(waitForLoad, 100);
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_origin.js
@@ -0,0 +1,333 @@
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+var h2Port;
+var prefs;
+var spdypref;
+var http2pref;
+var extpref;
+var loadGroup;
+
+function run_test() {
+  var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+  h2Port = env.get("MOZHTTP2_PORT");
+  do_check_neq(h2Port, null);
+  do_check_neq(h2Port, "");
+
+  // Set to allow the cert presented by our H2 server
+  do_get_profile();
+  prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+  spdypref = prefs.getBoolPref("network.http.spdy.enabled");
+  http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
+  extpref = prefs.getBoolPref("network.http.originextension");
+
+  prefs.setBoolPref("network.http.spdy.enabled", true);
+  prefs.setBoolPref("network.http.spdy.enabled.http2", true);
+  prefs.setBoolPref("network.http.originextension", true);
+  prefs.setCharPref("network.dns.localDomains", "foo.example.com, alt1.example.com");
+
+  // The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by CA.cert.der
+  // so add that cert to the trust list as a signing cert.
+  let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
+
+  doTest1();
+}
+
+function resetPrefs() {
+  prefs.setBoolPref("network.http.spdy.enabled", spdypref);
+  prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
+  prefs.setBoolPref("network.http.originextension", extpref);
+  prefs.clearUserPref("network.dns.localDomains");
+}
+
+function readFile(file) {
+  let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
+                  .createInstance(Ci.nsIFileInputStream);
+  fstream.init(file, -1, 0, 0);
+  let data = NetUtil.readInputStreamToString(fstream, fstream.available());
+  fstream.close();
+  return data;
+}
+
+function addCertFromFile(certdb, filename, trustString) {
+  let certFile = do_get_file(filename, false);
+  let der = readFile(certFile);
+  certdb.addCert(der, trustString);
+}
+
+function makeChan(origin) {
+  return NetUtil.newChannel({
+    uri: origin,
+    loadUsingSystemPrincipal: true
+  }).QueryInterface(Ci.nsIHttpChannel);
+}
+
+var nextTest;
+var nextPortExpectedToBeSame = false;
+var currentPort = 0;
+var forceReload = false;
+var forceFailListener = false;
+
+var Listener = function() {};
+Listener.prototype.clientPort = 0;
+Listener.prototype = {
+  onStartRequest: function testOnStartRequest(request, ctx) {
+    do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
+
+    if (!Components.isSuccessCode(request.status)) {
+      do_throw("Channel should have a success code! (" + request.status + ")");
+    }
+    do_check_eq(request.responseStatus, 200);
+    this.clientPort = parseInt(request.getResponseHeader("x-client-port"));
+  },
+
+  onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
+    read_stream(stream, cnt);
+  },
+
+  onStopRequest: function testOnStopRequest(request, ctx, status) {
+    do_check_true(Components.isSuccessCode(status));
+    if (nextPortExpectedToBeSame) {
+     do_check_eq(currentPort, this.clientPort);
+    } else {
+     do_check_neq(currentPort, this.clientPort);
+    }
+    currentPort = this.clientPort;
+    nextTest();
+    do_test_finished();
+  }
+};
+
+var FailListener = function() {};
+FailListener.prototype = {
+  onStartRequest: function testOnStartRequest(request, ctx) {
+    do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
+    do_check_false(Components.isSuccessCode(request.status));
+  },
+  onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
+    read_stream(stream, cnt);
+  },
+  onStopRequest: function testOnStopRequest(request, ctx, status) {
+    do_check_false(Components.isSuccessCode(request.status));
+    nextTest();
+    do_test_finished();
+  }
+};
+
+function testsDone()
+{
+  dump("testsDone\n");
+  resetPrefs();
+}
+
+function doTest()
+{
+  dump("execute doTest " + origin + "\n");
+  var chan = makeChan(origin);
+  var listener;
+  if (!forceFailListener) {
+    listener = new Listener();
+  } else {
+    listener = new FailListener();
+  }
+  forceFailListener = false;
+
+  if (!forceReload) {
+    chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
+  } else {
+    chan.loadFlags = Ci.nsIRequest.LOAD_FRESH_CONNECTION |
+                     Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
+  }
+  forceReload = false;
+  chan.asyncOpen2(listener);
+}
+
+function doTest1()
+{
+  dump("doTest1()\n");
+  origin = "https://foo.example.com:" + h2Port + "/origin-1";
+  nextTest = doTest2;
+  nextPortExpectedToBeSame = false;
+  do_test_pending();
+  doTest();
+}
+
+function doTest2()
+{
+  // plain connection reuse
+  dump("doTest2()\n");
+  origin = "https://foo.example.com:" + h2Port + "/origin-2";
+  nextTest = doTest3;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest3()
+{
+  // 7540 style coalescing
+  dump("doTest3()\n");
+  origin = "https://alt1.example.com:" + h2Port + "/origin-3";
+  nextTest = doTest4;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest4()
+{
+  // forces an empty origin frame to be omitted
+  dump("doTest4()\n");
+  origin = "https://foo.example.com:" + h2Port + "/origin-4";
+  nextTest = doTest5;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest5()
+{
+  // 7540 style coalescing should not work due to empty origin set
+  dump("doTest5()\n");
+  origin = "https://alt1.example.com:" + h2Port + "/origin-5";
+  nextTest = doTest6;
+  nextPortExpectedToBeSame = false;
+  do_test_pending();
+  doTest();
+}
+
+function doTest6()
+{
+  // get a fresh connection with alt1 and alt2 in origin set
+  // note that there is no dns for alt2
+  dump("doTest6()\n");
+  origin = "https://foo.example.com:" + h2Port + "/origin-6";
+  nextTest = doTest7;
+  nextPortExpectedToBeSame = false;
+  forceReload = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest7()
+{
+  // check conn reuse to ensure sni is implicit in origin set
+  dump("doTest7()\n");
+  origin = "https://foo.example.com:" + h2Port + "/origin-7";
+  nextTest = doTest8;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest8()
+{
+  // alt1 is in origin set (and is 7540 eligible)
+  dump("doTest8()\n");
+  origin = "https://alt1.example.com:" + h2Port + "/origin-8";
+  nextTest = doTest9;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest9()
+{
+  // alt2 is in origin set but does not have dns
+  dump("doTest9()\n");
+  origin = "https://alt2.example.com:" + h2Port + "/origin-9";
+  nextTest = doTest10;
+  nextPortExpectedToBeSame = true;
+  do_test_pending();
+  doTest();
+}
+
+function doTest10()
+{
+  // bar is in origin set but does not have dns like alt2
+  // but the cert is not valid for bar. so expect a failure
+  dump("doTest10()\n");
+  origin = "https://bar.example.com:" + h2Port + "/origin-10";
+  nextTest = doTest11;
+  nextPortExpectedToBeSame = false;
+  forceFailListener = true;
+  do_test_pending();
+  doTest();
+}
+
+var Http2PushApiListener = function() {};
+
+Http2PushApiListener.prototype = {
+  fooOK : false,
+  alt1OK : false,
+
+  getInterface: function(aIID) {
+    return this.QueryInterface(aIID);
+  },
+
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.nsIHttpPushListener) ||
+        aIID.equals(Ci.nsIStreamListener))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  // nsIHttpPushListener
+  onPush: function onPush(associatedChannel, pushChannel) {
+    dump("push api onpush " + pushChannel.originalURI.spec +
+         " associated to " + associatedChannel.originalURI.spec + "\n");
+
+    do_check_eq(associatedChannel.originalURI.spec, "https://foo.example.com:" + h2Port + "/origin-11-a");
+    do_check_eq(pushChannel.getRequestHeader("x-pushed-request"), "true");
+
+    if (pushChannel.originalURI.spec === "https://foo.example.com:" + h2Port + "/origin-11-b") {
+      this.fooOK = true;
+    } else if (pushChannel.originalURI.spec === "https://alt1.example.com:" + h2Port + "/origin-11-e") {
+      this.alt1OK = true;
+    } else {
+      // any push of bar or madeup should not end up in onPush()
+      do_check_eq(true, false);
+    }
+    pushChannel.cancel(Components.results.NS_ERROR_ABORT);
+  },
+
+ // normal Channel listeners
+  onStartRequest: function pushAPIOnStart(request, ctx) {
+    dump("push api onstart " + request.originalURI.spec + "\n");
+  },
+
+  onDataAvailable: function pushAPIOnDataAvailable(request, ctx, stream, offset, cnt) {
+    var data = read_stream(stream, cnt);
+  },
+
+  onStopRequest: function test_onStopR(request, ctx, status) {
+    dump("push api onstop " + request.originalURI.spec + "\n");
+    do_check_true(this.fooOK);
+    do_check_true(this.alt1OK);
+    nextTest();
+    do_test_finished();
+  }
+};
+
+
+function doTest11()
+{
+  // we are connected with an SNI of foo from test6
+  // but the origin set is alt1, alt2, bar - foo is implied
+  // and bar is not actually covered by the cert
+  //
+  // the server will push foo (b-OK), bar (c-NOT OK), madeup (d-NOT OK), alt1 (e-OK),
+
+  dump("doTest11()\n");
+  do_test_pending();
+  loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
+  var chan = makeChan("https://foo.example.com:" + h2Port + "/origin-11-a");
+  chan.loadGroup = loadGroup;
+  var listener = new Http2PushApiListener();
+  nextTest = testsDone;
+  chan.notificationCallbacks = listener;
+  chan.asyncOpen2(listener);
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -245,16 +245,19 @@ run-sequentially = node server exception
 [test_multipart_streamconv_missing_boundary_lead_dashes.js]
 [test_multipart_streamconv-byte-by-byte.js]
 [test_nestedabout_serialize.js]
 [test_net_addr.js]
 # Bug 732363: test fails on windows for unknown reasons.
 skip-if = os == "win"
 [test_nojsredir.js]
 [test_offline_status.js]
+[test_origin.js]
+# node server not runinng on android
+skip-if = os == "android"
 [test_original_sent_received_head.js]
 [test_parse_content_type.js]
 [test_permmgr.js]
 [test_plaintext_sniff.js]
 [test_post.js]
 [test_private_necko_channel.js]
 [test_private_cookie_changed.js]
 [test_progress.js]
--- a/rdf/base/nsInMemoryDataSource.cpp
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -801,50 +801,50 @@ InMemoryDataSource::LogOperation(const c
                                  nsIRDFNode* aTarget,
                                  bool aTruthValue)
 {
     if (! MOZ_LOG_TEST(gLog, LogLevel::Debug))
         return;
 
     nsXPIDLCString uri;
     aSource->GetValue(getter_Copies(uri));
-    PR_LogPrint
-           ("InMemoryDataSource(%p): %s", this, aOperation);
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("InMemoryDataSource(%p): %s", this, aOperation));
 
-    PR_LogPrint
-           ("  [(%p)%s]--", aSource, (const char*) uri);
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("  [(%p)%s]--", aSource, (const char*) uri));
 
     aProperty->GetValue(getter_Copies(uri));
 
     char tv = (aTruthValue ? '-' : '!');
-    PR_LogPrint
-           ("  --%c[(%p)%s]--", tv, aProperty, (const char*) uri);
+    MOZ_LOG(gLog, LogLevel::Debug,
+           ("  --%c[(%p)%s]--", tv, aProperty, (const char*) uri));
 
     nsCOMPtr<nsIRDFResource> resource;
     nsCOMPtr<nsIRDFLiteral> literal;
 
     if ((resource = do_QueryInterface(aTarget)) != nullptr) {
         resource->GetValue(getter_Copies(uri));
-        PR_LogPrint
-           ("  -->[(%p)%s]", aTarget, (const char*) uri);
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->[(%p)%s]", aTarget, (const char*) uri));
     }
     else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
         nsXPIDLString value;
         literal->GetValue(getter_Copies(value));
         nsAutoString valueStr(value);
         char* valueCStr = ToNewCString(valueStr);
 
-        PR_LogPrint
-           ("  -->(\"%s\")\n", valueCStr);
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->(\"%s\")\n", valueCStr));
 
         free(valueCStr);
     }
     else {
-        PR_LogPrint
-           ("  -->(unknown-type)\n");
+        MOZ_LOG(gLog, LogLevel::Debug,
+           ("  -->(unknown-type)\n"));
     }
 }
 
 
 NS_IMETHODIMP
 InMemoryDataSource::GetURI(char* *uri)
 {
     NS_PRECONDITION(uri != nullptr, "null ptr");
--- a/rdf/base/nsRDFContentSink.cpp
+++ b/rdf/base/nsRDFContentSink.cpp
@@ -436,19 +436,19 @@ RDFContentSinkImpl::HandleEndElement(con
 
   nsIRDFResource* resource;
   if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
       // XXX parser didn't catch unmatched tags?
       if (MOZ_LOG_TEST(gLog, LogLevel::Warning)) {
           nsAutoString tagStr(aName);
           char* tagCStr = ToNewCString(tagStr);
 
-          PR_LogPrint
+          MOZ_LOG(gLog, LogLevel::Warning,
                  ("rdfxml: extra close tag '%s' at line %d",
-                  tagCStr, 0/*XXX fix me */);
+                  tagCStr, 0/*XXX fix me */));
 
           free(tagCStr);
       }
 
       return NS_ERROR_UNEXPECTED; // XXX
   }
 
   // If we've just popped a member or property element, _now_ is the
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -468,20 +468,20 @@ nsNSSSocketInfo::IsAcceptableForHost(con
   }
 
   // All tests pass
   *_retval = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
-                                const nsACString& hostname,
-                                int32_t port,
-                                bool* _retval)
+nsNSSSocketInfo::TestJoinConnection(const nsACString& npnProtocol,
+                                    const nsACString& hostname,
+                                    int32_t port,
+                                    bool* _retval)
 {
   *_retval = false;
 
   // Different ports may not be joined together
   if (port != GetPort())
     return NS_OK;
 
   // Make sure NPN has been completed and matches requested npnProtocol
@@ -489,23 +489,32 @@ nsNSSSocketInfo::JoinConnection(const ns
     return NS_OK;
 
   if (mBypassAuthentication) {
     // An unauthenticated connection does not know whether or not it
     // is acceptable for a particular hostname
     return NS_OK;
   }
 
-  IsAcceptableForHost(hostname, _retval);
-
-  if (*_retval) {
+  IsAcceptableForHost(hostname, _retval); // sets _retval
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
+                                const nsACString& hostname,
+                                int32_t port,
+                                bool* _retval)
+{
+  nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval);
+  if (NS_SUCCEEDED(rv) && *_retval) {
     // All tests pass - this is joinable
     mJoined = true;
   }
-  return NS_OK;
+  return rv;
 }
 
 bool
 nsNSSSocketInfo::GetForSTARTTLS()
 {
   return mForSTARTTLS;
 }
 
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -10,23 +10,27 @@
 // linking to nsCocoaFeatures.mm in XUL.
 
 #include "Sandbox.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <CoreFoundation/CoreFoundation.h>
 
+#include <vector>
+
 #include "mozilla/Assertions.h"
 
 // XXX There are currently problems with the /usr/include/sandbox.h file on
-// some/all of the Macs in Mozilla's build system.  For the time being (until
-// this problem is resolved), we refer directly to what we need from it,
-// rather than including it here.
+// some/all of the Macs in Mozilla's build system. Further,
+// sandbox_init_with_parameters is not included in the header.  For the time
+// being (until this problem is resolved), we refer directly to what we need
+// from it, rather than including it here.
 extern "C" int sandbox_init(const char *profile, uint64_t flags, char **errorbuf);
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
 extern "C" void sandbox_free_error(char *errorbuf);
 
 #define MAC_OS_X_VERSION_10_0_HEX  0x00001000
 #define MAC_OS_X_VERSION_10_6_HEX  0x00001060
 #define MAC_OS_X_VERSION_10_7_HEX  0x00001070
 #define MAC_OS_X_VERSION_10_8_HEX  0x00001080
 #define MAC_OS_X_VERSION_10_9_HEX  0x00001090
 #define MAC_OS_X_VERSION_10_10_HEX 0x000010A0
@@ -117,17 +121,26 @@ OSXVersion::GetVersionNumber()
   }
   return mOSXVersion;
 }
 
 namespace mozilla {
 
 static const char pluginSandboxRules[] =
   "(version 1)\n"
-  "(deny default %s)\n"
+
+  "(define should-log (param \"SHOULD_LOG\"))\n"
+  "(define plugin-binary-path (param \"PLUGIN_BINARY_PATH\"))\n"
+  "(define app-path (param \"APP_PATH\"))\n"
+  "(define app-binary-path (param \"APP_BINARY_PATH\"))\n"
+
+  "(if (string=? should-log \"TRUE\")\n"
+  "    (deny default)\n"
+  "    (deny default (with no-log)))\n"
+
   "(allow signal (target self))\n"
   "(allow sysctl-read)\n"
   "(allow iokit-open (iokit-user-client-class \"IOHIDParamUserClient\"))\n"
   "(allow mach-lookup\n"
   "    (global-name \"com.apple.cfprefsd.agent\")\n"
   "    (global-name \"com.apple.cfprefsd.daemon\")\n"
   "    (global-name \"com.apple.system.opendirectoryd.libinfo\")\n"
   "    (global-name \"com.apple.system.logger\")\n"
@@ -135,36 +148,38 @@ static const char pluginSandboxRules[] =
   "(allow file-read*\n"
   "    (regex #\"^/etc$\")\n"
   "    (regex #\"^/dev/u?random$\")\n"
   "    (literal \"/usr/share/icu/icudt51l.dat\")\n"
   "    (regex #\"^/System/Library/Displays/Overrides/*\")\n"
   "    (regex #\"^/System/Library/CoreServices/CoreTypes.bundle/*\")\n"
   "    (regex #\"^/System/Library/PrivateFrameworks/*\")\n"
   "    (regex #\"^/usr/lib/libstdc\\+\\+\\..*dylib$\")\n"
-  "    (literal \"%s\")\n"
-  "    (literal \"%s\")\n"
-  "    (literal \"%s\"))\n";
+  "    (literal plugin-binary-path)\n"
+  "    (literal app-path)\n"
+  "    (literal app-binary-path))\n";
 
 static const char widevinePluginSandboxRulesAddend[] =
   "(allow mach-lookup (global-name \"com.apple.windowserver.active\"))\n";
 
 static const char contentSandboxRules[] =
   "(version 1)\n"
   "\n"
-  "(define sandbox-level %d)\n"
-  "(define macosMinorVersion %d)\n"
-  "(define appPath \"%s\")\n"
-  "(define appBinaryPath \"%s\")\n"
-  "(define appDir \"%s\")\n"
-  "(define appTempDir \"%s\")\n"
-  "(define hasProfileDir %d)\n"
-  "(define profileDir \"%s\")\n"
-  "(define home-path \"%s\")\n"
-  "(define hasFilePrivileges %d)\n"
+  "(define should-log (param \"SHOULD_LOG\"))\n"
+  "(define sandbox-level-1 (param \"SANDBOX_LEVEL_1\"))\n"
+  "(define sandbox-level-2 (param \"SANDBOX_LEVEL_2\"))\n"
+  "(define macosMinorVersion-9 (param \"MAC_OS_MINOR_9\"))\n"
+  "(define appPath (param \"APP_PATH\"))\n"
+  "(define appBinaryPath (param \"APP_BINARY_PATH\"))\n"
+  "(define appDir (param \"APP_DIR\"))\n"
+  "(define appTempDir (param \"APP_TEMP_DIR\"))\n"
+  "(define hasProfileDir (param \"HAS_SANDBOXED_PROFILE\"))\n"
+  "(define profileDir (param \"PROFILE_DIR\"))\n"
+  "(define home-path (param \"HOME_PATH\"))\n"
+  "(define hasFilePrivileges (param \"HAS_FILE_PRIVILEGES\"))\n"
   "\n"
   "; Allow read access to standard system paths.\n"
   "(allow file-read*\n"
   "  (require-all (file-mode #o0004)\n"
   "    (require-any (subpath \"/Library/Filesystems/NetFSPlugins\")\n"
   "      (subpath \"/System\")\n"
   "      (subpath \"/private/var/db/dyld\")\n"
   "      (subpath \"/usr/lib\")\n"
@@ -191,17 +206,19 @@ static const char contentSandboxRules[] 
   "  file-write-data\n"
   "  file-ioctl\n"
   "  (literal \"/dev/dtracehelper\"))\n"
   "\n"
   "; Used to read hw.ncpu, hw.physicalcpu_max, kern.ostype, and others\n"
   "(allow sysctl-read)\n"
   "\n"
   "(begin\n"
-  "  (deny default %s)\n"
+  "  (if (string=? should-log \"TRUE\")\n"
+  "    (deny default)\n"
+  "    (deny default (with no-log)))\n"
   "  (debug deny)\n"
   "\n"
   "  (define resolving-literal literal)\n"
   "  (define resolving-subpath subpath)\n"
   "  (define resolving-regex regex)\n"
   "\n"
   "  (define container-path appPath)\n"
   "  (define appdir-path appDir)\n"
@@ -268,17 +285,17 @@ static const char contentSandboxRules[] 
   "      (global-name \"com.apple.cache_delete\")\n"
   "      (global-name \"com.apple.pluginkit.pkd\")\n"
   "      (global-name \"com.apple.bird\")\n"
   "      (global-name \"com.apple.ocspd\")\n"
   "      (global-name \"com.apple.cmio.AppleCameraAssistant\")\n"
   "      (global-name \"com.apple.DesktopServicesHelper\"))\n"
   "\n"
   "; bug 1312273\n"
-  "  (if (= macosMinorVersion 9)\n"
+  "  (if (string=? macosMinorVersion-9 \"TRUE\")\n"
   "     (allow mach-lookup (global-name \"com.apple.xpcd\")))\n"
   "\n"
   "  (allow iokit-open\n"
   "      (iokit-user-client-class \"IOHIDParamUserClient\")\n"
   "      (iokit-user-client-class \"IOAudioControlUserClient\")\n"
   "      (iokit-user-client-class \"IOAudioEngineUserClient\")\n"
   "      (iokit-user-client-class \"IGAccelDevice\")\n"
   "      (iokit-user-client-class \"nvDevice\")\n"
@@ -343,28 +360,28 @@ static const char contentSandboxRules[] 
   "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\"))\n"
   "\n"
   "; The following rules impose file access restrictions which get\n"
   "; more restrictive in higher levels. When file-origin-specific\n"
   "; content processes are used for file:// origin browsing, the\n"
   "; global file-read* permission should be removed from each level.\n"
   "\n"
   "; level 1: global read access permitted, no global write access\n"
-  "  (if (= sandbox-level 1) (allow file-read*))\n"
+  "  (if (string=? sandbox-level-1 \"TRUE\") (allow file-read*))\n"
   "\n"
   "; level 2: global read access permitted, no global write access,\n"
   ";          no read/write access to ~/Library,\n"
   ";          no read/write access to $PROFILE,\n"
   ";          read access permitted to $PROFILE/{extensions,weave,chrome}\n"
-  "  (if (= sandbox-level 2)\n"
-  "    (if (not (zero? hasFilePrivileges))\n"
+  "  (if (string=? sandbox-level-2 \"TRUE\")\n"
+  "    (if (string=? hasFilePrivileges \"TRUE\")\n"
   "      ; This process has blanket file read privileges\n"
   "      (allow file-read*)\n"
   "      ; This process does not have blanket file read privileges\n"
-  "      (if (not (zero? hasProfileDir))\n"
+  "      (if (string=? hasProfileDir \"TRUE\")\n"
   "        ; we have a profile dir\n"
   "        (begin\n"
   "          (allow file-read* (require-all\n"
   "              (require-not (home-subpath \"/Library\"))\n"
   "              (require-not (subpath profileDir))))\n"
   "          (allow file-read*\n"
   "              (profile-subpath \"/extensions\")\n"
   "              (profile-subpath \"/weave\")\n"
@@ -407,51 +424,68 @@ static const char contentSandboxRules[] 
   "  (allow network-outbound (literal \"/private/var/run/cupsd\"))\n"
 #ifdef DEBUG
   "\n"
   "; bug 1303987\n"
   "  (allow file-write* (var-folders-regex \"/\"))\n"
 #endif
   ")\n";
 
-static const char* NO_LOGGING_CMD = "(with no-log)";
-
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage)
 {
+  std::vector<const char *> params;
   char *profile = NULL;
+  bool profile_needs_free = false;
   if (aInfo.type == MacSandboxType_Plugin) {
-    asprintf(&profile, pluginSandboxRules,
-             aInfo.shouldLog ? "" : NO_LOGGING_CMD,
-             aInfo.pluginInfo.pluginBinaryPath.c_str(),
-             aInfo.appPath.c_str(),
-             aInfo.appBinaryPath.c_str());
+    profile = const_cast<char *>(pluginSandboxRules);
+    params.push_back("SHOULD_LOG");
+    params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+    params.push_back("PLUGIN_BINARY_PATH");
+    params.push_back(aInfo.pluginInfo.pluginBinaryPath.c_str());
+    params.push_back("APP_PATH");
+    params.push_back(aInfo.appPath.c_str());
+    params.push_back("APP_BINARY_PATH");
+    params.push_back(aInfo.appBinaryPath.c_str());
 
-    if (profile &&
-      aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
+    if (aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
       char *widevineProfile = NULL;
       asprintf(&widevineProfile, "%s%s", profile,
         widevinePluginSandboxRulesAddend);
-      free(profile);
       profile = widevineProfile;
+      profile_needs_free = true;
     }
   }
   else if (aInfo.type == MacSandboxType_Content) {
     MOZ_ASSERT(aInfo.level >= 1);
     if (aInfo.level >= 1) {
-      asprintf(&profile, contentSandboxRules, aInfo.level,
-               OSXVersion::OSXVersionMinor(),
-               aInfo.appPath.c_str(),
-               aInfo.appBinaryPath.c_str(),
-               aInfo.appDir.c_str(),
-               aInfo.appTempDir.c_str(),
-               aInfo.hasSandboxedProfile ? 1 : 0,
-               aInfo.profileDir.c_str(),
-               getenv("HOME"),
-               aInfo.hasFilePrivileges ? 1 : 0,
-               aInfo.shouldLog ? "" : NO_LOGGING_CMD);
+      profile = const_cast<char *>(contentSandboxRules);
+      params.push_back("SHOULD_LOG");
+      params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+      params.push_back("SANDBOX_LEVEL_1");
+      params.push_back(aInfo.level == 1 ? "TRUE" : "FALSE");
+      params.push_back("SANDBOX_LEVEL_2");
+      params.push_back(aInfo.level == 2 ? "TRUE" : "FALSE");
+      params.push_back("MAC_OS_MINOR_9");
+      params.push_back(OSXVersion::OSXVersionMinor() == 9 ? "TRUE" : "FALSE");
+      params.push_back("APP_PATH");
+      params.push_back(aInfo.appPath.c_str());
+      params.push_back("APP_BINARY_PATH");
+      params.push_back(aInfo.appBinaryPath.c_str());
+      params.push_back("APP_DIR");
+      params.push_back(aInfo.appDir.c_str());
+      params.push_back("APP_TEMP_DIR");
+      params.push_back(aInfo.appTempDir.c_str());
+      params.push_back("PROFILE_DIR");
+      params.push_back(aInfo.profileDir.c_str());
+      params.push_back("HOME_PATH");
+      params.push_back(getenv("HOME"));
+      params.push_back("HAS_SANDBOXED_PROFILE");
+      params.push_back(aInfo.hasSandboxedProfile ? "TRUE" : "FALSE");
+      params.push_back("HAS_FILE_PRIVILEGES");
+      params.push_back(aInfo.hasFilePrivileges ? "TRUE" : "FALSE");
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
       return false;
     }
   }
   else {
     char *msg = NULL;
@@ -463,31 +497,50 @@ bool StartMacSandbox(MacSandboxInfo aInf
     return false;
   }
 
   if (!profile) {
     fprintf(stderr, "Out of memory in StartMacSandbox()!\n");
     return false;
   }
 
+// In order to avoid relying on any other Mozilla modules (as described at the
+// top of this file), we use our own #define instead of the existing MOZ_LOG
+// infrastructure. This can be used by developers to debug the macOS sandbox
+// policy.
+#define MAC_SANDBOX_PRINT_POLICY 0
+#if MAC_SANDBOX_PRINT_POLICY
+  printf("Sandbox params:\n");
+  for (size_t i = 0; i < params.size() / 2; i++) {
+    printf("  %s = %s\n", params[i * 2], params[(i * 2) + 1]);
+  }
+  printf("Sandbox profile:\n%s\n", profile);
+#endif
+
+  // The parameters array is null terminated.
+  params.push_back(nullptr);
+
   char *errorbuf = NULL;
-  int rv = sandbox_init(profile, 0, &errorbuf);
+  int rv = sandbox_init_with_parameters(profile, 0, params.data(),
+                                        &errorbuf);
   if (rv) {
     if (errorbuf) {
       char *msg = NULL;
       asprintf(&msg, "sandbox_init() failed with error \"%s\"", errorbuf);
       if (msg) {
         aErrorMessage.assign(msg);
         free(msg);
       }
       fprintf(stderr, "profile: %s\n", profile);
       sandbox_free_error(errorbuf);
     }
   }
-  free(profile);
+  if (profile_needs_free) {
+    free(profile);
+  }
   if (rv) {
     return false;
   }
 
   return true;
 }
 
 } // namespace mozilla
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -389,16 +389,20 @@ def mozharness_test_buildbot_bridge(conf
         test_name = '{}-{}'.format(test_name, this_chunk)
 
     if test.get('suite', '') == 'talos':
         # on linux64-<variant>/<build>, we add the variant to the buildername
         m = re.match(r'\w+-([^/]+)/.*', test['test-platform'])
         variant = ''
         if m and m.group(1):
             variant = m.group(1) + ' '
+        # On beta and release, we run nightly builds on-push; the talos
+        # builders need to run against non-nightly buildernames
+        if variant == 'nightly ':
+            variant = ''
         # this variant name has branch after the variant type in BBB bug 1338871
         if variant == 'stylo ':
             buildername = '{} {}{} talos {}'.format(
                 BUILDER_NAME_PREFIX[platform],
                 variant,
                 branch,
                 test_name
             )
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -1723,41 +1723,32 @@ class ADBDevice(ADBCommand):
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
         """
         # remove trailing /
         local = os.path.normpath(local)
         remote = os.path.normpath(remote)
         copy_required = False
-        if self._adb_version >= '1.0.36' and \
-           os.path.isdir(local) and self.is_dir(remote):
+        if os.path.isdir(local):
+            copy_required = True
+            temp_parent = tempfile.mkdtemp()
+            remote_name = os.path.basename(remote)
+            new_local = os.path.join(temp_parent, remote_name)
+            dir_util.copy_tree(local, new_local)
+            local = new_local
             # See do_sync_push in
             # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
             # Work around change in behavior in adb 1.0.36 where if
             # the remote destination directory exists, adb push will
             # copy the source directory *into* the destination
             # directory otherwise it will copy the source directory
             # *onto* the destination directory.
-            #
-            # If the destination directory does exist, push to its
-            # parent directory.  If the source and destination leaf
-            # directory names are different, copy the source directory
-            # to a temporary directory with the same leaf name as the
-            # destination so that when we push to the parent, the
-            # source is copied onto the destination directory.
-            local_name = os.path.basename(local)
-            remote_name = os.path.basename(remote)
-            if local_name != remote_name:
-                copy_required = True
-                temp_parent = tempfile.mkdtemp()
-                new_local = os.path.join(temp_parent, remote_name)
-                dir_util.copy_tree(local, new_local)
-                local = new_local
-            remote = '/'.join(remote.rstrip('/').split('/')[:-1])
+            if self._adb_version >= '1.0.36':
+                remote = '/'.join(remote.rstrip('/').split('/')[:-1])
         try:
             self.command_output(["push", local, remote], timeout=timeout)
         except:
             raise
         finally:
             if copy_required:
                 shutil.rmtree(temp_parent)
 
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -33,66 +33,66 @@ verbose_logging = False
 
 
 class AvdInfo(object):
     """
        Simple class to contain an AVD description.
     """
 
     def __init__(self, description, name, tooltool_manifest, extra_args,
-                 port):
+                 x86):
         self.description = description
         self.name = name
         self.tooltool_manifest = tooltool_manifest
         self.extra_args = extra_args
-        self.port = port
+        self.x86 = x86
 
 
 """
    A dictionary to map an AVD type to a description of that type of AVD.
 
    There is one entry for each type of AVD used in Mozilla automated tests
    and the parameters for each reflect those used in mozharness.
 """
 AVD_DICT = {
     '4.3': AvdInfo('Android 4.3',
                    'mozemulator-4.3',
                    'testing/config/tooltool-manifests/androidarm_4_3/mach-emulator.manifest',
                    ['-show-kernel', '-debug',
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket'],
-                   5554),
+                   False),
     '6.0': AvdInfo('Android 6.0',
                    'mozemulator-6.0',
                    'testing/config/tooltool-manifests/androidarm_6_0/mach-emulator.manifest',
                    ['-show-kernel', '-debug',
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket'],
-                   5554),
+                   False),
     '7.0': AvdInfo('Android 7.0',
                    'mozemulator-7.0',
                    'testing/config/tooltool-manifests/androidarm_7_0/mach-emulator.manifest',
                    ['-debug',
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket',
                     '-ranchu',
                     '-qemu', '-m', '2048'],
-                   5554),
+                   False),
     'x86': AvdInfo('Android 4.2 x86',
                    'mozemulator-x86',
                    'testing/config/tooltool-manifests/androidx86/mach-emulator.manifest',
                    ['-debug',
                     'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket',
                     '-qemu', '-m', '1024', '-enable-kvm'],
-                   5554),
+                   True),
     'x86-6.0': AvdInfo('Android 6.0 x86',
                        'mozemulator-x86-6.0',
                        'testing/config/tooltool-manifests/androidx86_6_0/mach-emulator.manifest',
                        ['-debug',
                         'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket',
                         '-ranchu',
                         '-qemu', '-m', '2048'],
-                       5554)
+                       True)
 }
 
 
 def verify_android_device(build_obj, install=False, xre=False, debugger=False, verbose=False):
     """
        Determine if any Android device is connected via adb.
        If no device is found, prompt to start an emulator.
        If a device is found or an emulator started and 'install' is
@@ -334,16 +334,17 @@ class AndroidEmulator(object):
         self.gpu = True
         self.restarted = False
         adb_path = _find_sdk_exe(substs, 'adb', False)
         if not adb_path:
             adb_path = 'adb'
         self.dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1,
                                    deviceSerial=device_serial)
         self.dm.default_timeout = 10
+        _log_debug("Running on %s" % platform.platform())
         _log_debug("Emulator created with type %s" % self.avd_type)
 
     def __del__(self):
         if self.emulator_log:
             self.emulator_log.close()
 
     def is_running(self):
         """
@@ -481,29 +482,33 @@ class AndroidEmulator(object):
             else:
                 time.sleep(10)
                 if self.check_completed():
                     return False
         _log_debug("Android boot status verified.")
 
         if not self._verify_emulator():
             return False
+        if self.avd_info.x86:
+            _log_info("Running the x86 emulator; be sure to install an x86 APK!")
+        else:
+            _log_info("Running the arm emulator; be sure to install an arm APK!")
         return True
 
     def check_completed(self):
         if self.proc.proc.poll() is not None:
             if not self.gpu and not self.restarted:
                 _log_warning("Emulator failed to start. Your emulator may be out of date.")
                 _log_warning("Trying to restart the emulator without -gpu argument.")
                 self.restarted = True
                 self.start()
                 return False
             _log_warning("Emulator has already completed!")
             log_path = os.path.join(EMULATOR_HOME_DIR, 'emulator.log')
-            _log_warning("See log at %s for more information." % log_path)
+            _log_warning("See log at %s and/or use --verbose for more information." % log_path)
             return True
         return False
 
     def wait(self):
         """
            Wait for the emulator to close. If interrupted, close the emulator.
         """
         try:
@@ -556,27 +561,27 @@ class AndroidEmulator(object):
         _log_debug("<<< " + result)
         return result
 
     def _verify_emulator(self):
         telnet_ok = False
         tn = None
         while(not telnet_ok):
             try:
-                tn = telnetlib.Telnet('localhost', self.avd_info.port, 10)
+                tn = telnetlib.Telnet('localhost', 5554, 10)
                 if tn is not None:
                     tn.read_until('OK', 10)
                     self._telnet_cmd(tn, 'avd status')
                     self._telnet_cmd(tn, 'redir list')
                     self._telnet_cmd(tn, 'network status')
                     tn.write('quit\n')
                     tn.read_all()
                     telnet_ok = True
                 else:
-                    _log_warning("Unable to connect to port %d" % self.avd_info.port)
+                    _log_warning("Unable to connect to port 5554")
             except:
                 _log_warning("Trying again after unexpected exception")
             finally:
                 if tn is not None:
                     tn.close()
             if not telnet_ok:
                 time.sleep(10)
                 if self.proc.proc.poll() is not None:
@@ -649,16 +654,30 @@ def _find_sdk_exe(substs, exe, tools):
         exe_path = find_executable(exe)
         if exe_path:
             found = True
         else:
             _log_debug("Unable to find executable on PATH")
 
     if found:
         _log_debug("%s found at %s" % (exe, exe_path))
+        try:
+            creation_time = os.path.getctime(exe_path)
+            _log_debug("  ...with creation time %s" % time.ctime(creation_time))
+        except:
+            _log_warning("Could not get creation time for %s" % exe_path)
+
+        prop_path = os.path.join(os.path.dirname(exe_path), "source.properties")
+        if os.path.exists(prop_path):
+            with open(prop_path, 'r') as f:
+                for line in f.readlines():
+                    if line.startswith("Pkg.Revision"):
+                        line = line.strip()
+                        _log_debug("  ...with SDK version in %s: %s" % (prop_path, line))
+                        break
     else:
         exe_path = None
     return exe_path
 
 
 def _log_debug(text):
     if verbose_logging:
         print "DEBUG: %s" % text
deleted file mode 100644
--- a/testing/web-platform/meta/dom/events/CustomEvent.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[CustomEvent.html]
-  type: testharness
-  [initCustomEvent's default parameter values.]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/dom/events/Event-initEvent.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[Event-initEvent.html]
-  type: testharness
-  [Tests initEvent's default parameter values.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -40,14 +40,8 @@
     expected: FAIL
 
   [Text interface: attribute assignedSlot]
     expected: FAIL
 
   [Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type (2)]
     expected: FAIL
 
-  [Event interface: operation initEvent(DOMString,boolean,boolean)]
-    expected: FAIL
-
-  [CustomEvent interface: operation initCustomEvent(DOMString,boolean,boolean,any)]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.html.ini
@@ -3485,11 +3485,20 @@
     expected: FAIL
 
   [HTMLScriptElement interface: document.createElement("script") must inherit property "nonce" with the proper type (8)]
     expected: FAIL
 
   [HTMLElement interface: document.createElement("noscript") must inherit property "forceSpellCheck" with the proper type (15)]
     expected: FAIL
 
-  [MessageEvent interface: operation initMessageEvent(DOMString,boolean,boolean,any,DOMString,DOMString,[object Object\],[object Object\],[object Object\])]
-    expected: FAIL
-
+  [Element interface: document.createElement("noscript") must inherit property "slot" with the proper type (7)]
+    expected: FAIL
+
+  [Element interface: document.createElement("noscript") must inherit property "attachShadow" with the proper type (24)]
+    expected: FAIL
+
+  [Element interface: calling attachShadow(ShadowRootInit) on document.createElement("noscript") with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [Element interface: document.createElement("noscript") must inherit property "assignedSlot" with the proper type (48)]
+     expected: FAIL
+
--- a/testing/web-platform/meta/html/dom/interfaces.worker.js.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.worker.js.ini
@@ -70,11 +70,8 @@
     expected: FAIL
 
   [SharedWorker interface: attribute onerror]
     expected: FAIL
 
   [WorkerGlobalScope interface: self must inherit property "onlanguagechange" with the proper type (5)]
     expected: FAIL
 
-  [MessageEvent interface: operation initMessageEvent(DOMString,boolean,boolean,any,DOMString,DOMString,[object Object\],[object Object\],[object Object\])]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/events/messageevent-constructor.https.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[messageevent-constructor.https.html]
-  type: testharness
-  [initMessageEvent operation default parameter values]
-    expected: FAIL
-
--- a/testing/web-platform/tests/html/dom/resources/untested-interfaces.idl
+++ b/testing/web-platform/tests/html/dom/resources/untested-interfaces.idl
@@ -18,175 +18,211 @@ interface Event {
   readonly attribute boolean bubbles;
   readonly attribute boolean cancelable;
   void preventDefault();
   readonly attribute boolean defaultPrevented;
 
   [Unforgeable] readonly attribute boolean isTrusted;
   readonly attribute DOMTimeStamp timeStamp;
 
-  void initEvent(DOMString type, boolean bubbles, boolean cancelable);
+  void initEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false);
 };
 
 dictionary EventInit {
   boolean bubbles = false;
   boolean cancelable = false;
 };
 
+
 [Constructor(DOMString type, optional CustomEventInit eventInitDict),
  Exposed=(Window,Worker)]
 interface CustomEvent : Event {
   readonly attribute any detail;
 
-  void initCustomEvent(DOMString type, boolean bubbles, boolean cancelable, any detail);
+  void initCustomEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any detail = null);
 };
 
 dictionary CustomEventInit : EventInit {
   any detail = null;
 };
 
+
 [Exposed=(Window,Worker)]
 interface EventTarget {
-  void addEventListener(DOMString type, EventListener? callback, optional boolean capture = false);
-  void removeEventListener(DOMString type, EventListener? callback, optional boolean capture = false);
+  void addEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
+  void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
   boolean dispatchEvent(Event event);
 };
 
 callback interface EventListener {
   void handleEvent(Event event);
 };
 
-[NoInterfaceObject]
+dictionary EventListenerOptions {
+  boolean capture;
+  boolean passive;
+};
+
+
+[NoInterfaceObject,
+ Exposed=Window]
 interface NonElementParentNode {
   Element? getElementById(DOMString elementId);
 };
 Document implements NonElementParentNode;
 DocumentFragment implements NonElementParentNode;
 
-[NoInterfaceObject]
+
+[NoInterfaceObject,
+ Exposed=Window]
+interface DocumentOrShadowRoot {
+};
+Document implements DocumentOrShadowRoot;
+ShadowRoot implements DocumentOrShadowRoot;
+
+
+[NoInterfaceObject,
+ Exposed=Window]
 interface ParentNode {
   [SameObject] readonly attribute HTMLCollection children;
   readonly attribute Element? firstElementChild;
   readonly attribute Element? lastElementChild;
   readonly attribute unsigned long childElementCount;
 
   [Unscopable] void prepend((Node or DOMString)... nodes);
   [Unscopable] void append((Node or DOMString)... nodes);
 
   Element? querySelector(DOMString selectors);
   [NewObject] NodeList querySelectorAll(DOMString selectors);
 };
 Document implements ParentNode;
 DocumentFragment implements ParentNode;
 Element implements ParentNode;
 
-[NoInterfaceObject]
+
+[NoInterfaceObject,
+ Exposed=Window]
 interface NonDocumentTypeChildNode {
   readonly attribute Element? previousElementSibling;
   readonly attribute Element? nextElementSibling;
 };
 Element implements NonDocumentTypeChildNode;
 CharacterData implements NonDocumentTypeChildNode;
 
-[NoInterfaceObject]
+
+[NoInterfaceObject,
+ Exposed=Window]
 interface ChildNode {
   [Unscopable] void before((Node or DOMString)... nodes);
   [Unscopable] void after((Node or DOMString)... nodes);
   [Unscopable] void replaceWith((Node or DOMString)... nodes);
   [Unscopable] void remove();
 };
 DocumentType implements ChildNode;
 Element implements ChildNode;
 CharacterData implements ChildNode;
 
-// XXX unrecognized tokens "class", "extends"
-// https://www.w3.org/Bugs/Public/show_bug.cgi?id=20020
-// https://www.w3.org/Bugs/Public/show_bug.cgi?id=23225
-//class Elements extends Array {
-//  Element? query(DOMString relativeSelectors);
-//  Elements queryAll(DOMString relativeSelectors);
-//};
 
+[NoInterfaceObject,
+ Exposed=Window]
+interface Slotable {
+  readonly attribute HTMLSlotElement? assignedSlot;
+};
+Element implements Slotable;
+Text implements Slotable;
+
+
+[Exposed=Window]
 interface NodeList {
   getter Node? item(unsigned long index);
   readonly attribute unsigned long length;
 //  iterable<Node>;
 };
 
+
+[Exposed=Window, LegacyUnenumerableNamedProperties]
 interface HTMLCollection {
   readonly attribute unsigned long length;
   getter Element? item(unsigned long index);
   getter Element? namedItem(DOMString name);
 };
 
+
 [Constructor(MutationCallback callback)]
 interface MutationObserver {
-  void observe(Node target, MutationObserverInit options);
+  void observe(Node target, optional MutationObserverInit options);
   void disconnect();
   sequence<MutationRecord> takeRecords();
 };
 
 callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
 
 dictionary MutationObserverInit {
   boolean childList = false;
   boolean attributes;
   boolean characterData;
   boolean subtree = false;
   boolean attributeOldValue;
   boolean characterDataOldValue;
   sequence<DOMString> attributeFilter;
 };
 
+
+[Exposed=Window]
 interface MutationRecord {
   readonly attribute DOMString type;
-  readonly attribute Node target;
+  [SameObject] readonly attribute Node target;
   [SameObject] readonly attribute NodeList addedNodes;
   [SameObject] readonly attribute NodeList removedNodes;
   readonly attribute Node? previousSibling;
   readonly attribute Node? nextSibling;
   readonly attribute DOMString? attributeName;
   readonly attribute DOMString? attributeNamespace;
   readonly attribute DOMString? oldValue;
 };
 
+
+[Exposed=Window]
 interface Node : EventTarget {
   const unsigned short ELEMENT_NODE = 1;
   const unsigned short ATTRIBUTE_NODE = 2; // historical
   const unsigned short TEXT_NODE = 3;
   const unsigned short CDATA_SECTION_NODE = 4;
   const unsigned short ENTITY_REFERENCE_NODE = 5; // historical
   const unsigned short ENTITY_NODE = 6; // historical
   const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
   const unsigned short COMMENT_NODE = 8;
   const unsigned short DOCUMENT_NODE = 9;
   const unsigned short DOCUMENT_TYPE_NODE = 10;
   const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
   const unsigned short NOTATION_NODE = 12; // historical
   readonly attribute unsigned short nodeType;
   readonly attribute DOMString nodeName;
 
-  readonly attribute DOMString? baseURI;
+  readonly attribute DOMString baseURI;
 
+  readonly attribute boolean isConnected;
   readonly attribute Document? ownerDocument;
+  Node getRootNode(optional GetRootNodeOptions options);
   readonly attribute Node? parentNode;
   readonly attribute Element? parentElement;
   boolean hasChildNodes();
   [SameObject] readonly attribute NodeList childNodes;
   readonly attribute Node? firstChild;
   readonly attribute Node? lastChild;
   readonly attribute Node? previousSibling;
   readonly attribute Node? nextSibling;
 
            attribute DOMString? nodeValue;
            attribute DOMString? textContent;
   void normalize();
 
   [NewObject] Node cloneNode(optional boolean deep = false);
-  boolean isEqualNode(Node? node);
+  boolean isEqualNode(Node? otherNode);
+  boolean isSameNode(Node? otherNode); // historical alias of ===
 
   const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
   const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02;
   const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04;
   const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08;
   const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
   const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
   unsigned short compareDocumentPosition(Node other);
@@ -197,167 +233,215 @@ interface Node : EventTarget {
   boolean isDefaultNamespace(DOMString? namespace);
 
   Node insertBefore(Node node, Node? child);
   Node appendChild(Node node);
   Node replaceChild(Node node, Node child);
   Node removeChild(Node child);
 };
 
-[Constructor]
+dictionary GetRootNodeOptions {
+  boolean composed = false;
+};
+
+[Constructor,
+ Exposed=Window]
 interface Document : Node {
   [SameObject] readonly attribute DOMImplementation implementation;
   readonly attribute DOMString URL;
   readonly attribute DOMString documentURI;
   readonly attribute DOMString origin;
   readonly attribute DOMString compatMode;
   readonly attribute DOMString characterSet;
-  readonly attribute DOMString charset; // legacy alias of .characterSet
-  readonly attribute DOMString inputEncoding; // legacy alias of .characterSet
+  readonly attribute DOMString charset; // historical alias of .characterSet
+  readonly attribute DOMString inputEncoding; // historical alias of .characterSet
   readonly attribute DOMString contentType;
 
   readonly attribute DocumentType? doctype;
   readonly attribute Element? documentElement;
-  HTMLCollection getElementsByTagName(DOMString localName);
+  HTMLCollection getElementsByTagName(DOMString qualifiedName);
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   HTMLCollection getElementsByClassName(DOMString classNames);
 
-  [NewObject] Element createElement(DOMString localName);
-  [NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+  [NewObject] Element createElement(DOMString localName, optional ElementCreationOptions options);
+  [NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options);
   [NewObject] DocumentFragment createDocumentFragment();
   [NewObject] Text createTextNode(DOMString data);
   [NewObject] CDATASection createCDATASection(DOMString data);
   [NewObject] Comment createComment(DOMString data);
   [NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
 
   [NewObject] Node importNode(Node node, optional boolean deep = false);
   Node adoptNode(Node node);
 
   [NewObject] Attr createAttribute(DOMString localName);
-  [NewObject] Attr createAttributeNS(DOMString? namespace, DOMString name);
+  [NewObject] Attr createAttributeNS(DOMString? namespace, DOMString qualifiedName);
 
   [NewObject] Event createEvent(DOMString interface);
 
   [NewObject] Range createRange();
 
   // NodeFilter.SHOW_ALL = 0xFFFFFFFF
   [NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
   [NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
 };
 
+[Exposed=Window]
 interface XMLDocument : Document {};
 
+dictionary ElementCreationOptions {
+  DOMString is;
+};
+
+
+[Exposed=Window]
 interface DOMImplementation {
   [NewObject] DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId);
   [NewObject] XMLDocument createDocument(DOMString? namespace, [TreatNullAs=EmptyString] DOMString qualifiedName, optional DocumentType? doctype = null);
   [NewObject] Document createHTMLDocument(optional DOMString title);
 
   boolean hasFeature(); // useless; always returns true
 };
 
-[Constructor]
-interface DocumentFragment : Node {
-};
 
+[Exposed=Window]
 interface DocumentType : Node {
   readonly attribute DOMString name;
   readonly attribute DOMString publicId;
   readonly attribute DOMString systemId;
 };
 
+
+[Constructor,
+ Exposed=Window]
+interface DocumentFragment : Node {
+};
+
+
+[Exposed=Window]
+interface ShadowRoot : DocumentFragment {
+  readonly attribute ShadowRootMode mode;
+  readonly attribute Element host;
+};
+
+enum ShadowRootMode { "open", "closed" };
+
+
+[Exposed=Window]
 interface Element : Node {
   readonly attribute DOMString? namespaceURI;
   readonly attribute DOMString? prefix;
   readonly attribute DOMString localName;
   readonly attribute DOMString tagName;
 
            attribute DOMString id;
            attribute DOMString className;
   [SameObject, PutForwards=value] readonly attribute DOMTokenList classList;
+           attribute DOMString slot;
 
   boolean hasAttributes();
   [SameObject] readonly attribute NamedNodeMap attributes;
   sequence<DOMString> getAttributeNames();
-  DOMString? getAttribute(DOMString name);
+  DOMString? getAttribute(DOMString qualifiedName);
   DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
-  void setAttribute(DOMString name, DOMString value);
-  void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);
-  void removeAttribute(DOMString name);
+  void setAttribute(DOMString qualifiedName, DOMString value);
+  void setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value);
+  void removeAttribute(DOMString qualifiedName);
   void removeAttributeNS(DOMString? namespace, DOMString localName);
-  boolean hasAttribute(DOMString name);
+  boolean hasAttribute(DOMString qualifiedName);
   boolean hasAttributeNS(DOMString? namespace, DOMString localName);
 
-  Attr? getAttributeNode(DOMString name);
+  Attr? getAttributeNode(DOMString qualifiedName);
   Attr? getAttributeNodeNS(DOMString? namespace, DOMString localName);
   Attr? setAttributeNode(Attr attr);
   Attr? setAttributeNodeNS(Attr attr);
   Attr removeAttributeNode(Attr attr);
 
+  ShadowRoot attachShadow(ShadowRootInit init);
+  readonly attribute ShadowRoot? shadowRoot;
+
   Element? closest(DOMString selectors);
   boolean matches(DOMString selectors);
+  boolean webkitMatchesSelector(DOMString selectors); // historical alias of .matches
 
-  HTMLCollection getElementsByTagName(DOMString localName);
+  HTMLCollection getElementsByTagName(DOMString qualifiedName);
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   HTMLCollection getElementsByClassName(DOMString classNames);
+
+  Element? insertAdjacentElement(DOMString where, Element element); // historical
+  void insertAdjacentText(DOMString where, DOMString data); // historical
 };
 
+dictionary ShadowRootInit {
+//  required ShadowRootMode mode;
+};
+
+
+[Exposed=Window, LegacyUnenumerableNamedProperties]
 interface NamedNodeMap {
   readonly attribute unsigned long length;
   getter Attr? item(unsigned long index);
-  getter Attr? getNamedItem(DOMString name);
+  getter Attr? getNamedItem(DOMString qualifiedName);
   Attr? getNamedItemNS(DOMString? namespace, DOMString localName);
   Attr? setNamedItem(Attr attr);
   Attr? setNamedItemNS(Attr attr);
-  Attr removeNamedItem(DOMString name);
+  Attr removeNamedItem(DOMString qualifiedName);
   Attr removeNamedItemNS(DOMString? namespace, DOMString localName);
 };
 
-interface Attr {
+
+[Exposed=Window]
+interface Attr : Node {
   readonly attribute DOMString? namespaceURI;
   readonly attribute DOMString? prefix;
   readonly attribute DOMString localName;
   readonly attribute DOMString name;
            attribute DOMString value;
-           attribute DOMString nodeValue; // legacy alias of .value
-           attribute DOMString textContent; // legacy alias of .value
 
   readonly attribute Element? ownerElement;
 
   readonly attribute boolean specified; // useless; always returns true
 };
 
+[Exposed=Window]
 interface CharacterData : Node {
   [TreatNullAs=EmptyString] attribute DOMString data;
   readonly attribute unsigned long length;
   DOMString substringData(unsigned long offset, unsigned long count);
   void appendData(DOMString data);
   void insertData(unsigned long offset, DOMString data);
   void deleteData(unsigned long offset, unsigned long count);
   void replaceData(unsigned long offset, unsigned long count, DOMString data);
 };
 
-[Constructor(optional DOMString data = "")]
+
+[Constructor(optional DOMString data = ""),
+ Exposed=Window]
 interface Text : CharacterData {
   [NewObject] Text splitText(unsigned long offset);
   readonly attribute DOMString wholeText;
 };
 
 [Exposed=Window]
 interface CDATASection : Text {
 };
 
+[Exposed=Window]
 interface ProcessingInstruction : CharacterData {
   readonly attribute DOMString target;
 };
 
-[Constructor(optional DOMString data = "")]
+[Constructor(optional DOMString data = ""),
+ Exposed=Window]
 interface Comment : CharacterData {
 };
 
-[Constructor]
+
+[Constructor,
+ Exposed=Window]
 interface Range {
   readonly attribute Node startContainer;
   readonly attribute unsigned long startOffset;
   readonly attribute Node endContainer;
   readonly attribute unsigned long endOffset;
   readonly attribute boolean collapsed;
   readonly attribute Node commonAncestorContainer;
 
@@ -389,44 +473,49 @@ interface Range {
   boolean isPointInRange(Node node, unsigned long offset);
   short comparePoint(Node node, unsigned long offset);
 
   boolean intersectsNode(Node node);
 
   stringifier;
 };
 
+
+[Exposed=Window]
 interface NodeIterator {
   [SameObject] readonly attribute Node root;
   readonly attribute Node referenceNode;
   readonly attribute boolean pointerBeforeReferenceNode;
   readonly attribute unsigned long whatToShow;
   readonly attribute NodeFilter? filter;
 
   Node? nextNode();
   Node? previousNode();
 
   void detach();
 };
 
+
+[Exposed=Window]
 interface TreeWalker {
   [SameObject] readonly attribute Node root;
   readonly attribute unsigned long whatToShow;
   readonly attribute NodeFilter? filter;
            attribute Node currentNode;
 
   Node? parentNode();
   Node? firstChild();
   Node? lastChild();
   Node? previousSibling();
   Node? nextSibling();
   Node? previousNode();
   Node? nextNode();
 };
 
+[Exposed=Window]
 callback interface NodeFilter {
   // Constants for acceptNode()
   const unsigned short FILTER_ACCEPT = 1;
   const unsigned short FILTER_REJECT = 2;
   const unsigned short FILTER_SKIP = 3;
 
   // Constants for whatToShow
   const unsigned long SHOW_ALL = 0xFFFFFFFF;
@@ -441,16 +530,17 @@ callback interface NodeFilter {
   const unsigned long SHOW_DOCUMENT = 0x100;
   const unsigned long SHOW_DOCUMENT_TYPE = 0x200;
   const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400;
   const unsigned long SHOW_NOTATION = 0x800; // historical
 
   unsigned short acceptNode(Node node);
 };
 
+
 interface DOMTokenList {
   readonly attribute unsigned long length;
   getter DOMString? item(unsigned long index);
   boolean contains(DOMString token);
   [CEReactions] void add(DOMString... tokens);
   [CEReactions] void remove(DOMString... tokens);
   [CEReactions] boolean toggle(DOMString token, optional boolean force);
   [CEReactions] void replace(DOMString token, DOMString newToken);
--- a/testing/web-platform/tests/url/urlsearchparams-constructor.html
+++ b/testing/web-platform/tests/url/urlsearchparams-constructor.html
@@ -143,17 +143,18 @@ test(function() {
     assert_equals(params.get("c"), "d");
     assert_throws(new TypeError(), function() { new URLSearchParams([[1]]); });
     assert_throws(new TypeError(), function() { new URLSearchParams([[1,2,3]]); });
 }, "Constructor with sequence of sequences of strings");
 
 [
   { "input": {"+": "%C2"}, "output": [["+", "%C2"]], "name": "object with +" },
   { "input": {c: "x", a: "?"}, "output": [["c", "x"], ["a", "?"]], "name": "object with two keys" },
-  { "input": [["c", "x"], ["a", "?"]], "output": [["c", "x"], ["a", "?"]], "name": "array with two keys" }
+  { "input": [["c", "x"], ["a", "?"]], "output": [["c", "x"], ["a", "?"]], "name": "array with two keys" },
+  { "input": {"a\0b": "42", "c\uD83D": "23", "d\u1234": "foo"}, "output": [["a\0b", "42"], ["c\uFFFD", "23"], ["d\u1234", "foo"]], "name": "object with NULL, non-ASCII, and surrogate keys" }
 ].forEach((val) => {
     test(() => {
         let params = new URLSearchParams(val.input),
             i = 0
         for (let param of params) {
             assert_array_equals(param, val.output[i])
             i++
         }
--- a/testing/xpcshell/moz-http2/http2-cert.pem
+++ b/testing/xpcshell/moz-http2/http2-cert.pem
@@ -1,79 +1,24 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 1 (0x1)
-    Signature Algorithm: sha256WithRSAEncryption
-        Issuer: C=US, ST=Maine, O=CA Example
-        Validity
-            Not Before: Apr 29 05:29:19 2015 GMT
-            Not After : Apr 26 05:29:19 2025 GMT
-        Subject: C=US, ST=Maine, O=Example Com, CN=foo.example.com
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:cf:ff:c0:27:3b:a3:11:b5:7f:5d:4f:22:f9:75:
-                    48:47:d9:3a:ce:9b:66:82:4e:e4:ae:ab:78:d3:4c:
-                    3a:9a:5c:37:97:b2:7b:4e:2a:54:77:16:2a:3e:6f:
-                    52:ee:4b:49:46:1d:6b:18:9a:ed:b1:ad:64:9f:8b:
-                    e5:fa:e4:60:7b:39:0e:db:e8:b4:2d:4b:e8:ab:37:
-                    e8:90:ec:eb:0f:3e:6b:40:7a:d1:da:e6:68:b3:f4:
-                    f6:68:54:5b:27:90:6d:c2:c3:04:de:85:23:2b:3c:
-                    66:4e:06:79:58:93:a1:71:d7:ec:74:55:a4:84:9d:
-                    41:22:2a:7a:76:ae:56:b1:6f:15:2d:f2:f5:9c:64:
-                    3e:4f:0f:6e:8f:b6:28:66:e9:89:04:5d:1d:21:77:
-                    f8:03:d3:89:ed:7c:f4:3b:42:02:c8:8d:de:47:74:
-                    1f:4a:5d:fe:8d:d1:57:37:08:54:bf:89:d8:f7:27:
-                    22:a7:2a:5d:aa:d5:b0:61:22:9b:96:75:ee:ab:09:
-                    ca:a9:cb:2b:1e:88:7c:5a:53:7e:5f:88:c4:43:ea:
-                    e8:a7:db:35:6c:b2:89:ad:98:e0:96:c9:83:c4:c1:
-                    e7:2a:5c:f8:99:5c:9e:01:9c:e6:99:bd:18:5c:69:
-                    d4:10:f1:46:88:37:0b:4e:76:5f:6a:1a:21:c2:a4:
-                    16:d1
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Subject Key Identifier: 
-                76:BC:13:90:F7:85:1B:1C:24:A1:CC:65:8A:4F:4C:0C:7F:10:D3:F5
-            X509v3 Authority Key Identifier: 
-                keyid:F7:FC:76:AF:C5:1A:E9:C9:42:6C:38:DF:8B:07:9E:2B:2C:E5:8E:20
-
-            X509v3 Basic Constraints: 
-                CA:FALSE
-            X509v3 Key Usage: 
-                Digital Signature, Key Encipherment
-    Signature Algorithm: sha256WithRSAEncryption
-         03:ab:2a:9e:e5:cd:5c:88:5a:6c:f7:4b:7a:7c:ef:85:2c:31:
-         df:03:79:31:a6:c5:c8:2b:c6:21:a5:33:2b:a0:4b:e2:7e:0a:
-         86:9b:72:25:b6:75:43:41:7c:30:9f:15:b4:9f:34:50:57:eb:
-         87:f9:1e:9f:b6:cd:81:36:92:61:66:d5:fe:e2:c5:ed:de:f1:
-         ce:85:0b:f9:6a:2b:32:4d:29:f1:a9:94:57:a3:0f:74:93:12:
-         c9:0a:28:5e:72:9f:4f:0f:78:f5:84:11:5a:9f:d7:1c:4c:fd:
-         13:d8:3d:4c:f8:dd:4c:c6:1c:fd:63:ee:f5:d5:96:f5:00:2c:
-         e6:bb:c9:4c:d8:6a:19:59:58:2b:d4:05:ab:57:47:1c:49:d6:
-         c5:56:1a:e3:64:10:19:9b:44:3e:74:8b:19:73:28:86:96:b4:
-         d1:2a:49:23:07:25:97:64:8f:1b:1c:64:76:12:e0:df:e3:cf:
-         55:d5:7c:e9:77:d4:69:2f:c7:9a:fd:ce:1a:29:ab:d7:88:68:
-         93:de:75:e4:d6:85:29:e2:b6:b7:59:20:e3:b5:20:b7:e8:0b:
-         23:9b:4c:b4:e8:d9:90:cf:e9:2f:9e:a8:22:a2:ef:6a:68:65:
-         f6:c4:81:ed:75:77:88:01:f2:47:03:1a:de:1f:44:38:47:fa:
-         aa:69:f2:98
 -----BEGIN CERTIFICATE-----
-MIIDVDCCAjygAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJVUzEO
-MAwGA1UECAwFTWFpbmUxEzARBgNVBAoMCkNBIEV4YW1wbGUwHhcNMTUwNDI5MDUy
-OTE5WhcNMjUwNDI2MDUyOTE5WjBNMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFTWFp
-bmUxFDASBgNVBAoMC0V4YW1wbGUgQ29tMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5j
-b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/8AnO6MRtX9dTyL5
-dUhH2TrOm2aCTuSuq3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSfi+X6
-5GB7OQ7b6LQtS+irN+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZOBnlY
-k6Fx1+x0VaSEnUEiKnp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04ntfPQ7
-QgLIjd5HdB9KXf6N0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxaU35f
-iMRD6uin2zVssomtmOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9qGiHC
-pBbRAgMBAAGjWjBYMB0GA1UdDgQWBBR2vBOQ94UbHCShzGWKT0wMfxDT9TAfBgNV
-HSMEGDAWgBT3/HavxRrpyUJsON+LB54rLOWOIDAJBgNVHRMEAjAAMAsGA1UdDwQE
-AwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAA6sqnuXNXIhabPdLenzvhSwx3wN5MabF
-yCvGIaUzK6BL4n4KhptyJbZ1Q0F8MJ8VtJ80UFfrh/ken7bNgTaSYWbV/uLF7d7x
-zoUL+WorMk0p8amUV6MPdJMSyQooXnKfTw949YQRWp/XHEz9E9g9TPjdTMYc/WPu
-9dWW9QAs5rvJTNhqGVlYK9QFq1dHHEnWxVYa42QQGZtEPnSLGXMohpa00SpJIwcl
-l2SPGxxkdhLg3+PPVdV86XfUaS/Hmv3OGimr14hok9515NaFKeK2t1kg47Ugt+gL
-I5tMtOjZkM/pL56oIqLvamhl9sSB7XV3iAHyRwMa3h9EOEf6qmnymA==
+MIID+TCCAuGgAwIBAgIJAKu6XZkGFQ8NMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNV
+BAYTAlVTMQ4wDAYDVQQIDAVNYWluZTETMBEGA1UECgwKQ0EgRXhhbXBsZTAeFw0x
+NzAzMjgwMjIxMjZaFw0yNzAzMjYwMjIxMjZaMIHIMQswCQYDVQQGEwJVUzERMA8G
+A1UECAwITmV3IFlvcmsxEjAQBgNVBAcMCVJvY2hlc3RlcjESMBAGA1UECgwJRW5k
+IFBvaW50MRcwFQYDVQQLDA5UZXN0aW5nIERvbWFpbjFLMEkGCSqGSIb3DQEJARY8
+eW91ci1hZG1pbmlzdHJhdGl2ZS1hZGRyZXNzQHlvdXItYXdlc29tZS1leGlzdGlu
+Zy1kb21haW4uY29tMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/8AnO6MRtX9dTyL5dUhH2TrOm2aCTuSu
+q3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSfi+X65GB7OQ7b6LQtS+ir
+N+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZOBnlYk6Fx1+x0VaSEnUEi
+Knp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04ntfPQ7QgLIjd5HdB9KXf6N
+0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxaU35fiMRD6uin2zVssomt
+mOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9qGiHCpBbRAgMBAAGjezB5
+MB8GA1UdIwQYMBaAFPf8dq/FGunJQmw434sHniss5Y4gMAkGA1UdEwQCMAAwCwYD
+VR0PBAQDAgTwMD4GA1UdEQQ3MDWCD2Zvby5leGFtcGxlLmNvbYIQYWx0MS5leGFt
+cGxlLmNvbYIQYWx0Mi5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEARitg
+nxH87RYCMo/wbfSyttkiDmlw1vuEFI6dq9D6iTYcJK9pD9VvQ8e/k0J0FdtnIGBD
+O9+kKuPCRIjJt0mRToQHXI4SFIEqUraI5xA5VdXT2FR5KsshNSw6LjV25gvv0hcI
+6YBOlJ1IzntSA3h7lGGhgqH2ln32hzTQ8ob8F8i3GecOIk6mDkgCHTPRe7tfyTKw
+7c6Z8By6Es84RCQdxXf6AouhJw9SfZl1T5bcy5vDbBcNYenfvueCLezNX6kK7orh
+KsqnxWr2cG8c3X1OIuuvAEUbQ78InOb4OPiQQXcfv+dzxnv7tK6pNRcmMUhabwM8
+J3i97uzqNXPwTFMu3Q==
 -----END CERTIFICATE-----
--- a/testing/xpcshell/moz-http2/moz-http2.js
+++ b/testing/xpcshell/moz-http2/moz-http2.js
@@ -738,16 +738,70 @@ function handleRequest(req, res) {
   else if (u.pathname === "/immutable-test-with-attribute") {
     res.setHeader('Cache-Control', 'max-age=100000, immutable');
     res.setHeader('Etag', '2');
      if (req.headers["if-none-match"]) {
        res.setHeader("x-conditional", "true");
      }
    // default response from here
   }
+  else if (u.pathname === "/origin-4") {
+   var originList = [ ];
+   req.stream.connection.originFrame(originList);
+   res.setHeader("x-client-port", req.remotePort);
+  }
+  else if (u.pathname === "/origin-6") {
+   var originList = [ "https://alt1.example.com:" + serverPort,
+		      "https://alt2.example.com:" + serverPort,
+                      "https://bar.example.com:" + serverPort ];
+   req.stream.connection.originFrame(originList);
+   res.setHeader("x-client-port", req.remotePort);
+  }
+  else if (u.pathname === "/origin-11-a") {
+    res.setHeader("x-client-port", req.remotePort);
+
+    pushb = res.push(
+        { hostname: 'foo.example.com:' + serverPort, port: serverPort, path : '/origin-11-b', method : 'GET',
+          headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
+    pushb.writeHead(200, {
+      'pushed' : 'yes',
+      'content-length' : 1
+      });
+    pushb.end('1');
+
+    pushc = res.push(
+        { hostname: 'bar.example.com:' + serverPort, port: serverPort, path : '/origin-11-c', method : 'GET',
+          headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
+    pushc.writeHead(200, {
+      'pushed' : 'yes',
+      'content-length' : 1
+      });
+    pushc.end('1');
+
+    pushd = res.push(
+        { hostname: 'madeup.example.com:' + serverPort, port: serverPort, path : '/origin-11-d', method : 'GET',
+          headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
+    pushd.writeHead(200, {
+      'pushed' : 'yes',
+      'content-length' : 1
+      });
+    pushd.end('1');
+
+    pushe = res.push(
+        { hostname: 'alt1.example.com:' + serverPort, port: serverPort, path : '/origin-11-e', method : 'GET',
+          headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
+    pushe.writeHead(200, {
+      'pushed' : 'yes',
+      'content-length' : 1
+      });
+    pushe.end('1');
+}
+  else if (u.pathname.substring(0,8) === "/origin-") { // test_origin.js coalescing
+    res.setHeader("x-client-port", req.remotePort);
+  }
 
   res.setHeader('Content-Type', 'text/html');
   if (req.httpVersionMajor != 2) {
     res.setHeader('Connection', 'close');
   }
   res.writeHead(200);
   res.end(content);
 }
--- a/testing/xpcshell/node-http2/lib/protocol/connection.js
+++ b/testing/xpcshell/node-http2/lib/protocol/connection.js
@@ -118,17 +118,17 @@ Connection.prototype._initializeStreamMa
   this.on('RECEIVING_SETTINGS_MAX_CONCURRENT_STREAMS', this._updateStreamLimit);
 };
 
 // `_writeControlFrame` is called when there's an incoming frame in the `_control` stream. It
 // broadcasts the message by creating an event on it.
 Connection.prototype._writeControlFrame = function _writeControlFrame(frame) {
   if ((frame.type === 'SETTINGS') || (frame.type === 'PING') ||
       (frame.type === 'GOAWAY') || (frame.type === 'WINDOW_UPDATE') ||
-      (frame.type === 'ALTSVC')) {
+      (frame.type === 'ALTSVC') || (frame.type == 'ORIGIN')) {
     this._log.debug({ frame: frame }, 'Receiving connection level frame');
     this.emit(frame.type, frame);
   } else {
     this._log.error({ frame: frame }, 'Invalid connection level frame');
     this.emit('error', 'PROTOCOL_ERROR');
   }
 };
 
@@ -552,16 +552,27 @@ Connection.prototype._receivePing = func
         ACK: true
       },
       stream: 0,
       data: frame.data
     });
   }
 };
 
+Connection.prototype.originFrame = function originFrame(originList) {
+  this._log.debug(originList, 'emitting origin frame');
+
+  this.push({
+    type: 'ORIGIN',
+    flags: {},
+    stream: 0,
+    originList : originList,
+  });
+};
+
 // Terminating the connection
 Connection.prototype.close = function close(error) {
   if (this._closed) {
     this._log.warn('Trying to close an already closed connection');
     return;
   }
 
   this._log.debug({ error: error }, 'Closing the connection');
--- a/testing/xpcshell/node-http2/lib/protocol/flow.js
+++ b/testing/xpcshell/node-http2/lib/protocol/flow.js
@@ -59,17 +59,16 @@ var INITIAL_WINDOW_SIZE = 65535;
 function Flow(flowControlId) {
   Duplex.call(this, { objectMode: true });
 
   this._window = this._initialWindow = INITIAL_WINDOW_SIZE;
   this._flowControlId = flowControlId;
   this._queue = [];
   this._ended = false;
   this._received = 0;
-  this._blocked = false;
 }
 Flow.prototype = Object.create(Duplex.prototype, { constructor: { value: Flow } });
 
 // Incoming frames
 // ---------------
 
 // `_receive` is called when there's an incoming frame.
 Flow.prototype._receive = function _receive(frame, callback) {
@@ -152,40 +151,33 @@ Flow.prototype._read = function _read() 
   // * if the flow control queue is empty, then let the user push more frames
   if (this._queue.length === 0) {
     this._send();
   }
 
   // * if there are items in the flow control queue, then let's put them into the output queue (to
   //   the extent it is possible with respect to the window size and output queue feedback)
   else if (this._window > 0) {
-    this._blocked = false;
     this._readableState.sync = true; // to avoid reentrant calls
     do {
       var moreNeeded = this._push(this._queue[0]);
       if (moreNeeded !== null) {
         this._queue.shift();
       }
     } while (moreNeeded && (this._queue.length > 0));
     this._readableState.sync = false;
 
     assert((!moreNeeded) ||                              // * output queue is full
            (this._queue.length === 0) ||                         // * flow control queue is empty
            (!this._window && (this._queue[0].type === 'DATA'))); // * waiting for window update
   }
 
   // * otherwise, come back when the flow control window is positive
-  else if (!this._blocked) {
-    this._parentPush({
-      type: 'BLOCKED',
-      flags: {},
-      stream: this._flowControlId
-    });
+  else {
     this.once('window_update', this._read);
-    this._blocked = true;
   }
 };
 
 var MAX_PAYLOAD_SIZE = 4096; // Must not be greater than MAX_HTTP_PAYLOAD_SIZE which is 16383
 
 // `read(limit)` is like the `read` of the Readable class, but it guarantess that the 'flow control
 // size' (0 for non-DATA frames, length of the payload for DATA frames) of the returned frame will
 // be under `limit`.
--- a/testing/xpcshell/node-http2/lib/protocol/framer.js
+++ b/testing/xpcshell/node-http2/lib/protocol/framer.js
@@ -1064,36 +1064,34 @@ Deserializer.ALTSVC = function readAltSv
       frame.port = parseInt(hostport[1], 10);
     } else if (chosenAltSvc[i].name == 'ma') {
       frame.maxAge = parseInt(chosenAltSvc[i].value, 10);
     }
     // Otherwise, we just ignore this
   }
 };
 
-// BLOCKED
-// ------------------------------------------------------------
-//
-// The BLOCKED frame (type=0xB) indicates that the sender is unable to send data
-// due to a closed flow control window.
-//
-// The BLOCKED frame does not define any flags and contains no payload.
+frameTypes[0xB] = 'ORIGIN';
+frameFlags.ORIGIN = [];
+typeSpecificAttributes.ORIGIN = ['originList'];
 
-frameTypes[0xB] = 'BLOCKED';
-
-frameFlags.BLOCKED = [];
-
-typeSpecificAttributes.BLOCKED = [];
-
-Serializer.BLOCKED = function writeBlocked(frame, buffers) {
+Serializer.ORIGIN = function writeOrigin(frame, buffers) {
+  for (var i = 0; i < frame.originList.length; i++) {
+    var buffer = new Buffer(2);
+    buffer.writeUInt16BE(frame.originList[i].length, 0);
+    buffers.push(buffer);
+    buffers.push(new Buffer(frame.originList[i], 'ascii'));
+  }
 };
 
-Deserializer.BLOCKED = function readBlocked(buffer, frame) {
+Deserializer.ORIGIN = function readOrigin(buffer, frame) {
+    // ignored
 };
 
+
 // [Error Codes](https://tools.ietf.org/html/rfc7540#section-7)
 // ------------------------------------------------------------
 
 var errorCodes = [
   'NO_ERROR',
   'PROTOCOL_ERROR',
   'INTERNAL_ERROR',
   'FLOW_CONTROL_ERROR',
--- a/testing/xpcshell/node-http2/lib/protocol/stream.js
+++ b/testing/xpcshell/node-http2/lib/protocol/stream.js
@@ -248,17 +248,17 @@ Stream.prototype._writeUpstream = functi
     this._processedHeaders = true;
     this._onHeaders(frame);
   } else if (frame.type === 'PUSH_PROMISE') {
     this._onPromise(frame);
   } else if (frame.type === 'PRIORITY') {
     this._onPriority(frame);
   } else if (frame.type === 'ALTSVC') {
     // TODO
-  } else if (frame.type === 'BLOCKED') {
+  } else if (frame.type === 'ORIGIN') {
     // TODO
   }
 
   // * If it's an invalid stream level frame, emit error
   else if ((frame.type !== 'DATA') &&
            (frame.type !== 'WINDOW_UPDATE') &&
            (frame.type !== 'RST_STREAM')) {
     this._log.error({ frame: frame }, 'Invalid stream level frame');
@@ -408,27 +408,27 @@ function activeState(state) {
 // `_transition` is called every time there's an incoming or outgoing frame. It manages state
 // transitions, and detects stream errors. A stream error is always caused by a frame that is not
 // allowed in the current state.
 Stream.prototype._transition = function transition(sending, frame) {
   var receiving = !sending;
   var connectionError;
   var streamError;
 
-  var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, BLOCKED = false;
+  var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, ORIGIN = false;
   var RST_STREAM = false, PUSH_PROMISE = false, WINDOW_UPDATE = false;
   switch(frame.type) {
     case 'DATA'         : DATA          = true; break;
     case 'HEADERS'      : HEADERS       = true; break;
     case 'PRIORITY'     : PRIORITY      = true; break;
     case 'RST_STREAM'   : RST_STREAM    = true; break;
     case 'PUSH_PROMISE' : PUSH_PROMISE  = true; break;
     case 'WINDOW_UPDATE': WINDOW_UPDATE = true; break;
     case 'ALTSVC'       : ALTSVC        = true; break;
-    case 'BLOCKED'      : BLOCKED       = true; break;
+    case 'ORIGIN'       : ORIGIN        = true; break;
   }
 
   var previousState = this.state;
 
   switch (this.state) {
     // All streams start in the **idle** state. In this state, no frames have been exchanged.
     //
     // * Sending or receiving a HEADERS frame causes the stream to become "open".
@@ -478,17 +478,17 @@ Stream.prototype._transition = function 
     // * Receiving a HEADERS frame causes the stream to transition to "half closed (local)".
     // * An endpoint MAY send PRIORITY frames in this state to reprioritize the stream.
     // * Receiving any other type of frame MUST be treated as a stream error of type PROTOCOL_ERROR.
     case 'RESERVED_REMOTE':
       if (RST_STREAM) {
         this._setState('CLOSED');
       } else if (receiving && HEADERS) {
         this._setState('HALF_CLOSED_LOCAL');
-      } else if (BLOCKED || PRIORITY) {
+      } else if (PRIORITY || ORIGIN) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // The **open** state is where both peers can send frames. In this state, sending peers observe
     // advertised stream level flow control limits.
@@ -513,17 +513,17 @@ Stream.prototype._transition = function 
     //
     // * A stream transitions from this state to "closed" when a frame that contains a END_STREAM
     //   flag is received, or when either peer sends a RST_STREAM frame.
     // * An endpoint MAY send or receive PRIORITY frames in this state to reprioritize the stream.
     // * WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the END_STREAM flag.
     case 'HALF_CLOSED_LOCAL':
       if (RST_STREAM || (receiving && frame.flags.END_STREAM)) {
         this._setState('CLOSED');
-      } else if (BLOCKED || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
+      } else if (ORIGIN || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // A stream that is **half closed (remote)** is no longer being used by the peer to send frames.
     // In this state, an endpoint is no longer obligated to maintain a receiver flow control window
@@ -533,17 +533,17 @@ Stream.prototype._transition = function 
     //   respond with a stream error of type STREAM_CLOSED.
     // * A stream can transition from this state to "closed" by sending a frame that contains a
     //   END_STREAM flag, or when either peer sends a RST_STREAM frame.
     // * An endpoint MAY send or receive PRIORITY frames in this state to reprioritize the stream.
     // * A receiver MAY receive a WINDOW_UPDATE frame on a "half closed (remote)" stream.
     case 'HALF_CLOSED_REMOTE':
       if (RST_STREAM || (sending && frame.flags.END_STREAM)) {
         this._setState('CLOSED');
-      } else if (BLOCKED || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
+      } else if (ORIGIN || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
         /* No state change */
       } else {
         connectionError = 'PROTOCOL_ERROR';
       }
       break;
 
     // The **closed** state is the terminal state.
     //
@@ -564,17 +564,17 @@ Stream.prototype._transition = function 
     //   time as being in error.
     // * An endpoint might receive a PUSH_PROMISE frame after it sends RST_STREAM. PUSH_PROMISE
     //   causes a stream to become "reserved". If promised streams are not desired, a RST_STREAM
     //   can be used to close any of those streams.
     case 'CLOSED':
       if (PRIORITY || (sending && RST_STREAM) ||
           (receiving && WINDOW_UPDATE) ||
           (receiving && this._closedByUs &&
-           (this._closedWithRst || RST_STREAM || ALTSVC))) {
+           (this._closedWithRst || RST_STREAM || ALTSVC || ORIGIN))) {
         /* No state change */
       } else {
         streamError = 'STREAM_CLOSED';
       }
       break;
   }
 
   // Noting that the connection was closed by the other endpoint. It may be important in edge cases.
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -655,16 +655,20 @@ class XPCShellTestThread(Thread):
             self.env['PYTHON'] = sys.executable
             self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath
             self.env['DMD_PRELOAD_VAR'] = preloadEnvVar
             self.env['DMD_PRELOAD_VALUE'] = libdmd
 
         if self.test_object.get('subprocess') == 'true':
             self.env['PYTHON'] = sys.executable
 
+        if self.test_object.get('headless', False):
+            self.env["MOZ_HEADLESS"] = '1'
+            self.env["DISPLAY"] = '77' # Set a fake display.
+
         testTimeoutInterval = self.harness_timeout
         # Allow a test to request a multiple of the timeout if it is expected to take long
         if 'requesttimeoutfactor' in self.test_object:
             testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
 
         testTimer = None
         if not self.interactive and not self.debuggerInfo and not self.jsDebuggerInfo:
             testTimer = Timer(testTimeoutInterval, lambda: self.testTimeout(proc))
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -816,18 +816,18 @@
   },
   "DEVICE_RESET_REASON": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
     "description": "GPU Device Reset Reason (ok, hung, removed, reset, internal error, invalid call, out of memory)"
   },
   "FAMILY_SAFETY": {
-    "alert_emails": ["seceng@mozilla.org"],
-    "expires_in_version": "55",
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 16,
     "bug_numbers": [1239166],
     "description": "Status of Family Safety detection and remediation. See nsNSSComponent.cpp."
   },
   "FEED_PROTOCOL_USAGE": {
     "alert_emails": ["jkt@mozilla.com"],
     "bug_numbers": [1345546],
@@ -4418,40 +4418,40 @@
     "expires_in_version": "60",
     "kind": "exponential",
     "high": 86400000,
     "n_buckets": 50,
     "bug_numbers": [1338082],
     "description": "Negative cache duration (ms) received in fullhash response from any v4 provider"
   },
   "CSP_DOCUMENTS_COUNT": {
-    "alert_emails": ["seceng@mozilla.com"],
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1252829],
-    "expires_in_version": "55",
+    "expires_in_version": "60",
     "kind": "count",
     "description": "Number of unique pages that contain a CSP"
   },
   "CSP_UNSAFE_INLINE_DOCUMENTS_COUNT": {
-    "alert_emails": ["seceng@mozilla.com"],
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1252829],
-    "expires_in_version": "55",
+    "expires_in_version": "60",
     "kind": "count",
     "description": "Number of unique pages that contain an unsafe-inline CSP directive"
   },
   "CSP_UNSAFE_EVAL_DOCUMENTS_COUNT": {
-    "alert_emails": ["seceng@mozilla.com"],
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1252829],
-    "expires_in_version": "55",
+    "expires_in_version": "60",
     "kind": "count",
     "description": "Number of unique pages that contain an unsafe-eval CSP directive"
   },
   "CSP_REFERRER_DIRECTIVE": {
     "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1303685],
-    "expires_in_version": "56",
+    "expires_in_version": "60",
     "kind": "boolean",
     "description": "Whether a document with a CSP policy (report-only or enforcing) contains a referrer directive ('true') or not ('false')."
   },
   "PLACES_PAGES_COUNT": {
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 1000,
     "high": 150000,
@@ -7770,18 +7770,18 @@
     "bug_numbers": [1319268],
     "expires_in_version": "58",
     "kind": "linear",
     "high": 30,
     "n_buckets": 29,
     "description": "The number of times AddIceCandidate failed on a given PeerConnection, given that ICE failed."
   },
   "WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN": {
-    "alert_emails": ["seceng@mozilla.org"],
-    "expires_in_version": "55",
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 15,
     "description": "Origins for getUserMedia calls (0=other, 1=HTTPS, 2=file, 3=app, 4=localhost, 5=loop, 6=privileged)",
     "releaseChannelCollection": "opt-out"
   },
   "WEBRTC_GET_USER_MEDIA_TYPE": {
     "alert_emails": ["webrtc-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
@@ -8763,35 +8763,35 @@
     "kind": "exponential",
     "low": 100,
     "high": 30000,
     "n_buckets": 100,
     "keyed": true,
     "description": "The amount of time required for HSTS priming requests (ms), keyed by success or failure of the priming request. (success, failure)"
   },
   "MIXED_CONTENT_OBJECT_SUBREQUEST": {
-    "alert_emails": ["seceng@mozilla.org"],
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1244116],
-    "expires_in_version": "55",
+    "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 10,
     "description": "How often objects load insecure content on secure pages (counting pages, not objects). 0=pages with no mixed object subrequests, 1=pages with mixed object subrequests"
   },
   "COOKIE_SCHEME_SECURITY": {
-    "alert_emails": ["seceng@mozilla.org"],
-    "expires_in_version": "55",
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 10,
     "releaseChannelCollection": "opt-out",
     "description": "How often are secure cookies set from non-secure origins, and vice-versa? 0=nonsecure/http, 1=nonsecure/https, 2=secure/http, 3=secure/https"
   },
   "COOKIE_LEAVE_SECURE_ALONE": {
     "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [976073, 1325909],
-    "expires_in_version": "57",
+    "expires_in_version": "60",
     "kind": "enumerated",
     "n_values": 10,
     "releaseChannelCollection": "opt-out",
     "description": "Strict Secure Cookies: 0=blocked secure cookie from http; 1=blocked shadowing secure cookie from http; 2=shadowing secure cookie from https; 3=evicted newer insecure cookie; 4=evicted oldest insecure cookie; 5=evicted secure cookie; 6=evicting secure cookie blocked; 7=blocked downgrading secure cookie from http; 8=downgraded secure cookie from https"
   },
   "NTLM_MODULE_USED_2": {
     "expires_in_version": "never",
     "kind": "enumerated",
@@ -11308,10 +11308,18 @@
     "bug_numbers": [1348113],
     "expires_in_version": "60",
     "kind": "exponential",
     "low": 32,
     "high": 750,
     "n_buckets": 40,
     "keyed": true,
     "description": "Measures the number of milliseconds we spend waiting for sync message manager IPC messages to finish sending, keyed by message name. Note: only messages that wait for more than 500 microseconds are included in this probe."
+  },
+  "DISPLAY_ITEM_USAGE_COUNT": {
+    "alert_emails": ["mchang@mozilla.com"],
+    "bug_numbers": [1353521],
+    "expires_in_version": "70",
+    "kind": "enumerated",
+    "n_values": 99,
+    "description": "Count of which display items are being used by type id"
   }
 }
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -1036,18 +1036,18 @@ var Impl = {
    * Descriptive metadata
    *
    * @param  reason
    *         The reason for the telemetry ping, this will be included in the
    *         returned metadata,
    * @return The metadata as a JS object
    */
   getMetadata: function getMetadata(reason) {
-    const sessionStartDate = Utils.toLocalTimeISOString(Utils.truncateToDays(this._sessionStartDate));
-    const subsessionStartDate = Utils.toLocalTimeISOString(Utils.truncateToDays(this._subsessionStartDate));
+    const sessionStartDate = Utils.toLocalTimeISOString(Utils.truncateToHours(this._sessionStartDate));
+    const subsessionStartDate = Utils.toLocalTimeISOString(Utils.truncateToHours(this._subsessionStartDate));
     const monotonicNow = Policy.monotonicNow();
 
     let ret = {
       reason,
       revision: AppConstants.SOURCE_REVISION_URL,
       asyncPluginInit: Preferences.get(PREF_ASYNC_PLUGIN_INIT, false),
 
       // Date.getTimezoneOffset() unintuitively returns negative values if we are ahead of
--- a/toolkit/components/telemetry/TelemetryUtils.jsm
+++ b/toolkit/components/telemetry/TelemetryUtils.jsm
@@ -45,26 +45,37 @@ this.TelemetryUtils = {
    * @param aMsec A number of milliseconds since Unix epoch.
    * @return The number of whole days since Unix epoch.
    */
   millisecondsToDays(aMsec) {
     return Math.floor(aMsec / MILLISECONDS_PER_DAY);
   },
 
   /**
-   * Takes a date and returns it trunctated to a date with daily precision.
+   * Takes a date and returns it truncated to a date with daily precision.
    */
   truncateToDays(date) {
     return new Date(date.getFullYear(),
                     date.getMonth(),
                     date.getDate(),
                     0, 0, 0, 0);
   },
 
   /**
+   * Takes a date and returns it truncated to a date with hourly precision.
+   */
+  truncateToHours(date) {
+    return new Date(date.getFullYear(),
+                    date.getMonth(),
+                    date.getDate(),
+                    date.getHours(),
+                    0, 0, 0);
+  },
+
+  /**
    * Check if the difference between the times is within the provided tolerance.
    * @param {Number} t1 A time in milliseconds.
    * @param {Number} t2 A time in milliseconds.
    * @param {Number} tolerance The tolerance, in milliseconds.
    * @return {Boolean} True if the absolute time difference is within the tolerance, false
    *                   otherwise.
    */
   areTimesClose(t1, t2, tolerance) {
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -39,18 +39,18 @@ Structure:
         subsessionId: <uuid>,  // random subsession id
         previousSessionId: <uuid>, // session id of the previous session, null on first run.
         previousSubsessionId: <uuid>, // subsession id of the previous subsession (even if it was in a different session),
                                       // null on first run.
 
         subsessionCounter: <unsigned integer>, // the running no. of this subsession since the start of the browser session
         profileSubsessionCounter: <unsigned integer>, // the running no. of all subsessions for the whole profile life time
 
-        sessionStartDate: <ISO date>, // daily precision
-        subsessionStartDate: <ISO date>, // daily precision, ISO date in local time
+        sessionStartDate: <ISO date>, // hourly precision, ISO date in local time
+        subsessionStartDate: <ISO date>, // hourly precision, ISO date in local time
         sessionLength: <integer>, // the session length until now in seconds, monotonic
         subsessionLength: <integer>, // the subsession length in seconds, monotonic
 
         flashVersion: <string>, // obsolete, use ``environment.addons.activePlugins``
         addons: <string>, // obsolete, use ``environment.addons``
       },
 
       processes: {...},
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -72,23 +72,16 @@ const ABORTED_SESSION_UPDATE_INTERVAL_MS
 
 XPCOMUtils.defineLazyGetter(this, "DATAREPORTING_PATH", function() {
   return OS.Path.join(OS.Constants.Path.profileDir, DATAREPORTING_DIR);
 });
 
 var gClientID = null;
 var gMonotonicNow = 0;
 
-function truncateDateToDays(date) {
-  return new Date(date.getFullYear(),
-                  date.getMonth(),
-                  date.getDate(),
-                  0, 0, 0, 0);
-}
-
 function sendPing() {
   TelemetrySession.gatherStartup();
   if (PingServer.started) {
     TelemetrySend.setServer("http://localhost:" + PingServer.port);
     return TelemetrySession.testPing();