Merge mozilla-central to mozilla-inbound. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Wed, 27 Feb 2019 06:38:29 +0200
changeset 461609 37097e092a93f25cce13409780607a372164d0f0
parent 461608 831da989e317d3110e322dce6b8b8058b1199986 (current diff)
parent 461389 5b8896aa3f698f812771b53dda2483eea62ba97c (diff)
child 461610 0573c6c669be0434b630bb90051298aaa5159ccc
push id35626
push usercsabou@mozilla.com
push dateThu, 28 Feb 2019 11:31:08 +0000
treeherdermozilla-central@2ea0c1db7e60 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone67.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/server/tests/unit/test_objectgrips-09.js
modules/libjar/nsIJARFactory.h
modules/libjar/nsIJARProtocolHandler.idl
testing/web-platform/meta/css/css-shapes/parsing/shape-margin-computed.html.ini
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1313,24 +1313,24 @@ dependencies = [
  "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "jsrust"
 version = "0.1.0"
 dependencies = [
  "jsrust_shared 0.1.0",
- "mozilla-central-workspace-hack 0.1.0",
 ]
 
 [[package]]
 name = "jsrust_shared"
 version = "0.1.0"
 dependencies = [
  "baldrdash 0.1.0",
+ "mozilla-central-workspace-hack 0.1.0",
 ]
 
 [[package]]
 name = "kernel32-sys"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1666,17 +1666,17 @@ dependencies = [
 name = "mozilla-central-workspace-hack"
 version = "0.1.0"
 dependencies = [
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.80 (git+https://github.com/servo/serde?branch=deserialize_from_enums9)",
- "syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "mozjs_sys"
 version = "0.0.0"
 dependencies = [
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1333,31 +1333,36 @@ void DocAccessible::NotifyOfLoading(bool
 }
 
 void DocAccessible::DoInitialUpdate() {
   if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
     mDocFlags |= eTabDocument;
     if (IPCAccessibilityActive()) {
       nsIDocShell* docShell = mDocumentNode->GetDocShell();
       if (RefPtr<dom::TabChild> tabChild = dom::TabChild::GetFrom(docShell)) {
-        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this, tabChild);
-        SetIPCDoc(ipcDoc);
+        DocAccessibleChild* ipcDoc = IPCDoc();
+        if (!ipcDoc) {
+          ipcDoc = new DocAccessibleChild(this, tabChild);
+          SetIPCDoc(ipcDoc);
+
+#if defined(XP_WIN)
+          IAccessibleHolder holder(
+              CreateHolderFromAccessible(WrapNotNull(this)));
+          MOZ_ASSERT(!holder.IsNull());
+          int32_t childID = AccessibleWrap::GetChildIDFor(this);
+#else
+          int32_t holder = 0, childID = 0;
+#endif
+          tabChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0, childID,
+                                                  holder);
+        }
+
         if (IsRoot()) {
           tabChild->SetTopLevelDocAccessibleChild(ipcDoc);
         }
-
-#if defined(XP_WIN)
-        IAccessibleHolder holder(CreateHolderFromAccessible(WrapNotNull(this)));
-        MOZ_ASSERT(!holder.IsNull());
-        int32_t childID = AccessibleWrap::GetChildIDFor(this);
-#else
-        int32_t holder = 0, childID = 0;
-#endif
-        tabChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0, childID,
-                                                holder);
       }
     }
   }
 
   mLoadState |= eTreeConstructed;
 
   // Set up a root element and ARIA role mapping.
   UpdateRootElIfNeeded();
@@ -2248,16 +2253,21 @@ bool DocAccessible::IsLoadEventTarget() 
     DocAccessible* parentDoc = ParentDocument();
     return parentDoc && parentDoc->HasLoadState(eCompletelyLoaded);
   }
 
   // It's content (not chrome) root document.
   return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
 }
 
+void DocAccessible::SetIPCDoc(DocAccessibleChild* aIPCDoc) {
+  MOZ_ASSERT(!mIPCDoc || !aIPCDoc, "Clobbering an attached IPCDoc!");
+  mIPCDoc = aIPCDoc;
+}
+
 void DocAccessible::DispatchScrollingEvent(uint32_t aEventType) {
   nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
   if (!sf) {
     return;
   }
 
   int32_t appUnitsPerDevPixel =
       mPresShell->GetPresContext()->AppUnitsPerDevPixel();
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -555,17 +555,17 @@ class DocAccessible : public HyperTextAc
    * coalescence).
    */
   bool IsLoadEventTarget() const;
 
   /*
    * Set the object responsible for communicating with the main process on
    * behalf of this document.
    */
-  void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; }
+  void SetIPCDoc(DocAccessibleChild* aIPCDoc);
 
   friend class DocAccessibleChildBase;
 
   /**
    * Used to fire scrolling end event after page scroll.
    *
    * @param aTimer    [in] the timer object
    * @param aClosure  [in] the document accessible where scrolling happens
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -72,16 +72,19 @@
       <popupnotificationcontent id="addon-install-confirmation-content" orient="vertical"/>
     </popupnotification>
 
     <popupnotification id="addon-webext-permissions-notification" hidden="true">
       <popupnotificationcontent class="addon-webext-perm-notification-content" orient="vertical">
         <description id="addon-webext-perm-text" class="addon-webext-perm-text"/>
         <label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
         <html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
+        <hbox>
+          <label id="addon-webext-perm-info" is="text-link" class="popup-notification-learnmore-link"/>
+        </hbox>
       </popupnotificationcontent>
     </popupnotification>
 
     <popupnotification id="contextual-feature-recommendation-notification" hidden="true">
       <popupnotificationheader id="cfr-notification-header">
         <stack id="cfr-notification-header-stack">
           <description id="cfr-notification-header-label"></description>
           <label id="cfr-notification-header-link" is="text-link">
--- a/browser/base/content/test/webextensions/browser_permissions_dismiss.js
+++ b/browser/base/content/test/webextensions/browser_permissions_dismiss.js
@@ -30,16 +30,23 @@ add_task(async function test_tab_switch_
   ContentTask.spawn(gBrowser.selectedBrowser, INSTALL_XPI, function(url) {
     content.wrappedJSObject.installMozAM(url);
   });
 
   await promisePopupNotificationShown("addon-webext-permissions");
   let permsUL = document.getElementById("addon-webext-perm-list");
   is(permsUL.childElementCount, 5, `Permissions list has 5 entries`);
 
+  let permsLearnMore = document.getElementById("addon-webext-perm-info");
+  ok(BrowserTestUtils.is_visible(permsLearnMore),
+     "Learn more link is shown on Permission popup");
+  is(permsLearnMore.href,
+     Services.urlFormatter.formatURLPref("app.support.baseURL") +
+     "extension-permissions", "Learn more link has desired URL");
+
   // Switching tabs dismisses the notification and cancels the install.
   let switchTo = await BrowserTestUtils.openNewForegroundTab(gBrowser);
   BrowserTestUtils.removeTab(switchTo);
   await installCanceled;
 
   let addon = await AddonManager.getAddonByID("permissions@test.mozilla.org");
   is(addon, null, "Extension is not installed");
 
--- a/browser/base/content/test/webextensions/head.js
+++ b/browser/base/content/test/webextensions/head.js
@@ -206,30 +206,33 @@ function checkPermissionString(string, k
  *        in this array is itself a 2-element array with the string key
  *        for the item (e.g., "webextPerms.description.foo") and an
  *        optional formatting parameter.
  */
 function checkNotification(panel, checkIcon, permissions) {
   let icon = panel.getAttribute("icon");
   let ul = document.getElementById("addon-webext-perm-list");
   let header = document.getElementById("addon-webext-perm-intro");
+  let learnMoreLink = document.getElementById("addon-webext-perm-info");
 
   if (checkIcon instanceof RegExp) {
     ok(checkIcon.test(icon), `Notification icon is correct ${JSON.stringify(icon)} ~= ${checkIcon}`);
   } else if (typeof checkIcon == "function") {
     ok(checkIcon(icon), "Notification icon is correct");
   } else {
     is(icon, checkIcon, "Notification icon is correct");
   }
 
   is(ul.childElementCount, permissions.length, `Permissions list has ${permissions.length} entries`);
   if (permissions.length == 0) {
     is(header.getAttribute("hidden"), "true", "Permissions header is hidden");
+    is(learnMoreLink.getAttribute("hidden"), "true", "Permissions learn more is hidden");
   } else {
     is(header.getAttribute("hidden"), "", "Permissions header is visible");
+    is(learnMoreLink.getAttribute("hidden"), "", "Permissions learn more is visible");
   }
 
   for (let i in permissions) {
     let [key, param] = permissions[i];
     checkPermissionString(ul.children[i].textContent, key, param,
                           `Permission number ${i + 1} is correct`);
   }
 }
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -1,10 +1,9 @@
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 ac_add_options --enable-verify-mar
 
 . $topsrcdir/build/mozconfig.stylo
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -1,8 +1,7 @@
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-verify-mar
-ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -1,10 +1,9 @@
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 ac_add_options --enable-verify-mar
 
 . $topsrcdir/build/mozconfig.stylo
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux
 
--- a/browser/config/mozconfigs/linux64/debug-fuzzing
+++ b/browser/config/mozconfigs/linux64/debug-fuzzing
@@ -1,10 +1,9 @@
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 ac_add_options --enable-verify-mar
 
 . $topsrcdir/build/mozconfig.stylo
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux
 
--- a/browser/config/mozconfigs/linux64/debug-searchfox-clang
+++ b/browser/config/mozconfigs/linux64/debug-searchfox-clang
@@ -1,16 +1,15 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 
 . $topsrcdir/build/mozconfig.stylo
 
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Save rust analysis (this requires unlocking the unstable features,
--- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang
@@ -1,16 +1,15 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 
 . $topsrcdir/build/mozconfig.stylo
 
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -1,8 +1,7 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-verify-mar
-ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -1,12 +1,11 @@
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 ac_add_options --enable-verify-mar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/browser/config/mozconfigs/macosx64/debug-searchfox
+++ b/browser/config/mozconfigs/macosx64/debug-searchfox
@@ -1,16 +1,15 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 
 # Save rust analysis (this requires unlocking the unstable features,
 # which is done in the taskcluster task definition via RUSTC_BOOTSTRAP)
 export RUSTFLAGS="-Zsave-analysis"
 
 ac_add_options --enable-clang-plugin
 ac_add_options --enable-mozsearch-plugin
 
--- a/browser/config/mozconfigs/macosx64/debug-static-analysis
+++ b/browser/config/mozconfigs/macosx64/debug-static-analysis
@@ -1,12 +1,11 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/nightly
@@ -1,14 +1,13 @@
 . "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
 
 ac_add_options --disable-install-strip
 ac_add_options --enable-verify-mar
 ac_add_options --enable-instruments
-ac_add_options --enable-dmd
 
 # Cross-compiled builds fail when dtrace is enabled
 if test `uname -s` != Linux; then
   ac_add_options --enable-dtrace
 fi
 
 ac_add_options --enable-lto
 
--- a/browser/config/mozconfigs/macosx64/opt-static-analysis
+++ b/browser/config/mozconfigs/macosx64/opt-static-analysis
@@ -1,14 +1,13 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --disable-debug
 ac_add_options --enable-optimize
-ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . "$topsrcdir/build/mozconfig.common.override"
 
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -4,17 +4,16 @@ whitelist = {
     'release': {},
     'nightly': {},
     }
 
 all_platforms = ['win64', 'win64-aarch64', 'win32', 'linux32', 'linux64', 'macosx64']
 
 for platform in all_platforms:
     whitelist['nightly'][platform] = [
-        'ac_add_options --enable-dmd',
         'ac_add_options --with-branding=browser/branding/nightly',
     ]
 
 whitelist['nightly']['macosx64'] += [
     'ac_add_options --disable-install-strip',
     'ac_add_options --enable-instruments',
     'ac_add_options --enable-dtrace',
     'if test `uname -s` != Linux; then',
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,17 +1,15 @@
 . "$topsrcdir/build/mozconfig.win-common"
 MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
-ac_add_options --enable-profiling  # needed for --enable-dmd to work on Windows
 ac_add_options --enable-verify-mar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/browser/config/mozconfigs/win32/debug-static-analysis
+++ b/browser/config/mozconfigs/win32/debug-static-analysis
@@ -1,16 +1,15 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -1,9 +1,8 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 ac_add_options --enable-verify-mar
-ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64-aarch64/debug
+++ b/browser/config/mozconfigs/win64-aarch64/debug
@@ -2,18 +2,16 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 . "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-win64"
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
-ac_add_options --enable-profiling  # needed for --enable-dmd to work on Windows
 ac_add_options --enable-verify-mar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/browser/config/mozconfigs/win64-aarch64/nightly
+++ b/browser/config/mozconfigs/win64-aarch64/nightly
@@ -1,14 +1,13 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-opt"
 
 ac_add_options --enable-verify-mar
-ac_add_options --enable-dmd
 
 ac_add_options --enable-lto
 
 ac_add_options --with-branding=browser/branding/nightly
 
 unset ENABLE_CLANG_PLUGIN
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -1,18 +1,16 @@
 . "$topsrcdir/build/mozconfig.win-common"
 MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
-ac_add_options --enable-dmd
-ac_add_options --enable-profiling  # needed for --enable-dmd to work on Windows
 ac_add_options --enable-verify-mar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -1,10 +1,9 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 ac_add_options --enable-verify-mar
-ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -42,16 +42,17 @@ xpinstallDisabledButton.accesskey=n
 webextPerms.header=Add %S?
 
 webextPerms.unsignedWarning=Caution: This add-on is unverified. Malicious add-ons can steal your private information or compromise your computer. Only install this add-on if you trust the source.
 
 # LOCALIZATION NOTE (webextPerms.listIntro)
 # This string will be followed by a list of permissions requested
 # by the webextension.
 webextPerms.listIntro=It requires your permission to:
+webextPerms.learnMore=Learn more about permissions
 webextPerms.add.label=Add
 webextPerms.add.accessKey=A
 webextPerms.cancel.label=Cancel
 webextPerms.cancel.accessKey=C
 
 # LOCALIZATION NOTE (webextPerms.sideloadMenuItem)
 # %1$S will be replaced with the localized name of the sideloaded add-on.
 # %2$S will be replace with the name of the application (e.g., Firefox, Nightly)
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -287,16 +287,17 @@ var ExtensionsUI = {
   _buildStrings(info) {
     let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
     let brandBundle = Services.strings.createBundle(BRAND_PROPERTIES);
     let appName = brandBundle.GetStringFromName("brandShortName");
     let info2 = Object.assign({appName}, info);
 
     let strings = ExtensionData.formatPermissionStrings(info2, bundle);
     strings.addonName = info.addon.name;
+    strings.learnMore = bundle.GetStringFromName("webextPerms.learnMore");
     return strings;
   },
 
   async showPermissionsPrompt(target, strings, icon, histkey) {
     let {browser, window} = getTabBrowser(target);
 
     // Wait for any pending prompts in this window to complete before
     // showing the next one.
@@ -312,16 +313,21 @@ var ExtensionsUI = {
           let textEl = doc.getElementById("addon-webext-perm-text");
           textEl.textContent = strings.text;
           textEl.hidden = !strings.text;
 
           let listIntroEl = doc.getElementById("addon-webext-perm-intro");
           listIntroEl.textContent = strings.listIntro;
           listIntroEl.hidden = (strings.msgs.length == 0);
 
+          let listInfoEl = doc.getElementById("addon-webext-perm-info");
+          listInfoEl.textContent = strings.learnMore;
+          listInfoEl.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "extension-permissions";
+          listInfoEl.hidden = (strings.msgs.length == 0);
+
           let list = doc.getElementById("addon-webext-perm-list");
           while (list.firstChild) {
             list.firstChild.remove();
           }
 
           for (let msg of strings.msgs) {
             let item = doc.createElementNS(HTML_NS, "li");
             item.textContent = msg;
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -659,29 +659,102 @@ async function sanitizeInternal(items, a
   if (!progress.isShutdown)
     removePendingSanitization(uid);
   progress = {};
   if (seenError) {
     throw new Error("Error sanitizing");
   }
 }
 
+// This is an helper that retrieves the principals with site data just once
+// and only when needed.
+class PrincipalsCollector {
+  constructor() {
+    this.principals = null;
+  }
+
+  async getAllPrincipals(progress) {
+    if (this.principals == null) {
+      // Here is the list of principals with site data.
+      progress.advancement = "get-principals";
+      this.principals = await this.getAllPrincipalsInternal(progress);
+    }
+
+    return this.principals;
+  }
+
+  async getAllPrincipalsInternal(progress) {
+    progress.step = "principals-quota-manager";
+    let principals = await new Promise(resolve => {
+      quotaManagerService.getUsage(request => {
+        progress.step = "principals-quota-manager-getUsage";
+        if (request.resultCode != Cr.NS_OK) {
+          // We are probably shutting down. We don't want to propagate the
+          // error, rejecting the promise.
+          resolve([]);
+          return;
+        }
+
+        let list = [];
+        for (let item of request.result) {
+          let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
+          let uri = principal.URI;
+          if (isSupportedURI(uri)) {
+            list.push(principal);
+          }
+        }
+
+        progress.step = "principals-quota-manager-completed";
+        resolve(list);
+      });
+    }).catch(ex => {
+      Cu.reportError("QuotaManagerService promise failed: " + ex);
+      return [];
+    });
+
+    progress.step = "principals-service-workers";
+    let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+    for (let i = 0; i < serviceWorkers.length; i++) {
+      let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+      // We don't need to check the scheme. SW are just exposed to http/https URLs.
+      principals.push(sw.principal);
+    }
+
+    // Let's take the list of unique hosts+OA from cookies.
+    progress.step = "principals-cookies";
+    let enumerator = Services.cookies.enumerator;
+    let hosts = new Set();
+    for (let cookie of enumerator) {
+      hosts.add(cookie.rawHost + ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
+    }
+
+    progress.step = "principals-host-cookie";
+    hosts.forEach(host => {
+      // Cookies and permissions are handled by origin/host. Doesn't matter if we
+      // use http: or https: schema here.
+      principals.push(
+        Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://" + host));
+    });
+
+    progress.step = "total-principals:" + principals.length;
+    return principals;
+  }
+}
+
 async function sanitizeOnShutdown(progress) {
   log("Sanitizing on shutdown");
 
   if (Sanitizer.shouldSanitizeOnShutdown) {
     // Need to sanitize upon shutdown
     progress.advancement = "shutdown-cleaner";
     let itemsToClear = getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
     await Sanitizer.sanitize(itemsToClear, { progress });
   }
 
-  // Here is the list of principals with site data.
-  progress.advancement = "get-principals";
-  let principals = await getAllPrincipals(progress);
+  let principalsCollector = new PrincipalsCollector();
 
   // Clear out QuotaManager storage for principals that have been marked as
   // session only.  The cookie service has special logic that avoids writing
   // such cookies to disk, but QuotaManager always touches disk, so we need to
   // wipe the data on shutdown (or startup if we failed to wipe it at
   // shutdown).  (Note that some session cookies do survive Firefox restarts
   // because the Session Store that remembers your tabs between sessions takes
   // on the responsibility for persisting them through restarts.)
@@ -699,16 +772,18 @@ async function sanitizeOnShutdown(progre
   // ACCEPT_NORMALLY need to be wiped.  Second, the set of origins that have
   // the permission explicitly set to ACCEPT_SESSION need to be wiped.  There
   // are also other ways to think about and accomplish this, but this is what
   // the logic below currently does!
   if (Services.prefs.getIntPref(PREF_COOKIE_LIFETIME,
                                 Ci.nsICookieService.ACCEPT_NORMALLY) == Ci.nsICookieService.ACCEPT_SESSION) {
     log("Session-only configuration detected");
     progress.advancement = "session-only";
+
+    let principals = await principalsCollector.getAllPrincipals(progress);
     await maybeSanitizeSessionPrincipals(progress, principals);
     return;
   }
 
   progress.advancement = "session-permission";
 
   // Let's see if we have to forget some particular site.
   for (let permission of Services.perms.enumerator) {
@@ -720,16 +795,17 @@ async function sanitizeOnShutdown(progre
     // We consider just permissions set for http, https and file URLs.
     if (!isSupportedURI(permission.principal.URI)) {
       continue;
     }
 
     log("Custom session cookie permission detected for: " + permission.principal.URI.spec);
 
     // We use just the URI here, because permissions ignore OriginAttributes.
+    let principals = await principalsCollector.getAllPrincipals(progress);
     let selectedPrincipals = extractMatchingPrincipals(principals, permission.principal.URI);
     await maybeSanitizeSessionPrincipals(progress, selectedPrincipals);
   }
 
   if (Sanitizer.shouldSanitizeNewTabContainer) {
     progress.advancement = "newtab-segregation";
     sanitizeNewTabSegregation();
     removePendingSanitization("newtab-container");
@@ -740,71 +816,16 @@ async function sanitizeOnShutdown(progre
     // sanitizing again on startup.
     removePendingSanitization("shutdown");
     Services.prefs.savePrefFile(null);
   }
 
   progress.advancement = "done";
 }
 
-// Retrieve the list of nsIPrincipals with site data.
-async function getAllPrincipals(progress) {
-  progress.step = "principals-quota-manager";
-  let principals = await new Promise(resolve => {
-    quotaManagerService.getUsage(request => {
-      progress.step = "principals-quota-manager-getUsage";
-      if (request.resultCode != Cr.NS_OK) {
-        // We are probably shutting down. We don't want to propagate the
-        // error, rejecting the promise.
-        resolve([]);
-        return;
-      }
-
-      let list = [];
-      for (let item of request.result) {
-        let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
-        let uri = principal.URI;
-        if (isSupportedURI(uri)) {
-          list.push(principal);
-        }
-      }
-
-      progress.step = "principals-quota-manager-completed";
-      resolve(list);
-    });
-  }).catch(() => []);
-
-  progress.step = "principals-service-workers";
-  let serviceWorkers = serviceWorkerManager.getAllRegistrations();
-  for (let i = 0; i < serviceWorkers.length; i++) {
-    let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-    // We don't need to check the scheme. SW are just exposed to http/https URLs.
-    principals.push(sw.principal);
-  }
-
-  // Let's take the list of unique hosts+OA from cookies.
-  progress.step = "principals-cookies";
-  let enumerator = Services.cookies.enumerator;
-  let hosts = new Set();
-  for (let cookie of enumerator) {
-    hosts.add(cookie.rawHost + ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
-  }
-
-  progress.step = "principals-host-cookie";
-  hosts.forEach(host => {
-    // Cookies and permissions are handled by origin/host. Doesn't matter if we
-    // use http: or https: schema here.
-    principals.push(
-      Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://" + host));
-  });
-
-  progress.step = "total-principals:" + principals.length;
-  return principals;
-}
-
 // Extracts the principals matching matchUri as root domain.
 function extractMatchingPrincipals(principals, matchUri) {
   return principals.filter(principal => {
     return Services.eTLD.hasRootDomain(matchUri.host, principal.URI.host);
   });
 }
 
 // This method receives a list of principals and it checks if some of them or
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -167,16 +167,21 @@
 #widget-overflow .webextension-popup-browser {
   background: #fff;
 }
 
 #addon-progress-notification-progressmeter {
   margin: 2px 4px;
 }
 
+/* Permissions popup learn more link. */
+#addon-webext-perm-info {
+  margin-inline-start: 0;
+}
+
 /* Contextual Feature Recommendation popup-notification */
 
 #cfr-notification-header {
   width: 100%;
   text-align: center;
   box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.2);
 }
 
--- a/build/workspace-hack/Cargo.toml
+++ b/build/workspace-hack/Cargo.toml
@@ -10,18 +10,18 @@ version = "0.1.0"
 #  "Each time Cargo runs a build it will re-resolve the dependency graph, "
 #  "notably selecting different features sometimes for each build."
 #
 # We work around this by specifying the union of the set of features selected
 # by dependencies in each cargo invocation for each instance they would differ.
 
 [build-dependencies]
 void = { features = ["std", "default"], version = "1.0.2" }
-syn = { features = ["extra-traits", "fold", "full"],  version = "0.14" }
-log = { features = ["release_max_level_info", "release_max_level_warn", "std"],  version = "0.4.5" }
+syn = { features = ["clone-impls", "default", "derive", "extra-traits", "full", "parsing", "printing", "proc-macro", "quote", "visit"],  version = "0.15" }
+log = { features = ["release_max_level_info", "release_max_level_warn", "std"],  version = "0.4.6" }
 serde = { features = ["default", "rc", "serde_derive", "std"], version = "1.0.66" }
 serde_derive = { features = ["default", "deserialize_in_place"], version = "1.0.66" }
 quote = { features = ["default", "proc-macro"], version = "0.5.2" }
 proc-macro2 = { features = ["default", "proc-macro"], version = "0.3.5" }
 
 [target."cfg(windows)".dependencies.winapi]
 version = "0.3.6"
 features = [
--- a/chrome/nsChromeRegistryContent.cpp
+++ b/chrome/nsChromeRegistryContent.cpp
@@ -165,20 +165,16 @@ nsChromeRegistryContent::CheckForOSAcces
 }
 
 NS_IMETHODIMP
 nsChromeRegistryContent::CheckForNewChrome() { CONTENT_NOT_IMPLEMENTED(); }
 
 NS_IMETHODIMP
 nsChromeRegistryContent::IsLocaleRTL(const nsACString& aPackage,
                                      bool* aResult) {
-  if (aPackage != nsDependentCString("global")) {
-    NS_ERROR("Packages other than global unavailable");
-    return NS_ERROR_NOT_AVAILABLE;
-  }
   *aResult = GetDirectionForLocale(mLocale);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsChromeRegistryContent::GetSelectedLocale(const nsACString& aPackage,
                                            bool aAsBCP47, nsACString& aLocale) {
   if (aPackage != nsDependentCString("global")) {
--- a/db/sqlite3/src/sqlite3.c
+++ b/db/sqlite3/src/sqlite3.c
@@ -1,11 +1,11 @@
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.27.1.  By combining all the individual C code files into this
+** version 3.27.2.  By combining all the individual C code files into this
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
 ** of 5% or more are commonly seen when SQLite is compiled as a single
 ** translation unit.
 **
 ** This file is all you need to compile SQLite.  To use SQLite in other
 ** programs, you need this file and the "sqlite3.h" header file that defines
@@ -1157,19 +1157,19 @@ extern "C" {
 ** or SHA3-256 hash of the entire source tree.  If the source code has
 ** been edited in any way since it was last checked in, then the last
 ** four hexadecimal digits of the hash may be modified.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.27.1"
-#define SQLITE_VERSION_NUMBER 3027001
-#define SQLITE_SOURCE_ID      "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd"
+#define SQLITE_VERSION        "3.27.2"
+#define SQLITE_VERSION_NUMBER 3027002
+#define SQLITE_SOURCE_ID      "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
@@ -3403,17 +3403,17 @@ SQLITE_API int sqlite3_changes(sqlite3*)
 ** part of trigger programs. ^Executing any other type of SQL statement
 ** does not affect the value returned by sqlite3_total_changes().
 ** 
 ** ^Changes made as part of [foreign key actions] are included in the
 ** count, but those made as part of REPLACE constraint resolution are
 ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers 
 ** are not counted.
 **
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
 ** of rows that changed due to SQL statement run against database
 ** connection D.  Any changes by other database connections are ignored.
 ** To detect changes against a database file from other database
 ** connections use the [PRAGMA data_version] command or the
 ** [SQLITE_FCNTL_DATA_VERSION] [file control].
 ** 
 ** If a separate thread makes changes on the same database connection
 ** while [sqlite3_total_changes()] is running then the value
@@ -14932,67 +14932,66 @@ typedef struct VdbeOpList VdbeOpList;
 #define OP_SequenceTest  115 /* synopsis: if( cursor[P1].ctr++ ) pc = P2   */
 #define OP_OpenPseudo    116 /* synopsis: P3 columns in r[P2]              */
 #define OP_Close         117
 #define OP_ColumnsUsed   118
 #define OP_SeekHit       119 /* synopsis: seekHit=P2                       */
 #define OP_Sequence      120 /* synopsis: r[P2]=cursor[P1].ctr++           */
 #define OP_NewRowid      121 /* synopsis: r[P2]=rowid                      */
 #define OP_Insert        122 /* synopsis: intkey=r[P3] data=r[P2]          */
-#define OP_InsertInt     123 /* synopsis: intkey=P3 data=r[P2]             */
-#define OP_Delete        124
-#define OP_ResetCount    125
-#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData    127 /* synopsis: r[P2]=data                       */
-#define OP_RowData       128 /* synopsis: r[P2]=data                       */
-#define OP_Rowid         129 /* synopsis: r[P2]=rowid                      */
-#define OP_NullRow       130
-#define OP_SeekEnd       131
-#define OP_SorterInsert  132 /* synopsis: key=r[P2]                        */
-#define OP_IdxInsert     133 /* synopsis: key=r[P2]                        */
-#define OP_IdxDelete     134 /* synopsis: key=r[P2@P3]                     */
-#define OP_DeferredSeek  135 /* synopsis: Move P3 to P1.rowid if needed    */
-#define OP_IdxRowid      136 /* synopsis: r[P2]=rowid                      */
-#define OP_Destroy       137
-#define OP_Clear         138
-#define OP_ResetSorter   139
-#define OP_CreateBtree   140 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
+#define OP_Delete        123
+#define OP_ResetCount    124
+#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData    126 /* synopsis: r[P2]=data                       */
+#define OP_RowData       127 /* synopsis: r[P2]=data                       */
+#define OP_Rowid         128 /* synopsis: r[P2]=rowid                      */
+#define OP_NullRow       129
+#define OP_SeekEnd       130
+#define OP_SorterInsert  131 /* synopsis: key=r[P2]                        */
+#define OP_IdxInsert     132 /* synopsis: key=r[P2]                        */
+#define OP_IdxDelete     133 /* synopsis: key=r[P2@P3]                     */
+#define OP_DeferredSeek  134 /* synopsis: Move P3 to P1.rowid if needed    */
+#define OP_IdxRowid      135 /* synopsis: r[P2]=rowid                      */
+#define OP_Destroy       136
+#define OP_Clear         137
+#define OP_ResetSorter   138
+#define OP_CreateBtree   139 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
+#define OP_SqlExec       140
 #define OP_Real          141 /* same as TK_FLOAT, synopsis: r[P2]=P4       */
-#define OP_SqlExec       142
-#define OP_ParseSchema   143
-#define OP_LoadAnalysis  144
-#define OP_DropTable     145
-#define OP_DropIndex     146
-#define OP_DropTrigger   147
-#define OP_IntegrityCk   148
-#define OP_RowSetAdd     149 /* synopsis: rowset(P1)=r[P2]                 */
-#define OP_Param         150
-#define OP_FkCounter     151 /* synopsis: fkctr[P1]+=P2                    */
-#define OP_MemMax        152 /* synopsis: r[P1]=max(r[P1],r[P2])           */
-#define OP_OffsetLimit   153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggInverse    154 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
-#define OP_AggStep       155 /* synopsis: accum=r[P3] step(r[P2@P5])       */
-#define OP_AggStep1      156 /* synopsis: accum=r[P3] step(r[P2@P5])       */
-#define OP_AggValue      157 /* synopsis: r[P3]=value N=P2                 */
-#define OP_AggFinal      158 /* synopsis: accum=r[P1] N=P2                 */
-#define OP_Expire        159
-#define OP_TableLock     160 /* synopsis: iDb=P1 root=P2 write=P3          */
-#define OP_VBegin        161
-#define OP_VCreate       162
-#define OP_VDestroy      163
-#define OP_VOpen         164
-#define OP_VColumn       165 /* synopsis: r[P3]=vcolumn(P2)                */
-#define OP_VRename       166
-#define OP_Pagecount     167
-#define OP_MaxPgcnt      168
-#define OP_Trace         169
-#define OP_CursorHint    170
-#define OP_Noop          171
-#define OP_Explain       172
-#define OP_Abortable     173
+#define OP_ParseSchema   142
+#define OP_LoadAnalysis  143
+#define OP_DropTable     144
+#define OP_DropIndex     145
+#define OP_DropTrigger   146
+#define OP_IntegrityCk   147
+#define OP_RowSetAdd     148 /* synopsis: rowset(P1)=r[P2]                 */
+#define OP_Param         149
+#define OP_FkCounter     150 /* synopsis: fkctr[P1]+=P2                    */
+#define OP_MemMax        151 /* synopsis: r[P1]=max(r[P1],r[P2])           */
+#define OP_OffsetLimit   152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggInverse    153 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
+#define OP_AggStep       154 /* synopsis: accum=r[P3] step(r[P2@P5])       */
+#define OP_AggStep1      155 /* synopsis: accum=r[P3] step(r[P2@P5])       */
+#define OP_AggValue      156 /* synopsis: r[P3]=value N=P2                 */
+#define OP_AggFinal      157 /* synopsis: accum=r[P1] N=P2                 */
+#define OP_Expire        158
+#define OP_TableLock     159 /* synopsis: iDb=P1 root=P2 write=P3          */
+#define OP_VBegin        160
+#define OP_VCreate       161
+#define OP_VDestroy      162
+#define OP_VOpen         163
+#define OP_VColumn       164 /* synopsis: r[P3]=vcolumn(P2)                */
+#define OP_VRename       165
+#define OP_Pagecount     166
+#define OP_MaxPgcnt      167
+#define OP_Trace         168
+#define OP_CursorHint    169
+#define OP_Noop          170
+#define OP_Explain       171
+#define OP_Abortable     172
 
 /* Properties such as "out2" or "jump" that are specified in
 ** comments following the "case" for each opcode in the vdbe.c
 ** are encoded into bitvectors as follows:
 */
 #define OPFLG_JUMP        0x01  /* jump:  P2 holds jmp target */
 #define OPFLG_IN1         0x02  /* in1:   P1 is an input */
 #define OPFLG_IN2         0x04  /* in2:   P2 is an input */
@@ -15011,22 +15010,22 @@ typedef struct VdbeOpList VdbeOpList;
 /*  64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\
 /*  72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
 /*  80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
 /*  88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\
 /*  96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\
 /* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
 /* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
 /* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\
-/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
-/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\
-/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
-/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,}
+/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
+/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00,\
+/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,}
 
 /* The sqlite3P2Values() routine is able to run faster if it knows
 ** the value of the largest JUMP opcode.  The smaller the maximum
 ** JUMP opcode the better, so the mkopcodeh.tcl script that
 ** generated this include file strives to group all JUMP opcodes
 ** together near the beginning of the list.
 */
 #define SQLITE_MX_JUMP_OPCODE  61  /* Maximum JUMP opcode */
@@ -19169,17 +19168,17 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3NPr
 SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int);
 SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
 SQLITE_PRIVATE void sqlite3AlterFunctions(void);
 SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
 SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
 SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
 SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
 SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
-SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int, int);
+SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
 SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*);
 SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
 SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
 SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
 SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
 SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
 SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
 SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
@@ -32134,67 +32133,66 @@ SQLITE_PRIVATE const char *sqlite3Opcode
     /* 115 */ "SequenceTest"     OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
     /* 116 */ "OpenPseudo"       OpHelp("P3 columns in r[P2]"),
     /* 117 */ "Close"            OpHelp(""),
     /* 118 */ "ColumnsUsed"      OpHelp(""),
     /* 119 */ "SeekHit"          OpHelp("seekHit=P2"),
     /* 120 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
     /* 121 */ "NewRowid"         OpHelp("r[P2]=rowid"),
     /* 122 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
-    /* 123 */ "InsertInt"        OpHelp("intkey=P3 data=r[P2]"),
-    /* 124 */ "Delete"           OpHelp(""),
-    /* 125 */ "ResetCount"       OpHelp(""),
-    /* 126 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
-    /* 127 */ "SorterData"       OpHelp("r[P2]=data"),
-    /* 128 */ "RowData"          OpHelp("r[P2]=data"),
-    /* 129 */ "Rowid"            OpHelp("r[P2]=rowid"),
-    /* 130 */ "NullRow"          OpHelp(""),
-    /* 131 */ "SeekEnd"          OpHelp(""),
-    /* 132 */ "SorterInsert"     OpHelp("key=r[P2]"),
-    /* 133 */ "IdxInsert"        OpHelp("key=r[P2]"),
-    /* 134 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
-    /* 135 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
-    /* 136 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
-    /* 137 */ "Destroy"          OpHelp(""),
-    /* 138 */ "Clear"            OpHelp(""),
-    /* 139 */ "ResetSorter"      OpHelp(""),
-    /* 140 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
+    /* 123 */ "Delete"           OpHelp(""),
+    /* 124 */ "ResetCount"       OpHelp(""),
+    /* 125 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+    /* 126 */ "SorterData"       OpHelp("r[P2]=data"),
+    /* 127 */ "RowData"          OpHelp("r[P2]=data"),
+    /* 128 */ "Rowid"            OpHelp("r[P2]=rowid"),
+    /* 129 */ "NullRow"          OpHelp(""),
+    /* 130 */ "SeekEnd"          OpHelp(""),
+    /* 131 */ "SorterInsert"     OpHelp("key=r[P2]"),
+    /* 132 */ "IdxInsert"        OpHelp("key=r[P2]"),
+    /* 133 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
+    /* 134 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
+    /* 135 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
+    /* 136 */ "Destroy"          OpHelp(""),
+    /* 137 */ "Clear"            OpHelp(""),
+    /* 138 */ "ResetSorter"      OpHelp(""),
+    /* 139 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
+    /* 140 */ "SqlExec"          OpHelp(""),
     /* 141 */ "Real"             OpHelp("r[P2]=P4"),
-    /* 142 */ "SqlExec"          OpHelp(""),
-    /* 143 */ "ParseSchema"      OpHelp(""),
-    /* 144 */ "LoadAnalysis"     OpHelp(""),
-    /* 145 */ "DropTable"        OpHelp(""),
-    /* 146 */ "DropIndex"        OpHelp(""),
-    /* 147 */ "DropTrigger"      OpHelp(""),
-    /* 148 */ "IntegrityCk"      OpHelp(""),
-    /* 149 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
-    /* 150 */ "Param"            OpHelp(""),
-    /* 151 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
-    /* 152 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
-    /* 153 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
-    /* 154 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
-    /* 155 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
-    /* 156 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
-    /* 157 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
-    /* 158 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
-    /* 159 */ "Expire"           OpHelp(""),
-    /* 160 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
-    /* 161 */ "VBegin"           OpHelp(""),
-    /* 162 */ "VCreate"          OpHelp(""),
-    /* 163 */ "VDestroy"         OpHelp(""),
-    /* 164 */ "VOpen"            OpHelp(""),
-    /* 165 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
-    /* 166 */ "VRename"          OpHelp(""),
-    /* 167 */ "Pagecount"        OpHelp(""),
-    /* 168 */ "MaxPgcnt"         OpHelp(""),
-    /* 169 */ "Trace"            OpHelp(""),
-    /* 170 */ "CursorHint"       OpHelp(""),
-    /* 171 */ "Noop"             OpHelp(""),
-    /* 172 */ "Explain"          OpHelp(""),
-    /* 173 */ "Abortable"        OpHelp(""),
+    /* 142 */ "ParseSchema"      OpHelp(""),
+    /* 143 */ "LoadAnalysis"     OpHelp(""),
+    /* 144 */ "DropTable"        OpHelp(""),
+    /* 145 */ "DropIndex"        OpHelp(""),
+    /* 146 */ "DropTrigger"      OpHelp(""),
+    /* 147 */ "IntegrityCk"      OpHelp(""),
+    /* 148 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
+    /* 149 */ "Param"            OpHelp(""),
+    /* 150 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
+    /* 151 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
+    /* 152 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+    /* 153 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
+    /* 154 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
+    /* 155 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
+    /* 156 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
+    /* 157 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
+    /* 158 */ "Expire"           OpHelp(""),
+    /* 159 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
+    /* 160 */ "VBegin"           OpHelp(""),
+    /* 161 */ "VCreate"          OpHelp(""),
+    /* 162 */ "VDestroy"         OpHelp(""),
+    /* 163 */ "VOpen"            OpHelp(""),
+    /* 164 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
+    /* 165 */ "VRename"          OpHelp(""),
+    /* 166 */ "Pagecount"        OpHelp(""),
+    /* 167 */ "MaxPgcnt"         OpHelp(""),
+    /* 168 */ "Trace"            OpHelp(""),
+    /* 169 */ "CursorHint"       OpHelp(""),
+    /* 170 */ "Noop"             OpHelp(""),
+    /* 171 */ "Explain"          OpHelp(""),
+    /* 172 */ "Abortable"        OpHelp(""),
   };
   return azName[i];
 }
 #endif
 
 /************** End of opcodes.c *********************************************/
 /************** Begin file os_unix.c *****************************************/
 /*
@@ -87930,24 +87928,17 @@ case OP_NewRowid: {           /* out2 */
 ** allocated, then ownership of P2 is transferred to the pseudo-cursor
 ** and register P2 becomes ephemeral.  If the cursor is changed, the
 ** value of register P2 will then change.  Make sure this does not
 ** cause any problems.)
 **
 ** This instruction only works on tables.  The equivalent instruction
 ** for indices is OP_IdxInsert.
 */
-/* Opcode: InsertInt P1 P2 P3 P4 P5
-** Synopsis: intkey=P3 data=r[P2]
-**
-** This works exactly like OP_Insert except that the key is the
-** integer value P3, not the value of the integer stored in register P3.
-*/
-case OP_Insert: 
-case OP_InsertInt: {
+case OP_Insert: {
   Mem *pData;       /* MEM cell holding data for the record to be inserted */
   Mem *pKey;        /* MEM cell holding key  for the record */
   VdbeCursor *pC;   /* Cursor to table into which insert is written */
   int seekResult;   /* Result of prior seek or 0 if no USESEEKRESULT flag */
   const char *zDb;  /* database name - used by the update hook */
   Table *pTab;      /* Table structure - used by update and pre-update hooks */
   BtreePayload x;   /* Payload to be inserted */
 
@@ -87958,26 +87949,21 @@ case OP_InsertInt: {
   assert( pC!=0 );
   assert( pC->eCurType==CURTYPE_BTREE );
   assert( pC->uc.pCursor!=0 );
   assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
   assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
   REGISTER_TRACE(pOp->p2, pData);
   sqlite3VdbeIncrWriteCounter(p, pC);
 
-  if( pOp->opcode==OP_Insert ){
-    pKey = &aMem[pOp->p3];
-    assert( pKey->flags & MEM_Int );
-    assert( memIsValid(pKey) );
-    REGISTER_TRACE(pOp->p3, pKey);
-    x.nKey = pKey->u.i;
-  }else{
-    assert( pOp->opcode==OP_InsertInt );
-    x.nKey = pOp->p3;
-  }
+  pKey = &aMem[pOp->p3];
+  assert( pKey->flags & MEM_Int );
+  assert( memIsValid(pKey) );
+  REGISTER_TRACE(pOp->p3, pKey);
+  x.nKey = pKey->u.i;
 
   if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
     assert( pC->iDb>=0 );
     zDb = db->aDb[pC->iDb].zDbSName;
     pTab = pOp->p4.pTab;
     assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
   }else{
     pTab = 0;
@@ -96126,16 +96112,48 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGr
       }
       resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
                    zType,0);
     }
   }
   return 0;
 }
 
+#ifndef SQLITE_OMIT_WINDOWFUNC
+/*
+** Walker callback for resolveRemoveWindows().
+*/
+static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
+  if( ExprHasProperty(pExpr, EP_WinFunc) ){
+    Window **pp;
+    for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
+      if( *pp==pExpr->y.pWin ){
+        *pp = (*pp)->pNextWin;
+        break;
+      }    
+    }
+  }
+  return WRC_Continue;
+}
+
+/*
+** Remove any Window objects owned by the expression pExpr from the
+** Select.pWin list of Select object pSelect.
+*/
+static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){
+  Walker sWalker;
+  memset(&sWalker, 0, sizeof(Walker));
+  sWalker.xExprCallback = resolveRemoveWindowsCb;
+  sWalker.u.pSelect = pSelect;
+  sqlite3WalkExpr(&sWalker, pExpr);
+}
+#else
+# define resolveRemoveWindows(x,y)
+#endif
+
 /*
 ** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
 ** The Name context of the SELECT statement is pNC.  zType is either
 ** "ORDER" or "GROUP" depending on which type of clause pOrderBy is.
 **
 ** This routine resolves each term of the clause into an expression.
 ** If the order-by term is an integer I between 1 and N (where N is the
 ** number of columns in the result set of the SELECT) then the expression
@@ -96192,29 +96210,20 @@ static int resolveOrderGroupBy(
 
     /* Otherwise, treat the ORDER BY term as an ordinary expression */
     pItem->u.x.iOrderByCol = 0;
     if( sqlite3ResolveExprNames(pNC, pE) ){
       return 1;
     }
     for(j=0; j<pSelect->pEList->nExpr; j++){
       if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
-#ifndef SQLITE_OMIT_WINDOWFUNC
-        if( ExprHasProperty(pE, EP_WinFunc) ){
-          /* Since this window function is being changed into a reference
-          ** to the same window function the result set, remove the instance
-          ** of this window function from the Select.pWin list. */
-          Window **pp;
-          for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
-            if( *pp==pE->y.pWin ){
-              *pp = (*pp)->pNextWin;
-            }    
-          }
-        }
-#endif
+        /* Since this expresion is being changed into a reference
+        ** to an identical expression in the result set, remove all Window
+        ** objects belonging to the expression from the Select.pWin list. */
+        resolveRemoveWindows(pSelect, pE);
         pItem->u.x.iOrderByCol = j+1;
       }
     }
   }
   return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
 }
 
 /*
@@ -99176,24 +99185,21 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
     /* Could not find an existing table or index to use as the RHS b-tree.
     ** We will have to generate an ephemeral table to do the job.
     */
     u32 savedNQueryLoop = pParse->nQueryLoop;
     int rMayHaveNull = 0;
     eType = IN_INDEX_EPH;
     if( inFlags & IN_INDEX_LOOP ){
       pParse->nQueryLoop = 0;
-      if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
-        eType = IN_INDEX_ROWID;
-      }
     }else if( prRhsHasNull ){
       *prRhsHasNull = rMayHaveNull = ++pParse->nMem;
     }
     assert( pX->op==TK_IN );
-    sqlite3CodeRhsOfIN(pParse, pX, iTab, eType==IN_INDEX_ROWID);
+    sqlite3CodeRhsOfIN(pParse, pX, iTab);
     if( rMayHaveNull ){
       sqlite3SetHasNullFlag(v, iTab, rMayHaveNull);
     }
     pParse->nQueryLoop = savedNQueryLoop;
   }
 
   if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){
     int i, n;
@@ -99284,35 +99290,28 @@ SQLITE_PRIVATE void sqlite3VectorErrorMs
 **     x IN (SELECT a FROM b)     -- IN operator with subquery on the right
 **
 ** The pExpr parameter is the IN operator.  The cursor number for the
 ** constructed ephermeral table is returned.  The first time the ephemeral
 ** table is computed, the cursor number is also stored in pExpr->iTable,
 ** however the cursor number returned might not be the same, as it might
 ** have been duplicated using OP_OpenDup.
 **
-** If parameter isRowid is non-zero, then LHS of the IN operator is guaranteed
-** to be a non-null integer. In this case, the ephemeral table can be an
-** table B-Tree that keyed by only integers.  The more general cases uses
-** an index B-Tree which can have arbitrary keys, but is slower to both
-** read and write.
-**
 ** If the LHS expression ("x" in the examples) is a column value, or
 ** the SELECT statement returns a column value, then the affinity of that
 ** column is used to build the index keys. If both 'x' and the
 ** SELECT... statement are columns, then numeric affinity is used
 ** if either column has NUMERIC or INTEGER affinity. If neither
 ** 'x' nor the SELECT... statement are columns, then numeric affinity
 ** is used.
 */
 SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
   Parse *pParse,          /* Parsing context */
   Expr *pExpr,            /* The IN operator */
-  int iTab,               /* Use this cursor number */
-  int isRowid             /* If true, LHS is a rowid */
+  int iTab                /* Use this cursor number */
 ){
   int addrOnce = 0;           /* Address of the OP_Once instruction at top */
   int addr;                   /* Address of OP_OpenEphemeral instruction */
   Expr *pLeft;                /* the LHS of the IN operator */
   KeyInfo *pKeyInfo = 0;      /* Key information */
   int nVal;                   /* Size of vector pLeft */
   Vdbe *v;                    /* The prepared statement under construction */
 
@@ -99355,46 +99354,43 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
     VdbeComment((v, "return address"));
 
     addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
   }
 
   /* Check to see if this is a vector IN operator */
   pLeft = pExpr->pLeft;
   nVal = sqlite3ExprVectorSize(pLeft);
-  assert( !isRowid || nVal==1 );
 
   /* Construct the ephemeral table that will contain the content of
   ** RHS of the IN operator.
   */
   pExpr->iTable = iTab;
-  addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, 
-      pExpr->iTable, (isRowid?0:nVal));
+  addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal);
 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
   if( ExprHasProperty(pExpr, EP_xIsSelect) ){
     VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId));
   }else{
     VdbeComment((v, "RHS of IN operator"));
   }
 #endif
-  pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
+  pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
 
   if( ExprHasProperty(pExpr, EP_xIsSelect) ){
     /* Case 1:     expr IN (SELECT ...)
     **
     ** Generate code to write the results of the select into the temporary
     ** table allocated and opened above.
     */
     Select *pSelect = pExpr->x.pSelect;
     ExprList *pEList = pSelect->pEList;
 
     ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d",
         addrOnce?"":"CORRELATED ", pSelect->selId
     ));
-    assert( !isRowid );
     /* If the LHS and RHS of the IN operator do not match, that
     ** error will have been caught long before we reach this point. */
     if( ALWAYS(pEList->nExpr==nVal) ){
       SelectDest dest;
       int i;
       sqlite3SelectDestInit(&dest, SRT_Set, iTab);
       dest.zAffSdst = exprINAffinity(pParse, pExpr);
       pSelect->iLimit = 0;
@@ -99437,46 +99433,33 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
     if( pKeyInfo ){
       assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
       pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
     }
 
     /* Loop through each expression in <exprlist>. */
     r1 = sqlite3GetTempReg(pParse);
     r2 = sqlite3GetTempReg(pParse);
-    if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
     for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
       Expr *pE2 = pItem->pExpr;
-      int iValToIns;
 
       /* If the expression is not constant then we will need to
       ** disable the test that was generated above that makes sure
       ** this code only executes once.  Because for a non-constant
       ** expression we need to rerun this code each time.
       */
       if( addrOnce && !sqlite3ExprIsConstant(pE2) ){
         sqlite3VdbeChangeToNoop(v, addrOnce);
         addrOnce = 0;
       }
 
       /* Evaluate the expression and insert it into the temp table */
-      if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
-        sqlite3VdbeAddOp3(v, OP_InsertInt, iTab, r2, iValToIns);
-      }else{
-        r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
-        if( isRowid ){
-          sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
-                            sqlite3VdbeCurrentAddr(v)+2);
-          VdbeCoverage(v);
-          sqlite3VdbeAddOp3(v, OP_Insert, iTab, r2, r3);
-        }else{
-          sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
-          sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
-        }
-      }
+      r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
+      sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
+      sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
     }
     sqlite3ReleaseTempReg(pParse, r1);
     sqlite3ReleaseTempReg(pParse, r2);
   }
   if( pKeyInfo ){
     sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
   }
   if( addrOnce ){
@@ -118102,20 +118085,23 @@ SQLITE_PRIVATE void sqlite3CompleteInser
     }
     pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0);
     if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
       assert( pParse->nested==0 );
       pik_flags |= OPFLAG_NCHANGE;
       pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
       if( update_flags==0 ){
-        sqlite3VdbeAddOp4(v, OP_InsertInt, 
-            iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE
+        int r = sqlite3GetTempReg(pParse);
+        sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
+        sqlite3VdbeAddOp4(v, OP_Insert, 
+            iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE
         );
         sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+        sqlite3ReleaseTempReg(pParse, r);
       }
 #endif
     }
     sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
                          aRegIdx[i]+1,
                          pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
     sqlite3VdbeChangeP5(v, pik_flags);
   }
@@ -136422,17 +136408,16 @@ static int codeEqualityTerm(
     pIn = pLevel->u.in.aInLoop;
     if( pIn ){
       int iMap = 0;               /* Index in aiMap[] */
       pIn += i;
       for(i=iEq;i<pLoop->nLTerm; i++){
         if( pLoop->aLTerm[i]->pExpr==pX ){
           int iOut = iReg + i - iEq;
           if( eType==IN_INDEX_ROWID ){
-            testcase( nEq>1 );  /* Happens with a UNIQUE index on ROWID */
             pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut);
           }else{
             int iCol = aiMap ? aiMap[iMap++] : 0;
             pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
           }
           sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
           if( i==iEq ){
             pIn->iCur = iTab;
@@ -137184,16 +137169,19 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeO
     testcase( pTerm->wtFlags & TERM_VIRTUAL );
     iReleaseReg = ++pParse->nMem;
     iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
     if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
     addrNxt = pLevel->addrNxt;
     sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
     VdbeCoverage(v);
     pLevel->op = OP_Noop;
+    if( (pTerm->prereqAll & pLevel->notReady)==0 ){
+      pTerm->wtFlags |= TERM_CODED;
+    }
   }else if( (pLoop->wsFlags & WHERE_IPK)!=0
          && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
   ){
     /* Case 3:  We have an inequality comparison against the ROWID field.
     */
     int testOp = OP_Noop;
     int start;
     int memEndValue = 0;
@@ -217071,17 +217059,17 @@ static void fts5Fts5Func(
 */
 static void fts5SourceIdFunc(
   sqlite3_context *pCtx,          /* Function call context */
   int nArg,                       /* Number of args */
   sqlite3_value **apUnused        /* Function arguments */
 ){
   assert( nArg==0 );
   UNUSED_PARAM2(nArg, apUnused);
-  sqlite3_result_text(pCtx, "fts5: 2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd", -1, SQLITE_TRANSIENT);
+  sqlite3_result_text(pCtx, "fts5: 2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7", -1, SQLITE_TRANSIENT);
 }
 
 /*
 ** Return true if zName is the extension on one of the shadow tables used
 ** by this module.
 */
 static int fts5ShadowName(const char *zName){
   static const char *azName[] = {
@@ -221835,15 +221823,15 @@ SQLITE_API int sqlite3_stmt_init(
   rc = sqlite3StmtVtabInit(db);
 #endif
   return rc;
 }
 #endif /* SQLITE_CORE */
 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
 
 /************** End of stmt.c ************************************************/
-#if __LINE__!=221843
+#if __LINE__!=221831
 #undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID      "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959alt2"
+#define SQLITE_SOURCE_ID      "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0alt2"
 #endif
 /* Return the source-id for this library */
 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
 /************************** End of sqlite3.c ******************************/
--- a/db/sqlite3/src/sqlite3.h
+++ b/db/sqlite3/src/sqlite3.h
@@ -118,19 +118,19 @@ extern "C" {
 ** or SHA3-256 hash of the entire source tree.  If the source code has
 ** been edited in any way since it was last checked in, then the last
 ** four hexadecimal digits of the hash may be modified.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.27.1"
-#define SQLITE_VERSION_NUMBER 3027001
-#define SQLITE_SOURCE_ID      "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd"
+#define SQLITE_VERSION        "3.27.2"
+#define SQLITE_VERSION_NUMBER 3027002
+#define SQLITE_SOURCE_ID      "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
@@ -2364,17 +2364,17 @@ SQLITE_API int sqlite3_changes(sqlite3*)
 ** part of trigger programs. ^Executing any other type of SQL statement
 ** does not affect the value returned by sqlite3_total_changes().
 ** 
 ** ^Changes made as part of [foreign key actions] are included in the
 ** count, but those made as part of REPLACE constraint resolution are
 ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers 
 ** are not counted.
 **
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
 ** of rows that changed due to SQL statement run against database
 ** connection D.  Any changes by other database connections are ignored.
 ** To detect changes against a database file from other database
 ** connections use the [PRAGMA data_version] command or the
 ** [SQLITE_FCNTL_DATA_VERSION] [file control].
 ** 
 ** If a separate thread makes changes on the same database connection
 ** while [sqlite3_total_changes()] is running then the value
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -9,16 +9,17 @@
 @import "resource://devtools/client/aboutdebugging-new/src/base.css";
 
 /*
 * Components
 */
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeActions.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css";
--- a/devtools/client/aboutdebugging-new/src/base.css
+++ b/devtools/client/aboutdebugging-new/src/base.css
@@ -45,39 +45,43 @@
 
   /* Typography from Photon */
   --body-10-font-size: 13px;
   --body-10-font-weight: 400;
   --body-20-font-size: 15px;
   --body-20-font-weight: 700;
   --caption-20-font-size: 13px;
   --caption-20-font-weight: 400;
+  --display-20-font-size: 36px;
+  --display-20-font-weight: 200;
   --title-20-font-size: 17px;
   --title-20-font-weight: 600;
+  --title-30-font-size: 22px;
+  --title-30-font-weight: 300;
 
   /* Global layout vars */
   --page-width: 664px;
   --base-unit: 4px;
 
   /* Global styles */
   --base-font-style: message-box;
   --base-font-size: var(--body-10-font-size);
   --base-font-weight: var(--body-10-font-weight);
   --base-line-height: 1.8;
   --button-font-size: var(--base-font-size);
   --micro-font-size: 11px;
 
   --monospace-font-family: monospace;
 
   /*
-  * Variables particular to about:debugging
-  */
+   * Variables particular to about:debugging
+   */
   --alt-heading-icon-size: calc(var(--base-unit) * 6);
   --alt-heading-icon-gap: var(--base-unit);
-  --main-heading-icon-size: calc(var(--base-unit) * 16);
+  --main-heading-icon-size: calc(var(--base-unit) * 17); /* 4px * 17 = 68px */
   --main-heading-icon-gap: calc(var(--base-unit) * 3);
   --main-subheading-icon-size: calc(var(--base-unit) * 4);
   --main-subheading-heading-icon-gap: calc(var(--base-unit) * 2);
 }
 
 /*
 * Reset some tags
 */
@@ -113,18 +117,17 @@ a {
 }
 a:hover {
   color: var(--link-color-hover);
 }
 a:active {
   color: var(--link-color-active);
 }
 
-p {
-  /* remove default browser margins on <p> elements */
+p, h1 {
   margin: 0;
 }
 
 
 /*
 * Utils
 */
 
@@ -145,38 +148,32 @@ p {
   -moz-user-select: none;
 }
 
 /*
 * Typography
 */
 
 /* Main style for heading (i.e. h1) */
-/* +--------+-------------+
-*  | [Icon] | Lorem ipsum |
-*  +--------+-------------+
-*/
 .main-heading {
-  font-size: 2.5em;
-  font-weight: lighter;
+  font-size: var(--display-20-font-size);
+  font-weight: var(--display-20-font-weight);
   line-height: 1.2;
   color: var(--in-content-text-color);
-  margin: 0;
-  margin-bottom: .5em;
-
-  display: grid;
-  grid-template-columns: var(--main-heading-icon-size) 1fr;
-  grid-column-gap: var(--main-heading-icon-gap);
-  align-items: center;
 }
 
 .main-heading__icon {
   width: 100%;
 }
 
+.main-heading-subtitle {
+  font-size: var(--title-30-font-size);
+  font-weight: var(--title-30-font-weight);
+}
+
 /* Main style for a subheading (i.e. h2). It features an icon */
 /* +--------+-------------+
 *  | [Icon] | Lorem ipsum |
 *  +--------+-------------+
 */
 .main-subheading {
   margin-block: calc(var(--base-unit) * 4);
   font-size: var(--title-20-font-size); /* Note: this is from Photon Title 20 */
--- a/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css
+++ b/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css
@@ -19,17 +19,16 @@
   align-items: center;
   background-color: var(--grey-20);
   display: grid;
   grid-template-columns: 1fr max-content;
   padding: var(--base-unit);
 }
 
 .profiler-dialog__header__title {
-  margin: 0;
   margin-inline-start: calc(var(--base-unit) * 2);
 
   /* Reset <h1> styles */
   font-size: 15px;
   font-weight: normal;
 }
 
 .profiler-dialog__inner {
--- a/devtools/client/aboutdebugging-new/src/components/RuntimeActions.css
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimeActions.css
@@ -1,18 +1,9 @@
 /* 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/. */
 
-.runtime-actions {
+.runtime-actions__toolbar {
   column-gap: var(--base-unit);
-  display: grid;
-  grid-template-areas: "start center end";
-  grid-template-columns: max-content 1fr max-content;
+  display: flex;
+  justify-content: right;
 }
-
-.runtime-actions__end {
-  grid-area: end;
-}
-
-.runtime-actions__start {
-  grid-area: start;
-}
--- a/devtools/client/aboutdebugging-new/src/components/RuntimeActions.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimeActions.js
@@ -39,72 +39,69 @@ class RuntimeActions extends PureCompone
   }
 
   renderConnectionPromptSetting() {
     const { dispatch, runtimeDetails, runtimeId } = this.props;
     const { connectionPromptEnabled } = runtimeDetails;
     // do not show the connection prompt setting in 'This Firefox'
     return runtimeId !== RUNTIMES.THIS_FIREFOX
              ? ConnectionPromptSetting({
-               className: "runtime-actions__end",
-               connectionPromptEnabled,
-               dispatch,
+                 connectionPromptEnabled,
+                 dispatch,
              })
              : null;
   }
 
   renderExtensionDebugSetting() {
     const { dispatch, runtimeDetails } = this.props;
     const { extensionDebugEnabled, info } = runtimeDetails;
     return isExtensionDebugSettingNeeded(info.type)
              ? ExtensionDebugSetting({
-                 className: "runtime-actions__start",
                  dispatch,
                  extensionDebugEnabled,
              })
              : null;
   }
 
   renderProfileButton() {
     const { runtimeId } = this.props;
 
     return runtimeId !== RUNTIMES.THIS_FIREFOX
          ? Localized(
            {
              id: "about-debugging-runtime-profile-button",
            },
            dom.button(
              {
-               className: "default-button runtime-actions__start " +
-                          "js-profile-runtime-button",
+               className: "default-button js-profile-runtime-button",
                onClick: () => this.onProfilerButtonClick(),
              },
              "Profile Runtime"
            ),
          )
          : null;
   }
 
   renderTemporaryExtensionInstaller() {
     const { dispatch, runtimeDetails } = this.props;
     const { type } = runtimeDetails.info;
     return isSupportedDebugTargetPane(type, DEBUG_TARGET_PANE.TEMPORARY_EXTENSION)
-             ? TemporaryExtensionInstaller({
-                 className: "runtime-actions__end",
-                 dispatch,
-             })
+             ? TemporaryExtensionInstaller({ dispatch })
              : null;
   }
 
   render() {
     return dom.div(
-      {
-        className: "runtime-actions",
-      },
-      this.renderConnectionPromptSetting(),
-      this.renderProfileButton(),
+      {},
+      dom.div(
+        {
+          className: "runtime-actions__toolbar",
+        },
+        this.renderTemporaryExtensionInstaller(),
+        this.renderProfileButton(),
+        this.renderConnectionPromptSetting(),
+      ),
       this.renderExtensionDebugSetting(),
-      this.renderTemporaryExtensionInstaller(),
     );
   }
 }
 
 module.exports = RuntimeActions;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * Layout for the runtime info container is:
+ *
+ *      <- 68px --x--------- 1fr ----------><---- max ---->
+ *   ∧  +---------+------------------------+--------------+
+ *  1fr |         | Runtime Info           |   [Action]   |
+ *   |  |  Icon   | eg "Firefox (70.0a1)"  |              |
+ *   x  |         +------------------------+              |
+ *  max |         | Device Name (optional) |              |
+ *   ∨  +---------+------------------------+--------------+
+ */
+.runtime-info {
+  align-items: center;
+  display: grid;
+
+  grid-column-gap: var(--main-heading-icon-gap);
+  grid-template-areas:
+  "icon title    action"
+  "icon subtitle action";
+  grid-template-columns: var(--main-heading-icon-size) 1fr max-content;
+  grid-template-rows: 1fr max-content;
+
+  margin-block-end: calc(var(--base-unit) * 5);
+}
+
+.runtime-info__icon {
+  grid-area: icon;
+}
+.runtime-info__title {
+  grid-area: title;
+}
+.runtime-info__subtitle {
+  grid-area: subtitle;
+}
+.runtime-info__action {
+  align-self: start;
+  /* The default-button has a font-size of 1em at the moment, this rule should not be
+   * necessary after the first patch from Bug 1525615 lands. */
+  font-size: var(--base-font-size);
+  grid-area: action;
+}
--- a/devtools/client/aboutdebugging-new/src/components/RuntimeInfo.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimeInfo.js
@@ -24,36 +24,49 @@ class RuntimeInfo extends PureComponent 
     };
   }
 
   render() {
     const { icon, deviceName, name, version } = this.props;
 
     return dom.h1(
       {
-        className: "main-heading",
+        className: "main-heading runtime-info",
       },
       dom.img(
         {
-          className: "main-heading__icon",
+          className: "main-heading__icon runtime-info__icon",
           src: icon,
         }
       ),
       Localized(
         {
-          id: deviceName ? "about-debugging-runtime-info-with-model"
-                          : "about-debugging-runtime-info",
+          id: "about-debugging-runtime-name",
           $name: name,
-          $deviceName: deviceName,
           $version: version,
         },
         dom.label(
           {
-            className: "js-runtime-info",
+            className: "js-runtime-name runtime-info__title",
+          },
+          `${ name } (${ version })`
+        )
+      ),
+      deviceName ?
+        dom.label(
+          {
+            className: "main-heading-subtitle runtime-info__subtitle",
           },
-          `${ name } on ${ deviceName } (${ version })`
-        )
+          deviceName
+        ) : null,
+      dom.button(
+        {
+          className: "default-button runtime-info__action",
+          // Implementation ongoing in Bug 1505128.
+          disabled: true,
+        },
+        "Disconnect"
       )
     );
   }
 }
 
 module.exports = RuntimeInfo;
--- a/devtools/client/aboutdebugging-new/src/components/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/moz.build
@@ -14,12 +14,13 @@ DevToolsModules(
     'App.js',
     'CompatibilityWarning.js',
     'ConnectionPromptSetting.js',
     'ExtensionDebugSetting.js',
     'ProfilerDialog.css',
     'ProfilerDialog.js',
     'RuntimeActions.css',
     'RuntimeActions.js',
+    'RuntimeInfo.css',
     'RuntimeInfo.js',
     'RuntimePage.js',
     'ServiceWorkersWarning.js',
 )
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_real_usb_runtime_page_runtime_info.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_real_usb_runtime_page_runtime_info.js
@@ -30,17 +30,17 @@ add_task(async function() {
     }, 5000)),
     /* eslint-enable mozilla/no-arbitrary-setTimeout */
   ]);
 
   info("Select a USB runtime");
   await selectRuntime(sidebarInfo.deviceName, runtimeDetails.info.name, document);
 
   info("Check that runtime info is properly displayed");
-  const runtimeInfo = document.querySelector(".js-runtime-info");
+  const runtimeInfo = document.querySelector(".js-runtime-name");
   ok(runtimeInfo, "Runtime info is displayed");
   const runtimeInfoText = runtimeInfo.textContent;
   ok(runtimeInfoText.includes(runtimeDetails.info.name),
      "Runtime info shows the correct runtime name: " + runtimeInfoText);
   ok(runtimeInfoText.includes(runtimeDetails.info.version),
      "Runtime info shows the correct version number: " + runtimeInfoText);
 
   await removeTab(tab);
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_routes.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_routes.js
@@ -21,17 +21,17 @@ add_task(async function() {
   // enable USB devices mocks
   const mocks = new Mocks();
 
   const { document, tab } = await openAboutDebugging();
 
   info("Check 'This Firefox' route");
   document.location.hash = "#/runtime/this-firefox";
   await waitUntil(() => document.querySelector(".js-runtime-page"));
-  const infoLabel = document.querySelector(".js-runtime-info").textContent;
+  const infoLabel = document.querySelector(".js-runtime-name").textContent;
   // NOTE: when using USB Mocks, we see only "Firefox" as the device name
   ok(infoLabel.includes("Firefox"), "Runtime is displayed as Firefox");
   ok(!infoLabel.includes(" on "), "Runtime is not associated to any device");
   is(
       document.title,
       "Debugging - Runtime / this-firefox",
       "Checking title for 'runtime' page"
   );
@@ -52,17 +52,17 @@ add_task(async function() {
     deviceName: "Fancy Phone",
     name: "Lorem ipsum",
   });
   mocks.emitUSBUpdate();
   await connectToRuntime("Fancy Phone", document);
   // navigate to it via URL
   document.location.hash = "#/runtime/1337id";
   await waitUntil(() => document.querySelector(".js-runtime-page"));
-  const runtimeLabel = document.querySelector(".js-runtime-info").textContent;
+  const runtimeLabel = document.querySelector(".js-runtime-name").textContent;
   is(
       document.title,
       "Debugging - Runtime / 1337id",
       "Checking title for 'runtime' page with USB device"
   );
   ok(runtimeLabel.includes("Lorem ipsum"), "Runtime is displayed with the mocked name");
 
   await removeTab(tab);
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_network_runtime.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_network_runtime.js
@@ -27,17 +27,17 @@ add_task(async function() {
     };
   };
 
   info("Test addons in runtime page for Network client");
   await connectToRuntime(NETWORK_RUNTIME_HOST, document);
   await selectRuntime(NETWORK_RUNTIME_HOST, NETWORK_RUNTIME_APP_NAME, document);
 
   info("Check that the network runtime mock is properly displayed");
-  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-info");
+  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-name");
   ok(thisFirefoxRuntimeInfo, "Runtime info for this-firefox runtime is displayed");
   const runtimeInfoText = thisFirefoxRuntimeInfo.textContent;
 
   ok(runtimeInfoText.includes(NETWORK_RUNTIME_APP_NAME),
     "network runtime info shows the correct runtime name: " + runtimeInfoText);
   ok(runtimeInfoText.includes(NETWORK_RUNTIME_VERSION),
     "network runtime info shows the correct version number: " + runtimeInfoText);
 
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_page_with_serviceworker.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_select_page_with_serviceworker.js
@@ -49,17 +49,17 @@ add_task(async function() {
   info("Click on the ThisFirefox item in the sidebar");
   const requestsSuccess = waitForRequestsSuccess(window.AboutDebugging.store);
   thisFirefoxLink.click();
 
   info("Wait for all target requests to complete");
   await requestsSuccess;
 
   info("Check that the runtime info is rendered for This Firefox");
-  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-info");
+  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-name");
   ok(thisFirefoxRuntimeInfo, "Runtime info for this-firefox runtime is displayed");
 
   const text = thisFirefoxRuntimeInfo.textContent;
   ok(text.includes("Firefox") && text.includes("63.0"),
     "this-firefox runtime info shows the correct values");
 
   await removeTab(tab);
 });
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox_runtime_info.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox_runtime_info.js
@@ -26,17 +26,17 @@ add_task(async function() {
   registerCleanupFunction(() => {
     disableRuntimeClientFactoryMock();
   });
 
   const { document, tab, window } = await openAboutDebugging();
   await selectThisFirefoxPage(document, window.AboutDebugging.store);
 
   info("Check that the 'This Firefox' mock is properly displayed");
-  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-info");
+  const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-name");
   ok(thisFirefoxRuntimeInfo, "Runtime info for this-firefox runtime is displayed");
   const runtimeInfoText = thisFirefoxRuntimeInfo.textContent;
   ok(runtimeInfoText.includes("Firefox"),
     "this-firefox runtime info shows the correct runtime name: " + runtimeInfoText);
   ok(runtimeInfoText.includes("63.0"),
     "this-firefox runtime info shows the correct version number: " + runtimeInfoText);
 
   await removeTab(tab);
--- a/devtools/client/aboutdebugging-new/test/browser/head.js
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -254,17 +254,17 @@ async function connectToRuntime(deviceNa
   await waitUntil(() => !sidebarItem.querySelector(".js-connect-button"));
 }
 
 async function selectRuntime(deviceName, name, document) {
   const sidebarItem = findSidebarItemByText(deviceName, document);
   sidebarItem.querySelector(".js-sidebar-link").click();
 
   await waitUntil(() => {
-    const runtimeInfo = document.querySelector(".js-runtime-info");
+    const runtimeInfo = document.querySelector(".js-runtime-name");
     return runtimeInfo && runtimeInfo.textContent.includes(name);
   });
 }
 
 function getToolbox(win) {
   return gDevTools.getToolboxes().find(toolbox => toolbox.win === win);
 }
 
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -259,23 +259,17 @@ about-debugging-worker-scope =
 # Displayed for service workers in runtime pages, to label the push service endpoint (url)
 # of a worker
 about-debugging-worker-push-service =
   .label = Push Service
 
 # Displayed for runtime info in runtime pages.
 # { $name } is brand name such as "Firefox Nightly"
 # { $version } is version such as "64.0a1"
-about-debugging-runtime-info = { $name } ({ $version })
-
-# Displayed for runtime info in runtime page when we display the device model as well.
-# { $name } is brand name such as "Firefox Nightly"
-# { $version } is version such as "64.0a1"
-# { $deviceName } is model name of device
-about-debugging-runtime-info-with-model = { $name } on { $deviceName } ({ $version })
+about-debugging-runtime-name = { $name } ({ $version })
 
 # Text of the connection prompt button displayed in Runtime pages, when the preference
 # "devtools.debugger.prompt-connection" is false on the target runtime.
 about-debugging-connection-prompt-enable-button = Enable connection prompt
 
 # Text of the connection prompt button displayed in Runtime pages, when the preference
 # "devtools.debugger.prompt-connection" is true on the target runtime.
 about-debugging-connection-prompt-disable-button = Disable connection prompt
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1800,57 +1800,30 @@ const ThreadActor = ActorClassWithSpec(t
     for (const actor of bpActors) {
       sourceActor.applyBreakpoint(actor);
     }
 
     this._debuggerSourcesSeen.add(source);
     return true;
   },
 
-  /**
-   * Get prototypes and properties of multiple objects.
-   */
-  onPrototypesAndProperties: function(request) {
-    const result = {};
-    for (const actorID of request.actors) {
-      // This code assumes that there are no lazily loaded actors returned
-      // by this call.
-      const actor = this.conn.getActor(actorID);
-      if (!actor) {
-        return { from: this.actorID,
-                 error: "noSuchActor" };
-      }
-      const handler = actor.prototypeAndProperties;
-      if (!handler) {
-        return { from: this.actorID,
-                 error: "unrecognizedPacketType",
-                 message: ('Actor "' + actorID +
-                           '" does not recognize the packet type ' +
-                           '"prototypeAndProperties"') };
-      }
-      result[actorID] = handler.call(actor, {});
-    }
-    return { from: this.actorID,
-             actors: result };
-  },
 });
 
 Object.assign(ThreadActor.prototype.requestTypes, {
   "attach": ThreadActor.prototype.onAttach,
   "detach": ThreadActor.prototype.onDetach,
   "reconfigure": ThreadActor.prototype.onReconfigure,
   "resume": ThreadActor.prototype.onResume,
   "clientEvaluate": ThreadActor.prototype.onClientEvaluate,
   "frames": ThreadActor.prototype.onFrames,
   "interrupt": ThreadActor.prototype.onInterrupt,
   "eventListeners": ThreadActor.prototype.onEventListeners,
   "releaseMany": ThreadActor.prototype.onReleaseMany,
   "sources": ThreadActor.prototype.onSources,
   "threadGrips": ThreadActor.prototype.onThreadGrips,
-  "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties,
   "skipBreakpoints": ThreadActor.prototype.onSkipBreakpoints,
 });
 
 exports.ThreadActor = ThreadActor;
 
 /**
  * Creates a PauseActor.
  *
deleted file mode 100644
--- a/devtools/server/tests/unit/test_objectgrips-09.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-/**
- * This tests exercises getProtypesAndProperties message accepted
- * by a thread actor.
- */
-
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-add_task(threadClientTest(async ({ threadClient, debuggee, client }) => {
-  return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const args = packet.frame.arguments;
-
-      threadClient.getPrototypesAndProperties(
-        [args[0].actor, args[1].actor], function(response) {
-          const obj1 = response.actors[args[0].actor];
-          const obj2 = response.actors[args[1].actor];
-          Assert.equal(obj1.ownProperties.x.configurable, true);
-          Assert.equal(obj1.ownProperties.x.enumerable, true);
-          Assert.equal(obj1.ownProperties.x.writable, true);
-          Assert.equal(obj1.ownProperties.x.value, 10);
-
-          Assert.equal(obj1.ownProperties.y.configurable, true);
-          Assert.equal(obj1.ownProperties.y.enumerable, true);
-          Assert.equal(obj1.ownProperties.y.writable, true);
-          Assert.equal(obj1.ownProperties.y.value, "kaiju");
-
-          Assert.equal(obj2.ownProperties.z.configurable, true);
-          Assert.equal(obj2.ownProperties.z.enumerable, true);
-          Assert.equal(obj2.ownProperties.z.writable, true);
-          Assert.equal(obj2.ownProperties.z.value, 123);
-
-          Assert.ok(obj1.prototype != undefined);
-          Assert.ok(obj2.prototype != undefined);
-
-          threadClient.resume(resolve);
-        });
-    });
-
-    debuggee.eval(function stopMe(arg1) {
-      debugger;
-    }.toString());
-    debuggee.eval("stopMe({ x: 10, y: 'kaiju'}, { z: 123 })");
-  });
-}));
-
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -155,17 +155,16 @@ skip-if = true # breakpoint sliding is n
 [test_objectgrips-01.js]
 [test_objectgrips-02.js]
 [test_objectgrips-03.js]
 [test_objectgrips-04.js]
 [test_objectgrips-05.js]
 [test_objectgrips-06.js]
 [test_objectgrips-07.js]
 [test_objectgrips-08.js]
-[test_objectgrips-09.js]
 [test_objectgrips-10.js]
 [test_objectgrips-11.js]
 [test_objectgrips-12.js]
 [test_objectgrips-13.js]
 [test_objectgrips-14.js]
 [test_objectgrips-15.js]
 [test_objectgrips-16.js]
 [test_objectgrips-17.js]
--- a/devtools/shared/client/thread-client.js
+++ b/devtools/shared/client/thread-client.js
@@ -700,27 +700,14 @@ ThreadClient.prototype = {
     if (form.actor in this._threadGrips) {
       return this._threadGrips[form.actor];
     }
 
     this._threadGrips[form.actor] = new SourceClient(this, form);
     return this._threadGrips[form.actor];
   },
 
-  /**
-   * Request the prototype and own properties of mutlipleObjects.
-   *
-   * @param onResponse function
-   *        Called with the request's response.
-   * @param actors [string]
-   *        List of actor ID of the queried objects.
-   */
-  getPrototypesAndProperties: DebuggerClient.requester({
-    type: "prototypesAndProperties",
-    actors: arg(0),
-  }),
-
   events: ["newSource", "progress"],
 };
 
 eventSource(ThreadClient.prototype);
 
 module.exports = ThreadClient;
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -583,17 +583,17 @@ nsresult FetchDriver::HttpFetch(
     }
     // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
     // If request’s referrer policy is the empty string,
     // then set request’s referrer policy to the user-set default policy.
     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
       nsCOMPtr<nsILoadInfo> loadInfo = httpChan->LoadInfo();
       bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
       net::ReferrerPolicy referrerPolicy = static_cast<net::ReferrerPolicy>(
-          NS_GetDefaultReferrerPolicy(isPrivate));
+          NS_GetDefaultReferrerPolicy(httpChan, uri, isPrivate));
       mRequest->SetReferrerPolicy(referrerPolicy);
     }
 
     rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
                                        mRequest);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Bug 1120722 - Authorization will be handled later.
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1712,16 +1712,63 @@ void HTMLMediaElement::ShutdownDecoder()
         mDecoder->GetNextOutputStreamTrackID();
   }
   mDecoder->Shutdown();
   DDUNLINKCHILD(mDecoder.get());
   mDecoder = nullptr;
   ReportAudioTrackSilenceProportionTelemetry();
 }
 
+void HTMLMediaElement::ReportPlayedTimeAfterBlockedTelemetry() {
+  if (!mHasPlayEverBeenBlocked) {
+    return;
+  }
+  mHasPlayEverBeenBlocked = false;
+
+  const double playTimeThreshold = 7.0;
+  const double playTimeAfterBlocked = mCurrentLoadPlayTime.Total();
+  if (playTimeAfterBlocked <= 0.0) {
+    return;
+  }
+
+  const bool isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd =
+      Duration() < playTimeThreshold && Ended();
+  LOG(LogLevel::Debug, ("%p PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED=%f, isVideo=%d",
+                        this, playTimeAfterBlocked, IsVideo()));
+  if (IsVideo() && playTimeAfterBlocked >= playTimeThreshold) {
+    AccumulateCategorical(
+        mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+            VPlayedMoreThan7s);
+  } else if (IsVideo() && playTimeAfterBlocked < playTimeThreshold) {
+    if (isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd) {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              VPlayedToTheEnd);
+    } else {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              VPlayedLessThan7s);
+    }
+  } else if (!IsVideo() && playTimeAfterBlocked >= playTimeThreshold) {
+    AccumulateCategorical(
+        mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+            APlayedMoreThan7s);
+  } else if (!IsVideo() && playTimeAfterBlocked < playTimeThreshold) {
+    if (isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd) {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              APlayedToTheEnd);
+    } else {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              APlayedLessThan7s);
+    }
+  }
+}
+
 void HTMLMediaElement::AbortExistingLoads() {
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
 
   // Set a new load ID. This will cause events which were enqueued
   // with a different load ID to silently be cancelled.
   mCurrentLoadID++;
 
@@ -1833,16 +1880,17 @@ void HTMLMediaElement::AbortExistingLoad
   mIsRunningSelectResource = false;
 
   if (mTextTrackManager) {
     mTextTrackManager->NotifyReset();
   }
 
   mEventDeliveryPaused = false;
   mPendingEvents.Clear();
+  mCurrentLoadPlayTime.Reset();
 
   AssertReadyStateIsNothing();
 }
 
 void HTMLMediaElement::NoSupportedMediaSourceError(
     const nsACString& aErrorDetails) {
   if (mDecoder) {
     ShutdownDecoder();
@@ -3524,16 +3572,17 @@ HTMLMediaElement::~HTMLMediaElement() {
   }
 
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->Shutdown();
     mAudioChannelWrapper = nullptr;
   }
 
   WakeLockRelease();
+  ReportPlayedTimeAfterBlockedTelemetry();
 
   DecoderDoctorLogger::LogDestruction(this);
 }
 
 void HTMLMediaElement::StopSuspendingAfterFirstFrame() {
   mAllowSuspendAfterFirstFrame = false;
   if (!mSuspendedAfterFirstFrame) return;
   mSuspendedAfterFirstFrame = false;
@@ -3633,17 +3682,16 @@ already_AddRefed<Promise> HTMLMediaEleme
     MaybeDoLoad();
     mPendingPlayPromises.AppendElement(promise);
     return promise.forget();
   }
 
   if (AudioChannelAgentBlockedPlay()) {
     LOG(LogLevel::Debug, ("%p play blocked by AudioChannelAgent.", this));
     promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
-    DispatchEventsWhenPlayWasNotAllowed();
     return promise.forget();
   }
 
   UpdateHadAudibleAutoplayState();
 
   const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
   mPendingPlayPromises.AppendElement(promise);
 
@@ -3666,16 +3714,17 @@ void HTMLMediaElement::DispatchEventsWhe
 #if defined(MOZ_WIDGET_ANDROID)
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       this, NS_LITERAL_STRING("MozAutoplayMediaBlocked"), CanBubble::eYes,
       ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 #endif
   OwnerDoc()->MaybeNotifyAutoplayBlocked();
   ReportToConsole(nsIScriptError::warningFlag, "BlockAutoplayError");
+  mHasPlayEverBeenBlocked = true;
 }
 
 void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) {
   if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE) {
     // The media load algorithm will be initiated by a user interaction.
     // We want to boost the channel priority for better responsiveness.
     // Note this must be done before UpdatePreloadAction() which will
     // update |mPreloadAction|.
@@ -5723,26 +5772,35 @@ void HTMLMediaElement::DispatchAsyncEven
   } else {
     event = new nsAsyncEventRunner(aName, this);
   }
 
   mMainThreadEventTarget->Dispatch(event.forget());
 
   if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) {
     mPlayTime.Start();
+    mCurrentLoadPlayTime.Start();
     if (IsHidden()) {
       HiddenVideoStart();
     }
   } else if (aName.EqualsLiteral("waiting")) {
     mPlayTime.Pause();
+    mCurrentLoadPlayTime.Pause();
     HiddenVideoStop();
   } else if (aName.EqualsLiteral("pause")) {
     mPlayTime.Pause();
+    mCurrentLoadPlayTime.Pause();
     HiddenVideoStop();
   }
+
+  // It would happen when (1) media aborts current load (2) media pauses (3)
+  // media end (4) media unbind from tree (because we would pause it)
+  if (aName.EqualsLiteral("pause")) {
+    ReportPlayedTimeAfterBlockedTelemetry();
+  }
 }
 
 nsresult HTMLMediaElement::DispatchPendingMediaEvents() {
   NS_ASSERTION(!mEventDeliveryPaused,
                "Must not be in bfcache when dispatching pending media events");
 
   uint32_t count = mPendingEvents.Length();
   for (uint32_t i = 0; i < count; ++i) {
@@ -5867,16 +5925,17 @@ void HTMLMediaElement::SuspendOrResumeEl
       ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d", this,
        aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
 
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
+      mCurrentLoadPlayTime.Pause();
       ReportTelemetry();
 
       // For EME content, we may force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
@@ -5884,16 +5943,19 @@ void HTMLMediaElement::SuspendOrResumeEl
         mMediaKeys->GetKeySystem(keySystem);
       }
       if (mDecoder) {
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
+      if (!mPaused) {
+        mCurrentLoadPlayTime.Start();
+      }
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1664,17 +1664,17 @@ class HTMLMediaElement : public nsGeneri
   bool mBlockedAsWithoutMetadata = false;
 
   // This promise is used to notify MediaElementAudioSourceNode that media
   // element is allowed to play when MediaElement is used as a source for web
   // audio.
   MozPromiseHolder<GenericNonExclusivePromise> mAllowedToPlayPromise;
 
  public:
-  // Helper class to measure times for MSE telemetry stats
+  // Helper class to measure times for playback telemetry stats
   class TimeDurationAccumulator {
    public:
     TimeDurationAccumulator() : mCount(0) {}
     void Start() {
       if (IsStarted()) {
         return;
       }
       mStartTime = TimeStamp::Now();
@@ -1697,16 +1697,21 @@ class HTMLMediaElement : public nsGeneri
     }
     uint32_t Count() const {
       if (!IsStarted()) {
         return mCount;
       }
       // Count current run in this report, without increasing the stored count.
       return mCount + 1;
     }
+    void Reset() {
+      mStartTime = TimeStamp();
+      mSum = TimeDuration();
+      mCount = 0;
+    }
 
    private:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 
  private:
@@ -1728,16 +1733,28 @@ class HTMLMediaElement : public nsGeneri
   TimeDurationAccumulator mPlayTime;
 
   // Total time a video has spent playing while hidden.
   TimeDurationAccumulator mHiddenPlayTime;
 
   // Total time a video has (or would have) spent in video-decode-suspend mode.
   TimeDurationAccumulator mVideoDecodeSuspendTime;
 
+  // Total time a video has spent playing on the current load, it would be reset
+  // when media aborts the current load; be paused when the docuemt enters the
+  // bf-cache and be resumed when the docuemt leaves the bf-cache.
+  TimeDurationAccumulator mCurrentLoadPlayTime;
+
+  // True if media has ever been blocked by autoplay policy before.
+  bool mHasPlayEverBeenBlocked = false;
+
+  // Report the Telemetry about whether media played over the specific time
+  // threshold.
+  void ReportPlayedTimeAfterBlockedTelemetry();
+
   // True if user has called load(), seek() or element has started playing
   // before. It's *only* use for checking autoplay policy
   bool mIsBlessed = false;
 
   // True if the first frame has been successfully loaded.
   bool mFirstFrameLoaded = false;
 
   // Media elements also have a default playback start position, which must
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -68,17 +68,18 @@ bool AudioData::AdjustForStartTime(int64
 
 bool AudioData::SetTrimWindow(const media::TimeInterval& aTrim) {
   if (!mAudioData) {
     // MoveableData got called. Can no longer work on it.
     return false;
   }
   const size_t originalFrames = mAudioData.Length() / mChannels;
   const TimeUnit originalDuration = FramesToTimeUnit(originalFrames, mRate);
-  if (aTrim.mStart < mOriginalTime ||
+  if (!aTrim.mStart.IsValid() || !aTrim.mEnd.IsValid() ||
+      aTrim.mStart < mOriginalTime ||
       aTrim.mEnd > mOriginalTime + originalDuration) {
     return false;
   }
 
   auto trimBefore = TimeUnitToFrames(aTrim.mStart - mOriginalTime, mRate);
   auto trimAfter = aTrim.mEnd == GetEndTime()
                        ? originalFrames
                        : TimeUnitToFrames(aTrim.mEnd - mOriginalTime, mRate);
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -8,16 +8,17 @@
 
 #include "TextureD3D11.h"
 
 #include "gfxWindowsPlatform.h"
 #include "nsIWidget.h"
 #include "mozilla/gfx/D3D11Checks.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/Swizzle.h"
 #include "mozilla/layers/ImageHost.h"
 #include "mozilla/layers/ContentHost.h"
 #include "mozilla/layers/Diagnostics.h"
 #include "mozilla/layers/DiagnosticsD3D11.h"
 #include "mozilla/layers/Effects.h"
 #include "mozilla/layers/HelpersD3D11.h"
 #include "nsWindowsHelpers.h"
 #include "gfxPrefs.h"
@@ -42,19 +43,70 @@ namespace mozilla {
 using namespace gfx;
 
 namespace layers {
 
 bool CanUsePartialPresents(ID3D11Device* aDevice);
 
 const FLOAT sBlendFactor[] = {0, 0, 0, 0};
 
+class AsyncReadbackBufferD3D11 final : public AsyncReadbackBuffer {
+ public:
+  AsyncReadbackBufferD3D11(ID3D11DeviceContext* aContext,
+                           ID3D11Texture2D* aTexture, const IntSize& aSize);
+
+  bool MapAndCopyInto(DataSourceSurface* aSurface,
+                      const IntSize& aReadSize) const override;
+
+  ID3D11Texture2D* GetTexture() { return mTexture; }
+
+ private:
+  RefPtr<ID3D11DeviceContext> mContext;
+  RefPtr<ID3D11Texture2D> mTexture;
+};
+
+AsyncReadbackBufferD3D11::AsyncReadbackBufferD3D11(
+    ID3D11DeviceContext* aContext, ID3D11Texture2D* aTexture,
+    const IntSize& aSize)
+    : AsyncReadbackBuffer(aSize), mContext(aContext), mTexture(aTexture) {}
+
+bool AsyncReadbackBufferD3D11::MapAndCopyInto(DataSourceSurface* aSurface,
+                                              const IntSize& aReadSize) const {
+  D3D11_MAPPED_SUBRESOURCE map;
+  HRESULT hr = mContext->Map(mTexture, 0, D3D11_MAP_READ, 0, &map);
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  RefPtr<DataSourceSurface> sourceSurface =
+      Factory::CreateWrappingDataSourceSurface(static_cast<uint8_t*>(map.pData),
+                                               map.RowPitch, mSize,
+                                               SurfaceFormat::B8G8R8A8);
+
+  bool result;
+  {
+    DataSourceSurface::ScopedMap sourceMap(sourceSurface,
+                                           DataSourceSurface::READ);
+    DataSourceSurface::ScopedMap destMap(aSurface, DataSourceSurface::WRITE);
+
+    result = SwizzleData(sourceMap.GetData(), sourceMap.GetStride(),
+                         SurfaceFormat::B8G8R8A8, destMap.GetData(),
+                         destMap.GetStride(), aSurface->GetFormat(), aReadSize);
+  }
+
+  mContext->Unmap(mTexture, 0);
+
+  return result;
+}
+
 CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent,
                                  widget::CompositorWidget* aWidget)
     : Compositor(aWidget, aParent),
+      mWindowRTCopy(nullptr),
       mAttachments(nullptr),
       mHwnd(nullptr),
       mDisableSequenceForNextFrame(false),
       mAllowPartialPresents(false),
       mIsDoubleBuffered(false),
       mVerifyBuffersFailed(false),
       mUseMutexOnPresent(false) {
   mUseMutexOnPresent = gfxPrefs::UseMutexOnPresent();
@@ -297,17 +349,17 @@ already_AddRefed<CompositingRenderTarget
     const gfx::IntRect& aRect, SurfaceInitMode aInit) {
   MOZ_ASSERT(!aRect.IsZeroArea());
 
   if (aRect.IsZeroArea()) {
     return nullptr;
   }
 
   CD3D11_TEXTURE2D_DESC desc(
-      DXGI_FORMAT_B8G8R8A8_UNORM, aRect.Width(), aRect.Height(), 1, 1,
+      DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
       D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
 
   RefPtr<ID3D11Texture2D> texture;
   HRESULT hr =
       mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
   if (FAILED(hr) || !texture) {
     gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr);
     return nullptr;
@@ -392,16 +444,124 @@ CompositorD3D11::CreateRenderTargetFromS
 
   RefPtr<CompositingRenderTargetD3D11> rt =
       new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
   rt->SetSize(aRect.Size());
 
   return rt.forget();
 }
 
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::GetWindowRenderTarget() const {
+#ifndef MOZ_GECKO_PROFILER
+  return nullptr;
+#else
+  if (!profiler_feature_active(ProfilerFeature::Screenshots)) {
+    return nullptr;
+  }
+
+  if (!mDefaultRT) {
+    return nullptr;
+  }
+
+  const IntSize size = mDefaultRT->GetSize();
+
+  RefPtr<ID3D11Texture2D> rtTexture;
+
+  if (!mWindowRTCopy || mWindowRTCopy->GetSize() != size) {
+    /*
+     * The compositor screenshots infrastructure is going to scale down the
+     * render target returned by this method. However, mDefaultRT does not
+     * contain a texture created wth the D3D11_BIND_SHADER_RESOURCE flag, so if
+     * we were to simply return mDefaultRT then scaling would fail.
+     */
+    CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width,
+                               size.height, 1, 1, D3D11_BIND_SHADER_RESOURCE);
+
+    HRESULT hr =
+        mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(rtTexture));
+    if (FAILED(hr)) {
+      return nullptr;
+    }
+
+    mWindowRTCopy = MakeRefPtr<CompositingRenderTargetD3D11>(
+        rtTexture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
+    mWindowRTCopy->SetSize(size);
+  } else {
+    rtTexture = mWindowRTCopy->GetD3D11Texture();
+  }
+
+  const RefPtr<ID3D11Texture2D> sourceTexture = mDefaultRT->GetD3D11Texture();
+  mContext->CopyResource(rtTexture, sourceTexture);
+
+  return RefPtr<CompositingRenderTarget>(
+             static_cast<CompositingRenderTarget*>(mWindowRTCopy))
+      .forget();
+#endif
+}
+
+bool CompositorD3D11::ReadbackRenderTarget(CompositingRenderTarget* aSource,
+                                           AsyncReadbackBuffer* aDest) {
+  RefPtr<CompositingRenderTargetD3D11> srcTexture =
+      static_cast<CompositingRenderTargetD3D11*>(aSource);
+  RefPtr<AsyncReadbackBufferD3D11> destBuffer =
+      static_cast<AsyncReadbackBufferD3D11*>(aDest);
+
+  mContext->CopyResource(destBuffer->GetTexture(),
+                         srcTexture->GetD3D11Texture());
+
+  return true;
+}
+
+already_AddRefed<AsyncReadbackBuffer>
+CompositorD3D11::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
+  RefPtr<ID3D11Texture2D> texture;
+
+  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
+                             aSize.height, 1, 1, 0, D3D11_USAGE_STAGING,
+                             D3D11_CPU_ACCESS_READ);
+
+  HRESULT hr =
+      mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+
+  if (FAILED(hr)) {
+    HandleError(hr);
+    return nullptr;
+  }
+
+  return MakeAndAddRef<AsyncReadbackBufferD3D11>(mContext, texture, aSize);
+}
+
+bool CompositorD3D11::BlitRenderTarget(CompositingRenderTarget* aSource,
+                                       const gfx::IntSize& aSourceSize,
+                                       const gfx::IntSize& aDestSize) {
+  RefPtr<CompositingRenderTargetD3D11> source =
+      static_cast<CompositingRenderTargetD3D11*>(aSource);
+
+  RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
+      SurfaceFormat::B8G8R8A8, source, SamplingFilter::LINEAR, true);
+  texturedEffect->mTextureCoords =
+      Rect(0, 0, Float(aSourceSize.width) / Float(source->GetSize().width),
+           Float(aSourceSize.height) / Float(source->GetSize().height));
+
+  EffectChain effect;
+  effect.mPrimaryEffect = texturedEffect;
+
+  const Float scaleX = Float(aDestSize.width) / Float(aSourceSize.width);
+  const Float scaleY = Float(aDestSize.height) / (aSourceSize.height);
+  const Matrix4x4 transform = Matrix4x4::Scaling(scaleX, scaleY, 1.0f);
+
+  const Rect sourceRect(0, 0, aSourceSize.width, aSourceSize.height);
+
+  DrawQuad(sourceRect, IntRect(0, 0, aDestSize.width, aDestSize.height), effect,
+           1.0f, transform, sourceRect);
+
+  return true;
+}
+
 bool CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect,
                                    RefPtr<ID3D11Texture2D>* aOutTexture,
                                    RefPtr<ID3D11ShaderResourceView>* aOutView) {
   RefPtr<ID3D11Texture2D> texture =
       CreateTexture(aRect, mCurrentRT, aRect.TopLeft());
   if (!texture) {
     return false;
   }
@@ -1045,16 +1205,20 @@ void CompositorD3D11::BeginFrame(const n
 
     mDiagnostics->Start(pixelsPerFrame);
   }
 }
 
 void CompositorD3D11::NormalDrawingDone() { mDiagnostics->End(); }
 
 void CompositorD3D11::EndFrame() {
+  if (!profiler_feature_active(ProfilerFeature::Screenshots) && mWindowRTCopy) {
+    mWindowRTCopy = nullptr;
+  }
+
   if (!mDefaultRT) {
     Compositor::EndFrame();
     return;
   }
 
   if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) {
     gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
     Compositor::EndFrame();
@@ -1276,16 +1440,17 @@ bool CompositorD3D11::VerifyBufferSize()
     RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView;
     RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV;
 
     // Make sure the texture, which belongs to the swapchain, is destroyed
     // before resizing the swapchain.
     if (mCurrentRT == mDefaultRT) {
       mCurrentRT = nullptr;
     }
+
     MOZ_ASSERT(mDefaultRT->hasOneRef());
     mDefaultRT = nullptr;
 
     RefPtr<ID3D11Resource> resource;
     rtView->GetResource(getter_AddRefs(resource));
 
     ULONG newRefCnt = rtView.forget().take()->Release();
 
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -53,16 +53,27 @@ class CompositorD3D11 : public Composito
                                const CompositingRenderTarget* aSource,
                                const gfx::IntPoint& aSourcePoint) override;
 
   virtual void SetRenderTarget(CompositingRenderTarget* aSurface) override;
   virtual already_AddRefed<CompositingRenderTarget> GetCurrentRenderTarget()
       const override {
     return do_AddRef(mCurrentRT);
   }
+  virtual already_AddRefed<CompositingRenderTarget> GetWindowRenderTarget()
+      const override;
+
+  virtual bool ReadbackRenderTarget(CompositingRenderTarget* aSource,
+                                    AsyncReadbackBuffer* aDest) override;
+  virtual already_AddRefed<AsyncReadbackBuffer> CreateAsyncReadbackBuffer(
+      const gfx::IntSize& aSize) override;
+
+  virtual bool BlitRenderTarget(CompositingRenderTarget* aSource,
+                                const gfx::IntSize& aSourceSize,
+                                const gfx::IntSize& aDestSize) override;
 
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
 
   /**
    * Declare an offset to use when rendering layers. This will be ignored when
    * rendering to a target instead of the screen.
    */
   virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
@@ -201,16 +212,17 @@ class CompositorD3D11 : public Composito
   template <typename VertexType>
   void SetVertexBuffer(ID3D11Buffer* aBuffer);
 
   RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<ID3D11Device> mDevice;
   RefPtr<IDXGISwapChain> mSwapChain;
   RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
   RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
+  mutable RefPtr<CompositingRenderTargetD3D11> mWindowRTCopy;
 
   RefPtr<ID3D11Query> mQuery;
 
   RefPtr<DeviceAttachmentsD3D11> mAttachments;
   UniquePtr<DiagnosticsD3D11> mDiagnostics;
 
   LayoutDeviceIntSize mSize;
 
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -891,16 +891,12 @@ void VRSystemManagerExternal::PushState(
     WaitForMutex lock(mMutex);
     status = lock.GetStatus();
 #  endif  // defined(XP_WIN)
     if (status) {
       mExternalShmem->geckoGenerationA++;
       memcpy((void*)&(mExternalShmem->geckoState), (void*)aBrowserState,
              sizeof(VRBrowserState));
       mExternalShmem->geckoGenerationB++;
-      mExternalShmem->geckoGenerationA++;
-      memcpy((void*)&(mExternalShmem->geckoState), (void*)aBrowserState,
-             sizeof(VRBrowserState));
-      mExternalShmem->geckoGenerationB++;
     }
 #endif    // defined(MOZ_WIDGET_ANDROID)
   }
 }
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1121,17 +1121,17 @@ pub extern "C" fn wr_window_new(window_i
 
     let cached_programs = match program_cache {
         Some(program_cache) => Some(Rc::clone(&program_cache.rc_get())),
         None => None,
     };
 
     let opts = RendererOptions {
         enable_aa: true,
-        enable_subpixel_aa: true,
+        enable_subpixel_aa: cfg!(not(target_os = "android")),
         support_low_priority_transactions,
         recorder: recorder,
         blob_image_handler: Some(Box::new(Moz2dBlobImageHandler::new(workers.clone()))),
         workers: Some(workers.clone()),
         thread_listener: Some(Box::new(GeckoProfilerThreadListener::new())),
         size_of_op: Some(size_of_op),
         enclosing_size_of_op: Some(enclosing_size_of_op),
         cached_programs,
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2454,29 +2454,17 @@ bool BytecodeEmitter::emitInitializeInst
   FieldInitializers fieldInfo = this->fieldInitializers_;
   MOZ_ASSERT(fieldInfo.valid);
   size_t numFields = fieldInfo.numFieldInitializers;
 
   if (numFields == 0) {
     return true;
   }
 
-  PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
-                    PropOpEmitter::ObjKind::Other);
-  if (!poe.prepareForObj()) {
-    return false;
-  }
-
-  // This is guaranteed to run after super(), so we don't need TDZ checks.
-  if (!emitGetName(cx->names().dotThis)) {
-    //              [stack] THIS
-    return false;
-  }
-
-  if (!poe.emitGet(cx->names().dotInitializers)) {
+  if (!emitGetName(cx->names().dotInitializers)) {
     //              [stack] ARRAY
     return false;
   }
 
   for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
     if (fieldIndex < numFields - 1) {
       // We DUP to keep the array around (it is consumed in the bytecode below)
       // for next iterations of this loop, except for the last iteration, which
@@ -8063,59 +8051,50 @@ bool BytecodeEmitter::emitPropertyList(L
     }
   }
 
   if (numFields > 0) {
     // .initializers is a variable that stores an array of lambdas containing
     // code (the initializer) for each field. Upon an object's construction,
     // these lambdas will be called, defining the values.
 
-    PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment,
-                      PropOpEmitter::ObjKind::Other);
-    if (!poe.prepareForObj()) {
-      return false;
-    }
-
-    if (!emit1(JSOP_DUP)) {
-      //            [stack] CTOR? OBJ OBJ
-      return false;
-    }
-
-    if (!poe.prepareForRhs()) {
+    NameOpEmitter noe(this, cx->names().dotInitializers,
+                      NameOpEmitter::Kind::Initialize);
+    if (!noe.prepareForRhs()) {
       return false;
     }
 
     if (!emitUint32Operand(JSOP_NEWARRAY, numFields)) {
-      //            [stack] CTOR? OBJ OBJ ARRAY
+      //            [stack] CTOR? OBJ ARRAY
       return false;
     }
 
     size_t curFieldIndex = 0;
     for (ParseNode* propdef : obj->contents()) {
       if (propdef->is<ClassField>()) {
         FunctionNode* initializer = &propdef->as<ClassField>().initializer();
         if (initializer == nullptr) {
           continue;
         }
 
         if (!emitTree(initializer)) {
-          //        [stack] CTOR? OBJ OBJ ARRAY LAMBDA
+          //        [stack] CTOR? OBJ ARRAY LAMBDA
           return false;
         }
 
         if (!emitUint32Operand(JSOP_INITELEM_ARRAY, curFieldIndex)) {
-          //        [stack] CTOR? OBJ OBJ ARRAY
+          //        [stack] CTOR? OBJ ARRAY
           return false;
         }
 
         curFieldIndex++;
       }
     }
 
-    if (!poe.emitAssignment(cx->names().dotInitializers)) {
+    if (!noe.emitAssignment()) {
       //            [stack] CTOR? OBJ ARRAY
       return false;
     }
 
     if (!emit1(JSOP_POP)) {
       //            [stack] CTOR? OBJ
       return false;
     }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -93,20 +93,20 @@ ParseNode* ParseNode::appendOrCreateList
   if (!list) {
     return nullptr;
   }
 
   list->append(right);
   return list;
 }
 
-const ParseNodeArity js::frontend::ParseNodeKindArity[] = {
-#define ARITY(_name, type) type::arity(),
-    FOR_EACH_PARSE_NODE_KIND(ARITY)
-#undef ARITY
+const ParseNode::TypeCode ParseNode::typeCodeTable[] = {
+#define TYPE_CODE(_name, type) type::classTypeCode(),
+    FOR_EACH_PARSE_NODE_KIND(TYPE_CODE)
+#undef TYPE_CODE
 };
 
 #ifdef DEBUG
 
 static const char* const parseNodeNames[] = {
 #  define STRINGIFY(name, _type) #  name,
     FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
 #  undef STRINGIFY
@@ -133,61 +133,29 @@ void ParseNode::dump(GenericPrinter& out
 }
 
 void ParseNode::dump() {
   js::Fprinter out(stderr);
   dump(out);
 }
 
 void ParseNode::dump(GenericPrinter& out, int indent) {
-  switch (getArity()) {
-    case PN_NULLARY:
-      as<NullaryNode>().dump(out);
-      return;
-    case PN_UNARY:
-      as<UnaryNode>().dump(out, indent);
-      return;
-    case PN_BINARY:
-      as<BinaryNode>().dump(out, indent);
-      return;
-    case PN_TERNARY:
-      as<TernaryNode>().dump(out, indent);
-      return;
-    case PN_FUNCTION:
-      as<FunctionNode>().dump(out, indent);
-      return;
-    case PN_MODULE:
-      as<ModuleNode>().dump(out, indent);
-      return;
-    case PN_LIST:
-      as<ListNode>().dump(out, indent);
-      return;
-    case PN_NAME:
-      as<NameNode>().dump(out, indent);
-      return;
-    case PN_NUMBER:
-      as<NumericLiteral>().dump(out, indent);
-      return;
-    case PN_BIGINT:
-      as<BigIntLiteral>().dump(out, indent);
-      return;
-    case PN_REGEXP:
-      as<RegExpLiteral>().dump(out, indent);
-      return;
-    case PN_LOOP:
-      as<LoopControlStatement>().dump(out, indent);
-      return;
-    case PN_SCOPE:
-      as<LexicalScopeNode>().dump(out, indent);
-      return;
+  switch (getKind()) {
+#  define DUMP(K, T)                 \
+    case ParseNodeKind::K:           \
+      as<T>().dumpImpl(out, indent); \
+      break;
+    FOR_EACH_PARSE_NODE_KIND(DUMP)
+#  undef DUMP
+    default:
+      out.printf("#<BAD NODE %p, kind=%u>", (void*)this, unsigned(getKind()));
   }
-  out.printf("#<BAD NODE %p, kind=%u>", (void*)this, unsigned(getKind()));
 }
 
-void NullaryNode::dump(GenericPrinter& out) {
+void NullaryNode::dumpImpl(GenericPrinter& out, int indent) {
   switch (getKind()) {
     case ParseNodeKind::TrueExpr:
       out.put("#true");
       break;
     case ParseNodeKind::FalseExpr:
       out.put("#false");
       break;
     case ParseNodeKind::NullExpr:
@@ -197,56 +165,56 @@ void NullaryNode::dump(GenericPrinter& o
       out.put("#undefined");
       break;
 
     default:
       out.printf("(%s)", parseNodeNames[size_t(getKind())]);
   }
 }
 
-void NumericLiteral::dump(GenericPrinter& out, int indent) {
+void NumericLiteral::dumpImpl(GenericPrinter& out, int indent) {
   ToCStringBuf cbuf;
   const char* cstr = NumberToCString(nullptr, &cbuf, value());
   if (!IsFinite(value())) {
     out.put("#");
   }
   if (cstr) {
     out.printf("%s", cstr);
   } else {
     out.printf("%g", value());
   }
 }
 
-void BigIntLiteral::dump(GenericPrinter& out, int indent) {
+void BigIntLiteral::dumpImpl(GenericPrinter& out, int indent) {
   out.printf("(%s)", parseNodeNames[size_t(getKind())]);
 }
 
-void RegExpLiteral::dump(GenericPrinter& out, int indent) {
+void RegExpLiteral::dumpImpl(GenericPrinter& out, int indent) {
   out.printf("(%s)", parseNodeNames[size_t(getKind())]);
 }
 
-void LoopControlStatement::dump(GenericPrinter& out, int indent) {
+void LoopControlStatement::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s", name);
   if (label()) {
     out.printf(" ");
     label()->dumpCharsNoNewline(out);
   }
   out.printf(")");
 }
 
-void UnaryNode::dump(GenericPrinter& out, int indent) {
+void UnaryNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s ", name);
   indent += strlen(name) + 2;
   DumpParseTree(kid(), out, indent);
   out.printf(")");
 }
 
-void BinaryNode::dump(GenericPrinter& out, int indent) {
+void BinaryNode::dumpImpl(GenericPrinter& out, int indent) {
   if (isKind(ParseNodeKind::DotExpr)) {
     out.put("(.");
 
     DumpParseTree(right(), out, indent + 2);
 
     out.putChar(' ');
     if (as<PropertyAccess>().isSuper()) {
       out.put("super");
@@ -262,45 +230,45 @@ void BinaryNode::dump(GenericPrinter& ou
   out.printf("(%s ", name);
   indent += strlen(name) + 2;
   DumpParseTree(left(), out, indent);
   IndentNewLine(out, indent);
   DumpParseTree(right(), out, indent);
   out.printf(")");
 }
 
-void TernaryNode::dump(GenericPrinter& out, int indent) {
+void TernaryNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s ", name);
   indent += strlen(name) + 2;
   DumpParseTree(kid1(), out, indent);
   IndentNewLine(out, indent);
   DumpParseTree(kid2(), out, indent);
   IndentNewLine(out, indent);
   DumpParseTree(kid3(), out, indent);
   out.printf(")");
 }
 
-void FunctionNode::dump(GenericPrinter& out, int indent) {
+void FunctionNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s ", name);
   indent += strlen(name) + 2;
   DumpParseTree(body(), out, indent);
   out.printf(")");
 }
 
-void ModuleNode::dump(GenericPrinter& out, int indent) {
+void ModuleNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s ", name);
   indent += strlen(name) + 2;
   DumpParseTree(body(), out, indent);
   out.printf(")");
 }
 
-void ListNode::dump(GenericPrinter& out, int indent) {
+void ListNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s [", name);
   if (ParseNode* listHead = head()) {
     indent += strlen(name) + 3;
     DumpParseTree(listHead, out, indent);
     for (ParseNode* item : contentsFrom(listHead->pn_next)) {
       IndentNewLine(out, indent);
       DumpParseTree(item, out, indent);
@@ -322,17 +290,17 @@ static void DumpName(GenericPrinter& out
     } else if (c <= 255) {
       out.printf("\\x%02x", unsigned(c));
     } else {
       out.printf("\\u%04x", unsigned(c));
     }
   }
 }
 
-void NameNode::dump(GenericPrinter& out, int indent) {
+void NameNode::dumpImpl(GenericPrinter& out, int indent) {
   switch (getKind()) {
     case ParseNodeKind::StringExpr:
     case ParseNodeKind::TemplateStringExpr:
     case ParseNodeKind::ObjectPropertyName:
       atom()->dumpCharsNoNewline(out);
       return;
 
     case ParseNodeKind::Name:
@@ -375,17 +343,17 @@ void NameNode::dump(GenericPrinter& out,
       indent += strlen(name) + 2;
       DumpParseTree(initializer(), out, indent);
       out.printf(")");
       return;
     }
   }
 }
 
-void LexicalScopeNode::dump(GenericPrinter& out, int indent) {
+void LexicalScopeNode::dumpImpl(GenericPrinter& out, int indent) {
   const char* name = parseNodeNames[size_t(getKind())];
   out.printf("(%s [", name);
   int nameIndent = indent + strlen(name) + 3;
   if (!isEmptyScope()) {
     LexicalScope::Data* bindings = scopeBindings();
     for (uint32_t i = 0; i < bindings->length; i++) {
       JSAtom* name = bindings->trailingNames[i].name();
       JS::AutoCheckCannotGC nogc;
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -521,31 +521,16 @@ inline bool IsTypeofKind(ParseNodeKind k
  *   scopeBindings: scope bindings
  *   scopeBody: scope body
  * Generator (NullaryNode)
  * InitialYield (UnaryNode)
  *   kid: generator object
  * YieldExpr, YieldStarExpr, AwaitExpr (UnaryNode)
  *   kid: expr or null
  */
-enum ParseNodeArity {
-  PN_NULLARY,  /* 0 kids */
-  PN_UNARY,    /* one kid, plus a couple of scalars */
-  PN_BINARY,   /* two kids, plus a couple of scalars */
-  PN_TERNARY,  /* three kids */
-  PN_FUNCTION, /* function definition node */
-  PN_MODULE,   /* module node */
-  PN_LIST,     /* generic singly linked list */
-  PN_NAME,     /* name, label, string */
-  PN_NUMBER,   /* numeric literal */
-  PN_BIGINT,   /* BigInt literal */
-  PN_REGEXP,   /* regexp literal */
-  PN_LOOP,     /* loop control (break/continue) */
-  PN_SCOPE     /* lexical scope */
-};
 
 // FIXME: Remove `*Type` (bug 1489008)
 #define FOR_EACH_PARSENODE_SUBCLASS(MACRO)                                   \
   MACRO(BinaryNode, BinaryNodeType, asBinary)                                \
   MACRO(AssignmentNode, AssignmentNodeType, asAssignment)                    \
   MACRO(CaseClause, CaseClauseType, asCaseClause)                            \
   MACRO(ClassMethod, ClassMethodType, asClassMethod)                         \
   MACRO(ClassField, ClassFieldType, asClassField)                            \
@@ -590,19 +575,16 @@ enum ParseNodeArity {
                                                                              \
   MACRO(UnaryNode, UnaryNodeType, asUnary)                                   \
   MACRO(ThisLiteral, ThisLiteralType, asThisLiteral)
 
 #define DECLARE_CLASS(typeName, longTypeName, asMethodName) class typeName;
 FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
 #undef DECLARE_CLASS
 
-// ParseNodeKindArity[size_t(pnk)] is the arity of a ParseNode of kind pnk.
-extern const ParseNodeArity ParseNodeKindArity[];
-
 enum class FunctionSyntaxKind {
   // A non-arrow function expression.
   Expression,
 
   // A named function appearing as a Statement.
   Statement,
 
   Arrow,
@@ -666,20 +648,35 @@ class ParseNode {
   bool isOp(JSOp op) const { return getOp() == op; }
 
   ParseNodeKind getKind() const {
     MOZ_ASSERT(pn_type < ParseNodeKind::Limit);
     return pn_type;
   }
   bool isKind(ParseNodeKind kind) const { return getKind() == kind; }
 
-  ParseNodeArity getArity() const {
-    return ParseNodeKindArity[size_t(getKind())];
-  }
-  bool isArity(ParseNodeArity a) const { return getArity() == a; }
+ protected:
+  // Used to implement test() on a few ParseNodes efficiently.
+  // (This enum doesn't fully reflect the ParseNode class hierarchy,
+  // so don't use it for anything else.)
+  enum class TypeCode : uint8_t {
+    Nullary,
+    Unary,
+    Binary,
+    Ternary,
+    List,
+    Name,
+    Other
+  };
+
+  // typeCodeTable[size_t(pnk)] is the type code of a ParseNode of kind pnk.
+  static const TypeCode typeCodeTable[];
+
+ public:
+  TypeCode typeCode() const { return typeCodeTable[size_t(getKind())]; }
 
   bool isBinaryOperation() const {
     ParseNodeKind kind = getKind();
     return ParseNodeKind::BinOpFirst <= kind &&
            kind <= ParseNodeKind::BinOpLast;
   }
   inline bool isName(PropertyName* name) const;
 
@@ -777,27 +774,29 @@ class NullaryNode : public ParseNode {
     MOZ_ASSERT(is<NullaryNode>());
   }
 
   NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
       : ParseNode(kind, op, pos) {
     MOZ_ASSERT(is<NullaryNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_NULLARY); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::Nullary;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_NULLARY; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Nullary; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 };
 
 class NameNode : public ParseNode {
   JSAtom* atom_;         /* lexical name or label atom */
   ParseNode* initOrStmt; /* var initializer, argument default, or label
                             statement target */
 
@@ -809,32 +808,34 @@ class NameNode : public ParseNode {
   }
 
  public:
   NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
       : ParseNode(kind, op, pos), atom_(atom), initOrStmt(nullptr) {
     MOZ_ASSERT(is<NameNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_NAME); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::Name;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_NAME; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Name; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     if (initOrStmt) {
       if (!visitor.visit(initOrStmt)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   JSAtom* atom() const { return atom_; }
 
   PropertyName* name() const {
     MOZ_ASSERT(isKind(ParseNodeKind::Name));
     return atom()->asPropertyName();
   }
@@ -855,32 +856,34 @@ class UnaryNode : public ParseNode {
   bool prologue; /* directive prologue member */
 
  public:
   UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
       : ParseNode(kind, JSOP_NOP, pos), kid_(kid), prologue(false) {
     MOZ_ASSERT(is<UnaryNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_UNARY); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::Unary;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_UNARY; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Unary; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     if (kid_) {
       if (!visitor.visit(kid_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   ParseNode* kid() const { return kid_; }
 
   /* Return true if this node appears in a Directive Prologue. */
   bool isDirectivePrologueMember() const { return prologue; }
 
   void setIsDirectivePrologueMember() { prologue = true; }
@@ -925,19 +928,21 @@ class BinaryNode : public ParseNode {
 
   BinaryNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
       : ParseNode(kind, op, TokenPos::box(left->pn_pos, right->pn_pos)),
         left_(left),
         right_(right) {
     MOZ_ASSERT(is<BinaryNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_BINARY); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::Binary;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_BINARY; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Binary; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     if (left_) {
       if (!visitor.visit(left_)) {
         return false;
       }
     }
@@ -945,17 +950,17 @@ class BinaryNode : public ParseNode {
       if (!visitor.visit(right_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   ParseNode* left() const { return left_; }
 
   ParseNode* right() const { return right_; }
 
   // Methods used by FoldConstants.cpp.
   // callers are responsible for keeping the list consistent.
@@ -1020,19 +1025,21 @@ class TernaryNode : public ParseNode {
                              (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end)) {}
 
   TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2,
               ParseNode* kid3, const TokenPos& pos)
       : ParseNode(kind, JSOP_NOP, pos), kid1_(kid1), kid2_(kid2), kid3_(kid3) {
     MOZ_ASSERT(is<TernaryNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_TERNARY); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::Ternary;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_TERNARY; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Ternary; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     if (kid1_) {
       if (!visitor.visit(kid1_)) {
         return false;
       }
     }
@@ -1045,17 +1052,17 @@ class TernaryNode : public ParseNode {
       if (!visitor.visit(kid3_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   ParseNode* kid1() const { return kid1_; }
 
   ParseNode* kid2() const { return kid2_; }
 
   ParseNode* kid3() const { return kid3_; }
 
@@ -1129,19 +1136,21 @@ class ListNode : public ParseNode {
     if (kid->pn_pos.begin < pn_pos.begin) {
       pn_pos.begin = kid->pn_pos.begin;
     }
     pn_pos.end = kid->pn_pos.end;
 
     MOZ_ASSERT(is<ListNode>());
   }
 
-  static bool test(const ParseNode& node) { return node.isArity(PN_LIST); }
+  static bool test(const ParseNode& node) {
+    return node.typeCode() == TypeCode::List;
+  }
 
-  static constexpr ParseNodeArity arity() { return PN_LIST; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::List; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     ParseNode** listp = &head_;
     for (; *listp; listp = &(*listp)->pn_next) {
       // Don't use PN*& because we want to check if it changed, so we can use
       // ReplaceNode
       ParseNode* pn = *listp;
@@ -1152,17 +1161,17 @@ class ListNode : public ParseNode {
         ReplaceNode(listp, pn);
       }
     }
     unsafeReplaceTail(listp);
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   ParseNode* head() const { return head_; }
 
   ParseNode** tail() const { return tail_; }
 
   uint32_t count() const { return count_; }
 
@@ -1418,36 +1427,34 @@ class FunctionNode : public ParseNode {
         body_(nullptr),
         syntaxKind_(syntaxKind) {
     MOZ_ASSERT(!body_);
     MOZ_ASSERT(!funbox_);
     MOZ_ASSERT(is<FunctionNode>());
   }
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::Function);
-    MOZ_ASSERT_IF(match, node.isArity(PN_FUNCTION));
-    return match;
+    return node.isKind(ParseNodeKind::Function);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_FUNCTION; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     // Note: body is null for lazily-parsed functions.
     if (body_) {
       if (!visitor.visit(body_)) {
         return false;
       }
     }
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   FunctionBox* funbox() const { return funbox_; }
 
   ListNode* body() const { return body_ ? &body_->as<ListNode>() : nullptr; }
 
   void setFunbox(FunctionBox* funbox) { funbox_ = funbox; }
 
@@ -1466,30 +1473,28 @@ class ModuleNode : public ParseNode {
  public:
   explicit ModuleNode(const TokenPos& pos)
       : ParseNode(ParseNodeKind::Module, JSOP_NOP, pos), body_(nullptr) {
     MOZ_ASSERT(!body_);
     MOZ_ASSERT(is<ModuleNode>());
   }
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::Module);
-    MOZ_ASSERT_IF(match, node.isArity(PN_MODULE));
-    return match;
+    return node.isKind(ParseNodeKind::Module);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_MODULE; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return visitor.visit(body_);
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   ListNode* body() const { return &body_->as<ListNode>(); }
 
   void setBody(ListNode* body) { body_ = body; }
 };
 
 class NumericLiteral : public ParseNode {
@@ -1498,30 +1503,28 @@ class NumericLiteral : public ParseNode 
 
  public:
   NumericLiteral(double value, DecimalPoint decimalPoint, const TokenPos& pos)
       : ParseNode(ParseNodeKind::NumberExpr, JSOP_NOP, pos),
         value_(value),
         decimalPoint_(decimalPoint) {}
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::NumberExpr);
-    MOZ_ASSERT_IF(match, node.isArity(PN_NUMBER));
-    return match;
+    return node.isKind(ParseNodeKind::NumberExpr);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_NUMBER; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   double value() const { return value_; }
 
   DecimalPoint decimalPoint() const { return decimalPoint_; }
 
   void setValue(double v) { value_ = v; }
 
@@ -1531,60 +1534,56 @@ class NumericLiteral : public ParseNode 
 class BigIntLiteral : public ParseNode {
   BigIntBox* box_;
 
  public:
   BigIntLiteral(BigIntBox* bibox, const TokenPos& pos)
       : ParseNode(ParseNodeKind::BigIntExpr, JSOP_NOP, pos), box_(bibox) {}
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::BigIntExpr);
-    MOZ_ASSERT_IF(match, node.isArity(PN_BIGINT));
-    return match;
+    return node.isKind(ParseNodeKind::BigIntExpr);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_BIGINT; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return true;
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   BigIntBox* box() const { return box_; }
 };
 
 class LexicalScopeNode : public ParseNode {
   LexicalScope::Data* bindings;
   ParseNode* body;
 
  public:
   LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body)
       : ParseNode(ParseNodeKind::LexicalScope, JSOP_NOP, body->pn_pos),
         bindings(bindings),
         body(body) {}
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::LexicalScope);
-    MOZ_ASSERT_IF(match, node.isArity(PN_SCOPE));
-    return match;
+    return node.isKind(ParseNodeKind::LexicalScope);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_SCOPE; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return visitor.visit(body);
   }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   Handle<LexicalScope::Data*> scopeBindings() const {
     MOZ_ASSERT(!isEmptyScope());
     // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
     // the rest of the frontend also depends on.
     return Handle<LexicalScope::Data*>::fromMarkedLocation(&bindings);
   }
@@ -1602,20 +1601,17 @@ class LabeledStatement : public NameNode
       : NameNode(ParseNodeKind::LabelStmt, JSOP_NOP, label, stmt,
                  TokenPos(begin, stmt->pn_pos.end)) {}
 
   PropertyName* label() const { return atom()->asPropertyName(); }
 
   ParseNode* statement() const { return initializer(); }
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::LabelStmt);
-    MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
-    MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
-    return match;
+    return node.isKind(ParseNodeKind::LabelStmt);
   }
 };
 
 // Inside a switch statement, a CaseClause is a case-label and the subsequent
 // statements. The same node type is used for DefaultClauses. The only
 // difference is that their caseExpression() is null.
 class CaseClause : public BinaryNode {
  public:
@@ -1649,28 +1645,25 @@ class LoopControlStatement : public Pars
     MOZ_ASSERT(is<LoopControlStatement>());
   }
 
  public:
   /* Label associated with this break/continue statement, if any. */
   PropertyName* label() const { return label_; }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::BreakStmt) ||
-                 node.isKind(ParseNodeKind::ContinueStmt);
-    MOZ_ASSERT_IF(match, node.isArity(PN_LOOP));
-    MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
-    return match;
+    return node.isKind(ParseNodeKind::BreakStmt) ||
+           node.isKind(ParseNodeKind::ContinueStmt);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_LOOP; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return true;
   }
 };
 
 class BreakStatement : public LoopControlStatement {
@@ -1823,27 +1816,24 @@ class RegExpLiteral : public ParseNode {
  public:
   RegExpLiteral(ObjectBox* reobj, const TokenPos& pos)
       : ParseNode(ParseNodeKind::RegExpExpr, JSOP_REGEXP, pos),
         objbox_(reobj) {}
 
   ObjectBox* objbox() const { return objbox_; }
 
 #ifdef DEBUG
-  void dump(GenericPrinter& out, int indent);
+  void dumpImpl(GenericPrinter& out, int indent);
 #endif
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::RegExpExpr);
-    MOZ_ASSERT_IF(match, node.isArity(PN_REGEXP));
-    MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP));
-    return match;
+    return node.isKind(ParseNodeKind::RegExpExpr);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_REGEXP; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   template <typename Visitor>
   bool accept(Visitor& visitor) {
     return true;
   }
 };
 
 class PropertyAccess : public BinaryNode {
@@ -1964,22 +1954,20 @@ class ClassField : public BinaryNode {
   ClassField(ParseNode* name, ParseNode* initializer)
       : BinaryNode(ParseNodeKind::ClassField, JSOP_NOP,
                    initializer == nullptr
                        ? name->pn_pos
                        : TokenPos::box(name->pn_pos, initializer->pn_pos),
                    name, initializer) {}
 
   static bool test(const ParseNode& node) {
-    bool match = node.isKind(ParseNodeKind::ClassField);
-    MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
-    return match;
+    return node.isKind(ParseNodeKind::ClassField);
   }
 
-  static constexpr ParseNodeArity arity() { return PN_BINARY; }
+  static constexpr TypeCode classTypeCode() { return TypeCode::Other; }
 
   ParseNode& name() const { return *left(); }
 
   bool hasInitializer() const { return right() != nullptr; }
 
   FunctionNode& initializer() const { return right()->as<FunctionNode>(); }
 };
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1824,16 +1824,24 @@ GeneralParser<ParseHandler, Unit>::funct
                                                 FunctionSyntaxKind kind,
                                                 FunctionBodyType type) {
   MOZ_ASSERT(pc_->isFunctionBox());
 
 #ifdef DEBUG
   uint32_t startYieldOffset = pc_->lastYieldOffset;
 #endif
 
+  if (kind == FunctionSyntaxKind::ClassConstructor) {
+    // Don't do DerivedClassConstructor here, that gets marked after super()
+    // calls.
+    if (!noteUsedName(cx_->names().dotInitializers)) {
+      return null();
+    }
+  }
+
   Node body;
   if (type == StatementListBody) {
     bool inheritedStrict = pc_->sc()->strict();
     body = statementList(yieldHandling);
     if (!body) {
       return null();
     }
 
@@ -6759,22 +6767,20 @@ GeneralParser<ParseHandler, Unit>::class
   ParseContext::ClassStatement classStmt(pc_);
 
   RootedAtom propAtom(cx_);
 
   // A named class creates a new lexical scope with a const binding of the
   // class name for the "inner name".
   Maybe<ParseContext::Statement> innerScopeStmt;
   Maybe<ParseContext::Scope> innerScope;
-  if (className) {
-    innerScopeStmt.emplace(pc_, StatementKind::Block);
-    innerScope.emplace(this);
-    if (!innerScope->init(pc_)) {
-      return null();
-    }
+  innerScopeStmt.emplace(pc_, StatementKind::Block);
+  innerScope.emplace(this);
+  if (!innerScope->init(pc_)) {
+    return null();
   }
 
   // Because the binding definitions keep track of their blockId, we need to
   // create at least the inner binding later. Keep track of the name's position
   // in order to provide it for the nodes created later.
   TokenPos namePos = pos();
 
   Node classHeritage = null();
@@ -7003,40 +7009,45 @@ GeneralParser<ParseHandler, Unit>::class
       // Field initializers can be retrieved if the class and constructor are
       // being compiled at the same time, but we need to stash the field
       // information if the constructor is being compiled lazily.
       FieldInitializers fieldInfo(numFieldsWithInitializers);
       ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
     }
   }
 
+  NameNodeType innerName;
   Node nameNode = null();
-  Node membersOrBlock = classMembers;
   if (className) {
     // The inner name is immutable.
     if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) {
       return null();
     }
 
-    NameNodeType innerName = newName(className, namePos);
+    innerName = newName(className, namePos);
     if (!innerName) {
       return null();
     }
-
-    Node classBlock = finishLexicalScope(*innerScope, classMembers);
-    if (!classBlock) {
-      return null();
-    }
-
-    membersOrBlock = classBlock;
-
-    // Pop the inner scope.
-    innerScope.reset();
-    innerScopeStmt.reset();
-
+  }
+
+  if (!noteDeclaredName(cx_->names().dotInitializers, DeclarationKind::Const,
+                        namePos)) {
+    return null();
+  }
+
+  Node classBlock = finishLexicalScope(*innerScope, classMembers);
+  if (!classBlock) {
+    return null();
+  }
+
+  // Pop the inner scope.
+  innerScope.reset();
+  innerScopeStmt.reset();
+
+  if (className) {
     NameNodeType outerName = null();
     if (classContext == ClassStatement) {
       // The outer name is mutable.
       if (!noteDeclaredName(className, DeclarationKind::Class, namePos)) {
         return null();
       }
 
       outerName = newName(className, namePos);
@@ -7048,17 +7059,17 @@ GeneralParser<ParseHandler, Unit>::class
     nameNode = handler_.newClassNames(outerName, innerName, namePos);
     if (!nameNode) {
       return null();
     }
   }
 
   MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
 
-  return handler_.newClass(nameNode, classHeritage, membersOrBlock,
+  return handler_.newClass(nameNode, classHeritage, classBlock,
                            TokenPos(classStartOffset, classEndOffset));
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::FunctionNodeType
 GeneralParser<ParseHandler, Unit>::synthesizeConstructor(
     HandleAtom className, uint32_t classNameOffset) {
   FunctionSyntaxKind functionSyntaxKind = FunctionSyntaxKind::ClassConstructor;
@@ -7112,16 +7123,20 @@ GeneralParser<ParseHandler, Unit>::synth
     return null();
   }
 
   auto stmtList = handler_.newStatementList(synthesizedBodyPos);
   if (!stmtList) {
     return null();
   }
 
+  if (!noteUsedName(cx_->names().dotInitializers)) {
+    return null();
+  }
+
   if (!noteUsedName(cx_->names().dotThis)) {
     return null();
   }
 
   bool canSkipLazyClosedOverBindings = handler_.canSkipLazyClosedOverBindings();
   if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
     return null();
   }
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1279,22 +1279,47 @@ bool jit::EliminateDeadResumePointOperan
     }
   }
 
   return true;
 }
 
 // Test whether |def| would be needed if it had no uses.
 bool js::jit::DeadIfUnused(const MDefinition* def) {
-  return !def->isEffectful() &&
-         (!def->isGuard() ||
-          (def->block() == def->block()->graph().osrBlock() &&
-           !def->isImplicitlyUsed())) &&
-         !def->isGuardRangeBailouts() && !def->isControlInstruction() &&
-         (!def->isInstruction() || !def->toInstruction()->resumePoint());
+  // Effectful instructions of course cannot be removed.
+  if (def->isEffectful()) {
+    return false;
+  }
+
+  // Guard instructions by definition are live if they have no uses, however,
+  // in the OSR block we are able to eliminate these guards, as some are
+  // artificially created and superceeded by failible unboxes.
+  if (def->isGuard() && (def->block() != def->block()->graph().osrBlock() ||
+                         def->isImplicitlyUsed())) {
+    return false;
+  }
+
+  // Required to be preserved, as the type guard related to this instruction
+  // is part of the semantics of a transformation.
+  if (def->isGuardRangeBailouts()) {
+    return false;
+  }
+
+  // Control instructions have no uses, but also shouldn't be optimized out
+  if (def->isControlInstruction()) {
+    return false;
+  }
+
+  // Used when lowering to generate the corresponding snapshots and aggregate
+  // the list of recover instructions to be repeated.
+  if (def->isInstruction() && def->toInstruction()->resumePoint()) {
+    return false;
+  }
+
+  return true;
 }
 
 // Test whether |def| may be safely discarded, due to being dead or due to being
 // located in a basic block which has itself been marked for discarding.
 bool js::jit::IsDiscardable(const MDefinition* def) {
   return !def->hasUses() && (DeadIfUnused(def) || def->block()->isMarked());
 }
 
--- a/js/src/jsapi-tests/testHashTable.cpp
+++ b/js/src/jsapi-tests/testHashTable.cpp
@@ -518,11 +518,18 @@ BEGIN_TEST(testHashLazyStorage) {
   }
   CHECK(set.count() == 16);
   CHECK(set.capacity() == 32);
   set.clear();
   CHECK(set.capacity() == 32);
   set.compact();
   CHECK(set.capacity() == 0);
 
+  // Lowest length for which reserve() will fail.
+  static const uint32_t toobig = (1 << 29) + 1;
+  CHECK(!set.reserve(toobig));
+  CHECK(set.capacity() == 0);  // unchanged
+  CHECK(set.reserve(16));
+  CHECK(set.capacity() == 32);
+
   return true;
 }
 END_TEST(testHashLazyStorage)
--- a/js/src/rust/Cargo.toml
+++ b/js/src/rust/Cargo.toml
@@ -5,9 +5,8 @@ authors = ["The Spidermonkey developers"
 
 [lib]
 name = "jsrust"
 crate-type = ["staticlib"]
 path = "lib.rs"
 
 [dependencies]
 jsrust_shared = { path = "./shared" }
-mozilla-central-workspace-hack = { path = "../../../build/workspace-hack" }
--- a/js/src/rust/shared/Cargo.toml
+++ b/js/src/rust/shared/Cargo.toml
@@ -5,12 +5,13 @@ authors = ["The Spidermonkey developers"
 
 [lib]
 crate-type = ["rlib"]
 name = "jsrust_shared"
 path = "lib.rs"
 
 [dependencies]
 baldrdash = { path = "../../wasm/cranelift" }
+mozilla-central-workspace-hack = { path = "../../../../build/workspace-hack" }
 
 # Uncomment this to enable perf support in release mode.
 #[profile.release]
 #debug = true
--- a/layout/reftests/w3c-css/submitted/shapes1/float-retry-push-image.html
+++ b/layout/reftests/w3c-css/submitted/shapes1/float-retry-push-image.html
@@ -10,25 +10,45 @@
 <style>
   body {
     margin: 0px;
     line-height: 1;
   }
 
   #too-wide {
     display: inline-block;
-    height: 20px;
+    height: 21px;
     width: 250px;
     background: blue;
   }
 
   #shape {
     width: 100px;
     height: 100px;
     float: left;
+    /* We use a gradient, which is part of the CSS 'image' type.
+     * We set it up to create a hard diagonal edge from the bottom left to the
+     * top right of #shape, which slices through each pixel along the diagonal.
+     * Theoretically, this should place #too-wide at position 50,50 within
+     * #shape's 100x100 region, but on some devices, the gradient rasterization
+     * may leave pixel 50,49 unshaded enough that #too-wide is placed there
+     * instead. To account for that possible off-by-one rounding scenario,
+     * we set things up as follows:
+     *  - We make #too-wide 1px taller than the corresponding content in the
+     * reference case.
+     *  - We clip the outermost div using a 'clip-path' that only paints
+     * the region where the corresponding content is in the reference case.
+     *  - If the testcase renders properly, then #too-wide will have 1px of
+     * content clipped off of its top or bottom (depending on how the
+     * linear-gradient rasterization and rounding works out). Either way,
+     * it'll match the reference case.
+     */
     shape-outside: linear-gradient(135deg, black, black 50%, transparent 50%);
   }
+  .clip {
+    clip-path: inset(50px 0 30px 0px);
+  }
 </style>
 
-<div style="width: 300px; height: 100px;">
+<div style="width: 300px; height: 100px;" class="clip"> 
 <div id="shape"></div>
 <span id="too-wide"></span>
 <div>
--- a/layout/reftests/w3c-css/submitted/shapes1/float-retry-push-ref.html
+++ b/layout/reftests/w3c-css/submitted/shapes1/float-retry-push-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <meta charset="utf-8">
-<title>Test for retrying floats and pushing them partway down the float area</title>
+<title>Reference for retrying floats and pushing them partway down the float area</title>
 <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com">
 <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
 <style>
   body {
     margin: 0px;
     line-height: 1;
   }
 
--- a/layout/reftests/w3c-css/submitted/shapes1/reftest.list
+++ b/layout/reftests/w3c-css/submitted/shapes1/reftest.list
@@ -100,13 +100,12 @@ fuzzy(0-108,0-81) == shape-outside-ellip
 == shape-outside-polygon-022.html shape-outside-polygon-022-ref.html
 == shape-outside-polygon-023.html shape-outside-polygon-023-ref.html
 == shape-outside-polygon-024.html shape-outside-polygon-024-ref.html
 == shape-outside-polygon-025.html shape-outside-polygon-025-ref.html
 fuzzy(0-101,0-2263) == shape-outside-polygon-032.html shape-outside-polygon-032-ref.html
 
 # Tests of shape-outside layout behavior with too-wide inline elements
 == float-retry-push-circle.html float-retry-push-ref.html
-# The next test offsets a 250px wide element up to one pixel due to small offsets in gradient generation.
-fuzzy(0-255,0-500) == float-retry-push-image.html float-retry-push-ref.html
+== float-retry-push-image.html float-retry-push-ref.html
 == float-retry-push-inset.html float-retry-push-ref.html
 == float-retry-push-polygon.html float-retry-push-ref.html
 == float-should-push.html float-should-push-ref.html
--- a/layout/style/test/test_computed_style.html
+++ b/layout/style/test/test_computed_style.html
@@ -177,18 +177,17 @@ var noframe_container = document.getElem
     [ "calc(3px - 3px) 0", "0px 0px", "computed 0 calc with units horizontal" ],
     [ "0 calc(3px - 3px)", "0px 0px", "computed 0 calc with units vertical" ],
     [ "calc(0%) 0", "0% 0px", "0% calc horizontal"],
     [ "0 calc(0%)", "0px 0%", "0% calc vertical"],
     [ "calc(3px + 2% - 2%) 0", "calc(0% + 3px) 0px",
                       "computed 0% calc horizontal"],
     [ "0 calc(3px + 2% - 2%)", "0px calc(0% + 3px)",
                       "computed 0% calc vertical"],
-    [ "calc(3px - 5px) calc(6px - 9px)",
-      "calc(-2px) calc(-3px)", "negative pixel width" ],
+    [ "calc(3px - 5px) calc(6px - 9px)", "0px 0px", "negative pixel width" ],
     [ "", "auto", "initial value" ],
   ];
 
   var p = document.createElement("p");
   var cs = getComputedStyle(p, "");
   frame_container.appendChild(p);
 
   for (var i = 0; i < backgroundSizes.length; ++i) {
--- a/layout/tools/reftest/jar.mn
+++ b/layout/tools/reftest/jar.mn
@@ -1,10 +1,11 @@
 reftest.jar:
   content/moz-bool-pref.css (../../../layout/reftests/css-parsing/moz-bool-pref.css)
+  content/osx-theme (../../../toolkit/themes/osx/reftests/*)
   content/reftest.xul (reftest.xul)
 
   res/globals.jsm (globals.jsm)
   res/reftest-content.js (reftest-content.js)
   res/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
   res/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
   res/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
   res/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm)
--- a/layout/tools/reftest/manifest.jsm
+++ b/layout/tools/reftest/manifest.jsm
@@ -639,28 +639,30 @@ function ServeTestBase(aURL, depth) {
     return testbase;
 }
 
 function CreateUrls(test) {
     let secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
                     .getService(Ci.nsIScriptSecurityManager);
 
     let manifestURL = g.ioService.newURI(test.manifest);
-    let principal = secMan.createCodebasePrincipal(manifestURL, {});
 
     let testbase = manifestURL;
     if (test.runHttp)
         testbase = ServeTestBase(manifestURL, test.httpDepth)
 
     function FileToURI(file)
     {
         if (file === null)
             return file;
 
         var testURI = g.ioService.newURI(file, null, testbase);
+        let isChrome = testURI.scheme == "chrome";
+        let principal = isChrome ? secMan.getSystemPrincipal() :
+                                   secMan.createCodebasePrincipal(manifestURL, {});
         secMan.checkLoadURIWithPrincipal(principal, testURI,
                                          Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
         return testURI;
     }
 
     let files = [test.url1, test.url2];
     [test.url1, test.url2] = files.map(FileToURI);
 
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -15,16 +15,19 @@ const IO_SERVICE_CONTRACTID = "@mozilla.
 
 // "<!--CLEAR-->"
 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://reftest/AsyncSpellCheckTestHelper.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+// This will load chrome Custom Elements inside chrome documents:
+ChromeUtils.import("resource://gre/modules/CustomElementsListener.jsm", null);
+
 var gBrowserIsRemote;
 var gIsWebRenderEnabled;
 var gHaveCanvasSnapshot = false;
 // Plugin layers can be updated asynchronously, so to make sure that all
 // layer surfaces have the right content, we need to listen for explicit
 // "MozPaintWait" and "MozPaintWaitFinished" events that signal when it's OK
 // to take snapshots. We cannot take a snapshot while the number of
 // "MozPaintWait" events fired exceeds the number of "MozPaintWaitFinished"
--- a/layout/tools/reftest/reftest/__init__.py
+++ b/layout/tools/reftest/reftest/__init__.py
@@ -116,17 +116,17 @@ class ReftestManifest(object):
                     tests.append(items[i+1])
                     break
 
                 if item == '==' or item == '!=' or item == 'print':
                     tests.extend(items[i+1:i+3])
                     break
 
             for f in tests:
-                # We can't package about: or data: URIs.
+                # We can't package about:, data:, or chrome: URIs.
                 # Discarding data isn't correct for a parser. But retaining
                 # all data isn't currently a requirement.
                 if RE_PROTOCOL.match(f):
                     continue
 
                 test = os.path.normpath(os.path.join(mdir, urlprefix + f))
                 self.files.add(test)
                 self.dirs.add(os.path.dirname(test))
--- a/memory/replace/dmd/test/moz.build
+++ b/memory/replace/dmd/test/moz.build
@@ -4,17 +4,17 @@
 # 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/.
 
 GeckoSimplePrograms([
     'SmokeDMD',
 ], linkage=None)
 
 # See the comment at the top of SmokeDMD.cpp:RunTests().
-if CONFIG['OS_ARCH'] == 'WINNT':
+if CONFIG['CXX_TYPE'] == 'clang-cl':
     CXXFLAGS += ['-Og-']
 else:
     CXXFLAGS += ['-O0']
 
 DEFINES['MOZ_NO_MOZALLOC'] = True
 
 DisableStlWrapping()
 
--- a/mfbt/HashTable.h
+++ b/mfbt/HashTable.h
@@ -1564,16 +1564,19 @@ class HashTable : private AllocPolicy {
   static uint32_t bestCapacity(uint32_t aLen) {
     static_assert(
         (sMaxInit * sAlphaDenominator) / sAlphaDenominator == sMaxInit,
         "multiplication in numerator below could overflow");
     static_assert(
         sMaxInit * sAlphaDenominator <= UINT32_MAX - sMaxAlphaNumerator,
         "numerator calculation below could potentially overflow");
 
+    // Callers should ensure this is true.
+    MOZ_ASSERT(aLen <= sMaxInit);
+
     // Compute the smallest capacity allowing |aLen| elements to be
     // inserted without rehashing: ceil(aLen / max-alpha).  (Ceiling
     // integral division: <http://stackoverflow.com/a/2745086>.)
     uint32_t capacity = (aLen * sAlphaDenominator + sMaxAlphaNumerator - 1) /
                         sMaxAlphaNumerator;
     capacity = (capacity < sMinCapacity) ? sMinCapacity : RoundUpPow2(capacity);
 
     MOZ_ASSERT(capacity >= aLen);
@@ -1983,16 +1986,20 @@ class HashTable : private AllocPolicy {
     compact();
   }
 
   MOZ_MUST_USE bool reserve(uint32_t aLen) {
     if (aLen == 0) {
       return true;
     }
 
+    if (MOZ_UNLIKELY(aLen > sMaxInit)) {
+      return false;
+    }
+
     uint32_t bestCapacity = this->bestCapacity(aLen);
     if (bestCapacity <= capacity()) {
       return true;  // Capacity is already sufficient.
     }
 
     RebuildStatus status = changeTableSize(bestCapacity, ReportFailure);
     MOZ_ASSERT(status != NotOverloaded);
     return status != RehashFailed;
--- a/modules/libjar/moz.build
+++ b/modules/libjar/moz.build
@@ -11,24 +11,24 @@ if CONFIG['MOZ_ZIPWRITER']:
     DIRS += ['zipwriter']
 
 MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 XPIDL_SOURCES += [
     'nsIJARChannel.idl',
-    'nsIJARProtocolHandler.idl',
     'nsIJARURI.idl',
     'nsIZipReader.idl',
 ]
 
 XPIDL_MODULE = 'jar'
 
 EXPORTS += [
+    'nsJARProtocolHandler.h',
     'nsJARURI.h',
     'nsZipArchive.h',
     'zipstruct.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsJAR.cpp',
     'nsJARChannel.cpp',
deleted file mode 100644
--- a/modules/libjar/nsIJARFactory.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsIJARFactory_h__
-#define nsIJARFactory_h__
-
-#endif  // nsIJARFactory_h__
deleted file mode 100644
--- a/modules/libjar/nsIJARProtocolHandler.idl
+++ /dev/null
@@ -1,17 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIProtocolHandler.idl"
-
-interface nsIZipReaderCache;
-
-[scriptable, uuid(92c3b42c-98c4-11d3-8cd9-0060b0fc14a3)]
-interface nsIJARProtocolHandler : nsIProtocolHandler {
-
-    /**
-     * JARCache contains the collection of open jar files.
-     */
-    readonly attribute nsIZipReaderCache JARCache;
-};
--- a/modules/libjar/nsIJARURI.idl
+++ b/modules/libjar/nsIJARURI.idl
@@ -24,22 +24,16 @@ interface nsIJARURI : nsIURL {
      */
     readonly attribute nsIURI JARFile;
 
     /**
      * Returns the entry specified for this JAR URI (e.g., "ocean.html").  This
      * value may contain %-escaped byte sequences.
      */
     readonly attribute AUTF8String JAREntry;
-
-    /**
-     * Create a clone of the JAR URI with a new root URI (the URI for the
-     * actual JAR file).
-     */
-    nsIJARURI cloneWithJARFile(in nsIURI jarFile);
 };
 
 [builtinclass, uuid(d66df117-eda7-4324-b4e4-1f670ff6718e)]
 interface nsIJARURIMutator : nsISupports
 {
     /**
      * Will initalize a URI using the passed spec, baseURI and charset.
      */
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -22,16 +22,17 @@
 #include "nsIFileURL.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "nsITabChild.h"
 #include "private/pprio.h"
 #include "nsInputStreamPump.h"
 #include "nsThreadUtils.h"
+#include "nsJARProtocolHandler.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 // the entry for a directory will either be empty (in the case of the
 // top-level directory) or will end with a slash
@@ -944,22 +945,20 @@ nsJARChannel::EnsureCached(bool *aIsCach
 
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIProtocolHandler> handler;
   rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIJARProtocolHandler> jarHandler = do_QueryInterface(handler);
+  auto jarHandler = static_cast<nsJARProtocolHandler *>(handler.get());
   MOZ_ASSERT(jarHandler);
 
-  nsCOMPtr<nsIZipReaderCache> jarCache;
-  rv = jarHandler->GetJARCache(getter_AddRefs(jarCache));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsIZipReaderCache *jarCache = jarHandler->JarCache();
 
   rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader));
   if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
     return NS_OK;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aIsCached = true;
--- a/modules/libjar/nsJARProtocolHandler.cpp
+++ b/modules/libjar/nsJARProtocolHandler.cpp
@@ -42,38 +42,31 @@ nsresult nsJARProtocolHandler::Init() {
 }
 
 nsIMIMEService *nsJARProtocolHandler::MimeService() {
   if (!mMimeService) mMimeService = do_GetService("@mozilla.org/mime;1");
 
   return mMimeService.get();
 }
 
-NS_IMPL_ISUPPORTS(nsJARProtocolHandler, nsIJARProtocolHandler,
-                  nsIProtocolHandler, nsISupportsWeakReference)
+NS_IMPL_ISUPPORTS(nsJARProtocolHandler, nsIProtocolHandler,
+                  nsISupportsWeakReference)
 
 already_AddRefed<nsJARProtocolHandler> nsJARProtocolHandler::GetSingleton() {
   if (!gJarHandler) {
     gJarHandler = new nsJARProtocolHandler();
     if (NS_SUCCEEDED(gJarHandler->Init())) {
       ClearOnShutdown(&gJarHandler);
     } else {
       gJarHandler = nullptr;
     }
   }
   return do_AddRef(gJarHandler);
 }
 
-NS_IMETHODIMP
-nsJARProtocolHandler::GetJARCache(nsIZipReaderCache **result) {
-  *result = mJARCache;
-  NS_ADDREF(*result);
-  return NS_OK;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsIProtocolHandler methods:
 
 NS_IMETHODIMP
 nsJARProtocolHandler::GetScheme(nsACString &result) {
   result.AssignLiteral("jar");
   return NS_OK;
 }
--- a/modules/libjar/nsJARProtocolHandler.h
+++ b/modules/libjar/nsJARProtocolHandler.h
@@ -2,41 +2,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsJARProtocolHandler_h__
 #define nsJARProtocolHandler_h__
 
 #include "mozilla/StaticPtr.h"
-#include "nsIJARProtocolHandler.h"
 #include "nsIProtocolHandler.h"
 #include "nsIJARURI.h"
 #include "nsIZipReader.h"
 #include "nsIMIMEService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 
-class nsJARProtocolHandler final : public nsIJARProtocolHandler,
+class nsJARProtocolHandler final : public nsIProtocolHandler,
                                    public nsSupportsWeakReference {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIPROTOCOLHANDLER
-  NS_DECL_NSIJARPROTOCOLHANDLER
 
   // nsJARProtocolHandler methods:
   nsJARProtocolHandler();
 
   static already_AddRefed<nsJARProtocolHandler> GetSingleton();
 
   nsresult Init();
 
   // returns non addref'ed pointer.
   nsIMIMEService *MimeService();
-  nsIZipReaderCache *JarCache() { return mJARCache; }
+  nsIZipReaderCache *JarCache() const { return mJARCache; }
 
  protected:
   virtual ~nsJARProtocolHandler();
 
   nsCOMPtr<nsIZipReaderCache> mJARCache;
   nsCOMPtr<nsIMIMEService> mMimeService;
 };
 
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -479,23 +479,21 @@ nsJARURI::SchemeIs(const char *i_Scheme,
     return NS_OK;
   }
 
   *o_Equals = PL_strcasecmp("jar", i_Scheme) ? false : true;
   return NS_OK;
 }
 
 nsresult nsJARURI::Clone(nsIURI **result) {
-  nsresult rv;
+  RefPtr<nsJARURI> uri = new nsJARURI();
+  uri->mJARFile = mJARFile;
+  uri->mJAREntry = mJAREntry;
+  uri.forget(result);
 
-  nsCOMPtr<nsIJARURI> uri;
-  rv = CloneWithJARFileInternal(mJARFile, eHonorRef, getter_AddRefs(uri));
-  if (NS_FAILED(rv)) return rv;
-
-  uri.forget(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARURI::Resolve(const nsACString &relativePath, nsACString &result) {
   nsresult rv;
 
   nsCOMPtr<nsIIOService> ioServ(do_GetIOService(&rv));
@@ -703,58 +701,16 @@ nsJARURI::GetJAREntry(nsACString &entryP
   return NS_OK;
 }
 
 nsresult nsJARURI::SetJAREntry(const nsACString &entryPath) {
   return CreateEntryURL(entryPath, mCharsetHint.get(),
                         getter_AddRefs(mJAREntry));
 }
 
-NS_IMETHODIMP
-nsJARURI::CloneWithJARFile(nsIURI *jarFile, nsIJARURI **result) {
-  return CloneWithJARFileInternal(jarFile, eHonorRef, result);
-}
-
-nsresult nsJARURI::CloneWithJARFileInternal(
-    nsIURI *jarFile, nsJARURI::RefHandlingEnum refHandlingMode,
-    nsIJARURI **result) {
-  return CloneWithJARFileInternal(jarFile, refHandlingMode, EmptyCString(),
-                                  result);
-}
-
-nsresult nsJARURI::CloneWithJARFileInternal(
-    nsIURI *jarFile, nsJARURI::RefHandlingEnum refHandlingMode,
-    const nsACString &newRef, nsIJARURI **result) {
-  if (!jarFile) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsIURI> newJARFile = jarFile;
-  nsCOMPtr<nsIURI> newJAREntryURI;
-  if (refHandlingMode == eHonorRef) {
-    newJAREntryURI = mJAREntry;
-  } else if (refHandlingMode == eReplaceRef) {
-    rv = NS_GetURIWithNewRef(mJAREntry, newRef, getter_AddRefs(newJAREntryURI));
-  } else {
-    rv = NS_GetURIWithoutRef(mJAREntry, getter_AddRefs(newJAREntryURI));
-  }
-  if (NS_FAILED(rv)) return rv;
-
-  nsCOMPtr<nsIURL> newJAREntry(do_QueryInterface(newJAREntryURI));
-  NS_ASSERTION(newJAREntry, "This had better QI to nsIURL!");
-
-  RefPtr<nsJARURI> uri = new nsJARURI();
-  uri->mJARFile = newJARFile;
-  uri->mJAREntry = newJAREntry;
-  uri.forget(result);
-
-  return NS_OK;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMETHODIMP
 nsJARURI::GetInnerURI(nsIURI **aURI) {
   nsCOMPtr<nsIURI> uri = mJARFile;
   uri.forget(aURI);
   return NS_OK;
 }
--- a/modules/libjar/nsJARURI.h
+++ b/modules/libjar/nsJARURI.h
@@ -67,24 +67,16 @@ class nsJARURI final : public nsIJARURI,
   // enum used in a few places to specify how .ref attribute should be handled
   enum RefHandlingEnum { eIgnoreRef, eHonorRef, eReplaceRef };
 
   // Helper to share code between Equals methods.
   virtual nsresult EqualsInternal(nsIURI *other,
                                   RefHandlingEnum refHandlingMode,
                                   bool *result);
 
-  // Helpers to share code between Clone methods.
-  nsresult CloneWithJARFileInternal(nsIURI *jarFile,
-                                    RefHandlingEnum refHandlingMode,
-                                    nsIJARURI **result);
-  nsresult CloneWithJARFileInternal(nsIURI *jarFile,
-                                    RefHandlingEnum refHandlingMode,
-                                    const nsACString &newRef,
-                                    nsIJARURI **result);
   nsCOMPtr<nsIURI> mJARFile;
   // mJarEntry stored as a URL so that we can easily access things
   // like extensions, refs, etc.
   nsCOMPtr<nsIURL> mJAREntry;
   nsCString mCharsetHint;
 
  private:
   nsresult Clone(nsIURI **aURI);
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1936,16 +1936,23 @@ VARCACHE_PREF(
 
 // Anti-tracking user-interaction document interval
 VARCACHE_PREF(
   "privacy.userInteraction.document.interval",
    privacy_userInteraction_document_interval,
   uint32_t, 1800 // 30 minutes (in seconds)
 )
 
+// Maximum client-side cookie life-time cap
+VARCACHE_PREF(
+  "privacy.documentCookies.maxage",
+   privacy_documentCookies_maxage,
+  uint32_t, 0 // Disabled (in seconds, set to 0 to disable)
+)
+
 // Anti-fingerprinting, disabled by default
 VARCACHE_PREF(
   "privacy.resistFingerprinting",
    privacy_resistFingerprinting,
   RelaxedAtomicBool, false
 )
 
 VARCACHE_PREF(
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1762,20 +1762,31 @@ pref("network.http.request.max-attempts"
 
 // Prefs allowing granular control of referers
 // 0=don't send any, 1=send only on clicks, 2=send on image requests as well
 pref("network.http.sendRefererHeader",      2);
 // Set the default Referrer Policy; to be used unless overriden by the site
 // 0=no-referrer, 1=same-origin, 2=strict-origin-when-cross-origin,
 // 3=no-referrer-when-downgrade
 pref("network.http.referer.defaultPolicy", 3);
+// Set the default Referrer Policy applied to third-party trackers when the
+// default cookie policy is set to reject third-party trackers;
+// to be used unless overriden by the site;
+// values are identical to defaultPolicy above
+pref("network.http.referer.defaultPolicy.trackers", 3);
 // Set the Private Browsing Default Referrer Policy;
 // to be used unless overriden by the site;
 // values are identical to defaultPolicy above
 pref("network.http.referer.defaultPolicy.pbmode", 2);
+// Set the Private Browsing Default Referrer Policy applied to third-party
+// trackers when the default cookie policy is set to reject third-party
+// trackers;
+// to be used unless overriden by the site;
+// values are identical to defaultPolicy above
+pref("network.http.referer.defaultPolicy.trackers.pbmode", 2);
 // false=real referer, true=spoof referer (use target URI as referer)
 pref("network.http.referer.spoofSource", false);
 // false=allow onion referer, true=hide onion referer (use empty referer)
 pref("network.http.referer.hideOnionSource", false);
 // 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
 pref("network.http.referer.trimmingPolicy", 0);
 // 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
 pref("network.http.referer.XOriginTrimmingPolicy", 0);
@@ -3402,16 +3413,20 @@ pref("browser.tabs.remote.separateFileUr
 // This has been added in case breaking any window references between these
 // sorts of pages, which we have to do when we run them in the normal web
 // content process, causes compatibility issues.
 pref("browser.tabs.remote.allowLinkedWebInFileUriProcess", true);
 
 // Pref to control whether we use separate privileged content processes.
 pref("browser.tabs.remote.separatePrivilegedContentProcess", false);
 
+// When this pref is enabled top level loads with a mismatched
+// Cross-Origin-Opener-Policy header will be loaded in a separate process.
+pref("browser.tabs.remote.useCrossOriginOpenerPolicy", false);
+
 // Enable the use of display-lists for SVG hit-testing and painting.
 pref("svg.display-lists.hit-testing.enabled", true);
 pref("svg.display-lists.painting.enabled", true);
 
 // Is support for the new getBBox method from SVG 2 enabled?
 // See https://svgwg.org/svg2-draft/single-page.html#types-SVGBoundingBoxOptions
 pref("svg.new-getBBox.enabled", false);
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsNetUtil.h"
 
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
@@ -25,16 +26,17 @@
 #include "nsIAsyncStreamCopier.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsIAuthPromptAdapterFactory.h"
 #include "nsIBufferedStreams.h"
 #include "nsIChannelEventSink.h"
 #include "nsIContentSniffer.h"
 #include "mozilla/dom/Document.h"
+#include "nsICookieService.h"
 #include "nsIDownloader.h"
 #include "nsIFileProtocolHandler.h"
 #include "nsIFileStreams.h"
 #include "nsIFileURL.h"
 #include "nsIIDNService.h"
 #include "nsIInputStreamChannel.h"
 #include "nsIInputStreamPump.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -89,20 +91,24 @@
 using namespace mozilla;
 using namespace mozilla::net;
 using mozilla::dom::BlobURLProtocolHandler;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::PerformanceStorage;
 using mozilla::dom::ServiceWorkerDescriptor;
 
 #define DEFAULT_RP 3
+#define DEFAULT_TRACKER_RP 3
 #define DEFAULT_PRIVATE_RP 2
+#define DEFAULT_TRACKER_PRIVATE_RP 2
 
 static uint32_t sDefaultRp = DEFAULT_RP;
+static uint32_t sDefaultTrackerRp = DEFAULT_TRACKER_RP;
 static uint32_t defaultPrivateRp = DEFAULT_PRIVATE_RP;
+static uint32_t defaultTrackerPrivateRp = DEFAULT_TRACKER_PRIVATE_RP;
 
 already_AddRefed<nsIIOService> do_GetIOService(nsresult *error /* = 0 */) {
   nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
   if (error) *error = io ? NS_OK : NS_ERROR_FAILURE;
   return io.forget();
 }
 
 nsresult NS_NewLocalFileInputStream(nsIInputStream **result, nsIFile *file,
@@ -2728,33 +2734,63 @@ nsresult NS_CompareLoadInfoAndLoadContex
   MOZ_ASSERT(originAttrsLoadInfo.mPrivateBrowsingId ==
                  originAttrsLoadContext.mPrivateBrowsingId,
              "The value of mPrivateBrowsingId in the loadContext and in the "
              "loadInfo are not the same!");
 
   return NS_OK;
 }
 
-uint32_t NS_GetDefaultReferrerPolicy(bool privateBrowsing) {
+uint32_t NS_GetDefaultReferrerPolicy(nsIHttpChannel *aChannel, nsIURI *aURI,
+                                     bool privateBrowsing) {
   static bool preferencesInitialized = false;
 
   if (!preferencesInitialized) {
     mozilla::Preferences::AddUintVarCache(
         &sDefaultRp, "network.http.referer.defaultPolicy", DEFAULT_RP);
     mozilla::Preferences::AddUintVarCache(
+        &sDefaultTrackerRp, "network.http.referer.defaultPolicy.trackers",
+        DEFAULT_TRACKER_RP);
+    mozilla::Preferences::AddUintVarCache(
         &defaultPrivateRp, "network.http.referer.defaultPolicy.pbmode",
         DEFAULT_PRIVATE_RP);
+    mozilla::Preferences::AddUintVarCache(
+        &defaultTrackerPrivateRp,
+        "network.http.referer.defaultPolicy.trackers.pbmode",
+        DEFAULT_TRACKER_PRIVATE_RP);
     preferencesInitialized = true;
   }
 
+  bool thirdPartyTrackerIsolated = false;
+  if (StaticPrefs::network_cookie_cookieBehavior() ==
+      nsICookieService::BEHAVIOR_REJECT_TRACKER) {
+    if (aChannel && aURI) {
+      uint32_t rejectedReason = 0;
+      thirdPartyTrackerIsolated =
+          !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+              aChannel, aURI, &rejectedReason);
+      // Here we intentionally do not notify about the rejection reason, if any
+      // in order to avoid this check to have any visible side-effects (e.g. a
+      // web console report.)
+    }
+  }
+
   uint32_t defaultToUse;
-  if (privateBrowsing) {
-    defaultToUse = defaultPrivateRp;
+  if (thirdPartyTrackerIsolated) {
+    if (privateBrowsing) {
+      defaultToUse = defaultTrackerPrivateRp;
+    } else {
+      defaultToUse = sDefaultTrackerRp;
+    }
   } else {
-    defaultToUse = sDefaultRp;
+    if (privateBrowsing) {
+      defaultToUse = defaultPrivateRp;
+    } else {
+      defaultToUse = sDefaultRp;
+    }
   }
 
   switch (defaultToUse) {
     case 0:
       return nsIHttpChannel::REFERRER_POLICY_NO_REFERRER;
     case 1:
       return nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN;
     case 2:
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -31,16 +31,17 @@ class nsIAsyncStreamCopier;
 class nsIAuthPrompt;
 class nsIAuthPrompt2;
 class nsIChannel;
 class nsIChannelPolicy;
 class nsIDownloadObserver;
 class nsIEventTarget;
 class nsIFileProtocolHandler;
 class nsIFileStream;
+class nsIHttpChannel;
 class nsIInputStream;
 class nsIInputStreamPump;
 class nsIInterfaceRequestor;
 class nsIOutputStream;
 class nsIParentChannel;
 class nsIPersistentProperties;
 class nsIProxyInfo;
 class nsIRequestObserver;
@@ -895,19 +896,25 @@ nsresult NS_ShouldSecureUpgrade(
 nsresult NS_GetSecureUpgradedURI(nsIURI *aURI, nsIURI **aUpgradedURI);
 
 nsresult NS_CompareLoadInfoAndLoadContext(nsIChannel *aChannel);
 
 /**
  * Return default referrer policy which is controlled by user
  * prefs:
  * network.http.referer.defaultPolicy for regular mode
+ * network.http.referer.defaultPolicy.trackers for third-party trackers
+ * in regular mode
  * network.http.referer.defaultPolicy.pbmode for private mode
+ * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers
+ * in private mode
  */
-uint32_t NS_GetDefaultReferrerPolicy(bool privateBrowsing = false);
+uint32_t NS_GetDefaultReferrerPolicy(nsIHttpChannel *aChannel = nullptr,
+                                     nsIURI *aURI = nullptr,
+                                     bool privateBrowsing = false);
 
 namespace mozilla {
 namespace net {
 
 const static uint64_t kJS_MAX_SAFE_UINTEGER = +9007199254740991ULL;
 const static int64_t kJS_MIN_SAFE_INTEGER = -9007199254740991LL;
 const static int64_t kJS_MAX_SAFE_INTEGER = +9007199254740991LL;
 
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -3270,18 +3270,19 @@ bool nsCookieService::CanSetCookie(nsIUR
                                 (isHTTPS ? 0x02 : 0x00) |
                                 (aCookieAttributes.isSecure ? 0x01 : 0x00));
     }
   }
 
   int64_t currentTimeInUsec = PR_Now();
 
   // calculate expiry time of cookie.
-  aCookieAttributes.isSession = GetExpiry(aCookieAttributes, aServerTime,
-                                          currentTimeInUsec / PR_USEC_PER_SEC);
+  aCookieAttributes.isSession =
+      GetExpiry(aCookieAttributes, aServerTime,
+                currentTimeInUsec / PR_USEC_PER_SEC, aFromHttp);
   if (aStatus == STATUS_ACCEPT_SESSION) {
     // force lifetime to session. note that the expiration time, if set above,
     // will still apply.
     aCookieAttributes.isSession = true;
   }
 
   // reject cookie if it's over the size limit, per RFC2109
   if ((aCookieAttributes.name.Length() + aCookieAttributes.value.Length()) >
@@ -4230,17 +4231,23 @@ bool nsCookieService::CheckPrefixes(nsCo
       return false;
     }
   }
 
   return true;
 }
 
 bool nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes,
-                                int64_t aServerTime, int64_t aCurrentTime) {
+                                int64_t aServerTime, int64_t aCurrentTime,
+                                bool aFromHttp) {
+  // maxageCap is in seconds.
+  // Disabled for HTTP cookies.
+  int64_t maxageCap =
+      aFromHttp ? 0 : StaticPrefs::privacy_documentCookies_maxage();
+
   /* Determine when the cookie should expire. This is done by taking the
    * difference between the server time and the time the server wants the cookie
    * to expire, and adding that difference to the client time. This localizes
    * the client time regardless of whether or not the TZ environment variable
    * was set on the client.
    *
    * Note: We need to consider accounting for network lag here, per RFC.
    */
@@ -4253,36 +4260,47 @@ bool nsCookieService::GetExpiry(nsCookie
 
     // default to session cookie if the conversion failed
     if (numInts != 1) {
       return true;
     }
 
     // if this addition overflows, expiryTime will be less than currentTime
     // and the cookie will be expired - that's okay.
-    aCookieAttributes.expiryTime = aCurrentTime + maxage;
+    if (maxageCap) {
+      aCookieAttributes.expiryTime = aCurrentTime + std::min(maxage, maxageCap);
+    } else {
+      aCookieAttributes.expiryTime = aCurrentTime + maxage;
+    }
 
     // check for expires attribute
   } else if (!aCookieAttributes.expires.IsEmpty()) {
     PRTime expires;
 
     // parse expiry time
     if (PR_ParseTimeString(aCookieAttributes.expires.get(), true, &expires) !=
         PR_SUCCESS) {
       return true;
     }
 
     // If set-cookie used absolute time to set expiration, and it can't use
     // client time to set expiration.
     // Because if current time be set in the future, but the cookie expire
     // time be set less than current time and more than server time.
     // The cookie item have to be used to the expired cookie.
-    aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
-
-    // default to session cookie if no attributes found
+    if (maxageCap) {
+      aCookieAttributes.expiryTime = std::min(
+          expires / int64_t(PR_USEC_PER_SEC), aCurrentTime + maxageCap);
+    } else {
+      aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
+    }
+
+    // default to session cookie if no attributes found.  Here we don't need to
+    // enforce the maxage cap, because session cookies are short-lived by
+    // definition.
   } else {
     return true;
   }
 
   return false;
 }
 
 /******************************************************************************
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -339,17 +339,17 @@ class nsCookieService final : public nsI
   static bool ParseAttributes(nsDependentCString &aCookieHeader,
                               nsCookieAttributes &aCookie);
   bool RequireThirdPartyCheck();
   static bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI,
                           const nsCString &aBaseDomain, bool aRequireHostMatch);
   static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
   static bool CheckPrefixes(nsCookieAttributes &aCookie, bool aSecureRequest);
   static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime,
-                        int64_t aCurrentTime);
+                        int64_t aCurrentTime, bool aFromHttp);
   void RemoveAllFromMemory();
   already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec);
   bool FindCookie(const nsCookieKey &aKey, const nsCString &aHost,
                   const nsCString &aName, const nsCString &aPath,
                   nsListIter &aIter);
   bool FindSecureCookie(const nsCookieKey &aKey, nsCookie *aCookie);
   void FindStaleCookies(nsCookieEntry *aEntry, int64_t aCurrentTime,
                         bool aIsSecure, nsTArray<nsListIter> &aOutput,
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -215,17 +215,17 @@ union OptionalCorsPreflightArgs
 struct HttpChannelOpenArgs
 {
   URIParams                   uri;
   // - TODO: bug 571161: unclear if any HTTP channel clients ever
   // set originalURI != uri (about:credits?); also not clear if
   // chrome channel would ever need to know.  Get rid of next arg?
   OptionalURIParams           original;
   OptionalURIParams           doc;
-  OptionalURIParams           referrer;
+  OptionalURIParams           originalReferrer;
   uint32_t                    referrerPolicy;
   OptionalURIParams           apiRedirectTo;
   OptionalURIParams           topWindowURI;
   nsIPrincipal                topWindowPrincipal;
   uint32_t                    loadFlags;
   RequestHeaderTuples         requestHeaders;
   nsCString                   requestMethod;
   IPCStream?                  uploadStream;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1559,18 +1559,18 @@ HttpBaseChannel::GetReferrer(nsIURI** re
   *referrer = mReferrer;
   NS_IF_ADDREF(*referrer);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetReferrer(nsIURI* referrer) {
   bool isPrivate = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-  return SetReferrerWithPolicy(referrer,
-                               NS_GetDefaultReferrerPolicy(isPrivate));
+  return SetReferrerWithPolicy(
+      referrer, NS_GetDefaultReferrerPolicy(this, mURI, isPrivate));
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetReferrerPolicy(uint32_t* referrerPolicy) {
   NS_ENSURE_ARG_POINTER(referrerPolicy);
   *referrerPolicy = mReferrerPolicy;
   return NS_OK;
 }
@@ -1606,28 +1606,30 @@ bool HttpBaseChannel::IsCrossOriginWithR
   return true;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetReferrerWithPolicy(nsIURI* referrer,
                                        uint32_t referrerPolicy) {
   ENSURE_CALLED_BEFORE_CONNECT();
 
+  nsIURI* originalReferrer = referrer;
+
   mReferrerPolicy = referrerPolicy;
 
   // clear existing referrer, if any
   mReferrer = nullptr;
   nsresult rv = mRequestHead.ClearHeader(nsHttp::Referer);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mReferrerPolicy == REFERRER_POLICY_UNSET) {
     bool isPrivate = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-    mReferrerPolicy = NS_GetDefaultReferrerPolicy(isPrivate);
+    mReferrerPolicy = NS_GetDefaultReferrerPolicy(this, mURI, isPrivate);
   }
 
   if (!referrer) {
     return NS_OK;
   }
 
   // Don't send referrer at all when the meta referrer setting is "no-referrer"
   if (mReferrerPolicy == REFERRER_POLICY_NO_REFERRER) {
@@ -1920,16 +1922,17 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     rv = clone->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
   }
 
   // finally, remember the referrer URI and set the Referer header.
   rv = SetRequestHeader(NS_LITERAL_CSTRING("Referer"), spec, false);
   if (NS_FAILED(rv)) return rv;
 
+  mOriginalReferrer = originalReferrer;
   mReferrer = clone;
   return NS_OK;
 }
 
 // Return the channel's proxy URI, or if it doesn't exist, the
 // channel's main URI.
 NS_IMETHODIMP
 HttpBaseChannel::GetProxyURI(nsIURI** aOut) {
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -437,16 +437,17 @@ class HttpBaseChannel : public nsHashPro
 
   MOZ_MUST_USE nsresult SetReferrerWithPolicyInternal(nsIURI *referrer,
                                                       uint32_t referrerPolicy) {
     nsAutoCString spec;
     nsresult rv = referrer->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) {
       return rv;
     }
+    mOriginalReferrer = referrer;
     mReferrer = referrer;
     mReferrerPolicy = referrerPolicy;
     rv = mRequestHead.SetHeader(nsHttp::Referer, spec);
     return rv;
   }
 
   MOZ_MUST_USE nsresult SetTopWindowURI(nsIURI *aTopWindowURI) {
     mTopWindowURI = aTopWindowURI;
@@ -540,16 +541,21 @@ class HttpBaseChannel : public nsHashPro
   // all the references need to be proxy released on main thread.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsILoadInfo> mLoadInfo;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsIProgressEventSink> mProgressSink;
+  // When the referrer is set before calling AsyncOpen, we remember the referrer
+  // argument passed in case we need to readjust the referrer header being used
+  // depending on the active cookie policy once we have determined whether the
+  // channel is a tracking third-party resource or not.
+  nsCOMPtr<nsIURI> mOriginalReferrer;
   nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
   nsCOMPtr<nsIURI> mAPIRedirectToURI;
   nsCOMPtr<nsIURI> mProxyURI;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIURI> mTopWindowURI;
   nsCOMPtr<nsIPrincipal> mTopWindowPrincipal;
   nsCOMPtr<nsIStreamListener> mListener;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2649,17 +2649,17 @@ nsresult HttpChannelChild::ContinueAsync
   SetTopLevelContentWindowId(contentWindowId);
 
   HttpChannelOpenArgs openArgs;
   // No access to HttpChannelOpenArgs members, but they each have a
   // function with the struct name that returns a ref.
   SerializeURI(mURI, openArgs.uri());
   SerializeURI(mOriginalURI, openArgs.original());
   SerializeURI(mDocumentURI, openArgs.doc());
-  SerializeURI(mReferrer, openArgs.referrer());
+  SerializeURI(mOriginalReferrer, openArgs.originalReferrer());
   openArgs.referrerPolicy() = mReferrerPolicy;
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
   mRequestHead.Method(openArgs.requestMethod());
   openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes;
 
   AutoIPCStream autoStream(openArgs.uploadStream());
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -128,21 +128,21 @@ void HttpChannelParent::ActorDestroy(Act
 
 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
   LOG(("HttpChannelParent::Init [this=%p]\n", this));
   AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
   switch (aArgs.type()) {
     case HttpChannelCreationArgs::THttpChannelOpenArgs: {
       const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
       return DoAsyncOpen(
-          a.uri(), a.original(), a.doc(), a.referrer(), a.referrerPolicy(),
-          a.apiRedirectTo(), a.topWindowURI(), a.topWindowPrincipal(),
-          a.loadFlags(), a.requestHeaders(), a.requestMethod(),
-          a.uploadStream(), a.uploadStreamHasHeaders(), a.priority(),
-          a.classOfService(), a.redirectionLimit(), a.allowSTS(),
+          a.uri(), a.original(), a.doc(), a.originalReferrer(),
+          a.referrerPolicy(), a.apiRedirectTo(), a.topWindowURI(),
+          a.topWindowPrincipal(), a.loadFlags(), a.requestHeaders(),
+          a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
+          a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
           a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
           a.chooseApplicationCache(), a.appCacheClientID(), a.allowSpdy(),
           a.allowAltSvc(), a.beConservative(), a.tlsFlags(), a.loadInfo(),
           a.synthesizedResponseHead(), a.synthesizedSecurityInfoSerialization(),
           a.cacheKey(), a.requestContextID(), a.preflightArgs(),
           a.initialRwin(), a.blockAuthPrompt(),
           a.suspendAfterSynthesizeResponse(), a.allowStaleCacheContent(),
           a.contentTypeHint(), a.corsMode(), a.redirectMode(), a.channelId(),
@@ -378,17 +378,18 @@ void HttpChannelParent::InvokeAsyncOpen(
   rv = mChannel->AsyncOpen(mParentListener);
   if (NS_FAILED(rv)) {
     AsyncOpenFailed(rv);
   }
 }
 
 bool HttpChannelParent::DoAsyncOpen(
     const URIParams& aURI, const OptionalURIParams& aOriginalURI,
-    const OptionalURIParams& aDocURI, const OptionalURIParams& aReferrerURI,
+    const OptionalURIParams& aDocURI,
+    const OptionalURIParams& aOriginalReferrerURI,
     const uint32_t& aReferrerPolicy, const OptionalURIParams& aAPIRedirectToURI,
     const OptionalURIParams& aTopWindowURI, nsIPrincipal* aTopWindowPrincipal,
     const uint32_t& aLoadFlags, const RequestHeaderTuples& requestHeaders,
     const nsCString& requestMethod, const Maybe<IPCStream>& uploadStream,
     const bool& uploadStreamHasHeaders, const int16_t& priority,
     const uint32_t& classOfService, const uint8_t& redirectionLimit,
     const bool& allowSTS, const uint32_t& thirdPartyFlags,
     const bool& doResumeAt, const uint64_t& startPos, const nsCString& entityID,
@@ -419,17 +420,17 @@ bool HttpChannelParent::DoAsyncOpen(
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
   nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
-  nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
+  nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aOriginalReferrerURI);
   nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
   nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
 
   LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64
        " topwinid=%" PRIx64 "]\n",
        this, uri->GetSpecOrDefault().get(), aChannelId,
        aTopLevelOuterContentWindowId));
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -134,17 +134,18 @@ class HttpChannelParent final : public n
  protected:
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during redirects.
   MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                    const bool& shouldIntercept);
 
   MOZ_MUST_USE bool DoAsyncOpen(
       const URIParams& uri, const OptionalURIParams& originalUri,
-      const OptionalURIParams& docUri, const OptionalURIParams& referrerUri,
+      const OptionalURIParams& docUri,
+      const OptionalURIParams& originalReferrerUri,
       const uint32_t& referrerPolicy,
       const OptionalURIParams& internalRedirectUri,
       const OptionalURIParams& topWindowUri, nsIPrincipal* aTopWindowPrincipal,
       const uint32_t& loadFlags, const RequestHeaderTuples& requestHeaders,
       const nsCString& requestMethod, const Maybe<IPCStream>& uploadStream,
       const bool& uploadStreamHasHeaders, const int16_t& priority,
       const uint32_t& classOfService, const uint8_t& redirectionLimit,
       const bool& allowSTS, const uint32_t& thirdPartyFlags,
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6626,16 +6626,18 @@ nsresult nsHttpChannel::BeginConnectActu
   if (mChannelClassifierCancellationPending) {
     LOG(
         ("Waiting for safe-browsing protection cancellation in "
          "BeginConnectActual [this=%p]\n",
          this));
     return NS_OK;
   }
 
+  ReEvaluateReferrerAfterTrackingStatusIsKnown();
+
   MaybeStartDNSPrefetch();
 
   nsresult rv = ContinueBeginConnectWithResult();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Start nsChannelClassifier to catch phishing and malware URIs.
@@ -9991,10 +9993,27 @@ nsresult nsHttpChannel::RedirectToInterc
     AutoRedirectVetoNotifier notifier(this);
 
     PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
   }
 
   return rv;
 }
 
+void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() {
+  if (StaticPrefs::network_cookie_cookieBehavior() ==
+      nsICookieService::BEHAVIOR_REJECT_TRACKER) {
+    // If our referrer has been set before, and our referrer policy is equal to
+    // the default policy if we thought the channel wasn't a third-party
+    // tracking channel, we may need to set our referrer with referrer policy
+    // once again to ensure our defaults properly take effect now.
+    bool isPrivate =
+        mLoadInfo && mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
+    if (mOriginalReferrer &&
+        mReferrerPolicy ==
+            NS_GetDefaultReferrerPolicy(nullptr, nullptr, isPrivate)) {
+      SetReferrer(mOriginalReferrer);
+    }
+  }
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -565,16 +565,21 @@ class nsHttpChannel final : public HttpB
   // nsChannelClassifier will be invoked twice in InitLocalBlockList() and
   // BeginConnectActual(), so save the nsChannelClassifier here to keep the
   // state of whether tracking protection is enabled or not.
   RefPtr<nsChannelClassifier> mChannelClassifier;
 
   // Proxy release all members above on main thread.
   void ReleaseMainThreadOnlyReferences();
 
+  // Called after the channel is made aware of its tracking status in order
+  // to readjust the referrer if needed according to the referrer default
+  // policy preferences.
+  void ReEvaluateReferrerAfterTrackingStatusIsKnown();
+
  private:
   nsCOMPtr<nsICancelable> mProxyRequest;
 
   RefPtr<nsInputStreamPump> mTransactionPump;
   RefPtr<nsHttpTransaction> mTransaction;
 
   uint64_t mLogicalOffset;
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/file_documentcookie_maxage_chromescript.js
@@ -0,0 +1,41 @@
+function getCookieService() {
+  return Cc["@mozilla.org/cookiemanager;1"]
+           .getService(Ci.nsICookieManager);
+}
+
+function getCookies(cs) {
+  let cookies = [];
+  for (let cookie of cs.enumerator) {
+    cookies.push({
+      host: cookie.host,
+      path: cookie.path,
+      name: cookie.name,
+      value: cookie.value,
+      expires: cookie.expires,
+    });
+  }
+  return cookies;
+}
+
+function removeAllCookies(cs) {
+  cs.removeAll();
+}
+
+addMessageListener("init", _ => {
+  let cs = getCookieService();
+  removeAllCookies(cs);
+  sendAsyncMessage("init:return");
+});
+
+addMessageListener("getCookies", _ => {
+  let cs = getCookieService();
+  let cookies = getCookies(cs);
+  removeAllCookies(cs);
+  sendAsyncMessage("getCookies:return", { cookies });
+});
+
+addMessageListener("shutdown", _ => {
+  let cs = getCookieService();
+  removeAllCookies(cs);
+  sendAsyncMessage("shutdown:return");
+});
--- a/netwerk/test/mochitests/mochitest.ini
+++ b/netwerk/test/mochitests/mochitest.ini
@@ -3,30 +3,33 @@ support-files =
   method.sjs
   partial_content.sjs
   rel_preconnect.sjs
   user_agent.sjs
   user_agent_update.sjs
   set_cookie_xhr.sjs
   reset_cookie_xhr.sjs
   web_packaged_app.sjs
+  file_documentcookie_maxage_chromescript.js
   file_loadinfo_redirectchain.sjs
   file_1331680.js
   file_1503201.sjs
   file_iframe_allow_scripts.html
   file_iframe_allow_same_origin.html
   redirect_idn.html^headers^
   redirect_idn.html
   empty.html
   redirect.sjs
   origin_header.sjs
   origin_header_form_post.html
   origin_header_form_post_xorigin.html
+  subResources.sjs
 
 [test_arraybufferinputstream.html]
+[test_documentcookies_maxage.html]
 [test_idn_redirect.html]
 [test_loadinfo_redirectchain.html]
 [test_partially_cached_content.html]
 [test_rel_preconnect.html]
 [test_redirect_ref.html]
 [test_uri_scheme.html]
 [test_user_agent_overrides.html]
 [test_user_agent_updates.html]
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/subResources.sjs
@@ -0,0 +1,47 @@
+const kTwoDays = 2 * 24 * 60 * 60;
+const kInTwoDays = (new Date().getTime() + kTwoDays * 1000);
+
+function getDateInTwoDays()
+{
+  let date2 = new Date(kInTwoDays);
+  let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+  let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+                "Nov", "Dec"];
+  let day = date2.getUTCDate();
+  if (day < 10) {
+    day = "0" + day;
+  }
+  let month = months[date2.getUTCMonth()];
+  let year = date2.getUTCFullYear();
+  let hour = date2.getUTCHours();
+  if (hour < 10) {
+    hour = "0" + hour;
+  }
+  let minute = date2.getUTCMinutes();
+  if (minute < 10) {
+    minute = "0" + minute;
+  }
+  let second = date2.getUTCSeconds();
+  if (second < 10) {
+    second = "0" + second;
+  }
+  return days[date2.getUTCDay()] + ", " + day + "-" + month + "-" +
+         year + " " + hour + ":" + minute + ":" + second + " GMT";
+}
+
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+  let suffix = " path=/; domain:.mochi.test";
+
+  if (aRequest.queryString.includes("3")) {
+    aResponse.setHeader("Set-Cookie", "test3=value3; expires=Fri, 02-Jan-2037 00:00:01 GMT;" + suffix);
+  } else if (aRequest.queryString.includes("4")) {
+    let date2 = getDateInTwoDays();
+
+    aResponse.setHeader("Set-Cookie", "test4=value4; expires=" + date2 + ";" + suffix);
+  }
+
+  aResponse.setHeader("Content-Type", "text/javascript", false);
+  aResponse.write("42;");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/test_documentcookies_maxage.html
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+-->
+<head>
+  <title>Test for document.cookie max-age pref</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+const kTwoDays = 2 * 24 * 60 * 60;
+const kSevenDays = 7 * 24 * 60 * 60;
+const kInTwoDays = (new Date().getTime() + kTwoDays * 1000);
+const kInSevenDays = (new Date().getTime() + kSevenDays * 1000);
+const kScriptURL = SimpleTest.getTestFileURL("file_documentcookie_maxage_chromescript.js");
+
+let gScript;
+
+function getDateInTwoDays()
+{
+  let date2 = new Date(kInTwoDays);
+  let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+  let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+                "Nov", "Dec"];
+  let day = date2.getUTCDate();
+  if (day < 10) {
+    day = "0" + day;
+  }
+  let month = months[date2.getUTCMonth()];
+  let year = date2.getUTCFullYear();
+  let hour = date2.getUTCHours();
+  if (hour < 10) {
+    hour = "0" + hour;
+  }
+  let minute = date2.getUTCMinutes();
+  if (minute < 10) {
+    minute = "0" + minute;
+  }
+  let second = date2.getUTCSeconds();
+  if (second < 10) {
+    second = "0" + second;
+  }
+  return days[date2.getUTCDay()] + ", " + day + "-" + month + "-" +
+         year + " " + hour + ":" + minute + ":" + second + " GMT";
+}
+
+function dotest()
+{
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({
+    set: [["privacy.documentCookies.maxage", kSevenDays]],
+  }).then(_ => {
+    gScript = SpecialPowers.loadChromeScript(kScriptURL);
+
+    return new Promise(resolve => {
+      gScript.addMessageListener("init:return", resolve);
+      gScript.sendAsyncMessage("init");
+    });
+  }).then(_ => {
+    let date2 = getDateInTwoDays();
+
+    document.cookie = "test1=value1; expires=Fri, 02-Jan-2037 00:00:01 GMT;";
+    document.cookie = "test2=value2; expires=" + date2 + ";";
+
+    return fetch("subResources.sjs?3");
+  }).then(_ => {
+    return fetch("subResources.sjs?4");
+  }).then(_ => {
+    return new Promise(resolve => {
+      gScript.addMessageListener("getCookies:return", resolve);
+      gScript.sendAsyncMessage("getCookies");
+    });
+  }).then(_ => {
+    for (let cookie of _.cookies) {
+      switch (cookie.name) {
+        case "test1": {
+          is(cookie.value, "value1", "The correct value expected");
+          let d = new Date(cookie.expires * 1000);
+          let [day, month, year] = [d.getUTCDate(), d.getUTCMonth(), d.getUTCFullYear()];
+          let d2 = new Date(kInSevenDays);
+          let [day2, month2, year2] = [d2.getUTCDate(), d2.getUTCMonth(), d2.getUTCFullYear()];
+          is(day, day2, "Days match");
+          is(month, month2, "Months match");
+          is(year, year2, "Years match");
+        }
+        break;
+
+      case "test2": {
+          is(cookie.value, "value2", "The correct value expected");
+          let d = new Date(cookie.expires * 1000);
+          let [day, month, year] = [d.getUTCDate(), d.getUTCMonth(), d.getUTCFullYear()];
+          let d2 = new Date(kInTwoDays);
+          let [day2, month2, year2] = [d2.getUTCDate(), d2.getUTCMonth(), d2.getUTCFullYear()];
+          is(day, day2, "Days match");
+          is(month, month2, "Months match");
+          is(year, year2, "Years match");
+        }
+        break;
+
+      case "test3": {
+          is(cookie.value, "value3", "The correct value expected");
+          let d = new Date(cookie.expires * 1000);
+          let [day, month, year] = [d.getUTCDate(), d.getUTCMonth(), d.getUTCFullYear()];
+          let d2 = new Date("Fri, 02 Jan 2037 00:00:01 GMT");
+          let [day2, month2, year2] = [d2.getUTCDate(), d2.getUTCMonth(), d2.getUTCFullYear()];
+          is(day, day2, "Days match");
+          is(month, month2, "Months match");
+          is(year, year2, "Years match");
+        }
+        break;
+
+      case "test4": {
+          is(cookie.value, "value4", "The correct value expected");
+          let d = new Date(cookie.expires * 1000);
+          let [day, month, year] = [d.getUTCDate(), d.getUTCMonth(), d.getUTCFullYear()];
+          let d2 = new Date(kInTwoDays);
+          let [day2, month2, year2] = [d2.getUTCDate(), d2.getUTCMonth(), d2.getUTCFullYear()];
+          is(day, day2, "Days match");
+          is(month, month2, "Months match");
+          is(year, year2, "Years match");
+        }
+        break;
+
+      default:
+        ok(false, "Unexpected cookie found!");
+        break;
+      }
+    }
+
+    return new Promise(resolve => {
+      gScript.addMessageListener("shutdown:return", resolve);
+      gScript.sendAsyncMessage("shutdown");
+    });
+  }).then(finish);
+}
+
+function finish()
+{
+  SimpleTest.finish();
+}
+</script>
+</head>
+<body onload="dotest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
--- a/old-configure.in
+++ b/old-configure.in
@@ -59,17 +59,17 @@ GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2
 CAIRO_VERSION=1.10
 GTK2_VERSION=2.18.0
 GTK3_VERSION=3.4.0
 GDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_4
 W32API_VERSION=3.14
 GCONF_VERSION=1.2.1
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.27.1
+SQLITE_VERSION=3.27.2
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 
 dnl Initialize the Pthread test variables early so they can be
 dnl  overridden by each platform.
 dnl ========================================================
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -13,20 +13,18 @@ import sys
 import types
 from collections import OrderedDict
 from contextlib import contextmanager
 from functools import wraps
 from mozbuild.configure.options import (
     CommandLineHelper,
     ConflictingOptionError,
     InvalidOptionError,
-    NegativeOptionValue,
     Option,
     OptionValue,
-    PositiveOptionValue,
 )
 from mozbuild.configure.help import HelpFormatter
 from mozbuild.configure.util import (
     ConfigureOutputHandler,
     getpreferredencoding,
     LineIO,
 )
 from mozbuild.util import (
@@ -441,20 +439,38 @@ class ConfigureSandbox(dict):
                 )
 
             self._value_for(option)
 
         # All implied options should exist.
         for implied_option in self._implied_options:
             value = self._resolve(implied_option.value)
             if value is not None:
-                raise ConfigureError(
-                    '`%s`, emitted from `%s` line %d, is unknown.'
-                    % (implied_option.option, implied_option.caller[1],
-                       implied_option.caller[2]))
+                # There are two ways to end up here: either the implied option
+                # is unknown, or it's known but there was a dependency loop
+                # that prevented the implication from being applied.
+                option = self._options.get(implied_option.name)
+                if not option:
+                    raise ConfigureError(
+                        '`%s`, emitted from `%s` line %d, is unknown.'
+                        % (implied_option.option, implied_option.caller[1],
+                           implied_option.caller[2]))
+                # If the option is known, check that the implied value doesn't
+                # conflict with what value was attributed to the option.
+                option_value = self._value_for_option(option)
+                if value != option_value:
+                    reason = implied_option.reason
+                    if isinstance(reason, Option):
+                        reason = self._raw_options.get(reason) or reason.option
+                        reason = reason.split('=', 1)[0]
+                    value = OptionValue.from_(value)
+                    raise InvalidOptionError(
+                        "'%s' implied by '%s' conflicts with '%s' from the %s"
+                        % (value.format(option.option), reason,
+                           option_value.format(option.option), option_value.origin))
 
         # All options should have been removed (handled) by now.
         for arg in self._helper:
             without_value = arg.split('=', 1)[0]
             msg = 'Unknown option: %s' % without_value
             if self._help:
                 self._logger.warning(msg)
             else:
@@ -529,45 +545,40 @@ class ConfigureSandbox(dict):
 
             if (implied_option.when and
                 not self._value_for(implied_option.when)):
                 continue
 
             value = self._resolve(implied_option.value)
 
             if value is not None:
-                if isinstance(value, OptionValue):
-                    pass
-                elif value is True:
-                    value = PositiveOptionValue()
-                elif value is False or value == ():
-                    value = NegativeOptionValue()
-                elif isinstance(value, types.StringTypes):
-                    value = PositiveOptionValue((value,))
-                elif isinstance(value, tuple):
-                    value = PositiveOptionValue(value)
-                else:
-                    raise TypeError("Unexpected type: '%s'"
-                                    % type(value).__name__)
-
+                value = OptionValue.from_(value)
                 opt = value.format(implied_option.option)
                 self._helper.add(opt, 'implied')
                 implied[opt] = implied_option
 
         try:
             value, option_string = self._helper.handle(option)
         except ConflictingOptionError as e:
             reason = implied[e.arg].reason
             if isinstance(reason, Option):
                 reason = self._raw_options.get(reason) or reason.option
                 reason = reason.split('=', 1)[0]
             raise InvalidOptionError(
                 "'%s' implied by '%s' conflicts with '%s' from the %s"
                 % (e.arg, reason, e.old_arg, e.old_origin))
 
+        if value.origin == 'implied':
+            recursed_value = getattr(self, '__value_for_option').get((option,))
+            if recursed_value is not None:
+                _, filename, line, _, _, _ = implied[value.format(option.option)].caller
+                raise ConfigureError(
+                    "'%s' appears somewhere in the direct or indirect dependencies when "
+                    "resolving imply_option at %s:%d" % (option.option, filename, line))
+
         if option_string:
             self._raw_options[option] = option_string
 
         when = self._conditions.get(option)
         # If `when` resolves to a false-ish value, we always return None.
         # This makes option(..., when='--foo') equivalent to
         # option(..., when=depends('--foo')(lambda x: x)).
         if when and not self._value_for(when) and value is not None:
--- a/python/mozbuild/mozbuild/configure/options.py
+++ b/python/mozbuild/mozbuild/configure/options.py
@@ -64,29 +64,47 @@ class OptionValue(tuple):
             raise TypeError('cannot compare a populated %s against an %s; '
                             'OptionValue instances are tuples - did you mean to '
                             'compare against member elements using [x]?' % (
                                 type(other).__name__, type(self).__name__))
 
         # Allow explicit tuples to be compared.
         if type(other) == tuple:
             return tuple.__eq__(self, other)
+        elif isinstance(other, bool):
+            return bool(self) == other
         # Else we're likely an OptionValue class.
         elif type(other) != type(self):
             return False
         else:
             return super(OptionValue, self).__eq__(other)
 
     def __ne__(self, other):
         return not self.__eq__(other)
 
     def __repr__(self):
         return '%s%s' % (self.__class__.__name__,
                          super(OptionValue, self).__repr__())
 
+    @staticmethod
+    def from_(value):
+        if isinstance(value, OptionValue):
+            return value
+        elif value is True:
+            return PositiveOptionValue()
+        elif value is False or value == ():
+            return NegativeOptionValue()
+        elif isinstance(value, types.StringTypes):
+            return PositiveOptionValue((value,))
+        elif isinstance(value, tuple):
+            return PositiveOptionValue(value)
+        else:
+            raise TypeError("Unexpected type: '%s'"
+                            % type(value).__name__)
+
 
 class PositiveOptionValue(OptionValue):
     '''Represents the value for a positive option (--enable/--with/--foo)
     in the form of a tuple for when values are given to the option (in the form
     --option=value[,value2...].
     '''
     def __nonzero__(self):
         return True
--- a/python/mozbuild/mozbuild/test/configure/test_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_configure.py
@@ -690,16 +690,145 @@ class TestConfigure(unittest.TestCase):
                 'QUX': NegativeOptionValue(),
             })
 
             config = self.get_config(['--with-foo'])
             self.assertEquals(config, {
                 'QUX': PositiveOptionValue(),
             })
 
+    def test_imply_option_dependency_loop(self):
+        with self.moz_configure('''
+            option('--without-foo', help='foo')
+
+            @depends('--with-foo')
+            def qux_default(foo):
+                return bool(foo)
+
+            option('--with-qux', default=qux_default, help='qux')
+
+            imply_option('--with-foo', depends('--with-qux')(lambda x: x or None))
+
+            set_config('FOO', depends('--with-foo')(lambda x: x))
+            set_config('QUX', depends('--with-qux')(lambda x: x))
+        '''):
+            config = self.get_config()
+            self.assertEquals(config, {
+                'FOO': PositiveOptionValue(),
+                'QUX': PositiveOptionValue(),
+            })
+
+            config = self.get_config(['--without-foo'])
+            self.assertEquals(config, {
+                'FOO': NegativeOptionValue(),
+                'QUX': NegativeOptionValue(),
+            })
+
+            config = self.get_config(['--with-qux'])
+            self.assertEquals(config, {
+                'FOO': PositiveOptionValue(),
+                'QUX': PositiveOptionValue(),
+            })
+
+            with self.assertRaises(InvalidOptionError) as e:
+                config = self.get_config(['--without-foo', '--with-qux'])
+
+            self.assertEquals(e.exception.message,
+                              "'--with-foo' implied by '--with-qux' conflicts "
+                              "with '--without-foo' from the command-line")
+
+            config = self.get_config(['--without-qux'])
+            self.assertEquals(config, {
+                'FOO': PositiveOptionValue(),
+                'QUX': NegativeOptionValue(),
+            })
+
+        with self.moz_configure('''
+            option('--with-foo', help='foo')
+
+            @depends('--with-foo')
+            def qux_default(foo):
+                return bool(foo)
+
+            option('--with-qux', default=qux_default, help='qux')
+
+            imply_option('--with-foo', depends('--with-qux')(lambda x: x or None))
+
+            set_config('FOO', depends('--with-foo')(lambda x: x))
+            set_config('QUX', depends('--with-qux')(lambda x: x))
+        '''):
+            config = self.get_config()
+            self.assertEquals(config, {
+                'FOO': NegativeOptionValue(),
+                'QUX': NegativeOptionValue(),
+            })
+
+            config = self.get_config(['--with-foo'])
+            self.assertEquals(config, {
+                'FOO': PositiveOptionValue(),
+                'QUX': PositiveOptionValue(),
+            })
+
+            with self.assertRaises(InvalidOptionError) as e:
+                config = self.get_config(['--with-qux'])
+
+            self.assertEquals(e.exception.message,
+                              "'--with-foo' implied by '--with-qux' conflicts "
+                              "with '--without-foo' from the default")
+
+            with self.assertRaises(InvalidOptionError) as e:
+                config = self.get_config(['--without-foo', '--with-qux'])
+
+            self.assertEquals(e.exception.message,
+                              "'--with-foo' implied by '--with-qux' conflicts "
+                              "with '--without-foo' from the command-line")
+
+            config = self.get_config(['--without-qux'])
+            self.assertEquals(config, {
+                'FOO': NegativeOptionValue(),
+                'QUX': NegativeOptionValue(),
+            })
+
+    def test_imply_option_recursion(self):
+        config_path = mozpath.abspath(
+            mozpath.join(test_data_path, 'moz.configure'))
+
+        message = ("'--without-foo' appears somewhere in the direct or indirect dependencies "
+                   "when resolving imply_option at %s:8" % config_path)
+
+        with self.moz_configure('''
+            option('--without-foo', help='foo')
+
+            imply_option('--with-qux', depends('--with-foo')(lambda x: x or None))
+
+            option('--with-qux', help='qux')
+
+            imply_option('--with-foo', depends('--with-qux')(lambda x: x or None))
+
+            set_config('FOO', depends('--with-foo')(lambda x: x))
+            set_config('QUX', depends('--with-qux')(lambda x: x))
+        '''):
+            # Note: no error is detected when the depends function in the
+            # imply_options resolve to None, which disables the imply_option.
+
+            with self.assertRaises(ConfigureError) as e:
+                config = self.get_config()
+
+            self.assertEquals(e.exception.message, message)
+
+            with self.assertRaises(ConfigureError) as e:
+                config = self.get_config(['--with-qux'])
+
+            self.assertEquals(e.exception.message, message)
+
+            with self.assertRaises(ConfigureError) as e:
+                config = self.get_config(['--without-foo', '--with-qux'])
+
+            self.assertEquals(e.exception.message, message)
+
     def test_option_failures(self):
         with self.assertRaises(ConfigureError) as e:
             with self.moz_configure('option("--with-foo", help="foo")'):
                 self.get_config()
 
         self.assertEquals(
             e.exception.message,
             'Option `--with-foo` is not handled ; reference it with a @depends'
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -198,22 +198,17 @@ impl LengthPercentage {
 
     /// Returns the percentage component if this could be represented as a
     /// non-calc percentage.
     pub fn as_percentage(&self) -> Option<Percentage> {
         if !self.has_percentage || self.length.px() != 0. {
             return None;
         }
 
-        if self.clamping_mode.clamp(self.percentage.0) != self.percentage.0 {
-            debug_assert!(self.was_calc);
-            return None;
-        }
-
-        Some(self.percentage)
+        Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
     }
 
     /// Convert the computed value into used value.
     #[inline]
     pub fn maybe_to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
         self.maybe_to_pixel_length(container_len).map(Au::from)
     }
 
@@ -428,24 +423,23 @@ impl ToComputedValue for specified::Leng
                 LengthPercentage::new(value.to_computed_value(context), None)
             },
             specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),
             specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
         }
     }
 
     fn from_computed_value(computed: &LengthPercentage) -> Self {
-        let length = computed.unclamped_length();
         if let Some(p) = computed.as_percentage() {
             return specified::LengthPercentage::Percentage(p);
         }
 
-        if !computed.has_percentage && computed.clamping_mode.clamp(length.px()) == length.px() {
+        if !computed.has_percentage {
             return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(
-                &length,
+                &computed.length(),
             ));
         }
 
         specified::LengthPercentage::Calc(Box::new(ToComputedValue::from_computed_value(computed)))
     }
 }
 
 impl IsZeroLength for LengthPercentage {
new file mode 100644
--- /dev/null
+++ b/storage/variant/src/bag.rs
@@ -0,0 +1,94 @@
+/* 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 nserror::{nsresult, NS_OK};
+use nsstring::nsString;
+use xpcom::{getter_addrefs, interfaces::nsIWritablePropertyBag, RefPtr};
+
+use crate::VariantType;
+
+extern "C" {
+    fn NS_NewHashPropertyBag(bag: *mut *const nsIWritablePropertyBag) -> libc::c_void;
+}
+
+/// A hash property bag backed by storage variant values.
+pub struct HashPropertyBag(RefPtr<nsIWritablePropertyBag>);
+
+impl Default for HashPropertyBag {
+    fn default() -> HashPropertyBag {
+        // This is safe to unwrap because `NS_NewHashPropertyBag` is infallible.
+        let bag = getter_addrefs(|p| {
+            unsafe { NS_NewHashPropertyBag(p) };
+            NS_OK
+        }).unwrap();
+        HashPropertyBag(bag)
+    }
+}
+
+impl HashPropertyBag {
+    /// Creates an empty property bag.
+    #[inline]
+    pub fn new() -> HashPropertyBag {
+        HashPropertyBag::default()
+    }
+
+    /// Returns the value for a property name. Fails with `NS_ERROR_FAILURE`
+    /// if the property doesn't exist, or `NS_ERROR_CANNOT_CONVERT_DATA` if the
+    /// property exists, but is not of the value type `V`.
+    pub fn get<K, V>(&self, name: K) -> Result<V, nsresult>
+    where
+        K: AsRef<str>,
+        V: VariantType,
+    {
+        getter_addrefs(|p| unsafe { self.0.GetProperty(&*nsString::from(name.as_ref()), p) })
+            .and_then(|v| V::from_variant(v.coerce()))
+    }
+
+    /// Returns the value for a property name, or the default if not set or
+    /// not of the value type `V`.
+    #[inline]
+    pub fn get_or_default<K, V>(&self, name: K) -> V
+    where
+        K: AsRef<str>,
+        V: VariantType + Default,
+    {
+        self.get(name).unwrap_or_default()
+    }
+
+    /// Sets a property with the name to the value, overwriting any previous
+    /// value.
+    pub fn set<K, V>(&mut self, name: K, value: V)
+    where
+        K: AsRef<str>,
+        V: VariantType,
+    {
+        let v = value.into_variant();
+        unsafe {
+            // This is safe to unwrap because
+            // `nsHashPropertyBagBase::SetProperty` only returns an error if `v`
+            // is a null pointer.
+            self.0
+                .SetProperty(&*nsString::from(name.as_ref()), v.coerce())
+                .to_result()
+                .unwrap()
+        }
+    }
+
+    /// Deletes a property with the name. Returns `true` if the property
+    /// was previously in the bag, `false` if not.
+    pub fn delete(&mut self, name: impl AsRef<str>) -> bool {
+        unsafe {
+            self.0
+                .DeleteProperty(&*nsString::from(name.as_ref()))
+                .to_result()
+                .is_ok()
+        }
+    }
+
+    /// Returns a reference to the backing `nsIWritablePropertyBag`.
+    #[inline]
+    pub fn bag(&self) -> &nsIWritablePropertyBag {
+        &self.0
+    }
+}
--- a/storage/variant/src/lib.rs
+++ b/storage/variant/src/lib.rs
@@ -2,21 +2,25 @@
  * 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/. */
 
 extern crate libc;
 extern crate nserror;
 extern crate nsstring;
 extern crate xpcom;
 
+mod bag;
+
 use libc::{c_double, int64_t, uint16_t};
 use nserror::{nsresult, NS_OK};
 use nsstring::{nsACString, nsAString, nsCString, nsString};
 use xpcom::{getter_addrefs, interfaces::nsIVariant, RefPtr};
 
+pub use crate::bag::HashPropertyBag;
+
 extern "C" {
     fn NS_GetDataType(variant: *const nsIVariant) -> uint16_t;
     fn NS_NewStorageNullVariant(result: *mut *const nsIVariant);
     fn NS_NewStorageBooleanVariant(value: bool, result: *mut *const nsIVariant);
     fn NS_NewStorageIntegerVariant(value: int64_t, result: *mut *const nsIVariant);
     fn NS_NewStorageFloatVariant(value: c_double, result: *mut *const nsIVariant);
     fn NS_NewStorageTextVariant(value: *const nsAString, result: *mut *const nsIVariant);
     fn NS_NewStorageUTF8TextVariant(value: *const nsACString, result: *mut *const nsIVariant);
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -822,17 +822,17 @@ def _log_warning(text):
 
 
 def _log_info(text):
     print("%s" % text)
 
 
 def _download_file(url, filename, path):
     _log_debug("Download %s to %s/%s..." % (url, path, filename))
-    f = urllib.urlopen(url)
+    f = urllib.request.urlopen(url)
     if not os.path.isdir(path):
         try:
             os.makedirs(path)
         except Exception as e:
             _log_warning(str(e))
             return False
     local_file = open(os.path.join(path, filename), 'wb')
     local_file.write(f.read())
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-shapes/parsing/shape-margin-computed.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[shape-margin-computed.html]
-  [Property shape-margin value 'calc(10px - 0.5em)' computes to '0px']
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for table element's UA-stylesheet-provided styles</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="refElem"></div>
+<!-- Note: this test puts the table inside of an element with a non-default
+     'text-indent' and 'border-collapse' values, so that we can verify that
+     the table does indeed use the initial value for these properties, rather
+     than simply inheriting. -->
+<div style="text-indent: 100px; border-collapse: collapse">
+  <table id="tableElem"></table>
+</div>
+
+<script>
+/* These styles come from the default `table` styling here:
+ *  https://html.spec.whatwg.org/multipage/rendering.html#tables-2
+ * We can't check for these values directly, because they may be
+ * serialized slightly differently when read from the computed style.
+ * So, for each property here, we apply it to a "reference" div and then
+ * read back the computed value, and we validate that a table element
+ * has that same computed value by default. */
+const defaultTablePropVals = {
+  'display':         'table',
+  'box-sizing':      'border-box',
+  'border-spacing':  '2px',
+  'border-collapse': 'separate',
+  'text-indent':     'initial',
+};
+
+for (var propName in defaultTablePropVals) {
+  test(function() {
+    refElem.style[propName] = defaultTablePropVals[propName];
+    let expectedComputedVal = getComputedStyle(refElem, "")[propName];
+
+    let actualComputedVal = getComputedStyle(tableElem, "")[propName];
+    assert_equals(actualComputedVal, expectedComputedVal);
+  }, `Computed '${propName}' on table should match html spec`);
+}
+</script>
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -51,16 +51,18 @@ skip-if = (os == "win" && os_version == 
 [browser_imageCache4-2.js]
 [browser_imageCache8.js]
 [browser_onBeforeRequestNotificationForTrackingResources.js]
 [browser_onModifyRequestNotificationForTrackingResources.js]
 [browser_permissionInNormalWindows.js]
 skip-if = serviceworker_e10s
 [browser_permissionInPrivateWindows.js]
 skip-if = serviceworker_e10s
+[browser_referrerDefaultPolicy.js]
+support-files = referrer.sjs
 [browser_siteSpecificWorkArounds.js]
 [browser_subResources.js]
 skip-if = serviceworker_e10s
 support-files = subResources.sjs
 [browser_script.js]
 support-files = tracker.js
 [browser_userInteraction.js]
 [browser_storageAccessDoorHanger.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_referrerDefaultPolicy.js
@@ -0,0 +1,119 @@
+const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/general/";
+Services.scriptloader.loadSubScript(CHROME_BASE + "head.js", this);
+/* import-globals-from ../../../../../browser/base/content/test/general/head.js */
+
+async function testOnWindow(private, expectedReferrer) {
+  info("Creating a new " + (private ? "private" : "normal") + " window");
+  let win = await BrowserTestUtils.openNewBrowserWindow({private});
+  let browser = win.gBrowser;
+  let tab = browser.selectedTab;
+  let b = browser.getBrowserForTab(tab);
+  await promiseTabLoadEvent(tab, TEST_TOP_PAGE);
+
+  info("Loading tracking scripts and tracking images");
+  await ContentTask.spawn(b, null, async function() {
+    {
+      let src = content.document.createElement("script");
+      let p = new content.Promise(resolve => { src.onload = resolve; });
+      content.document.body.appendChild(src);
+      src.src = "https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?what=script";
+      await p;
+    }
+
+    {
+      let img = content.document.createElement("img");
+      let p = new content.Promise(resolve => { img.onload = resolve; });
+      content.document.body.appendChild(img);
+      img.src = "https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?what=image";
+      await p;
+    }
+  });
+
+  await fetch("https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?result&what=image")
+    .then(r => r.text())
+    .then(text => {
+      is(text, expectedReferrer, "We sent the correct Referer header");
+    });
+
+  await fetch("https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?result&what=script")
+    .then(r => r.text())
+    .then(text => {
+      is(text, expectedReferrer, "We sent the correct Referer header");
+    });
+
+  await BrowserTestUtils.closeWindow(win);
+}
+
+add_task(async function() {
+  info("Starting referrer default policy test");
+
+  await SpecialPowers.flushPrefEnv();
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["browser.contentblocking.allowlist.annotations.enabled", true],
+    ["browser.contentblocking.allowlist.storage.enabled", true],
+    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+    ["privacy.trackingprotection.enabled", false],
+    ["privacy.trackingprotection.pbmode.enabled", false],
+    ["privacy.trackingprotection.annotate_channels", true],
+  ]});
+
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  // no-referrer-when-downgrade
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers", 3]]});
+  await testOnWindow(false, TEST_TOP_PAGE);
+
+  // strict-origin-when-cross-origin
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers", 2]]});
+  await testOnWindow(false, TEST_DOMAIN);
+
+  // same-origin
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers", 1]]});
+  await testOnWindow(false, "");
+
+  // no-referrer
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers", 0]]});
+  await testOnWindow(false, "");
+
+  // Reset the pref.
+  Services.prefs.clearUserPref("network.http.referer.defaultPolicy.trackers");
+
+  // no-referrer-when-downgrade
+  await SpecialPowers.pushPrefEnv({"set": [
+    // Set both prefs, because if we only set the trackers pref, then the PB
+    // mode default policy pref (2) would apply!
+    ["network.http.referer.defaultPolicy.pbmode", 3],
+    ["network.http.referer.defaultPolicy.trackers.pbmode", 3],
+  ]});
+  await testOnWindow(true, TEST_TOP_PAGE);
+
+  // strict-origin-when-cross-origin
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers.pbmode", 2]]});
+  await testOnWindow(true, TEST_DOMAIN);
+
+  // same-origin
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers.pbmode", 1]]});
+  await testOnWindow(true, "");
+
+  // no-referrer
+  await SpecialPowers.pushPrefEnv({"set":
+    [["network.http.referer.defaultPolicy.trackers.pbmode", 0]]});
+  await testOnWindow(true, "");
+
+  // Reset the pref.
+  Services.prefs.clearUserPref("network.http.referer.defaultPolicy.trackers.pbmode");
+});
+
+add_task(async function() {
+  info("Cleaning up.");
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+  });
+});
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/referrer.sjs
@@ -0,0 +1,29 @@
+// A 1x1 PNG image.
+// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+                   "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+  let key = aRequest.queryString.includes("what=script") ? "script" : "image";
+
+  if (aRequest.queryString.includes("result")) {
+    aResponse.write(getState(key));
+    setState(key, "");
+    return;
+  }
+
+  if (aRequest.hasHeader('Referer')) {
+    let referrer = aRequest.getHeader('Referer');
+    setState(key, referrer);
+  }
+
+  if (key == "script") {
+    aResponse.setHeader("Content-Type", "text/javascript", false);
+    aResponse.write("42;");
+  } else {
+    aResponse.setHeader("Content-Type", "image/png", false);
+    aResponse.write(IMAGE);
+  }
+}
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -159,26 +159,37 @@ var LoginManagerContent = {
    * fills for the same set of elements when a field gets added between arming and running the
    * DeferredTask.
    *
    * @type {WeakMap}
    */
   _formLikeByRootElement: new WeakMap(),
 
   /**
-   * WeakMap of the root element of a WeakMap to the DeferredTask to fill its fields.
+   * WeakMap of the root element of a FormLike to the DeferredTask to fill its fields.
    *
    * This is used to be able to throttle fills for a FormLike since onDOMInputPasswordAdded gets
    * dispatched for each password field added to a document but we only want to fill once per
    * FormLike when multiple fields are added at once.
    *
    * @type {WeakMap}
    */
   _deferredPasswordAddedTasksByRootElement: new WeakMap(),
 
+  /**
+   * WeakMap of a document to the array of callbacks to execute when it becomes visible
+   *
+   * This is used to defer handling DOMFormHasPassword and onDOMInputPasswordAdded events when the
+   * containing document is hidden.
+   * When the document first becomes visible, any queued events will be handled as normal.
+   *
+   * @type {WeakMap}
+   */
+  _onVisibleTasksByDocument: new WeakMap(),
+
   // Map from form login requests to information about that request.
   _requests: new Map(),
 
   // Number of outstanding requests to each manager.
   _managers: new Map(),
 
   _takeRequest(msg) {
     let data = msg.data;
@@ -348,44 +359,98 @@ var LoginManagerContent = {
 
     // We're invoked before the content's |submit| event handlers, so we
     // can grab form data before it might be modified (see bug 257781).
     log("notified before form submission");
     let formLike = LoginFormFactory.createFromForm(event.target);
     LoginManagerContent._onFormSubmit(formLike);
   },
 
+  onDocumentVisibilityChange(event) {
+    if (!event.isTrusted) {
+      return;
+    }
+    let document = event.target;
+    let onVisibleTasks = this._onVisibleTasksByDocument.get(document);
+    if (!onVisibleTasks) {
+      return;
+    }
+    for (let task of onVisibleTasks) {
+      log("onDocumentVisibilityChange, executing queued task");
+      task();
+    }
+    this._onVisibleTasksByDocument.delete(document);
+  },
+
+  _deferHandlingEventUntilDocumentVisible(event, document, fn) {
+    log(`document.visibilityState: ${document.visibilityState}, defer handling ${event.type}`);
+    let onVisibleTasks = this._onVisibleTasksByDocument.get(document);
+    if (!onVisibleTasks) {
+      log(`deferHandling, first queued event, register the visibilitychange handler`);
+      onVisibleTasks = [];
+      this._onVisibleTasksByDocument.set(document, onVisibleTasks);
+      document.addEventListener("visibilitychange", event => {
+        this.onDocumentVisibilityChange(event);
+      }, { once: true });
+    }
+    onVisibleTasks.push(fn);
+  },
+
   onDOMFormHasPassword(event) {
     if (!event.isTrusted) {
       return;
     }
+    let document = event.target.ownerDocument;
+    if (document.visibilityState == "visible") {
+      this._processDOMFormHasPasswordEvent(event);
+    } else {
+      // wait until the document becomes visible before handling this event
+      this._deferHandlingEventUntilDocumentVisible(event, document, () => {
+        this._processDOMFormHasPasswordEvent(event);
+      });
+    }
+  },
 
+  _processDOMFormHasPasswordEvent(event) {
     let form = event.target;
     let formLike = LoginFormFactory.createFromForm(form);
-    log("onDOMFormHasPassword:", form, formLike);
+    log("_processDOMFormHasPasswordEvent:", form, formLike);
     this._fetchLoginsFromParentAndFillForm(formLike);
   },
 
   onDOMInputPasswordAdded(event, topWindow) {
     if (!event.isTrusted) {
       return;
     }
 
     let pwField = event.originalTarget;
     if (pwField.form) {
       // Fill is handled by onDOMFormHasPassword which is already throttled.
       return;
     }
 
+    let document = pwField.ownerDocument;
+    if (document.visibilityState == "visible") {
+      this._processDOMInputPasswordAddedEvent(event, topWindow);
+    } else {
+      // wait until the document becomes visible before handling this event
+      this._deferHandlingEventUntilDocumentVisible(event, document, () => {
+        this._processDOMInputPasswordAddedEvent(event, topWindow);
+      });
+    }
+  },
+
+  _processDOMInputPasswordAddedEvent(event, topWindow) {
+    let pwField = event.originalTarget;
     // Only setup the listener for formless inputs.
     // Capture within a <form> but without a submit event is bug 1287202.
     this.setupProgressListener(topWindow);
 
     let formLike = LoginFormFactory.createFromField(pwField);
-    log("onDOMInputPasswordAdded:", pwField, formLike);
+    log(" _processDOMInputPasswordAddedEvent:", pwField, formLike);
 
     let deferredTask = this._deferredPasswordAddedTasksByRootElement.get(formLike.rootElement);
     if (!deferredTask) {
       log("Creating a DeferredTask to call _fetchLoginsFromParentAndFillForm soon");
       this._formLikeByRootElement.set(formLike.rootElement, formLike);
 
       deferredTask = new DeferredTask(() => {
         // Get the updated formLike instead of the one at the time of creating the DeferredTask via
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -52,16 +52,17 @@ support-files =
 skip-if = (os == "linux") || (os == "mac") # Bug 1337606
 [browser_exceptions_dialog.js]
 [browser_formless_submit_chrome.js]
 [browser_hasInsecureLoginForms.js]
 skip-if = verify
 [browser_hasInsecureLoginForms_streamConverter.js]
 [browser_http_autofill.js]
 skip-if = verify
+[browser_hidden_document_autofill.js]
 [browser_insecurePasswordConsoleWarning.js]
 skip-if = verify
 [browser_master_password_autocomplete.js]
 [browser_notifications.js]
 [browser_notifications_username.js]
 [browser_notifications_password.js]
 [browser_notifications_2.js]
 skip-if = os == "linux" # Bug 1272849 Main action button disabled state intermittent
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/browser/browser_hidden_document_autofill.js
@@ -0,0 +1,184 @@
+const TEST_URL_PATH = "/browser/toolkit/components/passwordmgr/test/browser/";
+const INITIAL_URL = `about:blank`;
+const FORM_URL = `https://example.org${TEST_URL_PATH}form_basic.html`;
+const FORMLESS_URL = `https://example.org${TEST_URL_PATH}formless_basic.html`;
+const testUrls = [FORM_URL, FORMLESS_URL];
+
+async function getDocumentVisibilityState(browser) {
+  let visibility = await ContentTask.spawn(browser, null, async function() {
+    return content.document.visibilityState;
+  });
+  return visibility;
+}
+
+async function addContentObserver(browser, topic) {
+  // add an observer.
+  await ContentTask.spawn(browser, [topic], function(contentTopic) {
+    this.gObserver = {
+      wasObserved: false,
+      observe: () => {
+        content.wasObserved = true;
+      },
+    };
+    Services.obs.addObserver(this.gObserver, contentTopic);
+  });
+}
+
+async function getContentObserverResult(browser, topic) {
+  let result = await ContentTask.spawn(browser, [topic], async function(contentTopic) {
+    const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+    try {
+      await TestUtils.waitForCondition(() => {
+        return content.wasObserved;
+      }, `Wait for "passwordmgr-processed-form"`);
+    } catch (ex) {
+      content.wasObserved = false;
+    }
+    Services.obs.removeObserver(this.gObserver, "passwordmgr-processed-form");
+    return content.wasObserved;
+  });
+  return result;
+}
+
+// Waits for the master password prompt and cancels it.
+function observeMasterPasswordDialog(window, result) {
+  let closedPromise;
+  function topicObserver(subject) {
+    if (subject.Dialog.args.title == "Password Required") {
+      result.wasShown = true;
+      subject.Dialog.ui.button1.click();
+      closedPromise = BrowserTestUtils.waitForEvent(window, "DOMModalDialogClosed");
+    }
+  }
+  Services.obs.addObserver(topicObserver, "common-dialog-loaded");
+
+  let waited = TestUtils.waitForCondition(() => {
+    return result.wasShown;
+  }, "Wait for master password dialog");
+
+  return Promise.all([waited, closedPromise]).catch(ex => {
+    info(`observeMasterPasswordDialog, caught exception from topicObserved: ${ex}`);
+  }).finally(() => {
+    Services.obs.removeObserver(topicObserver, "common-dialog-loaded");
+  });
+}
+
+add_task(async function setup() {
+  Services.logins.removeAllLogins();
+  let login = LoginTestUtils.testData.formLogin({
+    hostname: "http://example.org",
+    formSubmitURL: "http://example.org",
+    username: "user1",
+    password: "pass1",
+  });
+  Services.logins.addLogin(login);
+});
+
+add_task(async function test_processed_form_fired() {
+  // Sanity check. If this doesnt work any results for the subsequent tasks are suspect
+  const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL);
+  let tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+  is(tab1Visibility, "visible", "The first tab should be foreground");
+
+  await addContentObserver(tab1.linkedBrowser, "passwordmgr-processed-form");
+  await BrowserTestUtils.loadURI(tab1.linkedBrowser, FORM_URL);
+  let result = await getContentObserverResult(tab1.linkedBrowser, "passwordmgr-processed-form");
+  ok(result, "Observer should be notified when form is loaded into a visible document");
+  gBrowser.removeTab(tab1);
+});
+
+testUrls.forEach(testUrl => {
+  add_task(async function test_defer_autofill_until_visible() {
+    let result, tab1Visibility;
+    // open 2 tabs
+    const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL);
+    const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL);
+
+    // confirm document is hidden
+    tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+    is(tab1Visibility, "hidden", "The first tab should be backgrounded");
+
+    // we shouldn't even try to autofill while hidden, so look for the passwordmgr-processed-form
+    // to be observed rather than any result of filling the form
+    await addContentObserver(tab1.linkedBrowser, "passwordmgr-processed-form");
+    await BrowserTestUtils.loadURI(tab1.linkedBrowser, testUrl);
+    result = await getContentObserverResult(tab1.linkedBrowser, "passwordmgr-processed-form");
+    ok(!result, "Observer should not be notified when form is loaded into a hidden document");
+
+    // Add the observer before switching tab
+    await addContentObserver(tab1.linkedBrowser, "passwordmgr-processed-form");
+    await BrowserTestUtils.switchTab(gBrowser, tab1);
+    result = await getContentObserverResult(tab1.linkedBrowser, "passwordmgr-processed-form");
+    tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+    is(tab1Visibility, "visible", "The first tab should be foreground");
+    ok(result, "Observer should be notified when input's document becomes visible");
+
+    // the form should have been autofilled with the login
+    let fieldValues = await ContentTask.spawn(tab1.linkedBrowser, null, function() {
+      let doc = content.document;
+      return {
+        username: doc.getElementById("form-basic-username").value,
+        password: doc.getElementById("form-basic-password").value,
+      };
+    });
+    is(fieldValues.username, "user1", "Checking filled username");
+    is(fieldValues.password, "pass1", "Checking filled password");
+
+    gBrowser.removeTab(tab1);
+    gBrowser.removeTab(tab2);
+  });
+});
+
+add_task(async function test_defer_autofill_with_masterpassword() {
+  // Set master password prompt timeout to 3s.
+  // If this test goes intermittent, you likely have to increase this value.
+  await SpecialPowers.pushPrefEnv({set: [["signon.masterPasswordReprompt.timeout_ms", 3000]]});
+  LoginTestUtils.masterPassword.enable();
+
+  registerCleanupFunction(function() {
+    LoginTestUtils.masterPassword.disable();
+  });
+
+  let result, tab1Visibility, dialogObserved;
+  // open 2 tabs
+  const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL);
+  const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, INITIAL_URL);
+
+  info("sanity check by first loading a form into the visible tab");
+  is(await getDocumentVisibilityState(tab2.linkedBrowser),
+     "visible", "The second tab should be visible");
+  result = { wasShown: false };
+
+  dialogObserved = observeMasterPasswordDialog(tab2.ownerGlobal, result);
+  await BrowserTestUtils.loadURI(tab2.linkedBrowser, FORM_URL);
+  await dialogObserved;
+  ok(result.wasShown, "Dialog should be shown when form is loaded into a visible document");
+
+  info("load a background login form tab with a matching saved login " +
+       "and wait to see if the master password dialog is shown");
+  // confirm document is hidden
+  tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+  is(tab1Visibility, "hidden", "The first tab should be backgrounded");
+  result = { wasShown: false };
+
+  dialogObserved = observeMasterPasswordDialog(tab1.ownerGlobal, result);
+  await BrowserTestUtils.loadURI(tab1.linkedBrowser, FORM_URL);
+  await dialogObserved;
+  ok(!result.wasShown, "Dialog should not be shown when form is loaded into a hidden document");
+
+  info("switch to the form tab " +
+       "and confirm the master password dialog is then shown");
+  tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+  is(tab1Visibility, "hidden", "The first tab should be backgrounded");
+  result = { wasShown: false };
+
+  dialogObserved = observeMasterPasswordDialog(tab1.ownerGlobal, result);
+  await BrowserTestUtils.switchTab(gBrowser, tab1);
+  await dialogObserved;
+  tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
+  is(tab1Visibility, "visible", "The first tab should be foreground");
+  ok(result.wasShown, "Dialog should be shown when input's document becomes visible");
+
+  gBrowser.removeTab(tab1);
+  gBrowser.removeTab(tab2);
+});
--- a/toolkit/components/places/UnifiedComplete.jsm
+++ b/toolkit/components/places/UnifiedComplete.jsm
@@ -964,32 +964,36 @@ Search.prototype = {
             }
           }
           let alias =
             this._searchEngineAliasMatch &&
             this._searchEngineAliasMatch.alias ||
             "";
           searchSuggestionsCompletePromise =
             this._matchSearchSuggestions(engine, query, alias);
-          // If the user has used a search engine token alias, then the only
-          // results we want to show are suggestions from that engine, so we're
-          // done.  We're also done if we're restricting results to suggestions.
-          if ((this._searchEngineAliasMatch &&
-               this._searchEngineAliasMatch.isTokenAlias) ||
-              this.hasBehavior("restrict")) {
-            // Wait for the suggestions to be added.
-            await searchSuggestionsCompletePromise;
-            this._cleanUpNonCurrentMatches(null);
-            this._autocompleteSearch.finishSearch(true);
-            return;
-          }
         }
       }
     }
-    // In any case, clear previous suggestions.
+
+    // If the user used a search engine token alias, then the only results we
+    // want to show are suggestions from that engine, so we're done.  We're also
+    // done if we're restricting results to suggestions.
+    if ((this._searchEngineAliasMatch &&
+         this._searchEngineAliasMatch.isTokenAlias) ||
+        (this._enableActions &&
+         this.hasBehavior("search") &&
+         this.hasBehavior("restrict"))) {
+      // Wait for the suggestions to be added.
+      await searchSuggestionsCompletePromise;
+      this._cleanUpNonCurrentMatches(null);
+      this._autocompleteSearch.finishSearch(true);
+      return;
+    }
+
+    // Clear previous search suggestions.
     searchSuggestionsCompletePromise.then(() => {
       this._cleanUpNonCurrentMatches(UrlbarUtils.RESULT_GROUP.SUGGESTION);
     });
 
     // Run the adaptive query first.
     await conn.executeCached(this._adaptiveQuery[0], this._adaptiveQuery[1],
                              this._onResultRow.bind(this));
     if (!this.pending)
--- a/toolkit/components/places/tests/unifiedcomplete/test_search_engine_alias.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_search_engine_alias.js
@@ -124,86 +124,116 @@ add_task(async function basicGetAndPost(
   await cleanup();
 });
 
 
 // Uses an engine that provides search suggestions.
 add_task(async function engineWithSuggestions() {
   let engine = await addTestSuggestionsEngine();
 
-  await PlacesTestUtils.addVisits(engine.searchForm);
+  // History matches should not appear with @ aliases, so this visit/match
+  // should not appear when searching with the @ alias below.
+  let historyTitle = "fire";
+  await PlacesTestUtils.addVisits({
+    uri: engine.searchForm,
+    title: historyTitle,
+  });
   let historyMatch = {
     value: "http://localhost:9000/search",
-    comment: "test visit for http://localhost:9000/search",
+    comment: historyTitle,
   };
 
-  // Use a normal alias and then one with an "@".  For the @ alias, the only
-  // matches should be the search suggestions -- no history matches.
-  for (let alias of ["moz", "@moz"]) {
-    engine.alias = alias;
-    Assert.equal(engine.alias, alias);
+  // Search in both a non-private and private context.
+  for (let private of [false, true]) {
+    let searchParam = "enable-actions";
+    if (private) {
+      searchParam += " private-window";
+    }
 
-    let expectedMatches = [
-      makeSearchMatch(`${alias} `, {
-        engineName: SUGGESTIONS_ENGINE_NAME,
-        alias,
-        searchQuery: "",
-        heuristic: true,
-      }),
-    ];
-    if (alias[0] != "@") {
-      expectedMatches.push(historyMatch);
-    }
-    await check_autocomplete({
-      search: alias,
-      searchParam: "enable-actions",
-      matches: expectedMatches,
-    });
+    // Use a normal alias and then one with an "@".  For the @ alias, the only
+    // matches should be the search suggestions -- no history matches.
+    for (let alias of ["moz", "@moz"]) {
+      engine.alias = alias;
+      Assert.equal(engine.alias, alias);
 
-    expectedMatches = [
-      makeSearchMatch(`${alias} `, {
-        engineName: SUGGESTIONS_ENGINE_NAME,
-        alias,
-        searchQuery: "",
-        heuristic: true,
-      }),
-    ];
-    if (alias[0] != "@") {
-      expectedMatches.push(historyMatch);
-    }
-    await check_autocomplete({
-      search: `${alias} `,
-      searchParam: "enable-actions",
-      matches: expectedMatches,
-    });
+      // Search for "alias"
+      let expectedMatches = [
+        makeSearchMatch(`${alias} `, {
+          engineName: SUGGESTIONS_ENGINE_NAME,
+          alias,
+          searchQuery: "",
+          heuristic: true,
+        }),
+      ];
+      if (alias[0] != "@") {
+        expectedMatches.push(historyMatch);
+      }
+      await check_autocomplete({
+        search: alias,
+        searchParam,
+        matches: expectedMatches,
+      });
 
-    await check_autocomplete({
-      search: `${alias} fire`,
-      searchParam: "enable-actions",
-      matches: [
-        makeSearchMatch(`${alias} fire`, {
+      // Search for "alias " (trailing space)
+      expectedMatches = [
+        makeSearchMatch(`${alias} `, {
           engineName: SUGGESTIONS_ENGINE_NAME,
           alias,
-          searchQuery: "fire",
+          searchQuery: "",
           heuristic: true,
         }),
-        makeSearchMatch(`${alias} fire foo`, {
+      ];
+      if (alias[0] != "@") {
+        expectedMatches.push(historyMatch);
+      }
+      await check_autocomplete({
+        search: `${alias} `,
+        searchParam,
+        matches: expectedMatches,
+      });
+
+      // Search for "alias historyTitle" -- Include the history title so that
+      // the history result is eligible to be shown.  Whether or not it's
+      // actually shown depends on the alias: If it's an @ alias, it shouldn't
+      // be shown.
+      expectedMatches = [
+        makeSearchMatch(`${alias} ${historyTitle}`, {
           engineName: SUGGESTIONS_ENGINE_NAME,
           alias,
-          searchQuery: "fire",
-          searchSuggestion: "fire foo",
+          searchQuery: historyTitle,
+          heuristic: true,
         }),
-        makeSearchMatch(`${alias} fire bar`, {
-          engineName: SUGGESTIONS_ENGINE_NAME,
-          alias,
-          searchQuery: "fire",
-          searchSuggestion: "fire bar",
-        }),
-      ],
-    });
+      ];
+      // Suggestions should be shown in a non-private context but not in a
+      // private context.
+      if (!private) {
+        expectedMatches.push(
+          makeSearchMatch(`${alias} ${historyTitle} foo`, {
+            engineName: SUGGESTIONS_ENGINE_NAME,
+            alias,
+            searchQuery: historyTitle,
+            searchSuggestion: `${historyTitle} foo`,
+          }),
+          makeSearchMatch(`${alias} ${historyTitle} bar`, {
+            engineName: SUGGESTIONS_ENGINE_NAME,
+            alias,
+            searchQuery: historyTitle,
+            searchSuggestion: `${historyTitle} bar`,
+          })
+        );
+      }
+      if (alias[0] != "@") {
+        expectedMatches.push(historyMatch);
+      }
+      await check_autocomplete({
+        search: `${alias} ${historyTitle}`,
+        searchParam,
+        matches: expectedMatches,
+      });
+    }
   }
 
   engine.alias = "";
   await cleanup();
 });
 
 
 // When the search is simply "@", the results should be a list of all the "@"
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -301,17 +301,19 @@
   },
   "COMPOSITE_TIME" : {
     "record_in_processes": ["main", "content", "gpu"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "description": "Composite times in milliseconds",
     "kind": "exponential",
     "high": 1000,
-    "n_buckets": 50
+    "n_buckets": 50,
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1080160, 1529352]
   },
   "COMPOSITE_FRAME_ROUNDTRIP_TIME" : {
     "record_in_processes": ["main", "content", "gpu"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "description": "Time from vsync to finishing a composite in milliseconds.",
     "kind": "exponential",
     "high": 1000,
@@ -540,17 +542,18 @@
     "description": "Pause time for asynchronous deferred finalization (ms)"
   },
   "DEVICE_RESET_REASON": {
     "record_in_processes": ["main", "content", "gpu"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
-    "bug_numbers": [1135408],
+    "bug_numbers": [1135408, 1529352],
+    "releaseChannelCollection": "opt-out",
     "description": "GPU Device Reset Reason (ok, hung, removed, reset, internal error, invalid call, out of memory)"
   },
   "FETCH_IS_MAINTHREAD": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "50",
     "kind": "boolean",
     "description": "Was Fetch request initiated from the main thread?"
   },
@@ -1526,17 +1529,18 @@
     "bug_numbers": [1272808]
   },
   "CANVAS_WEBGL_SUCCESS": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "WebGL1 creation success",
-    "bug_numbers": [1247327]
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1247327, 1529352]
   },
   "CANVAS_WEBGL_USED": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "WebGL canvas used"
   },
   "CANVAS_WEBGL2_SUCCESS": {
@@ -6130,21 +6134,21 @@
     "n_buckets": 20,
     "bug_numbers": [1156592, 1489524],
     "releaseChannelCollection": "opt-out",
     "description": "Firefox: Time in ms between tab selection and tab content paint in e10s windows"
   },
   "FX_TAB_SWITCH_COMPOSITE_E10S_MS": {
     "record_in_processes": ["main"],
     "alert_emails": ["mwoodrow@mozilla.com"],
-    "expires_in_version": "70",
+    "expires_in_version": "73",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 20,
-    "bug_numbers": [1481704],
+    "bug_numbers": [1481704, 1529352],
     "releaseChannelCollection": "opt-out",
     "description": "Firefox: Time in ms between tab selection and first composite of the tab content in e10s windows"
   },
   "FX_TAB_SWITCH_SPINNER_VISIBLE_MS": {
     "record_in_processes": ["main"],
     "alert_emails": ["mconley@mozilla.com", "dothayer@mozilla.com"],
     "expires_in_version": "70",
     "kind": "exponential",
@@ -14382,16 +14386,30 @@
     ],
     "expires_in_version": "71",
     "kind": "categorical",
     "labels": ["AddAllow", "RemoveAllow", "AddBlock", "RemoveBlock"],
     "bug_numbers": [1520361],
     "description": "The number of times the user adds a site to the allow list, removes a site from the allow list, adds a site to the block list and removes a site from the block list.",
     "releaseChannelCollection": "opt-out"
   },
+  "MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": [
+      "alwu@mozilla.com",
+      "cpearce@mozilla.com",
+      "nohlmeier@mozilla.com"
+    ],
+    "expires_in_version": "72",
+    "kind": "categorical",
+    "labels": ["VPlayedMoreThan7s", "VPlayedLessThan7s", "VPlayedToTheEnd", "APlayedMoreThan7s", "APlayedLessThan7s", "APlayedToTheEnd"],
+    "bug_numbers": [1525156],
+    "description": "The number of times autoplay media (audio and video), which has been blocked by autoplay policy, played exactly 7 seconds or more, or less than 7 seconds after it has been resumed from the blocked state. If media's duration is less than 7 seconds and it played to the end before user pauses it, it would be in another category which is different from 'play >= 7 seconds' and 'play < 7 seconds'.",
+    "releaseChannelCollection": "opt-out"
+  },
   "QM_REPOSITORIES_INITIALIZATION_TIME": {
     "record_in_processes": ["main"],
     "expires_in_version": "72",
     "bug_numbers": [1481716, 1523682],
     "kind": "exponential",
     "high": 30000,
     "n_buckets": 30,
     "releaseChannelCollection": "opt-out",
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -557,17 +557,16 @@
     "CERT_VALIDATION_HTTP_REQUEST_FAILED_TIME",
     "CERT_VALIDATION_HTTP_REQUEST_RESULT",
     "CERT_VALIDATION_HTTP_REQUEST_SUCCEEDED_TIME",
     "CHANGES_OF_DETECTED_LANGUAGE",
     "CHANGES_OF_TARGET_LANGUAGE",
     "CHECK_ADDONS_MODIFIED_MS",
     "COMPONENTS_SHIM_ACCESSED_BY_CONTENT",
     "COMPOSITE_FRAME_ROUNDTRIP_TIME",
-    "COMPOSITE_TIME",
     "CONTENT_DOCUMENTS_DESTROYED",
     "COOKIE_SCHEME_SECURITY",
     "CRASH_STORE_COMPRESSED_BYTES",
     "CYCLE_COLLECTOR",
     "CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING",
     "CYCLE_COLLECTOR_COLLECTED",
     "CYCLE_COLLECTOR_FINISH_IGC",
     "CYCLE_COLLECTOR_FULL",
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -87,31 +87,42 @@ def gecko_profiler_parse_elf(value, targ
 set_config('MOZ_GECKO_PROFILER_PARSE_ELF', gecko_profiler_parse_elf)
 set_define('MOZ_GECKO_PROFILER_PARSE_ELF', gecko_profiler_parse_elf)
 
 # enable this by default if the profiler is enabled
 # Note: also requires jemalloc
 set_config('MOZ_PROFILER_MEMORY', gecko_profiler_define)
 set_define('MOZ_PROFILER_MEMORY', gecko_profiler_define)
 
-option('--enable-dmd', env='MOZ_DMD',
-       help='Enable Dark Matter Detector (heap profiler). '
+
+@depends('--enable-debug', milestone, build_project,
+         # Artifact builds are included because the downloaded artifacts can
+         # have DMD enabled.
+         when=artifact_builds | depends(when='--enable-replace-malloc')(lambda: True))
+def dmd_default(debug, milestone, build_project):
+    return bool(build_project == 'browser' and (debug or milestone.is_nightly))
+
+
+option('--enable-dmd', env='MOZ_DMD', default=dmd_default,
+       help='{Enable|Disable} Dark Matter Detector (heap profiler). '
             'Also enables jemalloc, replace-malloc and profiling')
 
+
 @depends('--enable-dmd')
 def dmd(value):
     if value:
         return True
 
+
 set_config('MOZ_DMD', dmd)
 set_define('MOZ_DMD', dmd)
 add_old_configure_assignment('MOZ_DMD', dmd)
 imply_option('--enable-profiling', dmd)
-imply_option('--enable-jemalloc', dmd)
-imply_option('--enable-replace-malloc', dmd)
+imply_option('--enable-jemalloc', dmd, when=compile_environment)
+imply_option('--enable-replace-malloc', dmd, when=compile_environment)
 
 # ALSA cubeb backend
 # ==============================================================
 option('--enable-alsa', env='MOZ_ALSA',
        help='Enable ALSA audio backend.')
 
 alsa = pkg_check_modules('MOZ_ALSA', 'alsa', when='--enable-alsa')
 
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
@@ -27,20 +27,20 @@
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsContentUtils.h"
 #include "nsChromeRegistry.h"
 #include "nsIDOMWindowUtils.h"  // for nsIJSRAIIHelper
 #include "nsIFileURL.h"
 #include "nsIIOService.h"
-#include "nsIJARProtocolHandler.h"
 #include "nsIJARURI.h"
 #include "nsIStringEnumerator.h"
 #include "nsIZipReader.h"
+#include "nsJARProtocolHandler.h"
 #include "nsJSUtils.h"
 #include "nsReadableUtils.h"
 #include "nsXULAppAPI.h"
 
 #include <stdlib.h>
 
 namespace mozilla {
 
@@ -186,22 +186,20 @@ static bool ParseJSON(JSContext* cx, nsA
 
 static Result<nsCOMPtr<nsIZipReaderCache>, nsresult> GetJarCache() {
   nsCOMPtr<nsIIOService> ios = services::GetIOService();
   NS_ENSURE_TRUE(ios, Err(NS_ERROR_FAILURE));
 
   nsCOMPtr<nsIProtocolHandler> jarProto;
   MOZ_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto)));
 
-  nsCOMPtr<nsIJARProtocolHandler> jar = do_QueryInterface(jarProto);
+  auto jar = static_cast<nsJARProtocolHandler*>(jarProto.get());
   MOZ_ASSERT(jar);
 
-  nsCOMPtr<nsIZipReaderCache> zipCache;
-  MOZ_TRY(jar->GetJARCache(getter_AddRefs(zipCache)));
-
+  nsCOMPtr<nsIZipReaderCache> zipCache = jar->JarCache();
   return std::move(zipCache);
 }
 
 static Result<FileLocation, nsresult> GetFileLocation(nsIURI* uri) {
   FileLocation location;
 
   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
   nsCOMPtr<nsIFile> file;
--- a/toolkit/mozapps/update/common/updatedefines.h
+++ b/toolkit/mozapps/update/common/updatedefines.h
@@ -1,24 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef UPDATEDEFINES_H
 #define UPDATEDEFINES_H
 
+#include <stdio.h>
+#include <stdarg.h>
 #include "readstrings.h"
 
 #if defined(XP_WIN)
 #  include <windows.h>
 #  include <shlwapi.h>
 #  include <direct.h>
 #  include <io.h>
-#  include <stdio.h>
-#  include <stdarg.h>
 
 #  ifndef F_OK
 #    define F_OK 00
 #  endif
 #  ifndef W_OK
 #    define W_OK 02
 #  endif
 #  ifndef R_OK
@@ -36,28 +36,17 @@
 #  define DELETE_DIR L"tobedeleted"
 #  define CALLBACK_BACKUP_EXT L".moz-callback"
 
 #  define LOG_S "%S"
 #  define NS_CONCAT(x, y) x##y
 // The extra layer of indirection here allows this macro to be passed macros
 #  define NS_T(str) NS_CONCAT(L, str)
 #  define NS_SLASH NS_T('\\')
-
-static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt,
-                              ...) {
-  size_t _count = count - 1;
-  va_list varargs;
-  va_start(varargs, fmt);
-  int result = _vsnwprintf(dest, count - 1, fmt, varargs);
-  va_end(varargs);
-  dest[_count] = L'\0';
-  return result;
-}
-#  define NS_tsnprintf mywcsprintf
+#  define NS_tvsnprintf _vsnwprintf
 #  define NS_taccess _waccess
 #  define NS_tatoi _wtoi64
 #  define NS_tchdir _wchdir
 #  define NS_tchmod _wchmod
 #  define NS_tfopen _wfopen
 #  define NS_tmkdir(path, perms) _wmkdir(path)
 #  define NS_tpid __int64
 #  define NS_tremove _wremove
@@ -95,17 +84,17 @@ static inline int mywcsprintf(WCHAR* des
 
 #  ifdef XP_MACOSX
 #    include <sys/time.h>
 #  endif
 
 #  define LOG_S "%s"
 #  define NS_T(str) str
 #  define NS_SLASH NS_T('/')
-#  define NS_tsnprintf snprintf
+#  define NS_tvsnprintf vsnprintf
 #  define NS_taccess access
 #  define NS_tatoi atoi
 #  define NS_tchdir chdir
 #  define NS_tchmod chmod
 #  define NS_tfopen fopen
 #  define NS_tmkdir mkdir
 #  define NS_tpid int
 #  define NS_tremove remove
@@ -140,9 +129,21 @@ static inline int mywcsprintf(WCHAR* des
 #    define MAXPATHLEN _MAX_PATH
 #  elif defined(CCHMAXPATH)
 #    define MAXPATHLEN CCHMAXPATH
 #  else
 #    define MAXPATHLEN 1024
 #  endif
 #endif
 
+static inline bool NS_tsnprintf(NS_tchar* dest, size_t count,
+                                const NS_tchar* fmt, ...) {
+  va_list varargs;
+  va_start(varargs, fmt);
+  int result = NS_tvsnprintf(dest, count - 1, fmt, varargs);
+  va_end(varargs);
+  dest[count - 1] = NS_T('\0');
+  // The size_t cast of result is safe because result can only be positive after
+  // the first check.
+  return result >= 0 && (size_t)result < count;
+}
+
 #endif
--- a/toolkit/mozapps/update/tests/Makefile.in
+++ b/toolkit/mozapps/update/tests/Makefile.in
@@ -1,7 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+ifndef MOZ_WINCONSOLE
+ifdef MOZ_DEBUG
 MOZ_WINCONSOLE = 1
+else
+MOZ_WINCONSOLE = 0
+endif
+endif
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/mozapps/update/tests/TestAUSHelper.cpp
+++ b/toolkit/mozapps/update/tests/TestAUSHelper.cpp
@@ -1,91 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+#include "updatedefines.h"
+
 #ifdef XP_WIN
-#  include <windows.h>
-#  include <wintrust.h>
-#  include <tlhelp32.h>
-#  include <softpub.h>
-#  include <direct.h>
-#  include <io.h>
 #  include "commonupdatedir.h"
-typedef WCHAR NS_tchar;
+#  include "updatehelper.h"
+#  include "certificatecheck.h"
 #  define NS_main wmain
-#  ifndef F_OK
-#    define F_OK 00
-#  endif
-#  ifndef W_OK
-#    define W_OK 02
-#  endif
-#  ifndef R_OK
-#    define R_OK 04
-#  endif
-#  if defined(_MSC_VER) && _MSC_VER < 1900
-#    define stat _stat
-#  endif
-#  define NS_T(str) L##str
-#  define NS_tsnprintf(dest, count, fmt, ...)       \
-    {                                               \
-      int _count = count - 1;                       \
-      _snwprintf(dest, _count, fmt, ##__VA_ARGS__); \
-      dest[_count] = L'\0';                         \
-    }
-#  define NS_taccess _waccess
-#  define NS_tchdir _wchdir
-#  define NS_tfopen _wfopen
-#  define NS_tstrcmp wcscmp
+#  define NS_tgetcwd _wgetcwd
 #  define NS_ttoi _wtoi
-#  define NS_tstat _wstat
-#  define NS_tgetcwd _wgetcwd
-#  define LOG_S "%S"
-
-#  include "../common/updatehelper.h"
-#  include "../common/certificatecheck.h"
-
 #else
-#  include <unistd.h>
 #  define NS_main main
-typedef char NS_tchar;
-#  define NS_T(str) str
-#  define NS_tsnprintf snprintf
-#  define NS_taccess access
-#  define NS_tchdir chdir
-#  define NS_tfopen fopen
-#  define NS_tstrcmp strcmp
+#  define NS_tgetcwd getcwd
 #  define NS_ttoi atoi
-#  define NS_tstat stat
-#  define NS_tgetcwd getcwd
-#  define NS_tfputs fputs
-#  define LOG_S "%s"
 #endif
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#ifndef MAXPATHLEN
-#  ifdef PATH_MAX
-#    define MAXPATHLEN PATH_MAX
-#  elif defined(MAX_PATH)
-#    define MAXPATHLEN MAX_PATH
-#  elif defined(_MAX_PATH)
-#    define MAXPATHLEN _MAX_PATH
-#  elif defined(CCHMAXPATH)
-#    define MAXPATHLEN CCHMAXPATH
-#  else
-#    define MAXPATHLEN 1024
-#  endif
-#endif
-
 static void WriteMsg(const NS_tchar *path, const char *status) {
   FILE *outFP = NS_tfopen(path, NS_T("wb"));
   if (!outFP) {
     return;
   }
 
   fprintf(outFP, "%s\n", status);
   fclose(outFP);
@@ -137,46 +80,56 @@ int NS_main(int argc, NS_tchar **argv) {
     if (!NS_tstrcmp(argv[1], NS_T("post-update-async")) ||
         !NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
       NS_tchar exePath[MAXPATHLEN];
 #ifdef XP_WIN
       if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN)) {
         return 1;
       }
 #else
-      NS_tsnprintf(exePath, sizeof(exePath) / sizeof(exePath[0]), NS_T("%s"),
-                   argv[0]);
+      if (!NS_tsnprintf(exePath, sizeof(exePath) / sizeof(exePath[0]),
+                        NS_T("%s"), argv[0])) {
+        return 1;
+      }
 #endif
       NS_tchar runFilePath[MAXPATHLEN];
-      NS_tsnprintf(runFilePath, sizeof(runFilePath) / sizeof(runFilePath[0]),
-                   NS_T("%s.running"), exePath);
+      if (!NS_tsnprintf(runFilePath,
+                        sizeof(runFilePath) / sizeof(runFilePath[0]),
+                        NS_T("%s.running"), exePath)) {
+        return 1;
+      }
 #ifdef XP_WIN
       if (!NS_taccess(runFilePath, F_OK)) {
         // This makes it possible to check if the post update process was
         // launched twice which happens when the service performs an update.
         NS_tchar runFilePathBak[MAXPATHLEN];
-        NS_tsnprintf(runFilePathBak,
-                     sizeof(runFilePathBak) / sizeof(runFilePathBak[0]),
-                     NS_T("%s.bak"), runFilePath);
+        if (!NS_tsnprintf(runFilePathBak,
+                          sizeof(runFilePathBak) / sizeof(runFilePathBak[0]),
+                          NS_T("%s.bak"), runFilePath)) {
+          return 1;
+        }
         MoveFileExW(runFilePath, runFilePathBak, MOVEFILE_REPLACE_EXISTING);
       }
 #endif
       WriteMsg(runFilePath, "running");
 
       if (!NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
 #ifdef XP_WIN
         Sleep(2000);
 #else
         sleep(2);
 #endif
       }
 
       NS_tchar logFilePath[MAXPATHLEN];
-      NS_tsnprintf(logFilePath, sizeof(logFilePath) / sizeof(logFilePath[0]),
-                   NS_T("%s.log"), exePath);
+      if (!NS_tsnprintf(logFilePath,
+                        sizeof(logFilePath) / sizeof(logFilePath[0]),
+                        NS_T("%s.log"), exePath)) {
+        return 1;
+      }
       WriteMsg(logFilePath, "post-update");
       return 0;
     }
   }
 
   if (argc < 3) {
     fprintf(
         stderr,
@@ -229,38 +182,46 @@ int NS_main(int argc, NS_tchar **argv) {
     // Not implemented on non-Windows platforms
     return 1;
 #endif
   }
 
   if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
 #ifdef XP_UNIX
     NS_tchar path[MAXPATHLEN];
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
-                 NS_T("/tmp"), argv[2]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
+                      NS_T("/tmp"), argv[2])) {
+      return 1;
+    }
     if (mkdir(path, 0755)) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
-                 NS_T("/tmp"), argv[2], argv[3]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
+                      NS_T("/tmp"), argv[2], argv[3])) {
+      return 1;
+    }
     if (mkdir(path, 0755)) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s/%s"),
-                 NS_T("/tmp"), argv[2], argv[3], argv[4]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s/%s"),
+                      NS_T("/tmp"), argv[2], argv[3], argv[4])) {
+      return 1;
+    }
     FILE *file = NS_tfopen(path, NS_T("w"));
     if (file) {
-      NS_tfputs(NS_T("test"), file);
+      fputs(NS_T("test"), file);
       fclose(file);
     }
     if (symlink(path, argv[5]) != 0) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
-                 NS_T("/tmp"), argv[2]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
+                      NS_T("/tmp"), argv[2])) {
+      return 1;
+    }
     if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
       if (chmod(path, 0644)) {
         return 1;
       }
     }
     return 0;
 #else
     // Not implemented on non-Unix platforms
@@ -270,33 +231,41 @@ int NS_main(int argc, NS_tchar **argv) {
 
   if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
 #ifdef XP_UNIX
     // The following can be called at the start of a test in case these symlinks
     // need to be removed if they already exist and at the end of a test to
     // remove the symlinks created by the test so ignore file doesn't exist
     // errors.
     NS_tchar path[MAXPATHLEN];
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
-                 NS_T("/tmp"), argv[2]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
+                      NS_T("/tmp"), argv[2])) {
+      return 1;
+    }
     if (chmod(path, 0755) && errno != ENOENT) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s/%s"),
-                 NS_T("/tmp"), argv[2], argv[3], argv[4]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s/%s"),
+                      NS_T("/tmp"), argv[2], argv[3], argv[4])) {
+      return 1;
+    }
     if (unlink(path) && errno != ENOENT) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
-                 NS_T("/tmp"), argv[2], argv[3]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s/%s"),
+                      NS_T("/tmp"), argv[2], argv[3])) {
+      return 1;
+    }
     if (rmdir(path) && errno != ENOENT) {
       return 1;
     }
-    NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
-                 NS_T("/tmp"), argv[2]);
+    if (!NS_tsnprintf(path, sizeof(path) / sizeof(path[0]), NS_T("%s/%s"),
+                      NS_T("/tmp"), argv[2])) {
+      return 1;
+    }
     if (rmdir(path) && errno != ENOENT) {
       return 1;
     }
     return 0;
 #else
     // Not implemented on non-Unix platforms
     return 1;
 #endif
@@ -397,21 +366,25 @@ int NS_main(int argc, NS_tchar **argv) {
   }
 
   // File in use test helper section
   if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
     // Note: glibc's getcwd() allocates the buffer dynamically using malloc(3)
     // if buf (the 1st param) is NULL so free cwd when it is no longer needed.
     NS_tchar *cwd = NS_tgetcwd(nullptr, 0);
     NS_tchar inFilePath[MAXPATHLEN];
-    NS_tsnprintf(inFilePath, sizeof(inFilePath) / sizeof(inFilePath[0]),
-                 NS_T("%s/%s"), cwd, argv[2]);
+    if (!NS_tsnprintf(inFilePath, sizeof(inFilePath) / sizeof(inFilePath[0]),
+                      NS_T("%s/%s"), cwd, argv[2])) {
+      return 1;
+    }
     NS_tchar outFilePath[MAXPATHLEN];
-    NS_tsnprintf(outFilePath, sizeof(outFilePath) / sizeof(outFilePath[0]),
-                 NS_T("%s/%s"), cwd, argv[3]);
+    if (!NS_tsnprintf(outFilePath, sizeof(outFilePath) / sizeof(outFilePath[0]),
+                      NS_T("%s/%s"), cwd, argv[3])) {
+      return 1;
+    }
     free(cwd);
 
     int seconds = NS_ttoi(argv[5]);
 #ifdef XP_WIN
     HANDLE hFile = INVALID_HANDLE_VALUE;
     if (argc == 7) {
       hFile = CreateFileW(argv[6], DELETE | GENERIC_WRITE, 0, nullptr,
                           OPEN_EXISTING, 0, nullptr);
@@ -439,18 +412,20 @@ int NS_main(int argc, NS_tchar **argv) {
 #endif
     WriteMsg(outFilePath, "finished");
     return 0;
   }
 
   {
     // Command line argument test helper section
     NS_tchar logFilePath[MAXPATHLEN];
-    NS_tsnprintf(logFilePath, sizeof(logFilePath) / sizeof(logFilePath[0]),
-                 NS_T("%s"), argv[2]);
+    if (!NS_tsnprintf(logFilePath, sizeof(logFilePath) / sizeof(logFilePath[0]),
+                      NS_T("%s"), argv[2])) {
+      return 1;
+    }
 
     FILE *logFP = NS_tfopen(logFilePath, NS_T("wb"));
     if (!logFP) {
       return 1;
     }
     for (int i = 1; i < argc; ++i) {
       fprintf(logFP, LOG_S "\n", argv[i]);
     }
--- a/toolkit/mozapps/update/tests/moz.build
+++ b/toolkit/mozapps/update/tests/moz.build
@@ -48,16 +48,19 @@ DEFINES['NS_NO_XPCOM'] = True
 DisableStlWrapping()
 
 if CONFIG['MOZ_MAINTENANCE_SERVICE']:
     DEFINES['MOZ_MAINTENANCE_SERVICE'] = CONFIG['MOZ_MAINTENANCE_SERVICE']
 
 # For debugging purposes only
 #DEFINES['DISABLE_UPDATER_AUTHENTICODE_CHECK'] = True
 
+if CONFIG['CC_TYPE'] == 'clang-cl':
+    WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
+
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
     USE_STATIC_LIBS = True
     if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
         WIN32_EXE_LDFLAGS += ['-municode']
 
 TEST_HARNESS_FILES.testing.mochitest.browser.toolkit.mozapps.update.tests.browser += [
@@ -102,12 +105,8 @@ FINAL_TARGET_FILES += [
 ]
 
 FINAL_TARGET_PP_FILES += [
     'data/xpcshellConstantsPP.js',
 ]
 
 with Files("browser/browser_TelemetryUpdatePing.js"):
     BUG_COMPONENT = ("Toolkit", "Telemetry")
-
-if CONFIG['CC_TYPE'] == 'gcc':
-    CXXFLAGS += ['-Wno-format-truncation']
-
--- a/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
+++ b/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
@@ -7,26 +7,26 @@
 
 XPCSHELLTESTDIR = $(topobjdir)/_tests/xpcshell/toolkit/mozapps/update/tests
 MOCHITESTCHROMEDIR = $(topobjdir)/_tests/testing/mochitest/chrome/toolkit/mozapps/update/tests/chrome
 
 ifeq (,$(MOZ_SUITE)$(MOZ_THUNDERBIRD))
 MOCHITESTBROWSERDIR = $(topobjdir)/_tests/testing/mochitest/browser/toolkit/mozapps/update/tests/browser
 endif
 
-include $(topsrcdir)/config/rules.mk
-
 ifndef MOZ_WINCONSOLE
 ifdef MOZ_DEBUG
 MOZ_WINCONSOLE = 1
 else
 MOZ_WINCONSOLE = 0
 endif
 endif
 
+include $(topsrcdir)/config/rules.mk
+
 tools::
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 	# Copy for xpcshell tests
 	$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
 	rsync -a -C --exclude '*.in' $(srcdir)/../macbuild/Contents $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
 	sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/../macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
 	  iconv -f UTF-8 -t UTF-16 > $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings
 	$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/MacOS
--- a/toolkit/themes/osx/reftests/reftest.list
+++ b/toolkit/themes/osx/reftests/reftest.list
@@ -1,5 +1,7 @@
-skip-if(!cocoaWidget) == 482681.xul 482681-ref.xul
-skip-if(!cocoaWidget) == radiosize.xul radiosize-ref.xul
-skip-if(!cocoaWidget) == checkboxsize.xul checkboxsize-ref.xul
-skip-if(!cocoaWidget) == baseline.xul about:blank
-skip-if(!cocoaWidget) == nostretch.xul nostretch-ref.xul
+# This folder is registered in the chrome manifest at layout/tools/reftest/jar.mn.
+
+skip-if(!cocoaWidget) == chrome://reftest/content/osx-theme/482681.xul chrome://reftest/content/osx-theme/482681-ref.xul
+skip-if(!cocoaWidget) == chrome://reftest/content/osx-theme/radiosize.xul chrome://reftest/content/osx-theme/radiosize-ref.xul
+skip-if(!cocoaWidget) == chrome://reftest/content/osx-theme/checkboxsize.xul chrome://reftest/content/osx-theme/checkboxsize-ref.xul
+skip-if(!cocoaWidget) == chrome://reftest/content/osx-theme/baseline.xul about:blank
+skip-if(!cocoaWidget) == chrome://reftest/content/osx-theme/nostretch.xul chrome://reftest/content/osx-theme/nostretch-ref.xul
--- a/xpcom/ds/PLDHashTable.cpp
+++ b/xpcom/ds/PLDHashTable.cpp
@@ -130,16 +130,19 @@ static inline uint32_t MinLoad(uint32_t 
 
 // Compute the minimum capacity (and the Log2 of that capacity) for a table
 // containing |aLength| elements while respecting the following contraints:
 // - table must be at most 75% full;
 // - capacity must be a power of two;
 // - capacity cannot be too small.
 static inline void BestCapacity(uint32_t aLength, uint32_t* aCapacityOut,
                                 uint32_t* aLog2CapacityOut) {
+  // Callers should ensure this is true.
+  MOZ_ASSERT(aLength <= PLDHashTable::kMaxInitialLength);
+
   // Compute the smallest capacity allowing |aLength| elements to be inserted
   // without rehashing.
   uint32_t capacity = (aLength * 4 + (3 - 1)) / 3;  // == ceil(aLength * 4 / 3)
   if (capacity < PLDHashTable::kMinCapacity) {
     capacity = PLDHashTable::kMinCapacity;
   }
 
   // Round up capacity to next power-of-two.
--- a/xpcom/ds/nsHashPropertyBag.cpp
+++ b/xpcom/ds/nsHashPropertyBag.cpp
@@ -9,16 +9,26 @@
 #include "nsArrayEnumerator.h"
 #include "nsIVariant.h"
 #include "nsIProperty.h"
 #include "nsThreadUtils.h"
 #include "nsVariant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 
+extern "C" {
+
+// This function uses C linkage because it's exposed to Rust to support the
+// `HashPropertyBag` wrapper in the `storage_variant` crate.
+void NS_NewHashPropertyBag(nsIWritablePropertyBag** aBag) {
+  MakeRefPtr<nsHashPropertyBag>().forget(aBag);
+}
+
+}  // extern "C"
+
 /*
  * nsHashPropertyBagBase implementation.
  */
 
 NS_IMETHODIMP
 nsHashPropertyBagBase::HasKey(const nsAString& aName, bool* aResult) {
   *aResult = mPropertyHash.Get(aName, nullptr);
   return NS_OK;