Merge mozilla-central to mozilla-inbound. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Wed, 08 May 2019 06:40:43 +0300
changeset 531866 a44f70589c96a0fbcfe065f868393fdec18e0b59
parent 531865 e68228c66c551c0e34b6e17454cd6909f910ef22 (current diff)
parent 531801 40bd76567ad73911f3dd035b3464ea5fdd7642a2 (diff)
child 531867 eb44ee7510d1ce6ec33e82453d774a91427c04fc
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. CLOSED TREE
devtools/client/debugger/src/utils/sources-tree/createTree.js
devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/sortTree.spec.js.snap
devtools/client/debugger/src/utils/sources-tree/tests/sortTree.spec.js
js/src/vm/Debugger.cpp
mobile/android/app/src/main/res/drawable-nodpi/firstrun_complete.png
testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html.ini
testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html.ini
toolkit/mozapps/update/tests/unit_aus_update/uiAutoPref.js
toolkit/mozapps/update/tests/unit_aus_update/uiSilentPref.js
toolkit/mozapps/update/tests/unit_aus_update/uiUnsupportedAlreadyNotified.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -406,17 +406,17 @@ pref("permissions.manager.defaultsUrl", 
 // Set default fallback values for site permissions we want
 // the user to be able to globally change.
 pref("permissions.default.camera", 0);
 pref("permissions.default.microphone", 0);
 pref("permissions.default.geo", 0);
 pref("permissions.default.desktop-notification", 0);
 pref("permissions.default.shortcuts", 0);
 
-#ifdef NIGHTLY_BUILD
+#ifdef EARLY_BETA_OR_EARLIER
 pref("permissions.desktop-notification.postPrompt.enabled", true);
 #else
 pref("permissions.desktop-notification.postPrompt.enabled", false);
 #endif
 
 pref("permissions.postPrompt.animate", true);
 
 // This is primarily meant to be enabled for studies.
--- a/browser/base/content/test/caps/browser.ini
+++ b/browser/base/content/test/caps/browser.ini
@@ -1,3 +1,4 @@
 [DEFAULT]
 
 [browser_principalSerialization_version1.js]
+[browser_principalSerialization_csp.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/caps/browser_principalSerialization_csp.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * Within Bug 965637 we move the CSP away from the Principal. Serialized Principals however
+ * might still have CSPs serialized within them. This tests ensures that we do not
+ * encounter a memory corruption when deserializing. It's fine that the deserialized
+ * CSP is null, but the Principal itself should deserialize correctly.
+ */
+
+add_task(async function test_deserialize_principal_with_csp() {
+  /*
+  This test should be resilient to changes in principal serialization, if these are failing then it's likely the code will break session storage.
+  To recreate this for another version, copy the function into the browser console, browse some pages and printHistory.
+
+  Generated with:
+  function printHistory() {
+    let tests = [];
+    let entries = SessionStore.getSessionHistory(gBrowser.selectedTab).entries.map((entry) => { return entry.triggeringPrincipal_base64 });
+    entries.push(E10SUtils.serializePrincipal(gBrowser.selectedTab.linkedBrowser._contentPrincipal));
+    for (let entry of entries) {
+      console.log(entry);
+      let testData = {};
+      testData.input = entry;
+      let principal = E10SUtils.deserializePrincipal(testData.input);
+      testData.output = {};
+      if (principal.URI === null) {
+        testData.output.URI = false;
+      } else {
+        testData.output.URISpec = principal.URI.spec;
+      }
+      testData.output.originAttributes = principal.originAttributes;
+      testData.output.cspJSON = principal.cspJSON;
+
+      tests.push(testData);
+    }
+    return tests;
+  }
+  printHistory(); // Copy this into: serializedPrincipalsFromFirefox
+  */
+
+  let serializedPrincipalsFromFirefox = [
+    {
+      "input": "ZT4OTT7kRfqycpfCC8AeuAAAAAAAAAAAwAAAAAAAAEYB3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAHmh0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTLwAAAAAAAAAFAAAACAAAAA8AAAAA/////wAAAAD/////AAAACAAAAA8AAAAXAAAABwAAABcAAAAHAAAAFwAAAAcAAAAeAAAAAAAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AQAAAAAAAAAAAAAAAQnZ7Rrl1EAEv+Anzrkj2ayzxMCuvV5MrYfgjSENuz+fAd6UctCANBHTk5kAEEug/UCSBzpUbXhPMJE6uHGBMgjGAAAAAv////8AAAG7AQAAAB5odHRwczovL3d3dy5tb3ppbGxhLm9yZy9lbi1VUy8AAAAAAAAABQAAAAgAAAAPAAAAAP////8AAAAA/////wAAAAgAAAAPAAAAFwAAAAcAAAAXAAAABwAAABcAAAAHAAAAHgAAAAAAAAAA/////wAAAAD/////AAAAAP////8AAAAA/////wEAAAAAAAAAAAABAAAFtgBzAGMAcgBpAHAAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIAAnAHUAbgBzAGEAZgBlAC0AaQBuAGwAaQBuAGUAJwAgACcAdQBuAHMAYQBmAGUALQBlAHYAYQBsACcAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdABhAGcAbQBhAG4AYQBnAGUAcgAuAGcAbwBvAGcAbABlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AcwAuAHkAdABpAG0AZwAuAGMAbwBtADsAIABpAG0AZwAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIABkAGEAdABhADoAIABoAHQAdABwAHMAOgAvAC8AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAZABzAGUAcgB2AGkAYwBlAC4AZwBvAG8AZwBsAGUALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AZABlACAAaAB0AHQAcABzADoALwAvAGEAZABzAGUAcgB2AGkAYwBlAC4AZwBvAG8AZwBsAGUALgBkAGsAIABoAHQAdABwAHMAOgAvAC8AYwByAGUAYQB0AGkAdgBlAGMAbwBtAG0AbwBuAHMALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwBhAGQALgBkAG8AdQBiAGwAZQBjAGwAaQBjAGsALgBuAGUAdAA7ACAAZABlAGYAYQB1AGwAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AOwAgAGYAcgBhAG0AZQAtAHMAcgBjACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAtAG4AbwBjAG8AbwBrAGkAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAcgBhAGMAawBlAHIAdABlAHMAdAAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AcwB1AHIAdgBlAHkAZwBpAHoAbQBvAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAuAGMAbgAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALgBjAG8AbQA7ACAAcwB0AHkAbABlAC0AcwByAGMAIAAnAHMAZQBsAGYAJwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG4AZQB0ACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBjAG8AbQAgACcAdQBuAHMAYQBmAGUALQBpAG4AbABpAG4AZQAnADsAIABjAG8AbgBuAGUAYwB0AC0AcwByAGMAIAAnAHMAZQBsAGYAJwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG4AZQB0ACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALwAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALgBjAG4ALwA7ACAAYwBoAGkAbABkAC0AcwByAGMAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC0AbgBvAGMAbwBvAGsAaQBlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdAByAGEAYwBrAGUAcgB0AGUAcwB0AC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBzAHUAcgB2AGUAeQBnAGkAegBtAG8ALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC4AYwBuACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAuAGMAbwBtAAA=",
+      "output": {
+        "cspJSON": "{\"csp-policies\":[{\"child-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"connect-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://accounts.firefox.com/\",\"https://accounts.firefox.com.cn/\"],\"default-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\"],\"frame-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"img-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"data:\",\"https://mozilla.org\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://adservice.google.com\",\"https://adservice.google.de\",\"https://adservice.google.dk\",\"https://creativecommons.org\",\"https://ad.doubleclick.net\"],\"report-only\":false,\"script-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\",\"'unsafe-eval'\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://tagmanager.google.com\",\"https://www.youtube.com\",\"https://s.ytimg.com\"],\"style-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\"]}]}",
+        "URISpec": "https://www.mozilla.org/en-US/",
+        "originAttributes": {
+          "firstPartyDomain": "",
+          "inIsolatedMozBrowser": false,
+          "privateBrowsingId": 0,
+          "userContextId": 0,
+        },
+      },
+    },
+    {
+      "input": "ZT4OTT7kRfqycpfCC8AeuAAAAAAAAAAAwAAAAAAAAEYB3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAL2h0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTL2ZpcmVmb3gvYWNjb3VudHMvAAAAAAAAAAUAAAAIAAAADwAAAAj/////AAAACP////8AAAAIAAAADwAAABcAAAAYAAAAFwAAABgAAAAXAAAAGAAAAC8AAAAAAAAAL/////8AAAAA/////wAAABf/////AAAAF/////8BAAAAAAAAAAAAAAABCdntGuXUQAS/4CfOuSPZrLPEwK69Xkyth+CNIQ27P58B3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAL2h0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTL2ZpcmVmb3gvYWNjb3VudHMvAAAAAAAAAAUAAAAIAAAADwAAAAj/////AAAACP////8AAAAIAAAADwAAABcAAAAYAAAAFwAAABgAAAAXAAAAGAAAAC8AAAAAAAAAL/////8AAAAA/////wAAABf/////AAAAF/////8BAAAAAAAAAAAAAQAABbYAcwBjAHIAaQBwAHQALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtACAAJwB1AG4AcwBhAGYAZQAtAGkAbgBsAGkAbgBlACcAIAAnAHUAbgBzAGEAZgBlAC0AZQB2AGEAbAAnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBnAG8AbwBnAGwAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHMALgB5AHQAaQBtAGcALgBjAG8AbQA7ACAAaQBtAGcALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtACAAZABhAHQAYQA6ACAAaAB0AHQAcABzADoALwAvAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBkAHMAZQByAHYAaQBjAGUALgBnAG8AbwBnAGwAZQAuAGQAZQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AZABrACAAaAB0AHQAcABzADoALwAvAGMAcgBlAGEAdABpAHYAZQBjAG8AbQBtAG8AbgBzAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AYQBkAC4AZABvAHUAYgBsAGUAYwBsAGkAYwBrAC4AbgBlAHQAOwAgAGQAZQBmAGEAdQBsAHQALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtADsAIABmAHIAYQBtAGUALQBzAHIAYwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALQBuAG8AYwBvAG8AawBpAGUALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB0AHIAYQBjAGsAZQByAHQAZQBzAHQALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHMAdQByAHYAZQB5AGcAaQB6AG0AbwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALgBjAG4AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC4AYwBvAG0AOwAgAHMAdAB5AGwAZQAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIAAnAHUAbgBzAGEAZgBlAC0AaQBuAGwAaQBuAGUAJwA7ACAAYwBvAG4AbgBlAGMAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC8AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC4AYwBuAC8AOwAgAGMAaABpAGwAZAAtAHMAcgBjACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAtAG4AbwBjAG8AbwBrAGkAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAcgBhAGMAawBlAHIAdABlAHMAdAAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AcwB1AHIAdgBlAHkAZwBpAHoAbQBvAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAuAGMAbgAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALgBjAG8AbQAA",
+      "output": {
+        "URISpec": "https://www.mozilla.org/en-US/firefox/accounts/",
+        "originAttributes": {
+          "firstPartyDomain": "",
+          "inIsolatedMozBrowser": false,
+          "privateBrowsingId": 0,
+          "userContextId": 0,
+        },
+        "cspJSON": "{\"csp-policies\":[{\"child-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"connect-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://accounts.firefox.com/\",\"https://accounts.firefox.com.cn/\"],\"default-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\"],\"frame-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"img-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"data:\",\"https://mozilla.org\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://adservice.google.com\",\"https://adservice.google.de\",\"https://adservice.google.dk\",\"https://creativecommons.org\",\"https://ad.doubleclick.net\"],\"report-only\":false,\"script-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\",\"'unsafe-eval'\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://tagmanager.google.com\",\"https://www.youtube.com\",\"https://s.ytimg.com\"],\"style-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\"]}]}",
+      },
+    },
+  ];
+
+  for (let test of serializedPrincipalsFromFirefox) {
+    let principal = E10SUtils.deserializePrincipal(test.input);
+    is(principal.cspJSON, test.output.cspJSON, "should have CSP");
+
+    for (let key in principal.originAttributes) {
+      is(principal.originAttributes[key], test.output.originAttributes[key], `Ensure value of ${key} is ${test.output.originAttributes[key]}`);
+    }
+
+    if ("URI" in test.output && test.output.URI === false) {
+      is(principal.URI, null, "Should have not have a URI for system");
+    } else {
+      is(principal.URI.spec, test.output.URISpec, `Should have spec ${test.output.URISpec}`);
+    }
+  }
+});
--- a/browser/base/content/test/caps/browser_principalSerialization_version1.js
+++ b/browser/base/content/test/caps/browser_principalSerialization_version1.js
@@ -117,42 +117,16 @@ add_task(async function test_realHistory
           "inIsolatedMozBrowser": false,
           "privateBrowsingId": 0,
           "userContextId": 0,
         },
         "cspJSON": "{}",
       },
     },
     {
-      "input": "ZT4OTT7kRfqycpfCC8AeuAAAAAAAAAAAwAAAAAAAAEYB3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAHmh0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTLwAAAAAAAAAFAAAACAAAAA8AAAAA/////wAAAAD/////AAAACAAAAA8AAAAXAAAABwAAABcAAAAHAAAAFwAAAAcAAAAeAAAAAAAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AQAAAAAAAAAAAAAAAQnZ7Rrl1EAEv+Anzrkj2ayzxMCuvV5MrYfgjSENuz+fAd6UctCANBHTk5kAEEug/UCSBzpUbXhPMJE6uHGBMgjGAAAAAv////8AAAG7AQAAAB5odHRwczovL3d3dy5tb3ppbGxhLm9yZy9lbi1VUy8AAAAAAAAABQAAAAgAAAAPAAAAAP////8AAAAA/////wAAAAgAAAAPAAAAFwAAAAcAAAAXAAAABwAAABcAAAAHAAAAHgAAAAAAAAAA/////wAAAAD/////AAAAAP////8AAAAA/////wEAAAAAAAAAAAABAAAFtgBzAGMAcgBpAHAAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIAAnAHUAbgBzAGEAZgBlAC0AaQBuAGwAaQBuAGUAJwAgACcAdQBuAHMAYQBmAGUALQBlAHYAYQBsACcAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdABhAGcAbQBhAG4AYQBnAGUAcgAuAGcAbwBvAGcAbABlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AcwAuAHkAdABpAG0AZwAuAGMAbwBtADsAIABpAG0AZwAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIABkAGEAdABhADoAIABoAHQAdABwAHMAOgAvAC8AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAZABzAGUAcgB2AGkAYwBlAC4AZwBvAG8AZwBsAGUALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AZABlACAAaAB0AHQAcABzADoALwAvAGEAZABzAGUAcgB2AGkAYwBlAC4AZwBvAG8AZwBsAGUALgBkAGsAIABoAHQAdABwAHMAOgAvAC8AYwByAGUAYQB0AGkAdgBlAGMAbwBtAG0AbwBuAHMALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwBhAGQALgBkAG8AdQBiAGwAZQBjAGwAaQBjAGsALgBuAGUAdAA7ACAAZABlAGYAYQB1AGwAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AOwAgAGYAcgBhAG0AZQAtAHMAcgBjACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAtAG4AbwBjAG8AbwBrAGkAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAcgBhAGMAawBlAHIAdABlAHMAdAAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AcwB1AHIAdgBlAHkAZwBpAHoAbQBvAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAuAGMAbgAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALgBjAG8AbQA7ACAAcwB0AHkAbABlAC0AcwByAGMAIAAnAHMAZQBsAGYAJwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG4AZQB0ACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBjAG8AbQAgACcAdQBuAHMAYQBmAGUALQBpAG4AbABpAG4AZQAnADsAIABjAG8AbgBuAGUAYwB0AC0AcwByAGMAIAAnAHMAZQBsAGYAJwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG4AZQB0ACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALwAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALgBjAG4ALwA7ACAAYwBoAGkAbABkAC0AcwByAGMAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC0AbgBvAGMAbwBvAGsAaQBlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdAByAGEAYwBrAGUAcgB0AGUAcwB0AC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBzAHUAcgB2AGUAeQBnAGkAegBtAG8ALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC4AYwBuACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAuAGMAbwBtAAA=",
-      "output": {
-        "cspJSON": "{\"csp-policies\":[{\"child-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"connect-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://accounts.firefox.com/\",\"https://accounts.firefox.com.cn/\"],\"default-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\"],\"frame-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"img-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"data:\",\"https://mozilla.org\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://adservice.google.com\",\"https://adservice.google.de\",\"https://adservice.google.dk\",\"https://creativecommons.org\",\"https://ad.doubleclick.net\"],\"report-only\":false,\"script-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\",\"'unsafe-eval'\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://tagmanager.google.com\",\"https://www.youtube.com\",\"https://s.ytimg.com\"],\"style-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\"]}]}",
-        "URISpec": "https://www.mozilla.org/en-US/",
-        "originAttributes": {
-          "firstPartyDomain": "",
-          "inIsolatedMozBrowser": false,
-          "privateBrowsingId": 0,
-          "userContextId": 0,
-        },
-      },
-    },
-    {
-      "input": "ZT4OTT7kRfqycpfCC8AeuAAAAAAAAAAAwAAAAAAAAEYB3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAL2h0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTL2ZpcmVmb3gvYWNjb3VudHMvAAAAAAAAAAUAAAAIAAAADwAAAAj/////AAAACP////8AAAAIAAAADwAAABcAAAAYAAAAFwAAABgAAAAXAAAAGAAAAC8AAAAAAAAAL/////8AAAAA/////wAAABf/////AAAAF/////8BAAAAAAAAAAAAAAABCdntGuXUQAS/4CfOuSPZrLPEwK69Xkyth+CNIQ27P58B3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAL2h0dHBzOi8vd3d3Lm1vemlsbGEub3JnL2VuLVVTL2ZpcmVmb3gvYWNjb3VudHMvAAAAAAAAAAUAAAAIAAAADwAAAAj/////AAAACP////8AAAAIAAAADwAAABcAAAAYAAAAFwAAABgAAAAXAAAAGAAAAC8AAAAAAAAAL/////8AAAAA/////wAAABf/////AAAAF/////8BAAAAAAAAAAAAAQAABbYAcwBjAHIAaQBwAHQALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtACAAJwB1AG4AcwBhAGYAZQAtAGkAbgBsAGkAbgBlACcAIAAnAHUAbgBzAGEAZgBlAC0AZQB2AGEAbAAnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBnAG8AbwBnAGwAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHMALgB5AHQAaQBtAGcALgBjAG8AbQA7ACAAaQBtAGcALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtACAAZABhAHQAYQA6ACAAaAB0AHQAcABzADoALwAvAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBkAHMAZQByAHYAaQBjAGUALgBnAG8AbwBnAGwAZQAuAGQAZQAgAGgAdAB0AHAAcwA6AC8ALwBhAGQAcwBlAHIAdgBpAGMAZQAuAGcAbwBvAGcAbABlAC4AZABrACAAaAB0AHQAcABzADoALwAvAGMAcgBlAGEAdABpAHYAZQBjAG8AbQBtAG8AbgBzAC4AbwByAGcAIABoAHQAdABwAHMAOgAvAC8AYQBkAC4AZABvAHUAYgBsAGUAYwBsAGkAYwBrAC4AbgBlAHQAOwAgAGQAZQBmAGEAdQBsAHQALQBzAHIAYwAgACcAcwBlAGwAZgAnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AbgBlAHQAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAGMAbwBtADsAIABmAHIAYQBtAGUALQBzAHIAYwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAHQAYQBnAG0AYQBuAGEAZwBlAHIALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAGcAbwBvAGcAbABlAC0AYQBuAGEAbAB5AHQAaQBjAHMALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALQBuAG8AYwBvAG8AawBpAGUALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwB0AHIAYQBjAGsAZQByAHQAZQBzAHQALgBvAHIAZwAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHMAdQByAHYAZQB5AGcAaQB6AG0AbwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAgAGgAdAB0AHAAcwA6AC8ALwBhAGMAYwBvAHUAbgB0AHMALgBmAGkAcgBlAGYAbwB4AC4AYwBvAG0ALgBjAG4AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgB5AG8AdQB0AHUAYgBlAC4AYwBvAG0AOwAgAHMAdAB5AGwAZQAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIAAnAHUAbgBzAGEAZgBlAC0AaQBuAGwAaQBuAGUAJwA7ACAAYwBvAG4AbgBlAGMAdAAtAHMAcgBjACAAJwBzAGUAbABmACcAIABoAHQAdABwAHMAOgAvAC8AKgAuAG0AbwB6AGkAbABsAGEALgBuAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwAqAC4AbQBvAHoAaQBsAGwAYQAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvACoALgBtAG8AegBpAGwAbABhAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQB0AGEAZwBtAGEAbgBhAGcAZQByAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AdwB3AHcALgBnAG8AbwBnAGwAZQAtAGEAbgBhAGwAeQB0AGkAYwBzAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC8AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtAC4AYwBuAC8AOwAgAGMAaABpAGwAZAAtAHMAcgBjACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUAdABhAGcAbQBhAG4AYQBnAGUAcgAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AZwBvAG8AZwBsAGUALQBhAG4AYQBsAHkAdABpAGMAcwAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AeQBvAHUAdAB1AGIAZQAtAG4AbwBjAG8AbwBrAGkAZQAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAHQAcgBhAGMAawBlAHIAdABlAHMAdAAuAG8AcgBnACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AcwB1AHIAdgBlAHkAZwBpAHoAbQBvAC4AYwBvAG0AIABoAHQAdABwAHMAOgAvAC8AYQBjAGMAbwB1AG4AdABzAC4AZgBpAHIAZQBmAG8AeAAuAGMAbwBtACAAaAB0AHQAcABzADoALwAvAGEAYwBjAG8AdQBuAHQAcwAuAGYAaQByAGUAZgBvAHgALgBjAG8AbQAuAGMAbgAgAGgAdAB0AHAAcwA6AC8ALwB3AHcAdwAuAHkAbwB1AHQAdQBiAGUALgBjAG8AbQAA",
-      "output": {
-        "URISpec": "https://www.mozilla.org/en-US/firefox/accounts/",
-        "originAttributes": {
-          "firstPartyDomain": "",
-          "inIsolatedMozBrowser": false,
-          "privateBrowsingId": 0,
-          "userContextId": 0,
-        },
-        "cspJSON": "{\"csp-policies\":[{\"child-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"connect-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://accounts.firefox.com/\",\"https://accounts.firefox.com.cn/\"],\"default-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\"],\"frame-src\":[\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://www.youtube-nocookie.com\",\"https://trackertest.org\",\"https://www.surveygizmo.com\",\"https://accounts.firefox.com\",\"https://accounts.firefox.com.cn\",\"https://www.youtube.com\"],\"img-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"data:\",\"https://mozilla.org\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://adservice.google.com\",\"https://adservice.google.de\",\"https://adservice.google.dk\",\"https://creativecommons.org\",\"https://ad.doubleclick.net\"],\"report-only\":false,\"script-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\",\"'unsafe-eval'\",\"https://www.googletagmanager.com\",\"https://www.google-analytics.com\",\"https://tagmanager.google.com\",\"https://www.youtube.com\",\"https://s.ytimg.com\"],\"style-src\":[\"'self'\",\"https://*.mozilla.net\",\"https://*.mozilla.org\",\"https://*.mozilla.com\",\"'unsafe-inline'\"]}]}",
-      },
-    },
-    {
       "input": "ZT4OTT7kRfqycpfCC8AeuAAAAAAAAAAAwAAAAAAAAEYB3pRy0IA0EdOTmQAQS6D9QJIHOlRteE8wkTq4cYEyCMYAAAAC/////wAAAbsBAAAAe2h0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTLz91dG1fc291cmNlPXd3dy5tb3ppbGxhLm9yZyZ1dG1fbWVkaXVtPXJlZmVycmFsJnV0bV9jYW1wYWlnbj1uYXYmdXRtX2NvbnRlbnQ9ZGV2ZWxvcGVycwAAAAAAAAAFAAAACAAAABUAAAAA/////wAAAAD/////AAAACAAAABUAAAAdAAAAXgAAAB0AAAAHAAAAHQAAAAcAAAAkAAAAAAAAAAD/////AAAAAP////8AAAAlAAAAVgAAAAD/////AQAAAAAAAAAAAAAAAA==",
       "output": {
         "URISpec": "https://developer.mozilla.org/en-US/?utm_source=www.mozilla.org&utm_medium=referral&utm_campaign=nav&utm_content=developers",
         "originAttributes": {
           "firstPartyDomain": "",
           "inIsolatedMozBrowser": false,
           "privateBrowsingId": 0,
           "userContextId": 0,
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_content_mainthreadio.js
@@ -0,0 +1,419 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test records I/O syscalls done on the main thread during startup.
+ *
+ * To run this test similar to try server, you need to run:
+ *   ./mach package
+ *   ./mach test --appname=dist <path to test>
+ *
+ * If you made changes that cause this test to fail, it's likely because you
+ * are touching more files or directories during startup.
+ * Most code has no reason to use main thread I/O.
+ * If for some reason accessing the file system on the main thread is currently
+ * unavoidable, consider defering the I/O as long as you can, ideally after
+ * the end of startup.
+ */
+
+"use strict";
+
+/* Set this to true only for debugging purpose; it makes the output noisy. */
+const kDumpAllStacks = false;
+
+// Shortcuts for conditions.
+const LINUX = AppConstants.platform == "linux";
+const WIN = AppConstants.platform == "win";
+const MAC = AppConstants.platform == "macosx";
+
+/* Paths in the whitelist can:
+ *  - be a full path, eg. "/etc/mime.types"
+ *  - have a prefix which will be resolved using Services.dirsvc
+ *    eg. "GreD:omni.ja"
+ *    It's possible to have only a prefix, in thise case the directory will
+ *    still be resolved, eg. "UAppData:"
+ *  - use * at the begining and/or end as a wildcard
+ * The folder separator is '/' even for Windows paths, where it'll be
+ * automatically converted to '\'.
+ *
+ * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
+ * without this the test is strict and will fail if a whitelist entry isn't used.
+ *
+ * Each entry specifies the maximum number of times an operation is expected to
+ * occur.
+ * The operations currently reported by the I/O interposer are:
+ *   create/open: only supported on Windows currently. The test currently
+ *     ignores these markers to have a shorter initial whitelist.
+ *     Adding Unix support is bug 1533779.
+ *   stat: supported on all platforms when checking the last modified date or
+ *     file size. Supported only on Windows when checking if a file exists;
+ *     fixing this inconsistency is bug 1536109.
+ *   read: supported on all platforms, but unix platforms will only report read
+ *     calls going through NSPR.
+ *   write: supported on all platforms, but Linux will only report write calls
+ *     going through NSPR.
+ *   close: supported only on Unix, and only for close calls going through NSPR.
+ *     Adding Windows support is bug 1524574.
+ *   fsync: supported only on Windows.
+ *
+ * If an entry specifies more than one operation, if at least one of them is
+ * encountered, the test won't report a failure for the entry. This helps when
+ * whitelisting cases where the reported operations aren't the same on all
+ * platforms due to the I/O interposer inconsistencies across platforms
+ * documented above.
+ */
+const processes = {
+  "Web Content": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+  ],
+  "Privileged Content": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+  ],
+  "WebExtensions": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:screenshots@mozilla.org.xpi",
+      condition: !WIN,
+      close: 1,
+    },
+  ],
+};
+
+function expandWhitelistPath(path) {
+  if (path.includes(":")) {
+    let [prefix, suffix] = path.split(":");
+    let [key, property] = prefix.split(".");
+    let dir = Services.dirsvc.get(key, Ci.nsIFile);
+    if (property) {
+      dir = dir[property];
+    }
+
+    // Resolve symLinks.
+    let dirPath = dir.path;
+    while (dir && !dir.isSymlink()) {
+      dir = dir.parent;
+    }
+    if (dir) {
+      dirPath = dirPath.replace(dir.path, dir.target);
+    }
+
+    path = dirPath;
+
+    if (suffix) {
+      path += "/" + suffix;
+    }
+  }
+  if (AppConstants.platform == "win") {
+    path = path.replace(/\//g, "\\");
+  }
+  return path;
+}
+
+function getStackFromProfile(profile, stack) {
+  const stackPrefixCol = profile.stackTable.schema.prefix;
+  const stackFrameCol = profile.stackTable.schema.frame;
+  const frameLocationCol = profile.frameTable.schema.location;
+
+  let result = [];
+  while (stack) {
+    let sp = profile.stackTable.data[stack];
+    let frame = profile.frameTable.data[sp[stackFrameCol]];
+    stack = sp[stackPrefixCol];
+    frame = profile.stringTable[frame[frameLocationCol]];
+    if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
+      result.push(frame);
+    }
+  }
+  return result;
+}
+
+function getIOMarkersFromProfile(profile) {
+  const nameCol = profile.markers.schema.name;
+  const dataCol = profile.markers.schema.data;
+
+  let markers = [];
+  for (let m of profile.markers.data) {
+    let markerName = profile.stringTable[m[nameCol]];
+
+    if (markerName != "FileIO")
+      continue;
+
+    let markerData = m[dataCol];
+    if (markerData.source == "sqlite-mainthread") {
+      continue;
+    }
+
+    let samples = markerData.stack.samples;
+    let stack = samples.data[0][samples.schema.stack];
+    markers.push({operation: markerData.operation,
+                  filename: markerData.filename,
+                  source: markerData.source,
+                  stackId: stack});
+  }
+
+  return markers;
+}
+
+function pathMatches(path, filename) {
+  path = path.toLowerCase();
+  return path == filename || // Full match
+    // Wildcard on both sides of the path
+    (path.startsWith("*") && path.endsWith("*") &&
+     filename.includes(path.slice(1, -1))) ||
+    // Wildcard suffix
+    (path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
+    // Wildcard prefix
+    (path.startsWith("*") && filename.endsWith(path.slice(1)));
+}
+
+add_task(async function() {
+  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
+    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
+       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
+       "non-debug build.");
+    return;
+  }
+
+  {
+    let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+    omniJa.append("omni.ja");
+    if (!omniJa.exists()) {
+      ok(false, "This test requires a packaged build, " +
+                "run 'mach package' and then use --appname=dist");
+      return;
+    }
+  }
+
+  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
+  await startupRecorder.done;
+
+  for (let process in processes) {
+    processes[process] =
+      processes[process].filter(entry => !("condition" in entry) || entry.condition);
+    processes[process].forEach(entry => {
+      entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
+    });
+  }
+
+  let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
+  let shouldPass = true;
+  for (let procName in processes) {
+    let whitelist = processes[procName];
+    info(`whitelisted paths for ${procName} process:\n` +
+         whitelist.map(e => {
+           let operations = Object.keys(e).filter(k => !["path", "condition"].includes(k))
+                                  .map(k => `${k}: ${e[k]}`);
+           return `  ${e.path} - ${operations.join(", ")}`;
+         }).join("\n"));
+
+    let profile;
+    for (let process of startupRecorder.data.profile.processes) {
+      if (process.threads[0].processName == procName) {
+        profile = process.threads[0];
+        break;
+      }
+    }
+    if (procName == "Privileged Content" && !profile) {
+      // The Privileged Content is started from an idle task that may not have
+      // been executed yet at the time we captured the startup profile in
+      // startupRecorder.
+      todo(false, `profile for ${procName} process not found`);
+    } else {
+      ok(profile, `Found profile for ${procName} process`);
+    }
+    if (!profile) {
+      continue;
+    }
+
+    let markers = getIOMarkersFromProfile(profile);
+    for (let marker of markers) {
+      if (marker.operation == "create/open") {
+        // TODO: handle these I/O markers once they are supported on
+        // non-Windows platforms.
+        continue;
+      }
+
+      // Convert to lower case before comparing because the OS X test slaves
+      // have the 'Firefox' folder in 'Library/Application Support' created
+      // as 'firefox' for some reason.
+      let filename = marker.filename.toLowerCase();
+
+      if (!filename) {
+        // We are still missing the filename on some mainthreadio markers,
+        // these markers are currently useless for the purpose of this test.
+        continue;
+      }
+
+      if (!WIN) {
+        if (filename == "/dev/urandom") {
+          continue;
+        }
+
+        // Ignore I/O due to IPC. This doesn't really touch the disk.
+        if (filename.startsWith(tmpPath + "/org.chromium.")) {
+          continue;
+        }
+      }
+
+      let expected = false;
+      for (let entry of whitelist) {
+        if (pathMatches(entry.path, filename)) {
+          entry[marker.operation] = (entry[marker.operation] || 0) - 1;
+          entry._used = true;
+          expected = true;
+          break;
+        }
+      }
+      if (!expected) {
+        record(false,
+               `unexpected ${marker.operation} on ${marker.filename} in ${procName} process`,
+               undefined,
+               "  " + getStackFromProfile(profile, marker.stackId).join("\n  "));
+        shouldPass = false;
+      }
+      info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
+      if (kDumpAllStacks) {
+        info(getStackFromProfile(profile, marker.stackId).map(f => "  " + f)
+                                                         .join("\n"));
+      }
+    }
+
+    for (let entry of whitelist) {
+      for (let op in entry) {
+        if (["path", "condition", "ignoreIfUnused", "_used"].includes(op)) {
+          continue;
+        }
+        let message = `${op} on ${entry.path} `;
+        if (entry[op] == 0) {
+          message += "as many times as expected";
+        } else if (entry[op] > 0) {
+          message += `allowed ${entry[op]} more times`;
+        } else {
+          message += `${entry[op] * -1} more times than expected`;
+        }
+        ok(entry[op] >= 0, `${message} in ${procName} process`);
+      }
+      if (!("_used" in entry) && !entry.ignoreIfUnused) {
+        ok(false, `unused whitelist entry ${procName}: ${entry.path}`);
+      }
+    }
+  }
+
+  if (shouldPass) {
+    ok(shouldPass, "No unexpected main thread I/O during startup");
+  } else {
+    const filename = "child-startup-mainthreadio-profile.json";
+    let path = Cc["@mozilla.org/process/environment;1"]
+                 .getService(Ci.nsIEnvironment)
+                 .get("MOZ_UPLOAD_DIR");
+    let encoder = new TextEncoder();
+    let profilePath = OS.Path.join(path, filename);
+    await OS.File.writeAtomic(profilePath,
+                              encoder.encode(JSON.stringify(startupRecorder.data.profile)));
+    ok(false,
+       "Found some unexpected main thread I/O during child process startup; " +
+       "profile uploaded in " + filename);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js
@@ -0,0 +1,933 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test records I/O syscalls done on the main thread during startup.
+ *
+ * To run this test similar to try server, you need to run:
+ *   ./mach package
+ *   ./mach test --appname=dist <path to test>
+ *
+ * If you made changes that cause this test to fail, it's likely because you
+ * are touching more files or directories during startup.
+ * Most code has no reason to use main thread I/O.
+ * If for some reason accessing the file system on the main thread is currently
+ * unavoidable, consider defering the I/O as long as you can, ideally after
+ * the end of startup.
+ * If your code isn't strictly required to show the first browser window,
+ * it shouldn't be loaded before we are done with first paint.
+ * Finally, if your code isn't really needed during startup, it should not be
+ * loaded before we have started handling user events.
+ */
+
+"use strict";
+
+const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+
+/* Set this to true only for debugging purpose; it makes the output noisy. */
+const kDumpAllStacks = false;
+
+// Shortcuts for conditions.
+const LINUX = AppConstants.platform == "linux";
+const WIN = AppConstants.platform == "win";
+const MAC = AppConstants.platform == "macosx";
+
+/* Paths in the whitelist can:
+ *  - be a full path, eg. "/etc/mime.types"
+ *  - have a prefix which will be resolved using Services.dirsvc
+ *    eg. "GreD:omni.ja"
+ *    It's possible to have only a prefix, in thise case the directory will
+ *    still be resolved, eg. "UAppData:"
+ *  - use * at the begining and/or end as a wildcard
+ *  - For Windows specific entries that require resolving the path to its
+ *    canonical form, ie. the old DOS 8.3 format, use canonicalize: true.
+ *    This is needed for stat calls to non-existent files.
+ * The folder separator is '/' even for Windows paths, where it'll be
+ * automatically converted to '\'.
+ *
+ * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
+ * without this the test is strict and will fail if a whitelist entry isn't used.
+ *
+ * Each entry specifies the maximum number of times an operation is expected to
+ * occur.
+ * The operations currently reported by the I/O interposer are:
+ *   create/open: only supported on Windows currently. The test currently
+ *     ignores these markers to have a shorter initial whitelist.
+ *     Adding Unix support is bug 1533779.
+ *   stat: supported on all platforms when checking the last modified date or
+ *     file size. Supported only on Windows when checking if a file exists;
+ *     fixing this inconsistency is bug 1536109.
+ *   read: supported on all platforms, but unix platforms will only report read
+ *     calls going through NSPR.
+ *   write: supported on all platforms, but Linux will only report write calls
+ *     going through NSPR.
+ *   close: supported only on Unix, and only for close calls going through NSPR.
+ *     Adding Windows support is bug 1524574.
+ *   fsync: supported only on Windows.
+ *
+ * If an entry specifies more than one operation, if at least one of them is
+ * encountered, the test won't report a failure for the entry. This helps when
+ * whitelisting cases where the reported operations aren't the same on all
+ * platforms due to the I/O interposer inconsistencies across platforms
+ * documented above.
+ */
+const startupPhases = {
+  // Anything done before or during app-startup must have a compelling reason
+  // to run before we have even selected the user profile.
+  "before profile selection": [
+    { // bug 1541226
+      path: "UAppData:",
+      condition: WIN,
+      stat: 3,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/InstallTime20*",
+      condition: AppConstants.MOZ_CRASHREPORTER,
+      stat: 1, // only caught on Windows.
+      read: 1,
+      write: 2,
+      close: 1,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/LastCrash",
+      condition: WIN && AppConstants.MOZ_CRASHREPORTER,
+      stat: 1, // only caught on Windows.
+      read: 1,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/LastCrash",
+      condition: !WIN && AppConstants.MOZ_CRASHREPORTER,
+      ignoreIfUnused: true, // only if we ever crashed on this machine
+      read: 1,
+      close: 1,
+    },
+    { // bug 1541226
+      path: "DefProfLRt.parent:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // At least the read seems unavoidable for a regular startup.
+      path: "UAppData:profiles.ini",
+      condition: MAC,
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1546931
+      path: "UAppData:installs.ini",
+      condition: WIN || MAC,
+      ignoreIfUnused: true, // only if a real profile exists on the system.
+      read: 1,
+      stat: 2,
+      close: 1,
+    },
+    { // At least the read seems unavoidable for a regular startup.
+      path: "UAppData:profiles.ini",
+      condition: WIN,
+      ignoreIfUnused: true, // only if a real profile exists on the system.
+      read: 1,
+      stat: 1,
+    },
+    { // bug 1541226, bug 1363586, bug 1541593
+      path: "ProfD:",
+      condition: WIN,
+      stat: 3,
+    },
+    {
+      path: "ProfLD:.startup-incomplete",
+      condition: !WIN, // Visible on Windows with an open marker
+      close: 1,
+    },
+    { // bug 1541491 to stop using this file, bug 1541494 to write correctly.
+      path: "ProfLD:compatibility.ini",
+      condition: !WIN, // Visible on Windows with an open marker
+      write: 18,
+      close: 1,
+    },
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    {
+      path: "ProfD:parent.lock",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541603
+      path: "ProfD:minidumps",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543746
+      path: "XCurProcD:defaults/preferences",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-child-current.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-child.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-current.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544037
+      path: "ProfLDS:startupCache/startupCache." +
+             (Services.appinfo.is64Bit ? 8 : 4) + ".little",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541601
+      path: "PrfDef:channel-prefs.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // At least the read seems unavoidable
+      path: "PrefD:prefs.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1543752
+      path: "PrefD:user.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    {
+      path: "*ld.so.conf*",
+      condition: LINUX,
+      ignoreIfUnused: true,
+      read: 22,
+      close: 11,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: WIN,
+      write: 1,
+      fsync: 1,
+    },
+  ],
+
+  "before opening first browser window": [
+    { // bug 1541226
+      path: "ProfD:",
+      condition: WIN,
+      stat: 2,
+    },
+    {
+      path: "XCurProcD:blocklist.xml",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite-journal",
+      condition: !LINUX,
+      stat: 3,
+      write: 4,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite",
+      condition: !LINUX,
+      stat: 2,
+      read: 2,
+      write: 1,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite-wal",
+      condition: WIN,
+      stat: 2,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite",
+      condition: WIN || MAC,
+      fsync: 7,
+      read: 2,
+      stat: 1,
+      write: 10,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite-journal",
+      condition: WIN || MAC,
+      fsync: 7,
+      stat: 26,
+      write: 38,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite-wal",
+      condition: WIN,
+      stat: 20,
+    },
+    { // Seems done by OS X and outside of our control.
+      path: "*.savedState/restorecount.plist",
+      condition: MAC,
+      ignoreIfUnused: true,
+      write: 1,
+    },
+    {
+      path: "*ld.so.conf*",
+      condition: LINUX,
+      ignoreIfUnused: true,
+      read: 22,
+      close: 11,
+    },
+    { // bug 1545167
+      path: "/etc/mime.types",
+      condition: LINUX,
+      read: 3,
+      close: 3,
+    },
+    {
+      path: "UChrm:userChrome.css",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541233
+      path: "UChrm:userContent.css",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XREUSysExt:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XRESysExtDev:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "ProfD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XCurProcD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "UAppData:",
+      ignoreIfUnused: true, // sometimes before opening first browser window,
+                            // sometimes before first paint
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: WIN,
+      read: 1,
+    },
+  ],
+
+  // We reach this phase right after showing the first browser window.
+  // This means that any I/O at this point delayed first paint.
+  "before first paint": [
+    { // bug 1541226
+      path: "ProfD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545119
+      path: "OldUpdRootD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1446012
+      path: "UpdRootD:updates/0/update.status",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:pluginreg.dat",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:pluginreg.dat.tmp",
+      stat: 1,
+      write: 64,
+      close: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "APlugns:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "UserPlugins.parent:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "UserPlugins:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/nptest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npsecondtest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npthirdtest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npswftest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1545167
+      path: "/etc/mime.types",
+      condition: LINUX,
+      read: 1,
+      close: 1,
+    },
+    { // We only hit this for new profiles.
+      path: "XREAppDist:distribution.ini",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "*WindowsApps/microsoft.windowscommunicationsapps*",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 3,
+    },
+    { // bug 1545139
+      path: "*Fonts/StaticCache.dat",
+      condition: WIN,
+      ignoreIfUnused: true, // Only on Win7
+      read: 1,
+    },
+    { // bug 1541246
+      path: "UAppData:",
+      ignoreIfUnused: true, // sometimes before opening first browser window,
+                            // sometimes before first paint
+      condition: WIN,
+      stat: 1,
+    },
+    { // Not in packaged builds; useful for artifact builds.
+      path: "GreD:ScalarArtifactDefinitions.json",
+      condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
+      stat: 1,
+    },
+    { // Not in packaged builds; useful for artifact builds.
+      path: "GreD:EventArtifactDefinitions.json",
+      condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
+      stat: 1,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: MAC,
+      write: 3,
+    },
+    { // bug 1543090
+      path: "GreD:omni.ja",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 2,
+    },
+  ],
+
+  // We are at this phase once we are ready to handle user events.
+  // Any IO at this phase or before gets in the way of the user
+  // interacting with the first browser window.
+  "before handling user events": [
+    {
+      path: "GreD:update.test",
+      ignoreIfUnused: true,
+      condition: LINUX,
+      close: 1,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db",
+      condition: WIN,
+      read: 2,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db",
+      condition: WIN,
+      ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:pkcs11.txt",
+      condition: WIN,
+      read: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db",
+      condition: WIN,
+      read: 2,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db",
+      condition: WIN,
+      ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 5,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 5,
+    },
+    {
+      path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
+      condition: !WIN,
+      ignoreIfUnused: true,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1003968
+      path: "XREAppDist:searchplugins",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "XCurProcD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "GreD:omni.ja",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 2,
+    },
+  ],
+
+  // Things that are expected to be completely out of the startup path
+  // and loaded lazily when used for the first time by the user should
+  // be blacklisted here.
+  "before becoming idle": [
+    {
+      path: "XREAppFeat:screenshots@mozilla.org.xpi",
+      ignoreIfUnused: true,
+      close: 1,
+    },
+    {
+      path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
+      ignoreIfUnused: true,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-journal",
+      ignoreIfUnused: true,
+      fsync: 1,
+      stat: 4,
+      write: 2,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-wal",
+      ignoreIfUnused: true,
+      stat: 4,
+      fsync: 3,
+      write: 148,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-shm",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 1,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite",
+      ignoreIfUnused: true,
+      fsync: 2,
+      read: 1,
+      stat: 3,
+      write: 1310,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-journal",
+      ignoreIfUnused: true,
+      fsync: 2,
+      stat: 7,
+      write: 7,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-wal",
+      ignoreIfUnused: true,
+      fsync: 2,
+      stat: 7,
+      write: 15,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-shm",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 2,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite",
+      ignoreIfUnused: true,
+      fsync: 3,
+      read: 4,
+      stat: 4,
+      write: 1300,
+    },
+    {
+      path: "ProfD:key4.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    {
+      path: "ProfD:key4.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    {
+      path: "ProfD:",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 3,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 7,
+    },
+  ],
+};
+
+for (let name of ["d3d11layers", "d3d9video", "glcontext", "d3d11video", "wmfvpxvideo"]) {
+  startupPhases["before first paint"].push({
+    path: `ProfD:${name}.guard`,
+    ignoreIfUnused: true,
+    stat: 1,
+  });
+}
+
+function expandWhitelistPath(path, canonicalize = false) {
+  if (path.includes(":")) {
+    let [prefix, suffix] = path.split(":");
+    let [key, property] = prefix.split(".");
+    let dir = Services.dirsvc.get(key, Ci.nsIFile);
+    if (property) {
+      dir = dir[property];
+    }
+
+    if (canonicalize) {
+      path = dir.QueryInterface(Ci.nsILocalFileWin).canonicalPath;
+    } else {
+      // Resolve symLinks.
+      let dirPath = dir.path;
+      while (dir && !dir.isSymlink()) {
+        dir = dir.parent;
+      }
+      if (dir) {
+        dirPath = dirPath.replace(dir.path, dir.target);
+      }
+
+      path = dirPath;
+    }
+
+    if (suffix) {
+      path += "/" + suffix;
+    }
+  }
+  if (AppConstants.platform == "win") {
+    path = path.replace(/\//g, "\\");
+  }
+  return path;
+}
+
+function getStackFromProfile(profile, stack) {
+  const stackPrefixCol = profile.stackTable.schema.prefix;
+  const stackFrameCol = profile.stackTable.schema.frame;
+  const frameLocationCol = profile.frameTable.schema.location;
+
+  let result = [];
+  while (stack) {
+    let sp = profile.stackTable.data[stack];
+    let frame = profile.frameTable.data[sp[stackFrameCol]];
+    stack = sp[stackPrefixCol];
+    frame = profile.stringTable[frame[frameLocationCol]];
+    if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
+      result.push(frame);
+    }
+  }
+  return result;
+}
+
+function pathMatches(path, filename) {
+  path = path.toLowerCase();
+  return path == filename || // Full match
+    // Wildcard on both sides of the path
+    (path.startsWith("*") && path.endsWith("*") &&
+     filename.includes(path.slice(1, -1))) ||
+    // Wildcard suffix
+    (path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
+    // Wildcard prefix
+    (path.startsWith("*") && filename.endsWith(path.slice(1)));
+}
+
+add_task(async function() {
+  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
+    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
+       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
+       "non-debug build.");
+    return;
+  }
+
+  {
+    let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+    omniJa.append("omni.ja");
+    if (!omniJa.exists()) {
+      ok(false, "This test requires a packaged build, " +
+                "run 'mach package' and then use --appname=dist");
+      return;
+    }
+  }
+
+  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
+  await startupRecorder.done;
+
+  // Add system add-ons to the whitelist dynamically.
+  // They should go in the omni.ja file (bug 1357205).
+  {
+    let addons = await AddonManager.getAddonsByTypes(["extension"]);
+    for (let addon of addons) {
+      if (addon.isSystem) {
+        startupPhases["before opening first browser window"].push({
+          path: `XREAppFeat:${addon.id}.xpi`,
+          stat: 3,
+          close: 2,
+        });
+        startupPhases["before handling user events"].push({
+          path: `XREAppFeat:${addon.id}.xpi`,
+          condition: WIN,
+          stat: 2,
+        });
+      }
+    }
+  }
+
+  // Check for main thread I/O markers in the startup profile.
+  let profile = startupRecorder.data.profile.threads[0];
+
+  let phases = {};
+  {
+    const nameCol = profile.markers.schema.name;
+    const dataCol = profile.markers.schema.data;
+
+    let markersForCurrentPhase = [];
+
+    for (let m of profile.markers.data) {
+      let markerName = profile.stringTable[m[nameCol]];
+      if (markerName.startsWith("startupRecorder:")) {
+        phases[markerName.split("startupRecorder:")[1]] = markersForCurrentPhase;
+        markersForCurrentPhase = [];
+        continue;
+      }
+
+      if (markerName != "FileIO")
+        continue;
+
+      let markerData = m[dataCol];
+      if (markerData.source == "sqlite-mainthread") {
+        continue;
+      }
+
+      let samples = markerData.stack.samples;
+      let stack = samples.data[0][samples.schema.stack];
+      markersForCurrentPhase.push({operation: markerData.operation,
+                                   filename: markerData.filename,
+                                   source: markerData.source,
+                                   stackId: stack});
+    }
+  }
+
+  for (let phase in startupPhases) {
+    startupPhases[phase] =
+      startupPhases[phase].filter(entry => !("condition" in entry) || entry.condition);
+    startupPhases[phase].forEach(entry => {
+      entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
+    });
+  }
+
+  let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
+  let shouldPass = true;
+  for (let phase in phases) {
+    let whitelist = startupPhases[phase];
+    info(`whitelisted paths ${phase}:\n` +
+         whitelist.map(e => {
+           let operations = Object.keys(e).filter(k => k != "path")
+                                  .map(k => `${k}: ${e[k]}`);
+           return `  ${e.path} - ${operations.join(", ")}`;
+         }).join("\n"));
+
+    let markers = phases[phase];
+    for (let marker of markers) {
+      if (marker.operation == "create/open") {
+        // TODO: handle these I/O markers once they are supported on
+        // non-Windows platforms.
+        continue;
+      }
+
+      // Convert to lower case before comparing because the OS X test slaves
+      // have the 'Firefox' folder in 'Library/Application Support' created
+      // as 'firefox' for some reason.
+      let filename = marker.filename.toLowerCase();
+
+      if (!filename) {
+        // We are still missing the filename on some mainthreadio markers,
+        // these markers are currently useless for the purpose of this test.
+        continue;
+      }
+
+      if (!WIN) {
+        if (filename == "/dev/urandom") {
+          continue;
+        }
+
+        // Ignore I/O due to IPC. This doesn't really touch the disk.
+        if (filename.startsWith(tmpPath + "/org.chromium.")) {
+          continue;
+        }
+      }
+
+      let expected = false;
+      for (let entry of whitelist) {
+        if (pathMatches(entry.path, filename)) {
+          entry[marker.operation] = (entry[marker.operation] || 0) - 1;
+          entry._used = true;
+          expected = true;
+          break;
+        }
+      }
+      if (!expected) {
+        record(false,
+               `unexpected ${marker.operation} on ${marker.filename} ${phase}`,
+               undefined,
+               "  " + getStackFromProfile(profile, marker.stackId).join("\n  "));
+        shouldPass = false;
+      }
+      info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
+      if (kDumpAllStacks) {
+        info(getStackFromProfile(profile, marker.stackId).map(f => "  " + f)
+                                                         .join("\n"));
+      }
+    }
+
+    for (let entry of whitelist) {
+      for (let op in entry) {
+        if (["path", "condition", "canonicalize", "ignoreIfUnused", "_used"].includes(op)) {
+          continue;
+        }
+        let message = `${op} on ${entry.path} `;
+        if (entry[op] == 0) {
+          message += "as many times as expected";
+        } else if (entry[op] > 0) {
+          message += `allowed ${entry[op]} more times`;
+        } else {
+          message += `${entry[op] * -1} more times than expected`;
+        }
+        ok(entry[op] >= 0, `${message} ${phase}`);
+      }
+      if (!("_used" in entry) && !entry.ignoreIfUnused) {
+        ok(false, `unused whitelist entry ${phase}: ${entry.path}`);
+      }
+    }
+  }
+
+  if (shouldPass) {
+    ok(shouldPass, "No unexpected main thread I/O during startup");
+  } else {
+    const filename = "startup-mainthreadio-profile.json";
+    let path = Cc["@mozilla.org/process/environment;1"]
+                 .getService(Ci.nsIEnvironment)
+                 .get("MOZ_UPLOAD_DIR");
+    let encoder = new TextEncoder();
+    let profilePath = OS.Path.join(path, filename);
+    await OS.File.writeAtomic(profilePath,
+                              encoder.encode(JSON.stringify(startupRecorder.data.profile)));
+    ok(false,
+       "Found some unexpected main thread I/O during startup; profile uploaded in " +
+       filename);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/io/browser.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+# Currently disabled on debug due to debug-only failures, see bug 1549723.
+skip-if = debug
+# to avoid overhead when running the browser normally, startupRecorder.js will
+# do almost nothing unless browser.startup.record is true.
+# gfx.canvas.willReadFrequently.enable is just an optimization, but needs to be
+# set during early startup to have an impact as a canvas will be used by
+# startupRecorder.js
+prefs =
+  # Skip migration work in BG__migrateUI for browser_startup.js since it isn't
+  # representative of common startup, and triggers Places I/O.
+  browser.migration.version=9999999
+  browser.startup.record=true
+  gfx.canvas.willReadFrequently.enable=true
+environment =
+  MOZ_PROFILER_STARTUP=1
+  MOZ_PROFILER_STARTUP_FEATURES=js,mainthreadio
+  MOZ_PROFILER_STARTUP_ENTRIES=10000000
+[../browser_startup_mainthreadio.js]
+[../browser_startup_content_mainthreadio.js]
--- a/browser/base/content/test/trackingUI/browser.ini
+++ b/browser/base/content/test/trackingUI/browser.ini
@@ -23,14 +23,15 @@ support-files =
 support-files =
   file_trackingUI_fetch.html
   file_trackingUI_fetch.js
   file_trackingUI_fetch.js^headers^
 [browser_trackingUI_fingerprinters.js]
 [browser_trackingUI_open_preferences.js]
 [browser_trackingUI_pbmode_exceptions.js]
 [browser_trackingUI_report_breakage.js]
+skip-if = os == 'linux' && (debug || asan) # Bug 1546797
 [browser_trackingUI_state.js]
 skip-if = serviceworker_e10s # see https://bugzilla.mozilla.org/show_bug.cgi?id=1511303#c1
 [browser_trackingUI_state_all_disabled.js]
 [browser_trackingUI_state_reset.js]
 [browser_trackingUI_telemetry.js]
 [browser_trackingUI_trackers_subview.js]
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -29,16 +29,17 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/historySwipeAnimation/browser.ini',
     'content/test/keyboard/browser.ini',
     'content/test/menubar/browser.ini',
     'content/test/metaTags/browser.ini',
     'content/test/pageActions/browser.ini',
     'content/test/pageinfo/browser.ini',
     'content/test/performance/browser.ini',
     'content/test/performance/hidpi/browser.ini',
+    'content/test/performance/io/browser.ini',
     'content/test/performance/legacyurlbar/browser.ini',
     'content/test/performance/lowdpi/browser.ini',
     'content/test/permissions/browser.ini',
     'content/test/plugins/browser.ini',
     'content/test/popupNotifications/browser.ini',
     'content/test/popups/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/sanitize/browser.ini',
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -125,16 +125,30 @@
                      buttonhighlight="true"
                      hidden="true">
     <popupnotificationcontent id="update-manual-notification-content" orient="vertical">
       <description id="update-manual-description" data-lazy-l10n-id="appmenu-update-manual-message"></description>
       <label id="update-manual-whats-new" is="text-link"  data-lazy-l10n-id="appmenu-update-whats-new"/>
     </popupnotificationcontent>
   </popupnotification>
 
+  <popupnotification id="appMenu-update-unsupported-notification"
+                     popupid="update-unsupported"
+                     data-lazy-l10n-id="appmenu-update-unsupported"
+                     data-l10n-attrs="buttonlabel, buttonaccesskey, secondarybuttonlabel, secondarybuttonaccesskey"
+                     closebuttonhidden="true"
+                     dropmarkerhidden="true"
+                     checkboxhidden="true"
+                     buttonhighlight="true"
+                     hidden="true">
+    <popupnotificationcontent id="update-unsupported-notification-content" orient="vertical">
+      <description id="update-unsupported-description" data-lazy-l10n-id="appmenu-update-unsupported-message"></description>
+    </popupnotificationcontent>
+  </popupnotification>
+
   <popupnotification id="appMenu-update-restart-notification"
                      popupid="update-restart"
                      data-lazy-l10n-id="appmenu-update-restart"
                      data-l10n-attrs="buttonlabel, buttonaccesskey, secondarybuttonlabel, secondarybuttonaccesskey"
                      closebuttonhidden="true"
                      dropmarkerhidden="true"
                      checkboxhidden="true"
                      buttonhighlight="true"
@@ -212,16 +226,17 @@
                   viewCacheId="appMenu-viewCache">
     <panelview id="appMenu-mainView" class="PanelUI-subView"
                descriptionheightworkaround="true">
       <vbox class="panel-subview-body">
         <vbox id="appMenu-addon-banners"/>
         <toolbarbutton class="panel-banner-item"
                        label-update-available="&updateAvailable.panelUI.label;"
                        label-update-manual="&updateManual.panelUI.label;"
+                       label-update-unsupported="&updateUnsupported.panelUI.label;"
                        label-update-restart="&updateRestart.panelUI.label2;"
                        oncommand="PanelUI._onBannerItemSelected(event)"
                        wrap="true"
                        hidden="true"/>
         <toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons sync-ui-item">
           <hbox id="appMenu-fxa-status"
                 flex="1"
                 defaultlabel="&fxaSignIn.label;"
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -461,16 +461,22 @@ add_task(async function testPolicyCatego
   let standardRadioOption = doc.getElementById("standardRadio");
   is(strictRadioOption.disabled, true, "the strict option is disabled");
   is(standardRadioOption.disabled, true, "the standard option is disabled");
 
   gBrowser.removeCurrentTab();
 
   // Cleanup after this particular test.
   if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
+    await EnterprisePolicyTesting.setupPolicyEngineWithJson({
+      policies: {"Cookies": {
+          "Locked": false,
+        },
+      },
+    });
     await EnterprisePolicyTesting.setupPolicyEngineWithJson("");
   }
   is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive at the end of the test");
 
   EnterprisePolicyTesting.resetRunOnceState();
   PoliciesPrefTracker.stop();
 });
 
--- a/browser/components/resistfingerprinting/test/browser/browser_dynamical_window_rounding.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_dynamical_window_rounding.js
@@ -6,19 +6,17 @@
  *   rounded if the window is resized when letterboxing is enabled.
  *
  * A helpful note: if this test starts randomly failing; it may be because the
  * zoom level was not reset by an earlier-run test. See Bug 1407366 for an
  * example.
  */
 
 const TEST_PATH = "http://example.net/browser/browser/components/resistfingerprinting/test/browser/";
-
-const DEFAULT_ROUNDED_WIDTH_STEP  = 200;
-const DEFAULT_ROUNDED_HEIGHT_STEP = 100;
+const { RFPHelper } = ChromeUtils.import("resource://gre/modules/RFPHelper.jsm");
 
 // A set of test cases which defines the width and the height of the outer window.
 const TEST_CASES = [
   {width: 1250, height: 1000},
   {width: 1500, height: 1050},
   {width: 1120, height: 760},
   {width: 800,  height: 600},
   {width: 640,  height: 400},
@@ -45,20 +43,19 @@ function handleOSFuzziness(aContent, aTa
   if (getPlatform() == "win") {
     return Math.abs(aContent - aTarget) <= 1;
   }
   return aContent == aTarget;
 }
 
 function checkForDefaultSetting(
   aContentWidth, aContentHeight, aRealWidth, aRealHeight) {
-  // The default behavior for rounding is to round window with 200x100 stepping.
-  // So, we can get the rounded size by subtracting the remainder.
-  let targetWidth = aRealWidth - (aRealWidth % DEFAULT_ROUNDED_WIDTH_STEP);
-  let targetHeight = aRealHeight - (aRealHeight % DEFAULT_ROUNDED_HEIGHT_STEP);
+  // We can get the rounded size by subtracting twice the margin.
+  let targetWidth = aRealWidth - (2 * RFPHelper.steppedRange(aRealWidth));
+  let targetHeight = aRealHeight - (2 * RFPHelper.steppedRange(aRealHeight));
 
   // This platform-specific code is explained in the large comment below.
   if (getPlatform() != "linux") {
     ok(handleOSFuzziness(aContentWidth, targetWidth),
       `Default Dimensions: The content window width is correctly rounded into. ${aRealWidth}px -> ${aContentWidth}px should equal ${targetWidth}px`);
 
     ok(handleOSFuzziness(aContentHeight, targetHeight),
       `Default Dimensions: The content window height is correctly rounded into. ${aRealHeight}px -> ${aContentHeight}px should equal ${targetHeight}px`);
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -58,16 +58,17 @@ startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   record(name) {
     if (!Services.prefs.getBoolPref("browser.startup.record", false))
       return;
 
+    Services.profiler.AddMarker("startupRecorder:" + name);
     this.data.code[name] = {
       components: Cu.loadedComponents,
       modules: Cu.loadedModules,
       services: Object.keys(Cc).filter(c => {
         try {
           return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
@@ -158,18 +159,34 @@ startupRecorder.prototype = {
         win.removeEventListener("MozAfterPaint", afterPaintListener);
         win = null;
         this.data.frames = paints;
         this.data.prefStats = {};
         if (AppConstants.DEBUG) {
           Services.prefs.readStats((key, value) => this.data.prefStats[key] = value);
         }
         paints = null;
-        this._resolve();
-        this._resolve = null;
+
+        let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+        if (!env.exists("MOZ_PROFILER_STARTUP")) {
+          this._resolve();
+          this._resolve = null;
+          return;
+        }
+
+        Services.profiler.getProfileDataAsync().then(profileData => {
+          this.data.profile = profileData;
+          // There's no equivalent StartProfiler call in this file because the
+          // profiler is started using the MOZ_PROFILER_STARTUP environment
+          // variable in browser/base/content/test/performance/browser.ini
+          Services.profiler.StopProfiler();
+
+          this._resolve();
+          this._resolve = null;
+        });
       });
     } else {
       const topicsToNames = {
         "profile-do-change": "before profile selection",
         "toplevel-window-ready": "before opening first browser window",
       };
       topicsToNames[firstPaintNotification] = "before first paint";
       this.record(topicsToNames[topic]);
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -474,70 +474,79 @@ class UrlbarView {
     let favicon = item._elements.get("favicon");
     if (result.type == UrlbarUtils.RESULT_TYPE.SEARCH ||
         result.type == UrlbarUtils.RESULT_TYPE.KEYWORD) {
       favicon.src = result.payload.icon || UrlbarUtils.ICON.SEARCH_GLASS;
     } else {
       favicon.src = result.payload.icon || UrlbarUtils.ICON.DEFAULT;
     }
 
+    let title = item._elements.get("title");
     this._addTextContentWithHighlights(
-      item._elements.get("title"), result.title, result.titleHighlights);
+      title, result.title, result.titleHighlights);
 
     let tagsContainer = item._elements.get("tagsContainer");
     tagsContainer.textContent = "";
     if (result.payload.tags && result.payload.tags.length > 0) {
       tagsContainer.append(...result.payload.tags.map((tag, i) => {
         const element = this._createElement("span");
         element.className = "urlbarView-tag";
         this._addTextContentWithHighlights(
           element, tag, result.payloadHighlights.tags[i]);
         return element;
       }));
     }
 
     let action = "";
+    let isVisitAction = false;
     let setURL = false;
     switch (result.type) {
       case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
         action = bundle.GetStringFromName("switchToTab2");
         setURL = true;
         break;
       case UrlbarUtils.RESULT_TYPE.REMOTE_TAB:
         action = result.payload.device;
         setURL = true;
         break;
       case UrlbarUtils.RESULT_TYPE.SEARCH:
         action = bundle.formatStringFromName("searchWithEngine",
                                              [result.payload.engine], 1);
         break;
       case UrlbarUtils.RESULT_TYPE.KEYWORD:
-        if (result.payload.input.trim() == result.payload.keyword) {
-          action = bundle.GetStringFromName("visit");
-        }
+        isVisitAction = result.payload.input.trim() == result.payload.keyword;
         break;
       case UrlbarUtils.RESULT_TYPE.OMNIBOX:
         action = result.payload.content;
         break;
       default:
         if (result.heuristic) {
-          action = bundle.GetStringFromName("visit");
+          isVisitAction = true;
         } else {
           setURL = true;
         }
         break;
     }
+
     let url = item._elements.get("url");
     if (setURL) {
       this._addTextContentWithHighlights(url, result.payload.displayUrl,
                                          result.payloadHighlights.displayUrl || []);
     } else {
       url.textContent = "";
     }
+
+    if (isVisitAction) {
+      action = bundle.GetStringFromName("visit");
+      title.setAttribute("isurl", "true");
+    } else {
+      title.removeAttribute("isurl");
+    }
     item._elements.get("action").textContent = action;
+
     item._elements.get("titleSeparator").hidden = !action && !setURL;
   }
 
   _removeStaleRows() {
     let row = this._rows.lastElementChild;
     while (row) {
       let next = row.previousElementSibling;
       if (row.hasAttribute("stale")) {
--- a/browser/locales/en-US/browser/appMenuNotifications.ftl
+++ b/browser/locales/en-US/browser/appMenuNotifications.ftl
@@ -13,16 +13,23 @@ appmenu-update-manual =
     .label = { -brand-shorter-name } can’t update to the latest version.
     .buttonlabel = Download { -brand-shorter-name }
     .buttonaccesskey = D
     .secondarybuttonlabel = Not Now
     .secondarybuttonaccesskey = N
 appmenu-update-manual-message = Download a fresh copy of { -brand-shorter-name } and we’ll help you to install it.
 appmenu-update-whats-new =
     .value = See what’s new.
+appmenu-update-unsupported =
+    .label = { -brand-shorter-name } is unable to update to the latest version.
+    .buttonlabel = Learn more
+    .buttonaccesskey = L
+    .secondarybuttonlabel = Close
+    .secondarybuttonaccesskey = C
+appmenu-update-unsupported-message = The latest version of { -brand-shorter-name } is not supported on your system.
 appmenu-update-restart =
     .label = Restart to update { -brand-shorter-name }.
     .buttonlabel = Restart and Restore
     .buttonaccesskey = R
     .secondarybuttonlabel = Not Now
     .secondarybuttonaccesskey = N
 appmenu-update-restart-message = After a quick restart, { -brand-shorter-name } will restore all your open tabs and windows that are not in Private Browsing mode.
 appmenu-addon-private-browsing-installed =
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -919,16 +919,17 @@ you can use these alternative items. Oth
 <!ENTITY panicButton.thankyou.msg2                "Safe browsing!">
 <!ENTITY panicButton.thankyou.buttonlabel         "Thanks!">
 
 <!ENTITY emeLearnMoreContextMenu.label            "Learn more about DRM…">
 <!ENTITY emeLearnMoreContextMenu.accesskey        "D">
 
 <!ENTITY updateAvailable.panelUI.label "Download &brandShorterName; update">
 <!ENTITY updateManual.panelUI.label "Download a fresh copy of &brandShorterName;">
+<!ENTITY updateUnsupported.panelUI.label "You cannot perform further updates">
 <!ENTITY updateRestart.panelUI.label2 "Restart to update &brandShorterName;">
 
 <!ENTITY newTabControlled.header.message "Your New Tab has changed.">
 <!ENTITY newTabControlled.keepButton.label "Keep Changes">
 <!ENTITY newTabControlled.keepButton.accesskey "K">
 <!ENTITY newTabControlled.disableButton.label "Disable Extension">
 <!ENTITY newTabControlled.disableButton.accesskey "D">
 
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -63,29 +63,46 @@
 
 #PanelUI-menu-button[badge-status="extension-new-tab"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #PanelUI-menu-button[badge-status="download-success"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: none;
 }
 
 #PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
-#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
-  background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
+#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
+#PanelUI-menu-button[badge-status="update-unsupported"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   border-radius: 50%;
   box-shadow: none;
   /* "!important" is necessary to override the rule in toolbarbutton.css */
   margin: -7px 0 0 !important;
   margin-inline-end: -4px !important;
   min-width: 12px;
   min-height: 12px;
 }
 
-.panel-banner-item[notificationid^=update]::after {
+#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
+#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
+#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
+}
+
+#PanelUI-menu-button[badge-status="update-unsupported"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+  background: #FFE900 url(chrome://browser/skin/update-badge.svg) no-repeat center;
+}
+
+.panel-banner-item[notificationid="update-available"]::after,
+.panel-banner-item[notificationid="update-manual"]::after,
+.panel-banner-item[notificationid="update-restart"]::after {
+  background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
+  border-radius: 50%;
+}
+
+.panel-banner-item[notificationid="update-unsupported"]::after {
+  background: #FFE900 url(chrome://browser/skin/update-badge.svg) no-repeat center;
   border-radius: 50%;
 }
 
 .panel-banner-item[notificationid^=update] {
   list-style-image: url(chrome://branding/content/icon16.png);
 }
 
 #PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
@@ -935,27 +952,51 @@ panelmultiview[mainViewId="PanelUI-fxa"]
 }
 
 .panel-banner-item:not([disabled]):hover:active {
   background-color: hsl(96,65%,75%);
 }
 
 :root[lwt-popup-brighttext] .panel-banner-item:not([disabled]) {
   background-color: rgba(48,230,11,.1);
-  color: #F9F9FA !important;
+  color: @appmenuWarningColorBrightText@ !important;
 }
 
 :root[lwt-popup-brighttext] .panel-banner-item:not([disabled]):hover {
   background-color: rgba(48,230,11,.15);
 }
 
 :root[lwt-popup-brighttext] .panel-banner-item:not([disabled]):active {
   background-color: rgba(48,230,11,.2);
 }
 
+.panel-banner-item[notificationid="update-unsupported"] {
+  background-color: @appmenuWarningBackgroundColor@;
+}
+
+.panel-banner-item[notificationid="update-unsupported"]:not([disabled]):hover {
+  background-color: @appmenuWarningBackgroundColorHover@;
+}
+
+.panel-banner-item[notificationid="update-unsupported"]:not([disabled]):hover:active {
+  background-color: @appmenuWarningBackgroundColorActive@;
+}
+
+:root[lwt-popup-brighttext] .panel-banner-item[notificationid="update-unsupported"]:not([disabled]) {
+  background-color: @appmenuWarningBackgroundColorBrightText@;
+}
+
+:root[lwt-popup-brighttext] .panel-banner-item[notificationid="update-unsupported"]:not([disabled]):hover {
+  background-color: @appmenuWarningBackgroundColorHoverBrightText@;
+}
+
+:root[lwt-popup-brighttext] .panel-banner-item[notificationid="update-unsupported"]:not([disabled]):active {
+  background-color: @appmenuWarningBackgroundColorActiveBrightText@;
+}
+
 #customization-palette .toolbarbutton-multiline-text,
 #customization-palette .toolbarbutton-text {
   display: none;
 }
 
 .subview-subheader,
 panelview .toolbarbutton-1,
 .subviewbutton,
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -376,8 +376,13 @@ html|*#webRTC-previewVideo {
 
 /* UPDATE */
 .popup-notification-icon[popupid="update-available"],
 .popup-notification-icon[popupid="update-manual"],
 .popup-notification-icon[popupid="update-restart"] {
   background: #74BF43 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
   border-radius: 50%;
 }
+
+.popup-notification-icon[popupid="update-unsupported"] {
+  background: #FFE900 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
+  border-radius: 50%;
+}
--- a/browser/themes/shared/places/editBookmarkPanel.inc.css
+++ b/browser/themes/shared/places/editBookmarkPanel.inc.css
@@ -53,41 +53,40 @@
 #editBookmarkPanelRecommendation[disabled] {
   transform: translateY(-100%);
 }
 
 #editBookmarkPanelRecommendation > div {
   border-radius: 2px;
   display: flex;
   flex-direction: column;
-  text-align: left;
   height: 150px;
   cursor: pointer;
   position: relative;
   padding: 0 16px;
 }
 
 #editBookmarkPanelRecommendation > div::-moz-focus-inner {
   border: none;
 }
 
 #editBookmarkPanelRecommendation > div * {
   max-width: 220px;
 }
 
 #editBookmarkPanelRecommendationTitle {
-  font-size: 1.5rem;
+  font-size: 16px;
   font-weight: 400;
   line-height: 1.25;
   margin-bottom: 6px;
   padding-top: 2px;
 }
 
 #editBookmarkPanelRecommendationContent {
-  font-size: 1rem;
+  font-size: 11px;
   line-height: 1.5;
   margin: 0;
 }
 
 #editBookmarkPanelRecommendationCta {
   -moz-appearance: none;
   background: transparent;
   border: none;
@@ -97,23 +96,25 @@
   font-weight: 700;
   margin: auto 0;
   padding: 0;
   text-align: start;
 }
 
 #editBookmarkPanelRecommendation #cfrClose {
   position: absolute;
-  right: 18px;
-  top: 18px;
+  padding: 3px 2px;
+  inset-inline-end: 16px;
+  top: 15px;
   width: 12px;
   height: 12px;
   background-image: url(chrome://browser/skin/stop.svg);
   background-size: 12px;
   background-repeat: no-repeat;
+  background-position: center;
   -moz-context-properties: fill, fill-opacity;
   fill: currentColor;
   fill-opacity: 0.6;
 }
 
 #editBookmarkPanelRecommendation #cfrClose:hover {
   fill-opacity: 1;
 }
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -67,20 +67,25 @@
   justify-content: start;
 }
 
 .urlbarView-title[overflow],
 .urlbarView-url[overflow] {
   mask-image: linear-gradient(to left, transparent, black 2em);
 }
 
-.urlbarView-title[overflow]:-moz-locale-dir(rtl) {
+.urlbarView-title[overflow]:not([isurl]):-moz-locale-dir(rtl) {
   mask-image: linear-gradient(to right, transparent, black 2em);
 }
 
+.urlbarView-title[isurl]:-moz-locale-dir(rtl),
+.urlbarView-url:-moz-locale-dir(rtl) {
+  direction: ltr !important;
+}
+
 .urlbarView-row:hover {
   background: var(--arrowpanel-dimmed);
 }
 
 .urlbarView-row[selected] {
   background: var(--autocomplete-popup-highlight-background);
   color: var(--autocomplete-popup-highlight-color);
   fill-opacity: 1;
@@ -151,20 +156,16 @@
   color: var(--urlbar-popup-action-color);
 }
 
 .urlbarView-url {
   overflow: hidden;
   color: var(--urlbar-popup-url-color);
 }
 
-.urlbarView-url:-moz-locale-dir(rtl) {
-  direction: ltr !important;
-}
-
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-title-separator::before,
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary {
   color: inherit;
 }
 
 .urlbarView-row[type=remotetab][selected] > .urlbarView-row-inner > .urlbarView-action,
 .urlbarView-row[type=remotetab]:hover > .urlbarView-row-inner > .urlbarView-action,
 .urlbarView-row[type=remotetab]:not([selected]):not(:hover) > .urlbarView-row-inner > .urlbarView-url,
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -54,17 +54,28 @@ const {
   WATCH_RUNTIME_FAILURE,
   WATCH_RUNTIME_START,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
 const CONNECTION_TIMING_OUT_DELAY = 3000;
 const CONNECTION_CANCEL_DELAY = 13000;
 
-async function getRuntimeIcon(channel) {
+async function getRuntimeIcon(runtime, channel) {
+  if (runtime.isFenix) {
+    switch (channel) {
+      case "release":
+      case "beta":
+        return "chrome://devtools/skin/images/aboutdebugging-fenix.svg";
+      case "aurora":
+      default:
+        return "chrome://devtools/skin/images/aboutdebugging-fenix-nightly.svg";
+    }
+  }
+
   return (channel === "release" || channel === "beta" || channel === "aurora")
     ? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
     : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
 }
 
 function onRemoteDebuggerClientClosed() {
   window.AboutDebugging.onNetworkLocationsUpdated();
   window.AboutDebugging.onUSBRuntimesUpdated();
@@ -102,17 +113,17 @@ function connectRuntime(id) {
     }, connectionCancelDelay);
 
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const clientWrapper = await createClientForRuntime(runtime);
 
       const deviceDescription = await clientWrapper.getDeviceDescription();
       const compatibilityReport = await clientWrapper.checkVersionCompatibility();
-      const icon = await getRuntimeIcon(deviceDescription.channel);
+      const icon = await getRuntimeIcon(runtime, deviceDescription.channel);
 
       const {
         CHROME_DEBUG_ENABLED,
         CONNECTION_PROMPT,
         PERMANENT_PRIVATE_BROWSING,
         REMOTE_DEBUG_ENABLED,
         SERVICE_WORKERS_ENABLED,
       } = RUNTIME_PREFERENCE;
@@ -124,28 +135,41 @@ function connectRuntime(id) {
             await clientWrapper.getPreference(REMOTE_DEBUG_ENABLED, true)
           : true;
       const privateBrowsing =
         await clientWrapper.getPreference(PERMANENT_PRIVATE_BROWSING, false);
       const serviceWorkersEnabled =
         await clientWrapper.getPreference(SERVICE_WORKERS_ENABLED, true);
       const serviceWorkersAvailable = serviceWorkersEnabled && !privateBrowsing;
 
+      // Fenix specific workarounds are needed until we can get proper server side APIs
+      // to detect Fenix and get the proper application names and versions.
+      // See https://github.com/mozilla-mobile/fenix/issues/2016.
+
+      // For Fenix runtimes, the ADB runtime name is more accurate than the one returned
+      // by the Device actor.
+      const runtimeName = runtime.isFenix ? runtime.name : deviceDescription.name;
+
+      // For Fenix runtimes, the version we should display is the application version
+      // retrieved from ADB, and not the Gecko version returned by the Device actor.
+      const version = runtime.isFenix ?
+        runtime.extra.adbPackageVersion : deviceDescription.version;
+
       const runtimeDetails = {
         clientWrapper,
         compatibilityReport,
         connectionPromptEnabled,
         extensionDebugEnabled,
         info: {
           deviceName: deviceDescription.deviceName,
           icon,
-          name: deviceDescription.name,
+          name: runtimeName,
           os: deviceDescription.os,
           type: runtime.type,
-          version: deviceDescription.version,
+          version,
         },
         isMultiE10s: deviceDescription.isMultiE10s,
         serviceWorkersAvailable,
       };
 
       const deviceFront = await clientWrapper.getFront("device");
       if (deviceFront) {
         deviceFront.on("multi-e10s-updated", onMultiE10sUpdated);
@@ -340,16 +364,17 @@ function updateNetworkRuntimes(locations
       id: location,
       extra: {
         connectionParameters: { host, port: parseInt(port, 10) },
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
       isConnectionTimeout: false,
+      isFenix: false,
       isUnavailable: false,
       isUnplugged: false,
       isUnknown: false,
       name: location,
       type: RUNTIMES.NETWORK,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.NETWORK);
@@ -361,21 +386,23 @@ function updateUSBRuntimes(adbRuntimes) 
     const socketPath = adbRuntime.socketPath;
     const deviceId = adbRuntime.deviceId;
     const connectionParameters = socketPath ? { deviceId, socketPath } : null;
     return {
       id: adbRuntime.id,
       extra: {
         connectionParameters,
         deviceName: adbRuntime.deviceName,
+        adbPackageVersion: adbRuntime.versionName,
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
       isConnectionTimeout: false,
+      isFenix: adbRuntime.isFenix,
       isUnavailable: adbRuntime.isUnavailable,
       isUnplugged: adbRuntime.isUnplugged,
       name: adbRuntime.shortName,
       type: RUNTIMES.USB,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.USB);
 }
--- a/devtools/client/aboutdebugging-new/src/components/RuntimeInfo.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimeInfo.js
@@ -32,17 +32,17 @@ class RuntimeInfo extends PureComponent 
     const { icon, deviceName, name, version, runtimeId, dispatch } = this.props;
 
     return dom.h1(
       {
         className: "main-heading runtime-info",
       },
       dom.img(
         {
-          className: "main-heading__icon runtime-info__icon",
+          className: "main-heading__icon runtime-info__icon qa-runtime-icon",
           src: icon,
         }
       ),
       Localized(
         {
           id: "about-debugging-runtime-name",
           $name: name,
           $version: version,
--- a/devtools/client/aboutdebugging-new/src/modules/usb-runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/modules/usb-runtimes.js
@@ -11,50 +11,56 @@ loader.lazyRequireGetter(this, "adb", "d
  */
 class UsbRuntime {
   constructor(adbRuntime) {
     this.id = adbRuntime.id;
     this.deviceId = adbRuntime.deviceId;
     this.deviceName = adbRuntime.deviceName;
     this.shortName = adbRuntime.shortName;
     this.socketPath = adbRuntime.socketPath;
+    this.isFenix = adbRuntime.isFenix;
     this.isUnavailable = false;
     this.isUnplugged = false;
+    this.versionName = adbRuntime.versionName;
   }
 }
 
 /**
  * Used when a device was detected, meaning USB debugging is enabled on the device, but no
  * runtime/browser is available for connection.
  */
 class UnavailableUsbRuntime {
   constructor(adbDevice) {
     this.id = adbDevice.id + "|unavailable";
     this.deviceId = adbDevice.id;
     this.deviceName = adbDevice.name;
     this.shortName = "Unavailable runtime";
     this.socketPath = null;
+    this.isFenix = false;
     this.isUnavailable = true;
     this.isUnplugged = false;
+    this.versionName = null;
   }
 }
 
 /**
  * Used to represent USB devices that were previously connected but are now missing
  * (presumably after being unplugged/disconnected from the computer).
  */
 class UnpluggedUsbRuntime {
   constructor(deviceId, deviceName) {
     this.id = deviceId + "|unplugged";
     this.deviceId = deviceId;
     this.deviceName = deviceName;
     this.shortName = "Unplugged runtime";
     this.socketPath = null;
+    this.isFenix = false;
     this.isUnavailable = true;
     this.isUnplugged = true;
+    this.versionName = null;
   }
 }
 
 /**
  * Map used to keep track of discovered usb devices. Will be used to create the unplugged
  * usb runtimes.
  */
 const devices = new Map();
--- a/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
@@ -100,17 +100,22 @@ function runtimesReducer(state = Runtime
         isConnectionNotResponding: false,
         isConnectionTimeout: true,
       };
       return _updateRuntimeById(id, updatedState, state);
     }
 
     case CONNECT_RUNTIME_SUCCESS: {
       const { id, runtimeDetails, type } = action.runtime;
-      remoteClientManager.setClient(id, type, runtimeDetails.clientWrapper.client);
+
+      // Update the remoteClientManager with the connected runtime.
+      const client = runtimeDetails.clientWrapper.client;
+      const runtimeInfo = runtimeDetails.info;
+      remoteClientManager.setClient(id, type, client, runtimeInfo);
+
       const updatedState = {
         isConnecting: false,
         isConnectionFailed: false,
         isConnectionNotResponding: false,
         isConnectionTimeout: false,
         runtimeDetails,
       };
       return _updateRuntimeById(id, updatedState, state);
--- a/devtools/client/aboutdebugging-new/src/types/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/types/runtime.js
@@ -100,16 +100,20 @@ const runtimeExtra = {
   connectionParameters: PropTypes.oneOfType([
     PropTypes.shape(networkRuntimeConnectionParameter),
     PropTypes.shape(usbRuntimeConnectionParameter),
   ]),
 
   // device name
   // unavailable on this-firefox and network-location runtimes
   deviceName: PropTypes.string,
+
+  // version of the application coming from ADB, only available via USB. Useful for Fenix
+  // runtimes, because the version can't be retrieved from Service.appInfo.
+  adbPackageVersion: PropTypes.string,
 };
 
 const runtime = {
   // unique id for the runtime
   id: PropTypes.string.isRequired,
 
   // object containing non standard properties that depend on the runtime type,
   // unavailable on this-firefox runtime
@@ -124,16 +128,21 @@ const runtime = {
 
   // will be true if connecting to runtime is taking time, will be false after connecting
   // or failing.
   isConnectionNotResponding: PropTypes.bool.isRequired,
 
   // this flag will be true when the connection was timeout.
   isConnectionTimeout: PropTypes.bool.isRequired,
 
+  // this flag will be true when the detected runtime is Fenix (Firefox Preview).
+  // Fenix need specific logic to get their display name, version and logos.
+  // Discussion ongoing in https://github.com/mozilla-mobile/fenix/issues/2016
+  isFenix: PropTypes.bool.isRequired,
+
   // unavailable runtimes are placeholders for devices where the runtime has not been
   // started yet. For instance an ADB device connected without a compatible runtime
   // running.
   isUnavailable: PropTypes.bool.isRequired,
 
   // unplugged runtimes are placeholders for devices that are no longer available. For
   // instance a USB device that was unplugged from the computer.
   isUnplugged: PropTypes.bool.isRequired,
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -65,16 +65,17 @@ skip-if = os == 'linux' && e10s && (asan
 [browser_aboutdebugging_devtoolstoolbox_reload.js]
 skip-if = verify || ccov # test loads the toolbox 2 times for each panel, might timeout or OOM
 [browser_aboutdebugging_devtoolstoolbox_shortcuts.js]
 skip-if = (os == "win" && ccov) # Bug 1521349
 [browser_aboutdebugging_devtoolstoolbox_splitconsole_key.js]
 [browser_aboutdebugging_devtoolstoolbox_target_destroyed.js]
 skip-if = debug || asan # This test leaks. See bug 1529005
 [browser_aboutdebugging_devtoolstoolbox_tooltip_markupview.js]
+[browser_aboutdebugging_fenix_runtime_display.js]
 [browser_aboutdebugging_message_close.js]
 [browser_aboutdebugging_navigate.js]
 [browser_aboutdebugging_persist_connection.js]
 [browser_aboutdebugging_process_category.js]
 [browser_aboutdebugging_process_main.js]
 [browser_aboutdebugging_profiler_dialog.js]
 [browser_aboutdebugging_real_usb_runtime_page_runtime_info.js]
 [browser_aboutdebugging_real_usb_sidebar.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_fenix_runtime_display.js
@@ -0,0 +1,144 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const RUNTIME_ID = "1337id";
+const DEVICE_NAME = "Fancy Phone";
+const SERVER_RUNTIME_NAME = "Mozilla Firefox";
+const ADB_RUNTIME_NAME = "Firefox Preview";
+const SERVER_VERSION = "v7.3.31";
+const ADB_VERSION = "v1.3.37";
+
+const FENIX_RELEASE_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-fenix.svg";
+const FENIX_NIGHTLY_ICON_SRC =
+  "chrome://devtools/skin/images/aboutdebugging-fenix-nightly.svg";
+
+/**
+ * Check that Fenix runtime information is correctly displayed in about:debugging.
+ */
+add_task(async function() {
+  const mocks = new Mocks();
+  mocks.createUSBRuntime(RUNTIME_ID, {
+    deviceName: DEVICE_NAME,
+    isFenix: true,
+    name: SERVER_RUNTIME_NAME,
+    shortName: ADB_RUNTIME_NAME,
+    versionName: ADB_VERSION,
+    version: SERVER_VERSION,
+  });
+
+  // open a remote runtime page
+  const { document, tab, window } = await openAboutDebugging();
+  await selectThisFirefoxPage(document, window.AboutDebugging.store);
+
+  mocks.emitUSBUpdate();
+  await connectToRuntime(DEVICE_NAME, document);
+  await selectRuntime(DEVICE_NAME, ADB_RUNTIME_NAME, document);
+
+  info("Check that the runtime information is displayed as expected");
+  const runtimeInfo = document.querySelector(".qa-runtime-name");
+  ok(runtimeInfo, "Runtime info for the Fenix runtime is displayed");
+  const runtimeInfoText = runtimeInfo.textContent;
+
+  ok(runtimeInfoText.includes(ADB_RUNTIME_NAME), "Name is the ADB name");
+  ok(!runtimeInfoText.includes(SERVER_RUNTIME_NAME),
+    "Name does not include the server name");
+
+  ok(runtimeInfoText.includes(ADB_VERSION), "Version contains the ADB version");
+  ok(!runtimeInfoText.includes(SERVER_VERSION),
+    "Version does not contain the server version");
+
+  const runtimeIcon = document.querySelector(".qa-runtime-icon");
+  is(runtimeIcon.src, FENIX_RELEASE_ICON_SRC, "The runtime icon is the Fenix icon");
+
+  info("Remove USB runtime");
+  mocks.removeUSBRuntime(RUNTIME_ID);
+  mocks.emitUSBUpdate();
+  await waitUntilUsbDeviceIsUnplugged(DEVICE_NAME, document);
+
+  await removeTab(tab);
+});
+
+/**
+ * Check that Fenix runtime information is correctly displayed in about:devtools-toolbox.
+ */
+add_task(async function() {
+  // We use a real local client combined with a mocked USB runtime to be able to open
+  // about:devtools-toolbox on a real target.
+  const clientWrapper = await createLocalClientWrapper();
+
+  // Mock getDeviceDescription() to force the local client to return "nightly" as the
+  // channel. Otherwise the value of the icon source would depend on the current channel.
+  const deviceDescription = await clientWrapper.getDeviceDescription();
+  clientWrapper.getDeviceDescription = function() {
+    return Object.assign({}, deviceDescription, {
+      channel: "nightly",
+    });
+  };
+
+  const mocks = new Mocks();
+  mocks.createUSBRuntime(RUNTIME_ID, {
+    clientWrapper: clientWrapper,
+    deviceName: DEVICE_NAME,
+    isFenix: true,
+    name: SERVER_RUNTIME_NAME,
+    shortName: ADB_RUNTIME_NAME,
+    versionName: ADB_VERSION,
+    version: SERVER_VERSION,
+  });
+
+  // open a remote runtime page
+  const { document, tab, window } = await openAboutDebugging();
+  await selectThisFirefoxPage(document, window.AboutDebugging.store);
+
+  mocks.emitUSBUpdate();
+  info("Select the runtime page for the USB runtime");
+  const onRequestSuccess = waitForRequestsSuccess(window.AboutDebugging.store);
+  await connectToRuntime(DEVICE_NAME, document);
+  await selectRuntime(DEVICE_NAME, ADB_RUNTIME_NAME, document);
+
+  info("Wait for requests to finish the USB runtime is backed by a real local client");
+  await onRequestSuccess;
+
+  info("Wait for the about:debugging target to be available");
+  await waitUntil(() => findDebugTargetByText("about:debugging", document));
+  const { devtoolsDocument, devtoolsTab } =
+    await openAboutDevtoolsToolbox(document, tab, window);
+
+  const runtimeInfo = devtoolsDocument.querySelector(".qa-runtime-info");
+  const runtimeInfoText = runtimeInfo.textContent;
+  ok(runtimeInfoText.includes(ADB_RUNTIME_NAME), "Name is the ADB runtime name");
+  ok(runtimeInfoText.includes(ADB_VERSION), "Version is the ADB version");
+
+  const runtimeIcon = devtoolsDocument.querySelector(".qa-runtime-icon");
+  is(runtimeIcon.src, FENIX_NIGHTLY_ICON_SRC, "The runtime icon is the Fenix icon");
+
+  info("Wait for all pending requests to settle on the DebuggerClient");
+  await clientWrapper.client.waitForRequestsToSettle();
+
+  await closeAboutDevtoolsToolbox(document, devtoolsTab, window);
+
+  info("Remove USB runtime");
+  mocks.removeUSBRuntime(RUNTIME_ID);
+  mocks.emitUSBUpdate();
+  await waitUntilUsbDeviceIsUnplugged(DEVICE_NAME, document);
+
+  await removeTab(tab);
+  await clientWrapper.close();
+});
+
+async function createLocalClientWrapper() {
+  info("Create a local DebuggerClient");
+  const { DebuggerServer } = require("devtools/server/main");
+  const { DebuggerClient } = require("devtools/shared/client/debugger-client");
+  const { ClientWrapper } =
+    require("devtools/client/aboutdebugging-new/src/modules/client-wrapper");
+
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
+  const client = new DebuggerClient(DebuggerServer.connectPipe());
+
+  await client.connect();
+  return new ClientWrapper(client);
+}
--- a/devtools/client/aboutdebugging-new/test/browser/helper-mocks.js
+++ b/devtools/client/aboutdebugging-new/test/browser/helper-mocks.js
@@ -119,44 +119,54 @@ class Mocks {
   }
 
   /**
    * Creates a USB runtime for which a client conenction can be established.
    * @param {String} id
    *        The id of the runtime.
    * @param {Object} optional object used to create the fake runtime & device
    *        - channel: {String} Release channel, for instance "release", "nightly"
+   *        - clientWrapper: {ClientWrapper} optional ClientWrapper for this runtime
    *        - deviceId: {String} Device id
    *        - deviceName: {String} Device name
+   *        - isFenix: {Boolean} set by ADB if the package name matches a Fenix package
    *        - name: {String} Application name, for instance "Firefox"
    *        - shortName: {String} Short name for the device
    *        - socketPath: {String} (should only be used for connecting, so not here)
    *        - version: {String} Version, for instance "63.0a"
+   *        - versionName: {String} Version return by ADB "63.0a"
    * @return {Object} Returns the mock client created for this runtime so that methods
    * can be overridden on it.
    */
   createUSBRuntime(id, runtimeInfo = {}) {
     // Add a new runtime to the list of scanned runtimes.
     this._usbRuntimes.push({
-      id: id,
-      socketPath: runtimeInfo.socketPath || "test/path",
       deviceId: runtimeInfo.deviceId || "test device id",
       deviceName: runtimeInfo.deviceName || "test device name",
+      id: id,
+      isFenix: runtimeInfo.isFenix,
       shortName: runtimeInfo.shortName || "testshort",
+      socketPath: runtimeInfo.socketPath || "test/path",
+      versionName: runtimeInfo.versionName || "1.0",
     });
 
     // Add a valid client that can be returned for this particular runtime id.
-    const mockUsbClient = createClientMock();
-    mockUsbClient.getDeviceDescription = () => {
-      return {
-        channel: runtimeInfo.channel || "release",
-        name: runtimeInfo.name || "TestBrand",
-        version: runtimeInfo.version || "1.0",
+    let mockUsbClient = runtimeInfo.clientWrapper;
+    if (!mockUsbClient) {
+      // If no clientWrapper was provided, create a mock client here.
+      mockUsbClient = createClientMock();
+      mockUsbClient.getDeviceDescription = () => {
+        return {
+          channel: runtimeInfo.channel || "release",
+          name: runtimeInfo.name || "TestBrand",
+          version: runtimeInfo.version || "1.0",
+        };
       };
-    };
+    }
+
     this._clients[RUNTIMES.USB][id] = mockUsbClient;
 
     return mockUsbClient;
   }
 
   removeUSBRuntime(id) {
     this._usbRuntimes = this._usbRuntimes.filter(runtime => runtime.id !== id);
     delete this._clients[RUNTIMES.USB][id];
--- a/devtools/client/framework/components/DebugTargetInfo.js
+++ b/devtools/client/framework/components/DebugTargetInfo.js
@@ -13,20 +13,20 @@ const { CONNECTION_TYPES, DEBUG_TARGET_T
  * This is header that should be displayed on top of the toolbox when using
  * about:devtools-toolbox.
  */
 class DebugTargetInfo extends PureComponent {
   static get propTypes() {
     return {
       debugTargetData: PropTypes.shape({
         connectionType: PropTypes.oneOf(Object.values(CONNECTION_TYPES)).isRequired,
-        deviceDescription: PropTypes.shape({
-          brandName: PropTypes.string.isRequired,
-          channel: PropTypes.string.isRequired,
+        runtimeInfo: PropTypes.shape({
           deviceName: PropTypes.string,
+          icon: PropTypes.string.isRequired,
+          name: PropTypes.string.isRequired,
           version: PropTypes.string.isRequired,
         }).isRequired,
         targetType: PropTypes.oneOf(Object.values(DEBUG_TARGET_TYPES)).isRequired,
       }).isRequired,
       L10N: PropTypes.object.isRequired,
       toolbox: PropTypes.object.isRequired,
     };
   }
@@ -53,22 +53,22 @@ class DebugTargetInfo extends PureCompon
         targetTypeStr,
         title
       );
     }
   }
 
   getRuntimeText() {
     const { debugTargetData, L10N } = this.props;
-    const { brandName, version } = debugTargetData.deviceDescription;
+    const { name, version } = debugTargetData.runtimeInfo;
     const { connectionType } = debugTargetData;
 
     return (connectionType === CONNECTION_TYPES.THIS_FIREFOX)
       ? L10N.getFormatStr("toolbox.debugTargetInfo.runtimeLabel.thisFirefox", version)
-      : L10N.getFormatStr("toolbox.debugTargetInfo.runtimeLabel", brandName, version);
+      : L10N.getFormatStr("toolbox.debugTargetInfo.runtimeLabel", name, version);
   }
 
   getAssetsForConnectionType() {
     const { connectionType } = this.props.debugTargetData;
 
     switch (connectionType) {
       case CONNECTION_TYPES.USB:
         return {
@@ -138,28 +138,30 @@ class DebugTargetInfo extends PureCompon
         className: "iconized-label qa-connection-info",
       },
       dom.img({ src: image, alt: `${connectionType} icon`}),
       this.props.L10N.getStr(l10nId),
     );
   }
 
   renderRuntime() {
-    const { channel, deviceName } = this.props.debugTargetData.deviceDescription;
+    if (!this.props.debugTargetData.runtimeInfo) {
+      // Skip the runtime render if no runtimeInfo is available.
+      // Runtime info is retrieved from the remote-client-manager, which might not be
+      // setup if about:devtools-toolbox was not opened from about:debugging.
+      return null;
+    }
 
-    const channelIcon =
-      (channel === "release" || channel === "beta" || channel === "aurora") ?
-      `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg` :
-      "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
+    const { icon, deviceName } = this.props.debugTargetData.runtimeInfo;
 
     return dom.span(
       {
-        className: "iconized-label",
+        className: "iconized-label qa-runtime-info",
       },
-      dom.img({ src: channelIcon, className: "channel-icon" }),
+      dom.img({ src: icon, className: "channel-icon qa-runtime-icon" }),
       dom.b({ className: "devtools-ellipsis-text" }, this.getRuntimeText()),
       dom.span({ className: "devtools-ellipsis-text" }, deviceName),
     );
   }
 
   renderTarget() {
     const title = this.props.toolbox.target.name;
     const url = this.props.toolbox.target.url;
--- a/devtools/client/framework/components/ToolboxToolbar.js
+++ b/devtools/client/framework/components/ToolboxToolbar.js
@@ -93,17 +93,17 @@ class ToolboxToolbar extends Component {
       onTabsOrderUpdated: PropTypes.func.isRequired,
       // Count of visible toolbox buttons which is used by ToolboxTabs component
       // to recognize that the visibility of toolbox buttons were changed.
       // Because in the component we cannot compare the visibility since the
       // button definition instance in toolboxButtons will be unchanged.
       visibleToolboxButtonCount: PropTypes.number,
       // Data to show debug target info, if needed
       debugTargetData: PropTypes.shape({
-        deviceDescription: PropTypes.object.isRequired,
+        runtimeInfo: PropTypes.object.isRequired,
         targetType: PropTypes.string.isRequired,
       }),
     };
   }
 
   constructor(props) {
     super(props);
 
--- a/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js
+++ b/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js
@@ -8,17 +8,17 @@
  * client instance.
  */
 add_task(async function() {
   const debuggerClient = await createLocalClient();
 
   info("Preload a local DebuggerClient as this-firefox in the remoteClientManager");
   const { remoteClientManager } =
     require("devtools/client/shared/remote-debugging/remote-client-manager");
-  remoteClientManager.setClient("this-firefox", "this-firefox", debuggerClient);
+  remoteClientManager.setClient("this-firefox", "this-firefox", debuggerClient, {});
   registerCleanupFunction(() => {
     remoteClientManager.removeAllClients();
   });
 
   info("Create a dummy target tab");
   const targetTab = await addTab("data:text/html,somehtml");
 
   const { tab } = await openAboutToolbox({
--- a/devtools/client/framework/test/jest/components/__snapshots__/debug-target-info.test.js.snap
+++ b/devtools/client/framework/test/jest/components/__snapshots__/debug-target-info.test.js.snap
@@ -9,20 +9,20 @@ exports[`DebugTargetInfo component Conne
   >
     <img
       alt="usb icon"
       src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
     />
     toolbox.debugTargetInfo.connection.usb
   </span>
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
     </b>
     <span
@@ -61,20 +61,20 @@ exports[`DebugTargetInfo component Targe
   >
     <img
       alt="usb icon"
       src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
     />
     toolbox.debugTargetInfo.connection.usb
   </span>
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
     </b>
     <span
@@ -113,20 +113,20 @@ exports[`DebugTargetInfo component Targe
   >
     <img
       alt="usb icon"
       src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
     />
     toolbox.debugTargetInfo.connection.usb
   </span>
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
     </b>
     <span
@@ -165,20 +165,20 @@ exports[`DebugTargetInfo component Targe
   >
     <img
       alt="usb icon"
       src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
     />
     toolbox.debugTargetInfo.connection.usb
   </span>
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
     </b>
     <span
@@ -217,20 +217,20 @@ exports[`DebugTargetInfo component Targe
   >
     <img
       alt="usb icon"
       src="chrome://devtools/skin/images/aboutdebugging-usb-icon.svg"
     />
     toolbox.debugTargetInfo.connection.usb
   </span>
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel-usbRuntimeBrandName-1.0.0
     </b>
     <span
@@ -260,20 +260,20 @@ exports[`DebugTargetInfo component Targe
 </header>
 `;
 
 exports[`DebugTargetInfo component Target title renders the expected snapshot for This Firefox target 1`] = `
 <header
   className="debug-target-info qa-debug-target-info"
 >
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel.thisFirefox-1.0.0
     </b>
     <span
@@ -301,20 +301,20 @@ exports[`DebugTargetInfo component Targe
 </header>
 `;
 
 exports[`DebugTargetInfo component Target title renders the expected snapshot for a Toolbox with an unnamed target 1`] = `
 <header
   className="debug-target-info qa-debug-target-info"
 >
   <span
-    className="iconized-label"
+    className="iconized-label qa-runtime-info"
   >
     <img
-      className="channel-icon"
+      className="channel-icon qa-runtime-icon"
       src="chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"
     />
     <b
       className="devtools-ellipsis-text"
     >
       toolbox.debugTargetInfo.runtimeLabel.thisFirefox-1.0.0
     </b>
     <span
--- a/devtools/client/framework/test/jest/components/debug-target-info.test.js
+++ b/devtools/client/framework/test/jest/components/debug-target-info.test.js
@@ -27,61 +27,63 @@ const findByClassName = (testInstance, c
   });
 };
 
 const TEST_TOOLBOX = {
   target: {
     name: "Test Tab Name",
     url: "http://some.target/url",
   },
+  doc: {},
 };
 
 const TEST_TOOLBOX_NO_NAME = {
   target: {
     url: "http://some.target/without/a/name",
   },
+  doc: {},
 };
 
 const USB_DEVICE_DESCRIPTION = {
-  brandName: "usbRuntimeBrandName",
-  channel: "release",
   deviceName: "usbDeviceName",
+  icon: "chrome://devtools/skin/images/aboutdebugging-firefox-release.svg",
+  name: "usbRuntimeBrandName",
   version: "1.0.0",
 };
 
 const THIS_FIREFOX_DEVICE_DESCRIPTION = {
-  brandName: "thisFirefoxRuntimeBrandName",
-  channel: "release",
+  icon: "chrome://devtools/skin/images/aboutdebugging-firefox-release.svg",
   version: "1.0.0",
+  name: "thisFirefoxRuntimeBrandName",
 };
 
 const USB_TARGET_INFO = {
   debugTargetData: {
     connectionType: CONNECTION_TYPES.USB,
-    deviceDescription: USB_DEVICE_DESCRIPTION,
+    runtimeInfo: USB_DEVICE_DESCRIPTION,
     targetType: DEBUG_TARGET_TYPES.TAB,
   },
   toolbox: TEST_TOOLBOX,
   L10N: stubL10N,
 };
 
 const THIS_FIREFOX_TARGET_INFO = {
   debugTargetData: {
     connectionType: CONNECTION_TYPES.THIS_FIREFOX,
-    deviceDescription: THIS_FIREFOX_DEVICE_DESCRIPTION,
+    runtimeInfo: THIS_FIREFOX_DEVICE_DESCRIPTION,
     targetType: DEBUG_TARGET_TYPES.TAB,
   },
   toolbox: TEST_TOOLBOX,
   L10N: stubL10N,
 };
 
 const THIS_FIREFOX_NO_NAME_TARGET_INFO = {
   debugTargetData: {
     connectionType: CONNECTION_TYPES.THIS_FIREFOX,
-    deviceDescription: THIS_FIREFOX_DEVICE_DESCRIPTION,
+    runtimeInfo: THIS_FIREFOX_DEVICE_DESCRIPTION,
     targetType: DEBUG_TARGET_TYPES.TAB,
   },
   toolbox: TEST_TOOLBOX_NO_NAME,
   L10N: stubL10N,
 };
 
 describe("DebugTargetInfo component", () => {
   describe("Connection info", () => {
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -458,17 +458,17 @@ Toolbox.prototype = {
         // Update the URL so that onceDOMReady watch for the right url.
         this._URL = this.win.location.href;
       }
 
       if (this.hostType === Toolbox.HostType.PAGE) {
         // Displays DebugTargetInfo which shows the basic information of debug target,
         // if `about:devtools-toolbox` URL opens directly.
         // DebugTargetInfo requires this._debugTargetData to be populated
-        this._debugTargetData = await this._getDebugTargetData();
+        this._debugTargetData = this._getDebugTargetData();
       }
 
       const domHelper = new DOMHelpers(this.win);
       const domReady = new Promise(resolve => {
         domHelper.onceDOMReady(() => {
           resolve();
         }, this._URL);
       });
@@ -789,30 +789,29 @@ Toolbox.prototype = {
 
   _removeWindowHostShortcuts: function() {
     if (this._windowHostShortcuts) {
       this._windowHostShortcuts.destroy();
       this._windowHostShortcuts = null;
     }
   },
 
-  _getDebugTargetData: async function() {
+  _getDebugTargetData: function() {
     const url = new URL(this.win.location);
     const searchParams = new this.win.URLSearchParams(url.search);
 
     const targetType = searchParams.get("type") || DEBUG_TARGET_TYPES.TAB;
 
-    const deviceFront = await this.target.client.mainRoot.getFront("device");
-    const deviceDescription = await deviceFront.getDescription();
     const remoteId = searchParams.get("remoteId");
+    const runtimeInfo = remoteClientManager.getRuntimeInfoByRemoteId(remoteId);
     const connectionType = remoteClientManager.getConnectionTypeByRemoteId(remoteId);
 
     return {
       connectionType,
-      deviceDescription,
+      runtimeInfo,
       targetType,
     };
   },
 
   _onTargetClosed: async function() {
     const win = this.win; // .destroy() will set this.win to null
 
     // clean up the toolbox
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -70,16 +70,18 @@ devtools.jar:
     skin/images/arrowhead-left.svg (themes/images/arrowhead-left.svg)
     skin/images/arrowhead-right.svg (themes/images/arrowhead-right.svg)
     skin/images/arrowhead-down.svg (themes/images/arrowhead-down.svg)
     skin/images/arrowhead-up.svg (themes/images/arrowhead-up.svg)
     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
     skin/images/checkbox.svg (themes/images/checkbox.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
     skin/images/aboutdebugging-connect-icon.svg (themes/images/aboutdebugging-connect-icon.svg)
+    skin/images/aboutdebugging-fenix-nightly.svg (themes/images/aboutdebugging-fenix-nightly.svg)
+    skin/images/aboutdebugging-fenix.svg (themes/images/aboutdebugging-fenix.svg)
     skin/images/aboutdebugging-firefox-aurora.svg (themes/images/aboutdebugging-firefox-aurora.svg)
     skin/images/aboutdebugging-firefox-beta.svg (themes/images/aboutdebugging-firefox-beta.svg)
     skin/images/aboutdebugging-firefox-logo.svg (themes/images/aboutdebugging-firefox-logo.svg)
     skin/images/aboutdebugging-firefox-nightly.svg (themes/images/aboutdebugging-firefox-nightly.svg)
     skin/images/aboutdebugging-firefox-release.svg (themes/images/aboutdebugging-firefox-release.svg)
     skin/images/aboutdebugging-globe-icon.svg (themes/images/aboutdebugging-globe-icon.svg)
     skin/images/aboutdebugging-information.svg (themes/images/aboutdebugging-information.svg)
     skin/images/aboutdebugging-process-icon.svg (themes/images/aboutdebugging-process-icon.svg)
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -25,18 +25,16 @@ const SWAPPED_BROWSER_STATE = [
   "_remoteFinder",
   "_securityUI",
   "_documentURI",
   "_documentContentType",
   "_contentTitle",
   "_characterSet",
   "_contentPrincipal",
   "_imageDocument",
-  "_fullZoom",
-  "_textZoom",
   "_isSyntheticDocument",
   "_innerWindowID",
 ];
 
 /**
  * Various parts of the Firefox code base expect to access properties on the browser
  * window in response to events (by reaching for the window via the event's target).
  *
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -347,16 +347,35 @@ ResponsiveUI.prototype = {
         debug("Wait until browser mounted");
         await message.wait(toolWindow, "browser-mounted");
         return ui.getViewportBrowser();
       },
     });
     debug("Wait until swap start");
     await this.swap.start();
 
+    // Set the ui toolWindow to fullZoom and textZoom of 100%. Directly change
+    // the zoom levels of the toolwindow docshell. That doesn't affect the zoom
+    // of the RDM content, but it does send events that confuse the Zoom UI.
+    // So before we adjust the zoom levels of the toolWindow, we first cache
+    // the reported zoom levels of the RDM content, because we'll have to
+    // re-apply them to re-sync the Zoom UI.
+
+    // Cache the values now and we'll re-apply them near the end of this function.
+    // This is important since other steps here can also cause the Zoom UI update
+    // event to be sent for other browsers, and this means that the changes from
+    // our Zoom UI update event would be overwritten. After this function, future
+    // changes to zoom levels will send Zoom UI update events in an order that
+    // keeps the Zoom UI synchronized with the RDM content zoom levels.
+    const fullZoom = this.tab.linkedBrowser.fullZoom;
+    const textZoom = this.tab.linkedBrowser.textZoom;
+
+    ui.toolWindow.docShell.contentViewer.fullZoom = 1;
+    ui.toolWindow.docShell.contentViewer.textZoom = 1;
+
     this.tab.addEventListener("BeforeTabRemotenessChange", this);
 
     // Notify the inner browser to start the frame script
     debug("Wait until start frame script");
     await message.request(this.toolWindow, "start-frame-script");
 
     // Get the protocol ready to speak with emulation actor
     debug("Wait until RDP server connect");
@@ -366,16 +385,21 @@ ResponsiveUI.prototype = {
     await this.restoreState();
 
     // Show the settings onboarding tooltip
     if (Services.prefs.getBoolPref(SHOW_SETTING_TOOLTIP_PREF)) {
       this.settingOnboardingTooltip =
         new SettingOnboardingTooltip(ui.toolWindow.document);
     }
 
+    // Re-apply our cached zoom levels. Other Zoom UI update events have finished
+    // by now.
+    this.tab.linkedBrowser.fullZoom = fullZoom;
+    this.tab.linkedBrowser.textZoom = textZoom;
+
     // Non-blocking message to tool UI to start any delayed init activities
     message.post(this.toolWindow, "post-init");
 
     debug("Init done");
   },
 
   /**
    * Close RDM and restore page content back into a regular tab.
--- a/devtools/client/responsive.html/test/browser/browser_toggle_zoom.js
+++ b/devtools/client/responsive.html/test/browser/browser_toggle_zoom.js
@@ -12,48 +12,62 @@ function getZoomForBrowser(browser) {
 }
 
 function setZoomForBrowser(browser, zoom) {
   ZoomManager.setZoomForBrowser(browser, zoom);
 }
 
 add_task(async function() {
   const INITIAL_ZOOM_LEVEL = 1;
+  const PRE_RDM_ZOOM_LEVEL = 1.5;
   const MID_RDM_ZOOM_LEVEL = 2;
 
   const tab = await addTab(TEST_URL);
   const browser = tab.linkedBrowser;
 
   await load(browser, TEST_URL);
 
+  // Get the initial zoom level.
+  const initialOuterZoom = getZoomForBrowser(browser);
+  is(initialOuterZoom, INITIAL_ZOOM_LEVEL, "Initial outer zoom should be " +
+      INITIAL_ZOOM_LEVEL + ".");
+
   // Change the zoom level before we open RDM.
-  setZoomForBrowser(browser, INITIAL_ZOOM_LEVEL);
+  setZoomForBrowser(browser, PRE_RDM_ZOOM_LEVEL);
+
+  const preRDMOuterZoom = getZoomForBrowser(browser);
+  is(preRDMOuterZoom, PRE_RDM_ZOOM_LEVEL, "Pre-RDM outer zoom should be " +
+      PRE_RDM_ZOOM_LEVEL + ".");
 
   // Start RDM on the tab. This will fundamentally change the way that browser behaves.
   // It will now pass all of its messages through to the RDM docshell, meaning that when
   // we request zoom level from it now, we are getting the RDM zoom level.
   const { ui } = await openRDM(tab);
   const uiDocShell = ui.toolWindow.docShell;
 
   // Bug 1541692: openRDM behaves differently in the test harness than it does
   // interactively. Interactively, many features of the container docShell -- including
   // zoom -- are copied over to the RDM browser. In the test harness, this seems to first
   // reset the docShell before toggling RDM, which makes checking the initial zoom of the
-  // UI and the RDM pane not useful.
+  // RDM pane not useful.
 
-  const initialUIZoom = uiDocShell.contentViewer.fullZoom;
-  isnot(initialUIZoom, MID_RDM_ZOOM_LEVEL,
-      "We should have a different UI initial zoom, so we can ensure it stays unchanged.");
+  const preZoomUIZoom = uiDocShell.contentViewer.fullZoom;
+  is(preZoomUIZoom, INITIAL_ZOOM_LEVEL,
+      "Pre-zoom UI zoom should be " + INITIAL_ZOOM_LEVEL + ".");
 
   // Set the zoom level. This should tunnel to the inner browser and leave the UI alone.
   setZoomForBrowser(browser, MID_RDM_ZOOM_LEVEL);
 
   // The UI zoom should be unchanged by this.
   const postZoomUIZoom = uiDocShell.contentViewer.fullZoom;
-  is(postZoomUIZoom, initialUIZoom, "UI zoom should be unchanged by RDM zoom.");
+  is(postZoomUIZoom, preZoomUIZoom, "UI zoom should be unchanged by RDM zoom.");
 
   // The RDM zoom should be changed.
   const finalRDMZoom = getZoomForBrowser(browser);
   is(finalRDMZoom, MID_RDM_ZOOM_LEVEL, "RDM zoom should be " + MID_RDM_ZOOM_LEVEL + ".");
 
   // Leave RDM. This should cause the outer pane to take on the full zoom of the RDM pane.
   await closeRDM(tab);
+
+  // Bug 1541692: the following todo_is check will become an is check when this bug lands.
+  const finalOuterZoom = getZoomForBrowser(browser);
+  todo_is(finalOuterZoom, finalRDMZoom, "Final outer zoom should match last RDM zoom.");
 });
--- a/devtools/client/shared/remote-debugging/remote-client-manager.js
+++ b/devtools/client/shared/remote-debugging/remote-client-manager.js
@@ -8,31 +8,37 @@ const { CONNECTION_TYPES } = require("de
 
 /**
  * This class is designed to be a singleton shared by all DevTools to get access to
  * existing clients created for remote debugging.
  */
 class RemoteClientManager {
   constructor() {
     this._clients = new Map();
+    this._runtimeInfoMap = new Map();
     this._onClientClosed = this._onClientClosed.bind(this);
   }
 
   /**
    * Store a remote client that is already connected.
    *
    * @param {String} id
    *        Remote runtime id (see devtools/client/aboutdebugging-new/src/types).
    * @param {String} type
    *        Remote runtime type (see devtools/client/aboutdebugging-new/src/types).
    * @param {DebuggerClient} client
+   * @param {Object} runtimeInfo
+   *        See runtimeInfo type from client/aboutdebugging-new/src/types/runtime.js
    */
-  setClient(id, type, client) {
+  setClient(id, type, client, runtimeInfo) {
     const key = this._getKey(id, type);
     this._clients.set(key, client);
+    if (runtimeInfo) {
+      this._runtimeInfoMap.set(key, runtimeInfo);
+    }
     client.addOneTimeListener("closed", this._onClientClosed);
   }
 
   // See JSDoc for id, type from setClient.
   hasClient(id, type) {
     return this._clients.has(this._getKey(id, type));
   }
 
@@ -61,47 +67,65 @@ class RemoteClientManager {
     return encodeURIComponent(this._getKey(id, type));
   }
 
   /**
    * Retrieve a managed client for a remote id. The remote id should have been generated
    * using getRemoteId.
    */
   getClientByRemoteId(remoteId) {
-    const key = decodeURIComponent(remoteId);
+    const key = this._getKeyByRemoteId(remoteId);
     return this._clients.get(key);
   }
 
   /**
+   * Retrieve the runtime info for a remote id. To display metadata about a runtime, such
+   * as name, device name, version... this runtimeInfo should be used rather than calling
+   * APIs on the client.
+   */
+  getRuntimeInfoByRemoteId(remoteId) {
+    const key = this._getKeyByRemoteId(remoteId);
+    return this._runtimeInfoMap.get(key);
+  }
+
+  /**
    * Retrieve a managed client for a remote id. The remote id should have been generated
    * using getRemoteId.
    */
   getConnectionTypeByRemoteId(remoteId) {
-    if (!remoteId) {
-      return CONNECTION_TYPES.THIS_FIREFOX;
-    }
-
-    const key = decodeURIComponent(remoteId);
+    const key = this._getKeyByRemoteId(remoteId);
     for (const type of Object.values(CONNECTION_TYPES)) {
       if (key.endsWith(type)) {
         return type;
       }
     }
     return CONNECTION_TYPES.UNKNOWN;
   }
 
   _getKey(id, type) {
     return id + "-" + type;
   }
 
+  _getKeyByRemoteId(remoteId) {
+    if (!remoteId) {
+      // If no remote id was provided, return the key corresponding to the local
+      // this-firefox runtime.
+      const { THIS_FIREFOX } = CONNECTION_TYPES;
+      return this._getKey(THIS_FIREFOX, THIS_FIREFOX);
+    }
+
+    return decodeURIComponent(remoteId);
+  }
+
   _removeClientByKey(key) {
     const client = this._clients.get(key);
     if (client) {
       client.removeListener("closed", this._onClientClosed);
       this._clients.delete(key);
+      this._runtimeInfoMap.delete(key);
     }
   }
 
   /**
    * Cleanup all closed clients when a "closed" notification is received from a client.
    */
   _onClientClosed() {
     const closedClientKeys = [...this._clients.keys()].filter(key => {
--- a/devtools/client/shared/remote-debugging/test/unit/test_remote_client_manager.js
+++ b/devtools/client/shared/remote-debugging/test/unit/test_remote_client_manager.js
@@ -6,46 +6,72 @@
 const { remoteClientManager } =
   require("devtools/client/shared/remote-debugging/remote-client-manager");
 const { CONNECTION_TYPES } =
   require("devtools/client/shared/remote-debugging/constants");
 
 add_task(async function testRemoteClientManager() {
   for (const type of Object.values(CONNECTION_TYPES)) {
     const fakeClient = createFakeClient();
+    const runtimeInfo = {};
     const clientId = "clientId";
     const remoteId = remoteClientManager.getRemoteId(clientId, type);
 
     const connectionType = remoteClientManager.getConnectionTypeByRemoteId(remoteId);
     equal(connectionType, type,
       `[${type}]: Correct connection type was returned by getConnectionTypeByRemoteId`);
 
     equal(remoteClientManager.hasClient(clientId, type), false,
       `[${type}]: hasClient returns false if no client was set`);
     equal(remoteClientManager.getClient(clientId, type), null,
       `[${type}]: getClient returns null if no client was set`);
     equal(remoteClientManager.getClientByRemoteId(remoteId), null,
       `[${type}]: getClientByRemoteId returns null if no client was set`);
+    equal(remoteClientManager.getRuntimeInfoByRemoteId(remoteId), null,
+      `[${type}]: getRuntimeInfoByRemoteId returns null if no client was set`);
 
-    remoteClientManager.setClient(clientId, type, fakeClient);
+    remoteClientManager.setClient(clientId, type, fakeClient, runtimeInfo);
     equal(remoteClientManager.hasClient(clientId, type), true,
       `[${type}]: hasClient returns true`);
     equal(remoteClientManager.getClient(clientId, type), fakeClient,
       `[${type}]: getClient returns the correct client`);
     equal(remoteClientManager.getClientByRemoteId(remoteId), fakeClient,
       `[${type}]: getClientByRemoteId returns the correct client`);
+    equal(remoteClientManager.getRuntimeInfoByRemoteId(remoteId), runtimeInfo,
+      `[${type}]: getRuntimeInfoByRemoteId returns the correct object`);
 
     remoteClientManager.removeClient(clientId, type);
     equal(remoteClientManager.hasClient(clientId, type), false,
       `[${type}]: hasClient returns false after removing the client`);
     equal(remoteClientManager.getClient(clientId, type), null,
       `[${type}]: getClient returns null after removing the client`);
     equal(remoteClientManager.getClientByRemoteId(remoteId), null,
       `[${type}]: getClientByRemoteId returns null after removing the client`);
+    equal(remoteClientManager.getRuntimeInfoByRemoteId(), null,
+      `[${type}]: getRuntimeInfoByRemoteId returns null after removing the client`);
   }
+
+  // Test various fallback scenarios for APIs relying on remoteId, when called without a
+  // remoteId, we expect to get the information for the local this-firefox runtime.
+  const { THIS_FIREFOX } = CONNECTION_TYPES;
+  const thisFirefoxClient = createFakeClient();
+  const thisFirefoxInfo = {};
+  remoteClientManager.setClient(THIS_FIREFOX, THIS_FIREFOX, thisFirefoxClient,
+    thisFirefoxInfo);
+
+  equal(remoteClientManager.getClientByRemoteId(), thisFirefoxClient,
+    `[fallback]: getClientByRemoteId returns this-firefox if remoteId is null`);
+  equal(remoteClientManager.getRuntimeInfoByRemoteId(), thisFirefoxInfo,
+    `[fallback]: getRuntimeInfoByRemoteId returns this-firefox if remoteId is null`);
+
+  const otherRemoteId = remoteClientManager.getRemoteId("clientId", CONNECTION_TYPES.USB);
+  equal(remoteClientManager.getClientByRemoteId(otherRemoteId), null,
+    `[fallback]: getClientByRemoteId does not fallback if remoteId is non-null`);
+  equal(remoteClientManager.getRuntimeInfoByRemoteId(otherRemoteId), null,
+    `[fallback]: getRuntimeInfoByRemoteId does not fallback if remoteId is non-null`);
 });
 
 add_task(async function testRemoteClientManagerWithUnknownType() {
   const remoteId = remoteClientManager.getRemoteId("someClientId", "NotARealType");
   const connectionType = remoteClientManager.getConnectionTypeByRemoteId(remoteId);
   equal(connectionType, CONNECTION_TYPES.UNKNOWN,
     `Connection type UNKNOWN was returned by getConnectionTypeByRemoteId`);
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/aboutdebugging-fenix-nightly.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="26 26 56 56">
+  <defs>
+    <linearGradient id="a" x1="78.92" y1="25.09" x2="37.26" y2="74.45" gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#88ffd1"/>
+      <stop offset=".04" stop-color="#7bfcd6"/>
+      <stop offset=".13" stop-color="#57f3e2"/>
+      <stop offset=".24" stop-color="#1ee5f5"/>
+      <stop offset=".29" stop-color="#0df"/>
+      <stop offset="1" stop-color="#9059ff"/>
+    </linearGradient>
+  </defs>
+  <path d="M78.12 44.5c-1.19-2.85-3.51-5.74-5.4-6.74a1 1 0 0 0-1 0 .91.91 0 0 0-.39.91c-.79-1-1.62-1.91-2.46-2.86A47.35 47.35 0 0 1 64 29.62c-.19-.31-.39-.61-.58-.93l-.24-.46a4.06 4.06 0 0 1-.3-.76 1 1 0 0 0-.79-.79.81.81 0 0 0-.31 0 .87.87 0 0 0-.38.11 19.31 19.31 0 0 0-9 13.78 9.61 9.61 0 0 0-2.3.66.91.91 0 0 0 .32 1.77.78.78 0 0 0 .36-.08 7.44 7.44 0 0 1 1.87-.52 22.18 22.18 0 0 1 2.29 0h.74l.68.11h.13l.55.13.2.06.55.17h.11l.64.25.16.07.48.22.18.08.6.33c.19.11.37.23.55.35l.18.12.46.33.09.07.53.44.11.09.26.24-.4-.08h-.08l-.36-.06h-1.98l-.41.05a1 1 0 0 0-.74.55.9.9 0 0 0 .44 1.16 9.25 9.25 0 0 1 4.59 10.83c-.08.3-.17.6-.27.88v.05c-.1.27-.21.53-.32.78a.25.25 0 0 0 0 .07 7.55 7.55 0 0 1-.37.69.41.41 0 0 1 0 .09c-.13.21-.27.42-.41.61l-.06.09c-.15.19-.3.38-.45.55l-.07.08c-.16.17-.32.34-.49.49l-.08.07c-.17.16-.35.3-.53.44l-.07.06c-.19.13-.37.26-.57.38h-.07c-.19.12-.39.23-.6.34L58 64l-.68.25-.72.22-.67.14h-.07l-.65.1h-.09l-.64.06h-1.32a10.18 10.18 0 0 1-3-.57l-.68-.28a3.39 3.39 0 0 1-.39-.18 9.69 9.69 0 0 1-5.41-7.26c.26-.64 1.71-3.49 7.38-3.49 1.2 0 3.59-2.3 3.9-3.43v-.28c0-.6-.34-.75-1.67-1.4a21.42 21.42 0 0 1-4.68-2.8l-.27-.26-.34-.33-.29-.29a4.12 4.12 0 0 0-.65-.77 7.34 7.34 0 0 0-.69-.52 10.76 10.76 0 0 1 .05-5.12.89.89 0 0 0-.42-1.1.94.94 0 0 0-.82 0 18.32 18.32 0 0 0-5.33 3.82 20.05 20.05 0 0 1-.32-4.51.71.71 0 0 0 0-.25.9.9 0 0 0-.56-.65c-.23-.1-.56-.23-1.69.54a18.75 18.75 0 0 0-2.58 2.2 22.19 22.19 0 0 0-2.45 2.91 21.89 21.89 0 0 0-3.51 7.88v.18c-.05.26-.23 1.38-.26 1.63-.09.44-.16.91-.23 1.45-.1.72-.16 1.4-.19 2.11v.13a26 26 0 0 0 51.7 4.35c0-.24.06-.48.08-.73v-.25a26.27 26.27 0 0 0-1.72-13zm-18.81-3h-.11c-.21-.09-.43-.17-.64-.24l-.23-.08a5.76 5.76 0 0 0-.59-.18h-.19c-.25-.07-.51-.13-.77-.18h-.15l-.63-.19h-.24l-.65-.07h-.89a18 18 0 0 1 4.51-9.27c0 .08-.05.16-.07.24a1.42 1.42 0 0 0-.07.22 3.57 3.57 0 0 0-.1.35l-.06.21c0 .13-.07.27-.1.4s0 .12 0 .18l-.12.59-.06.37v1.33a.41.41 0 0 0 0 .11v1.36a.31.31 0 0 1 0 .1v1.06c0 .04 0 .31.08.46 0 .16.07.32.11.49v.13c0 .13.07.26.1.39v.11c0 .17.1.33.16.5v.1l.15.41.06.14c.06.15.12.31.19.47.07.17.16.33.24.5v.06zm.49-6.5c0-.2 0-.4.05-.6a.88.88 0 0 0 0-.16c0-.16.05-.32.07-.47s0-.08 0-.12.07-.36.11-.54v-.13c0-.14.07-.28.1-.42s0-.09 0-.14l.15-.49a.14.14 0 0 1 0-.06c0-.14.09-.28.14-.41l.05-.14.14-.36v-.08l.18-.4a.47.47 0 0 1 0-.1 2.59 2.59 0 0 1 .14-.29V30q.09-.18.18-.33v-.06l.14-.25v-.13c0-.06.12.23.2.35l.6 1A49.71 49.71 0 0 0 67.51 37c2.44 2.76 4.75 5.36 6.38 9.35a7.53 7.53 0 0 1 .1.83 1.27 1.27 0 0 1 0 .2v1.9c0 .11 0 .4-.07.6a2.89 2.89 0 0 1 0 .29c0 .28-.09.57-.15.85-.06.3-.14.59-.22.87l-.09.28c-.06.2-.12.4-.19.6l-.12.31c-.07.19-.15.38-.23.57s-.09.2-.13.29-.21.44-.32.66l-.09.19c-.15.28-.3.55-.47.82l-.12.21c-.13.2-.26.41-.4.61l-.19.27-.4.54a2.47 2.47 0 0 1-.21.27c-.14.19-.3.37-.46.56l-.18.21a.23.23 0 0 1-.07.07c0-.18 0-.37.07-.55s0-.1 0-.15v-.47a.57.57 0 0 1 0-.13v-1.99a.19.19 0 0 1 0-.08c0-.18 0-.37-.07-.55s0-.11 0-.16a11.5 11.5 0 0 0-.32-1.27.75.75 0 0 0 0-.16c0-.19-.11-.38-.17-.56v-.09q-.1-.31-.24-.63l-.15-.1c-.07-.18-.15-.35-.24-.53l-.06-.12-.34-.62-.07-.12c-.11-.17-.21-.34-.33-.51l-.1-.14c-.14-.2-.28-.4-.44-.59l-.07-.08-.44-.51a1.85 1.85 0 0 1-.14-.14c-.18-.19-.37-.38-.57-.56l-.2-.16h-.05l-.06-.05v-.06c-.13-.23-.28-.47-.43-.69a.86.86 0 0 0-.07-.1c-.16-.23-.32-.46-.5-.69l-.06-.07c-.18-.24-.37-.47-.58-.7-.21-.24-.43-.47-.67-.71A11.58 11.58 0 0 1 59.8 35zm1.4 29.21l.66-.57.06-.06c.2-.2.4-.4.58-.6l.06-.08c.18-.2.35-.41.51-.62l.06-.09c.16-.21.31-.43.45-.65l.05-.08q.21-.34.39-.69a.25.25 0 0 0 0-.07 7.79 7.79 0 0 0 .38-.7v-.06c.1-.25.2-.5.29-.75q.13-.39.24-.78a11.22 11.22 0 0 0-2.86-11 8.1 8.1 0 0 1 2.44 1.12l.09.07c.11.09.22.17.3.25l.46.45.14.14.27.31.23.25.22.28a1.43 1.43 0 0 1 .14.2l.19.27.13.2c.06.09.12.18.17.27a2.12 2.12 0 0 1 .11.21l.15.28.1.21.12.27.09.21c0 .09.07.19.11.28s0 .14.07.2.06.19.09.29a1.17 1.17 0 0 1 .06.2c0 .1.06.2.08.29s0 .13 0 .19l.06.32v.16l.06.39v.55a3 3 0 0 1 0 .42.57.57 0 0 0 0 .13v.76a.81.81 0 0 1 0 .16 2.44 2.44 0 0 1 0 .27v.16a2.28 2.28 0 0 0 0 .26v.4A.59.59 0 0 1 68 58v.25a.5.5 0 0 0 0 .12v.26a.36.36 0 0 1 0 .1 1.84 1.84 0 0 1-.05.25V59l-.06.26v.05c-.1.41-.2.76-.29 1l-.06.18c0 .05 0 .09-.05.13l-.4.31c-.12.09-.24.19-.36.27a4.73 4.73 0 0 1-.46.32l-.33.24-.47.3-.32.2-.48.28-.3.18-.48.25-.29.16-.49.23-.28.13-.44.18-.83.33-.23.08zM51.39 51l-.18.12-.15.07h-1.14l-.78.07H49c-.27 0-.52.07-.77.12h-.19l-.62.14h-.1c-.22.06-.44.12-.64.19h-.16l-.5.19h-.1l-.53.25-.12.06-.4.22-.1.06a4.38 4.38 0 0 0-.42.28l-.09.06-.32.24-.08.07-.34.29-.06.06-.25.25-.06.07c-.09.1-.18.19-.26.29v.05l-.19.24v.07a2.11 2.11 0 0 0-.18.27l-.14.22v-.48a.25.25 0 0 0 0-.08v-1.46a.88.88 0 0 0-.23-.71.92.92 0 0 0-.68-.3 5.48 5.48 0 0 1-4.71-2.47 8.8 8.8 0 0 1 7.61.11l.5.22.19.07.32.13.22.07.27.1.23.07.27.07a1.55 1.55 0 0 0 .23.06l.25.07.24.05h4.52zm-7.07-11.81a12.42 12.42 0 0 0 0 2.72h-.05q-.3-.11-.6-.18h-.11l-.56-.19h-.07c-.45-.08-.89-.14-1.3-.19a17.12 17.12 0 0 1 2.69-2.16zM30 54.53a8.81 8.81 0 0 1 0-2.42c.07-.5.14-1 .22-1.38v-.06c0-.25.19-1.31.23-1.48v-.16a20 20 0 0 1 3.2-7.19l.06-.09A20.82 20.82 0 0 1 36 39.06a18.77 18.77 0 0 1 1.7-1.52c0 1.66.23 4.09 1.09 5.17a.91.91 0 0 0 .21.18l.1.06a.6.6 0 0 0 .19.06.65.65 0 0 0 .2 0h1.46a8.69 8.69 0 0 1 3.65.99l.5.33c.23.16.4.28.58.43s.7.65 1.41 1.34l.26.26.37.33.08.07.41.31.08.06.4.28h.09l.46.3.13.07.48.29.47.26.12.07.48.25h.1l.44.23H49c-.26 0-.53 0-.8-.07h-.99l-.21.01h-.18l-.25-.07-.18-.06-.26-.09-.17-.05-.29-.12h-.15l-.52-.28a10.36 10.36 0 0 0-10.16.45.89.89 0 0 0-.31 1.07 7.28 7.28 0 0 0 5.82 4.24c-.12 4.66 2.75 9 7.52 11.29l.33.16a14.56 14.56 0 0 0 7.17 1.66h.22a19.62 19.62 0 0 0 7.73-1.74l.19-.09c.21-.1.42-.19.63-.3l.34-.17.51-.27.45-.26L66 64l.52-.33.37-.24.61-.43.29-.21c.31-.22.61-.46.92-.7l.1-.11c.26-.22.52-.44.77-.67l.17-.16q.41-.36.78-.75l.11-.11c.24-.25.46-.5.69-.75l.09-.1c.23-.27.45-.54.67-.82a1.83 1.83 0 0 1 .13-.16l.61-.84.07-.11c.19-.28.38-.57.55-.86a.43.43 0 0 0 0-.07c.18-.3.35-.6.51-.91 0 0 .05-.09.07-.14.16-.3.31-.62.45-.93v-.07q.21-.5.39-1 .13-.34.24-.69v2.4a1.8 1.8 0 0 1 0 .22c0 .3-.06.6-.09.89v.18c0 .25-.07.51-.12.76v.19c-.05.29-.11.57-.17.84v.19c-.06.24-.11.48-.17.71a1.24 1.24 0 0 0 0 .16c-.08.26-.15.52-.23.78l-.06.18c-.07.23-.15.46-.23.68v.09c-.09.24-.18.48-.28.71l-.06.16c-.1.23-.2.46-.3.67-2.72 5.8-9.38 11.9-19.9 11.6-11.61-.35-21.6-8.8-23.45-19.68zm48 2.76v.26c0 .23 0 .46-.08.65a24.25 24.25 0 0 1-45.65 6.4l.15.2.15.23.52.69.07.09.6.74.18.21.51.58.15.16q.33.36.66.69l.18.17.55.53.2.18c.23.22.47.43.71.63l.13.11q.31.27.63.51l.23.18.7.52.09.07.79.54.24.15c.21.15.43.28.66.42a1.18 1.18 0 0 0 .19.11c.27.17.55.33.83.48l.22.12.68.35.24.12.88.41.16.07.76.32.26.1.88.32h.05c.31.11.63.21.94.3l.27.08.79.22h.19l1 .22.24.05.79.14h.25c.33 0 .66.1 1 .13h.17l.85.08h.28q.51 0 1 .06H54a22.6 22.6 0 0 0 19.56-10.17 22.29 22.29 0 0 0 1.28-2.32 23.44 23.44 0 0 0 .94-17.16c0-.1-.06-.2-.1-.3a23.75 23.75 0 0 0-.9-3.66 15.22 15.22 0 0 1 1.65 2.93A24.39 24.39 0 0 1 78 57.29z" fill="url(#a)" data-name="Foreground Layer"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/aboutdebugging-fenix.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="26 26 56 56">
+  <defs>
+    <linearGradient id="a" x1="78.92" y1="25.09" x2="37.26" y2="74.45" gradientUnits="userSpaceOnUse">
+      <stop offset=".09" stop-color="#ff980e"/>
+      <stop offset=".18" stop-color="#ff8b16"/>
+      <stop offset=".36" stop-color="#ff672a"/>
+      <stop offset=".56" stop-color="#ff3647"/>
+      <stop offset=".94" stop-color="#e31587"/>
+    </linearGradient>
+  </defs>
+  <path d="M78.12 44.5c-1.19-2.85-3.51-5.74-5.4-6.74a1 1 0 0 0-1 0 .91.91 0 0 0-.39.91c-.79-1-1.62-1.91-2.46-2.86A47.35 47.35 0 0 1 64 29.62c-.19-.31-.39-.61-.58-.93l-.24-.46a4.06 4.06 0 0 1-.3-.76 1 1 0 0 0-.79-.79.81.81 0 0 0-.31 0 .87.87 0 0 0-.38.11 19.31 19.31 0 0 0-9 13.78 9.61 9.61 0 0 0-2.3.66.91.91 0 0 0 .32 1.77.78.78 0 0 0 .36-.08 7.44 7.44 0 0 1 1.87-.52 22.18 22.18 0 0 1 2.29 0h.74l.68.11h.13l.55.13.2.06.55.17h.11l.64.25.16.07.48.22.18.08.6.33c.19.11.37.23.55.35l.18.12.46.33.09.07.53.44.11.09.26.24-.4-.08h-.08l-.36-.06h-1.98l-.41.05a1 1 0 0 0-.74.55.9.9 0 0 0 .44 1.16 9.25 9.25 0 0 1 4.59 10.83c-.08.3-.17.6-.27.88v.05c-.1.27-.21.53-.32.78a.25.25 0 0 0 0 .07 7.55 7.55 0 0 1-.37.69.41.41 0 0 1 0 .09c-.13.21-.27.42-.41.61l-.06.09c-.15.19-.3.38-.45.55l-.07.08c-.16.17-.32.34-.49.49l-.08.07c-.17.16-.35.3-.53.44l-.07.06c-.19.13-.37.26-.57.38h-.07c-.19.12-.39.23-.6.34L58 64l-.68.25-.72.22-.67.14h-.07l-.65.1h-.09l-.64.06h-1.32a10.18 10.18 0 0 1-3-.57l-.68-.28a3.39 3.39 0 0 1-.39-.18 9.69 9.69 0 0 1-5.41-7.26c.26-.64 1.71-3.49 7.38-3.49 1.2 0 3.59-2.3 3.9-3.43v-.28c0-.6-.34-.75-1.67-1.4a21.42 21.42 0 0 1-4.68-2.8l-.27-.26-.34-.33-.29-.29a4.12 4.12 0 0 0-.65-.77 7.34 7.34 0 0 0-.69-.52 10.76 10.76 0 0 1 .05-5.12.89.89 0 0 0-.42-1.1.94.94 0 0 0-.82 0 18.32 18.32 0 0 0-5.33 3.82 20.05 20.05 0 0 1-.32-4.51.71.71 0 0 0 0-.25.9.9 0 0 0-.56-.65c-.23-.1-.56-.23-1.69.54a18.75 18.75 0 0 0-2.58 2.2 22.19 22.19 0 0 0-2.45 2.91 21.89 21.89 0 0 0-3.51 7.88v.18c-.05.26-.23 1.38-.26 1.63-.09.44-.16.91-.23 1.45-.1.72-.16 1.4-.19 2.11v.13a26 26 0 0 0 51.7 4.35c0-.24.06-.48.08-.73v-.25a26.27 26.27 0 0 0-1.72-13zm-18.81-3h-.11c-.21-.09-.43-.17-.64-.24l-.23-.08a5.76 5.76 0 0 0-.59-.18h-.19c-.25-.07-.51-.13-.77-.18h-.15l-.63-.19h-.24l-.65-.07h-.89a18 18 0 0 1 4.51-9.27c0 .08-.05.16-.07.24a1.42 1.42 0 0 0-.07.22 3.57 3.57 0 0 0-.1.35l-.06.21c0 .13-.07.27-.1.4s0 .12 0 .18l-.12.59-.06.37v1.33a.41.41 0 0 0 0 .11v1.36a.31.31 0 0 1 0 .1v1.06c0 .04 0 .31.08.46 0 .16.07.32.11.49v.13c0 .13.07.26.1.39v.11c0 .17.1.33.16.5v.1l.15.41.06.14c.06.15.12.31.19.47.07.17.16.33.24.5v.06zm.49-6.5c0-.2 0-.4.05-.6a.88.88 0 0 0 0-.16c0-.16.05-.32.07-.47s0-.08 0-.12.07-.36.11-.54v-.13c0-.14.07-.28.1-.42s0-.09 0-.14l.15-.49a.14.14 0 0 1 0-.06c0-.14.09-.28.14-.41l.05-.14.14-.36v-.08l.18-.4a.47.47 0 0 1 0-.1 2.59 2.59 0 0 1 .14-.29V30q.09-.18.18-.33v-.06l.14-.25v-.13c0-.06.12.23.2.35l.6 1A49.71 49.71 0 0 0 67.51 37c2.44 2.76 4.75 5.36 6.38 9.35a7.53 7.53 0 0 1 .1.83 1.27 1.27 0 0 1 0 .2v1.9c0 .11 0 .4-.07.6a2.89 2.89 0 0 1 0 .29c0 .28-.09.57-.15.85-.06.3-.14.59-.22.87l-.09.28c-.06.2-.12.4-.19.6l-.12.31c-.07.19-.15.38-.23.57s-.09.2-.13.29-.21.44-.32.66l-.09.19c-.15.28-.3.55-.47.82l-.12.21c-.13.2-.26.41-.4.61l-.19.27-.4.54a2.47 2.47 0 0 1-.21.27c-.14.19-.3.37-.46.56l-.18.21a.23.23 0 0 1-.07.07c0-.18 0-.37.07-.55s0-.1 0-.15v-.47a.57.57 0 0 1 0-.13v-1.99a.19.19 0 0 1 0-.08c0-.18 0-.37-.07-.55s0-.11 0-.16a11.5 11.5 0 0 0-.32-1.27.75.75 0 0 0 0-.16c0-.19-.11-.38-.17-.56v-.09q-.1-.31-.24-.63l-.15-.1c-.07-.18-.15-.35-.24-.53l-.06-.12-.34-.62-.07-.12c-.11-.17-.21-.34-.33-.51l-.1-.14c-.14-.2-.28-.4-.44-.59l-.07-.08-.44-.51a1.85 1.85 0 0 1-.14-.14c-.18-.19-.37-.38-.57-.56l-.2-.16h-.05l-.06-.05v-.06c-.13-.23-.28-.47-.43-.69a.86.86 0 0 0-.07-.1c-.16-.23-.32-.46-.5-.69l-.06-.07c-.18-.24-.37-.47-.58-.7-.21-.24-.43-.47-.67-.71A11.58 11.58 0 0 1 59.8 35zm1.4 29.21l.66-.57.06-.06c.2-.2.4-.4.58-.6l.06-.08c.18-.2.35-.41.51-.62l.06-.09c.16-.21.31-.43.45-.65l.05-.08q.21-.34.39-.69a.25.25 0 0 0 0-.07 7.79 7.79 0 0 0 .38-.7v-.06c.1-.25.2-.5.29-.75q.13-.39.24-.78a11.22 11.22 0 0 0-2.86-11 8.1 8.1 0 0 1 2.44 1.12l.09.07c.11.09.22.17.3.25l.46.45.14.14.27.31.23.25.22.28a1.43 1.43 0 0 1 .14.2l.19.27.13.2c.06.09.12.18.17.27a2.12 2.12 0 0 1 .11.21l.15.28.1.21.12.27.09.21c0 .09.07.19.11.28s0 .14.07.2.06.19.09.29a1.17 1.17 0 0 1 .06.2c0 .1.06.2.08.29s0 .13 0 .19l.06.32v.16l.06.39v.55a3 3 0 0 1 0 .42.57.57 0 0 0 0 .13v.76a.81.81 0 0 1 0 .16 2.44 2.44 0 0 1 0 .27v.16a2.28 2.28 0 0 0 0 .26v.4A.59.59 0 0 1 68 58v.25a.5.5 0 0 0 0 .12v.26a.36.36 0 0 1 0 .1 1.84 1.84 0 0 1-.05.25V59l-.06.26v.05c-.1.41-.2.76-.29 1l-.06.18c0 .05 0 .09-.05.13l-.4.31c-.12.09-.24.19-.36.27a4.73 4.73 0 0 1-.46.32l-.33.24-.47.3-.32.2-.48.28-.3.18-.48.25-.29.16-.49.23-.28.13-.44.18-.83.33-.23.08zM51.39 51l-.18.12-.15.07h-1.14l-.78.07H49c-.27 0-.52.07-.77.12h-.19l-.62.14h-.1c-.22.06-.44.12-.64.19h-.16l-.5.19h-.1l-.53.25-.12.06-.4.22-.1.06a4.38 4.38 0 0 0-.42.28l-.09.06-.32.24-.08.07-.34.29-.06.06-.25.25-.06.07c-.09.1-.18.19-.26.29v.05l-.19.24v.07a2.11 2.11 0 0 0-.18.27l-.14.22v-.48a.25.25 0 0 0 0-.08v-1.46a.88.88 0 0 0-.23-.71.92.92 0 0 0-.68-.3 5.48 5.48 0 0 1-4.71-2.47 8.8 8.8 0 0 1 7.61.11l.5.22.19.07.32.13.22.07.27.1.23.07.27.07a1.55 1.55 0 0 0 .23.06l.25.07.24.05h4.52zm-7.07-11.81a12.42 12.42 0 0 0 0 2.72h-.05q-.3-.11-.6-.18h-.11l-.56-.19h-.07c-.45-.08-.89-.14-1.3-.19a17.12 17.12 0 0 1 2.69-2.16zM30 54.53a8.81 8.81 0 0 1 0-2.42c.07-.5.14-1 .22-1.38v-.06c0-.25.19-1.31.23-1.48v-.16a20 20 0 0 1 3.2-7.19l.06-.09A20.82 20.82 0 0 1 36 39.06a18.77 18.77 0 0 1 1.7-1.52c0 1.66.23 4.09 1.09 5.17a.91.91 0 0 0 .21.18l.1.06a.6.6 0 0 0 .19.06.65.65 0 0 0 .2 0h1.46a8.69 8.69 0 0 1 3.65.99l.5.33c.23.16.4.28.58.43s.7.65 1.41 1.34l.26.26.37.33.08.07.41.31.08.06.4.28h.09l.46.3.13.07.48.29.47.26.12.07.48.25h.1l.44.23H49c-.26 0-.53 0-.8-.07h-.99l-.21.01h-.18l-.25-.07-.18-.06-.26-.09-.17-.05-.29-.12h-.15l-.52-.28a10.36 10.36 0 0 0-10.16.45.89.89 0 0 0-.31 1.07 7.28 7.28 0 0 0 5.82 4.24c-.12 4.66 2.75 9 7.52 11.29l.33.16a14.56 14.56 0 0 0 7.17 1.66h.22a19.62 19.62 0 0 0 7.73-1.74l.19-.09c.21-.1.42-.19.63-.3l.34-.17.51-.27.45-.26L66 64l.52-.33.37-.24.61-.43.29-.21c.31-.22.61-.46.92-.7l.1-.11c.26-.22.52-.44.77-.67l.17-.16q.41-.36.78-.75l.11-.11c.24-.25.46-.5.69-.75l.09-.1c.23-.27.45-.54.67-.82a1.83 1.83 0 0 1 .13-.16l.61-.84.07-.11c.19-.28.38-.57.55-.86a.43.43 0 0 0 0-.07c.18-.3.35-.6.51-.91 0 0 .05-.09.07-.14.16-.3.31-.62.45-.93v-.07q.21-.5.39-1 .13-.34.24-.69v2.4a1.8 1.8 0 0 1 0 .22c0 .3-.06.6-.09.89v.18c0 .25-.07.51-.12.76v.19c-.05.29-.11.57-.17.84v.19c-.06.24-.11.48-.17.71a1.24 1.24 0 0 0 0 .16c-.08.26-.15.52-.23.78l-.06.18c-.07.23-.15.46-.23.68v.09c-.09.24-.18.48-.28.71l-.06.16c-.1.23-.2.46-.3.67-2.72 5.8-9.38 11.9-19.9 11.6-11.61-.35-21.6-8.8-23.45-19.68zm48 2.76v.26c0 .23 0 .46-.08.65a24.25 24.25 0 0 1-45.65 6.4l.15.2.15.23.52.69.07.09.6.74.18.21.51.58.15.16q.33.36.66.69l.18.17.55.53.2.18c.23.22.47.43.71.63l.13.11q.31.27.63.51l.23.18.7.52.09.07.79.54.24.15c.21.15.43.28.66.42a1.18 1.18 0 0 0 .19.11c.27.17.55.33.83.48l.22.12.68.35.24.12.88.41.16.07.76.32.26.1.88.32h.05c.31.11.63.21.94.3l.27.08.79.22h.19l1 .22.24.05.79.14h.25c.33 0 .66.1 1 .13h.17l.85.08h.28q.51 0 1 .06H54a22.6 22.6 0 0 0 19.56-10.17 22.29 22.29 0 0 0 1.28-2.32 23.44 23.44 0 0 0 .94-17.16c0-.1-.06-.2-.1-.3a23.75 23.75 0 0 0-.9-3.66 15.22 15.22 0 0 1 1.65 2.93A24.39 24.39 0 0 1 78 57.29z" fill="url(#a)" data-name="Foreground Layer"/>
+</svg>
--- a/devtools/server/actors/promises.js
+++ b/devtools/server/actors/promises.js
@@ -186,15 +186,15 @@ var PromisesActor = protocol.ActorClassW
     };
   },
 
   _onWindowReady: expectState("attached", function({ isTopLevel }) {
     if (!isTopLevel) {
       return;
     }
 
-    this._navigationLifetimePool.cleanup();
+    this._navigationLifetimePool.destroy();
     this.dbg.removeAllDebuggees();
     this.dbg.addDebuggees();
   }),
 });
 
 exports.PromisesActor = PromisesActor;
--- a/devtools/shared/adb/adb-runtime.js
+++ b/devtools/shared/adb/adb-runtime.js
@@ -1,39 +1,81 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { RuntimeTypes } = require("devtools/client/webide/modules/runtime-types");
 const { prepareTCPConnection } = require("devtools/shared/adb/commands/index");
+const { shell } = require("devtools/shared/adb/commands/index");
 
 class AdbRuntime {
   constructor(adbDevice, socketPath) {
     this.type = RuntimeTypes.USB;
 
     this._adbDevice = adbDevice;
     this._socketPath = socketPath;
   }
 
+  async init() {
+    const packageName = this._packageName();
+    const query = `dumpsys package ${packageName} | grep versionName`;
+    const versionNameString = await shell(this._adbDevice.id, query);
+    const matches = versionNameString.match(/versionName=([\d.]+)/);
+    if (matches && matches[1]) {
+      this._versionName = matches[1];
+    }
+  }
+
   get id() {
     return this._adbDevice.id + "|" + this._socketPath;
   }
 
+  get isFenix() {
+    return this._packageName().includes("org.mozilla.fenix");
+  }
+
   get deviceId() {
     return this._adbDevice.id;
   }
 
   get deviceName() {
     return this._adbDevice.name;
   }
 
+  get versionName() {
+    return this._versionName;
+  }
+
   get shortName() {
-    return `Firefox ${this._channel()}`;
+    const packageName = this._packageName();
+
+    switch (packageName) {
+      case "org.mozilla.firefox":
+        return "Firefox";
+      case "org.mozilla.firefox_beta":
+        return "Firefox Beta";
+      case "org.mozilla.fennec":
+      case "org.mozilla.fennec_aurora":
+        // This package name is now the one for Firefox Nightly distributed
+        // through the Google Play Store since "dawn project"
+        // cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1357351#c8
+        return "Firefox Nightly";
+      case "org.mozilla.fenix":
+        // The current Nightly build for Fenix is available under this package name
+        // but the official packages will use fenix, fenix.beta and fenix.nightly.
+        return "Firefox Preview";
+      case "org.mozilla.fenix.beta":
+        return "Firefox Preview Beta";
+      case "org.mozilla.fenix.nightly":
+        return "Firefox Preview Nightly";
+      default:
+        return "Firefox Custom";
+    }
   }
 
   get socketPath() {
     return this._socketPath;
   }
 
   get name() {
     return `${this.shortName} on Android (${this.deviceName})`;
@@ -42,35 +84,16 @@ class AdbRuntime {
   connect(connection) {
     return prepareTCPConnection(this.deviceId, this._socketPath).then(port => {
       connection.host = "localhost";
       connection.port = port;
       connection.connect();
     });
   }
 
-  _channel() {
-    const packageName = this._packageName();
-
-    switch (packageName) {
-      case "org.mozilla.firefox":
-        return "";
-      case "org.mozilla.firefox_beta":
-        return "Beta";
-      case "org.mozilla.fennec":
-      case "org.mozilla.fennec_aurora":
-        // This package name is now the one for Firefox Nightly distributed
-        // through the Google Play Store since "dawn project"
-        // cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1357351#c8
-        return "Nightly";
-      default:
-        return "Custom";
-    }
-  }
-
   _packageName() {
     // If using abstract socket address, it is "@org.mozilla.firefox/..."
     // If using path base socket, it is "/data/data/<package>...""
     // Until Fennec 62 only supports path based UNIX domain socket, but
     // Fennec 63+ supports both path based and abstract socket.
     return this._socketPath.startsWith("@") ?
       this._socketPath.substr(1).split("/")[0] :
       this._socketPath.split("/")[3];
--- a/devtools/shared/adb/adb.js
+++ b/devtools/shared/adb/adb.js
@@ -129,13 +129,19 @@ class Adb extends EventEmitter {
   }
 
   _onNoDevicesDetected() {
     this.updateRuntimes();
   }
 
   async _getDeviceRuntimes(device) {
     const socketPaths = [...await device.getRuntimeSocketPaths()];
-    return socketPaths.map(socketPath => new AdbRuntime(device, socketPath));
+    const runtimes = [];
+    for (const socketPath of socketPaths) {
+      const runtime = new AdbRuntime(device, socketPath);
+      await runtime.init();
+      runtimes.push(runtime);
+    }
+    return runtimes;
   }
 }
 
 exports.adb = new Adb();
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -45,16 +45,17 @@ inline bool nsIContent::IsInChromeDocume
 inline void nsIContent::SetPrimaryFrame(nsIFrame* aFrame) {
   MOZ_ASSERT(IsInUncomposedDoc() || IsInShadowTree(), "This will end badly!");
 
   // FIXME bug 749326
   NS_ASSERTION(!aFrame || !mPrimaryFrame || aFrame == mPrimaryFrame,
                "Losing track of existing primary frame");
 
   if (aFrame) {
+    MOZ_ASSERT(!aFrame->IsPlaceholderFrame());
     if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
         aFrame->GetContent() == this) {
       aFrame->SetIsPrimaryFrame(true);
     }
   } else if (nsIFrame* currentPrimaryFrame = GetPrimaryFrame()) {
     if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
         currentPrimaryFrame->GetContent() == this) {
       currentPrimaryFrame->SetIsPrimaryFrame(false);
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -302,22 +302,22 @@ WINDOW_EVENT(orientationchange, eOrienta
 #endif
 WINDOW_EVENT(pagehide, ePageHide, EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(pageshow, ePageShow, EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(popstate, ePopState,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
-// Not supported yet
-// WINDOW_EVENT(redo)
+WINDOW_EVENT(rejectionhandled, eRejectionHandled,
+             EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
 WINDOW_EVENT(storage, eStorage, EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
-// Not supported yet
-// WINDOW_EVENT(undo)
+WINDOW_EVENT(unhandledrejection, eUnhandledRejection,
+             EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass)
 WINDOW_EVENT(unload, eUnload,
              (EventNameType_XUL | EventNameType_SVGSVG |
               EventNameType_HTMLBodyOrFramesetOnly),
              eBasicEventClass)
 
 WINDOW_ONLY_EVENT(devicemotion, eDeviceMotion, EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(deviceorientation, eDeviceOrientation, EventNameType_None,
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -612,19 +612,21 @@ bool AudioCallbackDriver::Init() {
 
   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannels);
   mScratchBuffer =
       SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannels);
 
   output.channels = mOutputChannels;
   output.layout = CUBEB_LAYOUT_UNDEFINED;
   output.prefs = CubebUtils::GetDefaultStreamPrefs();
+#if !defined(XP_WIN)
   if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
     output.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE);
   }
+#endif
 
   uint32_t latency_frames = CubebUtils::GetCubebMSGLatencyInFrames(&output);
 
   // Macbook and MacBook air don't have enough CPU to run very low latency
   // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
   if (IsMacbookOrMacbookAir()) {
     latency_frames = std::max((uint32_t)512, latency_frames);
   }
--- a/dom/media/PeerConnection.jsm
+++ b/dom/media/PeerConnection.jsm
@@ -2194,33 +2194,32 @@ class RTCRtpReceiver {
     for (let delKey of removeKeys) {
       this._rtpSources.delete(delKey);
     }
   }
 
   _getRtpSourcesByType(type) {
     this._fetchRtpSources();
     // Only return the values from within the last 10 seconds as per the spec
-    let cutoffTime = this._rtpSourcesJsTimestamp - 10 * 1000;
-    let sources = [...this._rtpSources.values()].filter(
+    const cutoffTime = this._rtpSourcesJsTimestamp - 10 * 1000;
+    return [...this._rtpSources.values()].filter(
       (entry) => {
         return entry.sourceType == type &&
             (entry.timestamp + entry.sourceClockOffset) >= cutoffTime;
       }).map(e => {
-        let newEntry = {
+        const newEntry = {
           source: e.source,
           timestamp: e.timestamp + e.sourceClockOffset,
           audioLevel: e.audioLevel,
         };
         if (e.voiceActivityFlag !== undefined) {
           Object.assign(newEntry, {voiceActivityFlag: e.voiceActivityFlag});
         }
         return newEntry;
-      });
-      return sources;
+      }).sort((a, b) => b.timestamp - a.timestamp);
   }
 
   getContributingSources() {
     return this._getRtpSourcesByType("contributing");
   }
 
   getSynchronizationSources() {
     return this._getRtpSourcesByType("synchronization");
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -221,16 +221,17 @@ void CamerasParent::StopVideoCapture() {
           if (thread) {
             if (thread->IsRunning()) {
               thread->Stop();
             }
             delete thread;
           }
           nsresult rv = GetShutdownBarrier()->RemoveBlocker(self);
           MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+          Unused << rv;
           return NS_OK;
         }));
         if (NS_FAILED(rv)) {
           LOG(("Could not dispatch VideoCaptureThread destruction"));
         }
         return rv;
       }));
 #ifdef DEBUG
--- a/dom/media/test/test_can_play_type_mpeg.html
+++ b/dom/media/test/test_can_play_type_mpeg.html
@@ -91,17 +91,22 @@ function check_mp4(v, enabled) {
   [ "video/mp4; codecs=vp9",
     "video/mp4; codecs=\"vp9\"",
     "video/mp4; codecs=\"vp9.0\""
   ].forEach((codec) => {
     // canPlayType should support VP9 in MP4...
     check(codec, "probably");
     ok(MediaSource.isTypeSupported(codec), "VP9 in MP4 should be supported in MSE");
   });
-  check("video/mp4; codecs=\"av1\"", "probably");
+
+  if (IsAndroid()) {
+    check("video/mp4; codecs=\"av1\"", "");
+  } else {
+    check("video/mp4; codecs=\"av1\"", "probably");
+  }
 }
 
 function check_mp3(v, enabled) {
   function check(type, expected) {
     var ex = enabled ? expected : "";
     is(v.canPlayType(type), ex, type + "='" + ex + "'");
   }
 
@@ -133,16 +138,20 @@ function IsLinux() {
 function getPref(name) {
   var pref = false;
   try {
     pref = SpecialPowers.getBoolPref(name);
   } catch(ex) { }
   return pref;
 }
 
+function IsAndroid() {
+  return getAndroidVersion() >= 0;
+}
+
 function IsSupportedAndroid() {
   return getAndroidVersion() >= 14;
 }
 
 function IsJellyBeanOrLater() {
   return getAndroidVersion() >= 16;
 }
 
--- a/dom/media/tests/mochitest/test_peerConnection_audioContributingSources.html
+++ b/dom/media/tests/mochitest/test_peerConnection_audioContributingSources.html
@@ -106,16 +106,34 @@
     let source2 = contributingSources.find(c => c.source == csrc2);
     ok(source2, "third csrc was found");
     is(source2.audioLevel, 0,
       `Contributing source has audio level of 0 when RTP audio level is 127`);
     // Check caching
     is(JSON.stringify(contributingSources),
        JSON.stringify(remoteReceiver.getContributingSources()),
        "getContributingSources is cached");
+    // Check that sources are sorted in descending order by time stamp
+    const timestamp3 = SpWrap(test.pcLocal).mozGetNowInRtpSourceReferenceTime();
+    // Larger offsets are further back in time
+    const testOffsets = [3, 7, 5, 6, 1, 4];
+    for (const offset of testOffsets) {
+      SpWrap(test.pcLocal).mozInsertAudioLevelForContributingSource(
+          localReceiver,
+          offset, // Using offset for SSRC for convenience
+          timestamp3 - offset,
+          true,
+          offset);
+    }
+    const sources = localReceiver.getContributingSources();
+    const sourceOffsets = sources.map(s => s.source);
+    is(JSON.stringify(sourceOffsets),
+       JSON.stringify([...testOffsets].sort((a, b) => a - b)),
+          `Contributing sources are sorted in descending order by timestamp:`
+          + ` ${JSON.stringify(sources)}`);
   };
 
   var test;
   runNetworkTest(function(options) {
     test = new PeerConnectionTest(options);
     test.chain.insertAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW",
       [testGetContributingSources]);
     test.setMediaConstraints([{audio: true}], [{audio: true}]);
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -297,58 +297,56 @@ bool AudioBuffer::RestoreJSChannelData(J
   return true;
 }
 
 void AudioBuffer::CopyFromChannel(const Float32Array& aDestination,
                                   uint32_t aChannelNumber,
                                   uint32_t aStartInChannel, ErrorResult& aRv) {
   aDestination.ComputeLengthAndData();
 
-  uint32_t length = aDestination.Length();
   if (aChannelNumber >= NumberOfChannels() || aStartInChannel > Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
+  uint32_t count = std::min(Length() - aStartInChannel, aDestination.Length());
   JS::AutoCheckCannotGC nogc;
   JSObject* channelArray = mJSChannels[aChannelNumber];
   if (channelArray) {
     if (JS_GetTypedArrayLength(channelArray) != Length()) {
       // The array's buffer was detached.
       aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
       return;
     }
 
     bool isShared = false;
     const float* sourceData =
         JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
     // The sourceData arrays should all have originated in
     // RestoreJSChannelData, where they are created unshared.
     MOZ_ASSERT(!isShared);
-    PodMove(aDestination.Data(), sourceData + aStartInChannel,
-            std::min(Length() - aStartInChannel, length));
+    PodMove(aDestination.Data(), sourceData + aStartInChannel, count);
     return;
   }
 
   if (!mSharedChannels.IsNull()) {
     CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aStartInChannel,
-                           aDestination.Data(), length);
+                           aDestination.Data(), count);
     return;
   }
 
-  PodZero(aDestination.Data(), length);
+  PodZero(aDestination.Data(), count);
 }
 
 void AudioBuffer::CopyToChannel(JSContext* aJSContext,
                                 const Float32Array& aSource,
                                 uint32_t aChannelNumber,
                                 uint32_t aStartInChannel, ErrorResult& aRv) {
   aSource.ComputeLengthAndData();
 
-  uint32_t length = aSource.Length();
   if (aChannelNumber >= NumberOfChannels() || aStartInChannel > Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   if (!RestoreJSChannelData(aJSContext)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
@@ -357,23 +355,23 @@ void AudioBuffer::CopyToChannel(JSContex
   JS::AutoCheckCannotGC nogc;
   JSObject* channelArray = mJSChannels[aChannelNumber];
   if (JS_GetTypedArrayLength(channelArray) != Length()) {
     // The array's buffer was detached.
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
+  uint32_t count = std::min(Length() - aStartInChannel, aSource.Length());
   bool isShared = false;
   float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
   // The channelData arrays should all have originated in
   // RestoreJSChannelData, where they are created unshared.
   MOZ_ASSERT(!isShared);
-  PodMove(channelData + aStartInChannel, aSource.Data(),
-          std::min(Length() - aStartInChannel, length));
+  PodMove(channelData + aStartInChannel, aSource.Data(), count);
 }
 
 void AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
                                  JS::MutableHandle<JSObject*> aRetval,
                                  ErrorResult& aRv) {
   if (aChannel >= NumberOfChannels()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
--- a/dom/serviceworkers/test/mochitest.ini
+++ b/dom/serviceworkers/test/mochitest.ini
@@ -321,9 +321,9 @@ skip-if = verify || serviceworker_e10s
 [test_xslt.html]
 [test_async_waituntil.html]
 [test_worker_reference_gc_timeout.html]
 [test_nofetch_handler.html]
 [test_bad_script_cache.html]
 [test_file_upload.html]
 support-files = script_file_upload.js sw_file_upload.js server_file_upload.sjs
 [test_self_update_worker.html]
-skip-if = serviceworker_e10s
+skip-if = serviceworker_e10s || (toolkit == 'android' && !is_fennec)
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -150,17 +150,21 @@ interface WindowEventHandlers {
            attribute EventHandler onlanguagechange;
            attribute EventHandler onmessage;
            attribute EventHandler onmessageerror;
            attribute EventHandler onoffline;
            attribute EventHandler ononline;
            attribute EventHandler onpagehide;
            attribute EventHandler onpageshow;
            attribute EventHandler onpopstate;
+           [Func="mozilla::dom::DOMPrefs::dom_promise_rejection_events_enabled"]
+           attribute EventHandler onrejectionhandled;
            attribute EventHandler onstorage;
+           [Func="mozilla::dom::DOMPrefs::dom_promise_rejection_events_enabled"]
+           attribute EventHandler onunhandledrejection;
            attribute EventHandler onunload;
 };
 
 [NoInterfaceObject]
 interface DocumentAndElementEventHandlers {
   attribute EventHandler oncopy;
   attribute EventHandler oncut;
   attribute EventHandler onpaste;
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -21,16 +21,20 @@ interface WorkerGlobalScope : EventTarge
 
   [Throws]
   void importScripts(DOMString... urls);
 
   attribute OnErrorEventHandler onerror;
 
   attribute EventHandler onoffline;
   attribute EventHandler ononline;
+  [Func="mozilla::dom::DOMPrefs::dom_promise_rejection_events_enabled"]
+  attribute EventHandler onrejectionhandled;
+  [Func="mozilla::dom::DOMPrefs::dom_promise_rejection_events_enabled"]
+  attribute EventHandler onunhandledrejection;
   // also has additional members in a partial interface
 };
 
 WorkerGlobalScope implements GlobalCrypto;
 WorkerGlobalScope implements WindowOrWorkerGlobalScope;
 
 // Not implemented yet: bug 1072107.
 // WorkerGlobalScope implements FontFaceSource;
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -125,16 +125,18 @@ class WorkerGlobalScope : public DOMEven
 
   void GetOrigin(nsAString& aOrigin) const;
 
   void Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const;
   void Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
 
   IMPL_EVENT_HANDLER(online)
   IMPL_EVENT_HANDLER(offline)
+  IMPL_EVENT_HANDLER(rejectionhandled)
+  IMPL_EVENT_HANDLER(unhandledrejection)
 
   void Dump(const Optional<nsAString>& aString) const;
 
   Performance* GetPerformance();
 
   Performance* GetPerformanceIfExists() const { return mPerformance; }
 
   static bool IsInAutomation(JSContext* aCx, JSObject* /* unused */);
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2535,17 +2535,17 @@ nsresult EditorBase::InsertTextWithTrans
   CheckedInt<int32_t> lengthToInsert(aStringToInsert.Length());
   if (NS_WARN_IF(!lengthToInsert.isValid())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // In some cases, the node may be the anonymous div elemnt or a mozBR
   // element.  Let's try to look for better insertion point in the nearest
   // text node if there is.
-  EditorRawDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
+  EditorDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
 
   // If a neighboring text node already exists, use that
   if (!pointToInsert.IsInTextNode()) {
     nsIContent* child = nullptr;
     if (!pointToInsert.IsStartOfContainer() &&
         (child = pointToInsert.GetPreviousSiblingOfChild()) &&
         child->IsText()) {
       pointToInsert.Set(child, child->Length());
@@ -2568,32 +2568,32 @@ nsresult EditorBase::InsertTextWithTrans
       }
       pointToInsert.Set(newNode, 0);
       newOffset = lengthToInsert;
     } else {
       newOffset = lengthToInsert + pointToInsert.Offset();
       NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
     }
     nsresult rv = InsertTextIntoTextNodeWithTransaction(
-        aStringToInsert, *pointToInsert.GetContainerAsText(),
+        aStringToInsert, MOZ_KnownLive(*pointToInsert.GetContainerAsText()),
         pointToInsert.Offset());
     NS_ENSURE_SUCCESS(rv, rv);
     if (aPointAfterInsertedString) {
       aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
                                      newOffset.value());
     }
     return NS_OK;
   }
 
   if (pointToInsert.IsInTextNode()) {
     CheckedInt<int32_t> newOffset = lengthToInsert + pointToInsert.Offset();
     NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
     // we are inserting text into an existing text node.
     nsresult rv = InsertTextIntoTextNodeWithTransaction(
-        aStringToInsert, *pointToInsert.GetContainerAsText(),
+        aStringToInsert, MOZ_KnownLive(*pointToInsert.GetContainerAsText()),
         pointToInsert.Offset());
     NS_ENSURE_SUCCESS(rv, rv);
     if (aPointAfterInsertedString) {
       aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
                                      newOffset.value());
     }
     return NS_OK;
   }
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -906,16 +906,17 @@ class EditorBase : public nsIEditor,
    *
    * @param aStringToInsert     String to be inserted.
    * @param aTextNode           Text node to contain aStringToInsert.
    * @param aOffset             Offset at insertion point in aTextNode.
    * @param aSuppressIME        true if it's not a part of IME composition.
    *                            E.g., adjusting whitespaces during composition.
    *                            false, otherwise.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertTextIntoTextNodeWithTransaction(
       const nsAString& aStringToInsert, Text& aTextNode, int32_t aOffset,
       bool aSuppressIME = false);
 
   nsresult SetTextImpl(const nsAString& aString, Text& aTextNode);
 
   /**
    * DeleteNodeWithTransaction() removes aNode from the DOM tree.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1538,18 +1538,19 @@ nsresult HTMLEditRules::WillInsertText(E
             return rv;
           }
           pos++;
           currentPoint = pointAfterInsertedSpaces;
           pointToInsert = pointAfterInsertedSpaces;
         }
         // is it a return?
         else if (subStr.Equals(newlineStr)) {
-          RefPtr<Element> newBRElement = wsObj.InsertBreak(
-              *SelectionRefPtr(), currentPoint, nsIEditor::eNone);
+          RefPtr<Element> newBRElement =
+              wsObj.InsertBreak(MOZ_KnownLive(*SelectionRefPtr()), currentPoint,
+                                nsIEditor::eNone);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!newBRElement)) {
             return NS_ERROR_FAILURE;
           }
           pos++;
           if (newBRElement->GetNextSibling()) {
@@ -1848,19 +1849,19 @@ EditActionResult HTMLEditRules::WillInse
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "Failed to insert break into list item");
     return EditActionHandled();
   }
 
   if (HTMLEditUtils::IsHeader(*blockParent)) {
     // Headers: close (or split) header
-    nsresult rv =
-        ReturnInHeader(*blockParent, *atStartOfSelection.GetContainer(),
-                       atStartOfSelection.Offset());
+    nsresult rv = ReturnInHeader(
+        *blockParent, MOZ_KnownLive(*atStartOfSelection.GetContainer()),
+        atStartOfSelection.Offset());
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to handle insertParagraph in the heading element");
     return EditActionHandled();
   }
@@ -1947,18 +1948,18 @@ nsresult HTMLEditRules::InsertBRElement(
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(splitLinkNodeResult.Failed())) {
         return splitLinkNodeResult.Rv();
       }
       pointToBreak = splitLinkNodeResult.SplitPoint();
     }
-    brElement =
-        wsObj.InsertBreak(*SelectionRefPtr(), pointToBreak, nsIEditor::eNone);
+    brElement = wsObj.InsertBreak(MOZ_KnownLive(*SelectionRefPtr()),
+                                  pointToBreak, nsIEditor::eNone);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!brElement)) {
       return NS_ERROR_FAILURE;
     }
   }
 
@@ -2427,18 +2428,19 @@ nsresult HTMLEditRules::WillDeleteSelect
         NS_ASSERTION(range->GetStartContainer() == visNode,
                      "selection start not in visNode");
         NS_ASSERTION(range->GetEndContainer() == visNode,
                      "selection end not in visNode");
 
         so = range->StartOffset();
         eo = range->EndOffset();
       }
-      rv = WSRunObject::PrepareToDeleteRange(
-          &HTMLEditorRef(), address_of(visNode), &so, address_of(visNode), &eo);
+      rv = WSRunObject::PrepareToDeleteRange(MOZ_KnownLive(&HTMLEditorRef()),
+                                             address_of(visNode), &so,
+                                             address_of(visNode), &eo);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       *aHandled = true;
       rv = HTMLEditorRef().DeleteTextWithTransaction(
@@ -2572,18 +2574,18 @@ nsresult HTMLEditRules::WillDeleteSelect
                                 &otherWSType);
 
           if (otherWSType == WSType::br) {
             // Delete the <br>
             if (NS_WARN_IF(!otherNode->IsContent())) {
               return NS_ERROR_FAILURE;
             }
             nsIContent* otherContent = otherNode->AsContent();
-            rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
-                                                  otherContent);
+            rv = WSRunObject::PrepareToDeleteNode(
+                MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(otherContent));
             if (NS_WARN_IF(!CanHandleEditAction())) {
               return NS_ERROR_EDITOR_DESTROYED;
             }
             if (NS_WARN_IF(NS_FAILED(rv))) {
               return rv;
             }
             rv = HTMLEditorRef().DeleteNodeWithTransaction(*otherContent);
             if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -2598,18 +2600,18 @@ nsresult HTMLEditRules::WillDeleteSelect
         }
         // Else continue with normal delete code
       }
 
       if (NS_WARN_IF(!visNode->IsContent())) {
         return NS_ERROR_FAILURE;
       }
       // Found break or image, or hr.
-      rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
-                                            visNode->AsContent());
+      rv = WSRunObject::PrepareToDeleteNode(
+          MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(visNode->AsContent()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // Remember sibling to visnode, if any
       nsCOMPtr<nsIContent> sibling =
@@ -2872,17 +2874,17 @@ nsresult HTMLEditRules::WillDeleteSelect
     return NS_ERROR_FAILURE;
   }
   int32_t endOffset = firstRange->EndOffset();
 
   // Figure out if the endpoints are in nodes that can be merged.  Adjust
   // surrounding whitespace in preparation to delete selection.
   if (!IsPlaintextEditor()) {
     AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
-    rv = WSRunObject::PrepareToDeleteRange(&HTMLEditorRef(),
+    rv = WSRunObject::PrepareToDeleteRange(MOZ_KnownLive(&HTMLEditorRef()),
                                            address_of(startNode), &startOffset,
                                            address_of(endNode), &endOffset);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -3530,18 +3532,18 @@ EditActionResult HTMLEditRules::TryToJoi
   MOZ_DIAGNOSTIC_ASSERT(!leftBlockChild.IsSet());
 
   // Normal case.  Blocks are siblings, or at least close enough.  An example
   // of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The
   // first li and the p are not true siblings, but we still want to join them
   // if you backspace from li into p.
 
   // Adjust whitespace at block boundaries
-  nsresult rv =
-      WSRunObject::PrepareToJoinBlocks(&HTMLEditorRef(), leftBlock, rightBlock);
+  nsresult rv = WSRunObject::PrepareToJoinBlocks(
+      MOZ_KnownLive(&HTMLEditorRef()), leftBlock, rightBlock);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditActionIgnored(rv);
   }
   // Do br adjustment.
   nsCOMPtr<Element> brNode =
@@ -7673,17 +7675,17 @@ nsresult HTMLEditRules::ReturnInHeader(E
 
   // Remember where the header is
   nsCOMPtr<nsINode> headerParent = aHeader.GetParentNode();
   int32_t offset = headerParent ? headerParent->ComputeIndexOf(&aHeader) : -1;
 
   // Get ws code to adjust any ws
   nsCOMPtr<nsINode> node = &aNode;
   nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(
-      &HTMLEditorRef(), address_of(node), &aOffset);
+      MOZ_KnownLive(&HTMLEditorRef()), address_of(node), &aOffset);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (NS_WARN_IF(!node->IsContent())) {
     return NS_ERROR_FAILURE;
@@ -7995,17 +7997,17 @@ nsresult HTMLEditRules::SplitParagraph(
     nsIContent* aNextBRNode) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // split para
   // get ws code to adjust any ws
   nsCOMPtr<nsINode> selNode = aStartOfRightNode.GetContainer();
   int32_t selOffset = aStartOfRightNode.Offset();
   nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(
-      &HTMLEditorRef(), address_of(selNode), &selOffset);
+      MOZ_KnownLive(&HTMLEditorRef()), address_of(selNode), &selOffset);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (NS_WARN_IF(!selNode->IsContent())) {
     return NS_ERROR_FAILURE;
@@ -8191,17 +8193,17 @@ nsresult HTMLEditRules::ReturnInListItem
     }
     return NS_OK;
   }
 
   // Else we want a new list item at the same list level.  Get ws code to
   // adjust any ws.
   nsCOMPtr<nsINode> selNode = &aNode;
   nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(
-      &HTMLEditorRef(), address_of(selNode), &aOffset);
+      MOZ_KnownLive(&HTMLEditorRef()), address_of(selNode), &aOffset);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (NS_WARN_IF(!selNode->IsContent())) {
     return NS_ERROR_FAILURE;
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -653,46 +653,48 @@ class HTMLEditRules : public TextEditRul
    * paragraph separator).
    *
    * @param aHeader             The heading element to be split.
    * @param aNode               Typically, Selection start container,
    *                            where to be split.
    * @param aOffset             Typically, Selection start offset in the
    *                            start container, where to be split.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ReturnInHeader(Element& aHeader, nsINode& aNode,
                                        int32_t aOffset);
 
   /**
    * ReturnInParagraph() does the right thing for Enter key press or
    * 'insertParagraph' command in aParentDivOrP.  aParentDivOrP will be
    * split at start of first selection range.
    *
    * @param aParentDivOrP   The parent block.  This must be <p> or <div>
    *                        element.
    * @return                Returns with NS_OK if this doesn't meat any
    *                        unexpected situation.  If this method tries to
    *                        split the paragraph, marked as handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE EditActionResult ReturnInParagraph(Element& aParentDivOrP);
 
   /**
    * SplitParagraph() splits the parent block, aPara, at aSelNode - aOffset.
    *
    * @param aParentDivOrP       The parent block to be split.  This must be <p>
    *                            or <div> element.
    * @param aStartOfRightNode   The point to be start of right node after
    *                            split.  This must be descendant of
    *                            aParentDivOrP.
    * @param aNextBRNode         Next <br> node if there is.  Otherwise, nullptr.
    *                            If this is not nullptr, the <br> node may be
    *                            removed.
    */
   template <typename PT, typename CT>
-  MOZ_MUST_USE nsresult SplitParagraph(
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult SplitParagraph(
       Element& aParentDivOrP,
       const EditorDOMPointBase<PT, CT>& aStartOfRightNode, nsIContent* aBRNode);
 
   /**
    * ReturnInListItem() handles insertParagraph command (i.e., handling
    * Enter key press) in a list item element.
    *
    * @param aListItem           The list item which has the following point.
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -284,18 +284,19 @@ nsresult WSRunObject::InsertText(Documen
       // would become significant after text inserted.
       nsresult rv = DeleteRange(pointToInsert, afterRun->EndPoint());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else if (afterRun->mType == WSType::normalWS) {
       // Try to change an nbsp to a space, if possible, just to prevent nbsp
       // proliferation
-      nsresult rv = CheckLeadingNBSP(afterRun, pointToInsert.GetContainer(),
-                                     pointToInsert.Offset());
+      nsresult rv = CheckLeadingNBSP(
+          afterRun, MOZ_KnownLive(pointToInsert.GetContainer()),
+          pointToInsert.Offset());
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Handle any changes needed to ws run before inserted text
     if (!beforeRun || beforeRun->mType & WSType::leadingWS) {
       // Don't need to do anything.  Just insert text.  ws won't change.
     } else if (beforeRun->mType & WSType::trailingWS) {
       // Need to delete the trailing ws that is before insertion point, because
@@ -408,18 +409,18 @@ nsresult WSRunObject::DeleteWSBackward()
         eBoth, EditorRawDOMPoint(point.mTextNode, point.mOffset + 1),
         getter_AddRefs(startNodeText), &startOffset,
         getter_AddRefs(endNodeText), &endOffset);
 
     // adjust surrounding ws
     nsCOMPtr<nsINode> startNode = startNodeText.get();
     nsCOMPtr<nsINode> endNode = endNodeText.get();
     nsresult rv = WSRunObject::PrepareToDeleteRange(
-        mHTMLEditor, address_of(startNode), &startOffset, address_of(endNode),
-        &endOffset);
+        MOZ_KnownLive(mHTMLEditor), address_of(startNode), &startOffset,
+        address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
     rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
                      EditorRawDOMPoint(endNode, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -427,18 +428,18 @@ nsresult WSRunObject::DeleteWSBackward()
   }
 
   if (point.mChar == kNBSP) {
     nsCOMPtr<nsINode> node(point.mTextNode);
     // adjust surrounding ws
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset + 1;
     nsresult rv = WSRunObject::PrepareToDeleteRange(
-        mHTMLEditor, address_of(node), &startOffset, address_of(node),
-        &endOffset);
+        MOZ_KnownLive(mHTMLEditor), address_of(node), &startOffset,
+        address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
     rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
                      EditorRawDOMPoint(node, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -471,18 +472,18 @@ nsresult WSRunObject::DeleteWSForward() 
     GetASCIIWhitespacesBounds(
         eBoth, EditorRawDOMPoint(point.mTextNode, point.mOffset + 1),
         getter_AddRefs(startNodeText), &startOffset,
         getter_AddRefs(endNodeText), &endOffset);
 
     // Adjust surrounding ws
     nsCOMPtr<nsINode> startNode(startNodeText), endNode(endNodeText);
     nsresult rv = WSRunObject::PrepareToDeleteRange(
-        mHTMLEditor, address_of(startNode), &startOffset, address_of(endNode),
-        &endOffset);
+        MOZ_KnownLive(mHTMLEditor), address_of(startNode), &startOffset,
+        address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
     rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
                      EditorRawDOMPoint(endNode, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -490,18 +491,18 @@ nsresult WSRunObject::DeleteWSForward() 
   }
 
   if (point.mChar == kNBSP) {
     nsCOMPtr<nsINode> node(point.mTextNode);
     // Adjust surrounding ws
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset + 1;
     nsresult rv = WSRunObject::PrepareToDeleteRange(
-        mHTMLEditor, address_of(node), &startOffset, address_of(node),
-        &endOffset);
+        MOZ_KnownLive(mHTMLEditor), address_of(node), &startOffset,
+        address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
     rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
                      EditorRawDOMPoint(node, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -1496,17 +1497,18 @@ nsresult WSRunObject::InsertNBSPAndRemov
   if (NS_WARN_IF(!mHTMLEditor)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   // First, insert an NBSP.
   AutoTransactionsConserveSelection dontChangeMySelection(*htmlEditor);
   nsresult rv = htmlEditor->InsertTextIntoTextNodeWithTransaction(
-      nsDependentSubstring(&kNBSP, 1), *aPoint.mTextNode, aPoint.mOffset, true);
+      nsDependentSubstring(&kNBSP, 1), MOZ_KnownLive(*aPoint.mTextNode),
+      aPoint.mOffset, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Now, the text node may have been modified by mutation observer.
   // So, the NBSP may have gone.
   if (aPoint.mTextNode->TextDataLength() <= aPoint.mOffset ||
       aPoint.mTextNode->GetText()->CharAt(aPoint.mOffset) != kNBSP) {
@@ -1828,17 +1830,17 @@ nsresult WSRunObject::CheckTrailingNBSPO
         rightCheck = true;
       }
     }
     if (leftCheck && rightCheck) {
       // Now replace nbsp with space.  First, insert a space
       AutoTransactionsConserveSelection dontChangeMySelection(*htmlEditor);
       nsAutoString spaceStr(char16_t(32));
       nsresult rv = htmlEditor->InsertTextIntoTextNodeWithTransaction(
-          spaceStr, *thePoint.mTextNode, thePoint.mOffset, true);
+          spaceStr, MOZ_KnownLive(*thePoint.mTextNode), thePoint.mOffset, true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // Finally, delete that nbsp
       rv = DeleteRange(
           EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 1),
           EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 2));
@@ -1921,17 +1923,17 @@ nsresult WSRunObject::ReplacePreviousNBS
     return NS_ERROR_NOT_INITIALIZED;
   }
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   // First, insert a space before the previous NBSP.
   AutoTransactionsConserveSelection dontChangeMySelection(*htmlEditor);
   nsAutoString spaceStr(char16_t(32));
   nsresult rv = htmlEditor->InsertTextIntoTextNodeWithTransaction(
-      spaceStr, *thePoint.mTextNode, thePoint.mOffset, true);
+      spaceStr, MOZ_KnownLive(*thePoint.mTextNode), thePoint.mOffset, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Finally, delete the previous NBSP.
   rv = DeleteRange(EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 1),
                    EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 2));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1968,17 +1970,17 @@ nsresult WSRunObject::CheckLeadingNBSP(W
       return NS_ERROR_NOT_INITIALIZED;
     }
     RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
     // First, insert a space
     AutoTransactionsConserveSelection dontChangeMySelection(*htmlEditor);
     nsAutoString spaceStr(char16_t(32));
     nsresult rv = htmlEditor->InsertTextIntoTextNodeWithTransaction(
-        spaceStr, *thePoint.mTextNode, thePoint.mOffset, true);
+        spaceStr, MOZ_KnownLive(*thePoint.mTextNode), thePoint.mOffset, true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Finally, delete that nbsp
     rv = DeleteRange(
         EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 1),
         EditorRawDOMPoint(thePoint.mTextNode, thePoint.mOffset + 2));
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -179,44 +179,48 @@ class MOZ_STACK_CLASS WSRunObject final 
   // location relative to a block node.
   static nsresult ScrubBlockBoundary(HTMLEditor* aHTMLEditor,
                                      BlockBoundary aBoundary, nsINode* aBlock,
                                      int32_t aOffset = -1);
 
   // PrepareToJoinBlocks fixes up ws at the end of aLeftBlock and the
   // beginning of aRightBlock in preperation for them to be joined.  Example
   // of fixup: trailingws in aLeftBlock needs to be removed.
+  MOZ_CAN_RUN_SCRIPT
   static nsresult PrepareToJoinBlocks(HTMLEditor* aHTMLEditor,
                                       dom::Element* aLeftBlock,
                                       dom::Element* aRightBlock);
 
   // PrepareToDeleteRange fixes up ws before {aStartNode,aStartOffset}
   // and after {aEndNode,aEndOffset} in preperation for content
   // in that range to be deleted.  Note that the nodes and offsets
   // are adjusted in response to any dom changes we make while
   // adjusting ws.
   // example of fixup: trailingws before {aStartNode,aStartOffset}
   //                   needs to be removed.
+  MOZ_CAN_RUN_SCRIPT
   static nsresult PrepareToDeleteRange(HTMLEditor* aHTMLEditor,
                                        nsCOMPtr<nsINode>* aStartNode,
                                        int32_t* aStartOffset,
                                        nsCOMPtr<nsINode>* aEndNode,
                                        int32_t* aEndOffset);
 
   // PrepareToDeleteNode fixes up ws before and after aContent in preparation
   // for aContent to be deleted.  Example of fixup: trailingws before
   // aContent needs to be removed.
+  MOZ_CAN_RUN_SCRIPT
   static nsresult PrepareToDeleteNode(HTMLEditor* aHTMLEditor,
                                       nsIContent* aContent);
 
   // PrepareToSplitAcrossBlocks fixes up ws before and after
   // {aSplitNode,aSplitOffset} in preparation for a block parent to be split.
   // Note that the aSplitNode and aSplitOffset are adjusted in response to
   // any DOM changes we make while adjusting ws.  Example of fixup: normalws
   // before {aSplitNode,aSplitOffset} needs to end with nbsp.
+  MOZ_CAN_RUN_SCRIPT
   static nsresult PrepareToSplitAcrossBlocks(HTMLEditor* aHTMLEditor,
                                              nsCOMPtr<nsINode>* aSplitNode,
                                              int32_t* aSplitOffset);
 
   /**
    * InsertBreak() inserts a <br> node at (before) aPointToInsert and delete
    * unnecessary whitespaces around there and/or replaces whitespaces with
    * non-breaking spaces.  Note that if the point is in a text node, the
@@ -231,17 +235,17 @@ class MOZ_STACK_CLASS WSRunObject final 
    *                        If eNext, selection will be collapsed after the
    *                        <br> element.
    *                        If ePrevious, selection will be collapsed at the
    *                        <br> element.
    * @return                The new <br> node.  If failed to create new <br>
    *                        node, returns nullptr.
    */
   template <typename PT, typename CT>
-  already_AddRefed<dom::Element> InsertBreak(
+  MOZ_CAN_RUN_SCRIPT already_AddRefed<dom::Element> InsertBreak(
       Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert,
       nsIEditor::EDirection aSelect);
 
   /**
    * InsertText() inserts aStringToInsert to mScanStartPoint and makes any
    * needed adjustments to white spaces around both mScanStartPoint and
    * mScanEndPoint. E.g., trailing white spaces before mScanStartPoint needs to
    * be removed.  This calls EditorBase::InsertTextWithTransaction() after
@@ -262,22 +266,22 @@ class MOZ_STACK_CLASS WSRunObject final 
   MOZ_CAN_RUN_SCRIPT nsresult
   InsertText(dom::Document& aDocument, const nsAString& aStringToInsert,
              EditorRawDOMPoint* aPointAfterInsertedString = nullptr);
 
   // DeleteWSBackward deletes a single visible piece of ws before the ws
   // point (the point to create the wsRunObject, passed to its constructor).
   // It makes any needed conversion to adjacent ws to retain its
   // significance.
-  nsresult DeleteWSBackward();
+  MOZ_CAN_RUN_SCRIPT nsresult DeleteWSBackward();
 
   // DeleteWSForward deletes a single visible piece of ws after the ws point
   // (the point to create the wsRunObject, passed to its constructor).  It
   // makes any needed conversion to adjacent ws to retain its significance.
-  nsresult DeleteWSForward();
+  MOZ_CAN_RUN_SCRIPT nsresult DeleteWSForward();
 
   // PriorVisibleNode() returns the first piece of visible thing before aPoint.
   // If there is no visible ws qualifying it returns what is before the ws run.
   // If outVisNode and/or outvisOffset is unused, callers can use nullptr.
   // Note that {outVisNode,outVisOffset} is set to just AFTER the visible
   // object. Also outVisOffset might be invalid offset unless outVisNode is
   // start reason node.
   template <typename PT, typename CT>
@@ -305,17 +309,17 @@ class MOZ_STACK_CLASS WSRunObject final 
   template <typename PT, typename CT>
   void NextVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
                        WSType* outType) const {
     NextVisibleNode(aPoint, nullptr, nullptr, outType);
   }
 
   // AdjustWhitespace examines the ws object for nbsp's that can
   // be safely converted to regular ascii space and converts them.
-  nsresult AdjustWhitespace();
+  MOZ_CAN_RUN_SCRIPT nsresult AdjustWhitespace();
 
  protected:
   // WSFragment represents a single run of ws (all leadingws, or all normalws,
   // or all trailingws, or all leading+trailingws).  Note that this single run
   // may still span multiple nodes.
   struct WSFragment final {
     nsCOMPtr<nsINode> mStartNode;  // node where ws run starts
     nsCOMPtr<nsINode> mEndNode;    // node where ws run ends
@@ -365,18 +369,18 @@ class MOZ_STACK_CLASS WSRunObject final 
   void MakeSingleWSRun(WSType aType);
   nsIContent* GetPreviousWSNodeInner(nsINode* aStartNode,
                                      nsINode* aBlockParent);
   nsIContent* GetPreviousWSNode(const EditorDOMPoint& aPoint,
                                 nsINode* aBlockParent);
   nsIContent* GetNextWSNodeInner(nsINode* aStartNode, nsINode* aBlockParent);
   nsIContent* GetNextWSNode(const EditorDOMPoint& aPoint,
                             nsINode* aBlockParent);
-  nsresult PrepareToDeleteRangePriv(WSRunObject* aEndObject);
-  nsresult PrepareToSplitAcrossBlocksPriv();
+  MOZ_CAN_RUN_SCRIPT nsresult PrepareToDeleteRangePriv(WSRunObject* aEndObject);
+  MOZ_CAN_RUN_SCRIPT nsresult PrepareToSplitAcrossBlocksPriv();
 
   /**
    * DeleteRange() removes the range between aStartPoint and aEndPoint.
    * When aStartPoint and aEndPoint are same point, does nothing.
    * When aStartPoint and aEndPoint are in same text node, removes characters
    * between them.
    * When aStartPoint is in a text node, removes the text data after the point.
    * When aEndPoint is in a text node, removes the text data before the point.
@@ -417,16 +421,17 @@ class MOZ_STACK_CLASS WSRunObject final 
   template <typename PT, typename CT>
   WSPoint GetPreviousCharPointInternal(
       const EditorDOMPointBase<PT, CT>& aPoint) const;
 
   /**
    * InsertNBSPAndRemoveFollowingASCIIWhitespaces() inserts an NBSP first.
    * Then, if following characters are ASCII whitespaces, will remove them.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertNBSPAndRemoveFollowingASCIIWhitespaces(WSPoint aPoint);
 
   /**
    * GetASCIIWhitespacesBounds() retrieves whitespaces before and/or after the
    * point specified by aNode and aOffset.
    *
    * @param aDir            Specify eBefore if you want to scan text backward.
    *                        Specify eAfter if you want to scan text forward.
@@ -470,30 +475,31 @@ class MOZ_STACK_CLASS WSRunObject final 
    *                      if aPoint is before the first run, returns nullptr.
    *                      if aPoint is after the last run, returns the last run.
    */
   template <typename PT, typename CT>
   WSFragment* FindNearestRun(const EditorDOMPointBase<PT, CT>& aPoint,
                              bool aForward) const;
 
   char16_t GetCharAt(dom::Text* aTextNode, int32_t aOffset) const;
-  nsresult CheckTrailingNBSPOfRun(WSFragment* aRun);
+  MOZ_CAN_RUN_SCRIPT nsresult CheckTrailingNBSPOfRun(WSFragment* aRun);
 
   /**
    * ReplacePreviousNBSPIfUnncessary() replaces previous character of aPoint
    * if it's a NBSP and it's unnecessary.
    *
    * @param aRun        Current text run.  aPoint must be in this run.
    * @param aPoint      Current insertion point.  Its previous character is
    *                    unnecessary NBSP will be checked.
    */
   template <typename PT, typename CT>
-  nsresult ReplacePreviousNBSPIfUnncessary(
+  MOZ_CAN_RUN_SCRIPT nsresult ReplacePreviousNBSPIfUnncessary(
       WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode, int32_t aOffset);
 
   nsresult Scrub();
   bool IsBlockNode(nsINode* aNode);
 
   EditorRawDOMPoint StartPoint() const {
     return EditorRawDOMPoint(mStartNode, mStartOffset);
   }
--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -99,16 +99,17 @@ void CompositorVsyncScheduler::Destroy()
     return;
   }
   UnobserveVsync();
   mVsyncObserver->Destroy();
   mVsyncObserver = nullptr;
 
   mCompositeRequestedAt = TimeStamp();
   CancelCurrentCompositeTask();
+  CancelCurrentVRTask();
 }
 
 void CompositorVsyncScheduler::PostCompositeTask(
     VsyncId aId, TimeStamp aCompositeTimestamp) {
   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
   if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
     RefPtr<CancelableRunnable> task =
         NewCancelableRunnableMethod<VsyncId, TimeStamp>(
@@ -117,17 +118,17 @@ void CompositorVsyncScheduler::PostCompo
     mCurrentCompositeTask = task;
     ScheduleTask(task.forget());
   }
 }
 
 void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) {
   MonitorAutoLock lockVR(mCurrentVRTaskMonitor);
   if (mCurrentVRTask == nullptr && CompositorThreadHolder::Loop()) {
-    RefPtr<Runnable> task = NewRunnableMethod<TimeStamp>(
+    RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
         "layers::CompositorVsyncScheduler::DispatchVREvents", this,
         &CompositorVsyncScheduler::DispatchVREvents, aTimestamp);
     mCurrentVRTask = task;
     CompositorThreadHolder::Loop()->PostDelayedTask(task.forget(), 0);
   }
 }
 
 void CompositorVsyncScheduler::ScheduleComposition() {
@@ -185,16 +186,26 @@ bool CompositorVsyncScheduler::NotifyVsy
 #else
   PostCompositeTask(aVsync.mId, aVsync.mTime);
 #endif  // defined(MOZ_WIDGET_ANDROID)
 
   PostVRTask(aVsync.mTime);
   return true;
 }
 
+void CompositorVsyncScheduler::CancelCurrentVRTask() {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+             NS_IsMainThread());
+  MonitorAutoLock lock(mCurrentVRTaskMonitor);
+  if (mCurrentVRTask) {
+    mCurrentVRTask->Cancel();
+    mCurrentVRTask = nullptr;
+  }
+}
+
 void CompositorVsyncScheduler::CancelCurrentCompositeTask() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
              NS_IsMainThread());
   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
   if (mCurrentCompositeTask) {
     mCurrentCompositeTask->Cancel();
     mCurrentCompositeTask = nullptr;
   }
--- a/gfx/layers/ipc/CompositorVsyncScheduler.h
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -115,16 +115,21 @@ class CompositorVsyncScheduler {
   // Post a task to run Composite() on the compositor thread, if there isn't
   // such a task already queued. Can be called from any thread.
   void PostCompositeTask(VsyncId aId, TimeStamp aCompositeTimestamp);
 
   // Post a task to run DispatchVREvents() on the VR thread, if there isn't
   // such a task already queued. Can be called from any thread.
   void PostVRTask(TimeStamp aTimestamp);
 
+  /**
+   * Cancel any VR task that has been scheduled but hasn't run yet.
+   */
+  void CancelCurrentVRTask();
+
   // This gets run at vsync time and "does" a composite (which really means
   // update internal state and call the owner to do the composite).
   void Composite(VsyncId aId, TimeStamp aVsyncTimestamp);
 
   void ObserveVsync();
   void UnobserveVsync();
 
   void DispatchVREvents(TimeStamp aVsyncTimestamp);
@@ -154,15 +159,15 @@ class CompositorVsyncScheduler {
   int32_t mVsyncNotificationsSkipped;
   widget::CompositorWidget* mWidget;
   RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
 
   mozilla::Monitor mCurrentCompositeTaskMonitor;
   RefPtr<CancelableRunnable> mCurrentCompositeTask;
 
   mozilla::Monitor mCurrentVRTaskMonitor;
-  RefPtr<Runnable> mCurrentVRTask;
+  RefPtr<CancelableRunnable> mCurrentVRTask;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif  // mozilla_layers_CompositorVsyncScheduler_h
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1038,18 +1038,19 @@ impl AlphaBatchBuilder {
                             };
 
                             let raster_config = pic
                                 .raster_config
                                 .as_ref()
                                 .expect("BUG: 3d primitive was not assigned a surface");
                             let (uv_rect_address, _) = render_tasks.resolve_surface(
                                 ctx.surfaces[raster_config.surface_index.0]
-                                    .surface
-                                    .expect("BUG: no surface"),
+                                    .render_tasks
+                                    .expect("BUG: no surface")
+                                    .root,
                                 gpu_cache,
                             );
 
                             let prim_header_index = prim_headers.push(&prim_header, z_id, [
                                 uv_rect_address.as_int(),
                                 if raster_config.establishes_raster_root { 1 } else { 0 },
                                 0,
                                 clip_task_address.0 as i32,
@@ -1092,17 +1093,20 @@ impl AlphaBatchBuilder {
                             BrushFlags::empty()
                         };
 
                         let clip_task_address = ctx.get_prim_clip_task_address(
                             prim_info.clip_task_index,
                             render_tasks,
                         ).unwrap_or(OPAQUE_TASK_ADDRESS);
 
-                        let surface = ctx.surfaces[raster_config.surface_index.0].surface;
+                        let surface = ctx
+                            .surfaces[raster_config.surface_index.0]
+                            .render_tasks
+                            .map(|s| s.root);
 
                         match raster_config.composite_mode {
                             PictureCompositeMode::TileCache { .. } => {
                                 let tile_cache = picture.tile_cache.as_ref().unwrap();
 
                                 // If the tile cache is disabled, just recurse into the
                                 // picture like a normal pass-through picture, adding
                                 // any child primitives into the parent surface batches.
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -898,16 +898,19 @@ pub struct Capabilities {
     /// Whether we are able to use glBlitFramebuffers with the draw fbo
     /// bound to a non-0th layer of a texture array. This is buggy on
     /// Adreno devices.
     pub supports_blit_to_texture_array: bool,
     /// Whether we can use the pixel local storage functionality that
     /// is available on some mobile GPUs. This allows fast access to
     /// the per-pixel tile memory.
     pub supports_pixel_local_storage: bool,
+    /// Whether KHR_debug is supported for getting debug messages from
+    /// the driver.
+    pub supports_khr_debug: bool,
 }
 
 #[derive(Clone, Debug)]
 pub enum ShaderError {
     Compilation(String, String), // name, error message
     Link(String, String),        // name, error message
 }
 
@@ -1129,26 +1132,16 @@ impl<'a> From<DrawTarget<'a>> for ReadTa
 impl Device {
     pub fn new(
         mut gl: Rc<gl::Gl>,
         resource_override_path: Option<PathBuf>,
         upload_method: UploadMethod,
         cached_programs: Option<Rc<ProgramCache>>,
         allow_pixel_local_storage_support: bool,
     ) -> Device {
-        // On debug builds, assert that each GL call is error-free. We don't do
-        // this on release builds because the synchronous call can stall the
-        // pipeline.
-        if cfg!(debug_assertions) {
-            gl = gl::ErrorReactingGl::wrap(gl, |gl, name, code| {
-                Self::echo_driver_messages(gl);
-                panic!("Caught GL error {:x} at {}", code, name);
-            });
-        }
-
         let mut max_texture_size = [0];
         let mut max_texture_layers = [0];
         unsafe {
             gl.get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_texture_size);
             gl.get_integer_v(gl::MAX_ARRAY_TEXTURE_LAYERS, &mut max_texture_layers);
         }
 
         let max_texture_size = max_texture_size[0];
@@ -1160,16 +1153,29 @@ impl Device {
             gl.get_integer_v(gl::NUM_EXTENSIONS, &mut extension_count);
         }
         let extension_count = extension_count[0] as gl::GLuint;
         let mut extensions = Vec::new();
         for i in 0 .. extension_count {
             extensions.push(gl.get_string_i(gl::EXTENSIONS, i));
         }
 
+        // On debug builds, assert that each GL call is error-free. We don't do
+        // this on release builds because the synchronous call can stall the
+        // pipeline.
+        let supports_khr_debug = supports_extension(&extensions, "GL_KHR_debug");
+        if cfg!(debug_assertions) {
+            gl = gl::ErrorReactingGl::wrap(gl, move |gl, name, code| {
+                if supports_khr_debug {
+                    Self::log_driver_messages(gl);
+                }
+                panic!("Caught GL error {:x} at {}", code, name);
+            });
+        }
+
         // Our common-case image data in Firefox is BGRA, so we make an effort
         // to use BGRA as the internal texture storage format to avoid the need
         // to swizzle during upload. Currently we only do this on GLES (and thus
         // for Windows, via ANGLE).
         //
         // On Mac, Apple docs [1] claim that BGRA is a more efficient internal
         // format, so we may want to consider doing that at some point, since it
         // would give us both a more efficient internal format and avoid the
@@ -1280,16 +1286,17 @@ impl Device {
             upload_method,
             inside_frame: false,
 
             capabilities: Capabilities {
                 supports_multisampling: false, //TODO
                 supports_copy_image_sub_data,
                 supports_blit_to_texture_array,
                 supports_pixel_local_storage,
+                supports_khr_debug,
             },
 
             bgra_format_internal,
             bgra_format_external,
 
             depth_targets: FastHashMap::default(),
 
             bound_textures: [0; 16],
@@ -3088,17 +3095,23 @@ impl Device {
 
         if enable {
             self.gl.enable(gl::SHADER_PIXEL_LOCAL_STORAGE_EXT);
         } else {
             self.gl.disable(gl::SHADER_PIXEL_LOCAL_STORAGE_EXT);
         }
     }
 
-    pub fn echo_driver_messages(gl: &gl::Gl) {
+    pub fn echo_driver_messages(&self) {
+        if self.capabilities.supports_khr_debug {
+            Device::log_driver_messages(self.gl());
+        }
+    }
+
+    fn log_driver_messages(gl: &gl::Gl) {
         for msg in gl.get_debug_messages() {
             let level = match msg.severity {
                 gl::DEBUG_SEVERITY_HIGH => Level::Error,
                 gl::DEBUG_SEVERITY_MEDIUM => Level::Warn,
                 gl::DEBUG_SEVERITY_LOW => Level::Info,
                 gl::DEBUG_SEVERITY_NOTIFICATION => Level::Debug,
                 _ => Level::Trace,
             };
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -10,30 +10,31 @@ use crate::clip_scroll_tree::{ClipScroll
 use crate::display_list_flattener::{DisplayListFlattener};
 use crate::gpu_cache::{GpuCache, GpuCacheHandle};
 use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use crate::hit_test::{HitTester, HitTestingScene};
 #[cfg(feature = "replay")]
 use crate::hit_test::HitTestingSceneStats;
 use crate::internal_types::{FastHashMap, PlaneSplitter};
 use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex};
-use crate::picture::{RetainedTiles, TileCache, DirtyRegion};
+use crate::picture::{RetainedTiles, TileCache, DirtyRegion, SurfaceRenderTasks};
 use crate::prim_store::{PrimitiveStore, SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
 #[cfg(feature = "replay")]
 use crate::prim_store::{PrimitiveStoreStats};
 use crate::profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use crate::render_backend::{DataStores, FrameStamp};
 use crate::render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree, RenderTaskTreeCounters};
 use crate::resource_cache::{ResourceCache};
 use crate::scene::{ScenePipeline, SceneProperties};
 use crate::scene_builder::DocumentStats;
 use crate::segment::SegmentBuilder;
 use std::{f32, mem};
 use std::sync::Arc;
 use crate::tiling::{Frame, RenderPassKind, RenderTargetContext, RenderTarget};
+use crate::util::MaxRect;
 
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ChasePrimitive {
     Nothing,
     Id(PrimitiveDebugId),
@@ -439,32 +440,36 @@ impl FrameBuilder {
             global_device_pixel_scale,
         );
 
         let root_render_task_id = frame_state.render_tasks.add(root_render_task);
         frame_state
             .surfaces
             .first_mut()
             .unwrap()
-            .surface = Some(root_render_task_id);
+            .render_tasks = Some(SurfaceRenderTasks {
+                root: root_render_task_id,
+                port: root_render_task_id,
+            });
 
         // Push a default dirty region which culls primitives
         // against the screen world rect, in absence of any
         // other dirty regions.
         let mut default_dirty_region = DirtyRegion::new();
         default_dirty_region.push(
             frame_context.screen_world_rect,
         );
         frame_state.push_dirty_region(default_dirty_region);
 
         let (pic_context, mut pic_state, mut prim_list) = self
             .prim_store
             .pictures[self.root_pic_index.0]
             .take_context(
                 self.root_pic_index,
+                WorldRect::max_rect(),
                 root_spatial_node_index,
                 root_spatial_node_index,
                 ROOT_SURFACE_INDEX,
                 &mut frame_state,
                 &frame_context,
             )
             .unwrap();
 
@@ -487,27 +492,16 @@ impl FrameBuilder {
             prim_list,
             pic_context,
             pic_state,
             &mut frame_state,
         );
 
         frame_state.pop_dirty_region();
 
-        let child_tasks = frame_state
-            .surfaces[ROOT_SURFACE_INDEX.0]
-            .take_render_tasks();
-
-        for child_task_id in child_tasks {
-            frame_state.render_tasks.add_dependency(
-                root_render_task_id,
-                child_task_id,
-            );
-        }
-
         Some(root_render_task_id)
     }
 
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         stamp: FrameStamp,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1775,16 +1775,27 @@ impl<'a> PictureUpdateState<'a> {
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct SurfaceIndex(pub usize);
 
 pub const ROOT_SURFACE_INDEX: SurfaceIndex = SurfaceIndex(0);
 
+#[derive(Debug, Copy, Clone)]
+pub struct SurfaceRenderTasks {
+    /// The root of the render task chain for this surface. This
+    /// is attached to parent tasks, and also the surface that
+    /// gets added during batching.
+    pub root: RenderTaskId,
+    /// The port of the render task change for this surface. This
+    /// is where child tasks for this surface get attached to.
+    pub port: RenderTaskId,
+}
+
 /// Information about an offscreen surface. For now,
 /// it contains information about the size and coordinate
 /// system of the surface. In the future, it will contain
 /// information about the contents of the surface, which
 /// will allow surfaces to be cached / retained between
 /// frames and display lists.
 #[derive(Debug)]
 pub struct SurfaceInfo {
@@ -1794,19 +1805,17 @@ pub struct SurfaceInfo {
     /// Helper structs for mapping local rects in different
     /// coordinate systems into the surface coordinates.
     pub map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
     /// Defines the positioning node for the surface itself,
     /// and the rasterization root for this surface.
     pub raster_spatial_node_index: SpatialNodeIndex,
     pub surface_spatial_node_index: SpatialNodeIndex,
     /// This is set when the render task is created.
-    pub surface: Option<RenderTaskId>,
-    /// A list of render tasks that are dependencies of this surface.
-    pub tasks: Vec<RenderTaskId>,
+    pub render_tasks: Option<SurfaceRenderTasks>,
     /// How much the local surface rect should be inflated (for blur radii).
     pub inflation_factor: f32,
     /// The device pixel ratio specific to this surface.
     pub device_pixel_scale: DevicePixelScale,
     /// If true, subpixel AA rendering can be used on this surface.
     pub allow_subpixel_aa: bool,
 }
 
@@ -1834,31 +1843,24 @@ impl SurfaceInfo {
         let map_local_to_surface = SpaceMapper::new(
             surface_spatial_node_index,
             pic_bounds,
         );
 
         SurfaceInfo {
             rect: PictureRect::zero(),
             map_local_to_surface,
-            surface: None,
+            render_tasks: None,
             raster_spatial_node_index,
             surface_spatial_node_index,
-            tasks: Vec::new(),
             inflation_factor,
             device_pixel_scale,
             allow_subpixel_aa,
         }
     }
-
-    /// Take the set of child render tasks for this surface. This is
-    /// used when constructing the render task tree.
-    pub fn take_render_tasks(&mut self) -> Vec<RenderTaskId> {
-        mem::replace(&mut self.tasks, Vec::new())
-    }
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct RasterConfig {
     /// How this picture should be composited into
     /// the parent surface.
     pub composite_mode: PictureCompositeMode,
@@ -2147,17 +2149,17 @@ impl Default for PictureOptions {
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct PicturePrimitive {
     /// List of primitives, and associated info for this picture.
     pub prim_list: PrimitiveList,
 
     #[cfg_attr(feature = "capture", serde(skip))]
-    pub state: Option<(PictureState, PictureContext)>,
+    pub state: Option<PictureState>,
 
     /// The pipeline that the primitives on this picture belong to.
     pub pipeline_id: PipelineId,
 
     /// If true, apply the local clip rect to primitive drawn
     /// in this picture.
     pub apply_local_clip_rect: bool,
     /// If false and transform ends up showing the back of the picture,
@@ -2345,19 +2347,20 @@ impl PicturePrimitive {
             options,
             segments_are_valid: false,
         }
     }
 
     pub fn take_context(
         &mut self,
         pic_index: PictureIndex,
+        clipped_prim_bounding_rect: WorldRect,
         surface_spatial_node_index: SpatialNodeIndex,
         raster_spatial_node_index: SpatialNodeIndex,
-        surface_index: SurfaceIndex,
+        parent_surface_index: SurfaceIndex,
         frame_state: &mut FrameBuildingState,
         frame_context: &FrameBuildingContext,
     ) -> Option<(PictureContext, PictureState, PrimitiveList)> {
         if !self.is_visible() {
             return None;
         }
 
         // Extract the raster and surface spatial nodes from the raster
@@ -2373,17 +2376,17 @@ impl PicturePrimitive {
                     raster_config.surface_index,
                     surface.inflation_factor,
                 )
             }
             None => {
                 (
                     raster_spatial_node_index,
                     surface_spatial_node_index,
-                    surface_index,
+                    parent_surface_index,
                     0.0,
                 )
             }
         };
 
         let map_pic_to_world = SpaceMapper::new_with_target(
             ROOT_SPATIAL_NODE_INDEX,
             surface_spatial_node_index,
@@ -2413,37 +2416,323 @@ impl PicturePrimitive {
             Picture3DContext::In { root_data: Some(_), .. } => {
                 Some(PlaneSplitter::new())
             }
             Picture3DContext::In { root_data: None, .. } => {
                 None
             }
         };
 
+        let (is_composite, is_passthrough) = match self.raster_config {
+            Some(ref rc @ RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) => {
+                // For a picture surface, just push any child tasks and tile
+                // blits up to the parent surface.
+                let port = frame_state
+                    .surfaces[parent_surface_index.0]
+                    .render_tasks
+                    .expect("bug: no render tasks set for parent!")
+                    .port;
+
+                frame_state
+                    .surfaces[rc.surface_index.0]
+                    .render_tasks = Some(SurfaceRenderTasks {
+                        root: RenderTaskId::INVALID,
+                        port,
+                    });
+
+                (false, false)
+            },
+            Some(ref raster_config) => {
+                let pic_rect = PictureRect::from_untyped(&self.snapped_local_rect.to_untyped());
+
+                let device_pixel_scale = frame_state
+                    .surfaces[raster_config.surface_index.0]
+                    .device_pixel_scale;
+
+                let (clipped, unclipped) = match get_raster_rects(
+                    pic_rect,
+                    &map_pic_to_raster,
+                    &map_raster_to_world,
+                    clipped_prim_bounding_rect,
+                    device_pixel_scale,
+                ) {
+                    Some(info) => info,
+                    None => {
+                        return None
+                    }
+                };
+                let transform = map_pic_to_raster.get_transform();
+
+                let (root, port) = match raster_config.composite_mode {
+                    PictureCompositeMode::TileCache { .. } => {
+                        unreachable!();
+                    }
+                    PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
+                        let blur_std_deviation = blur_radius * device_pixel_scale.0;
+                        let scale_factors = scale_factors(&transform);
+                        let blur_std_deviation = DeviceSize::new(
+                            blur_std_deviation * scale_factors.0,
+                            blur_std_deviation * scale_factors.1
+                        );
+                        let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
+                        let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil() as i32;
+
+                        // The clipped field is the part of the picture that is visible
+                        // on screen. The unclipped field is the screen-space rect of
+                        // the complete picture, if no screen / clip-chain was applied
+                        // (this includes the extra space for blur region). To ensure
+                        // that we draw a large enough part of the picture to get correct
+                        // blur results, inflate that clipped area by the blur range, and
+                        // then intersect with the total screen rect, to minimize the
+                        // allocation size.
+                        let mut device_rect = clipped
+                            .inflate(inflation_factor, inflation_factor)
+                            .intersection(&unclipped.to_i32())
+                            .unwrap();
+                        // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
+                        // what would be even better is to rasterize the picture at the down-scaled size
+                        // directly.
+                        device_rect.size = RenderTask::adjusted_blur_source_size(
+                            device_rect.size,
+                            blur_std_deviation,
+                        );
+
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &device_rect,
+                            device_pixel_scale,
+                            true,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, device_rect.size),
+                            unclipped.size,
+                            pic_index,
+                            device_rect.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+
+                        let picture_task_id = frame_state.render_tasks.add(picture_task);
+
+                        let blur_render_task = RenderTask::new_blur(
+                            blur_std_deviation,
+                            picture_task_id,
+                            frame_state.render_tasks,
+                            RenderTargetKind::Color,
+                            ClearMode::Transparent,
+                        );
+
+                        let render_task_id = frame_state.render_tasks.add(blur_render_task);
+
+                        (render_task_id, picture_task_id)
+                    }
+                    PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) => {
+                        let blur_std_deviation = blur_radius * device_pixel_scale.0;
+                        let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+                        let rounded_std_dev = blur_std_deviation.round();
+                        let rounded_std_dev = DeviceSize::new(rounded_std_dev, rounded_std_dev);
+                        // The clipped field is the part of the picture that is visible
+                        // on screen. The unclipped field is the screen-space rect of
+                        // the complete picture, if no screen / clip-chain was applied
+                        // (this includes the extra space for blur region). To ensure
+                        // that we draw a large enough part of the picture to get correct
+                        // blur results, inflate that clipped area by the blur range, and
+                        // then intersect with the total screen rect, to minimize the
+                        // allocation size.
+                        let mut device_rect = clipped.inflate(blur_range, blur_range)
+                                .intersection(&unclipped.to_i32())
+                                .unwrap();
+                        device_rect.size = RenderTask::adjusted_blur_source_size(
+                            device_rect.size,
+                            rounded_std_dev,
+                        );
+
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &device_rect,
+                            device_pixel_scale,
+                            true,
+                        );
+
+                        let mut picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, device_rect.size),
+                            unclipped.size,
+                            pic_index,
+                            device_rect.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+                        picture_task.mark_for_saving();
+
+                        let picture_task_id = frame_state.render_tasks.add(picture_task);
+
+                        let blur_render_task = RenderTask::new_blur(
+                            rounded_std_dev,
+                            picture_task_id,
+                            frame_state.render_tasks,
+                            RenderTargetKind::Color,
+                            ClearMode::Transparent,
+                        );
+
+                        self.secondary_render_task_id = Some(picture_task_id);
+
+                        let render_task_id = frame_state.render_tasks.add(blur_render_task);
+
+                        (render_task_id, picture_task_id)
+                    }
+                    PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &clipped,
+                            device_pixel_scale,
+                            true,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            pic_index,
+                            clipped.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+
+                        let readback_task_id = frame_state.render_tasks.add(
+                            RenderTask::new_readback(clipped)
+                        );
+
+                        frame_state.render_tasks.add_dependency(
+                            frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port,
+                            readback_task_id,
+                        );
+
+                        self.secondary_render_task_id = Some(readback_task_id);
+
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+
+                        (render_task_id, render_task_id)
+                    }
+                    PictureCompositeMode::Filter(..) => {
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &clipped,
+                            device_pixel_scale,
+                            true,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            pic_index,
+                            clipped.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+
+                        (render_task_id, render_task_id)
+                    }
+                    PictureCompositeMode::ComponentTransferFilter(..) => {
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &clipped,
+                            device_pixel_scale,
+                            true,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            pic_index,
+                            clipped.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+
+                        (render_task_id, render_task_id)
+                    }
+                    PictureCompositeMode::MixBlend(..) |
+                    PictureCompositeMode::Blit(_) => {
+                        // The SplitComposite shader used for 3d contexts doesn't snap
+                        // to pixels, so we shouldn't snap our uv coordinates either.
+                        let supports_snapping = match self.context_3d {
+                            Picture3DContext::In{ .. } => false,
+                            _ => true,
+                        };
+
+                        let uv_rect_kind = calculate_uv_rect_kind(
+                            &pic_rect,
+                            &transform,
+                            &clipped,
+                            device_pixel_scale,
+                            supports_snapping,
+                        );
+
+                        let picture_task = RenderTask::new_picture(
+                            RenderTaskLocation::Dynamic(None, clipped.size),
+                            unclipped.size,
+                            pic_index,
+                            clipped.origin,
+                            Vec::new(),
+                            uv_rect_kind,
+                            raster_spatial_node_index,
+                            device_pixel_scale,
+                        );
+
+                        let render_task_id = frame_state.render_tasks.add(picture_task);
+
+                        (render_task_id, render_task_id)
+                    }
+                };
+
+                frame_state.surfaces[raster_config.surface_index.0].render_tasks = Some(SurfaceRenderTasks {
+                    root,
+                    port,
+                });
+
+                frame_state.render_tasks.add_dependency(
+                    frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port,
+                    root,
+                );
+
+                (true, false)
+            }
+            None => {
+                (false, true)
+            }
+        };
+
         let state = PictureState {
             //TODO: check for MAX_CACHE_SIZE here?
             map_local_to_pic,
             map_pic_to_world,
             map_pic_to_raster,
             map_raster_to_world,
             plane_splitter,
         };
 
-        let (is_composite, is_passthrough) = match self.raster_config {
-            Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) => {
-                (false, false)
-            },
-            Some(_) => {
-                (true, false)
-            }
-            None => {
-                (false, true)
-            }
-        };
-
         let mut dirty_region_count = 0;
 
         // If this is a picture cache, push the dirty region to ensure any
         // child primitives are culled and clipped to the dirty rect(s).
         if let Some(ref tile_cache) = self.tile_cache {
             // If the tile cache is disabled, it doesn't have a valid
             // dirty region to exclude primitives from.
             if tile_cache.is_enabled {
@@ -2483,20 +2772,20 @@ impl PicturePrimitive {
         frame_state: &mut FrameBuildingState,
     ) {
         // Pop any dirty regions this picture set
         for _ in 0 .. context.dirty_region_count {
             frame_state.pop_dirty_region();
         }
 
         self.prim_list = prim_list;
-        self.state = Some((state, context));
+        self.state = Some(state);
     }
 
-    pub fn take_state_and_context(&mut self) -> (PictureState, PictureContext) {
+    pub fn take_state(&mut self) -> PictureState {
         self.state.take().expect("bug: no state present!")
     }
 
     /// Add a primitive instance to the plane splitter. The function would generate
     /// an appropriate polygon, clip it against the frustum, and register with the
     /// given plane splitter.
     pub fn add_split_plane(
         splitter: &mut PlaneSplitter,
@@ -2871,203 +3160,44 @@ impl PicturePrimitive {
             {
                 parent_surface.rect = parent_surface.rect.union(&parent_surface_rect);
             }
         }
     }
 
     pub fn prepare_for_render(
         &mut self,
-        pic_index: PictureIndex,
-        clipped_prim_bounding_rect: WorldRect,
-        surface_index: SurfaceIndex,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         data_stores: &mut DataStores,
     ) -> bool {
-        let (mut pic_state_for_children, pic_context) = self.take_state_and_context();
+        let mut pic_state_for_children = self.take_state();
 
         if let Some(ref mut splitter) = pic_state_for_children.plane_splitter {
             self.resolve_split_planes(splitter, frame_state);
         }
 
         let raster_config = match self.raster_config {
             Some(ref mut raster_config) => raster_config,
             None => {
                 return true
             }
         };
 
-        let (raster_spatial_node_index, child_tasks, device_pixel_scale) = {
-            let surface_info = &mut frame_state.surfaces[raster_config.surface_index.0];
-            (
-                surface_info.raster_spatial_node_index,
-                surface_info.take_render_tasks(),
-                surface_info.device_pixel_scale,
-            )
-        };
-
-        let (map_raster_to_world, map_pic_to_raster) = create_raster_mappers(
-            self.spatial_node_index,
-            raster_spatial_node_index,
-            frame_context.screen_world_rect,
-            frame_context.clip_scroll_tree,
-        );
-
-        let pic_rect = PictureRect::from_untyped(&self.snapped_local_rect.to_untyped());
-
-        let (clipped, unclipped) = match get_raster_rects(
-            pic_rect,
-            &map_pic_to_raster,
-            &map_raster_to_world,
-            clipped_prim_bounding_rect,
-            device_pixel_scale,
-        ) {
-            Some(info) => info,
-            None => return false,
-        };
-        let transform = map_pic_to_raster.get_transform();
-
         // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
         //           to store the same type of data. The exception is the filter
         //           with a ColorMatrix, which stores the color matrix here. It's
         //           probably worth tidying this code up to be a bit more consistent.
         //           Perhaps store the color matrix after the common data, even though
         //           it's not used by that shader.
 
-        let surface = match raster_config.composite_mode {
-            PictureCompositeMode::TileCache { .. } => {
-                // For a picture surface, just push any child tasks and tile
-                // blits up to the parent surface.
-                let surface = &mut frame_state.surfaces[surface_index.0];
-                surface.tasks.extend(child_tasks);
-
-                return true;
-            }
-            PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
-                let blur_std_deviation = blur_radius * device_pixel_scale.0;
-                let scale_factors = scale_factors(&transform);
-                let blur_std_deviation = DeviceSize::new(
-                    blur_std_deviation * scale_factors.0,
-                    blur_std_deviation * scale_factors.1
-                );
-                let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
-                let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil() as i32;
-
-                // The clipped field is the part of the picture that is visible
-                // on screen. The unclipped field is the screen-space rect of
-                // the complete picture, if no screen / clip-chain was applied
-                // (this includes the extra space for blur region). To ensure
-                // that we draw a large enough part of the picture to get correct
-                // blur results, inflate that clipped area by the blur range, and
-                // then intersect with the total screen rect, to minimize the
-                // allocation size.
-                let mut device_rect = clipped
-                    .inflate(inflation_factor, inflation_factor)
-                    .intersection(&unclipped.to_i32())
-                    .unwrap();
-                // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
-                // what would be even better is to rasterize the picture at the down-scaled size
-                // directly.
-                device_rect.size = RenderTask::adjusted_blur_source_size(
-                    device_rect.size,
-                    blur_std_deviation,
-                );
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &device_rect,
-                    device_pixel_scale,
-                    true,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, device_rect.size),
-                    unclipped.size,
-                    pic_index,
-                    device_rect.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-
-                let picture_task_id = frame_state.render_tasks.add(picture_task);
-
-                let blur_render_task = RenderTask::new_blur(
-                    blur_std_deviation,
-                    picture_task_id,
-                    frame_state.render_tasks,
-                    RenderTargetKind::Color,
-                    ClearMode::Transparent,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(blur_render_task);
-
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-
-                render_task_id
-            }
-            PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color)) => {
-                let blur_std_deviation = blur_radius * device_pixel_scale.0;
-                let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
-                let rounded_std_dev = blur_std_deviation.round();
-                let rounded_std_dev = DeviceSize::new(rounded_std_dev, rounded_std_dev);
-                // The clipped field is the part of the picture that is visible
-                // on screen. The unclipped field is the screen-space rect of
-                // the complete picture, if no screen / clip-chain was applied
-                // (this includes the extra space for blur region). To ensure
-                // that we draw a large enough part of the picture to get correct
-                // blur results, inflate that clipped area by the blur range, and
-                // then intersect with the total screen rect, to minimize the
-                // allocation size.
-                let mut device_rect = clipped.inflate(blur_range, blur_range)
-                        .intersection(&unclipped.to_i32())
-                        .unwrap();
-                device_rect.size = RenderTask::adjusted_blur_source_size(
-                    device_rect.size,
-                    rounded_std_dev,
-                );
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &device_rect,
-                    device_pixel_scale,
-                    true,
-                );
-
-                let mut picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, device_rect.size),
-                    unclipped.size,
-                    pic_index,
-                    device_rect.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-                picture_task.mark_for_saving();
-
-                let picture_task_id = frame_state.render_tasks.add(picture_task);
-
-                let blur_render_task = RenderTask::new_blur(
-                    rounded_std_dev,
-                    picture_task_id,
-                    frame_state.render_tasks,
-                    RenderTargetKind::Color,
-                    ClearMode::Transparent,
-                );
-
-                self.secondary_render_task_id = Some(picture_task_id);
-
-                let render_task_id = frame_state.render_tasks.add(blur_render_task);
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-
+        match raster_config.composite_mode {
+            PictureCompositeMode::TileCache { .. } => {}
+            PictureCompositeMode::Filter(FilterOp::Blur(..)) => {}
+            PictureCompositeMode::Filter(FilterOp::DropShadow(offset, _, color)) => {
                 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
                     // TODO(gw): This is very hacky code below! It stores an extra
                     //           brush primitive below for the special case of a
                     //           drop-shadow where we need a different local
                     //           rect for the shadow. To tidy this up in future,
                     //           we could consider abstracting the code in prim_store.rs
                     //           that writes a brush primitive header.
 
@@ -3085,144 +3215,34 @@ impl PicturePrimitive {
                         0.0,
                         0.0,
                     ]);
 
                     // segment rect / extra data
                     request.push(shadow_rect);
                     request.push([0.0, 0.0, 0.0, 0.0]);
                 }
-
-                render_task_id
             }
-            PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &clipped,
-                    device_pixel_scale,
-                    true,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    pic_index,
-                    clipped.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-
-                let readback_task_id = frame_state.render_tasks.add(
-                    RenderTask::new_readback(clipped)
-                );
-
-                self.secondary_render_task_id = Some(readback_task_id);
-                frame_state.surfaces[surface_index.0].tasks.push(readback_task_id);
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-                render_task_id
-            }
+            PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {}
             PictureCompositeMode::Filter(ref filter) => {
                 if let FilterOp::ColorMatrix(m) = *filter {
                     if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
                         for i in 0..5 {
                             request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
                         }
                     }
                 }
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &clipped,
-                    device_pixel_scale,
-                    true,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    pic_index,
-                    clipped.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-                render_task_id
             }
             PictureCompositeMode::ComponentTransferFilter(handle) => {
                 let filter_data = &mut data_stores.filter_data[handle];
                 filter_data.update(frame_state);
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &clipped,
-                    device_pixel_scale,
-                    true,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    pic_index,
-                    clipped.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-                render_task_id
             }
             PictureCompositeMode::MixBlend(..) |
-            PictureCompositeMode::Blit(_) => {
-                // The SplitComposite shader used for 3d contexts doesn't snap
-                // to pixels, so we shouldn't snap our uv coordinates either.
-                let supports_snapping = match self.context_3d {
-                    Picture3DContext::In{ .. } => false,
-                    _ => true,
-                };
-
-                let uv_rect_kind = calculate_uv_rect_kind(
-                    &pic_rect,
-                    &transform,
-                    &clipped,
-                    device_pixel_scale,
-                    supports_snapping,
-                );
-
-                let picture_task = RenderTask::new_picture(
-                    RenderTaskLocation::Dynamic(None, clipped.size),
-                    unclipped.size,
-                    pic_index,
-                    clipped.origin,
-                    child_tasks,
-                    uv_rect_kind,
-                    pic_context.raster_spatial_node_index,
-                    device_pixel_scale,
-                );
-
-                let render_task_id = frame_state.render_tasks.add(picture_task);
-                frame_state.surfaces[surface_index.0].tasks.push(render_task_id);
-                render_task_id
-            }
-        };
-
-        frame_state.surfaces[raster_config.surface_index.0].surface = Some(surface);
+            PictureCompositeMode::Blit(_) => {}
+        }
 
         true
     }
 }
 
 // Calculate a single homogeneous screen-space UV for a picture.
 fn calculate_screen_uv(
     local_pos: &PicturePoint,
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -983,17 +983,20 @@ impl BrushSegment {
                     frame_state.render_tasks,
                     clip_data_store,
                     snap_offsets,
                     device_pixel_scale,
                     frame_context.fb_config,
                 );
 
                 let clip_task_id = frame_state.render_tasks.add(clip_task);
-                frame_state.surfaces[surface_index.0].tasks.push(clip_task_id);
+                frame_state.render_tasks.add_dependency(
+                    frame_state.surfaces[surface_index.0].render_tasks.unwrap().port,
+                    clip_task_id,
+                );
                 ClipMaskKind::Mask(clip_task_id)
             }
             None => {
                 ClipMaskKind::Clipped
             }
         }
     }
 }
@@ -2465,30 +2468,37 @@ impl PrimitiveStore {
         // For example, scrolling may affect the location of an item in
         // local space, which may force us to render this item on a larger
         // picture target, if being composited.
         let pic_info = {
             match prim_instance.kind {
                 PrimitiveInstanceKind::Picture { pic_index ,.. } => {
                     let pic = &mut self.pictures[pic_index.0];
 
+                    let clipped_prim_bounding_rect = scratch
+                        .prim_info[prim_instance.visibility_info.0 as usize]
+                        .clipped_world_rect;
+
                     match pic.take_context(
                         pic_index,
+                        clipped_prim_bounding_rect,
                         pic_context.surface_spatial_node_index,
                         pic_context.raster_spatial_node_index,
                         pic_context.surface_index,
                         frame_state,
                         frame_context,
                     ) {
                         Some(info) => Some(info),
                         None => {
                             if prim_instance.is_chased() {
                                 println!("\tculled for carrying an invisible composite filter");
                             }
 
+                            prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
+
                             return false;
                         }
                     }
                 }
                 PrimitiveInstanceKind::TextRun { .. } |
                 PrimitiveInstanceKind::Rectangle { .. } |
                 PrimitiveInstanceKind::LineDecoration { .. } |
                 PrimitiveInstanceKind::NormalBorder { .. } |
@@ -3066,19 +3076,16 @@ impl PrimitiveStore {
 
                 // TODO(gw): Consider whether it's worth doing segment building
                 //           for gradient primitives.
             }
             PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => {
                 let pic = &mut self.pictures[pic_index.0];
                 let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize];
                 if pic.prepare_for_render(
-                    *pic_index,
-                    prim_info.clipped_world_rect,
-                    pic_context.surface_index,
                     frame_context,
                     frame_state,
                     data_stores,
                 ) {
                     if let Some(ref mut splitter) = pic_state.plane_splitter {
                         PicturePrimitive::add_split_plane(
                             splitter,
                             frame_state.transforms,
@@ -3742,17 +3749,20 @@ impl PrimitiveInstance {
                 if self.is_chased() {
                     println!("\tcreated task {:?} with device rect {:?}",
                         clip_task_id, device_rect);
                 }
                 // Set the global clip mask instance for this primitive.
                 let clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _);
                 scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id));
                 prim_info.clip_task_index = clip_task_index;
-                frame_state.surfaces[pic_context.surface_index.0].tasks.push(clip_task_id);
+                frame_state.render_tasks.add_dependency(
+                    frame_state.surfaces[pic_context.surface_index.0].render_tasks.unwrap().port,
+                    clip_task_id,
+                );
             }
         }
     }
 }
 
 /// Mimics the GLSL mix() function.
 fn mix(x: f32, y: f32, a: f32) -> f32 {
     x * (1.0 - a) + y * a
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -3163,17 +3163,17 @@ impl Renderer {
                     x, 0.0,
                     ColorU::new(220, 30, 10, 255),
                     debug_renderer,
                 );
             }
         }
 
         if self.debug_flags.contains(DebugFlags::ECHO_DRIVER_MESSAGES) {
-            Device::echo_driver_messages(self.device.gl());
+            self.device.echo_driver_messages();
         }
 
         results.stats.texture_upload_kb = self.profile_counters.texture_data_uploaded.get();
         self.backend_profile_counters.reset();
         self.profile_counters.reset();
         self.profile_counters.frame_counter.inc();
         results.stats.resource_upload_time = self.resource_upload_time;
         self.resource_upload_time = 0;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2253,17 +2253,17 @@ bool BytecodeEmitter::isRunOnceLambda() 
          !funbox->isAsync() && !funbox->function()->explicitName();
 }
 
 bool BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset,
                                           uint32_t* resumeIndex) {
   static constexpr uint32_t MaxResumeIndex = JS_BITMASK(24);
 
   static_assert(
-      MaxResumeIndex < uint32_t(AbstractGeneratorObject::RESUME_INDEX_CLOSING),
+      MaxResumeIndex < uint32_t(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
       "resumeIndex should not include magic AbstractGeneratorObject "
       "resumeIndex values");
   static_assert(
       MaxResumeIndex <= INT32_MAX / sizeof(uintptr_t),
       "resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies "
       "on this when loading resume entries from BaselineScript");
 
   *resumeIndex = bytecodeSection().resumeOffsetList().length();
--- a/js/src/jit-test/tests/basic/bug1161762.js
+++ b/js/src/jit-test/tests/basic/bug1161762.js
@@ -1,8 +1,11 @@
+// |jit-test| crash; skip-if: getBuildConfiguration()['arm64']
+//
+// Test skipped on ARM64 due to bug 1549763.
 
 for (var actual = .5; actual < 100; actual++) {
   var test2 = {
     test4: actual + 6,
     test2: actual + 9,
     printStatus: actual + 10,
     isPrototypeOf: actual + 12,
     expect: actual + 14,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/getter-primitive-value.js
@@ -0,0 +1,116 @@
+function testNativeGetter() {
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].description;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testNativeGetter();
+
+function testScriptedGetter() {
+    Object.defineProperty(Symbol.prototype, "desc", {
+        get() {
+            "use strict";
+            assertEq(typeof this, "symbol");
+            return this.description;
+        }
+    });
+
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].desc;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetter();
+
+function testScriptedGetterNonStrict() {
+    Object.defineProperty(Symbol.prototype, "desc_nonstrict", {
+        get() {
+            assertEq(typeof this, "object");
+            return this.description;
+        }
+    });
+
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].desc_nonstrict;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterNonStrict();
+
+function testNativeGetterPrototype() {
+    Object.defineProperty(Object.prototype, "description_proto",
+        Object.getOwnPropertyDescriptor(Symbol.prototype, "description"));
+
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].description_proto;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testNativeGetterPrototype();
+
+function testScriptedGetterPrototype() {
+    Object.defineProperty(Object.prototype, "desc_proto", {
+        get() {
+            "use strict";
+            assertEq(typeof this, "symbol");
+            return this.description;
+        }
+    });
+
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].desc_proto;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterPrototype();
+
+function testScriptedGetterNonStrictPrototype() {
+    Object.defineProperty(Object.prototype, "desc_nonstrict_proto", {
+        get() {
+            assertEq(typeof this, "object");
+            return this.description;
+        }
+    });
+
+    function test() {
+        var xs = [Symbol("a"), Symbol("b")];
+        var ys = ["a", "b"];
+
+        for (var i = 0; i < 100; ++i) {
+            var r = xs[i & 1].desc_nonstrict_proto;
+            assertEq(r, ys[i & 1]);
+        }
+    }
+    for (var i = 0; i < 2; ++i) test();
+}
+testScriptedGetterNonStrictPrototype();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-13.js
@@ -0,0 +1,21 @@
+// An onPop handler can return its input argument for async generators. The
+// debugger correctly adjusts the state of the async generator object.
+
+let g = newGlobal({newCompartment: true});
+g.eval(`
+    async function* f() {}
+`);
+
+let hits = 0;
+let dbg = new Debugger(g);
+dbg.onEnterFrame = frame => {
+  frame.onPop = completion => {
+      hits++;
+      return completion;
+  };
+};
+
+let it = g.f();
+let p = it.next();
+
+assertEq(hits, 1);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -86,16 +86,23 @@ class MOZ_RAII BaselineCacheIRCompiler :
                         Register scratch2, bool isJitCall);
   void createThis(Register argcReg, Register calleeReg, Register scratch,
                   CallFlags flags);
   void updateReturnValue();
 
   enum class NativeCallType { Native, ClassHook };
   bool emitCallNativeShared(NativeCallType callType);
 
+  MOZ_MUST_USE bool emitCallScriptedGetterResultShared(
+      TypedOrValueRegister receiver);
+
+  template <typename T, typename CallVM>
+  MOZ_MUST_USE bool emitCallNativeGetterResultShared(T receiver,
+                                                     const CallVM& emitCallVM);
+
  public:
   friend class AutoStubFrame;
 
   BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer,
                           uint32_t stubDataOffset,
                           BaselineCacheIRStubKind stubKind)
       : CacheIRCompiler(cx, writer, stubDataOffset, Mode::Baseline,
                         StubFieldPolicy::Address),
@@ -548,19 +555,18 @@ bool BaselineCacheIRCompiler::emitGuardH
   masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectHasGetterSetterPure));
   masm.mov(ReturnReg, scratch1);
   masm.PopRegsInMask(volatileRegs);
 
   masm.branchIfFalseBool(scratch1, failure->label());
   return true;
 }
 
-bool BaselineCacheIRCompiler::emitCallScriptedGetterResult() {
-  JitSpew(JitSpew_Codegen, __FUNCTION__);
-  Register obj = allocator.useRegister(masm, reader.objOperandId());
+bool BaselineCacheIRCompiler::emitCallScriptedGetterResultShared(
+    TypedOrValueRegister receiver) {
   Address getterAddr(stubAddress(reader.stubOffset()));
   bool isSameRealm = reader.readBool();
 
   AutoScratchRegister code(allocator, masm);
   AutoScratchRegister callee(allocator, masm);
   AutoScratchRegister scratch(allocator, masm);
 
   // First, ensure our getter is non-lazy.
@@ -584,20 +590,20 @@ bool BaselineCacheIRCompiler::emitCallSc
   if (!isSameRealm) {
     masm.switchToObjectRealm(callee, scratch);
   }
 
   // Align the stack such that the JitFrameLayout is aligned on
   // JitStackAlignment.
   masm.alignJitStackBasedOnNArgs(0);
 
-  // Getter is called with 0 arguments, just |obj| as thisv.
+  // Getter is called with 0 arguments, just |receiver| as thisv.
   // Note that we use Push, not push, so that callJit will align the stack
   // properly on ARM.
-  masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+  masm.Push(receiver);
 
   EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
   masm.Push(Imm32(0));  // ActualArgc is 0
   masm.Push(callee);
   masm.Push(scratch);
 
   // Handle arguments underflow.
   Label noUnderflow;
@@ -617,42 +623,77 @@ bool BaselineCacheIRCompiler::emitCallSc
 
   if (!isSameRealm) {
     masm.switchToBaselineFrameRealm(R1.scratchReg());
   }
 
   return true;
 }
 
-bool BaselineCacheIRCompiler::emitCallNativeGetterResult() {
+bool BaselineCacheIRCompiler::emitCallScriptedGetterResult() {
   JitSpew(JitSpew_Codegen, __FUNCTION__);
   Register obj = allocator.useRegister(masm, reader.objOperandId());
+
+  return emitCallScriptedGetterResultShared(
+      TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+}
+
+bool BaselineCacheIRCompiler::emitCallScriptedGetterByValueResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+  return emitCallScriptedGetterResultShared(val);
+}
+
+template <typename T, typename CallVM>
+bool BaselineCacheIRCompiler::emitCallNativeGetterResultShared(
+    T receiver, const CallVM& emitCallVM) {
   Address getterAddr(stubAddress(reader.stubOffset()));
 
   AutoScratchRegister scratch(allocator, masm);
 
   allocator.discardStack(masm);
 
   AutoStubFrame stubFrame(*this);
   stubFrame.enter(masm, scratch);
 
   // Load the callee in the scratch register.
   masm.loadPtr(getterAddr, scratch);
 
-  masm.Push(obj);
+  masm.Push(receiver);
   masm.Push(scratch);
 
-  using Fn =
-      bool (*)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
-  callVM<Fn, CallNativeGetter>(masm);
+  emitCallVM();
 
   stubFrame.leave(masm);
   return true;
 }
 
+bool BaselineCacheIRCompiler::emitCallNativeGetterResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  Register obj = allocator.useRegister(masm, reader.objOperandId());
+
+  return emitCallNativeGetterResultShared(obj, [this]() {
+    using Fn =
+        bool (*)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
+    callVM<Fn, CallNativeGetter>(masm);
+  });
+}
+
+bool BaselineCacheIRCompiler::emitCallNativeGetterByValueResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+  return emitCallNativeGetterResultShared(val, [this]() {
+    using Fn =
+        bool (*)(JSContext*, HandleFunction, HandleValue, MutableHandleValue);
+    callVM<Fn, CallNativeGetterByValue>(masm);
+  });
+}
+
 bool BaselineCacheIRCompiler::emitCallProxyGetResult() {
   JitSpew(JitSpew_Codegen, __FUNCTION__);
   Register obj = allocator.useRegister(masm, reader.objOperandId());
   Address idAddr(stubAddress(reader.stubOffset()));
 
   AutoScratchRegister scratch(allocator, masm);
 
   allocator.discardStack(masm);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -915,59 +915,98 @@ static void EmitCallGetterResultNoGuards
                                          JSObject* holder, Shape* shape,
                                          ObjOperandId receiverId) {
   switch (IsCacheableGetPropCall(obj, holder, shape)) {
     case CanAttachNativeGetter: {
       JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
       MOZ_ASSERT(target->isBuiltinNative());
       writer.callNativeGetterResult(receiverId, target);
       writer.typeMonitorResult();
-    } break;
+      break;
+    }
     case CanAttachScriptedGetter: {
       JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
       MOZ_ASSERT(target->hasJitEntry());
       writer.callScriptedGetterResult(receiverId, target);
       writer.typeMonitorResult();
-    } break;
+      break;
+    }
     default:
+      // CanAttachNativeGetProp guarantees that the getter is either a native or
+      // a scripted function.
       MOZ_ASSERT_UNREACHABLE("Can't attach getter");
       break;
   }
 }
 
-static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj,
-                                 JSObject* holder, Shape* shape,
-                                 ObjOperandId objId, ObjOperandId receiverId,
-                                 ICState::Mode mode) {
+static void EmitCallGetterResultGuards(CacheIRWriter& writer, JSObject* obj,
+                                       JSObject* holder, Shape* shape,
+                                       ObjOperandId objId, ICState::Mode mode) {
   // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
   // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
   // require outerizing).
   if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
     TestMatchingReceiver(writer, obj, objId);
 
     if (obj != holder) {
       GeneratePrototypeGuards(writer, obj, holder, objId);
 
       // Guard on the holder's shape.
       ObjOperandId holderId = writer.loadObject(holder);
       TestMatchingHolder(writer, holder, holderId);
     }
   } else {
     writer.guardHasGetterSetter(objId, shape);
   }
-
+}
+
+static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj,
+                                 JSObject* holder, Shape* shape,
+                                 ObjOperandId objId, ObjOperandId receiverId,
+                                 ICState::Mode mode) {
+  EmitCallGetterResultGuards(writer, obj, holder, shape, objId, mode);
   EmitCallGetterResultNoGuards(writer, obj, holder, shape, receiverId);
 }
 
 static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj,
                                  JSObject* holder, Shape* shape,
                                  ObjOperandId objId, ICState::Mode mode) {
   EmitCallGetterResult(writer, obj, holder, shape, objId, objId, mode);
 }
 
+static void EmitCallGetterByValueResult(CacheIRWriter& writer, JSObject* obj,
+                                        JSObject* holder, Shape* shape,
+                                        ObjOperandId objId,
+                                        ValOperandId receiverId,
+                                        ICState::Mode mode) {
+  EmitCallGetterResultGuards(writer, obj, holder, shape, objId, mode);
+
+  switch (IsCacheableGetPropCall(obj, holder, shape)) {
+    case CanAttachNativeGetter: {
+      JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
+      MOZ_ASSERT(target->isBuiltinNative());
+      writer.callNativeGetterByValueResult(receiverId, target);
+      writer.typeMonitorResult();
+      break;
+    }
+    case CanAttachScriptedGetter: {
+      JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
+      MOZ_ASSERT(target->hasJitEntry());
+      writer.callScriptedGetterByValueResult(receiverId, target);
+      writer.typeMonitorResult();
+      break;
+    }
+    default:
+      // CanAttachNativeGetProp guarantees that the getter is either a native or
+      // a scripted function.
+      MOZ_ASSERT_UNREACHABLE("Can't attach getter");
+      break;
+  }
+}
+
 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId,
                                                      jsid id,
                                                      bool handleMissing) {
   MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
 
   // The stub handles the missing-properties case only if we're seeing one
   // now, to make sure Ion ICs correctly monitor the undefined type.
 
@@ -1845,43 +1884,69 @@ AttachDecision GetPropIRGenerator::tryAt
   if (!proto) {
     return AttachDecision::NoAction;
   }
 
   RootedShape shape(cx_);
   RootedNativeObject holder(cx_);
   NativeGetPropCacheability type = CanAttachNativeGetProp(
       cx_, proto, id, &holder, &shape, pc_, resultFlags_);
-  if (type == CanAttachTemporarilyUnoptimizable) {
-    return AttachDecision::TemporarilyUnoptimizable;
-  }
-  if (type != CanAttachReadSlot) {
-    return AttachDecision::NoAction;
-  }
-
-  if (holder) {
-    // Instantiate this property, for use during Ion compilation.
-    if (IsIonEnabled(cx_)) {
-      EnsureTrackPropertyTypes(cx_, holder, id);
+  switch (type) {
+    case CanAttachNone:
+      return AttachDecision::NoAction;
+    case CanAttachTemporarilyUnoptimizable:
+      return AttachDecision::TemporarilyUnoptimizable;
+    case CanAttachReadSlot: {
+      if (holder) {
+        // Instantiate this property, for use during Ion compilation.
+        if (IsIonEnabled(cx_)) {
+          EnsureTrackPropertyTypes(cx_, holder, id);
+        }
+      }
+
+      if (val_.isNumber()) {
+        writer.guardIsNumber(valId);
+      } else {
+        writer.guardType(valId, val_.type());
+      }
+      maybeEmitIdGuard(id);
+
+      ObjOperandId protoId = writer.loadObject(proto);
+      EmitReadSlotResult(writer, proto, holder, shape, protoId);
+      EmitReadSlotReturn(writer, proto, holder, shape);
+
+      trackAttached("PrimitiveSlot");
+      return AttachDecision::Attach;
     }
-  }
-
-  if (val_.isNumber()) {
-    writer.guardIsNumber(valId);
-  } else {
-    writer.guardType(valId, val_.type());
-  }
-  maybeEmitIdGuard(id);
-
-  ObjOperandId protoId = writer.loadObject(proto);
-  EmitReadSlotResult(writer, proto, holder, shape, protoId);
-  EmitReadSlotReturn(writer, proto, holder, shape);
-
-  trackAttached("Primitive");
-  return AttachDecision::Attach;
+    case CanAttachScriptedGetter:
+    case CanAttachNativeGetter: {
+      MOZ_ASSERT(!idempotent());
+
+      // The primitive stubs don't currently support |super| access.
+      if (isSuper()) {
+        return AttachDecision::NoAction;
+      }
+
+      if (val_.isNumber()) {
+        writer.guardIsNumber(valId);
+      } else {
+        writer.guardType(valId, val_.type());
+      }
+      maybeEmitIdGuard(id);
+
+      ObjOperandId protoId = writer.loadObject(proto);
+      EmitCallGetterByValueResult(writer, proto, holder, shape, protoId, valId,
+                                  mode_);
+
+      trackAttached("PrimitiveGetter");
+      return AttachDecision::Attach;
+    }
+  }
+
+  MOZ_CRASH("Bad NativeGetPropCacheability");
 }
 
 AttachDecision GetPropIRGenerator::tryAttachStringLength(ValOperandId valId,
                                                          HandleId id) {
   if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length)) {
     return AttachDecision::NoAction;
   }
 
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -321,17 +321,19 @@ extern const uint32_t ArgLengths[];
   _(LoadStringLengthResult, Id)                                                \
   _(LoadFrameCalleeResult, None)                                               \
   _(LoadFrameNumActualArgsResult, None)                                        \
   _(LoadFrameArgumentResult, Id)                                               \
   _(LoadEnvironmentFixedSlotResult, Id, Field)                                 \
   _(LoadEnvironmentDynamicSlotResult, Id, Field)                               \
   _(LoadObjectResult, Id)                                                      \
   _(CallScriptedGetterResult, Id, Field, Byte)                                 \
+  _(CallScriptedGetterByValueResult, Id, Field, Byte)                          \
   _(CallNativeGetterResult, Id, Field)                                         \
+  _(CallNativeGetterByValueResult, Id, Field)                                  \
   _(CallProxyGetResult, Id, Field)                                             \
   _(CallProxyGetByValueResult, Id, Id)                                         \
   _(CallProxyHasPropResult, Id, Id, Byte)                                      \
   _(CallObjectHasSparseElementResult, Id, Id)                                  \
   _(CallNativeGetElementResult, Id, Id)                                        \
   _(LoadUndefinedResult, None)                                                 \
   _(LoadBooleanResult, Byte)                                                   \
   _(LoadStringResult, Field)                                                   \
@@ -1634,20 +1636,29 @@ class MOZ_RAII CacheIRWriter : public JS
     writeOpWithOperandId(CacheOp::LoadStringCharResult, str);
     writeOperandId(index);
   }
   void callScriptedGetterResult(ObjOperandId obj, JSFunction* getter) {
     writeOpWithOperandId(CacheOp::CallScriptedGetterResult, obj);
     addStubField(uintptr_t(getter), StubField::Type::JSObject);
     buffer_.writeByte(cx_->realm() == getter->realm());
   }
+  void callScriptedGetterByValueResult(ValOperandId obj, JSFunction* getter) {
+    writeOpWithOperandId(CacheOp::CallScriptedGetterByValueResult, obj);
+    addStubField(uintptr_t(getter), StubField::Type::JSObject);
+    buffer_.writeByte(cx_->realm() == getter->realm());
+  }
   void callNativeGetterResult(ObjOperandId obj, JSFunction* getter) {
     writeOpWithOperandId(CacheOp::CallNativeGetterResult, obj);
     addStubField(uintptr_t(getter), StubField::Type::JSObject);
   }
+  void callNativeGetterByValueResult(ValOperandId obj, JSFunction* getter) {
+    writeOpWithOperandId(CacheOp::CallNativeGetterByValueResult, obj);
+    addStubField(uintptr_t(getter), StubField::Type::JSObject);
+  }
   void callProxyGetResult(ObjOperandId obj, jsid id) {
     writeOpWithOperandId(CacheOp::CallProxyGetResult, obj);
     addStubField(uintptr_t(JSID_BITS(id)), StubField::Type::Id);
   }
   void callProxyGetByValueResult(ObjOperandId obj, ValOperandId idVal) {
     writeOpWithOperandId(CacheOp::CallProxyGetByValueResult, obj);
     writeOperandId(idVal);
   }
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -4353,23 +4353,23 @@ bool CacheIRCompiler::emitCallIsSuspende
   masm.branchTestObject(Assembler::NotEqual, input, &returnFalse);
 
   // Test if it's a GeneratorObject.
   masm.unboxObject(input, scratch);
   masm.branchTestObjClass(Assembler::NotEqual, scratch,
                           &GeneratorObject::class_, scratch2, scratch,
                           &returnFalse);
 
-  // If the resumeIndex slot holds an int32 value < RESUME_INDEX_CLOSING,
+  // If the resumeIndex slot holds an int32 value < RESUME_INDEX_RUNNING,
   // the generator is suspended.
   Address addr(scratch, AbstractGeneratorObject::offsetOfResumeIndexSlot());
   masm.branchTestInt32(Assembler::NotEqual, addr, &returnFalse);
   masm.unboxInt32(addr, scratch);
   masm.branch32(Assembler::AboveOrEqual, scratch,
-                Imm32(AbstractGeneratorObject::RESUME_INDEX_CLOSING),
+                Imm32(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
                 &returnFalse);
 
   masm.moveValue(BooleanValue(true), output.valueReg());
   masm.jump(&done);
 
   masm.bind(&returnFalse);
   masm.moveValue(BooleanValue(false), output.valueReg());
 
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -108,16 +108,21 @@ class MOZ_RAII IonCacheIRCompiler : publ
 
   template <typename Fn, Fn fn>
   void callVM(MacroAssembler& masm) {
     VMFunctionId id = VMFunctionToId<Fn, fn>::id;
     callVMInternal(masm, id);
   }
 
   MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
+  MOZ_MUST_USE bool emitCallScriptedGetterResultShared(
+      TypedOrValueRegister receiver, TypedOrValueRegister output);
+  MOZ_MUST_USE bool emitCallNativeGetterResultShared(
+      TypedOrValueRegister receiver, const AutoOutputRegister& output,
+      AutoSaveLiveRegisters& save);
 
   bool needsPostBarrier() const {
     return ic_->asSetPropertyIC()->needsPostBarrier();
   }
 
   void pushStubCodePointer() {
     stubJitCodeOffset_.emplace(masm.PushWithPatch(ImmPtr((void*)-1)));
   }
@@ -920,22 +925,18 @@ bool IonCacheIRCompiler::emitGuardHasGet
   masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectHasGetterSetterPure));
   masm.mov(ReturnReg, scratch1);
   masm.PopRegsInMask(volatileRegs);
 
   masm.branchIfFalseBool(scratch1, failure->label());
   return true;
 }
 
-bool IonCacheIRCompiler::emitCallScriptedGetterResult() {
-  JitSpew(JitSpew_Codegen, __FUNCTION__);
-  AutoSaveLiveRegisters save(*this);
-  AutoOutputRegister output(*this);
-
-  Register obj = allocator.useRegister(masm, reader.objOperandId());
+bool IonCacheIRCompiler::emitCallScriptedGetterResultShared(
+    TypedOrValueRegister receiver, TypedOrValueRegister output) {
   JSFunction* target = &objectStubField(reader.stubOffset())->as<JSFunction>();
   AutoScratchRegister scratch(allocator, masm);
 
   bool isSameRealm = reader.readBool();
   MOZ_ASSERT(isSameRealm == (cx_->realm() == target->realm()));
 
   allocator.discardStack(masm);
 
@@ -956,17 +957,17 @@ bool IonCacheIRCompiler::emitCallScripte
       ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
   MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
   MOZ_ASSERT(padding < JitStackAlignment);
   masm.reserveStack(padding);
 
   for (size_t i = 0; i < target->nargs(); i++) {
     masm.Push(UndefinedValue());
   }
-  masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+  masm.Push(receiver);
 
   if (!isSameRealm) {
     masm.switchToRealm(target->realm(), scratch);
   }
 
   masm.movePtr(ImmGCPtr(target), scratch);
 
   descriptor = MakeFrameDescriptor(argSize + padding, FrameType::IonICCall,
@@ -992,40 +993,58 @@ bool IonCacheIRCompiler::emitCallScripte
     masm.switchToRealm(cx_->realm(), ReturnReg);
   }
 
   masm.storeCallResultValue(output);
   masm.freeStack(masm.framePushed() - framePushedBefore);
   return true;
 }
 
-bool IonCacheIRCompiler::emitCallNativeGetterResult() {
+bool IonCacheIRCompiler::emitCallScriptedGetterResult() {
   JitSpew(JitSpew_Codegen, __FUNCTION__);
   AutoSaveLiveRegisters save(*this);
   AutoOutputRegister output(*this);
 
   Register obj = allocator.useRegister(masm, reader.objOperandId());
+
+  return emitCallScriptedGetterResultShared(
+      TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), output);
+}
+
+bool IonCacheIRCompiler::emitCallScriptedGetterByValueResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  AutoSaveLiveRegisters save(*this);
+  AutoOutputRegister output(*this);
+
+  ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+  return emitCallScriptedGetterResultShared(val, output);
+}
+
+bool IonCacheIRCompiler::emitCallNativeGetterResultShared(
+    TypedOrValueRegister receiver, const AutoOutputRegister& output,
+    AutoSaveLiveRegisters& save) {
   JSFunction* target = &objectStubField(reader.stubOffset())->as<JSFunction>();
   MOZ_ASSERT(target->isNative());
 
-  AutoScratchRegister argJSContext(allocator, masm);
+  AutoScratchRegisterMaybeOutput argJSContext(allocator, masm, output);
   AutoScratchRegister argUintN(allocator, masm);
   AutoScratchRegister argVp(allocator, masm);
   AutoScratchRegister scratch(allocator, masm);
 
   allocator.discardStack(masm);
 
   // Native functions have the signature:
   //  bool (*)(JSContext*, unsigned, Value* vp)
   // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
   // are the function arguments.
 
   // Construct vp array:
-  // Push object value for |this|
-  masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+  // Push receiver value for |this|
+  masm.Push(receiver);
   // Push callee/outparam.
   masm.Push(ObjectValue(*target));
 
   // Preload arguments into registers.
   masm.loadJSContext(argJSContext);
   masm.move32(Imm32(0), argUintN);
   masm.moveStackPtrTo(argVp.get());
 
@@ -1066,16 +1085,37 @@ bool IonCacheIRCompiler::emitCallNativeG
   if (JitOptions.spectreJitToCxxCalls) {
     masm.speculationBarrier();
   }
 
   masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
   return true;
 }
 
+bool IonCacheIRCompiler::emitCallNativeGetterResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  AutoSaveLiveRegisters save(*this);
+  AutoOutputRegister output(*this);
+
+  Register obj = allocator.useRegister(masm, reader.objOperandId());
+
+  return emitCallNativeGetterResultShared(
+      TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), output, save);
+}
+
+bool IonCacheIRCompiler::emitCallNativeGetterByValueResult() {
+  JitSpew(JitSpew_Codegen, __FUNCTION__);
+  AutoSaveLiveRegisters save(*this);
+  AutoOutputRegister output(*this);
+
+  ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+  return emitCallNativeGetterResultShared(val, output, save);
+}
+
 bool IonCacheIRCompiler::emitCallProxyGetResult() {
   JitSpew(JitSpew_Codegen, __FUNCTION__);
   AutoSaveLiveRegisters save(*this);
   AutoOutputRegister output(*this);
 
   Register obj = allocator.useRegister(masm, reader.objOperandId());
   jsid id = idStubField(reader.stubOffset());
 
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -51,16 +51,17 @@ namespace jit {
   _(BitLsh, js::BitLsh)                                                        \
   _(BitNot, js::BitNot)                                                        \
   _(BitOr, js::BitOr)                                                          \
   _(BitRsh, js::BitRsh)                                                        \
   _(BitXor, js::BitXor)                                                        \
   _(BoxNonStrictThis, js::BoxNonStrictThis)                                    \
   _(BuiltinProtoOperation, js::BuiltinProtoOperation)                          \
   _(CallNativeGetter, js::jit::CallNativeGetter)                               \
+  _(CallNativeGetterByValue, js::jit::CallNativeGetterByValue)                 \
   _(CallNativeSetter, js::jit::CallNativeSetter)                               \
   _(CharCodeAt, js::jit::CharCodeAt)                                           \
   _(CheckClassHeritageOperation, js::CheckClassHeritageOperation)              \
   _(CheckGlobalOrEvalDeclarationConflicts,                                     \
     js::CheckGlobalOrEvalDeclarationConflicts)                                 \
   _(CheckIsCallable, js::jit::CheckIsCallable)                                 \
   _(CheckOverRecursed, js::jit::CheckOverRecursed)                             \
   _(CheckOverRecursedBaseline, js::jit::CheckOverRecursedBaseline)             \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1554,16 +1554,35 @@ bool CallNativeGetter(JSContext* cx, Han
   if (!natfun(cx, 0, vp.begin())) {
     return false;
   }
 
   result.set(vp[0]);
   return true;
 }
 
+bool CallNativeGetterByValue(JSContext* cx, HandleFunction callee,
+                             HandleValue receiver, MutableHandleValue result) {
+  AutoRealm ar(cx, callee);
+
+  MOZ_ASSERT(callee->isNative());
+  JSNative natfun = callee->native();
+
+  JS::AutoValueArray<2> vp(cx);
+  vp[0].setObject(*callee.get());
+  vp[1].set(receiver);
+
+  if (!natfun(cx, 0, vp.begin())) {
+    return false;
+  }
+
+  result.set(vp[0]);
+  return true;
+}
+
 bool CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj,
                       HandleValue rhs) {
   AutoRealm ar(cx, callee);
 
   MOZ_ASSERT(callee->isNative());
   JSNative natfun = callee->native();
 
   JS::AutoValueArray<3> vp(cx);
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -1051,16 +1051,20 @@ MOZ_MUST_USE bool ThrowBadDerivedReturn(
 MOZ_MUST_USE bool ThrowObjectCoercible(JSContext* cx, HandleValue v);
 
 MOZ_MUST_USE bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame,
                                           MutableHandleValue res);
 
 MOZ_MUST_USE bool CallNativeGetter(JSContext* cx, HandleFunction callee,
                                    HandleObject obj, MutableHandleValue result);
 
+MOZ_MUST_USE bool CallNativeGetterByValue(JSContext* cx, HandleFunction callee,
+                                          HandleValue receiver,
+                                          MutableHandleValue result);
+
 MOZ_MUST_USE bool CallNativeSetter(JSContext* cx, HandleFunction callee,
                                    HandleObject obj, HandleValue rhs);
 
 MOZ_MUST_USE bool EqualStringsHelperPure(JSString* str1, JSString* str2);
 
 MOZ_MUST_USE bool CheckIsCallable(JSContext* cx, HandleValue v,
                                   CheckIsCallableKind kind);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1671,27 +1671,33 @@ static void AdjustGeneratorResumptionVal
 
     // 1.  `return <value>` creates and returns a new object,
     //     `{value: <value>, done: true}`.
     //
     // For non-async generators, the iterator result object is created in
     // bytecode, so we have to simulate that here. For async generators, our
     // C++ implementation of AsyncGeneratorResolve will do this. So don't do it
     // twice:
-    if (!frame.callee()->isAsync()) {
+    if (!genObj->is<AsyncGeneratorObject>()) {
       JSObject* pair = CreateIterResultObject(cx, vp, true);
       if (!pair) {
         getAndClearExceptionThenThrow();
         return;
       }
       vp.setObject(*pair);
     }
 
     // 2.  The generator must be closed.
     genObj->setClosed();
+
+    // Async generators have additionally bookkeeping which must be adjusted
+    // when switching over to the closed state.
+    if (genObj->is<AsyncGeneratorObject>()) {
+      genObj->as<AsyncGeneratorObject>().setCompleted();
+    }
   } else if (frame.callee()->isAsync()) {
     if (AbstractGeneratorObject* genObj =
             GetGeneratorObjectForFrame(cx, frame)) {
       // Throw doesn't require any special processing for async functions when
       // the internal generator object is already present.
       if (resumeMode == ResumeMode::Throw) {
         return;
       }
@@ -2078,17 +2084,17 @@ ResumeMode Debugger::fireEnterFrame(JSCo
   RootedValue scriptFrame(cx);
 
   FrameIter iter(cx);
 
 #if DEBUG
   // Assert that the hook won't be able to re-enter the generator.
   if (iter.hasScript() && *iter.pc() == JSOP_AFTERYIELD) {
     auto* genObj = GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
-    MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
+    MOZ_ASSERT(genObj->isRunning());
   }
 #endif
 
   Maybe<AutoRealm> ar;
   ar.emplace(cx, object);
 
   if (!getFrame(cx, iter, &scriptFrame)) {
     return reportUncaughtException(ar);
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -95,17 +95,17 @@ bool AbstractGeneratorObject::suspend(JS
     genObj->setExpressionStack(*stack);
   }
 
   return true;
 }
 
 void AbstractGeneratorObject::finalSuspend(HandleObject obj) {
   auto* genObj = &obj->as<AbstractGeneratorObject>();
-  MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
+  MOZ_ASSERT(genObj->isRunning());
   genObj->setClosed();
 }
 
 AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
     JSContext* cx, AbstractFramePtr frame) {
   cx->check(frame);
   MOZ_ASSERT(frame.isGeneratorFrame());
 
@@ -134,27 +134,27 @@ void js::SetGeneratorClosed(JSContext* c
       callObj.getSlot(shape->slot()).toObject().as<AbstractGeneratorObject>();
   genObj.setClosed();
 }
 
 bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
                                 Handle<AbstractGeneratorObject*> genObj,
                                 HandleValue arg,
                                 GeneratorResumeKind resumeKind) {
+  MOZ_ASSERT(genObj->isRunning());
   if (resumeKind == GeneratorResumeKind::Throw) {
     cx->setPendingExceptionAndCaptureStack(arg);
   } else {
     MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return);
 
     MOZ_ASSERT_IF(genObj->is<GeneratorObject>(), arg.isObject());
     frame.setReturnValue(arg);
 
     RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
     cx->setPendingException(closing, nullptr);
-    genObj->setClosing();
   }
   return false;
 }
 
 bool AbstractGeneratorObject::resume(JSContext* cx,
                                      InterpreterActivation& activation,
                                      Handle<AbstractGeneratorObject*> genObj,
                                      HandleValue arg) {
@@ -304,17 +304,17 @@ bool AbstractGeneratorObject::isAfterYie
   return isAfterYieldOrAwait(JSOP_YIELD);
 }
 
 bool AbstractGeneratorObject::isAfterAwait() {
   return isAfterYieldOrAwait(JSOP_AWAIT);
 }
 
 bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op) {
-  if (isClosed() || isClosing() || isRunning()) {
+  if (isClosed() || isRunning()) {
     return false;
   }
 
   JSScript* script = callee().nonLazyScript();
   jsbytecode* code = script->code();
   uint32_t nextOffset = script->resumeOffsets()[resumeIndex()];
   if (code[nextOffset] != JSOP_AFTERYIELD) {
     return false;
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -15,20 +15,19 @@
 #include "vm/Stack.h"
 
 namespace js {
 
 enum class GeneratorResumeKind { Next, Throw, Return };
 
 class AbstractGeneratorObject : public NativeObject {
  public:
-  // Magic values stored in the resumeIndex slot when the generator is
+  // Magic value stored in the resumeIndex slot when the generator is
   // running or closing. See the resumeIndex comment below.
   static const int32_t RESUME_INDEX_RUNNING = INT32_MAX;
-  static const int32_t RESUME_INDEX_CLOSING = INT32_MAX - 1;
 
   enum {
     CALLEE_SLOT = 0,
     ENV_CHAIN_SLOT,
     ARGS_OBJ_SLOT,
     EXPRESSION_STACK_SLOT,
     RESUME_INDEX_SLOT,
     RESERVED_SLOTS
@@ -111,59 +110,49 @@ class AbstractGeneratorObject : public N
   }
   void clearExpressionStack() {
     setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
   }
 
   // The resumeIndex slot is abused for a few purposes.  It's undefined if
   // it hasn't been set yet (before the initial yield), and null if the
   // generator is closed. If the generator is running, the resumeIndex is
-  // RESUME_INDEX_RUNNING. If the generator is in that bizarre "closing"
-  // state, the resumeIndex is RESUME_INDEX_CLOSING.
+  // RESUME_INDEX_RUNNING.
   //
   // If the generator is suspended, it's the resumeIndex (stored as
   // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield instruction
   // that suspended the generator. The resumeIndex can be mapped to the
   // bytecode offset (interpreter) or to the native code offset (JIT).
 
   bool isBeforeInitialYield() const {
     return getFixedSlot(RESUME_INDEX_SLOT).isUndefined();
   }
   bool isRunning() const {
     return getFixedSlot(RESUME_INDEX_SLOT) == Int32Value(RESUME_INDEX_RUNNING);
   }
-  bool isClosing() const {
-    return getFixedSlot(RESUME_INDEX_SLOT) == Int32Value(RESUME_INDEX_CLOSING);
-  }
   bool isSuspended() const {
     // Note: also update Baseline's IsSuspendedGenerator code if this
     // changes.
-    static_assert(RESUME_INDEX_CLOSING < RESUME_INDEX_RUNNING,
-                  "test below should return false for RESUME_INDEX_RUNNING");
     Value resumeIndex = getFixedSlot(RESUME_INDEX_SLOT);
-    return resumeIndex.isInt32() && resumeIndex.toInt32() < RESUME_INDEX_CLOSING;
+    return resumeIndex.isInt32() && resumeIndex.toInt32() < RESUME_INDEX_RUNNING;
   }
   void setRunning() {
     MOZ_ASSERT(isSuspended());
     setFixedSlot(RESUME_INDEX_SLOT, Int32Value(RESUME_INDEX_RUNNING));
   }
-  void setClosing() {
-    MOZ_ASSERT(isRunning());
-    setFixedSlot(RESUME_INDEX_SLOT, Int32Value(RESUME_INDEX_CLOSING));
-  }
   void setResumeIndex(jsbytecode* pc) {
     MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD ||
                *pc == JSOP_AWAIT);
 
     MOZ_ASSERT_IF(JSOp(*pc) == JSOP_INITIALYIELD,
                   getFixedSlot(RESUME_INDEX_SLOT).isUndefined());
-    MOZ_ASSERT_IF(JSOp(*pc) != JSOP_INITIALYIELD, isRunning() || isClosing());
+    MOZ_ASSERT_IF(JSOp(*pc) != JSOP_INITIALYIELD, isRunning());
 
     uint32_t resumeIndex = GET_UINT24(pc);
-    MOZ_ASSERT(resumeIndex < uint32_t(RESUME_INDEX_CLOSING));
+    MOZ_ASSERT(resumeIndex < uint32_t(RESUME_INDEX_RUNNING));
 
     setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex));
     MOZ_ASSERT(isSuspended());
   }
   void setResumeIndex(int32_t resumeIndex) {
     setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex));
   }
   uint32_t resumeIndex() const {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -961,17 +961,17 @@ bool js::intrinsic_IsSuspendedGenerator(
 
 static bool intrinsic_GeneratorIsRunning(JSContext* cx, unsigned argc,
                                          Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   MOZ_ASSERT(args.length() == 1);
   MOZ_ASSERT(args[0].isObject());
 
   GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>();
-  args.rval().setBoolean(genObj->isRunning() || genObj->isClosing());
+  args.rval().setBoolean(genObj->isRunning());
   return true;
 }
 
 static bool intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc,
                                          Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   MOZ_ASSERT(args.length() == 1);
   MOZ_ASSERT(args[0].isObject());
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2415,18 +2415,17 @@ nsView* nsDocumentViewer::FindContainerV
     return nullptr;
   }
 
   nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
   if (!containerElement) {
     return nullptr;
   }
 
-  nsIFrame* subdocFrame =
-      nsLayoutUtils::GetRealPrimaryFrameFor(containerElement);
+  nsIFrame* subdocFrame = containerElement->GetPrimaryFrame();
   if (!subdocFrame) {
     // XXX Silenced by default in bug 1175289
     LAYOUT_WARNING("Subdocument container has no frame");
     return nullptr;
   }
 
   // subdocFrame might not be a subdocument frame; the frame
   // constructor can treat a <frame> as an inline in some XBL
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1574,26 +1574,16 @@ bool nsLayoutUtils::IsPrimaryStyleFrame(
   const nsIFrame* parent = aFrame->GetParent();
   if (parent && parent->IsTableWrapperFrame()) {
     return parent->PrincipalChildList().FirstChild() == aFrame;
   }
 
   return aFrame->IsPrimaryFrame();
 }
 
-/* static */
-nsIFrame* nsLayoutUtils::GetRealPrimaryFrameFor(const nsIContent* aContent) {
-  nsIFrame* frame = aContent->GetPrimaryFrame();
-  if (!frame) {
-    return nullptr;
-  }
-
-  return nsPlaceholderFrame::GetRealFrameFor(frame);
-}
-
 nsIFrame* nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
   NS_ASSERTION(aFrame->IsPlaceholderFrame(), "Must have a placeholder here");
   if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) {
     nsIFrame* outOfFlowFrame =
         nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
     NS_ASSERTION(outOfFlowFrame && outOfFlowFrame->IsFloating(),
                  "How did that happen?");
     return outOfFlowFrame;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -429,25 +429,16 @@ class nsLayoutUtils {
       const nsIFrame* aStyleFrame);
 
   /**
    * Similar to nsIFrame::IsPrimaryFrame except that this will return true
    * for the inner table frame rather than for its wrapper frame.
    */
   static bool IsPrimaryStyleFrame(const nsIFrame* aFrame);
 
-  /**
-   * Gets the real primary frame associated with the content object.
-   *
-   * In the case of absolutely positioned elements and floated elements,
-   * the real primary frame is the frame that is out of the flow and not the
-   * placeholder frame.
-   */
-  static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent);
-
 #ifdef DEBUG
   // TODO: remove, see bug 598468.
   static bool gPreventAssertInCompareTreePosition;
 #endif  // DEBUG
 
   /**
    * CompareTreePosition determines whether aContent1 comes before or
    * after aContent2 in a preorder traversal of the content tree.
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1900,28 +1900,30 @@ bool BuildTextRunsScanner::ContinueTextR
     return false;
   }
 
   if (sc1 == sc2) {
     return true;
   }
 
   const nsStyleText* textStyle2 = sc2->StyleText();
-  if (sc1 == sc2) return true;
+  if (textStyle1->mTextTransform != textStyle2->mTextTransform ||
+      textStyle1->EffectiveWordBreak() != textStyle2->EffectiveWordBreak()) {
+    return false;
+  }
 
   nsPresContext* pc = aFrame1->PresContext();
   MOZ_ASSERT(pc == aFrame2->PresContext());
 
   const nsStyleFont* fontStyle1 = sc1->StyleFont();
   const nsStyleFont* fontStyle2 = sc2->StyleFont();
   nscoord letterSpacing1 = LetterSpacing(aFrame1);
   nscoord letterSpacing2 = LetterSpacing(aFrame2);
   return fontStyle1->mFont == fontStyle2->mFont &&
          fontStyle1->mLanguage == fontStyle2->mLanguage &&
-         textStyle1->mTextTransform == textStyle2->mTextTransform &&
          nsLayoutUtils::GetTextRunFlagsForStyle(sc1, pc, fontStyle1, textStyle1,
                                                 letterSpacing1) ==
              nsLayoutUtils::GetTextRunFlagsForStyle(sc2, pc, fontStyle2,
                                                     textStyle2, letterSpacing2);
 }
 
 void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame) {
   LayoutFrameType frameType = aFrame->Type();
@@ -2565,17 +2567,18 @@ static bool HasCompressedLeadingWhitespa
   }
   return false;
 }
 
 void BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
                                                      const void* aTextPtr) {
   using mozilla::intl::LineBreaker;
 
-  auto wordBreak = mLineContainer->StyleText()->EffectiveWordBreak();
+  auto wordBreak =
+    mMappedFlows[0].mStartFrame->StyleText()->EffectiveWordBreak();
   switch (wordBreak) {
     case StyleWordBreak::BreakAll:
       mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_BreakAll);
       break;
     case StyleWordBreak::KeepAll:
       mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_KeepAll);
       break;
     case StyleWordBreak::Normal:
@@ -5850,17 +5853,17 @@ void nsTextFrame::PaintOneShadow(const P
   nscolor shadowColor =
       aShadowDetails->mColor.CalcColor(aParams.foregroundColor);
 
   if (auto* textDrawer = aParams.context->GetTextDrawer()) {
     wr::Shadow wrShadow;
 
     // Gecko already inflates the bounding rect of text shadows,
     // so tell WR not to inflate again.
-    wrShadow.should_inflate = false;
+    wrShadow.should_inflate = true;
 
     wrShadow.offset = {
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)};
 
     wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(blurRadius);
     wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8980,26 +8980,20 @@ void nsDisplayText::Paint(nsDisplayListB
 bool nsDisplayText::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   auto* f = static_cast<nsTextFrame*>(mFrame);
   auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
 
-  // FIXME: the webrender backend is having a lot of snapping issues, and
-  // having it do inflation for us is causing problems. For now, we pass
-  // down the wrong bounds. Try to turn this back on when things are in
-  // better shape.
-  //
-  // nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
+  nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
   // Bug 748228
-  // bounds.Inflate(appUnitsPerDevPixel);
-
-  nsRect bounds = mBounds;
+  bounds.Inflate(appUnitsPerDevPixel);
+
   if (bounds.IsEmpty()) {
     return true;
   }
 
   gfx::Point deviceOffset =
       LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
           .ToUnknownPoint();
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2097,14 +2097,14 @@ fuzzy(0-1,0-625) == 1466638-1.html 14666
 == 1483649-1.xul 1483649-1-ref.xul
 test-pref(layout.css.contain.enabled,true) == 1483946.html 1483946-ref.html
 test-pref(layout.css.visited_links_enabled,false) == 1488155.html 1488155-ref.html
 == 1492660-1.html 1492660-1-ref.html
 pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
 pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
 == 1509425-1.html 1509425-1-ref.html
 == 1511570.html 1511570-ref.html
-fuzzy-if(!webrender,1-5,66-547) fails-if(webrender) == 1529992-1.html 1529992-1-ref.html
+fuzzy-if(!webrender,1-5,66-547) == 1529992-1.html 1529992-1-ref.html
 fuzzy-if(!webrender,0-6,0-34) fails-if(webrender) == 1529992-2.html 1529992-2-ref.html
 == 1535040-1.html 1535040-1-ref.html
 == 1545360-1.xhtml 1545360-1-ref.xhtml
 skip-if(!asyncPan) == 1544895.html 1544895-ref.html
 == 1548809.html 1548809-ref.html
--- a/layout/reftests/text-shadow/reftest.list
+++ b/layout/reftests/text-shadow/reftest.list
@@ -1,16 +1,16 @@
 == 723669.html 723669-ref.html
 
 == basic.xul basic-ref.xul
 random-if(Android) == basic-negcoord.xul basic-negcoord-ref.xul
 != blur.xul blur-notref.xul
 == color-inherit.xul color-inherit-ref.xul
 == multiple-noblur.xul multiple-noblur-ref.xul
-== blur-opacity.html blur-opacity-ref.html
+fuzzy-if(webrender&&gtkWidget,128-128,160-160) == blur-opacity.html blur-opacity-ref.html
 
 == basic.html basic-ref.html
 == basic-negcoord.html basic-negcoord-ref.html
 == basic-opacity.html basic-opacity-ref.html
 != blur.html blur-notref.html
 == color-inherit.html color-inherit-ref.html
 == color-parserorder.html color-parserorder-ref.html
 == decorations-multiple-zorder.html decorations-multiple-zorder-ref.html
--- a/mfbt/IntegerRange.h
+++ b/mfbt/IntegerRange.h
@@ -136,30 +136,30 @@ class IntegerRange {
 
  private:
   IntTypeT mBegin;
   IntTypeT mEnd;
 };
 
 template <typename T, bool = IsUnsigned<T>::value>
 struct GeqZero {
-  static bool check(T t) { return t >= 0; }
+  static bool isNonNegative(T t) { return t >= 0; }
 };
 
 template <typename T>
 struct GeqZero<T, true> {
-  static bool check(T t) { return true; }
+  static bool isNonNegative(T t) { return true; }
 };
 
 }  // namespace detail
 
 template <typename IntType>
 detail::IntegerRange<IntType> IntegerRange(IntType aEnd) {
   static_assert(IsIntegral<IntType>::value, "value must be integral");
-  MOZ_ASSERT(detail::GeqZero<IntType>::check(aEnd),
+  MOZ_ASSERT(detail::GeqZero<IntType>::isNonNegative(aEnd),
              "Should never have negative value here");
   return detail::IntegerRange<IntType>(aEnd);
 }
 
 template <typename IntType1, typename IntType2>
 detail::IntegerRange<IntType2> IntegerRange(IntType1 aBegin, IntType2 aEnd) {
   static_assert(IsIntegral<IntType1>::value && IsIntegral<IntType2>::value,
                 "values must both be integral");
--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -761,17 +761,17 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_
                         std::forward<Ms>(aMs)...);
   }
 
   /**
    * Incorporate the current variant's tag into hashValue.
    * Note that this does not hash the actual contents; you must take
    * care of that yourself, perhaps by using a match.
    */
-  mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) {
+  mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
     return mozilla::AddToHash(hashValue, tag);
   }
 };
 
 /*
  * AsVariant() is used to construct a Variant<T,...> value containing the
  * provided T value using type inference. It can be used to construct Variant
  * values in expressions or return them from functions without specifying the
deleted file mode 100644
index 28c375a4d9cd94354d3dce46ef9a1cecf5592561..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/app/src/main/res/xml/preferences_notifications.xml
+++ b/mobile/android/app/src/main/res/xml/preferences_notifications.xml
@@ -1,10 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <org.mozilla.gecko.preferences.NotificationSettingsLinkPreference
+        android:key="android.not_a_preference.notifications.settings_link"
+        android:title="@string/pref_notification_settings_link" />
     <SwitchPreference android:key="android.not_a_preference.notifications.whats_new"
         android:title="@string/pref_whats_new_notification"
         android:summary="@string/pref_whats_new_notification_summary"
         android:defaultValue="true" />
     <SwitchPreference android:key="android.not_a_preference.notifications.features.tips"
         android:title="@string/pref_feature_tips_notification"
         android:summary="@string/pref_feature_tips_notification_summary"
         android:defaultValue="true" />
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -204,29 +204,25 @@ public class GeckoApplication extends Ap
 
         final BrowserDB db = BrowserDB.from(this);
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 db.expireHistory(getContentResolver(), BrowserContract.ExpirePriority.NORMAL);
             }
         });
-
-        GeckoNetworkManager.getInstance().stop();
     }
 
     public void onApplicationForeground() {
         if (mIsInitialResume) {
             GeckoBatteryManager.getInstance().start(this);
-            GeckoNetworkManager.getInstance().start(this);
             mIsInitialResume = false;
         } else if (mPausedGecko) {
             GeckoThread.onResume();
             mPausedGecko = false;
-            GeckoNetworkManager.getInstance().start(this);
         }
 
         mInBackground = false;
     }
 
     private void initFontScaleListener() {
         final SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
         prefs.registerOnSharedPreferenceChangeListener(this);
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/DefaultBrowserPreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/DefaultBrowserPreference.java
@@ -18,16 +18,20 @@ public class DefaultBrowserPreference ex
     public DefaultBrowserPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     public DefaultBrowserPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
 
+    /**
+     * Open Default apps screen of Settings for API Levels>=24.
+     * Support URL will open for lower API levels.
+     */
     @Override
     protected void onClick() {
         if (GeckoPreferences.PREFS_DEFAULT_BROWSER.equals(getKey()) && AppConstants.Versions.feature24Plus) {
             Toast.makeText(this.getContext(),
                     this.getContext().getString(R.string.default_browser_system_settings_toast),
                     Toast.LENGTH_LONG).show();
 
             // We are special casing the link to set the default browser here: On old Android versions we
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -1,16 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.preferences;
 
 import android.Manifest;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.NotificationManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -149,16 +150,17 @@ public class GeckoPreferences
     private static final String PREFS_SCREEN_ADVANCED = NON_PREF_PREFIX + "advanced_screen";
     public static final String PREFS_HOMEPAGE = NON_PREF_PREFIX + "homepage";
     public static final String PREFS_HOMEPAGE_FOR_EVERY_NEW_TAB = NON_PREF_PREFIX + "newtab.load_homepage";
     public static final String PREFS_HOMEPAGE_PARTNER_COPY = GeckoPreferences.PREFS_HOMEPAGE + ".partner";
     public static final String PREFS_HISTORY_SAVED_SEARCH = NON_PREF_PREFIX + "search.search_history.enabled";
     private static final String PREFS_FAQ_LINK = NON_PREF_PREFIX + "faq.link";
     private static final String PREFS_FEEDBACK_LINK = NON_PREF_PREFIX + "feedback.link";
     private static final String PREFS_SCREEN_NOTIFICATIONS = NON_PREF_PREFIX + "notifications_screen";
+    private static final String PREFS_NOTIFICATIONS_SETTINGS_LINK = NON_PREF_PREFIX + "notifications.settings_link";
     public static final String PREFS_NOTIFICATIONS_WHATS_NEW = NON_PREF_PREFIX + "notifications.whats_new";
     public static final String PREFS_NOTIFICATIONS_FEATURES_TIPS = NON_PREF_PREFIX + "notifications.features.tips";
     public static final String PREFS_APP_UPDATE_LAST_BUILD_ID = "app.update.last_build_id";
     public static final String PREFS_READ_PARTNER_CUSTOMIZATIONS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_customizations_provider";
     public static final String PREFS_READ_PARTNER_BOOKMARKS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_bookmarks_provider";
     public static final String PREFS_CUSTOM_TABS = NON_PREF_PREFIX + "customtabs_58";
     public static final String PREFS_CATEGORY_EXPERIMENTAL_FEATURES = NON_PREF_PREFIX + "category_experimental";
     public static final String PREFS_COMPACT_TABS = NON_PREF_PREFIX + "compact_tabs";
@@ -232,16 +234,17 @@ public class GeckoPreferences
         final String newTitle = getString(title);
         if (newTitle != null) {
             Log.v(LOGTAG, "Setting action bar title to " + newTitle);
 
             setTitle(newTitle);
         }
     }
 
+    @SuppressLint("PrivateResource")
     private void updateHomeAsUpIndicator() {
         final ActionBar actionBar = getSupportActionBar();
         if (actionBar == null) {
             return;
         }
         actionBar.setHomeAsUpIndicator(android.support.v7.appcompat.R.drawable.abc_ic_ab_back_material);
     }
 
@@ -842,16 +845,22 @@ public class GeckoPreferences
                         continue;
                     }
                 } else if (PREFS_COMPACT_TABS.equals(key)) {
                     if (HardwareUtils.isTablet()) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
+                } else if (PREFS_NOTIFICATIONS_SETTINGS_LINK.equals(key)) {
+                    if (!showNotificationSettingsLink()) {
+                        preferences.removePreference(pref);
+                        i--;
+                        continue;
+                    }
                 } else if (PREFS_NOTIFICATIONS_WHATS_NEW.equals(key)) {
                     if (!SwitchBoard.isInExperiment(this, Experiments.WHATSNEW_NOTIFICATION)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
                 } else if (PREFS_NOTIFICATIONS_FEATURES_TIPS.equals(key)) {
                     final boolean isLeanplumAvailable = MmaDelegate.isMmaExperimentEnabled(this);
@@ -1526,13 +1535,19 @@ public class GeckoPreferences
         return isHealthReportEnabled && isInMmaExperiment && areMmaMessagesAllowed;
     }
 
     public static boolean isHealthReportEnabled(Context context) {
         // Health Report is enabled by default so we'll return true if the preference is not found
         return GeckoPreferences.getBooleanPref(context, PREFS_HEALTHREPORT_UPLOAD_ENABLED, true);
     }
 
+    private static boolean showNotificationSettingsLink() {
+        // Notification channels are available starting from Oreo.
+        return AppConstants.Versions.feature26Plus;
+    }
+
     private static boolean haveNotificationsPreferences(@NonNull Context context) {
-        return SwitchBoard.isInExperiment(context, Experiments.WHATSNEW_NOTIFICATION) ||
+        return showNotificationSettingsLink() ||
+                SwitchBoard.isInExperiment(context, Experiments.WHATSNEW_NOTIFICATION) ||
                 MmaDelegate.isMmaExperimentEnabled(context);
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/LinkPreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/LinkPreference.java
@@ -22,17 +22,14 @@ class LinkPreference extends Preference 
         super(context, attrs, defStyle);
         mUrl = attrs.getAttributeValue(null, "url");
     }
 
     public void setUrl(String url) {
         mUrl = url;
     }
 
-    /**
-     * Open Default apps screen of Settings for API Levels>=24. Support URL will open for lower API levels
-     */
     @Override
     protected void onClick() {
         Tabs.getInstance().loadUrlInTab(mUrl);
         callChangeListener(mUrl);
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/NotificationSettingsLinkPreference.java
@@ -0,0 +1,38 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.preferences;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+/**
+ * On Android O and following, this will open the app's OS notification settings when clicked on.
+ */
+public class NotificationSettingsLinkPreference extends Preference {
+    public NotificationSettingsLinkPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+    public NotificationSettingsLinkPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onClick() {
+        final Context context = getContext();
+        final Intent intent = getLinkIntent(context);
+        context.startActivity(intent);
+    }
+
+    @TargetApi(26)
+    private Intent getLinkIntent(final Context context) {
+        return new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
+    }
+}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -914,16 +914,17 @@ See also https://bug1409261.bmoattachmen
 
 <!-- Used by accessibility services to identify the play/pause buttons shown in the
 Picture-in-picture mini window -->
 <!ENTITY pip_play_button_title "Play">
 <!ENTITY pip_play_button_description "Resume playing">
 <!ENTITY pip_pause_button_title "Pause">
 <!ENTITY pip_pause_button_description "Pause playing">
 
+<!ENTITY pref_notification_settings_link "Manage notification settings">
 <!-- Notification channels names -->
 <!ENTITY default_notification_channel2 "Browser">
 <!ENTITY mls_notification_channel "&vendorShortName; Location Service">
 <!ENTITY download_notification_channel "Downloads">
 <!ENTITY media_notification_channel2 "Sound and video">
 <!-- These push notifications come without a specific channel and/or name from Leanplum -->
 <!ENTITY leanplum_default_notifications_channel2 "&vendorShortName; tips and tricks">
 <!ENTITY updater_notification_channel "App updates">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -654,16 +654,17 @@
   <string name="pwa_onboarding_sumo">&pwa_onboarding_sumo;</string>
   <string name="pwa_continue_to_website">&pwa_continue_to_website;</string>
 
   <string name="pip_play_button_title">&pip_play_button_title;</string>
   <string name="pip_play_button_description">&pip_play_button_description;</string>
   <string name="pip_pause_button_title">&pip_pause_button_title;</string>
   <string name="pip_pause_button_description">&pip_pause_button_description;</string>
 
+  <string name="pref_notification_settings_link">&pref_notification_settings_link;</string>
   <string name="default_notification_channel2">&default_notification_channel2;</string>
   <string name="mls_notification_channel">&mls_notification_channel;</string>
   <string name="media_notification_channel2">&media_notification_channel2;</string>
   <string name="download_notification_channel">&download_notification_channel;</string>
   <string name="leanplum_default_notifications_channel2">&leanplum_default_notifications_channel2;</string>
   <string name="updater_notification_channel">&updater_notification_channel;</string>
   <string name="synced_tabs_notification_channel">&synced_tabs_notification_channel;</string>
   <string name="site_notifications_channel">&site_notifications_channel;</string>
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1766,19 +1766,17 @@ var BrowserApp = {
 
         console.log("New OS locale.");
 
         // Ensure that this choice is immediately persisted, because
         // Gecko won't be told again if it forgets.
         Services.prefs.setCharPref("intl.locale.os", languageTag);
         Services.prefs.savePrefFile(null);
 
-        let appLocale = this.getUALocalePref();
-
-        this.computeAcceptLanguages(languageTag, appLocale);
+        this.computeAcceptLanguages(languageTag);
 
         // Rebuild strings, in case we're mirroring OS locale.
         Strings.flush();
         break;
       }
 
       case "Locale:Changed": {
         if (data) {
@@ -1790,17 +1788,17 @@ var BrowserApp = {
         console.log("Gecko display locale: " + this.getUALocalePref());
 
         // Rebuild strings to reflect the new locale.
         Strings.flush();
 
         // Make sure we use the right Accept-Language header.
         let osLocale = Services.prefs.getCharPref("intl.locale.os");
 
-        this.computeAcceptLanguages(osLocale, data && data.languageTag);
+        this.computeAcceptLanguages(osLocale);
         break;
       }
 
       case "Passwords:Init": {
         let storage = Cc["@mozilla.org/login-manager/storage/mozStorage;1"].
                       getService(Ci.nsILoginManagerStorage);
         storage.initialize();
         GlobalEventDispatcher.unregisterListener(this, event);
@@ -2148,64 +2146,62 @@ var BrowserApp = {
    * Set intl.accept_languages accordingly.
    *
    * After Bug 881510 this will also accept a real Accept-Language choice as
    * input; all Accept-Language logic lives here.
    *
    * osLocale should never be null, but this method is safe regardless.
    * appLocale may explicitly be null.
    */
-  computeAcceptLanguages(osLocale, appLocale) {
+  computeAcceptLanguages(osLocale) {
     let defaultBranch = Services.prefs.getDefaultBranch(null);
     let defaultAccept = defaultBranch.getComplexValue("intl.accept_languages", Ci.nsIPrefLocalizedString).data;
     console.log("Default intl.accept_languages = " + defaultAccept);
 
     // A guard for potential breakage. Bug 438031.
     // This should not be necessary, because we're reading from the default branch,
     // but better safe than sorry.
     if (defaultAccept && defaultAccept.startsWith("chrome://")) {
       defaultAccept = null;
     } else {
       // Ensure lowercase everywhere so we can compare.
       defaultAccept = defaultAccept.toLowerCase();
     }
 
-    if (appLocale) {
-      appLocale = appLocale.toLowerCase();
-    }
-
     try {
         const resistFingerprinting = Services.prefs.getBoolPref("privacy.resistFingerprinting");
         if (resistFingerprinting) {
           osLocale = null;
         }
     } catch (e) {}
 
+    // Explicitly-set app prefs come first:
+    let chosen = Services.prefs.getCharPref("intl.locale.requested", "")
+                               .split(",")
+                               .map((x) => x.trim().toLowerCase())
+                               .filter((x) => x.length > 0);
+
+    // OS prefs come second:
     if (osLocale) {
       osLocale = osLocale.toLowerCase();
-    }
-
-    // Eliminate values if they're present in the default.
-    let chosen;
+      if (!chosen.includes(osLocale)) {
+        chosen.push(osLocale);
+      }
+    }
+
+    // Default app prefs come third:
     if (defaultAccept) {
       // intl.accept_languages is a comma-separated list, with no q-value params. Those
       // are added when the header is generated.
-      chosen = defaultAccept.split(",")
-                            .map((x) => x.trim())
-                            .filter((x) => (x != appLocale && x != osLocale));
-    } else {
-      chosen = [];
-    }
-
-    if (osLocale) {
-      chosen.unshift(osLocale);
-    }
-
-    if (appLocale && appLocale != osLocale) {
-      chosen.unshift(appLocale);
+      let defaults = defaultAccept.split(",").map((x) => x.trim());
+      for (let locale of defaults) {
+        if (!chosen.includes(locale)) {
+          chosen.push(locale);
+        }
+      }
     }
 
     let result = chosen.join(",");
     console.log("Setting intl.accept_languages to " + result);
     this.setLocalizedPref("intl.accept_languages", result);
   },
 
   // nsIAndroidBrowserApp
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/mozconfigs/android-aarch64/profile-use
@@ -0,0 +1,10 @@
+. "$topsrcdir/mobile/android/config/mozconfigs/android-aarch64/nightly"
+
+export LLVM_PROFDATA="$topsrcdir/clang/bin/llvm-profdata"
+
+ac_add_options --enable-profile-use
+
+# This is disabled because jarlog re-ordering breaks apk publishing tasks,
+# see bug 1539933.
+# ac_add_options --with-pgo-jarlog=/builds/worker/fetches/en-US.log
+ac_add_options --with-pgo-profile-path=/builds/worker/fetches/default.profraw
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -2001,39 +2001,29 @@ public class GeckoAppShell {
     @WrapForJNI(calledFrom = "any")
     public static void setCommunicationAudioModeOn(final boolean on) {
         final AudioManager am = (AudioManager)getApplicationContext()
                                 .getSystemService(Context.AUDIO_SERVICE);
         if (am == null) {
             return;
         }
 
-        if (sPreviousAudioMode == AudioManager.MODE_INVALID) {
-            sPreviousAudioMode = am.getMode();
-        }
-
         try {
             if (on)  {
                 Log.e(LOGTAG, "Setting communication mode ON");
-                sPreviousAudioMode = am.getMode();
                 am.startBluetoothSco();
                 am.setBluetoothScoOn(true);
-                am.setMode(AudioManager.MODE_IN_COMMUNICATION);
             } else {
                 Log.e(LOGTAG, "Setting communication mode OFF");
-                am.setMode(sPreviousAudioMode);
-                sPreviousAudioMode = AudioManager.MODE_INVALID;
                 am.stopBluetoothSco();
                 am.setBluetoothScoOn(false);
             }
         } catch (SecurityException e) {
             Log.e(LOGTAG, "could not set communication mode", e);
         }
-
-        am.setSpeakerphoneOn(!on);
     }
 
     private static String getLanguageTag(final Locale locale) {
         final StringBuilder out = new StringBuilder(locale.getLanguage());
         final String country = locale.getCountry();
         final String variant = locale.getVariant();
         if (!TextUtils.isEmpty(country)) {
             out.append('-').append(country);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java
@@ -114,17 +114,17 @@ public class GeckoDisplay {
      *
      * @param clippingHeight The height of the bottom clipped space in screen pixels.
      */
     @UiThread
     public void setVerticalClipping(final int clippingHeight) {
         ThreadUtils.assertOnUiThread();
 
         if (mSession != null) {
-            mSession.mCompositor.setFixedBottomOffset(clippingHeight);
+            mSession.setFixedBottomOffset(clippingHeight);
         }
     }
 
     /**
      * Return whether the display should be pinned on the screen.
      *
      * When pinned, the display should not be moved on the screen due to animation, scrolling, etc.
      * A common reason for the display being pinned is when the user is dragging a selection caret
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -128,16 +128,17 @@ public class GeckoSession implements Par
     private int mLeft;
     private int mTop; // Top of the surface (including toolbar);
     private int mClientTop; // Top of the client area (i.e. excluding toolbar);
     private int mOffsetX;
     private int mOffsetY;
     private int mWidth;
     private int mHeight; // Height of the surface (including toolbar);
     private int mClientHeight; // Height of the client area (i.e. excluding toolbar);
+    private int mFixedBottomOffset; // The margin for fixed elements attached to the bottom of the viewport.
     private float mViewportLeft;
     private float mViewportTop;
     private float mViewportZoom = 1.0f;
 
     //
     // NOTE: These values are also defined in
     // gfx/layers/ipc/UiCompositorControllerMessageTypes.h and must be kept in sync. Any
     // new AnimatorMessageType added here must also be added there.
@@ -4745,16 +4746,25 @@ public class GeckoSession implements Par
             return;
         }
 
         mLeft = left;
         mTop = top;
         onWindowBoundsChanged();
     }
 
+    /* package */ void setFixedBottomOffset(final int offset) {
+        mFixedBottomOffset = offset;
+
+        if (mCompositorReady) {
+            mCompositor.setFixedBottomOffset(mFixedBottomOffset);
+        }
+    }
+
+
     /* package */ void onCompositorAttached() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         mAttachedCompositor = true;
         mCompositor.attachNPZC(mPanZoomController.mNative);
 
@@ -4865,16 +4875,18 @@ public class GeckoSession implements Par
             // compositor now that the compositor is ready.
             onSurfaceChanged(mSurface, mOffsetX, mOffsetY, mWidth, mHeight);
             mSurface = null;
         }
 
         if (mToolbar != null) {
             mToolbar.onCompositorReady();
         }
+
+        mCompositor.setFixedBottomOffset(mFixedBottomOffset);
     }
 
     /* package */ void updateOverscrollVelocity(final float x, final float y) {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         if (mOverscroll == null) {
--- a/mobile/android/themes/core/aboutReader.css
+++ b/mobile/android/themes/core/aboutReader.css
@@ -23,16 +23,21 @@ body.light {
   color: #222222;
 }
 
 body.dark {
   background-color: #222222;
   color: #eeeeee;
 }
 
+body.sepia {
+  color: #5b4636;
+  background-color: #f4ecd8;
+}
+
 body.sans-serif {
   font-family: sans-serif;
 }
 
 body.serif {
   font-family: serif;
 }
 
@@ -64,38 +69,61 @@ body.dark > .container > .header > .doma
 body.dark > .container > .header > h1 {
   color: #eeeeee;
 }
 
 body.dark > .container > .header > .credits {
   color: #aaaaaa;
 }
 
+body.sepia > .container > .header > .domain {
+  border-bottom-color: #5b4636;
+}
+
+body.sepia > .container > .header > h1 {
+  color: #5b4636;
+}
+
+body.sepia > .container > .header > .credits {
+  color: #5b4636;
+}
+
 body.light > .container > .content .caption,
 body.light > .container > .content .wp-caption-text,
 body.light > .container > .content figcaption {
   color: #898989;
 }
 
 body.dark > .container > .content .caption,
 body.dark > .container > .content .wp-caption-text,
 body.dark > .container > .content figcaption {
   color: #aaaaaa;
 }
 
+body.sepia > .container > .content .caption,
+body.sepia > .container > .content .wp-caption-text,
+body.sepia > .container > .content figcaption {
+  color: #5b4636;
+}
+
 body.light > .container > .content blockquote {
   color: #898989 !important;
   border-left-color: #d0d0d0 !important;
 }
 
 body.dark > .container > .content blockquote {
   color: #aaaaaa !important;
   border-left-color: #777777 !important;
 }
 
+body.sepia > .container > .content blockquote {
+  color: #5b4636 !important;
+  border-left-color: #777777 !important;
+}
+
 .reader-message {
   margin-top: 40px;
   display: none;
   text-align: center;
   width: 100%;
   font-size: 0.9em;
 }
 
@@ -310,16 +338,21 @@ body.dark > .container > .content blockq
   border: 2px solid #0A84FF;
 }
 
 .dark-button {
   color: #eeeeee;
   background-color: #333333;
 }
 
+.sepia-button {
+  color: #5b4636;
+  background-color: #f4ecd8;
+}
+
 .auto-button {
   color: #000000;
   background-color: transparent;
 }
 
 .light-button {
   color: #333333;
   background-color: #ffffff;
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -361,17 +361,17 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "dom.webnotifications.allowinsecure",
    dom_webnotifications_allowinsecure,
   RelaxedAtomicBool, false
 )
 
-#ifdef NIGHTLY_BUILD
+#ifdef EARLY_BETA_OR_EARLIER
 # define PREF_VALUE  true
 #else
 # define PREF_VALUE  false
 #endif
 VARCACHE_PREF(
   "dom.webnotifications.requireuserinteraction",
    dom_webnotifications_requireuserinteraction,
   RelaxedAtomicBool, PREF_VALUE
@@ -1590,17 +1590,17 @@ VARCACHE_PREF(
   RelaxedAtomicBool, PREF_VALUE
 )
 #undef PREF_VALUE
 
 #if defined(XP_WIN) && !defined(_ARM64_)
 # define PREF_VALUE true
 #elif defined(XP_MACOSX)
 # define PREF_VALUE true
-#elif defined(XP_UNIX)
+#elif defined(XP_LINUX) && !defined(ANDROID)
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
 VARCACHE_PREF(
   "media.rdd-process.enabled",
    MediaRddProcessEnabled,
   RelaxedAtomicBool, PREF_VALUE
--- a/mozglue/build/Authenticode.cpp
+++ b/mozglue/build/Authenticode.cpp
@@ -26,27 +26,30 @@
 #  define NTDDI_VERSION NTDDI_WIN8
 #endif  // defined(NTDDI_VERSION)
 
 #include "Authenticode.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 
 #include <windows.h>
 #include <softpub.h>
 #include <wincrypt.h>
 #include <wintrust.h>
 #include <mscat.h>
 
+#include <string.h>
+
 namespace {
 
 struct CertStoreDeleter {
   typedef HCERTSTORE pointer;
   void operator()(pointer aStore) { ::CertCloseStore(aStore, 0); }
 };
 
 struct CryptMsgDeleter {
@@ -302,22 +305,37 @@ bool SignedBinary::VerifySignature(const
   static const mozilla::DynamicallyLinkedFunctionPtr<decltype(
       &::CryptCATAdminEnumCatalogFromHash)>
       pCryptCATAdminEnumCatalogFromHash(L"wintrust.dll",
                                         "CryptCATAdminEnumCatalogFromHash");
   if (!pCryptCATAdminEnumCatalogFromHash) {
     return false;
   }
 
+  static const mozilla::DynamicallyLinkedFunctionPtr<decltype(
+      &::CryptCATAdminReleaseCatalogContext)>
+    pCryptCATAdminReleaseCatalogContext(L"wintrust.dll",
+                                        "CryptCATAdminReleaseCatalogContext");
+  if (!pCryptCATAdminReleaseCatalogContext) {
+    return false;
+  }
+
   HCATINFO catInfoHdl = pCryptCATAdminEnumCatalogFromHash(
       rawCatAdmin, hashBuf.get(), hashLen, 0, nullptr);
   if (!catInfoHdl) {
     return false;
   }
 
+  // We can't use UniquePtr for this because the deleter function requires two
+  // parameters.
+  auto cleanCatInfoHdl = mozilla::MakeScopeExit(
+    [rawCatAdmin, catInfoHdl]() -> void {
+    pCryptCATAdminReleaseCatalogContext(rawCatAdmin, catInfoHdl, 0);
+  });
+
   // We found a catalog! Now query for the path to the catalog file.
 
   static const mozilla::DynamicallyLinkedFunctionPtr<decltype(
       &::CryptCATCatalogInfoFromContext)>
       pCryptCATCatalogInfoFromContext(L"wintrust.dll",
                                       "CryptCATCatalogInfoFromContext");
   if (!pCryptCATCatalogInfoFromContext) {
     return false;
@@ -334,16 +352,23 @@ bool SignedBinary::VerifySignature(const
   DWORD strHashBufLen = (hashLen * 2) + 1;
   auto strHashBuf = mozilla::MakeUnique<wchar_t[]>(strHashBufLen);
   if (!::CryptBinaryToStringW(hashBuf.get(), hashLen,
                               CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
                               strHashBuf.get(), &strHashBufLen)) {
     return false;
   }
 
+  // Ensure that the tag is uppercase for WinVerifyTrust
+  // NB: CryptBinaryToStringW overwrites strHashBufLen with the length excluding
+  //     the null terminator, so we need to add it back for this call.
+  if (_wcsupr_s(strHashBuf.get(), strHashBufLen + 1)) {
+    return false;
+  }
+
   // Now, given the path to the catalog, and the path to the member (ie, the
   // binary whose hash we are validating), we may now validate. If the
   // validation is successful, we then QueryObject on the *catalog file*
   // instead of the binary.
 
   WINTRUST_CATALOG_INFO wtCatInfo = {sizeof(wtCatInfo)};
   wtCatInfo.pcwszCatalogFilePath = catInfo.wszCatalogFile;
   wtCatInfo.pcwszMemberTag = strHashBuf.get();
--- a/remote/domains/content/Page.jsm
+++ b/remote/domains/content/Page.jsm
@@ -60,24 +60,27 @@ class Page extends ContentProcessDomain 
       throw new UnsupportedError("frameId not supported");
     }
 
     const opts = {
       loadFlags: transitionToLoadFlag(transitionType),
       referrerURI: referrer,
       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
     };
-    this.docShell.QueryInterface(Ci.nsIWebNavigation);
     this.docShell.loadURI(url, opts);
 
     return {
       frameId: this.content.windowUtils.outerWindowID,
     };
   }
 
+  async reload() {
+    this.docShell.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
+  }
+
   getFrameTree() {
     return {
       frameTree: {
         frame: {
           // id, parentId
         },
         childFrames: [],
       },
--- a/remote/sessions/ContentProcessSession.jsm
+++ b/remote/sessions/ContentProcessSession.jsm
@@ -10,16 +10,19 @@ const {ContentProcessDomains} = ChromeUt
 const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
 
 class ContentProcessSession {
   constructor(messageManager, browsingContext, content, docShell) {
     this.messageManager = messageManager;
     this.browsingContext = browsingContext;
     this.content = content;
     this.docShell = docShell;
+    // Most children or sibling classes are going to assume that docShell
+    // implements the following interface. So do the QI only once from here.
+    this.docShell.QueryInterface(Ci.nsIWebNavigation);
 
     this.domains = new Domains(this, ContentProcessDomains);
     this.messageManager.addMessageListener("remote:request", this);
     this.messageManager.addMessageListener("remote:destroy", this);
   }
 
   destroy() {
     this.messageManager.removeMessageListener("remote:request", this);
--- a/remote/test/browser/browser_page_frameNavigated.js
+++ b/remote/test/browser/browser_page_frameNavigated.js
@@ -2,16 +2,19 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test the Page navigation events
 
 const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
 
+const promises = new Set();
+const resolutions = new Map();
+
 add_task(async function() {
   // Open a test page, to prevent debugging the random default page
   await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
 
   // Start the CDP server
   await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
 
   // Retrieve the chrome-remote-interface library object
@@ -27,35 +30,60 @@ add_task(async function() {
   ok(true, "CDP client has been instantiated");
 
   const {Page} = client;
 
   // turn on navigation related events, such as DOMContentLoaded et al.
   await Page.enable();
   ok(true, "Page domain has been enabled");
 
-  const promises = [];
-  const resolutions = new Map();
+  // Save the given `promise` resolution into the `promises` global Set
   function recordPromise(name, promise) {
     promise.then(event => {
       ok(true, `Received Page.${name}`);
       resolutions.set(name, event);
     });
-    promises.push(promise);
+    promises.add(promise);
   }
-  recordPromise("frameStoppedLoading", Page.frameStoppedLoading());
-  recordPromise("navigatedWithinDocument", Page.navigatedWithinDocument());
-  recordPromise("domContentEventFired", Page.domContentEventFired());
-  recordPromise("loadEventFired", Page.loadEventFired());
-  recordPromise("frameNavigated", Page.frameNavigated());
+  // Record all Page events that we assert in this test
+  function recordPromises() {
+    recordPromise("frameStoppedLoading", Page.frameStoppedLoading());
+    recordPromise("navigatedWithinDocument", Page.navigatedWithinDocument());
+    recordPromise("domContentEventFired", Page.domContentEventFired());
+    recordPromise("loadEventFired", Page.loadEventFired());
+    recordPromise("frameNavigated", Page.frameNavigated());
+  }
+
+  info("Test Page.navigate");
+  recordPromises();
+
   const url = "data:text/html;charset=utf-8,test-page";
   const { frameId } = await Page.navigate({ url });
   ok(true, "A new page has been loaded");
   ok(frameId, "Page.navigate returned a frameId");
 
+  await assertNavigationEvents({ url, frameId });
+
+  info("Test Page.reload");
+  recordPromises();
+
+  await Page.reload();
+  ok(true, "The page has been reloaded");
+
+  await assertNavigationEvents({ url, frameId });
+
+  await client.close();
+  ok(true, "The client is closed");
+
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  await RemoteAgent.close();
+});
+
+async function assertNavigationEvents({ url, frameId }) {
   // Wait for all the promises to resolve
   await Promise.all(promises);
 
   // Assert the order in which they resolved
   const expectedResolutions = [
     "frameNavigated",
     "domContentEventFired",
     "loadEventFired",
@@ -65,31 +93,23 @@ add_task(async function() {
   Assert.deepEqual([...resolutions.keys()],
      expectedResolutions,
      "Received various Page navigation events in the expected order");
 
   // Now assert the data exposed by each of these events
   const frameNavigated = resolutions.get("frameNavigated");
   ok(!frameNavigated.frame.parentId, "frameNavigated is for the top level document and" +
     " has a null parentId");
-  is(frameNavigated.frame.id, frameId, "frameNavigated id is the same than the one " +
-    "returned by Page.navigate");
+  is(frameNavigated.frame.id, frameId, "frameNavigated id is the right one");
   is(frameNavigated.frame.name, undefined, "frameNavigated name isn't implemented yet");
-  is(frameNavigated.frame.url, url, "frameNavigated url is the same being given to " +
-    "Page.navigate");
+  is(frameNavigated.frame.url, url, "frameNavigated url is the right one");
 
   const navigatedWithinDocument = resolutions.get("navigatedWithinDocument");
   is(navigatedWithinDocument.frameId, frameId, "navigatedWithinDocument frameId is " +
-    "the same than the one returned by Page.navigate");
-  is(navigatedWithinDocument.url, url, "navigatedWithinDocument url is the same than " +
-    "the one being given to Page.navigate");
+    "the same one");
+  is(navigatedWithinDocument.url, url, "navigatedWithinDocument url is the same one");
 
   const frameStoppedLoading = resolutions.get("frameStoppedLoading");
-  is(frameStoppedLoading.frameId, frameId, "frameStoppedLoading frameId is the same " +
-    "than the one returned by Page.navigate");
+  is(frameStoppedLoading.frameId, frameId, "frameStoppedLoading frameId is the same one");
 
-  await client.close();
-  ok(true, "The client is closed");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
-  await RemoteAgent.close();
-});
+  promises.clear();
+  resolutions.clear();
+}
--- a/remote/test/browser/browser_runtime_executionContext.js
+++ b/remote/test/browser/browser_runtime_executionContext.js
@@ -25,17 +25,18 @@ add_task(async function() {
     },
   });
   ok(true, "CDP client has been instantiated");
 
   const firstContext = await testRuntimeEnable(client);
   await testEvaluate(client, firstContext);
   const secondContext = await testNavigate(client, firstContext);
   await testNavigateBack(client, firstContext, secondContext);
-  await testNavigateViaLocation(client, firstContext);
+  const thirdContext = await testNavigateViaLocation(client, firstContext);
+  await testReload(client, thirdContext);
 
   await client.close();
   ok(true, "The client is closed");
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   await RemoteAgent.close();
 });
@@ -58,16 +59,17 @@ async function testEvaluate({ Runtime },
   const contextId = previousContext.id;
 
   const { result } = await Runtime.evaluate({ contextId, expression: "location.href" });
   is(result.value, TEST_URI, "Runtime.evaluate works and is against the test page");
 }
 
 async function testNavigate({ Runtime, Page }, previousContext) {
   info("Navigate to a new URL");
+
   const executionContextDestroyed = Runtime.executionContextDestroyed();
   const executionContextCreated = Runtime.executionContextCreated();
 
   const url = "data:text/html;charset=utf-8,test-page";
   const { frameId } = await Page.navigate({ url });
   ok(true, "A new page has been loaded");
   is(frameId, previousContext.auxData.frameId, "Page.navigate returns the same frameId than executionContextCreated");
 
@@ -87,16 +89,17 @@ async function testNavigate({ Runtime, P
   return context;
 }
 
 // Navigates back to the previous page.
 // This should resurect the original document from the BF Cache and recreate the
 // context for it
 async function testNavigateBack({ Runtime }, firstContext, previousContext) {
   info("Navigate back to the previous document");
+
   const executionContextDestroyed = Runtime.executionContextDestroyed();
   const executionContextCreated = Runtime.executionContextCreated();
 
   gBrowser.selectedBrowser.goBack();
 
   const { context } = await executionContextCreated;
   is(context.id, firstContext.id, "The new execution context should be the same than the first one");
   ok(context.auxData.isDefault, "The execution context is the default one");
@@ -105,16 +108,18 @@ async function testNavigateBack({ Runtim
   const { executionContextId } = await executionContextDestroyed;
   is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
 
   const { result } = await Runtime.evaluate({ contextId: context.id, expression: "location.href" });
   is(result.value, TEST_URI, "Runtime.evaluate works and is against the page we just navigated to");
 }
 
 async function testNavigateViaLocation({ Runtime }, previousContext) {
+  info("Navigate via window.location and Runtime.evaluate");
+
   const executionContextDestroyed = Runtime.executionContextDestroyed();
   const executionContextCreated = Runtime.executionContextCreated();
 
   const url2 = "data:text/html;charset=utf-8,test-page-2";
   await Runtime.evaluate({ contextId: previousContext.id, expression: `window.location = '${url2}';` });
 
   const { executionContextId } = await executionContextDestroyed;
   is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
@@ -122,9 +127,32 @@ async function testNavigateViaLocation({
   const { context } = await executionContextCreated;
   ok(!!context.id, "The execution context has an id");
   ok(context.auxData.isDefault, "The execution context is the default one");
   is(context.auxData.frameId, previousContext.auxData.frameId, "The execution context frame id is the same " +
     "the one returned by Page.navigate");
 
   isnot(executionContextId, context.id, "The destroyed id is different from the " +
     "created one");
+
+  return context;
 }
+
+async function testReload({ Runtime, Page }, previousContext) {
+  info("Test reloading via Page.reload");
+
+  const executionContextDestroyed = Runtime.executionContextDestroyed();
+  const executionContextCreated = Runtime.executionContextCreated();
+
+  await Page.reload();
+
+  const { executionContextId } = await executionContextDestroyed;
+  is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
+
+  const { context } = await executionContextCreated;
+  ok(!!context.id, "The execution context has an id");
+  ok(context.auxData.isDefault, "The execution context is the default one");
+  is(context.auxData.frameId, previousContext.auxData.frameId, "The execution context " +
+    "frame id is the same one");
+
+  isnot(executionContextId, context.id, "The destroyed id is different from the " +
+    "created one");
+}
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -492,16 +492,71 @@ android-aarch64/opt:
         - linux64-clang
         - linux64-rust-android
         - linux64-rust-size
         - linux64-cbindgen
         - linux64-sccache
         - linux64-nasm
         - linux64-node
 
+android-aarch64/pgo:
+    description: "Android 5.0 AArch64 PGO"
+    use-pgo: android-api-16/pgo
+    index:
+        product: mobile
+        job-name: android-aarch64-pgo
+    attributes:
+        enable-full-crashsymbols: true
+    treeherder:
+        platform: android-5-0-aarch64/pgo
+        symbol: Bpgo(B)
+    worker-type: b-linux
+    worker:
+        docker-image: {in-tree: android-build}
+        max-run-time: 7200
+        env:
+            GRADLE_USER_HOME: "/builds/worker/workspace/build/src/mobile/android/gradle/dotgradle-offline"
+            TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
+        artifacts:
+            - name: public/android/maven
+              # TODO Bug 1433198. Remove the following entry once target.maven.zip is uploaded to a maven repository
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
+              type: directory
+            - name: public/build/target.maven.zip
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/target.maven.zip
+              type: file
+            - name: public/build/geckoview-androidTest.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/outputs/apk/androidTest/withGeckoBinaries/debug/geckoview-withGeckoBinaries-debug-androidTest.apk
+              type: file
+            - name: public/build/geckoview_example.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/withGeckoBinaries/debug/geckoview_example-withGeckoBinaries-debug.apk
+              type: file
+            - name: public/build
+              path: /builds/worker/artifacts/
+              type: directory
+    run:
+        using: mozharness
+        actions: [get-secrets, build]
+        config:
+            - builds/releng_base_android_64_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: aarch64-pgo
+        tooltool-downloads: internal
+    toolchains:
+        - android-gradle-dependencies
+        - android-ndk-linux
+        - android-sdk-linux
+        - linux64-clang
+        - linux64-rust-android
+        - linux64-rust-size
+        - linux64-cbindgen
+        - linux64-nasm
+        - linux64-node
+
 android-aarch64/debug:
     description: "Android 5.0 AArch64 Debug"
     index:
         product: mobile
         job-name: android-aarch64-debug
     attributes:
         enable-full-crashsymbols: true
     treeherder:
--- a/taskcluster/ci/test/compiled.yml
+++ b/taskcluster/ci/test/compiled.yml
@@ -90,16 +90,17 @@ jittest:
             # platform "linux64" doesn't have enough characters for both the
             # beginning /linux/ and the final /...../.
             #
             # Additionally, platforms contain suffixes like "/opt" or "/debug".
             (?=linux).*(?!-ccov)...../.*: []  # redundant with SM(...)
             (?=windows).*(?!-ccov)...../.*: []  # redundant with SM(p)
             windows10-aarch64/opt: ['try', 'mozilla-central']
             android-hw-.*-api-16/opt: ['try']
+            android-hw-.*-aarch64/opt: ['try']
             default: built-projects
     chunks:
         by-test-platform:
             windows.*: 1
             windows10-64-ccov/debug: 6
             macosx.*: 1
             macosx64/debug: 3
             macosx64-ccov/debug: 4
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -10,16 +10,17 @@ job-defaults:
             android-hw.*: /builds/worker
             default: /home/cltbld
     run-on-projects:
         by-test-platform:
             windows10-64-ux/opt: ['try', 'mozilla-central']
             windows10-aarch64/opt: ['try', 'mozilla-central']
             (?:windows10-64|windows7-32|linux64)(?:-qr)?/opt: ['mozilla-central', 'try']
             android-hw-.*-api-16/opt: ['try']
+            android-hw-.*-aarch64/opt: ['try']
             macosx64(?:-qr)?/opt: ['mozilla-central', 'try']
             default: ['try', 'trunk', 'mozilla-beta']
     tier:
         by-test-platform:
             windows10-aarch64/.*: 2
             windows10-64-ccov/.*: 3
             linux64-ccov/.*: 3
             android-hw-g5.*: 2
@@ -344,17 +345,16 @@ raptor-tp6m-2-refbrow:
 raptor-tp6m-3-geckoview:
     description: "Raptor tp6m-3 on Geckoview"
     try-name: raptor-tp6m-3-geckoview
     treeherder-symbol: Rap(tp6m-3)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw.*aarch64/opt: ['try', 'mozilla-central']
             default: ['try']
     tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6m-3
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
 
@@ -376,17 +376,16 @@ raptor-tp6m-3-refbrow:
 raptor-tp6m-4-geckoview:
     description: "Raptor tp6m-4 on Geckoview"
     try-name: raptor-tp6m-4-geckoview
     treeherder-symbol: Rap(tp6m-4)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw.*aarch64/opt: ['try', 'mozilla-central']
             default: ['try']
     tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6m-4
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
 
@@ -474,17 +473,16 @@ raptor-tp6m-6-refbrow:
 
 raptor-tp6m-7-geckoview:
     description: "Raptor tp6m-7 on Geckoview"
     try-name: raptor-tp6m-7-geckoview
     treeherder-symbol: Rap(tp6m-7)
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw.*aarch64/opt: ['try', 'mozilla-central']
             default: ['try']
     target: geckoview_example.apk
     tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6m-7
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
@@ -541,17 +539,16 @@ raptor-tp6m-8-refbrow:
 raptor-tp6m-9-geckoview:
     description: "Raptor tp6m-9 on Geckoview"
     try-name: raptor-tp6m-9-geckoview
     treeherder-symbol: Rap(tp6m-9)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw.*aarch64/opt: ['try', 'mozilla-central']
             default: ['try']
     tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6m-9
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
             - --activity=GeckoViewActivity
@@ -574,17 +571,16 @@ raptor-tp6m-9-refbrow:
 raptor-tp6m-10-geckoview:
     description: "Raptor tp6m-10 on Geckoview"
     try-name: raptor-tp6m-10-geckoview
     treeherder-symbol: Rap(tp6m-10)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw.*aarch64/opt: ['try', 'mozilla-central']
             default: ['try']
     tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6m-10
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
             - --activity=GeckoViewActivity
@@ -1013,18 +1009,17 @@ raptor-speedometer-geckoview:
 raptor-speedometer-geckoview-power:
     description: "Raptor Speedometer Power on Geckoview"
     try-name: raptor-speedometer-geckoview-power
     treeherder-symbol: Rap-P(sp)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
             android-hw.*/pgo: ['try', 'mozilla-central']
-            android-hw-.*-aarch64/opt: ['try', 'mozilla-central']
-            android-hw-.*-api-16/opt: ['try']
+            default: ['try']
     max-run-time: 1800
     mozharness:
         extra-options:
             - --test=raptor-speedometer
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
             - --power-test
             - --page-cycles 5
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -397,16 +397,23 @@ android-hw-p2-8-0-arm7-api-16/debug:
 
 android-hw-p2-8-0-android-aarch64/opt:
     build-platform: android-aarch64/opt
     test-sets:
         - android-hw-aarch64-opt-unittests
         - android-hw-aarch64-raptor
         - android-hw-aarch64-raptor-power
 
+android-hw-p2-8-0-android-aarch64/pgo:
+    build-platform: android-aarch64-pgo/opt
+    test-sets:
+        - android-hw-aarch64-opt-unittests
+        - android-hw-aarch64-raptor
+        - android-hw-aarch64-raptor-power
+
 android-hw-p2-8-0-arm7-api-16-nightly/opt:
     build-platform: android-api-16-nightly/opt
     test-sets:
         - android-hw-arm7-raptor-nightly
 
 android-hw-p2-8-0-android-aarch64-nightly/opt:
     build-platform: android-aarch64-nightly/opt
     test-sets:
--- a/taskcluster/scripts/tester/test-linux.sh
+++ b/taskcluster/scripts/tester/test-linux.sh
@@ -129,19 +129,16 @@ fi
 if $START_VNC; then
     x11vnc > "$WORKING_DIR/artifacts/public/x11vnc.log" 2>&1 &
 fi
 
 if $NEED_WINDOW_MANAGER; then
     # This is read by xsession to select the window manager
     echo DESKTOP_SESSION=ubuntu > $HOME/.xsessionrc
 
-    # note that doing anything with this display before running Xsession will cause sadness (like,
-    # crashes in compiz). Make sure that X has enough time to start
-    sleep 15
     # DISPLAY has already been set above
     # XXX: it would be ideal to add a semaphore logic to make sure that the
     # window manager is ready
     /etc/X11/Xsession 2>&1 &
 
     # Turn off the screen saver and screen locking
     gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
     gsettings set org.gnome.desktop.screensaver lock-enabled false
--- a/testing/mozbase/mozproxy/mozproxy/backends/mitm.py
+++ b/testing/mozbase/mozproxy/mozproxy/backends/mitm.py
@@ -77,16 +77,24 @@ class Mitmproxy(Playback):
         self.mitmproxy_proc = None
         self.mitmdump_path = None
         self.browser_path = config.get("binary")
         self.policies_dir = None
         self.ignore_mitmdump_exit_failure = config.get(
             "ignore_mitmdump_exit_failure", False
         )
 
+        if self.config.get("playback_version") is None:
+            LOG.info("mitmproxy was not provided with a 'playback_version' "
+                     "getting 'playback_version' from 'playback_binary_manifest'")
+            if "4.0.4" in self.config["playback_binary_manifest"]:
+                self.config["playback_version"] = "4.0.4"
+            else:
+                self.config["playback_version"] = "2.0.2"
+
         # mozproxy_dir is where we will download all mitmproxy required files
         # when running locally it comes from obj_path via mozharness/mach
         if self.config.get("obj_path") is not None:
             self.mozproxy_dir = self.config.get("obj_path")
         else:
             # in production it is ../tasks/task_N/build/, in production that dir
             # is not available as an envvar, however MOZ_UPLOAD_DIR is set as
             # ../tasks/task_N/build/blobber_upload_dir so take that and go up 1 level
@@ -105,37 +113,51 @@ class Mitmproxy(Playback):
         # where to get the data
         os.environ["MOZPROXY_DIR"] = self.mozproxy_dir
 
     def start(self):
         # go ahead and download and setup mitmproxy
         self.download()
 
         # mitmproxy must be started before setup, so that the CA cert is available
-        self.mitmdump_path = os.path.join(self.mozproxy_dir, "mitmdump")
         self.start_mitmproxy_playback(self.mitmdump_path, self.browser_path)
 
         # In case the setup fails, we want to stop the process before raising.
         try:
             self.setup()
         except Exception:
             self.stop()
             raise
 
     def download(self):
         """Download and unpack mitmproxy binary and pageset using tooltool"""
         if not os.path.exists(self.mozproxy_dir):
             os.makedirs(self.mozproxy_dir)
 
-        LOG.info("downloading mitmproxy binary")
         _manifest = os.path.join(here, self.config["playback_binary_manifest"])
         transformed_manifest = transform_platform(_manifest, self.config["platform"])
-        tooltool_download(
-            transformed_manifest, self.config["run_local"], self.mozproxy_dir
-        )
+
+        # generate the mitmdump_path
+        self.mitmdump_path = os.path.join(self.mozproxy_dir, "mitmdump-%s" %
+                                          self.config["playback_version"], "mitmdump")
+
+        # Check if mitmproxy bin exists
+        if os.path.exists(self.mitmdump_path):
+            LOG.info("mitmproxy binary already exists. Skipping download")
+        else:
+            # Download and unpack mitmproxy binary
+            download_path = os.path.dirname(self.mitmdump_path)
+            LOG.info("create mitmproxy %s dir" % self.config["playback_version"])
+            if not os.path.exists(download_path):
+                os.makedirs(download_path)
+
+            LOG.info("downloading mitmproxy binary")
+            tooltool_download(
+                transformed_manifest, self.config["run_local"],
+                download_path)
 
         if "playback_pageset_manifest" in self.config:
             # we use one pageset for all platforms
             LOG.info("downloading mitmproxy pageset")
             _manifest = self.config["playback_pageset_manifest"]
             transformed_manifest = transform_platform(
                 _manifest, self.config["platform"]
             )
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_aarch64_pgo.py
@@ -0,0 +1,5 @@
+config = {
+    'stage_platform': 'android-aarch64-pgo',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-aarch64/profile-use',
+    'multi_locale_config_platform': 'android',
+}
--- a/testing/mozharness/configs/unittests/linux_unittest.py
+++ b/testing/mozharness/configs/unittests/linux_unittest.py
@@ -156,33 +156,33 @@ config = {
                            "--valgrind-supp-files=" + VALGRIND_SUPP_ARCH +
                                "," + VALGRIND_SUPP_CROSS_ARCH,
                            "--timeout=900", "--max-timeouts=50"],
         "mochitest-plain": [],
         "mochitest-plain-gpu": ["--subsuite=gpu"],
         "mochitest-plain-chunked": ["--chunk-by-dir=4"],
         "mochitest-plain-chunked-coverage": ["--chunk-by-dir=4", "--timeout=1200"],
         "mochitest-media": ["--subsuite=media"],
-        "mochitest-chrome": ["--flavor=chrome"],
+        "mochitest-chrome": ["--flavor=chrome", "--disable-e10s"],
         "mochitest-chrome-gpu": ["--flavor=chrome", "--subsuite=gpu", "--disable-e10s"],
-        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4"],
+        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4", "--disable-e10s"],
         "mochitest-browser-chrome": ["--flavor=browser"],
         "mochitest-browser-chrome-chunked": ["--flavor=browser", "--chunk-by-runtime"],
         "mochitest-browser-chrome-coverage": ["--flavor=browser", "--chunk-by-runtime", "--timeout=1200"],
         "mochitest-browser-chrome-screenshots": ["--flavor=browser", "--subsuite=screenshots"],
         "mochitest-browser-chrome-instrumentation": ["--flavor=browser"],
         "mochitest-webgl1-core": ["--subsuite=webgl1-core"],
         "mochitest-webgl1-ext": ["--subsuite=webgl1-ext"],
         "mochitest-webgl2-core": ["--subsuite=webgl2-core"],
         "mochitest-webgl2-ext": ["--subsuite=webgl2-ext"],
         "mochitest-webgl2-deqp": ["--subsuite=webgl2-deqp"],
         "mochitest-devtools-chrome": ["--flavor=browser", "--subsuite=devtools"],
         "mochitest-devtools-chrome-chunked": ["--flavor=browser", "--subsuite=devtools", "--chunk-by-runtime"],
         "mochitest-devtools-chrome-coverage": ["--flavor=browser", "--subsuite=devtools", "--chunk-by-runtime", "--timeout=1200"],
-        "mochitest-a11y": ["--flavor=a11y"],
+        "mochitest-a11y": ["--flavor=a11y", "--disable-e10s"],
     },
     # local reftest suites
     "all_reftest_suites": {
         "crashtest": {
             "options": ["--suite=crashtest"],
             "tests": ["tests/reftest/tests/testing/crashtest/crashtests.list"]
         },
         "jsreftest": {
--- a/testing/mozharness/configs/unittests/mac_unittest.py
+++ b/testing/mozharness/configs/unittests/mac_unittest.py
@@ -121,32 +121,32 @@ config = {
         },
     },
     # local mochi suites
     "all_mochitest_suites": {
         "mochitest-plain": [],
         "mochitest-plain-gpu": ["--subsuite=gpu"],
         "mochitest-plain-chunked": ["--chunk-by-dir=4"],
         "mochitest-media": ["--subsuite=media"],
-        "mochitest-chrome": ["--flavor=chrome"],
+        "mochitest-chrome": ["--flavor=chrome", "--disable-e10s"],
         "mochitest-chrome-gpu": ["--flavor=chrome", "--subsuite=gpu", "--disable-e10s"],
-        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4"],
+        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4", "--disable-e10s"],
         "mochitest-browser-chrome": ["--flavor=browser"],
         "mochitest-browser-chrome-chunked": ["--flavor=browser", "--chunk-by-runtime"],
         "mochitest-browser-chrome-screenshots": ["--flavor=browser", "--subsuite=screenshots"],
         "mochitest-browser-chrome-instrumentation": ["--flavor=browser"],
         "mochitest-webgl1-core": ["--subsuite=webgl1-core"],
         "mochitest-webgl1-ext": ["--subsuite=webgl1-ext"],
         "mochitest-webgl2-core": ["--subsuite=webgl2-core"],
         "mochitest-webgl2-ext": ["--subsuite=webgl2-ext"],
         "mochitest-webgl2-deqp": ["--subsuite=webgl2-deqp"],
         "mochitest-devtools-chrome": ["--flavor=browser", "--subsuite=devtools"],
         "mochitest-devtools-chrome-chunked": ["--flavor=browser", "--subsuite=devtools", "--chunk-by-runtime"],
         "mochitest-devtools-chrome-webreplay": ["--flavor=browser", "--subsuite=devtools-webreplay"],
-        "mochitest-a11y": ["--flavor=a11y"],
+        "mochitest-a11y": ["--flavor=a11y", "--disable-e10s"],
     },
     # local reftest suites
     "all_reftest_suites": {
         "crashtest": {
             'options': ["--suite=crashtest"],
             'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
         },
         "jsreftest": {
--- a/testing/mozharness/configs/unittests/win_unittest.py
+++ b/testing/mozharness/configs/unittests/win_unittest.py
@@ -144,31 +144,31 @@ config = {
     },
     # local mochi suites
     "all_mochitest_suites":
     {
         "mochitest-plain": [],
         "mochitest-plain-gpu": ["--subsuite=gpu"],
         "mochitest-plain-chunked": ["--chunk-by-dir=4"],
         "mochitest-media": ["--subsuite=media"],
-        "mochitest-chrome": ["--flavor=chrome"],
+        "mochitest-chrome": ["--flavor=chrome", "--disable-e10s"],
         "mochitest-chrome-gpu": ["--flavor=chrome", "--subsuite=gpu", "--disable-e10s"],
-        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4"],
+        "mochitest-chrome-chunked": ["--flavor=chrome", "--chunk-by-dir=4", "--disable-e10s"],
         "mochitest-browser-chrome": ["--flavor=browser"],
         "mochitest-browser-chrome-chunked": ["--flavor=browser", "--chunk-by-runtime"],
         "mochitest-browser-chrome-screenshots": ["--flavor=browser", "--subsuite=screenshots"],
         "mochitest-browser-chrome-instrumentation": ["--flavor=browser"],
         "mochitest-webgl1-core": ["--subsuite=webgl1-core"],
         "mochitest-webgl1-ext": ["--subsuite=webgl1-ext"],
         "mochitest-webgl2-core": ["--subsuite=webgl2-core"],
         "mochitest-webgl2-ext": ["--subsuite=webgl2-ext"],
         "mochitest-webgl2-deqp": ["--subsuite=webgl2-deqp"],
         "mochitest-devtools-chrome": ["--flavor=browser", "--subsuite=devtools"],
         "mochitest-devtools-chrome-chunked": ["--flavor=browser", "--subsuite=devtools", "--chunk-by-runtime"],
-        "mochitest-a11y": ["--flavor=a11y"],
+        "mochitest-a11y": ["--flavor=a11y", "--disable-e10s"],
     },
     # local reftest suites
     "all_reftest_suites": {
         "crashtest": {
             'options': ["--suite=crashtest"],
             'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
         },
         "jsreftest": {
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -373,16 +373,17 @@ class BuildOptionParser(object):
         'x86_64': 'builds/releng_sub_%s_configs/%s_x86_64.py',
         'x86_64-beta': 'builds/releng_sub_%s_configs/%s_x86_64_beta.py',
         'x86_64-beta-debug': 'builds/releng_sub_%s_configs/%s_x86_64_beta_debug.py',
         'x86_64-debug': 'builds/releng_sub_%s_configs/%s_x86_64_debug.py',
         'api-16-partner-sample1': 'builds/releng_sub_%s_configs/%s_api_16_partner_sample1.py',
         'aarch64': 'builds/releng_sub_%s_configs/%s_aarch64.py',
         'aarch64-beta': 'builds/releng_sub_%s_configs/%s_aarch64_beta.py',
         'aarch64-beta-debug': 'builds/releng_sub_%s_configs/%s_aarch64_beta_debug.py',
+        'aarch64-pgo': 'builds/releng_sub_%s_configs/%s_aarch64_pgo.py',
         'aarch64-debug': 'builds/releng_sub_%s_configs/%s_aarch64_debug.py',
         'android-test': 'builds/releng_sub_%s_configs/%s_test.py',
         'android-test-ccov': 'builds/releng_sub_%s_configs/%s_test_ccov.py',
         'android-checkstyle': 'builds/releng_sub_%s_configs/%s_checkstyle.py',
         'android-api-lint': 'builds/releng_sub_%s_configs/%s_api_lint.py',
         'android-lint': 'builds/releng_sub_%s_configs/%s_lint.py',
         'android-findbugs': 'builds/releng_sub_%s_configs/%s_findbugs.py',
         'android-geckoview-docs': 'builds/releng_sub_%s_configs/%s_geckoview_docs.py',
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/word-break/word-break-break-all-inline-004.html.ini
@@ -0,0 +1,2 @@
+[word-break-break-all-inline-004.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/word-break/word-break-break-all-inline-005.html.ini
@@ -0,0 +1,2 @@
+[word-break-break-all-inline-005.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/word-break/word-break-break-all-inline-006.html.ini
@@ -0,0 +1,2 @@
+[word-break-break-all-inline-006.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/word-break/word-break-break-all-inline-007.html.ini
@@ -0,0 +1,2 @@
+[word-break-break-all-inline-007.html]
+  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[track-add-track.html]
-  ['addtrack' event is fired when a TextTrack is created]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/__dir__.ini
+++ b/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.promise_rejection_events.enabled:true]
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[allow-crossorigin.html]
-  expected: TIMEOUT
-  [Promise rejection event should be received for the cross-origin CORS script]
-    expected: TIMEOUT
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html.ini
@@ -0,0 +1,5 @@
+[disallow-crossorigin.html]
+  expected: ERROR
+  [Promise rejection event should be muted for cross-origin non-CORS script]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[promise-rejection-event-constructor.html]
-  [This tests the constructor for the PromiseRejectionEvent DOM class.]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[promise-rejection-events-attached-in-event.html]
-  expected: TIMEOUT
-  [Attaching a handler in unhandledrejection should not trigger rejectionhandled.]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[promise-rejection-events-onerror.html]
-  expected: TIMEOUT
-  [Throwing inside an unhandledrejection handler invokes the error handler.]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-[promise-rejection-events.dedicatedworker.html]
-  expected: TIMEOUT
-  [unhandledrejection: from Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a synchronous rejection in new Promise]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a task-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from Promise.reject, indirected through Promise.all]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-task delay before attaching a handler causes unhandledrejection]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [mutationObserverMicrotask vs. postMessageTask ordering is not disturbed inside unhandledrejection events]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html.ini
+++ /dev/null
@@ -1,59 +0,0 @@
-[promise-rejection-events.html]
-  expected: TIMEOUT
-  [unhandledrejection: from Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a synchronous rejection in new Promise]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a task-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from Promise.reject, indirected through Promise.all]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-task delay before attaching a handler causes unhandledrejection]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [mutationObserverMicrotask vs. postMessageTask ordering is not disturbed inside unhandledrejection events]
-    expected: TIMEOUT
-
-  [postMessageTask ordering vs. the task queued for unhandled rejection notification (1)]
-    expected: TIMEOUT
-
-  [postMessageTask ordering vs. the task queued for unhandled rejection notification (2)]
-    expected: TIMEOUT
-
-  [rejectionhandled is dispatched from a queued task, and not immediately]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html.ini
+++ /dev/null
@@ -1,53 +0,0 @@
-[promise-rejection-events.serviceworker.https.html]
-  expected: TIMEOUT
-  [unhandledrejection: from Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a synchronous rejection in new Promise]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a task-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from Promise.reject, indirected through Promise.all]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-task delay before attaching a handler causes unhandledrejection]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [mutationObserverMicrotask vs. postMessageTask ordering is not disturbed inside unhandledrejection events]
-    expected: TIMEOUT
-
-  [Service worker setup]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-[promise-rejection-events.sharedworker.html]
-  expected: TIMEOUT
-  [unhandledrejection: from Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a synchronous rejection in new Promise]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a task-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of Promise.reject]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from a throw in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler]
-    expected: TIMEOUT
-
-  [unhandledrejection: from Promise.reject, indirected through Promise.all]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-task delay before attaching a handler causes unhandledrejection]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask before promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [delayed handling: a nested-postMessageTask after promise creation/rejection, plus many promise microtasks, is too late to attach a rejection handler]
-    expected: TIMEOUT
-
-  [mutationObserverMicrotask vs. postMessageTask ordering is not disturbed inside unhandledrejection events]
-    expected: TIMEOUT
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-001-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">bbbbbb<br>bbbb</div>
+<div class="testdiv">bbbbbb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-002-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaa bb<br>bbbb</div>
+<div class="testdiv">aaa bb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-003-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaabb<br>bbbb</div>
+<div class="testdiv">aaaabb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-004-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaaaaa<br>bbb</div>
+<div class="testdiv">aaaaaaa<br>bbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-005-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaa bb<br>bbbbbb<br>bbb<br>ccc</div>
+<div class="testdiv">aaa bb<br>bbbbbb<br>bbb<br>ccc</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-006-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaabb<br>bbbbbb<br>bbbccccc</div>
+<div class="testdiv">aaaabb<br>bbbbbb<br>bbbccccc</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/reference/word-break-break-all-inline-007-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element - reference</title>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaaaaa<br>bbbbbb<br>bcccccc</div>
+<div class="testdiv">aaaaaaa<br>bbbbbb<br>bcccccc</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-001-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv"><span class="test">bbbbbbbbbb</span></div>
+<div class="testdiv">bbbbbb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-002.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-002-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaa <span class="test">bbbbbb</span></div>
+<div class="testdiv">aaa bb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-003.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-003-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaa<span class="test">bbbbbb</span></div>
+<div class="testdiv">aaaabb<br>bbbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-004.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-004-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaaaaa<span class="test">bbb</span></div>
+<div class="testdiv">aaaaaaa<br>bbb</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-005.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-005-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaa <span class="test">bbbbbbbbbbb</span> ccc</div>
+<div class="testdiv">aaa bb<br>bbbbbb<br>bbb<br>ccc</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-006.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-006-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaa<span class="test">bbbbbbbbbbb</span>ccccc</div>
+<div class="testdiv">aaaabb<br>bbbbbb<br>bbbccccc</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-all-inline-007.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>word-break: break-all on inline element</title>
+<meta name="assert" content="word-break: break-all works when specified on inline element">
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#word-break-property'>
+<link rel='match' href='reference/word-break-break-all-inline-007-ref.html'>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style>
+.testdiv { font-family: monospace; width: 6.1ch; padding: 1px; margin: 10px; border: 1px solid silver; }
+.test { word-break: break-all; }
+</style>
+<div>Test passes if the two boxes are the same.</div>
+<div class="testdiv">aaaaaaa<span class="test">bbbbbbb</span>cccccc</div>
+<div class="testdiv">aaaaaaa<br>bbbbbb<br>bcccccc</div>
--- a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html
+++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-add-track.html
@@ -8,17 +8,17 @@ async_test(function(t) {
 
     var trackElement = document.createElement('track');
     video.appendChild(trackElement);
     var tracks = [];
     tracks.push(trackElement.track);
 
     // Register the 'addtrack' listener after creating the element
     // to make sure the event is dispatched asynchronously.
-    video.textTracks.onaddtrack = t.step_func(function() {
+    video.textTracks.onaddtrack = t.step_func(function(event) {
         assert_equals(event.target, video.textTracks);
         assert_true(event instanceof TrackEvent, 'instanceof');
         assert_equals(event.track, tracks[video.textTracks.length - 1]);
 
         if (video.textTracks.length == 1) {
             tracks.push(video.addTextTrack('captions', 'Caption Track', 'en'));
             assert_equals(video.textTracks.length, 2);
         } else {
@@ -26,9 +26,9 @@ async_test(function(t) {
         }
     });
 
     trackElement.src = 'resources/webvtt-file.vtt';
     trackElement.track.mode = 'hidden';
     assert_equals(video.textTracks.length, 1);
     assert_equals(trackElement.readyState, HTMLTrackElement.NONE);
 });
-</script>
\ No newline at end of file
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<div id="log"></div><br>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+'use strict';
+
+setup({
+  allow_uncaught_exception: true
+});
+
+async_test(function(t) {
+  createIframeAndStartTest(t, function(w) {
+    let e = new Error();
+    let promise = new w.Promise(function(_, reject) {
+      setTimeout(function() {
+        reject(e);
+      }, 1);
+    });
+
+    let unhandled = function(evt) {
+      if (evt.promise === promise) {
+        t.step(function() {
+          assert_equals(evt.reason, e);
+          assert_equals(evt.promise, promise);
+        });
+        t.done();
+      }
+    };
+    let handled = function(evt) {
+      if (evt.promise === promise) {
+        t.step(function() {
+          assert_unreached('rejectionhandled event is not supposed to be triggered');
+        });
+      }
+    };
+
+    w.addEventListener('unhandledrejection', unhandled);