Merge mozilla-central to mozilla-inbound
authorarthur.iakab <aiakab@mozilla.com>
Fri, 19 Apr 2019 12:49:24 +0300
changeset 470179 c06f27cbfe40d9f05272813d63680b8e73b1e370
parent 470058 f1455cdc78edbbc11dda951ef0944e3cd387fb12 (current diff)
parent 470178 0160424142d14988f7b30595e03f156c31278a42 (diff)
child 470194 590157f23b85a1cfcd6d3e910fbf4baefec5f3c6
push id112843
push useraiakab@mozilla.com
push dateFri, 19 Apr 2019 09:50:22 +0000
treeherdermozilla-inbound@c06f27cbfe40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/base/content/test/general/browser_scope.js
devtools/client/netmonitor/src/components/RequestListItem.js
layout/style/SheetType.h
testing/web-platform/meta/uievents/click/auxclick_event.html.ini
testing/web-platform/meta/xhr/overridemimetype-unsent-state-force-shiftjis.any.js.ini
testing/web-platform/meta/xhr/responsexml-document-properties.htm.ini
--- a/accessible/tests/mochitest/editabletext/editabletext.js
+++ b/accessible/tests/mochitest/editabletext/editabletext.js
@@ -27,22 +27,22 @@ function editableTextTestRun() {
 /**
  * Used to test nsIEditableTextAccessible methods.
  */
 function editableTextTest(aID) {
   /**
    * Schedule a test, the given function with its arguments will be executed
    * when preceding test is complete.
    */
-  this.scheduleTest = function scheduleTest(aFunc) {
+  this.scheduleTest = function scheduleTest(aFunc, ...aFuncArgs) {
     // A data container acts like a dummy invoker, it's never invoked but
     // it's used to generate real invoker when previous invoker was handled.
     var dataContainer = {
       func: aFunc,
-      funcArgs: Array.slice(arguments, 1),
+      funcArgs: aFuncArgs,
     };
     this.mEventQueue.push(dataContainer);
 
     if (!this.mEventQueueReady) {
       this.unwrapNextTest();
       this.mEventQueueReady = true;
     }
   };
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -255,29 +255,31 @@ void XULMenuitemAccessible::ActionNameAt
 
 uint8_t XULMenuitemAccessible::ActionCount() const { return 1; }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULMenuitemAccessible: Widgets
 
 bool XULMenuitemAccessible::IsActiveWidget() const {
   // Parent menu item is a widget, it's active when its popup is open.
-  nsIContent* menuPopupContent = mContent->GetFirstChild();
+  // Typically the <menupopup> is included in the document markup, and
+  // <menu> prepends content in front of it.
+  nsIContent* menuPopupContent = mContent->GetLastChild();
   if (menuPopupContent) {
     nsMenuPopupFrame* menuPopupFrame =
         do_QueryFrame(menuPopupContent->GetPrimaryFrame());
     return menuPopupFrame && menuPopupFrame->IsOpen();
   }
   return false;
 }
 
 bool XULMenuitemAccessible::AreItemsOperable() const {
   // Parent menu item is a widget, its items are operable when its popup is
   // open.
-  nsIContent* menuPopupContent = mContent->GetFirstChild();
+  nsIContent* menuPopupContent = mContent->GetLastChild();
   if (menuPopupContent) {
     nsMenuPopupFrame* menuPopupFrame =
         do_QueryFrame(menuPopupContent->GetPrimaryFrame());
     return menuPopupFrame && menuPopupFrame->IsOpen();
   }
   return false;
 }
 
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -14,17 +14,28 @@ ChromeUtils.defineModuleGetter(this, "Pr
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 ChromeUtils.defineModuleGetter(this, "E10SUtils",
                                "resource://gre/modules/E10SUtils.jsm");
 
 class ClickHandlerChild extends ActorChild {
   handleEvent(event) {
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
+    if (!event.isTrusted || event.defaultPrevented || event.button == 2 ||
+        (event.type == "click" && event.button == 1)) {
+      return;
+    }
+    // Don't do anything on editable things, we shouldn't open links in
+    // contenteditables, and editor needs to possibly handle middlemouse paste
+    let composedTarget = event.composedTarget;
+    if (composedTarget.isContentEditable ||
+        (composedTarget.ownerDocument &&
+         composedTarget.ownerDocument.designMode == "on") ||
+        ChromeUtils.getClassName(composedTarget) == "HTMLInputElement" ||
+        ChromeUtils.getClassName(composedTarget) == "HTMLTextAreaElement") {
       return;
     }
 
     let originalTarget = event.originalTarget;
     let ownerDoc = originalTarget.ownerDocument;
     if (!ownerDoc) {
       return;
     }
--- a/browser/actors/WebRTCChild.jsm
+++ b/browser/actors/WebRTCChild.jsm
@@ -215,16 +215,25 @@ function prompt(aContentWindow, aWindowI
     setupPendingListsInitially(aContentWindow);
   }
   aContentWindow.pendingGetUserMediaRequests.set(aCallID, devices);
 
   // Record third party origins for telemetry.
   let isThirdPartyOrigin =
     aContentWindow.document.location.origin != aContentWindow.top.document.location.origin;
 
+  // WebRTC prompts have a bunch of special requirements, such as being able to
+  // grant two permissions (microphone and camera), selecting devices and showing
+  // a screen sharing preview. All this could have probably been baked into
+  // nsIContentPermissionRequest prompts, but the team that implemented this back
+  // then chose to just build their own prompting mechanism instead.
+  //
+  // So, what you are looking at here is not a real nsIContentPermissionRequest, but
+  // something that looks really similar and will be transmitted to webrtcUI.jsm
+  // for showing the prompt.
   let request = {
     callID: aCallID,
     windowID: aWindowID,
     origin: aContentWindow.origin,
     documentURI: aContentWindow.document.documentURI,
     secure: aSecure,
     isHandlingUserInput: aIsHandlingUserInput,
     isThirdPartyOrigin,
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1555100575898" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1555503879816" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2811,16 +2811,28 @@
     <emItem blockID="371796e4-387a-4dd0-9ddc-47ba1dd85be7" id="{132cb2fd-a6ae-45d2-84cf-b48d591f037d}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="3a921aa8-d44a-4272-be63-0fd102577f59" id="{d8b03707-e39f-4b17-8e56-56354fb14af5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="94621e2f-28a0-4b18-97c6-5f6203f5912e" id="{8ee8602c-aba6-4e2a-9faa-1724c3f4f9ba}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="881d3476-f18a-4560-b065-cded406783d2" id="{40cd7fd2-a3e4-43f9-9d86-0e0a70376203}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="8aff4cb0-4d5f-4e74-8db7-b04f616c3b60" id="{674fff65-6cd0-488a-9453-fb91fc3d7397}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -414,16 +414,20 @@ pref("permissions.default.shortcuts", 0)
 #ifdef NIGHTLY_BUILD
 pref("permissions.desktop-notification.postPrompt.enabled", true);
 #else
 pref("permissions.desktop-notification.postPrompt.enabled", false);
 #endif
 
 pref("permissions.postPrompt.animate", true);
 
+// This is meant to be enabled only for studies, not for
+// permanent data collection on any channel.
+pref("permissions.eventTelemetry.enabled", false);
+
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
 
 // handle external links (i.e. links opened from a different application)
 // default: use browser.link.open_newwindow
 // 1-3: see browser.link.open_newwindow for interpretation
 pref("browser.link.open_newwindow.override.external", -1);
@@ -1852,20 +1856,16 @@ pref("fission.frontend.simulate-messages
 
 // Curve25519 public keys for Prio servers
 pref("prio.publicKeyA", "35AC1C7576C7C6EDD7FED6BCFC337B34D48CB4EE45C86BEEFB40BD8875707733");
 pref("prio.publicKeyB", "26E6674E65425B823F1F1D5F96E3BB3EF9E406EC7FBA7DEF8B08A35DD135AF50");
 
 // Coverage ping is disabled by default.
 pref("toolkit.coverage.enabled", false);
 pref("toolkit.coverage.endpoint.base", "https://coverage.mozilla.org");
-// Whether Prio-encoded Telemetry will be sent in the prio ping.
-#if defined(NIGHTLY_BUILD)
-pref("toolkit.telemetry.prioping.enabled", true);
-#endif
 
 // Discovery prefs
 pref("browser.discovery.enabled", true);
 pref("browser.discovery.containers.enabled", true);
 pref("browser.discovery.sites", "addons.mozilla.org");
 
 pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds)
 
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -15,21 +15,23 @@ ChromeUtils.defineModuleGetter(this, "Up
                                "resource://gre/modules/UpdateUtils.jsm");
 
 const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
 const PREF_APP_UPDATE_ELEVATE_NEVER    = "app.update.elevate.never";
 
 var gAppUpdater;
 
 function onUnload(aEvent) {
-  if (gAppUpdater.isChecking)
-    gAppUpdater.checker.stopCurrentCheck();
-  // Safe to call even when there isn't a download in progress.
-  gAppUpdater.removeDownloadListener();
-  gAppUpdater = null;
+  if (gAppUpdater) {
+    if (gAppUpdater.isChecking)
+      gAppUpdater.checker.stopCurrentCheck();
+    // Safe to call even when there isn't a download in progress.
+    gAppUpdater.removeDownloadListener();
+    gAppUpdater = null;
+  }
 }
 
 
 function appUpdater(options = {}) {
   XPCOMUtils.defineLazyServiceGetter(this, "aus",
                                      "@mozilla.org/updates/update-service;1",
                                      "nsIApplicationUpdateService");
   XPCOMUtils.defineLazyServiceGetter(this, "checker",
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -284,17 +284,18 @@ var ctrlTab = {
       aPreview.hidden = true;
       aPreview._label.removeAttribute("value");
       aPreview.removeAttribute("tooltiptext");
       aPreview._favicon.removeAttribute("src");
     }
   },
 
   advanceFocus: function ctrlTab_advanceFocus(aForward) {
-    let selectedIndex = Array.indexOf(this.previews, this.selected);
+    let selectedIndex = Array.prototype.indexOf.call(this.previews,
+      this.selected);
     do {
       selectedIndex += aForward ? 1 : -1;
       if (selectedIndex < 0)
         selectedIndex = this.previews.length - 1;
       else if (selectedIndex >= this.previews.length)
         selectedIndex = 0;
     } while (this.previews[selectedIndex].hidden);
 
@@ -560,17 +561,17 @@ var ctrlTab = {
     for (let i = 0; i < thumbnailCount; i++)
       urls.push(this.tabList[i].linkedBrowser.currentURI.spec);
 
     aCallback(urls);
   },
 
   _initRecentlyUsedTabs() {
     this._recentlyUsedTabs =
-      Array.filter(gBrowser.tabs, tab => !tab.closing)
+      Array.prototype.filter.call(gBrowser.tabs, tab => !tab.closing)
            .sort((tab1, tab2) => tab2.lastAccessed - tab1.lastAccessed);
   },
 
   _init: function ctrlTab__init(enable) {
     var toggleEventListener = enable ? "addEventListener" : "removeEventListener";
 
     window[toggleEventListener]("SSWindowRestored", this);
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6276,17 +6276,17 @@ function hrefAndLinkNodeForClickEvent(ev
  *
  * @param event
  *        The click event.
  * @param isPanelClick
  *        Whether the event comes from an extension panel.
  * @note default event is prevented if the click is handled.
  */
 function contentAreaClick(event, isPanelClick) {
-  if (!event.isTrusted || event.defaultPrevented || event.button == 2)
+  if (!event.isTrusted || event.defaultPrevented || event.button != 0)
     return;
 
   let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
   if (!href) {
     // Not a link, handle middle mouse navigation.
     if (event.button == 1 &&
         Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
         !Services.prefs.getBoolPref("general.autoScroll")) {
@@ -7667,21 +7667,16 @@ const gRemoteControl = {
     if (enabled) {
       mainWindow.setAttribute("remotecontrol", "true");
     } else {
       mainWindow.removeAttribute("remotecontrol");
     }
   },
 };
 
-/* DEPRECATED */
-function getBrowser() {
-  return gBrowser;
-}
-
 const gAccessibilityServiceIndicator = {
   init() {
     // Pref to enable accessibility service indicator.
     Services.prefs.addObserver("accessibility.indicator.enabled", this);
     // Accessibility service init/shutdown event.
     Services.obs.addObserver(this, "a11y-init-or-shutdown");
     this._update(Services.appinfo.accessibilityEnabled);
   },
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -710,16 +710,18 @@ nsContextMenu.prototype = {
       this.setItemAttr("context-media-playbackrate-200x", "disabled", hasError);
       this.setItemAttr("context-media-showcontrols", "disabled", hasError);
       this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
       if (this.onVideo) {
         let canSaveSnapshot = !this.onDRMMedia && this.target.readyState >= this.target.HAVE_CURRENT_DATA;
         this.setItemAttr("context-video-saveimage", "disabled", !canSaveSnapshot);
         this.setItemAttr("context-video-fullscreen", "disabled", hasError);
         this.setItemAttr("context-video-pictureinpicture", "checked", this.onPiPVideo);
+        this.setItemAttr("context-video-pictureinpicture", "disabled",
+                         !this.onPiPVideo && hasError);
       }
     }
     this.showItem("context-media-sep-commands", onMedia);
   },
 
   initClickToPlayItems() {
     this.showItem("context-ctp-play", this.onCTPPlugin);
     this.showItem("context-ctp-hide", this.onCTPPlugin);
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -96,20 +96,20 @@ pageInfoTreeView.prototype = {
         this,
         this.data,
         treecol.index,
         function textComparator(a, b) { return (a || "").toLowerCase().localeCompare((b || "").toLowerCase()); },
         this.sortcol,
         this.sortdir
       );
 
-    Array.forEach(tree.columns, function(col) {
+    for (let col of tree.columns) {
       col.element.removeAttribute("sortActive");
       col.element.removeAttribute("sortDirection");
-    });
+    }
     treecol.element.setAttribute("sortActive", "true");
     treecol.element.setAttribute("sortDirection", this.sortdir ?
                                                   "ascending" : "descending");
 
     this.sortcol = treecol.index;
   },
 
   getRowProperties(row) { return ""; },
@@ -190,20 +190,20 @@ gImageView.onPageMediaSort = function(co
       this,
       this.data,
       index,
       comparator,
       this.sortcol,
       this.sortdir
     );
 
-  Array.forEach(tree.columns, function(col) {
+  for (let col of tree.columns) {
     col.element.removeAttribute("sortActive");
     col.element.removeAttribute("sortDirection");
-  });
+  }
   treecol.element.setAttribute("sortActive", "true");
   treecol.element.setAttribute("sortDirection", this.sortdir ?
                                                 "ascending" : "descending");
 
   this.sortcol = index;
 };
 
 var gImageHash = { };
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -175,17 +175,17 @@
             this.setAttribute("overflow", "true");
             this._positionPinnedTabs();
             this._handleTabSelect(true);
           });
 
           // Override scrollbox.xml method, since our scrollbox's children are
           // inherited from the scrollbox binding parent (this).
           arrowScrollbox._getScrollableElements = () => {
-            return Array.filter(this.children, arrowScrollbox._canScrollToElement);
+            return Array.prototype.filter.call(this.children, arrowScrollbox._canScrollToElement);
           };
           arrowScrollbox._canScrollToElement = tab => {
             return !tab._pinnedUnscrollable && !tab.hidden;
           };
         ]]></body>
       </method>
 
       <method name="observe">
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -356,18 +356,16 @@ skip-if = (e10s && debug && os == "win")
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_link_when_window_navigates.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_video.js]
 skip-if = (os == 'mac') || (verify && (os == 'mac')) || (os == 'win' && debug) || (os =='linux') #Bug 1212419
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_save_video_frame.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_scope.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_contentSearchUI.js]
 support-files =
   contentSearchUI.html
   contentSearchUI.js
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_selectTabAtIndex.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_ssl_error_reports.js]
--- a/browser/base/content/test/general/browser_bug406216.js
+++ b/browser/base/content/test/general/browser_bug406216.js
@@ -32,17 +32,17 @@ function addTab(aURI, aIndex) {
 
 function doTabsTest() {
   is(gBrowser.tabs.length, URIS.length, "Correctly opened all expected tabs");
 
   // sample of "close related tabs" feature
   gBrowser.tabContainer.addEventListener("TabClose", function(event) {
     var closedTab = event.originalTarget;
     var scheme = closedTab.linkedBrowser.currentURI.scheme;
-    Array.slice(gBrowser.tabs).forEach(function(aTab) {
+    Array.from(gBrowser.tabs).forEach(function(aTab) {
       if (aTab != closedTab && aTab.linkedBrowser.currentURI.scheme == scheme)
         gBrowser.removeTab(aTab, {skipPermitUnload: true});
     });
   }, {capture: true, once: true});
 
   gBrowser.removeTab(gBrowser.tabs[0], {skipPermitUnload: true});
   is(gBrowser.tabs.length, 1, "Related tabs are not closed unexpectedly");
 
--- a/browser/base/content/test/general/browser_bug533232.js
+++ b/browser/base/content/test/general/browser_bug533232.js
@@ -27,10 +27,10 @@ function test() {
   gBrowser.removeTab(gBrowser.selectedTab, { skipPermitUnload: true });
   is(idx(gBrowser.selectedTab), idx(tab2),
      "closing the last tab in a set of child tabs doesn't go back to the parent");
 
   gBrowser.removeTab(tab2, { skipPermitUnload: true });
 }
 
 function idx(tab) {
-  return Array.indexOf(gBrowser.tabs, tab);
+  return Array.prototype.indexOf.call(gBrowser.tabs, tab);
 }
--- a/browser/base/content/test/general/browser_contentAreaClick.js
+++ b/browser/base/content/test/general/browser_contentAreaClick.js
@@ -118,46 +118,49 @@ var gTests = [
     preventDefault: true,
   },
 
   {
     desc: "Simple middle click opentab",
     setup() {},
     clean() {},
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ],
     expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ],
     preventDefault: true,
   },
 
   {
     desc: "Simple middle click openwin",
     setup() {
       Services.prefs.setBoolPref("browser.tabs.opentabfor.middleclick", false);
     },
     clean() {
       Services.prefs.clearUserPref("browser.tabs.opentabfor.middleclick");
     },
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ],
     expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ],
     preventDefault: true,
   },
 
   {
     desc: "Middle mouse paste",
     setup() {
       Services.prefs.setBoolPref("middlemouse.contentLoadURL", true);
       Services.prefs.setBoolPref("general.autoScroll", false);
     },
     clean() {
       Services.prefs.clearUserPref("middlemouse.contentLoadURL");
       Services.prefs.clearUserPref("general.autoScroll");
     },
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "emptylink" ],
     expectedInvokedMethods: [ "middleMousePaste" ],
     preventDefault: true,
   },
 
 ];
 
 // Array of method names that will be replaced in the new window.
@@ -203,19 +206,23 @@ function test() {
       }, gTestWin.content, true);
     }, gTestWin);
   });
 }
 
 // Click handler used to steal click events.
 var gClickHandler = {
   handleEvent(event) {
+    if (event.type == "click" && event.button != 0) {
+      return;
+    }
     let linkId = event.target.id || event.target.localName;
-    is(event.type, "click",
-       gCurrentTest.desc + ":Handler received a click event on " + linkId);
+    let wantedEvent = gCurrentTest.wantedEvent || "click";
+    is(event.type, wantedEvent,
+       `${gCurrentTest.desc}:Handler received a ${wantedEvent} event on ${linkId}`);
 
     let isPanelClick = linkId == "panellink";
     gTestWin.contentAreaClick(event, isPanelClick);
     let prevent = event.defaultPrevented;
     is(prevent, gCurrentTest.preventDefault,
        gCurrentTest.desc + ": event.defaultPrevented is correct (" + prevent + ")");
 
     // Check that all required methods have been called.
@@ -236,16 +243,17 @@ var gClickHandler = {
 
     executeSoon(runNextTest);
   },
 };
 
 function setupTestBrowserWindow() {
   // Steal click events and don't propagate them.
   gTestWin.addEventListener("click", gClickHandler, true);
+  gTestWin.addEventListener("auxclick", gClickHandler, true);
 
   // Replace methods.
   gReplacedMethods.forEach(function(methodName) {
     let targetObj = methodName == "getShortcutOrURIAndPostData" ? UrlbarUtils : gTestWin;
     sinon.stub(targetObj, methodName).returnsArg(0);
   });
 
   // Inject links in content.
@@ -281,20 +289,21 @@ function runNextTest() {
   }
 
   // Move to next target.
   sinon.resetHistory();
   let target = gCurrentTest.targets.shift();
 
   info(gCurrentTest.desc + ": testing " + target);
 
-  // Fire click event.
+  // Fire (aux)click event.
   let targetElt = gTestWin.content.document.getElementById(target);
   ok(targetElt, gCurrentTest.desc + ": target is valid (" + targetElt.id + ")");
   EventUtils.synthesizeMouseAtCenter(targetElt, gCurrentTest.event, gTestWin.content);
 }
 
 function finishTest() {
   info("Restoring browser...");
   gTestWin.removeEventListener("click", gClickHandler, true);
+  gTestWin.removeEventListener("auxclick", gClickHandler, true);
   gTestWin.close();
   finish();
 }
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -194,16 +194,17 @@ add_task(async function test_canvas() {
     }
   );
 });
 
 add_task(async function test_video_ok() {
   await SpecialPowers.pushPrefEnv({
     set: [["media.videocontrols.picture-in-picture.enabled", true]],
   });
+
   await test_contextmenu("#test-video-ok",
     ["context-media-play",             true,
      "context-media-mute",             true,
      "context-media-playbackrate",     null,
          ["context-media-playbackrate-050x", true,
           "context-media-playbackrate-100x", true,
           "context-media-playbackrate-125x", true,
           "context-media-playbackrate-150x", true,
@@ -216,18 +217,23 @@ add_task(async function test_video_ok() 
      "context-viewvideo",              true,
      "context-copyvideourl",           true,
      "---",                            null,
      "context-savevideo",              true,
      "context-video-saveimage",        true,
      "context-sendvideo",              true,
     ]
   );
+
   await SpecialPowers.popPrefEnv();
 
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", false]],
+  });
+
   await test_contextmenu("#test-video-ok",
     ["context-media-play",             true,
      "context-media-mute",             true,
      "context-media-playbackrate",     null,
          ["context-media-playbackrate-050x", true,
           "context-media-playbackrate-100x", true,
           "context-media-playbackrate-125x", true,
           "context-media-playbackrate-150x", true,
@@ -239,16 +245,18 @@ add_task(async function test_video_ok() 
      "context-viewvideo",              true,
      "context-copyvideourl",           true,
      "---",                            null,
      "context-savevideo",              true,
      "context-video-saveimage",        true,
      "context-sendvideo",              true,
     ]
   );
+
+  await SpecialPowers.popPrefEnv();
 });
 
 add_task(async function test_audio_in_video() {
   await test_contextmenu("#test-audio-in-video",
     ["context-media-play",         true,
      "context-media-mute",         true,
      "context-media-playbackrate", null,
          ["context-media-playbackrate-050x", true,
@@ -263,16 +271,49 @@ add_task(async function test_audio_in_vi
      "---",                        null,
      "context-saveaudio",          true,
      "context-sendaudio",          true,
     ]
   );
 });
 
 add_task(async function test_video_bad() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", true]],
+  });
+
+  await test_contextmenu("#test-video-bad",
+    ["context-media-play",         false,
+     "context-media-mute",         false,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", false,
+          "context-media-playbackrate-100x", false,
+          "context-media-playbackrate-125x", false,
+          "context-media-playbackrate-150x", false,
+          "context-media-playbackrate-200x", false], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", false,
+     "context-video-fullscreen",   false,
+     "context-video-pictureinpicture", false,
+     "---",                        null,
+     "context-viewvideo",          true,
+     "context-copyvideourl",       true,
+     "---",                        null,
+     "context-savevideo",          true,
+     "context-video-saveimage",    false,
+     "context-sendvideo",          true,
+    ]
+  );
+
+  await SpecialPowers.popPrefEnv();
+
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", false]],
+  });
+
   await test_contextmenu("#test-video-bad",
     ["context-media-play",         false,
      "context-media-mute",         false,
      "context-media-playbackrate", null,
          ["context-media-playbackrate-050x", false,
           "context-media-playbackrate-100x", false,
           "context-media-playbackrate-125x", false,
           "context-media-playbackrate-150x", false,
@@ -284,19 +325,54 @@ add_task(async function test_video_bad()
      "context-viewvideo",          true,
      "context-copyvideourl",       true,
      "---",                        null,
      "context-savevideo",          true,
      "context-video-saveimage",    false,
      "context-sendvideo",          true,
     ]
   );
+
+  await SpecialPowers.popPrefEnv();
 });
 
 add_task(async function test_video_bad2() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", true]],
+  });
+
+  await test_contextmenu("#test-video-bad2",
+    ["context-media-play",         false,
+     "context-media-mute",         false,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", false,
+          "context-media-playbackrate-100x", false,
+          "context-media-playbackrate-125x", false,
+          "context-media-playbackrate-150x", false,
+          "context-media-playbackrate-200x", false], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", false,
+     "context-video-fullscreen",   false,
+     "context-video-pictureinpicture", false,
+     "---",                        null,
+     "context-viewvideo",          false,
+     "context-copyvideourl",       false,
+     "---",                        null,
+     "context-savevideo",          false,
+     "context-video-saveimage",    false,
+     "context-sendvideo",          false,
+    ]
+  );
+
+  await SpecialPowers.popPrefEnv();
+
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", false]],
+  });
+
   await test_contextmenu("#test-video-bad2",
     ["context-media-play",         false,
      "context-media-mute",         false,
      "context-media-playbackrate", null,
          ["context-media-playbackrate-050x", false,
           "context-media-playbackrate-100x", false,
           "context-media-playbackrate-125x", false,
           "context-media-playbackrate-150x", false,
@@ -308,16 +384,18 @@ add_task(async function test_video_bad2(
      "context-viewvideo",          false,
      "context-copyvideourl",       false,
      "---",                        null,
      "context-savevideo",          false,
      "context-video-saveimage",    false,
      "context-sendvideo",          false,
     ]
   );
+
+  await SpecialPowers.popPrefEnv();
 });
 
 add_task(async function test_iframe() {
   await test_contextmenu("#test-iframe",
     ["context-navigation", null,
          ["context-back",         false,
           "context-forward",      false,
           "context-reload",       true,
@@ -347,16 +425,61 @@ add_task(async function test_iframe() {
      "---",                  null,
      "context-viewsource",   true,
      "context-viewinfo",     true,
     ]
   );
 });
 
 add_task(async function test_video_in_iframe() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", true]],
+  });
+
+  await test_contextmenu("#test-video-in-iframe",
+    ["context-media-play",         true,
+     "context-media-mute",         true,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", true,
+          "context-media-playbackrate-100x", true,
+          "context-media-playbackrate-125x", true,
+          "context-media-playbackrate-150x", true,
+          "context-media-playbackrate-200x", true], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", true,
+     "context-video-fullscreen",   true,
+     "context-video-pictureinpicture", true,
+     "---",                        null,
+     "context-viewvideo",          true,
+     "context-copyvideourl",       true,
+     "---",                        null,
+     "context-savevideo",          true,
+     "context-video-saveimage",    true,
+     "context-sendvideo",          true,
+     "frame",                null,
+         ["context-showonlythisframe", true,
+          "context-openframeintab",    true,
+          "context-openframe",         true,
+          "---",                       null,
+          "context-reloadframe",       true,
+          "---",                       null,
+          "context-bookmarkframe",     true,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframeinfo",     true], null]
+  );
+
+  await SpecialPowers.popPrefEnv();
+
+  await SpecialPowers.pushPrefEnv({
+    set: [["media.videocontrols.picture-in-picture.enabled", false]],
+  });
+
   await test_contextmenu("#test-video-in-iframe",
     ["context-media-play",         true,
      "context-media-mute",         true,
      "context-media-playbackrate", null,
          ["context-media-playbackrate-050x", true,
           "context-media-playbackrate-100x", true,
           "context-media-playbackrate-125x", true,
           "context-media-playbackrate-150x", true,
@@ -380,16 +503,18 @@ add_task(async function test_video_in_if
           "---",                       null,
           "context-bookmarkframe",     true,
           "context-saveframe",         true,
           "---",                       null,
           "context-printframe",        true,
           "---",                       null,
           "context-viewframeinfo",     true], null]
   );
+
+  await SpecialPowers.popPrefEnv();
 });
 
 add_task(async function test_audio_in_iframe() {
   await test_contextmenu("#test-audio-in-iframe",
     ["context-media-play",         true,
      "context-media-mute",         true,
      "context-media-playbackrate", null,
          ["context-media-playbackrate-050x", true,
--- a/browser/base/content/test/general/browser_contextmenu_childprocess.js
+++ b/browser/base/content/test/general/browser_contextmenu_childprocess.js
@@ -27,17 +27,17 @@ function checkItems(menuitem, arr) {
   for (let i = 0; i < arr.length; i += 2) {
     let str = arr[i];
     let details = arr[i + 1];
     if (str == "---") {
       is(menuitem.localName, "menuseparator", "menuseparator");
     } else if ("children" in details) {
       is(menuitem.localName, "menu", "submenu");
       is(menuitem.getAttribute("label"), str, str + " label");
-      checkItems(menuitem.firstElementChild.firstElementChild, details.children);
+      checkItems(menuitem.menupopup.firstElementChild, details.children);
     } else {
       is(menuitem.localName, "menuitem", str + " menuitem");
 
       is(menuitem.getAttribute("label"), str, str + " label");
       is(menuitem.getAttribute("type"), details.type, str + " type");
       is(menuitem.getAttribute("image"), details.icon ? gBaseURL + details.icon : "", str + " icon");
 
       if (details.checked)
--- a/browser/base/content/test/general/browser_duplicateIDs.js
+++ b/browser/base/content/test/general/browser_duplicateIDs.js
@@ -1,8 +1,8 @@
 function test() {
   var ids = {};
-  Array.forEach(document.querySelectorAll("[id]"), function(node) {
+  Array.prototype.forEach.call(document.querySelectorAll("[id]"), function(node) {
     var id = node.id;
     ok(!(id in ids), id + " should be unique");
     ids[id] = null;
   });
 }
--- a/browser/base/content/test/general/browser_page_style_menu.js
+++ b/browser/base/content/test/general/browser_page_style_menu.js
@@ -24,17 +24,17 @@ add_task(async function() {
 
   items = items.map(el => ({
     label: el.getAttribute("label"),
     checked: el.getAttribute("checked") == "true",
   }));
 
   let validLinks = await ContentTask.spawn(gBrowser.selectedBrowser, items, function(contentItems) {
     let contentValidLinks = 0;
-    Array.forEach(content.document.querySelectorAll("link, style"), function(el) {
+    Array.prototype.forEach.call(content.document.querySelectorAll("link, style"), function(el) {
       var title = el.getAttribute("title");
       var rel = el.getAttribute("rel");
       var media = el.getAttribute("media");
       var idstring = el.nodeName + " " + (title ? title : "without title and") +
                      " with rel=\"" + rel + "\"" +
                      (media ? " and media=\"" + media + "\"" : "");
 
       var item = contentItems.filter(aItem => aItem.label == title);
--- a/browser/base/content/test/general/browser_relatedTabs.js
+++ b/browser/base/content/test/general/browser_relatedTabs.js
@@ -35,17 +35,17 @@ add_task(async function() {
   gBrowser.removeTab(tabs.pop());
   await addTab("about:blank", gBrowser.currentURI);
   gBrowser.moveTabTo(gBrowser.selectedTab, 1);
   await addTab("http://mochi.test:8888/#6", gBrowser.currentURI);
   await addTab();
   await addTab("http://mochi.test:8888/#7");
 
   function testPosition(tabNum, expectedPosition, msg) {
-    is(Array.indexOf(gBrowser.tabs, tabs[tabNum]), expectedPosition, msg);
+    is(Array.prototype.indexOf.call(gBrowser.tabs, tabs[tabNum]), expectedPosition, msg);
   }
 
   testPosition(0, 3, "tab without referrer was opened to the far right");
   testPosition(1, 7, "tab without referrer was opened to the far right");
   testPosition(2, 5, "tab with referrer opened immediately to the right");
   testPosition(3, 1, "next tab with referrer opened further to the right");
   testPosition(4, 4, "tab selection changed, tab opens immediately to the right");
   testPosition(5, 6, "blank tab with referrer opens to the right of 3rd original tab where removed tab was");
deleted file mode 100644
--- a/browser/base/content/test/general/browser_scope.js
+++ /dev/null
@@ -1,4 +0,0 @@
-function test() {
-  ok(!!gBrowser, "gBrowser exists");
-  is(gBrowser, getBrowser(), "both ways of getting tabbrowser work");
-}
--- a/browser/base/content/test/general/browser_visibleTabs.js
+++ b/browser/base/content/test/general/browser_visibleTabs.js
@@ -60,17 +60,17 @@ add_task(async function() {
   gBrowser.tabContainer.advanceSelectedTab(-1, true);
   is(gBrowser.selectedTab, testTab, "going backwards to last tab");
   gBrowser.tabContainer.advanceSelectedTab(-1, true);
   is(gBrowser.selectedTab, pinned, "next to pinned");
   gBrowser.tabContainer.advanceSelectedTab(-1, true);
   is(gBrowser.selectedTab, testTab, "next to test tab again");
 
   // Try showing all tabs
-  gBrowser.showOnlyTheseTabs(Array.slice(gBrowser.tabs));
+  gBrowser.showOnlyTheseTabs(Array.from(gBrowser.tabs));
   is(gBrowser.visibleTabs.length, 3, "all 3 tabs are visible again");
 
   // Select the pinned tab and show the testTab to make sure selection updates
   gBrowser.selectedTab = pinned;
   gBrowser.showOnlyTheseTabs([testTab]);
   is(gBrowser.tabs[1], origTab, "make sure origTab is in the middle");
   is(origTab.hidden, true, "make sure it's hidden");
   gBrowser.removeTab(pinned);
--- a/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js
+++ b/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js
@@ -17,15 +17,15 @@ function test() {
 
     let uris = PlacesCommandHook.uniqueCurrentPages;
     is(uris.length, 1, "Only one uri is returned");
 
     is(uris[0].uri.spec, tabTwo.linkedBrowser.currentURI.spec, "It's the correct URI");
 
     gBrowser.removeTab(tabOne);
     gBrowser.removeTab(tabTwo);
-    Array.forEach(gBrowser.tabs, function(tab) {
+    for (let tab of gBrowser.tabs) {
       gBrowser.showTab(tab);
-    });
+    }
 
     finish();
   });
 }
--- a/browser/base/content/test/pageActions/browser_page_action_menu.js
+++ b/browser/base/content/test/pageActions/browser_page_action_menu.js
@@ -896,12 +896,12 @@ function checkSendToDeviceItems(expected
         Assert.equal(attrVal, expected.attrs[name]);
       }
     }
   }
 }
 
 function collectContextMenuItems() {
   let contextMenu = document.getElementById("pageActionContextMenu");
-  return Array.filter(contextMenu.children, node => {
+  return Array.prototype.filter.call(contextMenu.children, node => {
     return window.getComputedStyle(node).visibility == "visible";
   });
 }
--- a/browser/base/content/test/pageActions/browser_page_action_menu_add_search_engine.js
+++ b/browser/base/content/test/pageActions/browser_page_action_menu_add_search_engine.js
@@ -113,17 +113,17 @@ add_task(async function many() {
     EventUtils.synthesizeMouseAtCenter(button, {});
     let view = await viewPromise;
     let viewID =
        BrowserPageActions._panelViewNodeIDForActionID("addSearchEngine", false);
     Assert.equal(view.id, viewID, "View ID");
     let bodyID = viewID + "-body";
     let body = document.getElementById(bodyID);
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
 
@@ -142,17 +142,17 @@ add_task(async function many() {
 
     // Open the panel and show the subview again.  The installed engine should
     // be gone.
     await promisePageActionPanelOpen();
     viewPromise = promisePageActionViewShown();
     EventUtils.synthesizeMouseAtCenter(button, {});
     await viewPromise;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
 
     // Click the next engine to install it.
@@ -245,17 +245,17 @@ add_task(async function many() {
     Assert.equal(button.label, expectedTitle, "Button label");
     Assert.equal(button.classList.contains("subviewbutton-nav"), true,
                  "Button should expand into a subview");
     viewPromise = promisePageActionViewShown();
     EventUtils.synthesizeMouseAtCenter(button, {});
     await viewPromise;
     body = document.getElementById(bodyID);
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
       ],
       "Subview children"
     );
     EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
     await promisePageActionPanelHidden();
@@ -267,17 +267,17 @@ add_task(async function many() {
     await enginePromise;
 
     // Open the panel again and check the subview.
     await promisePageActionPanelOpen();
     viewPromise = promisePageActionViewShown();
     EventUtils.synthesizeMouseAtCenter(button, {});
     await viewPromise;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
     EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
@@ -363,17 +363,17 @@ add_task(async function urlbarMany() {
     // it should contain the addSearchEngine subview.
     EventUtils.synthesizeMouseAtCenter(button, {});
     let view = await waitForActivatedActionPanel();
     let viewID =
        BrowserPageActions._panelViewNodeIDForActionID("addSearchEngine", true);
     Assert.equal(view.id, viewID, "View ID");
     let body = view.firstElementChild;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
 
@@ -391,17 +391,17 @@ add_task(async function urlbarMany() {
     let feedbackText = await feedbackPromise;
     Assert.equal(feedbackText, "Search engine added!", "Feedback text");
 
     // Open the panel again.  The installed engine should be gone.
     EventUtils.synthesizeMouseAtCenter(button, {});
     view = await waitForActivatedActionPanel();
     body = view.firstElementChild;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
 
     // Click the next engine to install it.
@@ -456,17 +456,17 @@ add_task(async function urlbarMany() {
     await enginePromise;
 
     // Open the panel again and check the subview.  The subview should be
     // present now that there are two offered engines again.
     EventUtils.synthesizeMouseAtCenter(button, {});
     view = await waitForActivatedActionPanel();
     body = view.firstElementChild;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
       ],
       "Subview children"
     );
 
     // Hide the panel.
@@ -481,17 +481,17 @@ add_task(async function urlbarMany() {
     await Services.search.removeEngine(engines.shift());
     await enginePromise;
 
     // Open the panel again and check the subview.
     EventUtils.synthesizeMouseAtCenter(button, {});
     view = await waitForActivatedActionPanel();
     body = view.firstElementChild;
     Assert.deepEqual(
-      Array.map(body.children, n => n.label),
+      Array.from(body.children, n => n.label),
       [
         "page_action_menu_add_search_engine_0",
         "page_action_menu_add_search_engine_1",
         "page_action_menu_add_search_engine_2",
       ],
       "Subview children"
     );
 
--- a/browser/base/content/test/permissions/browser.ini
+++ b/browser/base/content/test/permissions/browser.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 support-files=
   head.js
   permissions.html
 
 [browser_canvas_fingerprinting_resistance.js]
 skip-if = debug || os == "linux" && asan # Bug 1522069
 [browser_permissions.js]
+[browser_permissions_event_telemetry.js]
 [browser_permissions_postPrompt.js]
 support-files=
   dummy.js
 [browser_permissions_handling_user_input.js]
 support-files=
   dummy.js
 [browser_reservedkey.js]
 [browser_temporary_permissions.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/permissions/browser_permissions_event_telemetry.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const ORIGIN = "https://example.com";
+const PERMISSIONS_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) + "permissions.html";
+
+async function showPermissionPrompt(browser) {
+  let popupshown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+
+  await ContentTask.spawn(browser, null, function() {
+    E10SUtils.wrapHandlingUserInput(content, true, () => {
+      // We need to synthesize the click instead of calling .click(),
+      // otherwise the document will not correctly register the user gesture.
+      let EventUtils = ContentTaskUtils.getEventUtils(content);
+      let notificationButton = content.document.getElementById("desktop-notification");
+      EventUtils.synthesizeMouseAtCenter(notificationButton, {isSynthesized: false}, content);
+    });
+  });
+
+  await popupshown;
+
+  ok(true, "Notification permission prompt was shown");
+}
+
+function checkEventTelemetry(method) {
+  let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_ALL_CHANNELS, true).parent;
+  events = events.filter(
+    e => e[1] == "security.ui.permissionprompt" && e[2] == method && e[3] == "notifications");
+  is(events.length, 1, "recorded telemetry for showing the prompt");
+  ok(typeof events[0][4] == "string", "recorded a hashed and salted variant of the domain");
+  is(events[0][4].length * 4, 256, "hash is a 256 bit string");
+  ok(!events[0][4].includes("example.com"), "we're not including the domain by accident");
+
+  // We assume that even the slowest infra machines are able to show
+  // a permission prompt within five minutes.
+  const FIVE_MINUTES = 1000 * 60 * 5;
+
+  let timeOnPage = Number(events[0][5].timeOnPage);
+  let lastInteraction = Number(events[0][5].lastInteraction);
+  ok(timeOnPage > 0 && timeOnPage < FIVE_MINUTES, `Has recorded time on page (${timeOnPage})`);
+  is(events[0][5].hasUserInput, "true", "Has recorded user input");
+  is(events[0][5].allPermsDenied, "3", "Has recorded total denied permissions");
+  is(events[0][5].allPermsGranted, method == "accept" ? "3" : "2", "Has recorded total granted permissions");
+  is(events[0][5].thisPermDenied, "0", "Has recorded denied notification permissions");
+  is(events[0][5].thisPermGranted, method == "accept" ? "2" : "1",
+    "Has recorded granted notification permissions");
+  is(events[0][5].docHasUserInput, "true", "Has recorded user input on document");
+  ok(lastInteraction > (Date.now() - FIVE_MINUTES) &&
+     lastInteraction < Date.now(), `Has recorded last user input time (${lastInteraction})`);
+}
+
+add_task(async function setup() {
+  let oldCanRecord = Services.telemetry.canRecordExtended;
+  Services.telemetry.canRecordExtended = true;
+
+  Services.prefs.setBoolPref("permissions.eventTelemetry.enabled", true);
+
+  // Add some example permissions.
+  let uri = Services.io.newURI(PERMISSIONS_PAGE);
+  let uri2 = Services.io.newURI("https://example.org");
+  let uri3 = Services.io.newURI("http://sub.example.org");
+  Services.perms.add(uri, "geo", Services.perms.ALLOW_ACTION);
+  Services.perms.add(uri3, "desktop-notification", Services.perms.ALLOW_ACTION);
+  Services.perms.add(uri2, "microphone", Services.perms.DENY_ACTION);
+  Services.perms.add(uri, "camera", Services.perms.DENY_ACTION);
+  Services.perms.add(uri2, "geo", Services.perms.DENY_ACTION);
+
+  registerCleanupFunction(() => {
+    Services.perms.removeAll();
+    Services.prefs.clearUserPref("permissions.eventTelemetry.enabled");
+    Services.telemetry.canRecordExtended = oldCanRecord;
+  });
+
+  Services.telemetry.clearEvents();
+});
+
+add_task(async function testAccept() {
+  await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(browser) {
+    await showPermissionPrompt(browser);
+
+    checkEventTelemetry("show");
+
+    let notification = PopupNotifications.panel.firstElementChild;
+    EventUtils.synthesizeMouseAtCenter(notification.button, {});
+
+    checkEventTelemetry("accept");
+
+    Services.telemetry.clearEvents();
+    Services.perms.remove(Services.io.newURI(PERMISSIONS_PAGE), "desktop-notification");
+  });
+});
+
+add_task(async function testDeny() {
+  await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(browser) {
+    await showPermissionPrompt(browser);
+
+    checkEventTelemetry("show");
+
+    let notification = PopupNotifications.panel.firstElementChild;
+    EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
+
+    checkEventTelemetry("deny");
+
+    Services.telemetry.clearEvents();
+    Services.perms.remove(Services.io.newURI(PERMISSIONS_PAGE), "desktop-notification");
+  });
+});
+
+add_task(async function testLeave() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PERMISSIONS_PAGE);
+  await showPermissionPrompt(tab.linkedBrowser);
+
+  checkEventTelemetry("show");
+
+  let tabClosed = BrowserTestUtils.waitForTabClosing(tab);
+  await BrowserTestUtils.removeTab(tab);
+  await tabClosed;
+
+  checkEventTelemetry("leave");
+
+  Services.telemetry.clearEvents();
+  Services.perms.remove(Services.io.newURI(PERMISSIONS_PAGE), "desktop-notification");
+});
--- a/browser/base/content/test/plugins/browser_private_clicktoplay.js
+++ b/browser/base/content/test/plugins/browser_private_clicktoplay.js
@@ -16,17 +16,17 @@ function finishTest() {
     gPrivateWindow.close();
   }
   window.focus();
 }
 
 let createPrivateWindow = async function createPrivateWindow(url) {
   gPrivateWindow = await BrowserTestUtils.openNewBrowserWindow({private: true});
   ok(!!gPrivateWindow, "should have created a private window.");
-  gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
+  gPrivateBrowser = gPrivateWindow.gBrowser.selectedBrowser;
 
   BrowserTestUtils.loadURI(gPrivateBrowser, url);
   await BrowserTestUtils.browserLoaded(gPrivateBrowser, false, url);
   info("loaded " + url);
 };
 
 add_task(async function test() {
   registerCleanupFunction(function() {
--- a/browser/base/content/test/popupNotifications/head.js
+++ b/browser/base/content/test/popupNotifications/head.js
@@ -212,17 +212,17 @@ function checkPopup(popup, notifyObj) {
     let secondaryAction = notifyObj.secondaryActions[0];
     is(notification.getAttribute("secondarybuttonlabel"), secondaryAction.label,
        "secondary action label matches");
     is(notification.getAttribute("secondarybuttonaccesskey"),
        secondaryAction.accessKey, "secondary action accesskey matches");
   }
   // Additional secondary actions appear as menu items.
   let actualExtraSecondaryActions =
-    Array.filter(notification.menupopup.childNodes, child => child.nodeName == "menuitem");
+    Array.prototype.filter.call(notification.menupopup.childNodes, child => child.nodeName == "menuitem");
   let extraSecondaryActions = notifyObj.secondaryActions ? notifyObj.secondaryActions.slice(1) : [];
   is(actualExtraSecondaryActions.length, extraSecondaryActions.length,
      "number of extra secondary actions matches");
   extraSecondaryActions.forEach(function(a, i) {
     is(actualExtraSecondaryActions[i].getAttribute("label"), a.label,
        "label for extra secondary action " + i + " matches");
     is(actualExtraSecondaryActions[i].getAttribute("accesskey"), a.accessKey,
        "accessKey for extra secondary action " + i + " matches");
--- a/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
+++ b/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
@@ -42,19 +42,19 @@ add_task(async function test_simple() {
 
     let { gIdentityHandler } = gBrowser.ownerGlobal;
     let promisePanelOpen = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
     gIdentityHandler._identityBox.click();
     await promisePanelOpen;
 
     // Messages should be visible when the scheme is HTTP, and invisible when
     // the scheme is HTTPS.
-    is(Array.every(document.getElementById("identity-popup-mainView")
-                           .querySelectorAll("[when-loginforms=insecure]"),
-                   element => !BrowserTestUtils.is_hidden(element)),
+    is(Array.prototype.every.call(document.getElementById("identity-popup-mainView")
+                                          .querySelectorAll("[when-loginforms=insecure]"),
+                                  element => !BrowserTestUtils.is_hidden(element)),
        expectWarning,
        "The relevant messages should be visible or hidden in the main view.");
 
     let promiseViewShown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "ViewShown");
     document.getElementById("identity-popup-security-expander").click();
     await promiseViewShown;
 
     if (expectWarning) {
@@ -80,19 +80,19 @@ add_task(async function test_simple() {
          "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
          "Using expected icon image in the Control Center subview");
       ok(!BrowserTestUtils.is_hidden(document.getElementById("identity-popup-insecure-login-forms-learn-more")),
          "The 'Learn more' link should be visible.");
     }
 
     // Messages should be visible when the scheme is HTTP, and invisible when
     // the scheme is HTTPS.
-    is(Array.every(document.getElementById("identity-popup-securityView")
-                           .querySelectorAll("[when-loginforms=insecure]"),
-                   element => !BrowserTestUtils.is_hidden(element)),
+    is(Array.prototype.every.call(document.getElementById("identity-popup-securityView")
+                                          .querySelectorAll("[when-loginforms=insecure]"),
+                                  element => !BrowserTestUtils.is_hidden(element)),
        expectWarning,
        "The relevant messages should be visible or hidden in the security view.");
 
     if (gIdentityHandler._identityPopup.state != "closed") {
       let hideEvent = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
       info("hiding popup");
       gIdentityHandler._identityPopup.hidePopup();
       await hideEvent;
@@ -156,19 +156,19 @@ add_task(async function test_ignoring_wi
     await stateChangePromise;
 
     // Open the identity popup.
     let { gIdentityHandler } = gBrowser.ownerGlobal;
     let promisePanelOpen = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
     gIdentityHandler._identityBox.click();
     await promisePanelOpen;
 
-    ok(Array.every(document.getElementById("identity-popup-mainView")
-                           .querySelectorAll("[when-loginforms=insecure]"),
-                   element => BrowserTestUtils.is_hidden(element)),
+    ok(Array.prototype.every.call(document.getElementById("identity-popup-mainView")
+                                          .querySelectorAll("[when-loginforms=insecure]"),
+                                  element => BrowserTestUtils.is_hidden(element)),
        "All messages should be hidden in the main view.");
 
     let promiseViewShown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "ViewShown");
     document.getElementById("identity-popup-security-expander").click();
     await promiseViewShown;
 
     ok(BrowserTestUtils.is_visible(document.getElementById("connection-icon")),
        "Connection icon is visible");
@@ -190,19 +190,19 @@ add_task(async function test_ignoring_wi
        "Using expected icon image in the identity block");
     is(securityViewBG,
        "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
        "Using expected icon image in the Control Center main view");
     is(securityContentBG,
        "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
        "Using expected icon image in the Control Center subview");
 
-    ok(Array.every(document.getElementById("identity-popup-securityView")
-                           .querySelectorAll("[when-loginforms=insecure]"),
-                   element => BrowserTestUtils.is_hidden(element)),
+    ok(Array.prototype.every.call(document.getElementById("identity-popup-securityView")
+                                          .querySelectorAll("[when-loginforms=insecure]"),
+                                  element => BrowserTestUtils.is_hidden(element)),
        "All messages should be hidden in the security view.");
 
     if (gIdentityHandler._identityPopup.state != "closed") {
       info("hiding popup");
       let hideEvent = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
       gIdentityHandler._identityPopup.hidePopup();
       await hideEvent;
     }
--- a/browser/base/content/test/siteIdentity/head.js
+++ b/browser/base/content/test/siteIdentity/head.js
@@ -221,19 +221,19 @@ async function assertMixedContentBlockin
         "CC using degraded icon");
     }
   }
 
   if (activeLoaded || activeBlocked || passiveLoaded) {
     let promiseViewShown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "ViewShown");
     doc.getElementById("identity-popup-security-expander").click();
     await promiseViewShown;
-    is(Array.filter(doc.getElementById("identity-popup-securityView")
-                       .querySelectorAll(".identity-popup-mcb-learn-more"),
-                    element => !BrowserTestUtils.is_hidden(element)).length, 1,
+    is(Array.prototype.filter.call(doc.getElementById("identity-popup-securityView")
+                                      .querySelectorAll(".identity-popup-mcb-learn-more"),
+                                   element => !BrowserTestUtils.is_hidden(element)).length, 1,
        "The 'Learn more' link should be visible once.");
   }
 
   if (gIdentityHandler._identityPopup.state != "closed") {
     let hideEvent = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
     info("Hiding identity popup");
     gIdentityHandler._identityPopup.hidePopup();
     await hideEvent;
--- a/browser/base/content/test/tabs/browser_pinnedTabs.js
+++ b/browser/base/content/test/tabs/browser_pinnedTabs.js
@@ -1,12 +1,12 @@
 var tabs;
 
 function index(tab) {
-  return Array.indexOf(gBrowser.tabs, tab);
+  return Array.prototype.indexOf.call(gBrowser.tabs, tab);
 }
 
 function indexTest(tab, expectedIndex, msg) {
   var diag = "tab " + tab + " should be at index " + expectedIndex;
   if (msg)
     msg = msg + " (" + diag + ")";
   else
     msg = diag;
--- a/browser/base/content/test/tabs/browser_pinnedTabs_clickOpen.js
+++ b/browser/base/content/test/tabs/browser_pinnedTabs_clickOpen.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 function index(tab) {
-  return Array.indexOf(gBrowser.tabs, tab);
+  return Array.prototype.indexOf.call(gBrowser.tabs, tab);
 }
 
 async function testNewTabPosition(expectedPosition, modifiers = {}) {
   let opening = BrowserTestUtils.waitForNewTab(gBrowser, "http://mochi.test:8888/");
   BrowserTestUtils.synthesizeMouseAtCenter("#link", modifiers, gBrowser.selectedBrowser);
   let newtab = await opening;
   is(index(newtab), expectedPosition, "clicked tab is in correct position");
   return newtab;
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -72,16 +72,17 @@ let ACTORS = {
     },
   },
 
   ClickHandler: {
     child: {
       module: "resource:///actors/ClickHandlerChild.jsm",
       events: {
         "click": {capture: true, mozSystemGroup: true},
+        "auxclick": {capture: true, mozSystemGroup: true},
       },
     },
   },
 
   ContextMenu: {
     child: {
       module: "resource:///actors/ContextMenuChild.jsm",
       events: {
@@ -990,16 +991,17 @@ BrowserGlue.prototype = {
     os.removeObserver(this, "profile-before-change");
     os.removeObserver(this, "keyword-search");
     os.removeObserver(this, "browser-search-engine-modified");
     os.removeObserver(this, "flash-plugin-hang");
     os.removeObserver(this, "xpi-signature-changed");
     os.removeObserver(this, "sync-ui-state:update");
     os.removeObserver(this, "shield-init-complete");
 
+    Services.prefs.removeObserver("permissions.eventTelemetry.enabled", this._togglePermissionPromptTelemetry);
     Services.prefs.removeObserver("privacy.trackingprotection", this._matchCBCategory);
     Services.prefs.removeObserver("network.cookie.cookieBehavior", this._matchCBCategory);
     Services.prefs.removeObserver(ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY, this._updateCBCategory);
     Services.prefs.removeObserver("browser.contentblocking.features.standard", this._setPrefExpectations);
     Services.prefs.removeObserver("browser.contentblocking.features.strict", this._setPrefExpectations);
   },
 
   // runs on startup, before the first command line handler is invoked
@@ -1353,16 +1355,30 @@ BrowserGlue.prototype = {
   _matchCBCategory() {
     ContentBlockingCategoriesPrefs.matchCBCategory();
   },
 
   _updateCBCategory() {
     ContentBlockingCategoriesPrefs.updateCBCategory();
   },
 
+  _togglePermissionPromptTelemetry() {
+    let enablePermissionPromptTelemetry =
+      Services.prefs.getBoolPref("permissions.eventTelemetry.enabled", false);
+
+    Services.telemetry.setEventRecordingEnabled("security.ui.permissionprompt",
+      enablePermissionPromptTelemetry);
+
+    if (!enablePermissionPromptTelemetry) {
+      // Remove the saved unique identifier to reduce the (remote) chance
+      // of leaking it to our servers in the future.
+      Services.prefs.clearUserPref("permissions.eventTelemetry.uuid");
+    }
+  },
+
   _recordContentBlockingTelemetry() {
     let recordIdentityPopupEvents = Services.prefs.getBoolPref("security.identitypopup.recordEventElemetry");
     Services.telemetry.setEventRecordingEnabled("security.ui.identitypopup", recordIdentityPopupEvents);
 
     let tpEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled");
     Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED").add(tpEnabled);
 
     let tpPBDisabled = Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled");
@@ -1594,16 +1610,21 @@ BrowserGlue.prototype = {
     Services.tm.idleDispatchToMainThread(() => {
       let enableCertErrorUITelemetry =
         Services.prefs.getBoolPref("security.certerrors.recordEventTelemetry", false);
       Services.telemetry.setEventRecordingEnabled("security.ui.certerror",
         enableCertErrorUITelemetry);
     });
 
     Services.tm.idleDispatchToMainThread(() => {
+      Services.prefs.addObserver("permissions.eventTelemetry.enabled", this._togglePermissionPromptTelemetry);
+      this._togglePermissionPromptTelemetry();
+    });
+
+    Services.tm.idleDispatchToMainThread(() => {
       this._recordContentBlockingTelemetry();
     });
 
     // Load the Login Manager data from disk off the main thread, some time
     // after startup.  If the data is required before this runs, for example
     // because a restored page contains a password field, it will be loaded on
     // the main thread, and this initialization request will be ignored.
     Services.tm.idleDispatchToMainThread(() => {
--- a/browser/components/aboutconfig/test/browser/head.js
+++ b/browser/components/aboutconfig/test/browser/head.js
@@ -118,17 +118,17 @@ class AboutConfigTest {
     return this.document.getElementById("prefs");
   }
 
   /**
    * Array of AboutConfigRowTest objects, one for each row in the main table.
    */
   get rows() {
     let elements = this.prefsTable.querySelectorAll("tr:not(.hidden)");
-    return Array.map(elements, element => new AboutConfigRowTest(element));
+    return Array.from(elements, element => new AboutConfigRowTest(element));
   }
 
   /**
    * Returns the AboutConfigRowTest object for the row in the main table which
    * corresponds to the given preference name, or undefined if none is present.
    */
   getRow(name) {
     return this.rows.find(row => row.name == name);
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -1696,17 +1696,17 @@ CustomizeMode.prototype = {
       // they're attempting to append a child to that target.
       dragOverItem = (targetAreaType == "toolbar"
                         ? this._findVisiblePreviousSiblingNode(targetNode.lastElementChild)
                         : targetNode.lastElementChild) ||
                      targetNode;
       dragValue = "after";
     } else {
       let targetParent = targetNode.parentNode;
-      let position = Array.indexOf(targetParent.children, targetNode);
+      let position = Array.prototype.indexOf.call(targetParent.children, targetNode);
       if (position == -1) {
         dragOverItem = (targetAreaType == "toolbar"
                           ? this._findVisiblePreviousSiblingNode(targetNode.lastElementChild)
                           : targetNode.lastElementChild);
         dragValue = "after";
       } else {
         dragOverItem = targetParent.children[position];
         if (targetAreaType == "toolbar") {
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -148,24 +148,24 @@ var DownloadsCommon = {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
     for (let string of sb.getSimpleEnumeration()) {
       let stringName = string.key;
       if (stringName in kDownloadsStringsRequiringFormatting) {
         strings[stringName] = function() {
           // Convert "arguments" to a real array before calling into XPCOM.
           return sb.formatStringFromName(stringName,
-                                         Array.slice(arguments, 0),
+                                         Array.from(arguments),
                                          arguments.length);
         };
       } else if (stringName in kDownloadsStringsRequiringPluralForm) {
         strings[stringName] = function(aCount) {
           // Convert "arguments" to a real array before calling into XPCOM.
           let formattedString = sb.formatStringFromName(stringName,
-                                         Array.slice(arguments, 0),
+                                         Array.from(arguments),
                                          arguments.length);
           return PluralForm.get(aCount, formattedString);
         };
       } else {
         strings[stringName] = string.value;
       }
     }
     delete this.strings;
--- a/browser/components/downloads/content/allDownloadsView.js
+++ b/browser/components/downloads/content/allDownloadsView.js
@@ -290,18 +290,19 @@ DownloadsPlacesView.prototype = {
       // XXXmano: places.js relies on this behavior (see Bug 822203).
       this.searchTerm = "";
     } else {
       this._place = val;
     }
   },
 
   get selectedNodes() {
-      return Array.filter(this._richlistbox.selectedItems,
-                          element => element._shell.download.placesNode);
+    return Array.prototype.filter.call(
+      this._richlistbox.selectedItems,
+      element => element._shell.download.placesNode);
   },
 
   get selectedNode() {
     let selectedNodes = this.selectedNodes;
     return selectedNodes.length == 1 ? selectedNodes[0] : null;
   },
 
   get hasSelection() {
@@ -496,24 +497,25 @@ DownloadsPlacesView.prototype = {
         return this._richlistbox.selectedItems.length == 1;
       case "cmd_selectAll":
         return true;
       case "cmd_paste":
         return this._canDownloadClipboardURL();
       case "downloadsCmd_clearDownloads":
         return this.canClearDownloads(this._richlistbox);
       default:
-        return Array.every(this._richlistbox.selectedItems,
-                           element => element._shell.isCommandEnabled(aCommand));
+        return Array.prototype.every.call(
+          this._richlistbox.selectedItems,
+          element => element._shell.isCommandEnabled(aCommand));
     }
   },
 
   _copySelectedDownloadsToClipboard() {
-    let urls = Array.map(this._richlistbox.selectedItems,
-                         element => element._shell.download.source.url);
+    let urls = Array.from(this._richlistbox.selectedItems,
+                          element => element._shell.download.source.url);
 
     Cc["@mozilla.org/widget/clipboardhelper;1"]
       .getService(Ci.nsIClipboardHelper)
       .copyString(urls.join("\n"));
   },
 
   _getURLFromClipboardData() {
     let trans = Cc["@mozilla.org/widget/transferable;1"].
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -178,17 +178,17 @@ add_task(async function() {
   is(items.length, 0, "contextMenu item for selection was not found (context=image)");
 
   items = extensionMenuRoot.getElementsByAttribute("label", "parentToDel");
   is(items.length, 0, "contextMenu item for removed parent was not found (context=image)");
 
   items = extensionMenuRoot.getElementsByAttribute("label", "parent");
   is(items.length, 1, "contextMenu item for parent was found (context=image)");
 
-  is(items[0].childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
+  is(items[0].menupopup.children.length, 2, "child items for parent were found (context=image)");
 
   // Click on ext-image item and check the click results
   await closeExtensionContextMenu(image);
 
   let result = await extension.awaitMessage("onclick");
   checkClickInfo(result);
   result = await extension.awaitMessage("browser.contextMenus.onClicked");
   checkClickInfo(result);
--- a/browser/components/extensions/test/browser/browser_ext_menus.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus.js
@@ -82,17 +82,17 @@ add_task(async function test_actionConte
   for (const kind of ["page", "browser"]) {
     const menu = await openActionContextMenu(extension, kind);
     const [submenu, second, , , , last, separator] = menu.children;
 
     is(submenu.tagName, "menu", "Correct submenu type");
     is(submenu.label, "parent", "Correct submenu title");
 
     const popup = await openSubmenu(submenu);
-    is(popup, submenu.firstElementChild, "Correct submenu opened");
+    is(popup, submenu.menupopup, "Correct submenu opened");
     is(popup.children.length, 2, "Correct number of submenu items");
 
     let idPrefix = `${makeWidgetId(extension.id)}-menuitem-_`;
 
     is(second.tagName, "menuitem", "Second menu item type is correct");
     is(second.label, "click 1", "Second menu item title is correct");
     is(second.id, `${idPrefix}1`, "Second menu item id is correct");
 
@@ -141,17 +141,17 @@ add_task(async function test_hiddenPageA
 
   const extension = ExtensionTestUtils.loadExtension({manifest, background});
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
 
   await extension.startup();
   await extension.awaitMessage("ready");
 
   const menu = await openContextMenuInPageActionPanel(extension);
-  const menuItems = Array.filter(menu.children, node => {
+  const menuItems = Array.prototype.filter.call(menu.children, node => {
     return window.getComputedStyle(node).visibility == "visible";
   });
 
   is(menuItems.length, 3, "Correct number of children");
   const [dontShowItem, separator, manageItem] = menuItems;
 
   is(dontShowItem.label, "Remove from Address Bar", "Correct first child");
   is(separator.tagName, "menuseparator", "Correct second child");
@@ -246,17 +246,17 @@ add_task(async function test_tabContextM
   is(submenu.label, "alpha-beta parent", "Correct submenu title");
 
   isnot(gamma.label, "dummy", "`page` context menu item should not appear here");
 
   is(gamma.tagName, "menuitem", "Third menu item type is correct");
   is(gamma.label, "gamma", "Third menu item label is correct");
 
   const popup = await openSubmenu(submenu);
-  is(popup, submenu.firstElementChild, "Correct submenu opened");
+  is(popup, submenu.menupopup, "Correct submenu opened");
   is(popup.children.length, 2, "Correct number of submenu items");
 
   const [alpha, beta] = popup.children;
   is(alpha.tagName, "menuitem", "First menu item type is correct");
   is(alpha.label, "alpha", "First menu item label is correct");
   is(beta.tagName, "menuitem", "Second menu item type is correct");
   is(beta.label, "beta", "Second menu item label is correct");
 
@@ -391,17 +391,17 @@ add_task(async function test_tools_menu(
   const tabId = await second.awaitMessage("ready");
   const menu = await openToolsMenu();
 
   const [separator, submenu, gamma] = Array.from(menu.children).slice(-3);
   is(separator.tagName, "menuseparator", "Separator before first extension item");
 
   is(submenu.tagName, "menu", "Correct submenu type");
   is(submenu.getAttribute("label"), "Generated extension", "Correct submenu title");
-  is(submenu.firstElementChild.children.length, 2, "Correct number of submenu items");
+  is(submenu.menupopup.children.length, 2, "Correct number of submenu items");
 
   is(gamma.tagName, "menuitem", "Third menu item type is correct");
   is(gamma.getAttribute("label"), "gamma", "Third menu item label is correct");
 
   closeToolsMenu(gamma);
 
   const click = await second.awaitMessage("click");
   is(click.info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
--- a/browser/components/extensions/test/browser/browser_ext_menus_replace_menu.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_replace_menu.js
@@ -177,17 +177,17 @@ add_task(async function overrideContext_
     checkIsDefaultMenuItemVisible(getVisibleChildrenIds(menu));
 
     let menuItems = menu.getElementsByAttribute("ext-type", "top-level-menu");
     is(menuItems.length, 1, "Expected top-level menu element for extension.");
     let topLevelExtensionMenuItem = menuItems[0];
     is(topLevelExtensionMenuItem.nextSibling, null, "Extension menu should be the last element.");
 
     const submenu = await openSubmenu(topLevelExtensionMenuItem);
-    is(submenu, topLevelExtensionMenuItem.firstElementChild, "Correct submenu opened");
+    is(submenu, topLevelExtensionMenuItem.menupopup, "Correct submenu opened");
 
     Assert.deepEqual(
       getVisibleChildrenIds(submenu),
       EXPECTED_EXTENSION_MENU_IDS,
       "Extension menu items should be in the submenu by default.");
 
     await closeContextMenu();
   }
--- a/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_context.js
@@ -195,17 +195,17 @@ add_task(async function overrideContext_
       `${makeWidgetId(extension.id)}-menuitem-_tab_context`,
       `${makeWidgetId(extension.id)}-menuitem-_tab_context_http`,
       `${makeWidgetId(extension.id)}-menuitem-_tab_context_viewType_moz`,
       `menuseparator`,
       topLevels[0].id,
     ], "Expected menu items after changing context to tab");
 
     let submenu = await openSubmenu(topLevels[0]);
-    is(submenu, topLevels[0].firstElementChild, "Correct submenu opened");
+    is(submenu, topLevels[0].menupopup, "Correct submenu opened");
 
     Assert.deepEqual(getVisibleChildrenIds(submenu), [
       `${makeWidgetId(otherExtension.id)}-menuitem-_tab_context`,
       `${makeWidgetId(otherExtension.id)}-menuitem-_tab_context_http`,
       `${makeWidgetId(otherExtension.id)}-menuitem-_tab_context_viewType_moz`,
     ], "Expected menu items in submenu after changing context to tab");
 
     extension.sendMessage("testTabAccess", tabId);
@@ -298,17 +298,17 @@ add_task(async function overrideContext_
       `${makeWidgetId(extension.id)}-menuitem-_bookmark_context_http`,
       `${makeWidgetId(extension.id)}-menuitem-_bookmark_context_moz`,
       `${makeWidgetId(extension.id)}-menuitem-_bookmark_context_viewType_moz`,
       `menuseparator`,
       topLevels[0].id,
     ], "Expected menu items after changing context to bookmark");
 
     let submenu = await openSubmenu(topLevels[0]);
-    is(submenu, topLevels[0].firstElementChild, "Correct submenu opened");
+    is(submenu, topLevels[0].menupopup, "Correct submenu opened");
 
     Assert.deepEqual(getVisibleChildrenIds(submenu), [
       `${makeWidgetId(otherExtension.id)}-menuitem-_bookmark_context`,
       `${makeWidgetId(otherExtension.id)}-menuitem-_bookmark_context_http`,
       `${makeWidgetId(otherExtension.id)}-menuitem-_bookmark_context_moz`,
       `${makeWidgetId(otherExtension.id)}-menuitem-_bookmark_context_viewType_moz`,
     ], "Expected menu items in submenu after changing context to bookmark");
     await closeContextMenu(menu);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
@@ -224,17 +224,17 @@ add_task(async function test_zoom_api() 
     extension.sendMessage("msg-done", id, resp);
   });
 
   let url = "http://example.com/";
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
 
   let privateWindow = await BrowserTestUtils.openNewBrowserWindow({private: true});
-  let selectedBrowser = privateWindow.getBrowser().selectedBrowser;
+  let selectedBrowser = privateWindow.gBrowser.selectedBrowser;
   BrowserTestUtils.loadURI(selectedBrowser, url);
   await BrowserTestUtils.browserLoaded(selectedBrowser, false, url);
 
   gBrowser.selectedTab = tab1;
 
   await extension.startup();
 
   await extension.awaitFinish("tab-zoom");
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -458,17 +458,17 @@ async function openChromeContextMenu(men
   const menu = win.document.getElementById(menuId);
   const shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
   EventUtils.synthesizeMouseAtCenter(node, {type: "contextmenu"}, win);
   await shown;
   return menu;
 }
 
 async function openSubmenu(submenuItem, win = window) {
-  const submenu = submenuItem.firstElementChild;
+  const submenu = submenuItem.menupopup;
   const shown = BrowserTestUtils.waitForEvent(submenu, "popupshown");
   EventUtils.synthesizeMouseAtCenter(submenuItem, {}, win);
   await shown;
   return submenu;
 }
 
 function closeChromeContextMenu(menuId, itemToSelect, win = window) {
   const menu = win.document.getElementById(menuId);
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1431,16 +1431,19 @@ var gMainPane = {
   },
 
 
   // EventListener
 
   handleEvent(aEvent) {
     if (aEvent.type == "unload") {
       this.destroy();
+      if (AppConstants.MOZ_UPDATER) {
+        onUnload();
+      }
     }
   },
 
 
   // Composed Model Construction
 
   _loadData() {
     this._loadInternalHandlers();
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -1120,17 +1120,17 @@ var gPrivacyPane = {
     }
   },
 
   /**
    * Reload all tabs in all windows.
    */
   reloadAllTabs() {
     for (let win of window.BrowserWindowTracker.orderedWindows) {
-      let tabbrowser = win.getBrowser();
+      let tabbrowser = win.gBrowser;
       tabbrowser.reloadTabs(tabbrowser.tabs);
     }
   },
 
   /**
    * Show a warning to the user that they need to reload their tabs to apply the setting.
    */
   notifyUserToReload() {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -19,17 +19,17 @@
 <!-- Tracking / Content Blocking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" aria-describedby="contentBlockingDescription">
   <label id="contentBlockingHeader"><html:h2 data-l10n-id="content-blocking-header"/></label>
   <vbox data-subcategory="trackingprotection">
     <hbox align="start">
       <image id="trackingProtectionShield"/>
       <vbox flex="1">
         <description class="description-with-side-element">
-          <html:span id="contentBlockingDescription" class="tail-with-learn-more" data-l10n-id="content-blocking-description"></html:span>
+          <html:span id="contentBlockingDescription" class="tail-with-learn-more" data-l10n-id="content-blocking-section-description"></html:span>
           <label id="contentBlockingLearnMore" class="learnMore" data-l10n-id="content-blocking-learn-more" is="text-link"/>
         </description>
       </vbox>
       <vbox>
         <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
         <hbox>
           <button id="trackingProtectionExceptions"
                   class="accessory-button"
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
@@ -28,17 +28,17 @@ add_task(async function test() {
   let non_private_browser = gBrowser.selectedBrowser;
   let url = prefix + "?action=set&name=test&value=value&initial=true";
   BrowserTestUtils.loadURI(non_private_browser, url);
   await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
 
 
   // Step 2
   let private_window = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-  let private_browser = private_window.getBrowser().selectedBrowser;
+  let private_browser = private_window.gBrowser.selectedBrowser;
   url = prefix + "?action=set&name=test2&value=value2";
   BrowserTestUtils.loadURI(private_browser, url);
   await BrowserTestUtils.browserLoaded(private_browser, false, url);
 
 
   // Step 3
   url = prefix + "?action=get&name=test2";
   BrowserTestUtils.loadURI(non_private_browser, url);
@@ -58,33 +58,33 @@ add_task(async function test() {
 
 
   // Reopen the private window again, without privateBrowsing, which should clear the
   // the private storage.
   private_window.close();
   private_window = await BrowserTestUtils.openNewBrowserWindow({ private: false });
   private_browser = null;
   await new Promise(resolve => Cu.schedulePreciseGC(resolve));
-  private_browser = private_window.getBrowser().selectedBrowser;
+  private_browser = private_window.gBrowser.selectedBrowser;
 
   url = prefix + "?action=get&name=test2";
   BrowserTestUtils.loadURI(private_browser, url);
   await BrowserTestUtils.browserLoaded(private_browser, false, url);
   elts = await getElts(private_browser);
   isnot(elts[0], "value2", "public window shouldn't see cleared private storage");
   is(elts[1], "1", "public window should only see public items");
 
 
   // Making it private again should clear the storage and it shouldn't
   // be able to see the old private storage as well.
   private_window.close();
   private_window = await BrowserTestUtils.openNewBrowserWindow({ private: true });
   private_browser = null;
   await new Promise(resolve => Cu.schedulePreciseGC(resolve));
-  private_browser = private_window.getBrowser().selectedBrowser;
+  private_browser = private_window.gBrowser.selectedBrowser;
 
   url = prefix + "?action=set&name=test3&value=value3";
   BrowserTestUtils.loadURI(private_browser, url);
   await BrowserTestUtils.browserLoaded(private_browser, false, url);
   elts = await getElts(private_browser);
   is(elts[1], "1", "private window should only see new private items");
 
   // Cleanup.
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -41,16 +41,17 @@
         this.closest("searchbar")._textboxInitialized = true;
       ]]></constructor>
 
       <destructor><![CDATA[
         // If the context menu has never been opened, there won't be anything
         // to remove here.
         // Also, XBL and the customize toolbar code sometimes interact poorly.
         try {
+          this.closest("searchbar")._textboxInitialized = false;
           this.controllers.removeController(this.searchbarController);
         } catch (ex) { }
       ]]></destructor>
 
       // Add items to context menu and attach controller to handle them the
       // first time the context menu is opened.
       <method name="initContextMenu">
         <parameter name="aMenu"/>
--- a/browser/components/search/test/browser/browser_426329.js
+++ b/browser/components/search/test/browser/browser_426329.js
@@ -28,18 +28,18 @@ function checkMenuEntries(expectedValues
   is(actualValues.length, expectedValues.length, "Checking length of expected menu");
   for (let i = 0; i < expectedValues.length; i++)
     is(actualValues[i], expectedValues[i], "Checking menu entry #" + i);
 }
 
 function getMenuEntries() {
   // Could perhaps pull values directly from the controller, but it seems
   // more reliable to test the values that are actually in the richlistbox?
-  return Array.map(searchBar.textbox.popup.richlistbox.itemChildren,
-                   item => item.getAttribute("ac-value"));
+  return Array.from(searchBar.textbox.popup.richlistbox.itemChildren,
+                    item => item.getAttribute("ac-value"));
 }
 
 function countEntries(name, value) {
   return new Promise(resolve => {
     let count = 0;
     let obj = name && value ? {fieldname: name, value} : {};
     FormHistory.count(obj,
                       { handleResult(result) { count = result; },
--- a/browser/components/search/test/browser/browser_private_search_perwindowpb.js
+++ b/browser/components/search/test/browser/browser_private_search_perwindowpb.js
@@ -63,11 +63,11 @@ add_task(async function() {
   windowsToClose.forEach(function(win) {
     win.close();
   });
 });
 
 function getMenuEntries(searchBar) {
   // Could perhaps pull values directly from the controller, but it seems
   // more reliable to test the values that are actually in the richlistbox?
-  return Array.map(searchBar.textbox.popup.richlistbox.itemChildren,
-                   item => item.getAttribute("ac-value"));
+  return Array.from(searchBar.textbox.popup.richlistbox.itemChildren,
+                    item => item.getAttribute("ac-value"));
 }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3662,17 +3662,17 @@ var SessionStoreInternal = {
     let arrowScrollbox = tabbrowser.tabContainer.arrowScrollbox;
     let smoothScroll = arrowScrollbox.smoothScroll;
     arrowScrollbox.smoothScroll = false;
 
     // We need to keep track of the initially open tabs so that they
     // can be moved to the end of the restored tabs.
     let initialTabs;
     if (!overwriteTabs && firstWindow) {
-      initialTabs = Array.slice(tabbrowser.tabs);
+      initialTabs = Array.from(tabbrowser.tabs);
     }
 
     // Get rid of tabs that aren't needed anymore.
     if (overwriteTabs) {
       for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
         if (!tabbrowser.tabs[i].selected) {
           tabbrowser.removeTab(tabbrowser.tabs[i]);
         }
@@ -4047,17 +4047,17 @@ var SessionStoreInternal = {
 
     // It's important to set the window state to dirty so that
     // we collect their data for the first time when saving state.
     DirtyWindows.add(window);
 
     // In case we didn't collect/receive data for any tabs yet we'll have to
     // fill the array with at least empty tabData objects until |_tPos| or
     // we'll end up with |null| entries.
-    for (let otherTab of Array.slice(tabbrowser.tabs, 0, tab._tPos)) {
+    for (let otherTab of Array.prototype.slice.call(tabbrowser.tabs, 0, tab._tPos)) {
       let emptyState = {entries: [], lastAccessed: otherTab.lastAccessed};
       this._windows[window.__SSi].tabs.push(emptyState);
     }
 
     // Update the tab state in case we shut down without being notified.
     this._windows[window.__SSi].tabs[tab._tPos] = tabData;
 
     // Prepare the tab so that it can be properly restored. We'll pin/unpin
--- a/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
@@ -49,32 +49,32 @@ function test() {
     let node = getElementByXPath(aTab, aQuery);
     if (typeof aValue == "string")
       node.value = aValue;
     else if (typeof aValue == "boolean")
       node.checked = aValue;
     else if (typeof aValue == "number")
       node.selectedIndex = aValue;
     else
-      Array.forEach(node.options, (aOpt, aIx) =>
+      Array.prototype.forEach.call(node.options, (aOpt, aIx) =>
         (aOpt.selected = aValue.indexOf(aIx) > -1));
   }
 
   function compareFormValue(aTab, aQuery, aValue) {
     let node = getElementByXPath(aTab, aQuery);
     if (!node)
       return false;
     if (ChromeUtils.getClassName(node) === "HTMLInputElement")
       return aValue == (node.type == "checkbox" || node.type == "radio" ?
                        node.checked : node.value);
     if (ChromeUtils.getClassName(node) === "HTMLTextAreaElement")
       return aValue == node.value;
     if (!node.multiple)
       return aValue == node.selectedIndex;
-    return Array.every(node.options, (aOpt, aIx) =>
+    return Array.prototype.every.call(node.options, (aOpt, aIx) =>
             (aValue.indexOf(aIx) > -1) == aOpt.selected);
   }
 
   /**
    * Test (B) : Session data restoration between windows
    */
 
   let rootDir = getRootDirectory(gTestPath);
--- a/browser/components/sessionstore/test/browser_579879.js
+++ b/browser/components/sessionstore/test/browser_579879.js
@@ -3,18 +3,18 @@
 add_task(async function() {
   let tab1 = BrowserTestUtils.addTab(gBrowser, "data:text/plain;charset=utf-8,foo");
   gBrowser.pinTab(tab1);
 
   await promiseBrowserLoaded(tab1.linkedBrowser);
   let tab2 = BrowserTestUtils.addTab(gBrowser);
   gBrowser.pinTab(tab2);
 
-  is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
+  is(Array.prototype.indexOf.call(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
   await promiseRemoveTabAndSessionState(tab1);
 
   tab1 = undoCloseTab();
   ok(tab1.pinned, "pinned tab 1 has been restored as a pinned tab");
-  is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
+  is(Array.prototype.indexOf.call(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
 
   BrowserTestUtils.removeTab(tab1);
   BrowserTestUtils.removeTab(tab2);
 });
--- a/browser/components/sessionstore/test/browser_607016.js
+++ b/browser/components/sessionstore/test/browser_607016.js
@@ -80,17 +80,17 @@ add_task(async function() {
       BrowserTestUtils.removeTab(gBrowser.tabs[1]);
     }
   }
 
   // Set the test state.
   await setBrowserState(state);
 
   // Wait until the selected tab is restored and all others are pending.
-  await Promise.all(Array.map(gBrowser.tabs, tab => {
+  await Promise.all(Array.from(gBrowser.tabs, tab => {
     return (tab == gBrowser.selectedTab) ?
       promiseTabRestored(tab) : promiseTabRestoring(tab);
   }));
 
   // Kick off the actual tests.
   await progressCallback();
 
   // Cleanup.
--- a/browser/components/sessionstore/test/content-forms.js
+++ b/browser/components/sessionstore/test/content-forms.js
@@ -83,23 +83,23 @@ defineListener("getSelectedIndex", funct
 defineListener("setSelectedIndex", function(data) {
   let input = queryElement(data);
   input.selectedIndex = data.index;
   dispatchUIEvent(input, "input");
 });
 
 defineListener("getMultipleSelected", function(data) {
   let input = queryElement(data);
-  return Array.map(input.options, (opt, idx) => idx)
+  return Array.from(input.options, (opt, idx) => idx)
               .filter(idx => input.options[idx].selected);
 });
 
 defineListener("setMultipleSelected", function(data) {
   let input = queryElement(data);
-  Array.forEach(input.options, (opt, idx) => opt.selected = data.indices.indexOf(idx) > -1);
+  Array.prototype.forEach.call(input.options, (opt, idx) => opt.selected = data.indices.indexOf(idx) > -1);
   dispatchUIEvent(input, "input");
 });
 
 defineListener("getFileNameArray", function(data) {
   return queryElement(data).mozGetFileNameArray();
 });
 
 defineListener("setFileNameArray", function(data) {
--- a/browser/components/sessionstore/test/content.js
+++ b/browser/components/sessionstore/test/content.js
@@ -49,17 +49,17 @@ if (sessionHistory) {
  */
 
 addEventListener("hashchange", function() {
   sendAsyncMessage("ss-test:hashchange");
 });
 
 addMessageListener("ss-test:getStyleSheets", function(msg) {
   let sheets = content.document.styleSheets;
-  let titles = Array.map(sheets, ss => [ss.title, ss.disabled]);
+  let titles = Array.from(sheets, ss => [ss.title, ss.disabled]);
   sendSyncMessage("ss-test:getStyleSheets", titles);
 });
 
 addMessageListener("ss-test:enableStyleSheetsForSet", function(msg) {
   let sheets = content.document.styleSheets;
   let change = false;
   for (let i = 0; i < sheets.length; i++) {
     if (sheets[i].disabled != (!msg.data.includes(sheets[i].title))) {
--- a/browser/extensions/webcompat/aboutCompat.css
+++ b/browser/extensions/webcompat/aboutCompat.css
@@ -145,18 +145,23 @@
 
   .tab.active + table {
     display: block;
   }
 
   td {
     display: block;
     position: relative;
+  }
+  td:dir(ltr) {
     padding-right: 6.5em;
   }
+  td:dir(rtl) {
+    padding-left: 6.5em;
+  }
 
   td[colspan="4"] {
     padding: 1em;
     font-style: italic;
     text-align: center;
   }
 
   td:not([colspan]):nth-child(1) {
@@ -169,22 +174,27 @@
 
   td:nth-child(3) {
     display: contents;
   }
 
   button {
     background: #e8e8e7;
     position: absolute;
-    right: 0;
     top: 0;
     bottom: 0;
     width: 6em;
     border: 0;
     border-left: 1px solid #d7d9db;
     -moz-appearance: none;
     color: #000;
   }
+  button:dir(ltr) {
+    right: 0;
+  }
+  button:dir(rtl) {
+    left: 0;
+  }
 
   button::-moz-focus-inner {
     border: 0;
   }
 }
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -819,17 +819,17 @@ addressbar-locbar-openpage-option =
     .accesskey = O
 
 addressbar-suggestions-settings = Change preferences for search engine suggestions
 
 ## Privacy Section - Content Blocking
 
 content-blocking-header = Content Blocking
 
-content-blocking-description = Block third-party content that tracks you around the web. Control how much of your online activity gets stored and shared between websites.
+content-blocking-section-description = Protect your privacy while you browse. Block invisible content that tracks the sites you visit and profiles you. Blocking some of this content may make pages load faster.
 
 content-blocking-learn-more = Learn more
 
 # The terminology used to refer to categories of Content Blocking is also used in chrome/browser/browser.properties and should be translated consistently.
 # "Standard" in this case is an adjective, meaning "default" or "normal".
 content-blocking-setting-standard =
   .label = Standard
   .accesskey = d
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -414,17 +414,17 @@ class AsyncTabSwitcher {
     if (this.visibleTab !== showTab) {
       this.tabbrowser._adjustFocusBeforeTabSwitch(this.visibleTab, showTab);
       this.visibleTab = showTab;
 
       this.maybeVisibleTabs.add(showTab);
 
       let tabpanels = this.tabbrowser.tabpanels;
       let showPanel = this.tabbrowser.tabContainer.getRelatedElement(showTab);
-      let index = Array.indexOf(tabpanels.children, showPanel);
+      let index = Array.prototype.indexOf.call(tabpanels.children, showPanel);
       if (index != -1) {
         this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
         tabpanels.setAttribute("selectedIndex", index);
         if (showTab === this.requestedTab) {
           if (requestedTabState == this.STATE_LOADED) {
             // The new tab will be made visible in the next paint, record the expected
             // transaction id for that, and we'll mark when we get notified of its
             // completion.
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -65,16 +65,18 @@ const {XPCOMUtils} = ChromeUtils.import(
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "SitePermissions",
   "resource:///modules/SitePermissions.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "URICountListener",
   "resource:///modules/BrowserUsageTelemetry.jsm");
+ChromeUtils.defineModuleGetter(this, "PermissionUITelemetry",
+  "resource:///modules/PermissionUITelemetry.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "IDNService",
   "@mozilla.org/network/idn-service;1", "nsIIDNService");
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings
                  .createBundle("chrome://browser/locale/browser.properties");
 });
@@ -144,16 +146,27 @@ var PermissionPromptPrototype = {
    * the prompt will be skipped and the allow or deny choice
    * will be selected automatically.
    */
   get permissionKey() {
     return undefined;
   },
 
   /**
+   * A string that needs to be set to include this prompt in
+   * experimental event telemetry collection.
+   *
+   * This needs to conform to event telemetry string rules,
+   * i.e. it needs to be an alphabetic string under 20 characters.
+   */
+  get permissionTelemetryKey() {
+    return undefined;
+  },
+
+  /**
    * If true, user permissions will be read from and written to.
    * When this is false, we still provide integration with
    * infrastructure such as temporary permissions. permissionKey should
    * still return a valid name in those cases for that integration to work.
    */
   get usePermissionManager() {
     return true;
   },
@@ -398,16 +411,18 @@ var PermissionPromptPrototype = {
     }
 
     let chromeWin = this.browser.ownerGlobal;
     if (!chromeWin.PopupNotifications) {
       this.cancel();
       return;
     }
 
+    this._buttonAction = null;
+
     // Transform the PermissionPrompt actions into PopupNotification actions.
     let popupNotificationActions = [];
     for (let promptAction of this.promptActions) {
       let action = {
         label: promptAction.label,
         accessKey: promptAction.accessKey,
         callback: state => {
           if (promptAction.callback) {
@@ -435,19 +450,26 @@ var PermissionPromptPrototype = {
               SitePermissions.set(this.principal.URI,
                                   this.permissionKey,
                                   promptAction.action,
                                   SitePermissions.SCOPE_TEMPORARY,
                                   this.browser);
             }
 
             // Grant permission if action is ALLOW.
+            // Record buttonAction for telemetry.
             if (promptAction.action == SitePermissions.ALLOW) {
+              this._buttonAction = "accept";
               this.allow();
             } else {
+              if (promptAction.scope == SitePermissions.SCOPE_PERSISTENT) {
+                this._buttonAction = "never";
+              } else {
+                this._buttonAction = "deny";
+              }
               this.cancel();
             }
           } else if (this.permissionKey) {
             // TODO: Add support for permitTemporaryAllow
             if (promptAction.action == SitePermissions.BLOCK) {
               // Temporarily store BLOCK permissions.
               // We don't consider subframes when storing temporary
               // permissions on a tab, thus storing ALLOW could be exploited.
@@ -521,32 +543,45 @@ var PermissionPromptPrototype = {
       anchor.addEventListener("animationend", () => anchor.removeAttribute("animate"), {once: true});
       anchor.setAttribute("animate", "true");
     }
 
     this._showNotification(popupNotificationActions, true);
   },
 
   _showNotification(actions, postPrompt = false) {
+    let chromeWin = this.browser.ownerGlobal;
     let mainAction = actions.length ? actions[0] : null;
     let secondaryActions = actions.splice(1);
 
     let options = this.popupOptions;
 
+    let telemetryData = null;
+    if (this.request && this.permissionTelemetryKey) {
+      telemetryData = {
+        permissionTelemetryKey: this.permissionTelemetryKey,
+        permissionKey: this.permissionKey,
+        principal: this.principal,
+        documentDOMContentLoadedTimestamp: this.request.documentDOMContentLoadedTimestamp,
+        isHandlingUserInput: this.request.isHandlingUserInput,
+        userHadInteractedWithDocument: this.request.userHadInteractedWithDocument,
+      };
+    }
+
     if (!options.hasOwnProperty("displayURI") || options.displayURI) {
       options.displayURI = this.principal.URI;
     }
 
     if (!postPrompt) {
       // Permission prompts are always persistent; the close button is controlled by a pref.
       options.persistent = true;
       options.hideClose = true;
     }
 
-    options.eventCallback = (topic) => {
+    options.eventCallback = (topic, nextRemovalReason) => {
       // When the docshell of the browser is aboout to be swapped to another one,
       // the "swapping" event is called. Returning true causes the notification
       // to be moved to the new browser.
       if (topic == "swapping") {
         return true;
       }
       // The prompt has been shown, notify the PermissionUI.
       // onShown() is currently not called for post-prompts,
@@ -557,37 +592,43 @@ var PermissionPromptPrototype = {
         this.onShown();
       }
       // The prompt has been removed, notify the PermissionUI.
       // onAfterShow() is currently not called for post-prompts,
       // because there is no prompt that would make use of this.
       // You can remove this restriction if you need it, but be
       // mindful of other consumers.
       if (topic == "removed" && !postPrompt) {
+        if (telemetryData) {
+          PermissionUITelemetry.onRemoved(telemetryData, this._buttonAction,
+                                          nextRemovalReason);
+        }
         this.onAfterShow();
       }
       return false;
     };
 
     // Post-prompts show up as dismissed.
     options.dismissed = postPrompt;
 
     // onBeforeShow() is currently not called for post-prompts,
     // because there is no prompt that would make use of this.
     // You can remove this restriction if you need it, but be
     // mindful of other consumers.
     if (postPrompt || this.onBeforeShow() !== false) {
-      let chromeWin = this.browser.ownerGlobal;
       chromeWin.PopupNotifications.show(this.browser,
                                         this.notificationID,
                                         this.message,
                                         this.anchorID,
                                         mainAction,
                                         secondaryActions,
                                         options);
+      if (telemetryData) {
+        PermissionUITelemetry.onShow(telemetryData);
+      }
     }
   },
 };
 
 PermissionUI.PermissionPromptPrototype = PermissionPromptPrototype;
 
 /**
  * A subclass of PermissionPromptPrototype that assumes
@@ -639,16 +680,20 @@ function GeolocationPermissionPrompt(req
 
 GeolocationPermissionPrompt.prototype = {
   __proto__: PermissionPromptForRequestPrototype,
 
   get permissionKey() {
     return "geo";
   },
 
+  get permissionTelemetryKey() {
+    return "geo";
+  },
+
   get popupOptions() {
     let pref = "browser.geolocation.warning.infoURL";
     let options = {
       learnMoreURL: Services.urlFormatter.formatURLPref(pref),
       displayURI: false,
       name: this.principalName,
     };
 
@@ -722,16 +767,20 @@ function DesktopNotificationPermissionPr
 
 DesktopNotificationPermissionPrompt.prototype = {
   __proto__: PermissionPromptForRequestPrototype,
 
   get permissionKey() {
     return "desktop-notification";
   },
 
+  get permissionTelemetryKey() {
+    return "notifications";
+  },
+
   get popupOptions() {
     let learnMoreURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
 
     return {
       learnMoreURL,
       displayURI: false,
       name: this.principalName,
new file mode 100644
--- /dev/null
+++ b/browser/modules/PermissionUITelemetry.jsm
@@ -0,0 +1,147 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+  "PermissionUITelemetry",
+];
+
+ChromeUtils.defineModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "CryptoUtils",
+  "resource://services-crypto/utils.js");
+
+const TELEMETRY_STAT_REMOVAL_LEAVE_PAGE = 6;
+
+var PermissionUITelemetry = {
+  // Returns a hash of the host name in combination with a unique local user id.
+  // This allows us to track duplicate prompts on sites while not revealing the user's
+  // browsing history.
+  _uniqueHostHash(host) {
+    // Gets a unique user ID as salt, that needs to stay local to this profile and not be
+    // sent to any server!
+    let salt = Services.prefs.getStringPref("permissions.eventTelemetry.salt", null);
+    if (!salt) {
+      salt = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
+               .generateUUID().toString();
+      Services.prefs.setStringPref("permissions.eventTelemetry.salt", salt);
+    }
+
+    let domain;
+    try {
+      domain = Services.eTLD.getBaseDomainFromHost(host);
+    } catch (e) {
+      domain = host;
+    }
+
+    return CryptoUtils.sha256(domain + salt);
+  },
+
+  _previousVisitCount(host) {
+    let historyService = Cc["@mozilla.org/browser/nav-history-service;1"]
+                           .getService(Ci.nsINavHistoryService);
+
+    let options = historyService.getNewQueryOptions();
+    options.resultType = options.RESULTS_AS_VISIT;
+
+    // Search for visits to this host before today
+    let query = historyService.getNewQuery();
+    query.endTimeReference = query.TIME_RELATIVE_TODAY;
+    query.endTime = 0;
+    query.domain = host;
+
+    let result = historyService.executeQuery(query, options);
+    result.root.containerOpen = true;
+    let cc = result.root.childCount;
+    result.root.containerOpen = false;
+    return cc;
+  },
+
+  _collectExtraKeys(prompt) {
+    let lastInteraction = 0;
+    // "storageAccessAPI" is the name of the permission that tells us whether the
+    // user has interacted with a particular site in the first-party context before.
+    let interactionPermission = Services.perms.getPermissionObject(
+      prompt.principal, "storageAccessAPI", false);
+    if (interactionPermission) {
+      lastInteraction = interactionPermission.modificationTime;
+    }
+
+    let allPermsDenied = 0;
+    let allPermsGranted = 0;
+    let thisPermDenied = 0;
+    let thisPermGranted = 0;
+
+    let commonPermissions = ["geo", "desktop-notification", "camera", "microphone", "screen"];
+    for (let perm of Services.perms.enumerator) {
+      if (!commonPermissions.includes(perm.type)) {
+        continue;
+      }
+
+      if (perm.capability == Services.perms.ALLOW_ACTION) {
+        allPermsGranted++;
+        if (perm.type == prompt.permissionKey) {
+          thisPermGranted++;
+        }
+      }
+
+      if (perm.capability == Services.perms.DENY_ACTION) {
+        allPermsDenied++;
+        if (perm.type == prompt.permissionKey) {
+          thisPermDenied++;
+        }
+      }
+    }
+
+    let promptHost = prompt.principal.URI.host;
+
+    return {
+      previousVisits: this._previousVisitCount(promptHost).toString(),
+      timeOnPage: (Date.now() - prompt.documentDOMContentLoadedTimestamp).toString(),
+      hasUserInput: prompt.isHandlingUserInput.toString(),
+      docHasUserInput: prompt.userHadInteractedWithDocument.toString(),
+      lastInteraction: lastInteraction.toString(),
+      allPermsDenied: allPermsDenied.toString(),
+      allPermsGranted: allPermsGranted.toString(),
+      thisPermDenied: thisPermDenied.toString(),
+      thisPermGranted: thisPermGranted.toString(),
+    };
+  },
+
+  onShow(prompt) {
+    let object = prompt.permissionTelemetryKey;
+    if (!object) {
+      return;
+    }
+
+    let extraKeys = this._collectExtraKeys(prompt);
+    let hostHash = this._uniqueHostHash(prompt.principal.URI.host);
+    Services.telemetry.recordEvent("security.ui.permissionprompt",
+      "show", object, hostHash, extraKeys);
+  },
+
+  onRemoved(prompt, buttonAction, telemetryReason) {
+    let object = prompt.permissionTelemetryKey;
+    if (!object) {
+      return;
+    }
+
+    let method = "other";
+    if (buttonAction == "accept") {
+      method = "accept";
+    } else if (buttonAction == "deny") {
+      method = "deny";
+    } else if (buttonAction == "never") {
+      method = "never";
+    } else if (telemetryReason == TELEMETRY_STAT_REMOVAL_LEAVE_PAGE) {
+      method = "leave";
+    }
+
+    let extraKeys = this._collectExtraKeys(prompt);
+    let hostHash = this._uniqueHostHash(prompt.principal.URI.host);
+    Services.telemetry.recordEvent("security.ui.permissionprompt",
+      method, object, hostHash, extraKeys);
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -74,16 +74,19 @@ with Files("LiveBookmarkMigrator.jsm"):
     BUG_COMPONENT = ("Firefox", "General")
 
 with Files("OpenInTabsUtils.jsm"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
 with Files("PermissionUI.jsm"):
    BUG_COMPONENT = ("Firefox", "Site Identity and Permission Panels")
 
+with Files("PermissionUITelemetry.jsm"):
+   BUG_COMPONENT = ("Firefox", "Site Identity and Permission Panels")
+
 with Files("ProcessHangMonitor.jsm"):
     BUG_COMPONENT = ("Core", "DOM: Content Processes")
 
 with Files("ReaderParent.jsm"):
     BUG_COMPONENT = ("Toolkit", "Reader Mode")
 
 with Files("Sanitizer.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
@@ -145,16 +148,17 @@ EXTRA_JS_MODULES += [
     'FormValidationHandler.jsm',
     'HomePage.jsm',
     'LaterRun.jsm',
     'LiveBookmarkMigrator.jsm',
     'NewTabPagePreloading.jsm',
     'OpenInTabsUtils.jsm',
     'PageActions.jsm',
     'PermissionUI.jsm',
+    'PermissionUITelemetry.jsm',
     'PingCentre.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SelectionChangedMenulist.jsm',
     'SiteDataManager.jsm',
     'SitePermissions.jsm',
--- a/browser/modules/test/browser/browser_PageActions.js
+++ b/browser/modules/test/browser/browser_PageActions.js
@@ -786,17 +786,17 @@ add_task(async function nonBuiltFirst() 
   Assert.deepEqual(PageActions._nonBuiltInActions.map(a => a.id), [action.id],
                    "Action should be in PageActions._nonBuiltInActions");
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     [BrowserPageActions.panelButtonNodeIDForActionID(action.id)],
     "Action should be in panel"
   );
 
   // Now add back all the actions.
   for (let a of initialActions) {
     PageActions.addAction(a);
   }
@@ -828,17 +828,17 @@ add_task(async function nonBuiltFirst() 
     PageActions.actionsInPanel(window).map(a => a.id),
     initialActionsInPanel.map(a => a.id).concat(
       [PageActions.ACTION_ID_BUILT_IN_SEPARATOR],
       [action.id]
     ),
     "All actions should be in PageActions.actionsInPanel()"
   );
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat(
       [PageActions.ACTION_ID_BUILT_IN_SEPARATOR],
       [action.id]
     ).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Panel should contain all actions"
   );
 
   // Remove the test action.
@@ -866,17 +866,17 @@ add_task(async function nonBuiltFirst() 
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
     PageActions.actionsInPanel(window).map(a => a.id),
     initialActionsInPanel.map(a => a.id),
     "Action should no longer be in PageActions.actionsInPanel()"
   );
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => BrowserPageActions.panelButtonNodeIDForActionID(a.id)),
     "Action should no longer be in panel"
   );
 });
 
 
 // Makes sure that urlbar nodes appear in the correct order in a new window.
 add_task(async function urlbarOrderNewWindow() {
@@ -1504,17 +1504,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should be updated"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat([
       PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
       action.id,
     ]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Actions in panel should be correct"
   );
 
   Assert.equal(onPlacedInPanelCount, 1,
@@ -1531,17 +1531,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should revert to initial"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel
       .map(a => BrowserPageActions.panelButtonNodeIDForActionID(a.id)),
     "Actions in panel should be correct"
   );
 
   // Enable the action.  It should be added back to the panel.
   action.setDisabled(false, window);
 
@@ -1554,17 +1554,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should be updated"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat([
       PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
       action.id,
     ]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Actions in panel should be correct"
   );
 
   Assert.equal(onPlacedInPanelCount, 2,
@@ -1589,17 +1589,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should be updated"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat([
       PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
       otherAction.id,
       PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
       action.id,
     ]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Actions in panel should be correct"
   );
@@ -1621,17 +1621,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should be updated"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat([
       PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
       otherAction.id,
     ]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Actions in panel should be correct"
   );
 
   // Enable the action again.  It should be added back to the panel.
@@ -1648,17 +1648,17 @@ add_task(async function transient() {
     "PageActions.actionsInPanel() should be updated"
   );
 
   // Check the panel.
   await promiseOpenPageActionPanel();
   EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
   await promisePageActionPanelHidden();
   Assert.deepEqual(
-    Array.map(BrowserPageActions.mainViewBodyNode.children, n => n.id),
+    Array.from(BrowserPageActions.mainViewBodyNode.children, n => n.id),
     initialActionsInPanel.map(a => a.id).concat([
       PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
       otherAction.id,
       PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
       action.id,
     ]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
     "Actions in panel should be correct"
   );
@@ -1784,12 +1784,12 @@ function promisePageActionViewChildrenVi
       }
     }
     return false;
   });
 }
 
 function collectContextMenuItems() {
   let contextMenu = document.getElementById("pageActionContextMenu");
-  return Array.filter(contextMenu.children, node => {
+  return Array.prototype.filter.call(contextMenu.children, node => {
     return window.getComputedStyle(node).visibility == "visible";
   });
 }
--- a/browser/modules/test/browser/head.js
+++ b/browser/modules/test/browser/head.js
@@ -70,16 +70,19 @@ function makeMockPermissionRequest(brows
   let type = {
     options: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIArray),
     QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
   };
   let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
   types.appendElement(type);
   let result = {
     types,
+    documentDOMContentLoadedTimestamp: 0,
+    isHandlingUserInput: false,
+    userHadInteractedWithDocument: false,
     principal: browser.contentPrincipal,
     topLevelPrincipal: browser.contentPrincipal,
     requester: null,
     _cancelled: false,
     cancel() {
       this._cancelled = true;
     },
     _allowed: false,
--- a/browser/themes/shared/places/tree-icons.css
+++ b/browser/themes/shared/places/tree-icons.css
@@ -1,13 +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/. */
 
-treechildren:-moz-tree-image {
+treechildren::-moz-tree-image {
   -moz-context-properties: fill, fill-opacity;
   fill-opacity: 0.7;
 }
 
 treechildren::-moz-tree-image(title) {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
   padding-inline-end: 2px;
   margin: 0 2px;
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -24,16 +24,17 @@
 #include "nsJSPrincipals.h"
 #include "mozilla/BasePrincipal.h"
 #include "ExpandedPrincipal.h"
 #include "SystemPrincipal.h"
 #include "DomainPolicy.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
+#include "nsContentSecurityManager.h"
 #include "nsDocShell.h"
 #include "nsError.h"
 #include "nsGlobalWindowInner.h"
 #include "nsDOMCID.h"
 #include "nsTextFormatter.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsIEffectiveTLDService.h"
@@ -485,69 +486,24 @@ nsScriptSecurityManager::GetChannelURIPr
 NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
-#if defined(DEBUG) && !defined(ANDROID)
-static void AssertEvalNotUsingSystemPrincipal(nsIPrincipal* subjectPrincipal,
-                                              JSContext* cx) {
-  if (!subjectPrincipal->IsSystemPrincipal()) {
-    return;
-  }
-
-  if (Preferences::GetBool("security.allow_eval_with_system_principal")) {
-    return;
-  }
-
-  static StaticAutoPtr<nsTArray<nsCString>> sUrisAllowEval;
-  JS::AutoFilename scriptFilename;
-  if (JS::DescribeScriptedCaller(cx, &scriptFilename)) {
-    if (!sUrisAllowEval) {
-      sUrisAllowEval = new nsTArray<nsCString>();
-      nsAutoCString urisAllowEval;
-      Preferences::GetCString("security.uris_using_eval_with_system_principal",
-                              urisAllowEval);
-      for (const nsACString& filenameString : urisAllowEval.Split(',')) {
-        sUrisAllowEval->AppendElement(filenameString);
-      }
-      ClearOnShutdown(&sUrisAllowEval);
-    }
-
-    nsAutoCString fileName;
-    fileName = nsAutoCString(scriptFilename.get());
-    // Extract file name alone if scriptFilename contains line number
-    // separated by multiple space delimiters in few cases.
-    int32_t fileNameIndex = fileName.FindChar(' ');
-    if (fileNameIndex != -1) {
-      fileName = Substring(fileName, 0, fileNameIndex);
-    }
-    ToLowerCase(fileName);
-
-    for (auto& uriEntry : *sUrisAllowEval) {
-      if (StringEndsWith(fileName, uriEntry)) {
-        return;
-      }
-    }
-  }
-
-  MOZ_ASSERT(false, "do not use eval with system privileges");
-}
-#endif
-
 bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
     JSContext* cx, JS::HandleValue aValue) {
   MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
   nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
 
 #if defined(DEBUG) && !defined(ANDROID)
-  AssertEvalNotUsingSystemPrincipal(subjectPrincipal, cx);
+  nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(subjectPrincipal,
+                                                              cx);
 #endif
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
   NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
 
   // don't do anything unless there's a CSP
   if (!csp) return true;
--- a/devtools/client/application/src/components/Worker.css
+++ b/devtools/client/application/src/components/Worker.css
@@ -27,17 +27,17 @@
   font-size: 1.2rem;
 }
 
 .worker:first-child {
   padding-top: 0;
 }
 
 .worker:not(:last-child) {
-  border-bottom: 1px solid var(--theme-body-color-alt);
+  border-bottom: 1px solid var(--theme-text-color-alt);
 }
 
 .worker__header {
   grid-column: 1/3;
   display: grid;
   grid-template-columns: 1fr auto;
   grid-column-gap: 2rem;
   align-items: center;
@@ -66,10 +66,10 @@
   grid-column-gap: 1rem;
 }
 
 .worker__data > * {
   margin: 0;
 }
 
 .worker__data__updated {
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
--- a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
@@ -194,17 +194,17 @@ export class ConditionalPanel extends Pu
 
     const panel = document.createElement("div");
     ReactDOM.render(
       <div
         className={classNames("conditional-breakpoint-panel", {
           "log-point": log
         })}
         onClick={() => this.keepFocusOnInput()}
-        // onBlur={this.props.closeConditionalPanel}
+        onBlur={this.props.closeConditionalPanel}
         ref={node => (this.panelNode = node)}
       >
         <div className="prompt">»</div>
         <textarea
           defaultValue={defaultValue}
           ref={input => this.createEditor(input)}
         />
       </div>,
--- a/devtools/client/debugger/src/components/Editor/Highlight.css
+++ b/devtools/client/debugger/src/components/Editor/Highlight.css
@@ -5,25 +5,25 @@
 .cm-highlight {
   position: relative;
 }
 
 .cm-highlight::before {
   position: absolute;
   border-top-style: solid;
   border-bottom-style: solid;
-  border-top-color: var(--theme-comment-alt);
-  border-bottom-color: var(--theme-comment-alt);
+  border-top-color: var(--theme-text-color-inactive);
+  border-bottom-color: var(--theme-text-color-inactive);
   border-top-width: 1px;
   border-bottom-width: 1px;
   top: -1px;
   bottom: 0;
   left: 0;
   right: 0;
   content: "";
   margin-bottom: -1px;
 }
 
 .cm-highlight-full::before {
-  border: 1px solid var(--theme-comment-alt);
+  border: 1px solid var(--theme-text-color-inactive);
   border-radius: 2px;
   margin: 0 -1px -1px -1px;
 }
--- a/devtools/client/debugger/src/components/Editor/Preview.css
+++ b/devtools/client/debugger/src/components/Editor/Preview.css
@@ -90,17 +90,17 @@
   display: -ms-flexbox;
   display: flex;
   -webkit-box-align: center;
   -ms-flex-align: center;
   align-items: center;
   font-size: 14px;
   line-height: 30px;
   background: var(--theme-toolbar-background);
-  color: var(--theme-comment-alt);
+  color: var(--theme-text-color-inactive);
   padding: 0 4px;
 }
 
 .add-to-expression-bar .prompt {
   width: 1em;
 }
 
 .add-to-expression-bar .expression-to-save-label {
--- a/devtools/client/debugger/src/components/Editor/Preview/Popup.css
+++ b/devtools/client/debugger/src/components/Editor/Preview/Popup.css
@@ -105,17 +105,17 @@
   display: -ms-flexbox;
   display: flex;
   -webkit-box-align: center;
   -ms-flex-align: center;
   align-items: center;
   font-size: 14px;
   line-height: 30px;
   background: var(--theme-toolbar-background);
-  color: var(--theme-comment-alt);
+  color: var(--theme-text-color-inactive);
   padding: 0 4px;
 }
 
 .add-to-expression-bar .prompt {
   width: 1em;
 }
 
 .add-to-expression-bar .expression-to-save-label {
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -202,34 +202,27 @@ class Editor extends PureComponent<Props
     this.onEditorScroll();
     this.setState({ editor });
     return editor;
   }
 
   componentDidMount() {
     const { shortcuts } = this.context;
 
-    const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
-    const searchAgainPrevKey = L10N.getStr(
-      "sourceSearch.search.againPrev.key2"
-    );
-
     shortcuts.on(L10N.getStr("toggleBreakpoint.key"), this.onToggleBreakpoint);
     shortcuts.on(
       L10N.getStr("toggleCondPanel.breakpoint.key"),
       this.onToggleConditionalPanel
     );
     shortcuts.on(
       L10N.getStr("toggleCondPanel.logPoint.key"),
       this.onToggleConditionalPanel
     );
     shortcuts.on(L10N.getStr("sourceTabs.closeTab.key"), this.onClosePress);
     shortcuts.on("Esc", this.onEscape);
-    shortcuts.on(searchAgainPrevKey, this.onSearchAgain);
-    shortcuts.on(searchAgainKey, this.onSearchAgain);
   }
 
   onClosePress = (key, e: KeyboardEvent) => {
     const { cx, selectedSource } = this.props;
     if (selectedSource) {
       e.preventDefault();
       e.stopPropagation();
       this.props.closeTab(cx, selectedSource);
@@ -238,27 +231,21 @@ class Editor extends PureComponent<Props
 
   componentWillUnmount() {
     if (this.state.editor) {
       this.state.editor.destroy();
       this.state.editor.codeMirror.off("scroll", this.onEditorScroll);
       this.setState({ editor: (null: any) });
     }
 
-    const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
-    const searchAgainPrevKey = L10N.getStr(
-      "sourceSearch.search.againPrev.key2"
-    );
     const shortcuts = this.context.shortcuts;
     shortcuts.off(L10N.getStr("sourceTabs.closeTab.key"));
     shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
     shortcuts.off(L10N.getStr("toggleCondPanel.breakpoint.key"));
     shortcuts.off(L10N.getStr("toggleCondPanel.logPoint.key"));
-    shortcuts.off(searchAgainPrevKey);
-    shortcuts.off(searchAgainKey);
   }
 
   getCurrentLine() {
     const { codeMirror } = this.state.editor;
     const { selectedSource } = this.props;
     if (!selectedSource) {
       return;
     }
@@ -324,20 +311,16 @@ class Editor extends PureComponent<Props
 
     const { codeMirror } = this.state.editor;
     if (codeMirror.listSelections().length > 1) {
       codeMirror.execCommand("singleSelection");
       e.preventDefault();
     }
   };
 
-  onSearchAgain = (_, e: KeyboardEvent) => {
-    this.props.traverseResults(this.props.cx, e.shiftKey, this.state.editor);
-  };
-
   openMenu(event: MouseEvent) {
     event.stopPropagation();
     event.preventDefault();
 
     const {
       cx,
       selectedSource,
       breakpointActions,
--- a/devtools/client/debugger/src/components/PrimaryPanes/Sources.css
+++ b/devtools/client/debugger/src/components/PrimaryPanes/Sources.css
@@ -29,17 +29,17 @@
 .sources-clear-root .home {
   background-color: var(--theme-icon-dimmed-color);
 }
 
 .sources-clear-root .breadcrumb {
   width: 5px;
   margin: 0 2px 0 6px;
   vertical-align: bottom;
-  background: var(--theme-body-color-alt);
+  background: var(--theme-text-color-alt);
 }
 
 .sources-clear-root-label {
   margin-left: 5px;
   line-height: 16px;
 }
 
 .sources-pane {
--- a/devtools/client/debugger/src/components/ProjectSearch.css
+++ b/devtools/client/debugger/src/components/ProjectSearch.css
@@ -63,17 +63,17 @@
   overflow-x: hidden;
 }
 
 .project-text-search .tree-indent {
   display: none;
 }
 
 .project-text-search .no-result-msg {
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   font-size: 24px;
   padding: 4px 15px;
   max-width: 100%;
   overflow-wrap: break-word;
   hyphens: auto;
 }
 
 .project-text-search .file-result {
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
@@ -38,17 +38,17 @@
 
 .breakpoints-list .breakpoint-heading .filename span {
   opacity: 0.7;
   padding-left: 4px;
 }
 
 .breakpoints-list .breakpoint-heading,
 .breakpoints-list .breakpoint {
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
   position: relative;
   cursor: pointer;
 }
 
 .breakpoints-list .breakpoint-heading,
 .breakpoints-list .breakpoint,
 .breakpoints-exceptions,
 .breakpoints-exceptions-caught {
--- a/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.css
@@ -23,17 +23,17 @@ html[dir="rtl"] .command-bar {
   flex-grow: 1;
 }
 
 .command-bar .replay-inactive {
   opacity: 0.5;
 }
 
 .command-bar .step-position {
-  color: var(--theme-comment-alt);
+  color: var(--theme-text-color-inactive);
   padding-top: 8px;
   margin-inline-end: 4px;
 }
 
 .command-bar .replay-active {
   color: var(--theme-highlight-blue);
 }
 
--- a/devtools/client/debugger/src/components/SecondaryPanes/Workers.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Workers.css
@@ -8,17 +8,17 @@
 
 .workers-list * {
   -moz-user-select: none;
   user-select: none;
 }
 
 .workers-list > .worker {
   font-size: inherit;
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
   padding: 2px 6px;
   padding-inline-start: 20px;
   line-height: 16px;
   position: relative;
   cursor: pointer;
   display: flex;
   align-items: center;
 }
--- a/devtools/client/debugger/src/components/ShortcutsModal.css
+++ b/devtools/client/debugger/src/components/ShortcutsModal.css
@@ -7,17 +7,17 @@
   column-width: 250px;
   cursor: default;
   user-select: none;
 }
 
 .shortcuts-content h2 {
   margin-top: 2px;
   margin-bottom: 2px;
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
 
 .shortcuts-section {
   display: inline-block;
   margin: 5px;
   margin-bottom: 15px;
   width: 250px;
 }
--- a/devtools/client/debugger/src/components/shared/ResultList.css
+++ b/devtools/client/debugger/src/components/shared/ResultList.css
@@ -119,17 +119,17 @@
   white-space: nowrap;
 }
 
 .theme-dark .result-list.big li.selected .subtitle {
   color: white;
 }
 
 .theme-dark .result-list.big li .subtitle {
-  color: var(--theme-comment-alt);
+  color: var(--theme-text-color-inactive);
 }
 
 .search-bar .result-list li.selected .subtitle {
   color: white;
 }
 
 .search-bar .result-list {
   border-bottom: 1px solid var(--theme-splitter-color);
--- a/devtools/client/debugger/src/components/shared/SearchInput.css
+++ b/devtools/client/debugger/src/components/shared/SearchInput.css
@@ -95,17 +95,17 @@
 }
 
 .search-field-summary {
   align-self: center;
   padding: 2px 4px;
   white-space: nowrap;
   text-align: center;
   user-select: none;
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 .search-field .search-nav-buttons {
   display: flex;
   user-select: none;
 }
 
 .search-field .search-nav-buttons .nav-btn {
--- a/devtools/client/debugger/src/components/shared/reps.css
+++ b/devtools/client/debugger/src/components/shared/reps.css
@@ -5,17 +5,17 @@
 
 .theme-dark,
 .theme-light {
   --number-color: var(--theme-highlight-green);
   --string-color: var(--theme-highlight-orange);
   --null-color: var(--theme-comment);
   --object-color: var(--theme-body-color);
   --caption-color: var(--theme-highlight-blue);
-  --location-color: var(--theme-content-color1);
+  --location-color: var(--theme-text-color-strong);
   --source-link-color: var(--theme-highlight-blue);
   --node-color: var(--theme-highlight-bluegrey);
   --reference-color: var(--theme-highlight-purple);
 }
 
 /******************************************************************************/
 
 .objectLink:hover {
--- a/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js
@@ -44,16 +44,22 @@ add_task(async function() {
   is(state.posFrom.line, 3);
 
   pressKey(dbg, "Enter");
   is(state.posFrom.line, 4);
 
   pressKey(dbg, "ShiftEnter");
   is(state.posFrom.line, 3);
 
+  pressKey(dbg, "fileSearchNext");
+  is(state.posFrom.line, 4);
+
+  pressKey(dbg, "fileSearchPrev");
+  is(state.posFrom.line, 3);
+
   pressKey(dbg, "fileSearch");
   type(dbg, "fun");
 
   pressKey(dbg, "Enter");
   is(state.posFrom.line, 4);
 
   // selecting another source keeps search open
   await selectSource(dbg, "simple2");
--- a/devtools/client/debugger/test/mochitest/helpers.js
+++ b/devtools/client/debugger/test/mochitest/helpers.js
@@ -1039,16 +1039,18 @@ const startKey = isMac
 const keyMappings = {
   close: { code: "w", modifiers: cmdOrCtrl },
   debugger: { code: "s", modifiers: shiftOrAlt },
   inspector: { code: "c", modifiers: shiftOrAlt },
   quickOpen: { code: "p", modifiers: cmdOrCtrl },
   quickOpenFunc: { code: "o", modifiers: cmdShift },
   quickOpenLine: { code: ":", modifiers: cmdOrCtrl },
   fileSearch: { code: "f", modifiers: cmdOrCtrl },
+  fileSearchNext: { code: "g", modifiers: cmdOrCtrl },
+  fileSearchPrev: { code: "g", modifiers: cmdShift },
   Enter: { code: "VK_RETURN" },
   ShiftEnter: { code: "VK_RETURN", modifiers: shiftOrAlt },
   Up: { code: "VK_UP" },
   Down: { code: "VK_DOWN" },
   Right: { code: "VK_RIGHT" },
   Left: { code: "VK_LEFT" },
   End: endKey,
   Start: startKey,
--- a/devtools/client/inspector/layout/components/Accordion.css
+++ b/devtools/client/inspector/layout/components/Accordion.css
@@ -38,17 +38,17 @@
   -moz-user-select: none;
 }
 
 .accordion ._header:hover {
   background-color: var(--theme-toolbar-hover);
 }
 
 .accordion ._header:hover svg {
-  fill: var(--theme-comment-alt);
+  fill: var(--theme-text-color-inactive);
 }
 
 .accordion ._header .truncate {
   display: block;
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
 }
--- a/devtools/client/jsonview/css/text-panel.css
+++ b/devtools/client/jsonview/css/text-panel.css
@@ -9,10 +9,10 @@
 .textPanelBox {
   height: 100%;
 }
 
 .textPanelBox pre {
   margin: 8px;
   white-space: pre-wrap;
   font-family: var(--monospace-font-family);
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
--- a/devtools/client/netmonitor/src/assets/styles/NetworkDetailsPanel.css
+++ b/devtools/client/netmonitor/src/assets/styles/NetworkDetailsPanel.css
@@ -42,17 +42,17 @@
 
 .network-monitor .properties-view .devtools-searchbox input {
   margin: 1px 3px;
 }
 
 /* Empty notices in tab panels */
 
 .network-monitor .empty-notice {
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   padding: 3px 8px;
   text-align: center;
   display: flex;
   align-items: center;
   justify-content: center;
   height: 100%;
   font-size: 24px;
 }
--- a/devtools/client/netmonitor/src/assets/styles/RequestList.css
+++ b/devtools/client/netmonitor/src/assets/styles/RequestList.css
@@ -212,21 +212,21 @@
   transition: box-shadow 0.5s ease-in-out;
 }
 
 .request-list-item.selected .requests-list-status-icon {
   filter: brightness(1.3);
 }
 
 .requests-list-status-icon:not([data-code]) {
-  background-color: var(--theme-content-color2);
+  background-color: var(--theme-body-color);
 }
 
 .requests-list-status-icon[data-code="cached"] {
-  border: 2px solid var(--theme-content-color2);
+  border: 2px solid var(--theme-body-color);
   background-color: transparent;
 }
 
 .requests-list-status-icon[data-code^="1"] {
   background-color: var(--status-code-color-1xx);
 }
 
 .requests-list-status-icon[data-code^="2"] {
@@ -322,17 +322,17 @@
 }
 
 .request-list-item .status-code {
   margin-inline-start: 0px;
 }
 
 .requests-list-cause-stack {
   display: inline-block;
-  background-color: var(--theme-body-color-alt);
+  background-color: var(--theme-text-color-alt);
   color: var(--theme-body-background);
   font-size: 8px;
   font-weight: bold;
   line-height: 10px;
   border-radius: 3px;
   padding: 0 2px;
   margin: 0;
   margin-inline-end: 3px;
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -13,16 +13,19 @@ const { HTMLTooltip } = require("devtool
 const Actions = require("../actions/index");
 const { formDataURI } = require("../utils/request-utils");
 const {
   getDisplayedRequests,
   getSelectedRequest,
   getWaterfallScale,
 } = require("../selectors/index");
 
+loader.lazyRequireGetter(this, "openRequestInTab",
+  "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab",
+  true);
 loader.lazyGetter(this, "setImageTooltip", function() {
   return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
     .setImageTooltip;
 });
 loader.lazyGetter(this, "getImageDimensions", function() {
   return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
     .getImageDimensions;
 });
@@ -71,16 +74,18 @@ class RequestListContent extends Compone
 
   constructor(props) {
     super(props);
     this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
     this.onHover = this.onHover.bind(this);
     this.onScroll = this.onScroll.bind(this);
     this.onResize = this.onResize.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
+    this.openRequestInTab = this.openRequestInTab.bind(this);
+    this.onDoubleClick = this.onDoubleClick.bind(this);
     this.onContextMenu = this.onContextMenu.bind(this);
     this.onFocusedNodeChange = this.onFocusedNodeChange.bind(this);
   }
 
   componentWillMount() {
     this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
     window.addEventListener("resize", this.onResize);
   }
@@ -232,32 +237,50 @@ class RequestListContent extends Compone
     if (delta) {
       // Prevent scrolling when pressing navigation keys.
       evt.preventDefault();
       evt.stopPropagation();
       this.props.onSelectDelta(delta);
     }
   }
 
+  /**
+   * Opens selected item in a new tab.
+   */
+  async openRequestInTab(id, url, requestHeaders, requestPostData) {
+    requestHeaders = requestHeaders ||
+      await this.props.connector.requestData(id, "requestHeaders");
+
+    requestPostData = requestPostData ||
+      await this.props.connector.requestData(id, "requestPostData");
+
+    openRequestInTab(url, requestHeaders, requestPostData);
+  }
+
+  onDoubleClick({ id, url, requestHeaders, requestPostData }) {
+    this.openRequestInTab(id, url, requestHeaders, requestPostData);
+  }
+
   onContextMenu(evt) {
     evt.preventDefault();
     const { selectedRequest, displayedRequests } = this.props;
 
     if (!this.contextMenu) {
       const {
         connector,
         cloneSelectedRequest,
         sendCustomRequest,
         openStatistics,
       } = this.props;
       this.contextMenu = new RequestListContextMenu({
         connector,
         cloneSelectedRequest,
         sendCustomRequest,
         openStatistics,
+        openRequestInTab: this.openRequestInTab,
       });
     }
 
     this.contextMenu.open(evt, selectedRequest, displayedRequests);
   }
 
   /**
    * If selection has just changed (by keyboard navigation), don't keep the list
@@ -304,16 +327,17 @@ class RequestListContent extends Compone
               connector,
               columns,
               item,
               index,
               isSelected: item.id === (selectedRequest && selectedRequest.id),
               key: item.id,
               onContextMenu: this.onContextMenu,
               onFocusedNodeChange: this.onFocusedNodeChange,
+              onDoubleClick: () => this.onDoubleClick(item),
               onMouseDown: () => onItemMouseDown(item.id),
               onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
               onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
               onWaterfallMouseDown: () => onWaterfallMouseDown(),
               requestFilterTypes,
             }))
           ) // end of requests-list-row-group">
         )
--- a/devtools/client/netmonitor/src/components/RequestListItem.js
+++ b/devtools/client/netmonitor/src/components/RequestListItem.js
@@ -128,16 +128,17 @@ class RequestListItem extends Component 
       connector: PropTypes.object.isRequired,
       columns: PropTypes.object.isRequired,
       item: PropTypes.object.isRequired,
       index: PropTypes.number.isRequired,
       isSelected: PropTypes.bool.isRequired,
       firstRequestStartedMillis: PropTypes.number.isRequired,
       fromCache: PropTypes.bool,
       onCauseBadgeMouseDown: PropTypes.func.isRequired,
+      onDoubleClick: PropTypes.func.isRequired,
       onContextMenu: PropTypes.func.isRequired,
       onFocusedNodeChange: PropTypes.func,
       onMouseDown: PropTypes.func.isRequired,
       onSecurityIconMouseDown: PropTypes.func.isRequired,
       onWaterfallMouseDown: PropTypes.func.isRequired,
       requestFilterTypes: PropTypes.object.isRequired,
       waterfallWidth: PropTypes.number,
     };
@@ -188,16 +189,17 @@ class RequestListItem extends Component 
     const {
       connector,
       columns,
       item,
       index,
       isSelected,
       firstRequestStartedMillis,
       fromCache,
+      onDoubleClick,
       onContextMenu,
       onMouseDown,
       onCauseBadgeMouseDown,
       onSecurityIconMouseDown,
       onWaterfallMouseDown,
     } = this.props;
 
     const classList = ["request-list-item", index % 2 ? "odd" : "even"];
@@ -207,16 +209,17 @@ class RequestListItem extends Component 
     return (
       dom.tr({
         ref: "listItem",
         className: classList.join(" "),
         "data-id": item.id,
         tabIndex: 0,
         onContextMenu,
         onMouseDown,
+        onDoubleClick,
       },
         columns.status && RequestListColumnStatus({ item }),
         columns.method && RequestListColumnMethod({ item }),
         columns.domain && RequestListColumnDomain({
             item,
             onSecurityIconMouseDown,
         }),
         columns.file && RequestListColumnFile({ item }),
--- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
@@ -13,17 +13,16 @@ const {
   getUrlBaseName,
   parseQueryString,
 } = require("../utils/request-utils");
 
 loader.lazyRequireGetter(this, "Curl", "devtools/client/shared/curl", true);
 loader.lazyRequireGetter(this, "saveAs", "devtools/client/shared/file-saver", true);
 loader.lazyRequireGetter(this, "copyString", "devtools/shared/platform/clipboard", true);
 loader.lazyRequireGetter(this, "showMenu", "devtools/client/shared/components/menu/utils", true);
-loader.lazyRequireGetter(this, "openRequestInTab", "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab", true);
 loader.lazyRequireGetter(this, "HarMenuUtils", "devtools/client/netmonitor/src/har/har-menu-utils", true);
 
 class RequestListContextMenu {
   constructor(props) {
     this.props = props;
   }
 
   open(event, selectedRequest, requests) {
@@ -44,16 +43,17 @@ class RequestListContextMenu {
       responseContentAvailable,
       url,
     } = selectedRequest;
     const {
       connector,
       cloneSelectedRequest,
       sendCustomRequest,
       openStatistics,
+      openRequestInTab,
     } = this.props;
     const menu = [];
     const copySubmenu = [];
 
     copySubmenu.push({
       id: "request-list-context-copy-url",
       label: L10N.getStr("netmonitor.context.copyUrl"),
       accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
@@ -197,17 +197,17 @@ class RequestListContextMenu {
       visible: copySubmenu.slice(15, 16).some((subMenu) => subMenu.visible),
     });
 
     menu.push({
       id: "request-list-context-newtab",
       label: L10N.getStr("netmonitor.context.newTab"),
       accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"),
       visible: !!selectedRequest,
-      click: () => this.openRequestInTab(id, url, requestHeaders, requestPostData),
+      click: () => openRequestInTab(id, url, requestHeaders, requestPostData),
     });
 
     menu.push({
       id: "request-list-context-open-in-debugger",
       label: L10N.getStr("netmonitor.context.openInDebugger"),
       accesskey: L10N.getStr("netmonitor.context.openInDebugger.accesskey"),
       visible: !!(selectedRequest && mimeType && mimeType.includes("javascript")),
       click: () => this.openInDebugger(url),
@@ -233,29 +233,16 @@ class RequestListContextMenu {
 
     showMenu(menu, {
       screenX: event.screenX,
       screenY: event.screenY,
     });
   }
 
   /**
-   * Opens selected item in a new tab.
-   */
-  async openRequestInTab(id, url, requestHeaders, requestPostData) {
-    requestHeaders = requestHeaders ||
-      await this.props.connector.requestData(id, "requestHeaders");
-
-    requestPostData = requestPostData ||
-      await this.props.connector.requestData(id, "requestPostData");
-
-    openRequestInTab(url, requestHeaders, requestPostData);
-  }
-
-  /**
    * Opens selected item in the debugger
    */
   openInDebugger(url) {
     const toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget());
     toolbox.viewSourceInDebugger(url, 0);
   }
 
   /**
--- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
+++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
- * Tests if Open in new tab works.
+ * Tests if Open in new tab works by ContextMenu.
  */
 
 add_task(async function() {
   const { tab, monitor } = await initNetMonitor(OPEN_REQUEST_IN_TAB_URL);
   info("Starting test...");
 
   const { document, store, windowRequire } = monitor.panelWin;
   const contextMenuDoc = monitor.panelWin.parent.document;
@@ -18,36 +18,38 @@ add_task(async function() {
 
   store.dispatch(Actions.batchEnable(false));
 
   // Post data may be fetched by the Header panel,
   // so set the Timings panel as the new default.
   store.getState().ui.detailsPanelSelectedTab = "timings";
 
   // Open GET request in new tab
-  await performRequest("GET");
+  await performRequest(monitor, tab, "GET");
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "GET");
   gBrowser.removeCurrentTab();
 
   // Open POST request in new tab
-  await performRequest("POST", "application/x-www-form-urlencoded", "foo=bar&baz=42");
+  await performRequest(monitor, tab, "POST", "application/x-www-form-urlencoded",
+    "foo=bar&baz=42");
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "POST", "application/x-www-form-urlencoded",
     "foo=bar&amp;baz=42");
   gBrowser.removeCurrentTab();
 
   // Open POST application/json request in new tab
-  await performRequest("POST", "application/json", '{"foo":"bar"}');
+  await performRequest(monitor, tab, "POST", "application/json", '{"foo":"bar"}');
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "POST", "application/json", '{"foo":"bar"}');
   gBrowser.removeCurrentTab();
 
   await teardown(monitor);
 
+  // OpenLastRequestInTab by ContextMenu
   async function openLastRequestInTab() {
     const wait = waitForDOM(contextMenuDoc, "#request-list-context-newtab");
     const requestItems = document.querySelectorAll(".request-list-item");
     const lastRequest = requestItems[requestItems.length - 1];
     EventUtils.sendMouseEvent({ type: "mousedown" }, lastRequest);
     EventUtils.sendMouseEvent({ type: "contextmenu" }, lastRequest);
     await wait;
 
@@ -58,32 +60,90 @@ add_task(async function() {
     info("A new tab has been opened");
 
     const awaitedTab = gBrowser.selectedTab;
     await BrowserTestUtils.browserLoaded(awaitedTab.linkedBrowser);
     info("The tab load completed");
 
     return awaitedTab;
   }
+});
 
-  async function performRequest(method, contentType, payload) {
-    const wait = waitForNetworkEvents(monitor, 1);
-    await ContentTask.spawn(tab.linkedBrowser, [method, contentType, payload],
-      async function([method_, contentType_, payload_]) {
-        content.wrappedJSObject.performRequest(method_, contentType_, payload_);
-      }
-    );
-    await wait;
-    info("Performed request to test server");
-  }
+/**
+ * Tests if Open in new tab works by DoubleClick RequestItem.
+ */
+
+add_task(async function() {
+  const { tab, monitor } = await initNetMonitor(OPEN_REQUEST_IN_TAB_URL);
+  info("Starting test...");
+
+  const { document, store, windowRequire } = monitor.panelWin;
+  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+  let newTab;
+
+  store.dispatch(Actions.batchEnable(false));
+
+  // Post data may be fetched by the Header panel,
+  // so set the Timings panel as the new default.
+  store.getState().ui.detailsPanelSelectedTab = "timings";
+
+  // Open GET request in new tab
+  await performRequest(monitor, tab, "GET");
+  newTab = await openLastRequestInTab();
+  await checkTabResponse(newTab, "GET");
+  gBrowser.removeCurrentTab();
 
-  async function checkTabResponse(checkedTab, method, contentType, payload) {
-    await ContentTask.spawn(checkedTab.linkedBrowser, [method, contentType, payload],
-      async function([method_, contentType_, payload_]) {
-        const { body } = content.wrappedJSObject.document;
-        const expected = [method_, contentType_, payload_].join("\n");
-        info("Response from the server:" + body.innerHTML.replace(/\n/g, "\\n"));
-        ok(body.innerHTML.includes(expected),
-          "Tab method and data match original request");
-      }
-    );
+  // Open POST request in new tab
+  await performRequest(monitor, tab, "POST", "application/x-www-form-urlencoded",
+    "foo=bar&baz=42");
+  newTab = await openLastRequestInTab();
+  await checkTabResponse(newTab, "POST", "application/x-www-form-urlencoded",
+    "foo=bar&amp;baz=42");
+  gBrowser.removeCurrentTab();
+
+  // Open POST application/json request in new tab
+  await performRequest(monitor, tab, "POST", "application/json", '{"foo":"bar"}');
+  newTab = await openLastRequestInTab();
+  await checkTabResponse(newTab, "POST", "application/json", '{"foo":"bar"}');
+  gBrowser.removeCurrentTab();
+
+  await teardown(monitor);
+
+  // OpenLastRequestInTab by DoubleClick
+  async function openLastRequestInTab() {
+    const requestItems = document.querySelectorAll(".request-list-item");
+    const lastRequest = requestItems[requestItems.length - 1];
+
+    const onTabOpen = once(gBrowser.tabContainer, "TabOpen", false);
+    EventUtils.sendMouseEvent({ type: "dblclick" }, lastRequest);
+    await onTabOpen;
+    info("A new tab has been opened");
+
+    const awaitedTab = gBrowser.selectedTab;
+    await BrowserTestUtils.browserLoaded(awaitedTab.linkedBrowser);
+    info("The tab load completed");
+
+    return awaitedTab;
   }
 });
+
+async function performRequest(monitor, tab, method, contentType, payload) {
+  const wait = waitForNetworkEvents(monitor, 1);
+  await ContentTask.spawn(tab.linkedBrowser, [method, contentType, payload],
+    async function([method_, contentType_, payload_]) {
+      content.wrappedJSObject.performRequest(method_, contentType_, payload_);
+    }
+  );
+  await wait;
+  info("Performed request to test server");
+}
+
+async function checkTabResponse(checkedTab, method, contentType, payload) {
+  await ContentTask.spawn(checkedTab.linkedBrowser, [method, contentType, payload],
+    async function([method_, contentType_, payload_]) {
+      const { body } = content.wrappedJSObject.document;
+      const expected = [method_, contentType_, payload_].join("\n");
+      info("Response from the server:" + body.innerHTML.replace(/\n/g, "\\n"));
+      ok(body.innerHTML.includes(expected),
+        "Tab method and data match original request");
+    }
+  );
+}
--- a/devtools/client/performance/modules/widgets/markers-overview.js
+++ b/devtools/client/performance/modules/widgets/markers-overview.js
@@ -220,15 +220,15 @@ MarkersOverview.prototype = extend(Abstr
     this.theme = theme = theme || "light";
     this.backgroundColor = getColor("body-background", theme);
     this.selectionBackgroundColor = colorUtils.setAlpha(
       getColor("selection-background", theme), 0.25);
     this.selectionStripesColor = colorUtils.setAlpha("#fff", 0.1);
     this.headerBackgroundColor = getColor("body-background", theme);
     this.headerTextColor = getColor("body-color", theme);
     this.headerTimelineStrokeColor = colorUtils.setAlpha(
-      getColor("body-color-alt", theme), 0.25);
+      getColor("text-color-alt", theme), 0.25);
     this.alternatingBackgroundColor = colorUtils.setAlpha(
       getColor("body-color", theme), 0.05);
   },
 });
 
 exports.MarkersOverview = MarkersOverview;
--- a/devtools/client/performance/test/browser_perf-marker-details.js
+++ b/devtools/client/performance/test/browser_perf-marker-details.js
@@ -39,18 +39,18 @@ async function spawnTest() {
   info("All the markers should be displayed by default.");
 
   let bars = Array.prototype.filter.call($$(".waterfall-marker-bar"),
              (bar) => MARKER_TYPES.includes(bar.getAttribute("type")));
   let markers = PerformanceController.getCurrentRecording().getMarkers()
                 .filter(m => MARKER_TYPES.includes(m.name));
 
   info(`Got ${bars.length} bars and ${markers.length} markers.`);
-  info("Markers types from datasrc: " + Array.map(markers, e => e.name));
-  info("Markers names from sidebar: " + Array.map(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
+  info("Markers types from datasrc: " + Array.from(markers, e => e.name));
+  info("Markers names from sidebar: " + Array.from(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
 
   ok(bars.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (1)`);
   ok(markers.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (2)`);
 
   // Sanity check that markers are in chronologically ascending order
   markers.reduce((previous, m) => {
     if (m.start <= previous) {
       ok(false, "Markers are not in order");
--- a/devtools/client/performance/test/browser_perf-tree-view-03.js
+++ b/devtools/client/performance/test/browser_perf-tree-view-03.js
@@ -24,17 +24,18 @@ add_task(function() {
 
   const $$fun = i => container.querySelectorAll(".call-tree-cell[type=function]")[i];
   const $$nam = i => container.querySelectorAll(
     ".call-tree-cell[type=function] > .call-tree-name")[i];
   const $$dur = i => container.querySelectorAll(".call-tree-cell[type=duration]")[i];
 
   is(container.childNodes.length, 7,
     "The container node should have all children available.");
-  is(Array.filter(container.childNodes, e => e.className != "call-tree-item").length, 0,
+  is(Array.from(container.childNodes).filter(e => e.className != "call-tree-item").length,
+     0,
     "All item nodes in the tree have the correct class name.");
 
   is($$fun(0).style.marginInlineStart, "0px",
     "The root node's function cell has the correct indentation.");
   is($$fun(1).style.marginInlineStart, "16px",
     "The .A node's function cell has the correct indentation.");
   is($$fun(2).style.marginInlineStart, "32px",
     "The .A.B node's function cell has the correct indentation.");
--- a/devtools/client/performance/test/browser_timeline-waterfall-rerender.js
+++ b/devtools/client/performance/test/browser_timeline-waterfall-rerender.js
@@ -45,32 +45,32 @@ async function spawnTest() {
   // Focus the second item in the tree.
   WaterfallView._markersRoot.getChild(1).focus();
 
   let beforeResizeBarsCount = $$(".waterfall-marker-bar").length;
   info("Before resize bars count: " + beforeResizeBarsCount);
   ok(beforeResizeBarsCount < initialBarsCount,
     "A subset of the total markers was selected.");
 
-  is(Array.indexOf($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
+  is(Array.prototype.indexOf.call($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
     "The correct item was focused in the tree.");
   ok(!$("#waterfall-details").hidden,
     "The waterfall sidebar is now visible.");
 
   // Simulate a resize on the marker details.
   rerendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
   EventUtils.sendMouseEvent({ type: "mouseup" }, WaterfallView.detailsSplitter);
   await rerendered;
 
   let afterResizeBarsCount = $$(".waterfall-marker-bar").length;
   info("After resize bars count: " + afterResizeBarsCount);
   is(afterResizeBarsCount, beforeResizeBarsCount,
     "The same subset of the total markers remained visible.");
 
-  is(Array.indexOf($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
+  is(Array.prototype.indexOf.call($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
     "The correct item is still focused in the tree.");
   ok(!$("#waterfall-details").hidden,
     "The waterfall sidebar is still visible.");
 
   await teardown(panel);
   finish();
 }
 /* eslint-enable */
--- a/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js
+++ b/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js
@@ -35,18 +35,18 @@ async function spawnTest() {
 
   info("No need to select everything in the timeline.");
   info("All the markers should be displayed by default.");
 
   let bars = $$(".waterfall-marker-bar");
   let markers = PerformanceController.getCurrentRecording().getMarkers();
 
   info(`Got ${bars.length} bars and ${markers.length} markers.`);
-  info("Markers types from datasrc: " + Array.map(markers, e => e.name));
-  info("Markers names from sidebar: " + Array.map(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
+  info("Markers types from datasrc: " + Array.from(markers, e => e.name));
+  info("Markers names from sidebar: " + Array.from(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
 
   ok(bars.length > 2, "Got at least 3 markers (1)");
   ok(markers.length > 2, "Got at least 3 markers (2)");
 
   let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms);
 
   for (let i = 0; i < bars.length; i++) {
     let bar = bars[i];
--- a/devtools/client/performance/views/toolbar.js
+++ b/devtools/client/performance/views/toolbar.js
@@ -138,17 +138,18 @@ const ToolbarView = {
   },
 
   /**
    * Fired when a menu item in the markers filter popup is checked or unchecked.
    */
   _onHiddenMarkersChanged: function() {
     const checkedMenuItems =
       $$("#performance-filter-menupopup menuitem[marker-type]:not([checked])");
-    const hiddenMarkers = Array.map(checkedMenuItems, e => e.getAttribute("marker-type"));
+    const hiddenMarkers =
+      Array.from(checkedMenuItems, e => e.getAttribute("marker-type"));
     PerformanceController.setPref("hidden-markers", hiddenMarkers);
   },
 
   /**
    * Fired when a preference changes in the underlying OptionsView.
    * Propogated by the PerformanceController.
    */
   _onPrefChanged: function(prefName) {
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -647,27 +647,27 @@ input:-moz-focusring {
 
 #device-form label {
   display: flex;
   flex-direction: column;
   margin: 5px;
 }
 
 #device-form label > .viewport-dimension {
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   display: flex;
   align-items: center;
 }
 
 #device-form input {
   background: transparent;
   border: 1px solid;
   border-radius: 2px;
   text-align: center;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   transition: all 0.25s ease;
 }
 
 #device-form #device-form-name input,
 #device-form #device-form-user-agent input {
   text-align: left;
   padding-left: 12px;
   padding-right: 12px;
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -1227,17 +1227,17 @@ var Scratchpad = {
     const maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
     const recentFilesMenu = document.getElementById("sp-open_recent-menu");
 
     if (maxRecent < 1) {
       recentFilesMenu.setAttribute("hidden", true);
       return;
     }
 
-    const recentFilesPopup = recentFilesMenu.firstChild;
+    const recentFilesPopup = recentFilesMenu.menupopup;
     const filePaths = this.getRecentFiles();
     const filename = this.getState().filename;
 
     recentFilesMenu.setAttribute("disabled", true);
     while (recentFilesPopup.hasChildNodes()) {
       recentFilesPopup.firstChild.remove();
     }
 
@@ -1300,17 +1300,17 @@ var Scratchpad = {
     const maxRecent = Services.prefs.getIntPref(PREF_RECENT_FILES_MAX);
     const menu = document.getElementById("sp-open_recent-menu");
 
     // Hide the menu if the 'PREF_RECENT_FILES_MAX'-pref is set to zero or less.
     if (maxRecent < 1) {
       menu.setAttribute("hidden", true);
     } else {
       if (menu.hasAttribute("hidden")) {
-        if (!menu.firstChild.hasChildNodes()) {
+        if (!menu.menupopup.hasChildNodes()) {
           this.populateRecentFilesMenu();
         }
 
         menu.removeAttribute("hidden");
       }
 
       const filePaths = this.getRecentFiles();
       if (maxRecent < filePaths.length) {
--- a/devtools/client/shared/components/tree/TreeView.css
+++ b/devtools/client/shared/components/tree/TreeView.css
@@ -49,17 +49,17 @@
   width: 100%;
   height: 20px;
   margin: 0;
   margin-inline-start: -2px;
   border: solid 1px transparent;
   outline: none;
   box-shadow: none;
   padding: 0 1px;
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
   background: var(--theme-sidebar-background);
 }
 
 .treeTable .treeValueCell.inputEnabled input:focus {
   border-color: var(--theme-textbox-box-shadow);
   transition: all 150ms ease-in-out;
 }
 
--- a/devtools/client/shared/sourceeditor/codemirror/mozilla.css
+++ b/devtools/client/shared/sourceeditor/codemirror/mozilla.css
@@ -99,45 +99,45 @@
 .cm-highlight {
   position: relative;
 }
 
 .cm-highlight:before {
   position: absolute;
   border-top-style: solid;
   border-bottom-style: solid;
-  border-top-color: var(--theme-comment-alt);
-  border-bottom-color: var(--theme-comment-alt);
+  border-top-color: var(--theme-text-color-inactive);
+  border-bottom-color: var(--theme-text-color-inactive);
   border-top-width: 1px;
   border-bottom-width: 1px;
   top: -1px;
   bottom: 0;
   left: 0;
   right: 0;
   content: "";
   margin-bottom: -1px;
 }
 
 .cm-highlight-full:before {
-  border: 1px solid var(--theme-comment-alt);
+  border: 1px solid var(--theme-text-color-inactive);
 }
 
 .cm-highlight-start:before {
   border-left-width: 1px;
   border-left-style: solid;
-  border-left-color: var(--theme-comment-alt);
+  border-left-color: var(--theme-text-color-inactive);
   margin: 0 0 -1px -1px;
   border-top-left-radius: 2px;
   border-bottom-left-radius: 2px;
 }
 
 .cm-highlight-end:before {
   border-right-width: 1px;
   border-right-style: solid;
-  border-right-color: var(--theme-comment-alt);
+  border-right-color: var(--theme-text-color-inactive);
   margin: 0 -1px -1px 0;
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
 }
 
 /* CodeMirror dialogs styling */
 
 .CodeMirror-dialog {
@@ -170,17 +170,17 @@
 
 .CodeMirror-foldgutter-open,
 .CodeMirror-foldgutter-folded {
   height: 14px;
   background-image: url("chrome://devtools/skin/images/arrow.svg");
   background-position: center;
   background-repeat: no-repeat;
   -moz-context-properties: fill;
-  fill: var(--theme-content-color3);
+  fill: var(--theme-icon-dimmed-color);
   /* make the icons smaller than regular twistys (10->8px) */
   background-size: 8px;
   cursor: pointer;
 }
 
 .CodeMirror-foldgutter-folded {
   transform: rotate(-90deg);
 }
--- a/devtools/client/shared/test/browser_theme.js
+++ b/devtools/client/shared/test/browser_theme.js
@@ -76,19 +76,18 @@ function testGetColor() {
   setTheme(originalTheme);
 }
 
 function testColorExistence() {
   const vars = [
     "body-background", "sidebar-background", "contrast-background",
     "tab-toolbar-background", "toolbar-background", "selection-background",
     "selection-color", "selection-background-hover", "splitter-color",
-    "comment", "body-color", "body-color-alt", "content-color1", "content-color2",
-    "content-color3", "highlight-green", "highlight-blue", "highlight-bluegrey",
-    "highlight-purple", "highlight-lightorange", "highlight-orange", "highlight-red",
-    "highlight-pink",
+    "comment", "body-color", "text-color-alt", "text-color-inactive", "text-color-strong",
+    "highlight-green", "highlight-blue", "highlight-bluegrey", "highlight-purple",
+    "highlight-lightorange", "highlight-orange", "highlight-red", "highlight-pink",
   ];
 
   for (const type of vars) {
     ok(getColor(type, "light"), `${type} is a valid color in light theme`);
     ok(getColor(type, "dark"), `${type} is a valid color in dark theme`);
   }
 }
--- a/devtools/client/shared/widgets/AbstractTreeItem.jsm
+++ b/devtools/client/shared/widgets/AbstractTreeItem.jsm
@@ -465,17 +465,18 @@ AbstractTreeItem.prototype = {
    *
    * @param number delta
    *        The offset from this item to the target item.
    * @return Node
    *         The element displaying the target item at the specified offset.
    */
   _getSiblingAtDelta: function(delta) {
     const childNodes = this._containerNode.childNodes;
-    const indexOfSelf = Array.indexOf(childNodes, this._targetNode);
+    const indexOfSelf =
+      Array.prototype.indexOf.call(childNodes, this._targetNode);
     if (indexOfSelf + delta >= 0) {
       return childNodes[indexOfSelf + delta];
     }
     return undefined;
   },
 
   _getNodesPerPageSize: function() {
     const childNodes = this._containerNode.childNodes;
--- a/devtools/client/shared/widgets/cubic-bezier.css
+++ b/devtools/client/shared/widgets/cubic-bezier.css
@@ -190,17 +190,17 @@
   border-radius: 3px;
   background-color: var(--theme-body-background);
   margin: 0 auto;
 }
 
 .cubic-bezier-container .preset p {
   font-size: 80%;
   margin: 2px auto 0px auto;
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
   text-transform: capitalize;
   text-overflow: ellipsis;
   overflow: hidden;
 }
 
 .cubic-bezier-container .active-preset p,
 .cubic-bezier-container .active-preset:hover p {
   color: var(--theme-body-color);
--- a/devtools/client/shared/widgets/filter-widget.css
+++ b/devtools/client/shared/widgets/filter-widget.css
@@ -174,17 +174,17 @@
 
 #filter-container .preset span {
   flex: 2 100%;
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
   display: block;
   order: 3;
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 #filter-container .remove-button {
   width: 16px;
   height: 16px;
   background: url(chrome://devtools/skin/images/close.svg);
   background-size: cover;
   font-size: 0;
--- a/devtools/client/themes/animation.css
+++ b/devtools/client/themes/animation.css
@@ -218,17 +218,17 @@ select.playback-rate-selector.devtools-b
   position: relative;
 }
 
 .animation-summary-graph.compositor::after {
   background-image: var(--fast-track-image);
   background-repeat: no-repeat;
   content: "";
   display: block;
-  fill: var(--theme-content-color3);
+  fill: var(--theme-icon-dimmed-color);
   height: 100%;
   position: absolute;
   right: 0;
   top: 5px;
   width: 15px;
   -moz-context-properties: fill;
 }
 
--- a/devtools/client/themes/breadcrumbs.css
+++ b/devtools/client/themes/breadcrumbs.css
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
  :root {
   --breadcrumb-id-class-color: #909090;
   --breadcrumb-pseudo-class-text-color: var(--yellow-70) ;
  }
 
  .theme-dark:root {
-  --breadcrumb-id-class-color: var(--theme-content-color1);
+  --breadcrumb-id-class-color: var(--theme-text-color-strong);
   --breadcrumb-pseudo-class-text-color: var(--yellow-50);
  }
 
 /* Inspector HTMLBreadcrumbs */
 
 .breadcrumbs-widget-container {
   margin-inline-end: 3px;
   max-height: 24px; /* Set max-height for proper sizing on linux */
--- a/devtools/client/themes/chart.css
+++ b/devtools/client/themes/chart.css
@@ -9,22 +9,22 @@
   transform: translateZ(1px);
 }
 
 .theme-dark .generic-chart-container {
   color: var(--theme-selection-color);
 }
 
 .theme-light .generic-chart-container {
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 .chart-colored-blob {
-  fill: var(--theme-content-color2);
-  background: var(--theme-content-color2);
+  fill: var(--theme-body-color);
+  background: var(--theme-body-color);
 }
 
 /* Chart: Pie */
 
 .pie-chart-slice {
   stroke-width: 1px;
   cursor: pointer;
 }
@@ -113,17 +113,17 @@
 }
 
 .table-chart-totals {
   margin-top: 8px;
   padding-top: 6px;
 }
 
 .table-chart-totals {
-  border-top: 1px solid var(--theme-body-color-alt); /* Grey foreground text */
+  border-top: 1px solid var(--theme-text-color-alt); /* Grey foreground text */
 }
 
 .table-chart-summary-label {
   font-weight: 600;
   padding: 1px 0px;
 }
 
 .theme-dark .table-chart-summary-label {
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -46,26 +46,26 @@
 :root[platform="linux"] {
   --monospace-font-family: monospace;
 }
 
 /**
  * Customize the dark theme's scrollbar colors to avoid excessive contrast.
  */
 :root.theme-dark {
-  scrollbar-color: var(--theme-body-color-inactive) var(--theme-splitter-color);
+  scrollbar-color: var(--grey-50) var(--theme-splitter-color);
 }
 
 /**
  * Customize scrollbar colors on Linux + light theme, to avoid visual conflicts
  * between the light theme and the selected GTK theme (see bug 1471163).
  * This removes GTK scrollbars and uses a fallback design (see bug 1464723).
  */
 :root[platform="linux"].theme-light {
-  scrollbar-color: var(--theme-body-color-inactive) var(--grey-20);
+  scrollbar-color: var(--grey-40) var(--grey-20);
 }
 
 .devtools-monospace {
   font-family: var(--monospace-font-family);
   font-size: var(--theme-code-font-size);
 }
 
 /**
--- a/devtools/client/themes/computed.css
+++ b/devtools/client/themes/computed.css
@@ -98,17 +98,17 @@
   vertical-align: -0.5px;
   width: 8px;
   height: 8px;
   margin: 0 1px;
   background-image: url(images/arrow-e.svg);
   background-repeat: no-repeat;
   background-size: contain;
   -moz-context-properties: fill;
-  fill: var(--theme-body-color-inactive);
+  fill: var(--theme-text-color-inactive);
 }
 
 .computed-other-property-value:dir(rtl)::before {
   transform: scaleX(-1);
 }
 
 .computed-property-value {
   overflow-x: hidden;
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -179,26 +179,26 @@ body {
 
 .CodeMirror.cm-s-mozilla  { /* Inherit platform specific font sizing and styles */
   font-family: inherit;
   font-size: inherit;
   background: transparent;
 }
 
 .CodeMirror.cm-s-mozilla  pre {
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
 
 .cm-s-mozilla .cm-operator {
   color: var(--theme-body-color);
 }
 
 .cm-s-mozilla .cm-variable-3,
 .cm-s-mozilla .cm-special {
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
 
 .cm-s-mozilla .CodeMirror-lines .CodeMirror-cursor {
   border-left: solid 1px #fff;
 }
 
 .cm-s-mozilla.CodeMirror-focused .CodeMirror-selected { /* selected text (focused) */
   background: var(--theme-selection-background-hover);
--- a/devtools/client/themes/fonts.css
+++ b/devtools/client/themes/fonts.css
@@ -2,27 +2,24 @@
  * 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/. */
 
 /* CSS Variables specific to the font editor that aren't defined by the themes */
 :root {
   --highlight-color: var(--blue-55);
   --input-background-color: white;
   --input-border-color: var(--grey-30);
-  --input-text-color: var(--grey-90);
   --preview-input-background: var(--theme-toolbar-background);
-  --secondary-label-color: var(--grey-45);
   --slider-thumb-color: var(--grey-50);
   --slider-track-color: var(--grey-30);
 }
 
 :root.theme-dark {
   --input-background-color: var(--grey-70);
   --input-border-color: var(--grey-70);
-  --input-text-color: var(--grey-40);
   --preview-input-background: #222225;
   --slider-thumb-color: var(--grey-40);
   --slider-track-color: var(--grey-60);
 }
 
 #sidebar-panel-fontinspector {
   margin: 0;
   display: flex;
@@ -116,17 +113,17 @@
   font-weight: normal;
   white-space: nowrap;
 }
 
 .font-name {
   display: inline-block;
   margin-bottom: 0.6em;
   font-size: 1em;
-  color: var(--grey-50);
+  color: var(--theme-text-color-alt);
 }
 
 .font-family-name {
   margin-bottom: 0.2em;
   font-size: 1.2em;
 }
 
 .font-group {
@@ -159,30 +156,30 @@
   background-color: var(--theme-selection-background-hover);
 }
 
 .font-css-code {
   direction: ltr;
   margin: 0;
   overflow: hidden;
   text-overflow: ellipsis;
-  color: var(--theme-toolbar-color);
+  color: var(--theme-text-color-strong);
   grid-column: span 2;
   position: relative;
   inset-inline-start: -4px;
 }
 
 .font-css-code-expander::before {
   content: "\2026";
   display: inline-block;
   width: 12px;
   height: 8px;
   margin: 0 2px;
   line-height: 3px;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   border-radius: 3px;
   border-style: solid;
   border-width: 1px;
   text-align: center;
   vertical-align: middle;
 }
 
 .font-control {
@@ -244,30 +241,30 @@
 .font-control-label-text {
   display: block;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
 .font-control-label-detail {
-  color: var(--secondary-label-color);
+  color: var(--theme-text-color-alt);
   font-size: smaller;
 }
 
 .font-value-input {
   margin-left: 10px;
   text-align: right;
   width: 60px;
   padding: 2px 3px;
 }
 
 .font-value-input,
 .font-value-select {
-  color: var(--input-text-color);
+  color: var(--theme-text-color-strong);
   border: 1px solid var(--input-border-color);
   background-color: var(--input-background-color);
 }
 
 /* Styles for disabled input fields */
 .font-value-input[disabled],
 .font-value-select[disabled],
 .font-value-slider[disabled] {
@@ -321,17 +318,17 @@
   -moz-appearance: none;
   box-shadow: none;
   padding: 1px 10px 1px 2px;
   min-width: 3.8em;
 }
 
 .font-value-select:-moz-focusring {
   color: transparent;
-  text-shadow: 0 0 0 var(--input-text-color);
+  text-shadow: 0 0 0 var(--theme-text-color-strong);
 }
 
 .font-value-input:focus,
 .font-value-select:focus {
   outline: 1px solid var(--highlight-color);
   outline-offset: -1px;
 }
 
@@ -340,17 +337,17 @@
   min-width: 50px;
   position: relative;
 }
 
 /* Firefox doesn't support pseudo-elements on inputs. Using the container instead. */
 .font-value-slider-container::before,
 .font-value-slider-container::after {
   -moz-user-select: none;
-  color: var(--secondary-label-color);
+  color: var(--theme-text-color-alt);
   font-size: smaller;
   position: absolute;
   bottom: -.6em;
   visibility: hidden;
 }
 
 .font-control-input:hover .font-value-slider-container::before,
 .font-control-input:hover .font-value-slider-container::after,
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -119,17 +119,17 @@ body.dragging .tag-line {
 .tag-line.drag-target::before {
   content: "";
   position: absolute;
   top: -1px;
   height: 2px;
   /* Offset these by 1000px to make sure they cover the full width of the view */
   width: calc(100% + 1000px);
   left: -1000px;
-  background-color: var(--theme-content-color2);
+  background-color: var(--theme-body-color);
 }
 
 .tag-line.drop-target::before {
   background-color: var(--theme-contrast-background);
 }
 
 /* In case the indicator is put on the closing .tag-line, the indentation level
  * will become misleading, so we push it forward to match the indentation level */
@@ -300,17 +300,17 @@ ul.children + .tag-line::before {
 }
 
 .editor.text pre,
 .editor.comment pre {
   font: inherit;
 }
 
 .theme-dark .editor.text pre {
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
 
 /* Whitespace only text nodes are sometimes shown in the markup-view, and when they do
    they get a greyed-out whitespace symbol so users know what they are */
 .editor.text .whitespace {
   padding: 0 .5em;
 }
 
@@ -318,17 +318,17 @@ ul.children + .tag-line::before {
   /* black circle (full) character */
   content: "\25cf";
   display: inline-block;
   width: 12px;
   height: 8px;
   margin: 0 2px;
   line-height: 7px;
   font-size: 0.6em;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   border-radius: 3px;
   border-style: solid;
   border-width: 1px;
   text-align: center;
   vertical-align: middle;
 }
 
 .tag-line[selected] .editor.text .whitespace::before {
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -240,17 +240,17 @@ html, body, #app, #memory-tool {
 .snapshot-list-item > .snapshot-title > input[type=checkbox] {
   margin: 0;
   margin-inline-end: 5px;
 }
 
 .snapshot-list-item > .snapshot-state,
 .snapshot-list-item > .snapshot-totals {
   font-size: 90%;
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 .snapshot-list-item.selected > .snapshot-state,
 .snapshot-list-item.selected > .snapshot-totals {
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
@@ -501,17 +501,17 @@ html, body, #app, #memory-tool {
 .heap-tree-percent,
 .heap-tree-item-name {
   white-space: nowrap;
 }
 
 .heap-tree-number {
   padding: 0 3px;
   flex: 1;
-  color: var(--theme-content-color3);
+  color: var(--theme-text-color-alt);
   /* Make sure number doesn't appear backwards on RTL locales */
   direction: ltr;
 }
 
 .heap-tree-percent {
   padding-inline-start: 3px;
   padding-inline-end: 3px;
 }
@@ -615,17 +615,17 @@ html, body, #app, #memory-tool {
  */
 
 svg {
   --arrow-color: var(--theme-splitter-color);
   --text-color: var(--theme-body-color);
 }
 
 .theme-dark svg {
-  --arrow-color: var(--theme-body-color-alt);
+  --arrow-color: var(--theme-text-color-alt);
 }
 
 svg #arrowhead {
   /* !important is needed to override inline style */
   fill: var(--arrow-color) !important;
 }
 
 .edgePath path {
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -197,23 +197,22 @@
   text-decoration: underline;
   cursor: pointer;
   font-size: 90%;
   padding:0;
 }
 
 .recording-list-item-duration,
 .recording-list-item-save {
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 .recording-list-item.selected .recording-list-item-duration,
 .recording-list-item.selected .recording-list-item-save {
-  color: var(--theme-body-color-alt);
-  color: var(--theme-selection-color);
+  color: inherit;
 }
 
 #recordings-list .selected label {
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
 /* Recording notices */
@@ -422,17 +421,17 @@
 .call-tree-column {
   color: var(--theme-highlight-orange);
   opacity: 0.6;
 }
 
 .call-tree-host {
   margin-inline-start: 8px !important;
   font-size: 90%;
-  color: var(--theme-content-color2);
+  color: var(--theme-body-color);
 }
 
 .call-tree-category {
   transform: scale(0.75);
   transform-origin: center right;
 }
 
 /**
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -1,26 +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/. */
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 :root {
   --rule-highlight-background-color: var(--theme-highlight-yellow);
-  --rule-overridden-item-border-color: var(--theme-content-color3);
   --rule-header-background-color: var(--theme-toolbar-background);
   --rule-pseudo-class-text-color: var(--yellow-70) ;
   /* This should be --yellow-50 but since we need an opacity of 0.4, we hard-code the
   resulting color here for now. */
   --rule-property-highlight-background-color: #FFF697;
 }
 
 :root.theme-dark {
   --rule-highlight-background-color: #521C76;
-  --rule-overridden-item-border-color: var(--theme-content-color1);
   --rule-header-background-color: #222225;
   --rule-pseudo-class-text-color: var(--yellow-50);
   /* This should be --yellow-50 but since we need an opacity of 0.3, we hard-code the
   resulting color here for now. */
   --rule-property-highlight-background-color: #605913;
 }
 
 /* Rule View Tabpanel */
@@ -114,17 +112,17 @@
 #ruleview-class-panel .classes label span {
   white-space: nowrap;
   text-overflow: ellipsis;
   overflow: hidden;
 }
 
 #ruleview-class-panel .no-classes {
   flex: 1;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-text-color-inactive);
   margin: 0;
   text-align: center;
 }
 
 /* Rule View Container */
 
 #ruleview-container {
   -moz-user-select: text;
@@ -410,39 +408,41 @@
   list-style: none;
   line-height: 1.5em;
 }
 
 .ruleview-overridden-item {
   position: relative;
 }
 
-.ruleview-overridden-item::before {
+.ruleview-overridden-item::before,
+.ruleview-overridden-item::after {
+  content: "";
   position: absolute;
-  left: -15px;
+  display: block;
+  border: 0px solid var(--theme-text-color-alt);
+}
+
+.ruleview-overridden-item::before {
   top: 0px;
-  content: '';
-  display: block;
-  border-left: 0.5px solid var(--rule-overridden-item-border-color);
+  left: -15px;
   height: 0.8em;
-  border-bottom: 0.5px solid var(--rule-overridden-item-border-color);
   width: 10px;
+  border-left-width: 0.5px;
+  border-bottom-width: 0.5px;
 }
 
 .ruleview-overridden-item::after {
-  position: absolute;
   left: -15px;
   bottom: -7px;
-  content: '';
-  display: block;
-  border-left: 0.5px solid var(--rule-overridden-item-border-color);
   height: 100%;
+  border-left-width: 0.5px;
 }
 
-.ruleview-overridden-item:last-child:after {
+.ruleview-overridden-item:last-child::after {
   display: none;
 }
 
 .ruleview-overridden-item .ruleview-namecontainer {
   margin-left: 0;
 }
 
 /* All swatches */
@@ -536,35 +536,31 @@
   background-image: url("chrome://devtools/skin/images/filter-swatch.svg");
 }
 
 .ruleview-angleswatch {
   background-image: url("chrome://devtools/skin/images/angle-swatch.svg");
 }
 
 .ruleview-overridden {
-  text-decoration: line-through;
+  text-decoration: line-through solid var(--theme-text-color-alt);
 }
 
 /**
  * Hide swatches (tool icons) from properties that are overwritten by higher specificity * rules.
  * .ruleview-swatch is a base class for many tool swatches (shapes, color, bezier curves)
  * .ruleview-flex and .ruleview-grid are custom
  */
 .ruleview-overridden .ruleview-flex,
 .ruleview-overridden .ruleview-grid,
 .ruleview-overridden .ruleview-shapeswatch,
 .ruleview-overridden .ruleview-swatch {
   display: none;
 }
 
-.theme-light .ruleview-overridden {
-  text-decoration-color: var(--theme-content-color3);
-}
-
 .ruleview-font-family.used-font {
   text-decoration: underline;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
   padding: 0;
   margin: -1px -3px -1px -1px;
--- a/devtools/client/themes/storage.css
+++ b/devtools/client/themes/storage.css
@@ -54,17 +54,17 @@
 #storage-table textbox {
   -moz-appearance: none;
   /* make sure the outline is not cut off */
   position: relative;
   max-width: calc(100% - 1px);
   padding: 3px 3px;
   font: inherit;
   line-height: 14px;
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
   background-color: var(--theme-body-background);
 }
 
 /* Variables View Sidebar */
 
 #storage-sidebar {
   max-width: 500px;
   min-width: 250px;
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -570,17 +570,17 @@
 .event-tooltip-attributes-box {
   display: flex;
   flex-shrink: 0;
   align-items: center;
   height: 14px;
   border-radius: 3px;
   padding: 2px;
   margin-inline-start: 5px;
-  background-color: var(--theme-body-color-alt);
+  background-color: var(--theme-text-color-alt);
   color: var(--theme-body-background);
 }
 
 .event-tooltip-attributes {
   margin: 0;
   font-size: 9px;
   padding-top: 2px;
 }
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -48,23 +48,20 @@
 
   /* Icon colors */
   --theme-icon-color: rgba(12, 12, 13, 0.8);
   --theme-icon-dimmed-color: rgba(135, 135, 137, 0.9);
   --theme-icon-checked-color: var(--blue-60);
 
   /* Text color */
   --theme-comment: var(--grey-50);
-  --theme-comment-alt: var(--grey-40);
-  --theme-body-color: var(--grey-60);
-  --theme-body-color-alt: var(--grey-50);
-  --theme-body-color-inactive: var(--grey-40);
-  --theme-content-color1: var(--grey-80);
-  --theme-content-color2: var(--grey-60);
-  --theme-content-color3: var(--grey-45);
+  --theme-body-color: var(--grey-70);
+  --theme-text-color-alt: var(--grey-50);
+  --theme-text-color-inactive: var(--grey-40);
+  --theme-text-color-strong: var(--grey-90);
 
   --theme-highlight-green: var(--green-70);
   --theme-highlight-blue: var(--blue-55);
   --theme-highlight-purple: var(--blue-70);
   --theme-highlight-red: var(--magenta-65);
   --theme-highlight-yellow: #FFF89E;
 
   /* These theme-highlight color variables have not been photonized. */
@@ -141,23 +138,20 @@
 
   /* Icon colors */
   --theme-icon-color: rgba(249, 249, 250, 0.7);
   --theme-icon-dimmed-color: rgba(147, 147, 149, 0.9);
   --theme-icon-checked-color: var(--blue-30);
 
   /* Text color */
   --theme-comment: var(--grey-45);
-  --theme-comment-alt: var(--grey-50);
   --theme-body-color: var(--grey-40);
-  --theme-body-color-alt: var(--grey-45);
-  --theme-body-color-inactive: var(--grey-50);
-  --theme-content-color1: var(--grey-30);
-  --theme-content-color2: var(--grey-40);
-  --theme-content-color3: var(--grey-55);
+  --theme-text-color-alt: var(--grey-45);
+  --theme-text-color-inactive: var(--grey-50);
+  --theme-text-color-strong: var(--grey-30);
 
   --theme-highlight-green: #86DE74;
   --theme-highlight-blue: #75BFFF;
   --theme-highlight-purple: #B98EFF;
   --theme-highlight-red: #FF7DE9;
   --theme-highlight-yellow: #FFF89E;
 
   /* These theme-highlight color variables have not been photonized. */
@@ -254,15 +248,14 @@
 
   --grey-10: #f9f9fa;
   --grey-20: #ededf0;
   --grey-25: #e0e0e2;
   --grey-30: #d7d7db;
   --grey-40: #b1b1b3;
   --grey-45: #939395;
   --grey-50: #737373;
-  --grey-55: #5c5c5f;
   --grey-60: #4a4a4f;
   --grey-70: #38383d;
   --grey-80: #2a2a2e;
   --grey-85: #1b1b1d;
   --grey-90: #0c0c0d;
 }
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -365,17 +365,17 @@ a {
 
 .message .learn-more-link {
   -moz-user-select: none;
   color: var(--theme-highlight-blue);
   margin: 0 1ch;
 }
 
 .message.network .xhr {
-  background-color: var(--theme-body-color-alt);
+  background-color: var(--theme-text-color-alt);
   color: var(--theme-body-background);
   border-radius: 3px;
   font-weight: bold;
   font-size: 10px;
   padding: 1px 2px;
   line-height: 10px;
   margin-inline-start: 0;
   margin-inline-end: 1ex;
@@ -393,17 +393,17 @@ html #webconsole-notificationbox {
   background-color: var(--theme-tab-toolbar-background);
   border-top: 1px solid var(--theme-splitter-color);
   position: relative;
 }
 
 .jsterm-input-node {
   box-sizing: border-box;
   min-height: 100%;
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
   /* input icon */
   background-image: url(chrome://devtools/skin/images/webconsole/input.svg);
   background-position-x: calc(10px + var(--console-icon-horizontal-offset));
   background-position-y: 7px;
   background-repeat: no-repeat;
   background-size: 12px 12px;
   -moz-context-properties: fill;
   fill: var(--theme-icon-dimmed-color);
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -108,17 +108,17 @@
   transform: translateZ(1px);
 }
 
 .variables-view-empty-notice {
   padding: 2px;
 }
 
 .variables-view-empty-notice {
-  color: var(--theme-body-color-alt);
+  color: var(--theme-text-color-alt);
 }
 
 .variables-view-scope:focus > .title,
 .variable-or-property:focus > .title {
   background-color: var(--theme-selection-background);
   color: var(--theme-selection-color);
 }
 
@@ -513,26 +513,26 @@
   left: 14px;
 }
 
 .line-graph-widget-tooltip[type=average] {
   right: 4px;
 }
 
 .line-graph-widget-tooltip > [text=info] {
-  color: var(--theme-content-color1);
+  color: var(--theme-text-color-strong);
 }
 
 .line-graph-widget-tooltip > [text=value] {
   margin-inline-start: 3px;
 }
 
 .line-graph-widget-tooltip > [text=metric] {
   margin-inline-start: 1px;
-  color: var(--theme-content-color3);
+  color: var(--theme-text-color-alt);
 }
 
 .theme-light .line-graph-widget-tooltip > [text=value],
 .theme-light .line-graph-widget-tooltip > [text=metric] {
   text-shadow: 1px  0px rgba(255,255,255,0.5),
               -1px  0px rgba(255,255,255,0.5),
                0px -1px rgba(255,255,255,0.5),
                0px  1px rgba(255,255,255,0.5);
--- a/devtools/server/actors/accessibility/walker.js
+++ b/devtools/server/actors/accessibility/walker.js
@@ -151,16 +151,17 @@ function getAudit(acc, report) {
  * accessibility engine by storing a reference to the XPCOM accessibility
  * service.
  */
 const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
   initialize(conn, targetActor) {
     Actor.prototype.initialize.call(this, conn);
     this.targetActor = targetActor;
     this.refMap = new Map();
+    this._loadedSheets = new WeakMap();
     this.setA11yServiceGetter();
     this.onPick = this.onPick.bind(this);
     this.onHovered = this.onHovered.bind(this);
     this._preventContentEvent = this._preventContentEvent.bind(this);
     this.onKey = this.onKey.bind(this);
     this.onHighlighterEvent = this.onHighlighterEvent.bind(this);
   },
 
@@ -522,45 +523,53 @@ const AccessibleWalkerActor = ActorClass
    * Ensure that nothing interferes with the audit for an accessible object
    * (CSS, overlays) by load accessibility highlighter style sheet used for
    * preventing transitions and applying transparency when calculating colour
    * contrast as well as temporarily hiding accessible highlighter overlay.
    * @param  {Object} win
    *         Window where highlighting happens.
    */
   clearStyles(win) {
-    if (this._sheetLoaded) {
+    const requests = this._loadedSheets.get(win);
+    if (requests != null) {
+      this._loadedSheets.set(win, requests + 1);
       return;
     }
 
     // Disable potential mouse driven transitions (This is important because accessibility
     // highlighter temporarily modifies text color related CSS properties. In case where
     // there are transitions that affect them, there might be unexpected side effects when
     // taking a snapshot for contrast measurement).
     loadSheet(win, HIGHLIGHTER_STYLES_SHEET);
-    this._sheetLoaded = true;
+    this._loadedSheets.set(win, 1);
     this.hideHighlighter();
   },
 
   /**
    * Restore CSS and overlays that could've interfered with the audit for an
    * accessible object by unloading accessibility highlighter style sheet used
    * for preventing transitions and applying transparency when calculating
    * colour contrast and potentially restoring accessible highlighter overlay.
    * @param  {Object} win
    *         Window where highlighting was happenning.
    */
   restoreStyles(win) {
-    if (!this._sheetLoaded) {
+    const requests = this._loadedSheets.get(win);
+    if (!requests) {
+      return;
+    }
+
+    if (requests > 1) {
+      this._loadedSheets.set(win, requests - 1);
       return;
     }
 
     this.showHighlighter();
     removeSheet(win, HIGHLIGHTER_STYLES_SHEET);
-    this._sheetLoaded = false;
+    this._loadedSheets.delete(win);
   },
 
   hideHighlighter() {
     // TODO: Fix this workaround that temporarily removes higlighter bounds
     // overlay that can interfere with the contrast ratio calculation.
     if (this._highlighter) {
       const highlighter = this._highlighter.instance;
       highlighter.hideAccessibleBounds();
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -639,17 +639,17 @@ BrowserTabList.prototype._listenToMediat
 
 /**
  * nsIWindowMediatorListener implementation.
  *
  * See _onTabClosed for explanation of why we needn't actually tweak any
  * actors or tables here.
  *
  * An nsIWindowMediatorListener's methods get passed all sorts of windows; we
- * only care about the tab containers. Those have 'getBrowser' methods.
+ * only care about the tab containers. Those have 'gBrowser' members.
  */
 BrowserTabList.prototype.onOpenWindow =
 DevToolsUtils.makeInfallible(function(window) {
   const handleLoad = DevToolsUtils.makeInfallible(() => {
     /* We don't want any further load events from this window. */
     window.removeEventListener("load", handleLoad);
 
     if (appShellDOMWindowType(window) !== DebuggerServer.chromeWindowType) {
--- a/devtools/server/actors/webconsole/utils.js
+++ b/devtools/server/actors/webconsole/utils.js
@@ -52,17 +52,17 @@ var WebConsoleUtils = {
     if (typeof object != "object") {
       return object;
     }
 
     let temp;
 
     if (Array.isArray(object)) {
       temp = [];
-      Array.forEach(object, function(value, index) {
+      object.forEach(function(value, index) {
         if (!filter || filter(index, value, object)) {
           temp.push(recursive ? WebConsoleUtils.cloneObject(value) : value);
         }
       });
     } else {
       temp = {};
       for (const key in object) {
         const value = object[key];
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -121,16 +121,17 @@
 #include "nsNetUtil.h"  // for NS_NewURI
 #include "nsIInputStreamChannel.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "nsIPrivateBrowsingChannel.h"
 #include "ExpandedPrincipal.h"
 #include "mozilla/NullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsFocusManager.h"
 #include "nsICookiePermission.h"
 #include "nsICookieService.h"
@@ -1232,16 +1233,17 @@ Document::Document(const char* aContentT
       mParserAborted(false),
       mReportedUseCounters(false),
       mHasReportedShadowDOMUsage(false),
       mDocTreeHadAudibleMedia(false),
       mDocTreeHadPlayRevoked(false),
       mHasDelayedRefreshEvent(false),
       mLoadEventFiring(false),
       mSkipLoadEventAfterClose(false),
+      mDisableCookieAccess(false),
       mPendingFullscreenRequests(0),
       mXMLDeclarationBits(0),
       mOnloadBlockCount(0),
       mAsyncOnloadBlockCount(0),
       mCompatMode(eCompatibility_FullStandards),
       mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
       mAncestorIsLoading(false),
 #ifdef MOZILLA_INTERNAL_API
@@ -2198,30 +2200,64 @@ already_AddRefed<nsIPrincipal> Document:
         }
       }
     }
   }
   nsCOMPtr<nsIPrincipal> principal(aPrincipal);
   return principal.forget();
 }
 
+size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet& aSheet) {
+  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+
+  // lowest index first
+  int32_t newDocIndex = IndexOfSheet(aSheet);
+
+  size_t count = mStyleSet->SheetCount(StyleOrigin::Author);
+  size_t index = 0;
+  for (; index < count; index++) {
+    auto* sheet = mStyleSet->SheetAt(StyleOrigin::Author, index);
+    MOZ_ASSERT(sheet);
+    int32_t sheetDocIndex = IndexOfSheet(*sheet);
+    if (sheetDocIndex > newDocIndex) break;
+
+    // If the sheet is not owned by the document it can be an author
+    // sheet registered at nsStyleSheetService or an additional author
+    // sheet on the document, which means the new
+    // doc sheet should end up before it.
+    if (sheetDocIndex < 0) {
+      if (sheetService) {
+        auto& authorSheets = *sheetService->AuthorStyleSheets();
+        if (authorSheets.IndexOf(sheet) != authorSheets.NoIndex) {
+          break;
+        }
+      }
+      if (sheet == GetFirstAdditionalAuthorSheet()) {
+        break;
+      }
+    }
+  }
+
+  return index;
+}
+
 void Document::RemoveDocStyleSheetsFromStyleSets() {
   MOZ_ASSERT(mStyleSetFilled);
   // The stylesheets should forget us
   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
     sheet->ClearAssociatedDocumentOrShadowRoot();
     if (sheet->IsApplicable()) {
       mStyleSet->RemoveDocStyleSheet(sheet);
     }
     // XXX Tell observers?
   }
 }
 
 void Document::RemoveStyleSheetsFromStyleSets(
-    const nsTArray<RefPtr<StyleSheet>>& aSheets, SheetType aType) {
+    const nsTArray<RefPtr<StyleSheet>>& aSheets, StyleOrigin aType) {
   // The stylesheets should forget us
   for (StyleSheet* sheet : Reversed(aSheets)) {
     sheet->ClearAssociatedDocumentOrShadowRoot();
     if (mStyleSetFilled && sheet->IsApplicable()) {
       mStyleSet->RemoveStyleSheet(aType, sheet);
     }
     // XXX Tell observers?
   }
@@ -2231,26 +2267,26 @@ void Document::ResetStylesheetsToURI(nsI
   MOZ_ASSERT(aURI);
 
   if (mStyleSetFilled) {
     // Skip removing style sheets from the style set if we know we haven't
     // filled the style set.  (This allows us to avoid calling
     // GetStyleBackendType() too early.)
     RemoveDocStyleSheetsFromStyleSets();
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet],
-                                   SheetType::Agent);
+                                   StyleOrigin::UserAgent);
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet],
-                                   SheetType::User);
+                                   StyleOrigin::User);
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet],
-                                   SheetType::Doc);
+                                   StyleOrigin::Author);
 
     if (nsStyleSheetService* sheetService =
             nsStyleSheetService::GetInstance()) {
       RemoveStyleSheetsFromStyleSets(*sheetService->AuthorStyleSheets(),
-                                     SheetType::Doc);
+                                     StyleOrigin::Author);
     }
   }
 
   // Release all the sheets
   mStyleSheets.Clear();
   for (auto& sheets : mAdditionalSheets) {
     sheets.Clear();
   }
@@ -2277,118 +2313,120 @@ void Document::ResetStylesheetsToURI(nsI
     if (mStyleSet->StyleSheetsHaveChanged()) {
       ApplicableStylesChanged();
     }
   }
 }
 
 static void AppendSheetsToStyleSet(ServoStyleSet* aStyleSet,
                                    const nsTArray<RefPtr<StyleSheet>>& aSheets,
-                                   SheetType aType) {
+                                   StyleOrigin aOrigin) {
   for (StyleSheet* sheet : Reversed(aSheets)) {
-    aStyleSet->AppendStyleSheet(aType, sheet);
+    aStyleSet->AppendStyleSheet(aOrigin, sheet);
   }
 }
 
 void Document::FillStyleSetUserAndUASheets() {
   // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
   // ordering.
 
   // The document will fill in the document sheets when we create the presshell
   auto cache = nsLayoutStylesheetCache::Singleton();
 
   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   MOZ_ASSERT(sheetService,
              "should never be creating a StyleSet after the style sheet "
              "service has gone");
 
   for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
-    mStyleSet->AppendStyleSheet(SheetType::User, sheet);
+    mStyleSet->AppendStyleSheet(StyleOrigin::User, sheet);
   }
 
   StyleSheet* sheet = IsInChromeDocShell() ? cache->GetUserChromeSheet()
                                            : cache->GetUserContentSheet();
   if (sheet) {
-    mStyleSet->AppendStyleSheet(SheetType::User, sheet);
-  }
-
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->UASheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::User, sheet);
+  }
+
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->UASheet());
 
   if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->MathMLSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->MathMLSheet());
   }
 
   if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->SVGSheet());
-  }
-
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->HTMLSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->SVGSheet());
+  }
+
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->HTMLSheet());
 
   if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->NoFramesSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->NoFramesSheet());
   }
 
   if (nsLayoutUtils::ShouldUseNoScriptSheet(this)) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->NoScriptSheet());
-  }
-
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->CounterStylesSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->NoScriptSheet());
+  }
+
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent,
+                              cache->CounterStylesSheet());
 
   // Load the minimal XUL rules for scrollbars and a few other XUL things
   // that non-XUL (typically HTML) documents commonly use.
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->MinimalXULSheet());
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->MinimalXULSheet());
 
   // Only load the full XUL sheet if we'll need it.
   if (LoadsFullXULStyleSheetUpFront()) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->XULSheet());
-  }
-
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->FormsSheet());
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->ScrollbarsSheet());
-  mStyleSet->AppendStyleSheet(SheetType::Agent, cache->PluginProblemSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->XULSheet());
+  }
+
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->FormsSheet());
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->ScrollbarsSheet());
+  mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent,
+                              cache->PluginProblemSheet());
 
   for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, sheet);
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, sheet);
   }
 
   MOZ_ASSERT(!mQuirkSheetAdded);
   if (NeedsQuirksSheet()) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->QuirkSheet());
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, cache->QuirkSheet());
     mQuirkSheetAdded = true;
   }
 }
 
 void Document::FillStyleSet() {
   MOZ_ASSERT(!mStyleSetFilled);
   FillStyleSetUserAndUASheets();
   FillStyleSetDocumentSheets();
   mStyleSetFilled = true;
 }
 
 void Document::FillStyleSetDocumentSheets() {
-  MOZ_ASSERT(mStyleSet->SheetCount(SheetType::Doc) == 0,
+  MOZ_ASSERT(mStyleSet->SheetCount(StyleOrigin::Author) == 0,
              "Style set already has document sheets?");
 
   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
     if (sheet->IsApplicable()) {
-      mStyleSet->AddDocStyleSheet(sheet, this);
+      mStyleSet->AddDocStyleSheet(sheet);
     }
   }
 
   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
-    mStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
+    mStyleSet->AppendStyleSheet(StyleOrigin::Author, sheet);
   }
 
   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eAgentSheet],
-                         SheetType::Agent);
+                         StyleOrigin::UserAgent);
   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eUserSheet],
-                         SheetType::User);
+                         StyleOrigin::User);
   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eAuthorSheet],
-                         SheetType::Doc);
+                         StyleOrigin::Author);
 }
 
 void Document::CompatibilityModeChanged() {
   MOZ_ASSERT(IsHTMLOrXHTML());
   CSSLoader()->SetCompatibilityMode(mCompatMode);
   mStyleSet->CompatibilityModeChanged();
   if (PresShell* presShell = GetPresShell()) {
     // Selectors may have become case-sensitive / case-insensitive, the stylist
@@ -2400,19 +2438,19 @@ void Document::CompatibilityModeChanged(
     return;
   }
   if (mQuirkSheetAdded == NeedsQuirksSheet()) {
     return;
   }
   auto cache = nsLayoutStylesheetCache::Singleton();
   StyleSheet* sheet = cache->QuirkSheet();
   if (mQuirkSheetAdded) {
-    mStyleSet->RemoveStyleSheet(SheetType::Agent, sheet);
+    mStyleSet->RemoveStyleSheet(StyleOrigin::UserAgent, sheet);
   } else {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, sheet);
+    mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, sheet);
   }
   mQuirkSheetAdded = !mQuirkSheetAdded;
   ApplicableStylesChanged();
 }
 
 static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
                                      uint32_t aSandboxFlags,
                                      nsIChannel* aChannel) {
@@ -3377,16 +3415,162 @@ void Document::SetLastFocusTime(const Ti
 
 void Document::GetReferrer(nsAString& aReferrer) const {
   if (mIsSrcdocDocument && mParentDocument)
     mParentDocument->GetReferrer(aReferrer);
   else
     CopyUTF8toUTF16(mReferrer, aReferrer);
 }
 
+void Document::GetCookie(nsAString& aCookie, ErrorResult& rv) {
+  aCookie.Truncate();  // clear current cookie in case service fails;
+                       // no cookie isn't an error condition.
+
+  if (mDisableCookieAccess) {
+    return;
+  }
+
+  // If the document's sandboxed origin flag is set, access to read cookies
+  // is prohibited.
+  if (mSandboxFlags & SANDBOXED_ORIGIN) {
+    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsContentUtils::StorageAccess storageAccess =
+      nsContentUtils::StorageAllowedForDocument(this);
+  if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
+    return;
+  }
+
+  if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
+      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    return;
+  }
+
+  // If the document is a cookie-averse Document... return the empty string.
+  if (IsCookieAverse()) {
+    return;
+  }
+
+  // not having a cookie service isn't an error
+  nsCOMPtr<nsICookieService> service =
+      do_GetService(NS_COOKIESERVICE_CONTRACTID);
+  if (service) {
+    // Get a URI from the document principal. We use the original
+    // codebase in case the codebase was changed by SetDomain
+    nsCOMPtr<nsIURI> codebaseURI;
+    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+
+    if (!codebaseURI) {
+      // Document's principal is not a codebase (may be system), so
+      // can't set cookies
+
+      return;
+    }
+
+    nsCOMPtr<nsIChannel> channel(mChannel);
+    if (!channel) {
+      channel = CreateDummyChannelForCookies(codebaseURI);
+      if (!channel) {
+        return;
+      }
+    }
+
+    nsCString cookie;
+    service->GetCookieString(codebaseURI, channel, getter_Copies(cookie));
+    // CopyUTF8toUTF16 doesn't handle error
+    // because it assumes that the input is valid.
+    UTF_8_ENCODING->DecodeWithoutBOMHandling(cookie, aCookie);
+  }
+}
+
+void Document::SetCookie(const nsAString& aCookie, ErrorResult& rv) {
+  if (mDisableCookieAccess) {
+    return;
+  }
+
+  // If the document's sandboxed origin flag is set, access to write cookies
+  // is prohibited.
+  if (mSandboxFlags & SANDBOXED_ORIGIN) {
+    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsContentUtils::StorageAccess storageAccess =
+      nsContentUtils::StorageAllowedForDocument(this);
+  if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
+    return;
+  }
+
+  if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
+      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
+    return;
+  }
+
+  // If the document is a cookie-averse Document... do nothing.
+  if (IsCookieAverse()) {
+    return;
+  }
+
+  // not having a cookie service isn't an error
+  nsCOMPtr<nsICookieService> service =
+      do_GetService(NS_COOKIESERVICE_CONTRACTID);
+  if (service && mDocumentURI) {
+    // The code for getting the URI matches Navigator::CookieEnabled
+    nsCOMPtr<nsIURI> codebaseURI;
+    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+
+    if (!codebaseURI) {
+      // Document's principal is not a codebase (may be system), so
+      // can't set cookies
+
+      return;
+    }
+
+    nsCOMPtr<nsIChannel> channel(mChannel);
+    if (!channel) {
+      channel = CreateDummyChannelForCookies(codebaseURI);
+      if (!channel) {
+        return;
+      }
+    }
+
+    NS_ConvertUTF16toUTF8 cookie(aCookie);
+    service->SetCookieString(codebaseURI, nullptr, cookie.get(), channel);
+  }
+}
+
+already_AddRefed<nsIChannel> Document::CreateDummyChannelForCookies(
+    nsIURI* aCodebaseURI) {
+  // The cookie service reads the privacy status of the channel we pass to it in
+  // order to determine which cookie database to query.  In some cases we don't
+  // have a proper channel to hand it to the cookie service though.  This
+  // function creates a dummy channel that is not used to load anything, for the
+  // sole purpose of handing it to the cookie service.  DO NOT USE THIS CHANNEL
+  // FOR ANY OTHER PURPOSE.
+  MOZ_ASSERT(!mChannel);
+
+  // The following channel is never openend, so it does not matter what
+  // securityFlags we pass; let's follow the principle of least privilege.
+  nsCOMPtr<nsIChannel> channel;
+  NS_NewChannel(getter_AddRefs(channel), aCodebaseURI, this,
+                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+                nsIContentPolicy::TYPE_INVALID);
+  nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
+  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
+  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+  if (!pbChannel || !loadContext) {
+    return nullptr;
+  }
+  pbChannel->SetPrivate(loadContext->UsePrivateBrowsing());
+
+  return channel.forget();
+}
+
 mozilla::net::ReferrerPolicy Document::GetReferrerPolicy() const {
   if (mIsSrcdocDocument && mParentDocument &&
       mReferrerPolicy == mozilla::net::RP_Unset) {
     return mParentDocument->GetReferrerPolicy();
   }
   return mReferrerPolicy;
 }
 
@@ -3798,18 +3982,16 @@ bool Document::ShouldThrottleFrameReques
 }
 
 void Document::DeletePresShell() {
   mExternalResourceMap.HideViewers();
   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
     presContext->RefreshDriver()->CancelPendingFullscreenEvents(this);
   }
 
-  mStyleSet->ShellDetachedFromDocument();
-
   // When our shell goes away, request that all our images be immediately
   // discarded, so we don't carry around decoded image data for a document we
   // no longer intend to paint.
   ImageTracker()->RequestDiscardAll();
 
   // Now that we no longer have a shell, we need to forget about any FontFace
   // objects for @font-face rules that came from the style set. There's no need
   // to call EnsureStyleFlush either, the shell is going away anyway, so there's
@@ -3817,16 +3999,20 @@ void Document::DeletePresShell() {
   MarkUserFontSetDirty();
 
   PresShell* oldPresShell = mPresShell;
   mPresShell = nullptr;
   UpdateFrameRequestCallbackSchedulingState(oldPresShell);
 
   ClearStaleServoData();
   AssertNoStaleServoDataIn(*this);
+
+  mStyleSet->ShellDetachedFromDocument();
+  mStyleSetFilled = false;
+  mQuirkSheetAdded = false;
 }
 
 void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
   MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
 
   if (mPresShell) {
     if (aEntry) {
       mPresShell->StopObservingRefreshDriver();
@@ -4008,17 +4194,17 @@ void Document::RemoveChildNode(nsIConten
   nsINode::RemoveChildNode(aKid, aNotify);
   MOZ_ASSERT(mCachedRootElement != aKid,
              "Stale pointer in mCachedRootElement, after we tried to clear it "
              "(maybe somebody called GetRootElement() too early?)");
 }
 
 void Document::AddStyleSheetToStyleSets(StyleSheet* aSheet) {
   if (mStyleSetFilled) {
-    mStyleSet->AddDocStyleSheet(aSheet, this);
+    mStyleSet->AddDocStyleSheet(aSheet);
     ApplicableStylesChanged();
   }
 }
 
 void Document::RecordShadowStyleChange(ShadowRoot& aShadowRoot) {
   mStyleSet->RecordShadowStyleChange(aShadowRoot);
   ApplicableStylesChanged();
 }
@@ -4181,29 +4367,27 @@ void Document::NotifyStyleSheetApplicabl
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
   if (observerService) {
     observerService->NotifyObservers(
         ToSupports(this), "style-sheet-applicable-state-changed", nullptr);
   }
 }
 
-static SheetType ConvertAdditionalSheetType(
+static StyleOrigin ConvertAdditionalSheetType(
     Document::additionalSheetType aType) {
   switch (aType) {
     case Document::eAgentSheet:
-      return SheetType::Agent;
+      return StyleOrigin::UserAgent;
     case Document::eUserSheet:
-      return SheetType::User;
+      return StyleOrigin::User;
     case Document::eAuthorSheet:
-      return SheetType::Doc;
+      return StyleOrigin::Author;
     default:
-      MOZ_ASSERT(false, "wrong type");
-      // we must return something although this should never happen
-      return SheetType::Count;
+      MOZ_CRASH("Wrong sheet type");
   }
 }
 
 static int32_t FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets,
                          nsIURI* aSheetURI) {
   for (int32_t i = aSheets.Length() - 1; i >= 0; i--) {
     bool bEqual;
     nsIURI* uri = aSheets[i]->GetSheetURI();
@@ -4259,18 +4443,17 @@ nsresult Document::AddAdditionalStyleShe
                                            StyleSheet* aSheet) {
   if (mAdditionalSheets[aType].Contains(aSheet)) return NS_ERROR_INVALID_ARG;
 
   if (!aSheet->IsApplicable()) return NS_ERROR_INVALID_ARG;
 
   mAdditionalSheets[aType].AppendElement(aSheet);
 
   if (mStyleSetFilled) {
-    SheetType type = ConvertAdditionalSheetType(aType);
-    mStyleSet->AppendStyleSheet(type, aSheet);
+    mStyleSet->AppendStyleSheet(ConvertAdditionalSheetType(aType), aSheet);
     ApplicableStylesChanged();
   }
 
   // Passing false, so documet.styleSheets.length will not be affected by
   // these additional sheets.
   NotifyStyleSheetAdded(aSheet, false);
   return NS_OK;
 }
@@ -4284,18 +4467,18 @@ void Document::RemoveAdditionalStyleShee
   int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
   if (i >= 0) {
     RefPtr<StyleSheet> sheetRef = sheets[i];
     sheets.RemoveElementAt(i);
 
     if (!mIsGoingAway) {
       MOZ_ASSERT(sheetRef->IsApplicable());
       if (mStyleSetFilled) {
-        SheetType type = ConvertAdditionalSheetType(aType);
-        mStyleSet->RemoveStyleSheet(type, sheetRef);
+        mStyleSet->RemoveStyleSheet(ConvertAdditionalSheetType(aType),
+                                    sheetRef);
         ApplicableStylesChanged();
       }
     }
 
     // Passing false, so documet.styleSheets.length will not be affected by
     // these additional sheets.
     NotifyStyleSheetRemoved(sheetRef, false);
     sheetRef->ClearAssociatedDocumentOrShadowRoot();
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1234,16 +1234,22 @@ class Document : public nsINode,
 
   /**
    * Called when the document was decoded as UTF-8 and decoder encountered no
    * errors.
    */
   void EnableEncodingMenu() { mEncodingMenuDisabled = false; }
 
   /**
+   * Called to disable client access to cookies through the document.cookie API
+   * from user JavaScript code.
+   */
+  void DisableCookieAccess() { mDisableCookieAccess = true; }
+
+  /**
    * Access HTTP header data (this may also get set from other
    * sources, like HTML META tags).
    */
   void GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const;
   void SetHeaderData(nsAtom* aheaderField, const nsAString& aData);
 
   /**
    * Create a new presentation shell that will use aContext for its
@@ -1700,27 +1706,20 @@ class Document : public nsINode,
                                    StyleSheet* aSheet);
   void RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* sheetURI);
 
   StyleSheet* GetFirstAdditionalAuthorSheet() {
     return mAdditionalSheets[eAuthorSheet].SafeElementAt(0);
   }
 
   /**
-   * Assuming that aDocSheets is an array of document-level style
-   * sheets for this document, returns the index that aSheet should
-   * be inserted at to maintain document ordering.
-   *
-   * Type T has to cast to StyleSheet*.
-   *
-   * Defined in DocumentInlines.h.
-   */
-  template <typename T>
-  size_t FindDocStyleSheetInsertionPoint(const nsTArray<T>& aDocSheets,
-                                         const StyleSheet& aSheet);
+   * Returns the index that aSheet should be inserted at to maintain document
+   * ordering.
+   */
+  size_t FindDocStyleSheetInsertionPoint(const StyleSheet& aSheet);
 
   /**
    * Get this document's CSSLoader.  This is guaranteed to not return null.
    */
   css::Loader* CSSLoader() const { return mCSSLoader; }
 
   /**
    * Get this document's StyleImageLoader.  This is guaranteed to not return
@@ -3236,16 +3235,18 @@ class Document : public nsINode,
                                                     ErrorResult& rv);
   already_AddRefed<Attr> CreateAttribute(const nsAString& aName,
                                          ErrorResult& rv);
   already_AddRefed<Attr> CreateAttributeNS(const nsAString& aNamespaceURI,
                                            const nsAString& aQualifiedName,
                                            ErrorResult& rv);
   void GetInputEncoding(nsAString& aInputEncoding) const;
   already_AddRefed<Location> GetLocation() const;
+  void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
+  void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
 
   void GetTitle(nsAString& aTitle);
   void SetTitle(const nsAString& aTitle, ErrorResult& rv);
   void GetDir(nsAString& aDirection) const;
   void SetDir(const nsAString& aDirection);
@@ -3816,17 +3817,17 @@ class Document : public nsINode,
   }
 
   bool GetChildDocumentUseCounter(UseCounter aUseCounter) {
     return mChildDocumentUseCounters[aUseCounter];
   }
 
   void RemoveDocStyleSheetsFromStyleSets();
   void RemoveStyleSheetsFromStyleSets(
-      const nsTArray<RefPtr<StyleSheet>>& aSheets, SheetType aType);
+      const nsTArray<RefPtr<StyleSheet>>& aSheets, StyleOrigin);
   void ResetStylesheetsToURI(nsIURI* aURI);
   void FillStyleSet();
   void FillStyleSetUserAndUASheets();
   void FillStyleSetDocumentSheets();
   void CompatibilityModeChanged();
   bool NeedsQuirksSheet() const {
     // SVG documents never load quirk.css.
     return mCompatMode == eCompatibility_NavQuirks && !IsSVGDocument();
@@ -3915,16 +3916,20 @@ class Document : public nsINode,
 
   // Helpers for GetElementsByName.
   static bool MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
                                  nsAtom* aAtom, void* aData);
   static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
 
   void MaybeResolveReadyForIdle();
 
+  // This should *ONLY* be used in GetCookie/SetCookie.
+  already_AddRefed<nsIChannel> CreateDummyChannelForCookies(
+      nsIURI* aCodebaseURI);
+
   nsCString mReferrer;
   nsString mLastModified;
 
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mChromeXHRDocURI;
   nsCOMPtr<nsIURI> mDocumentBaseURI;
   nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
@@ -4292,16 +4297,19 @@ class Document : public nsINode,
   // The HTML spec has a "mute iframe load" flag, but that doesn't seem to have
   // the right semantics.  See <https://github.com/whatwg/html/issues/4292>.
   // What we have instead is a flag that is set if completion of our document
   // via document.close() should skip firing the load event.  Note that this
   // flag is only relevant for HTML documents, but lives here for reasons that
   // are documented above on SkipLoadEventAfterClose().
   bool mSkipLoadEventAfterClose : 1;
 
+  // When false, the .cookies property is completely disabled
+  bool mDisableCookieAccess : 1;
+
   uint8_t mPendingFullscreenRequests;
 
   uint8_t mXMLDeclarationBits;
 
   // Currently active onload blockers.
   uint32_t mOnloadBlockCount;
 
   // Onload blockers which haven't been activated yet.
--- a/dom/base/DocumentInlines.h
+++ b/dom/base/DocumentInlines.h
@@ -25,52 +25,16 @@ inline nsPresContext* Document::GetPresC
   PresShell* presShell = GetPresShell();
   return presShell ? presShell->GetPresContext() : nullptr;
 }
 
 inline HTMLBodyElement* Document::GetBodyElement() {
   return static_cast<HTMLBodyElement*>(GetHtmlChildElement(nsGkAtoms::body));
 }
 
-template <typename T>
-size_t Document::FindDocStyleSheetInsertionPoint(const nsTArray<T>& aDocSheets,
-                                                 const StyleSheet& aSheet) {
-  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
-
-  // lowest index first
-  int32_t newDocIndex = IndexOfSheet(aSheet);
-
-  size_t count = aDocSheets.Length();
-  size_t index = 0;
-  for (; index < count; index++) {
-    auto* sheet = static_cast<StyleSheet*>(aDocSheets[index]);
-    MOZ_ASSERT(sheet);
-    int32_t sheetDocIndex = IndexOfSheet(*sheet);
-    if (sheetDocIndex > newDocIndex) break;
-
-    // If the sheet is not owned by the document it can be an author
-    // sheet registered at nsStyleSheetService or an additional author
-    // sheet on the document, which means the new
-    // doc sheet should end up before it.
-    if (sheetDocIndex < 0) {
-      if (sheetService) {
-        auto& authorSheets = *sheetService->AuthorStyleSheets();
-        if (authorSheets.IndexOf(sheet) != authorSheets.NoIndex) {
-          break;
-        }
-      }
-      if (sheet == GetFirstAdditionalAuthorSheet()) {
-        break;
-      }
-    }
-  }
-
-  return size_t(index);
-}
-
 inline void Document::SetServoRestyleRoot(nsINode* aRoot, uint32_t aDirtyBits) {
   MOZ_ASSERT(aRoot);
 
   MOZ_ASSERT(!mServoRestyleRoot || mServoRestyleRoot == aRoot ||
              nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
                  mServoRestyleRoot, aRoot));
   MOZ_ASSERT(aRoot == aRoot->OwnerDocAsNode() || aRoot->IsElement());
   mServoRestyleRoot = aRoot;
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -600,19 +600,16 @@ void DocumentOrShadowRoot::Traverse(Docu
     }
     // The style set or mServoStyles keep more references to it if the sheet is
     // applicable.
     if (tmp->mKind == Kind::ShadowRoot) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
       cb.NoteXPCOMChild(sheet);
     } else if (tmp->AsNode().AsDocument()->StyleSetFilled()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
-          cb, "mStyleSet->mStyleSheets[SheetType::Author][i]");
-      cb.NoteXPCOMChild(sheet);
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
           cb, "mStyleSet->mRawSet.stylist.stylesheets.author[i]");
       cb.NoteXPCOMChild(sheet);
     }
   }
   for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
     iter.Get()->Traverse(&cb);
   }
   for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
--- a/dom/base/PopupBlocker.cpp
+++ b/dom/base/PopupBlocker.cpp
@@ -288,16 +288,17 @@ PopupBlocker::PopupControlState PopupBlo
             break;
           default:
             break;
         }
       }
       break;
     case eMouseEventClass:
       if (aEvent->IsTrusted()) {
+        // eLeftButton
         if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
           abuse = PopupBlocker::openBlocked;
           switch (aEvent->mMessage) {
             case eMouseUp:
               if (PopupAllowedForEvent("mouseup")) {
                 abuse = PopupBlocker::openControlled;
               }
               break;
@@ -318,16 +319,26 @@ PopupBlocker::PopupControlState PopupBlo
             case eMouseDoubleClick:
               if (PopupAllowedForEvent("dblclick")) {
                 abuse = PopupBlocker::openControlled;
               }
               break;
             default:
               break;
           }
+        } else if (aEvent->mMessage == eMouseAuxClick) {
+          // Not eLeftButton
+          // There's not a strong reason to ignore other events (eg eMouseUp)
+          // for non-primary clicks as far as we know, so we could add them if
+          // it becomes a compat issue
+          if (PopupAllowedForEvent("auxclick")) {
+            abuse = PopupBlocker::openControlled;
+          } else {
+            abuse = PopupBlocker::openBlocked;
+          }
         }
 
         switch (aEvent->mMessage) {
           case eContextMenu:
             if (PopupAllowedForEvent("contextmenu")) {
               abuse = PopupBlocker::openControlled;
             } else {
               abuse = PopupBlocker::openBlocked;
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -7,16 +7,17 @@
 #include <map>
 #include "nsCOMPtr.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/PContentPermission.h"
+#include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Preferences.h"
@@ -114,53 +115,59 @@ VisibilityChangeListener::GetCallback() 
   return callback.forget();
 }
 
 namespace mozilla {
 namespace dom {
 
 class ContentPermissionRequestParent : public PContentPermissionRequestParent {
  public:
-  ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
-                                 Element* aElement,
-                                 const IPC::Principal& aPrincipal,
-                                 const IPC::Principal& aTopLevelPrincipal,
-                                 const bool aIsHandlingUserInput);
+  ContentPermissionRequestParent(
+      const nsTArray<PermissionRequest>& aRequests, Element* aElement,
+      const IPC::Principal& aPrincipal,
+      const IPC::Principal& aTopLevelPrincipal, const bool aIsHandlingUserInput,
+      const bool aUserHadInteractedWithDocument,
+      const DOMTimeStamp aDocumentDOMContentLoadedTimestamp);
   virtual ~ContentPermissionRequestParent();
 
   bool IsBeingDestroyed();
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<Element> mElement;
   bool mIsHandlingUserInput;
+  bool mUserHadInteractedWithDocument;
+  DOMTimeStamp mDocumentDOMContentLoadedTimestamp;
   RefPtr<nsContentPermissionRequestProxy> mProxy;
   nsTArray<PermissionRequest> mRequests;
 
  private:
   // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual mozilla::ipc::IPCResult Recvprompt() override;
   virtual mozilla::ipc::IPCResult RecvNotifyVisibility(
       const bool& aIsVisible) override;
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
   virtual void ActorDestroy(ActorDestroyReason why) override;
 };
 
 ContentPermissionRequestParent::ContentPermissionRequestParent(
     const nsTArray<PermissionRequest>& aRequests, Element* aElement,
     const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
-    const bool aIsHandlingUserInput) {
+    const bool aIsHandlingUserInput, const bool aUserHadInteractedWithDocument,
+    const DOMTimeStamp aDocumentDOMContentLoadedTimestamp) {
   MOZ_COUNT_CTOR(ContentPermissionRequestParent);
 
   mPrincipal = aPrincipal;
   mTopLevelPrincipal = aTopLevelPrincipal;
   mElement = aElement;
   mRequests = aRequests;
   mIsHandlingUserInput = aIsHandlingUserInput;
+  mUserHadInteractedWithDocument = aUserHadInteractedWithDocument;
+  mDocumentDOMContentLoadedTimestamp = aDocumentDOMContentLoadedTimestamp;
 }
 
 ContentPermissionRequestParent::~ContentPermissionRequestParent() {
   MOZ_COUNT_DTOR(ContentPermissionRequestParent);
 }
 
 mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
   mProxy = new nsContentPermissionRequestProxy(this);
@@ -317,20 +324,22 @@ nsresult nsContentPermissionUtils::Creat
   return NS_OK;
 }
 
 /* static */
 PContentPermissionRequestParent*
 nsContentPermissionUtils::CreateContentPermissionRequestParent(
     const nsTArray<PermissionRequest>& aRequests, Element* aElement,
     const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
-    const bool aIsHandlingUserInput, const TabId& aTabId) {
+    const bool aIsHandlingUserInput, const bool aUserHadInteractedWithDocument,
+    const DOMTimeStamp aDocumentDOMContentLoadedTimestamp,
+    const TabId& aTabId) {
   PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
-      aRequests, aElement, aPrincipal, aTopLevelPrincipal,
-      aIsHandlingUserInput);
+      aRequests, aElement, aPrincipal, aTopLevelPrincipal, aIsHandlingUserInput,
+      aUserHadInteractedWithDocument, aDocumentDOMContentLoadedTimestamp);
   ContentPermissionRequestParentMap()[parent] = aTabId;
 
   return parent;
 }
 
 /* static */
 nsresult nsContentPermissionUtils::AskPermission(
     nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
@@ -360,23 +369,34 @@ nsresult nsContentPermissionUtils::AskPe
     nsCOMPtr<nsIPrincipal> topLevelPrincipal;
     rv = aRequest->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool isHandlingUserInput;
     rv = aRequest->GetIsHandlingUserInput(&isHandlingUserInput);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    bool userHadInteractedWithDocument;
+    rv = aRequest->GetUserHadInteractedWithDocument(
+        &userHadInteractedWithDocument);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    DOMTimeStamp documentDOMContentLoadedTimestamp;
+    rv = aRequest->GetDocumentDOMContentLoadedTimestamp(
+        &documentDOMContentLoadedTimestamp);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     ContentChild::GetSingleton()->SetEventTargetForActor(
         req, aWindow->EventTargetFor(TaskCategory::Other));
 
     req->IPDLAddRef();
     ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
         req, permArray, IPC::Principal(principal),
         IPC::Principal(topLevelPrincipal), isHandlingUserInput,
+        userHadInteractedWithDocument, documentDOMContentLoadedTimestamp,
         child->GetTabId());
     ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
 
     req->Sendprompt();
     return NS_OK;
   }
 
   // for chrome process
@@ -523,17 +543,36 @@ ContentPermissionRequestBase::ContentPer
     nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow,
     const nsACString& aPrefName, const nsACString& aType)
     : mPrincipal(aPrincipal),
       mTopLevelPrincipal(aWindow ? ::GetTopLevelPrincipal(aWindow) : nullptr),
       mWindow(aWindow),
       mRequester(aWindow ? new nsContentPermissionRequester(aWindow) : nullptr),
       mPrefName(aPrefName),
       mType(aType),
-      mIsHandlingUserInput(EventStateManager::IsHandlingUserInput()) {}
+      mIsHandlingUserInput(EventStateManager::IsHandlingUserInput()),
+      mUserHadInteractedWithDocument(false),
+      mDocumentDOMContentLoadedTimestamp(0) {
+  if (!aWindow) {
+    return;
+  }
+
+  Document* doc = aWindow->GetExtantDoc();
+  if (!doc) {
+    return;
+  }
+
+  mUserHadInteractedWithDocument = doc->UserHasInteracted();
+
+  nsDOMNavigationTiming* navTiming = doc->GetNavigationTiming();
+  if (navTiming) {
+    mDocumentDOMContentLoadedTimestamp =
+        navTiming->GetDomContentLoadedEventEnd();
+  }
+}
 
 NS_IMETHODIMP
 ContentPermissionRequestBase::GetPrincipal(
     nsIPrincipal** aRequestingPrincipal) {
   NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
   return NS_OK;
 }
 
@@ -560,16 +599,30 @@ ContentPermissionRequestBase::GetElement
 NS_IMETHODIMP
 ContentPermissionRequestBase::GetIsHandlingUserInput(
     bool* aIsHandlingUserInput) {
   *aIsHandlingUserInput = mIsHandlingUserInput;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+ContentPermissionRequestBase::GetUserHadInteractedWithDocument(
+    bool* aUserHadInteractedWithDocument) {
+  *aUserHadInteractedWithDocument = mUserHadInteractedWithDocument;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetDocumentDOMContentLoadedTimestamp(
+    DOMTimeStamp* aDocumentDOMContentLoadedTimestamp) {
+  *aDocumentDOMContentLoadedTimestamp = mDocumentDOMContentLoadedTimestamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 ContentPermissionRequestBase::GetRequester(
     nsIContentPermissionRequester** aRequester) {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
   return NS_OK;
 }
@@ -859,16 +912,39 @@ nsContentPermissionRequestProxy::GetIsHa
   if (mParent == nullptr) {
     return NS_ERROR_FAILURE;
   }
   *aIsHandlingUserInput = mParent->mIsHandlingUserInput;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsContentPermissionRequestProxy::GetUserHadInteractedWithDocument(
+    bool* aUserHadInteractedWithDocument) {
+  NS_ENSURE_ARG_POINTER(aUserHadInteractedWithDocument);
+  if (mParent == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+  *aUserHadInteractedWithDocument = mParent->mUserHadInteractedWithDocument;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentPermissionRequestProxy::GetDocumentDOMContentLoadedTimestamp(
+    DOMTimeStamp* aDocumentDOMContentLoadedTimestamp) {
+  NS_ENSURE_ARG_POINTER(aDocumentDOMContentLoadedTimestamp);
+  if (mParent == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+  *aDocumentDOMContentLoadedTimestamp =
+      mParent->mDocumentDOMContentLoadedTimestamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsContentPermissionRequestProxy::Cancel() {
   if (mParent == nullptr) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't send out the delete message when the managing protocol (PBrowser) is
   // being destroyed and PContentPermissionRequest will soon be.
   if (mParent->IsBeingDestroyed()) {
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -67,16 +67,18 @@ class nsContentPermissionUtils {
   static nsresult CreatePermissionArray(const nsACString& aType,
                                         const nsTArray<nsString>& aOptions,
                                         nsIArray** aTypesArray);
 
   static PContentPermissionRequestParent* CreateContentPermissionRequestParent(
       const nsTArray<PermissionRequest>& aRequests, Element* aElement,
       const IPC::Principal& aPrincipal,
       const IPC::Principal& aTopLevelPrincipal, const bool aIsHandlingUserInput,
+      const bool aUserHadInteractedWithDocument,
+      const DOMTimeStamp aDocumentDOMContentLoadedTimestamp,
       const TabId& aTabId);
 
   static nsresult AskPermission(nsIContentPermissionRequest* aRequest,
                                 nsPIDOMWindowInner* aWindow);
 
   static nsTArray<PContentPermissionRequestParent*>
   GetContentPermissionRequestParentById(const TabId& aTabId);
 
@@ -116,16 +118,20 @@ class ContentPermissionRequestBase : pub
   NS_DECL_CYCLE_COLLECTION_CLASS(ContentPermissionRequestBase)
 
   NS_IMETHOD GetTypes(nsIArray** aTypes) override;
   NS_IMETHOD GetPrincipal(nsIPrincipal** aPrincipal) override;
   NS_IMETHOD GetTopLevelPrincipal(nsIPrincipal** aTopLevelPrincipal) override;
   NS_IMETHOD GetWindow(mozIDOMWindow** aWindow) override;
   NS_IMETHOD GetElement(mozilla::dom::Element** aElement) override;
   NS_IMETHOD GetIsHandlingUserInput(bool* aIsHandlingUserInput) override;
+  NS_IMETHOD GetUserHadInteractedWithDocument(
+      bool* aUserHadInteractedWithDocument) override;
+  NS_IMETHOD GetDocumentDOMContentLoadedTimestamp(
+      DOMTimeStamp* aDocumentDOMContentLoadedTimestamp) override;
   NS_IMETHOD GetRequester(nsIContentPermissionRequester** aRequester) override;
   // Overrides for Allow() and Cancel() aren't provided by this class.
   // That is the responsibility of the subclasses.
 
   enum class PromptResult {
     Granted,
     Denied,
     Pending,
@@ -150,16 +156,18 @@ class ContentPermissionRequestBase : pub
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIContentPermissionRequester> mRequester;
   nsCString mPrefName;
   nsCString mType;
   bool mIsHandlingUserInput;
+  bool mUserHadInteractedWithDocument;
+  DOMTimeStamp mDocumentDOMContentLoadedTimestamp;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 using mozilla::dom::ContentPermissionRequestParent;
 
 class nsContentPermissionRequestProxy : public nsIContentPermissionRequest {
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -163,16 +163,18 @@ static StaticRefPtr<IdleTaskRunner> sCCR
 static StaticRefPtr<IdleTaskRunner> sICCRunner;
 static nsITimer* sFullGCTimer;
 static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
 
 static TimeStamp sLastCCEndTime;
 
 static TimeStamp sLastForgetSkippableCycleEndTime;
 
+static TimeStamp sCurrentGCStartTime;
+
 static bool sCCLockedOut;
 static PRTime sCCLockedOutTime;
 
 static JS::GCSliceCallback sPrevGCSliceCallback;
 
 static bool sHasRunGC;
 
 static uint32_t sCCollectedWaitingForGC;
@@ -2168,16 +2170,17 @@ NotifyGCEndRunnable::Run() {
 static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
                                const JS::GCDescription& aDesc) {
   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
 
   switch (aProgress) {
     case JS::GC_CYCLE_BEGIN: {
       // Prevent cycle collections and shrinking during incremental GC.
       sCCLockedOut = true;
+      sCurrentGCStartTime = TimeStamp::Now();
       break;
     }
 
     case JS::GC_CYCLE_END: {
       PRTime delta = GetCollectionTimeDelta();
 
       if (StaticPrefs::javascript_options_mem_log()) {
         nsString gcstats;
@@ -2234,16 +2237,18 @@ static void DOMGCSliceCallback(JSContext
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       if (!aDesc.isZone_) {
         sNeedsFullGC = false;
       }
 
+      Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS,
+                            TimeBetween(sCurrentGCStartTime, TimeStamp::Now()));
       break;
     }
 
     case JS::GC_SLICE_BEGIN:
       break;
 
     case JS::GC_SLICE_END:
       sGCUnnotifiedTotalTime +=
--- a/dom/base/test/chrome/test_domparsing.xul
+++ b/dom/base/test/chrome/test_domparsing.xul
@@ -77,17 +77,17 @@ function runTest(parser, serializer) {
          "serializeToStream test for " + t.type + ", charset=" + charset);
     }
 
     if (t.type === "text/html") {
       // parseFromBuffer and parseFromStream don't support "text/html".
       continue;
     }
 
-    let array = Array.map(t.input, function(c) { return c.charCodeAt(c); });
+    let array = Array.from(t.input, function(c) { return c.charCodeAt(c); });
     let inputs = [
       {array: array, name: "parseFromBuffer (array)"},
       {array: new Uint8Array(array), name: "parseFromBuffer (Uint8Array)"},
     ];
     for (let input of inputs) {
       let a = input.array;
       is(serializer.serializeToString(parser.parseFromBuffer(a, t.type)), t.expected,
          input.name + " test for " + t.type);
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -80,21 +80,21 @@ TestInterfaceJS.prototype = {
     Services.netUtils.notImplemented();
   },
 
   testThrowCallbackError(callback) {
     callback();
   },
 
   testThrowXraySelfHosted() {
-    this._win.Array.indexOf();
+    this._win.Array.prototype.forEach();
   },
 
   testThrowSelfHosted() {
-    Array.indexOf();
+    Array.prototype.forEach();
   },
 
   testPromiseWithThrowingChromePromiseInit() {
     return new this._win.Promise(function() {
       noSuchMethodExistsYo1();
     });
   },
 
--- a/dom/bindings/test/test_exception_options_from_jsimplemented.html
+++ b/dom/bindings/test/test_exception_options_from_jsimplemented.html
@@ -84,34 +84,34 @@ https://bugzilla.mozilla.org/show_bug.cg
       is(e.fileName,
          file,
          "Should still have the right file name for TypeError");
       is(e.lineNumber, 72, "Should still have the right line number for TypeError");
       is(e.columnNumber, 9, "Should have the right column number for TypeError");
     }
 
     try {
-      t.testThrowCallbackError(function() { Array.indexOf(); });
+      t.testThrowCallbackError(function() { Array.prototype.forEach(); });
     } catch (e) {
       ok(e instanceof TypeError, "Should have a TypeError here (3)");
       ok(!(e instanceof DOMException), "Should not have DOMException here (3)");
       ok(!("code" in e), "Should not have a 'code' property (3)");
       is(e.name, "TypeError", "Should be named TypeError (3)");
-      is(e.message, "missing argument 0 when calling function Array.indexOf",
+      is(e.message, "missing argument 0 when calling function Array.prototype.forEach",
          "Should also have the right message (3)");
       is(e.stack,
-         `doTest/<@${file}:92:51
+         `doTest/<@${file}:92:61
 doTest@${file}:92:9
 ${asyncFrame}`,
          "Exception stack for TypeError should only show our code (3)");
       is(e.fileName,
          file,
          "Should still have the right file name for TypeError (3)");
       is(e.lineNumber, 92, "Should still have the right line number for TypeError (3)");
-      is(e.columnNumber, 51, "Should have the right column number for TypeError (3)");
+      is(e.columnNumber, 61, "Should have the right column number for TypeError (3)");
     }
 
     try {
       t.testThrowXraySelfHosted();
     } catch (e) {
       ok(!(e instanceof Error), "Should have an Exception here (4)");
       ok(!(e instanceof DOMException), "Should not have DOMException here (4)");
       ok(!("code" in e), "Should not have a 'code' property (4)");
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -22,18 +22,17 @@ function handleWindowEvent(e) {
     beps.forEach(bep => bep._handleOwnerEvent(e));
   }
 }
 
 function defineNoReturnMethod(fn) {
   return function method() {
     if (!this._domRequestReady) {
       // Remote browser haven't been created, we just queue the API call.
-      let args = Array.slice(arguments);
-      args.unshift(this);
+      let args = [this, ...arguments];
       this._pendingAPICalls.push(method.bind.apply(fn, args));
       return;
     }
     if (this._isAlive()) {
       fn.apply(this, arguments);
     }
   };
 }
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -28,17 +28,17 @@ const browserElementTestHelpers = {
       SpecialPowers.pushPrefEnv({"set": [[pref, value]]}, this.unlockTestReady.bind(this));
     } else {
       SpecialPowers.pushPrefEnv({"clear": [[pref]]}, this.unlockTestReady.bind(this));
     }
   },
 
   _setPrefs() {
     this.lockTestReady();
-    SpecialPowers.pushPrefEnv({"set": Array.slice(arguments)}, this.unlockTestReady.bind(this));
+    SpecialPowers.pushPrefEnv({"set": Array.from(arguments)}, this.unlockTestReady.bind(this));
   },
 
   _testReadyLockCount: 0,
   _firedTestReady: false,
   lockTestReady() {
     this._testReadyLockCount++;
   },
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4858,16 +4858,18 @@ nsresult EventStateManager::InitAndDispa
                          aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
 
   event.mRefPoint = aMouseUpEvent->mRefPoint;
   event.mClickCount = aMouseUpEvent->mClickCount;
   event.mModifiers = aMouseUpEvent->mModifiers;
   event.buttons = aMouseUpEvent->buttons;
   event.mTime = aMouseUpEvent->mTime;
   event.mTimeStamp = aMouseUpEvent->mTimeStamp;
+  event.mFlags.mOnlyChromeDispatch =
+      aNoContentDispatch && !aMouseUpEvent->mUseLegacyNonPrimaryDispatch;
   event.mFlags.mNoContentDispatch = aNoContentDispatch;
   event.button = aMouseUpEvent->button;
   event.pointerId = aMouseUpEvent->pointerId;
   event.inputSource = aMouseUpEvent->inputSource;
   nsIContent* target = aMouseUpContent;
   nsIFrame* targetFrame = aCurrentTarget;
   if (aOverrideClickTarget) {
     target = aOverrideClickTarget;
@@ -4985,44 +4987,45 @@ nsresult EventStateManager::DispatchClic
   AutoWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
   nsresult rv = InitAndDispatchClickEvent(
       aMouseUpEvent, aStatus, eMouseClick, aPresShell, aClickTarget,
       currentTarget, notDispatchToContents, aOverrideClickTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  // Fire auxclick event if necessary.
+  if (fireAuxClick && *aStatus != nsEventStatus_eConsumeNoDefault &&
+      aClickTarget && aClickTarget->IsInComposedDoc()) {
+    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
+                                   aPresShell, aClickTarget, currentTarget,
+                                   false, aOverrideClickTarget);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch eMouseAuxClick");
+  }
+
   // Fire double click event if click count is 2.
-  if (aMouseUpEvent->mClickCount == 2 && aClickTarget &&
+  if (aMouseUpEvent->mClickCount == 2 && !fireAuxClick && aClickTarget &&
       aClickTarget->IsInComposedDoc()) {
     rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
                                    aPresShell, aClickTarget, currentTarget,
                                    notDispatchToContents, aOverrideClickTarget);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  // Fire auxclick even if necessary.
-  if (fireAuxClick && aClickTarget && aClickTarget->IsInComposedDoc()) {
-    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
-                                   aPresShell, aClickTarget, currentTarget,
-                                   false, aOverrideClickTarget);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch eMouseAuxClick");
-  }
-
   return rv;
 }
 
 nsresult EventStateManager::HandleMiddleClickPaste(
     nsIPresShell* aPresShell, WidgetMouseEvent* aMouseEvent,
     nsEventStatus* aStatus, TextEditor* aTextEditor) {
   MOZ_ASSERT(aPresShell);
   MOZ_ASSERT(aMouseEvent);
-  MOZ_ASSERT((aMouseEvent->mMessage == eMouseClick &&
+  MOZ_ASSERT((aMouseEvent->mMessage == eMouseAuxClick &&
               aMouseEvent->button == WidgetMouseEventBase::eMiddleButton) ||
              EventCausesClickEvents(*aMouseEvent));
   MOZ_ASSERT(aStatus);
   MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault);
 
   // Even if we're called twice or more for a mouse operation, we should
   // handle only once.  Although mMultipleActionsPrevented may be set to
   // true by different event handler in the future, we can use it for now.
--- a/dom/events/test/bug426082.html
+++ b/dom/events/test/bug426082.html
@@ -37,17 +37,17 @@ function runTests() {
   label = $("label");
   outside = $("outside");
   SimpleTest.executeSoon(executeTests);
 }
 
 SimpleTest.waitForFocus(runTests);
 
 function isRectContainedInRectFromRegion(rect, region) {
-  return Array.some(region, function (r) {
+  return Array.prototype.some.call(region, function (r) {
     return rect.left >= r.left &&
            rect.top >= r.top &&
            rect.right <= r.right &&
            rect.bottom <= r.bottom;
   });
 }
 
 function paintListener(e) {
--- a/dom/events/test/bug656379-1.html
+++ b/dom/events/test/bug656379-1.html
@@ -53,17 +53,17 @@ function runTests() {
   label = $("label");
   outside = $("outside");
   SimpleTest.executeSoon(executeTests);
 }
 
 SimpleTest.waitForFocus(runTests);
 
 function isRectContainedInRectFromRegion(rect, region) {
-  return Array.some(region, function (r) {
+  return Array.prototype.some.call(region, function (r) {
     return rect.left >= r.left &&
            rect.top >= r.top &&
            rect.right <= r.right &&
            rect.bottom <= r.bottom;
   });
 }
 
 function paintListener(e) {
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -174,16 +174,17 @@ skip-if = toolkit == 'android' #TIMED_OU
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_eventctors_sensors.html]
 [test_disabled_events.html]
 [test_eventhandler_scoping.html]
 [test_eventTimeStamp.html]
 [test_focus_disabled.html]
 [test_focus_abspos.html]
 [test_legacy_event.html]
+[test_legacy_non-primary_click.html]
 [test_legacy_touch_api.html]
 [test_messageEvent.html]
 [test_messageEvent_init.html]
 [test_moz_mouse_pixel_scroll_event.html]
 [test_offsetxy.html]
 [test_onerror_handler_args.html]
 [test_passive_listeners.html]
 [test_paste_image.html]
--- a/dom/events/test/test_clickevent_on_input.html
+++ b/dom/events/test/test_clickevent_on_input.html
@@ -55,24 +55,21 @@ function isEnabledAccessibleCaret()
     return SpecialPowers.getBoolPref("layout.accessiblecaret.enabled");
   } catch (e) {
     return false;
   }
 }
 
 function doTest(aButton)
 {
-  // NOTE #1: Right click causes a context menu to popup, then, the click event
-  //          isn't generated.
-  // NOTE #2: If middle click causes text to be pasted, then, the click event
-  //          isn't generated.
-  // NOTE #3: If touch caret is enabled, touch caret would ovelap input element,
+  // NOTE #1: Non-primary buttons don't generate 'click' events
+  // NOTE #2: If touch caret is enabled, touch caret would ovelap input element,
   //          then, the click event isn't generated.
   if (aButton != 2 &&
-      (aButton != 1 || !isEnabledMiddleClickPaste()) &&
+      aButton != 1 &&
       (aButton != 0 || !isEnabledAccessibleCaret())) {
     gClickCount = 0;
     // click on border of input
     synthesizeMouse(input, 5, 5, { button: aButton });
     is(gClickCount, 1,
        "click event doesn't fired on input element (button is " +
          aButton + ")");
 
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_legacy_non-primary_click.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for dispatching of legacy non-primary click when domain in pref</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<a id="link-test" href="example.org">example link</a>
+<script>
+"use strict";
+SimpleTest.waitForExplicitFinish();
+
+const HACK_PREF = "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch";
+const testEl = document.getElementById("test");
+const linkEl = document.getElementById("link-test");
+let seenClick = false;
+
+SpecialPowers.pushPrefEnv(
+  { set: [[HACK_PREF, document.domain]] },
+  SimpleTest.waitForFocus(() => {
+    // Test seeing the non-primary 'click'
+    document.addEventListener("click", (e) => {
+      ok(true, "Saw 'click' event");
+      seenClick = true;
+    }, { once: true });
+    document.addEventListener("auxclick", (e) => {
+      ok(true, "Saw 'auxclick' event");
+      ok(seenClick, "Saw 'click' event before 'auxclick' event");
+    }, { once: true });
+    synthesizeMouseAtCenter(testEl, { button: 1 });
+
+    // Test preventDefaulting on non-primary 'click'
+    document.addEventListener("click", (e) => {
+      is(e.target, linkEl, "Saw 'click' on link");
+      e.preventDefault();
+      SimpleTest.finish();
+    }, { once: true, capture: true });
+    document.addEventListener("auxclick", (e) => {
+      ok(false, "Shouldn't have got 'auxclick' after preventDefaulting 'click'");
+    }, { once: true });
+    synthesizeMouseAtCenter(linkEl, { button: 1 });
+  })
+);
+</script>
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -122,26 +122,25 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 // XXX align=left, hspace, vspace, border? other nav4 attrs
 
 namespace mozilla {
 namespace dom {
 
 // First bits are needed for the control type.
 #define NS_OUTER_ACTIVATE_EVENT (1 << 9)
 #define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
-#define NS_NO_CONTENT_DISPATCH (1 << 11)
+// (1 << 11 is unused)
 #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
 #define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
 #define NS_PRE_HANDLE_INPUT_EVENT (1 << 14)
 #define NS_IN_SUBMIT_CLICK (1 << 15)
-#define NS_CONTROL_TYPE(bits)                                            \
-  ((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE |      \
-              NS_NO_CONTENT_DISPATCH | NS_ORIGINAL_INDETERMINATE_VALUE | \
-              NS_PRE_HANDLE_BLUR_EVENT | NS_PRE_HANDLE_INPUT_EVENT |     \
-              NS_IN_SUBMIT_CLICK))
+#define NS_CONTROL_TYPE(bits)                                              \
+  ((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE |        \
+              NS_ORIGINAL_INDETERMINATE_VALUE | NS_PRE_HANDLE_BLUR_EVENT | \
+              NS_PRE_HANDLE_INPUT_EVENT | NS_IN_SUBMIT_CLICK))
 
 // whether textfields should be selected once focused:
 //  -1: no, 1: yes, 0: uninitialized
 static int32_t gSelectTextFieldOnFocus;
 UploadLastDir* HTMLInputElement::gUploadLastDir;
 
 static const nsAttrValue::EnumTable kInputTypeTable[] = {
     {"button", NS_FORM_INPUT_BUTTON},
@@ -3197,29 +3196,16 @@ void HTMLInputElement::GetEventTargetPar
         break;
     }
   }
 
   if (originalCheckedValue) {
     aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
   }
 
-  // If mNoContentDispatch is true we will not allow content to handle
-  // this event.  But to allow middle mouse button paste to work we must allow
-  // middle clicks to go to text fields anyway.
-  if (aVisitor.mEvent->mFlags.mNoContentDispatch) {
-    aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
-  }
-  if (IsSingleLineTextControl(false) &&
-      aVisitor.mEvent->mMessage == eMouseClick &&
-      aVisitor.mEvent->AsMouseEvent()->button ==
-          WidgetMouseEvent::eMiddleButton) {
-    aVisitor.mEvent->mFlags.mNoContentDispatch = false;
-  }
-
   // We must cache type because mType may change during JS event (bug 2369)
   aVisitor.mItemFlags |= mType;
 
   if (aVisitor.mEvent->mMessage == eFocus && aVisitor.mEvent->IsTrusted() &&
       MayFireChangeOnBlur() &&
       // StartRangeThumbDrag already set mFocusedValue on 'mousedown' before
       // we get the 'focus' event.
       !mIsDraggingRange) {
@@ -3700,17 +3686,16 @@ nsresult HTMLInputElement::PostHandleEve
 
     UpdateState(true);
   }
 
   nsresult rv = NS_OK;
   bool outerActivateEvent = !!(aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT);
   bool originalCheckedValue =
       !!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
-  bool noContentDispatch = !!(aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH);
   uint8_t oldType = NS_CONTROL_TYPE(aVisitor.mItemFlags);
 
   // Ideally we would make the default action for click and space just dispatch
   // DOMActivate, and the default action for DOMActivate flip the checkbox/
   // radio state and fire onchange.  However, for backwards compatibility, we
   // need to flip the state before firing click, and we need to fire click
   // when space is pressed.  So, we just nest the firing of DOMActivate inside
   // the click event handling, and allow cancellation of DOMActivate to cancel
@@ -3751,19 +3736,16 @@ nsresult HTMLInputElement::PostHandleEve
         // will be flushed or forgoten.
         mForm->OnSubmitClickEnd();
         break;
       default:
         break;
     }
   }
 
-  // Reset the flag for other content besides this text field
-  aVisitor.mEvent->mFlags.mNoContentDispatch = noContentDispatch;
-
   // now check to see if the event was "cancelled"
   if (mCheckedIsToggled && outerActivateEvent) {
     if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
       // if it was cancelled and a radio button, then set the old
       // selected btn to TRUE. if it is a checkbox then set it to its
       // original value
       if (oldType == NS_FORM_INPUT_RADIO) {
         nsCOMPtr<nsIContent> content = do_QueryInterface(aVisitor.mItemData);
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -38,18 +38,16 @@
 #include "nsPresContext.h"
 #include "mozilla/PresState.h"
 #include "nsReadableUtils.h"
 #include "nsStyleConsts.h"
 #include "nsTextEditorState.h"
 #include "nsBaseCommandController.h"
 #include "nsXULControllers.h"
 
-#define NS_NO_CONTENT_DISPATCH (1 << 0)
-
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
 namespace mozilla {
 namespace dom {
 
 HTMLTextAreaElement::HTMLTextAreaElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser)
@@ -447,28 +445,16 @@ void HTMLTextAreaElement::GetEventTarget
   // one.
   if (aVisitor.mEvent->mMessage == eFormSelect) {
     if (mHandlingSelect) {
       return;
     }
     mHandlingSelect = true;
   }
 
-  // If noContentDispatch is true we will not allow content to handle
-  // this event.  But to allow middle mouse button paste to work we must allow
-  // middle clicks to go to text fields anyway.
-  if (aVisitor.mEvent->mFlags.mNoContentDispatch) {
-    aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
-  }
-  if (aVisitor.mEvent->mMessage == eMouseClick &&
-      aVisitor.mEvent->AsMouseEvent()->button ==
-          WidgetMouseEvent::eMiddleButton) {
-    aVisitor.mEvent->mFlags.mNoContentDispatch = false;
-  }
-
   if (aVisitor.mEvent->mMessage == eBlur) {
     // Set mWantsPreHandleEvent and fire change event in PreHandleEvent to
     // prevent it breaks event target chain creation.
     aVisitor.mWantsPreHandleEvent = true;
   }
 
   nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
 }
@@ -515,20 +501,16 @@ nsresult HTMLTextAreaElement::PostHandle
     } else {  // eBlur
       mCanShowInvalidUI = true;
       mCanShowValidUI = true;
     }
 
     UpdateState(true);
   }
 
-  // Reset the flag for other content besides this text field
-  aVisitor.mEvent->mFlags.mNoContentDispatch =
-      ((aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH) != 0);
-
   return NS_OK;
 }
 
 void HTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified) {
   if (!mValueChanged) {
     if (!mDoneAddingChildren) {
       // Reset now that we're done adding children if the content sink tried to
       // sneak some text in without calling AppendChildTo.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -28,35 +28,32 @@
 #include "nsPresContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMString.h"
 #include "nsIStreamListener.h"
 #include "nsIURI.h"
 #include "nsIURIMutator.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
-#include "nsIPrivateBrowsingChannel.h"
 #include "nsIContentViewer.h"
 #include "nsDocShell.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsContentList.h"
 #include "nsError.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsAttrName.h"
 #include "nsNodeUtils.h"
 
 #include "nsNetCID.h"
-#include "nsICookieService.h"
-
 #include "nsIServiceManager.h"
 #include "nsIConsoleService.h"
 #include "nsIComponentManager.h"
 #include "nsParserCIID.h"
 #include "mozilla/parser/PrototypeDocumentParser.h"
 #include "mozilla/dom/PrototypeDocumentContentSink.h"
 #include "nsNameSpaceManager.h"
 #include "nsGenericHTMLElement.h"
@@ -175,17 +172,16 @@ nsHTMLDocument::nsHTMLDocument()
       mNumForms(0),
       mWriteLevel(0),
       mLoadFlags(0),
       mTooDeepWriteRecursion(false),
       mDisableDocWrite(false),
       mWarnedWidthHeight(false),
       mContentEditableCount(0),
       mEditingState(EditingState::eOff),
-      mDisableCookieAccess(false),
       mPendingMaybeEditingStateChanged(false),
       mHasBeenEditable(false),
       mIsPlainText(false) {
   mType = eHTML;
   mDefaultElementType = kNameSpaceID_XHTML;
   mCompatMode = eCompatibility_NavQuirks;
 }
 
@@ -964,179 +960,16 @@ void nsHTMLDocument::SetDomain(const nsA
     // Error: illegal domain
     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   rv = NodePrincipal()->SetDomain(newURI);
 }
 
-already_AddRefed<nsIChannel> nsHTMLDocument::CreateDummyChannelForCookies(
-    nsIURI* aCodebaseURI) {
-  // The cookie service reads the privacy status of the channel we pass to it in
-  // order to determine which cookie database to query.  In some cases we don't
-  // have a proper channel to hand it to the cookie service though.  This
-  // function creates a dummy channel that is not used to load anything, for the
-  // sole purpose of handing it to the cookie service.  DO NOT USE THIS CHANNEL
-  // FOR ANY OTHER PURPOSE.
-  MOZ_ASSERT(!mChannel);
-
-  // The following channel is never openend, so it does not matter what
-  // securityFlags we pass; let's follow the principle of least privilege.
-  nsCOMPtr<nsIChannel> channel;
-  NS_NewChannel(getter_AddRefs(channel), aCodebaseURI, this,
-                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
-                nsIContentPolicy::TYPE_INVALID);
-  nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
-  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
-  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
-  if (!pbChannel || !loadContext) {
-    return nullptr;
-  }
-  pbChannel->SetPrivate(loadContext->UsePrivateBrowsing());
-
-  nsCOMPtr<nsIHttpChannel> docHTTPChannel = do_QueryInterface(GetChannel());
-  if (docHTTPChannel) {
-    bool isTracking = docHTTPChannel->IsTrackingResource();
-    if (isTracking) {
-      // If our document channel is from a tracking resource, we must
-      // override our channel's tracking status.
-      nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-      MOZ_ASSERT(httpChannel,
-                 "How come we're coming from an HTTP doc but "
-                 "we don't have an HTTP channel here?");
-      if (httpChannel) {
-        httpChannel->OverrideTrackingFlagsForDocumentCookieAccessor(
-            docHTTPChannel);
-      }
-    }
-  }
-
-  return channel.forget();
-}
-
-void nsHTMLDocument::GetCookie(nsAString& aCookie, ErrorResult& rv) {
-  aCookie.Truncate();  // clear current cookie in case service fails;
-                       // no cookie isn't an error condition.
-
-  if (mDisableCookieAccess) {
-    return;
-  }
-
-  // If the document's sandboxed origin flag is set, access to read cookies
-  // is prohibited.
-  if (mSandboxFlags & SANDBOXED_ORIGIN) {
-    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-  nsContentUtils::StorageAccess storageAccess =
-      nsContentUtils::StorageAllowedForDocument(this);
-  if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
-    return;
-  }
-
-  if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
-    return;
-  }
-
-  // If the document is a cookie-averse Document... return the empty string.
-  if (IsCookieAverse()) {
-    return;
-  }
-
-  // not having a cookie service isn't an error
-  nsCOMPtr<nsICookieService> service =
-      do_GetService(NS_COOKIESERVICE_CONTRACTID);
-  if (service) {
-    // Get a URI from the document principal. We use the original
-    // codebase in case the codebase was changed by SetDomain
-    nsCOMPtr<nsIURI> codebaseURI;
-    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
-
-    if (!codebaseURI) {
-      // Document's principal is not a codebase (may be system), so
-      // can't set cookies
-
-      return;
-    }
-
-    nsCOMPtr<nsIChannel> channel(mChannel);
-    if (!channel) {
-      channel = CreateDummyChannelForCookies(codebaseURI);
-      if (!channel) {
-        return;
-      }
-    }
-
-    nsCString cookie;
-    service->GetCookieString(codebaseURI, channel, getter_Copies(cookie));
-    // CopyUTF8toUTF16 doesn't handle error
-    // because it assumes that the input is valid.
-    UTF_8_ENCODING->DecodeWithoutBOMHandling(cookie, aCookie);
-  }
-}
-
-void nsHTMLDocument::SetCookie(const nsAString& aCookie, ErrorResult& rv) {
-  if (mDisableCookieAccess) {
-    return;
-  }
-
-  // If the document's sandboxed origin flag is set, access to write cookies
-  // is prohibited.
-  if (mSandboxFlags & SANDBOXED_ORIGIN) {
-    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-  nsContentUtils::StorageAccess storageAccess =
-      nsContentUtils::StorageAllowedForDocument(this);
-  if (storageAccess == nsContentUtils::StorageAccess::eDeny) {
-    return;
-  }
-
-  if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
-      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
-    return;
-  }
-
-  // If the document is a cookie-averse Document... do nothing.
-  if (IsCookieAverse()) {
-    return;
-  }
-
-  // not having a cookie service isn't an error
-  nsCOMPtr<nsICookieService> service =
-      do_GetService(NS_COOKIESERVICE_CONTRACTID);
-  if (service && mDocumentURI) {
-    // The for getting the URI matches nsNavigator::GetCookieEnabled
-    nsCOMPtr<nsIURI> codebaseURI;
-    NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
-
-    if (!codebaseURI) {
-      // Document's principal is not a codebase (may be system), so
-      // can't set cookies
-
-      return;
-    }
-
-    nsCOMPtr<nsIChannel> channel(mChannel);
-    if (!channel) {
-      channel = CreateDummyChannelForCookies(codebaseURI);
-      if (!channel) {
-        return;
-      }
-    }
-
-    NS_ConvertUTF16toUTF8 cookie(aCookie);
-    service->SetCookieString(codebaseURI, nullptr, cookie.get(), channel);
-  }
-}
-
 mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> nsHTMLDocument::Open(
     const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
     bool aReplace, ErrorResult& rv) {
   MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
              "XOW should have caught this!");
 
   nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
   if (!window) {
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -101,18 +101,16 @@ class nsHTMLDocument : public mozilla::d
   }
 
   nsresult ChangeContentEditableCount(nsIContent* aElement,
                                       int32_t aChange) override;
   void DeferredContentEditableCountChange(nsIContent* aElement);
 
   virtual EditingState GetEditingState() override { return mEditingState; }
 
-  virtual void DisableCookieAccess() override { mDisableCookieAccess = true; }
-
   class nsAutoEditingState {
    public:
     nsAutoEditingState(nsHTMLDocument* aDoc, EditingState aState)
         : mDoc(aDoc), mSavedState(aDoc->mEditingState) {
       aDoc->mEditingState = aState;
     }
     ~nsAutoEditingState() { mDoc->mEditingState = mSavedState; }
 
@@ -142,18 +140,16 @@ class nsHTMLDocument : public mozilla::d
 
   // WebIDL API
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) override;
   void GetDomain(nsAString& aDomain);
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
                                             const nsACString& aOrigHost);
-  void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
-  void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
                    JS::MutableHandle<JSObject*> aRetval,
                    mozilla::ErrorResult& rv) {
     JS::Rooted<JS::Value> v(cx);
     if ((aFound = ResolveName(cx, aName, &v, rv))) {
       aRetval.set(v.toObjectOrNull());
     }
   }
@@ -235,20 +231,16 @@ class nsHTMLDocument : public mozilla::d
       const nsAString& aHostSuffixString, nsIURI* aOrigHost);
 
   void WriteCommon(const nsAString& aText, bool aNewlineTerminate,
                    mozilla::ErrorResult& aRv);
   // A version of WriteCommon used by WebIDL bindings
   void WriteCommon(const mozilla::dom::Sequence<nsString>& aText,
                    bool aNewlineTerminate, mozilla::ErrorResult& rv);
 
-  // This should *ONLY* be used in GetCookie/SetCookie.
-  already_AddRefed<nsIChannel> CreateDummyChannelForCookies(
-      nsIURI* aCodebaseURI);
-
   /**
    * Like IsEditingOn(), but will flush as needed first.
    */
   bool IsEditingOnAfterFlush();
 
   void* GenerateParserKey(void);
 
   // A helper class to keep nsContentList objects alive for a short period of
@@ -331,19 +323,16 @@ class nsHTMLDocument : public mozilla::d
   // of places, and I'm pretty sure the exact ExecCommand call it
   // makes cannot actually run script.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult EditingStateChanged();
   void MaybeEditingStateChanged();
 
   uint32_t mContentEditableCount;
   EditingState mEditingState;
 
-  // When false, the .cookies property is completely disabled
-  bool mDisableCookieAccess;
-
   /**
    * Temporary flag that is set in EndUpdate() to ignore
    * MaybeEditingStateChanged() script runners from a nested scope.
    */
   bool mPendingMaybeEditingStateChanged;
 
   // mHasBeenEditable is set to true when mEditingState is firstly set to
   // eDesignMode or eContentEditable.
--- a/dom/html/nsIHTMLDocument.h
+++ b/dom/html/nsIHTMLDocument.h
@@ -89,21 +89,16 @@ class nsIHTMLDocument : public nsISuppor
   /**
    * Set the editing state of the document. Don't use this if you want
    * to enable/disable editing, call EditingStateChanged() or
    * SetDesignMode().
    */
   virtual nsresult SetEditingState(EditingState aState) = 0;
 
   /**
-   * Disables getting and setting cookies
-   */
-  virtual void DisableCookieAccess() = 0;
-
-  /**
    * Called when this nsIHTMLDocument's editor is destroyed.
    */
   virtual void TearingDownEditor() = 0;
 
   virtual void SetIsXHTML(bool aXHTML) = 0;
 
   virtual void SetDocWriteDisabled(bool aDisabled) = 0;
 };
--- a/dom/interfaces/base/nsIContentPermissionPrompt.idl
+++ b/dom/interfaces/base/nsIContentPermissionPrompt.idl
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
+#include "domstubs.idl"
 
 interface nsIPrincipal;
 interface mozIDOMWindow;
 interface nsIArray;
 
 webidl Element;
 
 /**
@@ -83,16 +84,18 @@ interface nsIContentPermissionRequest : 
    *  originated in.  Typically the element will be non-null
    *  in when using out of process content.  window or
    *  element can be null but not both.
    */
   readonly attribute mozIDOMWindow window;
   readonly attribute Element element;
 
   readonly attribute boolean isHandlingUserInput;
+  readonly attribute boolean userHadInteractedWithDocument;
+  readonly attribute DOMTimeStamp documentDOMContentLoadedTimestamp;
 
   /**
    *  The requester to get the required information of
    *  the window.
    */
   readonly attribute nsIContentPermissionRequester requester;
 
   /**
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1664,26 +1664,22 @@ static bool StartMacOSContentSandbox() {
   if (profileDir) {
     info.hasSandboxedProfile = true;
     info.profileDir.assign(profileDirPath.get());
   } else {
     info.hasSandboxedProfile = false;
   }
 
 #  ifdef DEBUG
-  // When a content process dies intentionally (|NoteIntentionalCrash|), for
-  // tests it wants to log that it did this. Allow writing to this location
-  // that the testrunner wants.
-  char* bloatLog = PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
-  if (bloatLog != nullptr) {
-    // |bloatLog| points to a specific file, but we actually write to a sibling
-    // of that path.
-    nsAutoCString bloatDirectoryPath =
-        nsMacUtilsImpl::GetDirectoryPath(bloatLog);
-    info.debugWriteDir.assign(bloatDirectoryPath.get());
+  // For bloat/leak logging or when a content process dies intentionally
+  // (|NoteIntentionalCrash|) for tests, it wants to log that it did this.
+  // Allow writing to this location.
+  nsAutoCString bloatLogDirPath;
+  if (NS_SUCCEEDED(nsMacUtilsImpl::GetBloatLogDir(bloatLogDirPath))) {
+    info.debugWriteDir = bloatLogDirPath.get();
   }
 #  endif  // DEBUG
 
   std::string err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("sandbox_init() failed");
   }
@@ -3020,17 +3016,18 @@ mozilla::ipc::IPCResult ContentChild::Re
   return IPC_FAIL_NO_REASON(this);
 #endif
 }
 
 PContentPermissionRequestChild*
 ContentChild::AllocPContentPermissionRequestChild(
     const InfallibleTArray<PermissionRequest>& aRequests,
     const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
-    const bool& aIsHandlingUserInput, const TabId& aTabId) {
+    const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
+    const DOMTimeStamp aPageLoadTimestamp, const TabId& aTabId) {
   MOZ_CRASH("unused");
   return nullptr;
 }
 
 bool ContentChild::DeallocPContentPermissionRequestChild(
     PContentPermissionRequestChild* actor) {
   nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor);
   auto child = static_cast<RemotePermissionRequest*>(actor);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -532,17 +532,18 @@ class ContentChild final : public PConte
   PWebrtcGlobalChild* AllocPWebrtcGlobalChild();
 
   bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor);
 
   PContentPermissionRequestChild* AllocPContentPermissionRequestChild(
       const InfallibleTArray<PermissionRequest>& aRequests,
       const IPC::Principal& aPrincipal,
       const IPC::Principal& aTopLevelPrincipal,
-      const bool& aIsHandlingUserInput, const TabId& aTabId);
+      const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
+      const DOMTimeStamp aPageLoadTimestamp, const TabId& aTabId);
   bool DeallocPContentPermissionRequestChild(
       PContentPermissionRequestChild* actor);
 
   // Windows specific - set up audio session
   mozilla::ipc::IPCResult RecvSetAudioSessionData(const nsID& aId,
                                                   const nsString& aDisplayName,
                                                   const nsString& aIconPath);
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1985,26 +1985,22 @@ static void CacheSandboxParams(std::vect
     }
     nsCString objDirPath;
     Unused << objDir->GetNativePath(objDirPath);
     info.testingReadPath4 = objDirPath.get();
   }
 
   // DEBUG_WRITE_DIR
 #  ifdef DEBUG
-  // When a content process dies intentionally (|NoteIntentionalCrash|), for
-  // tests it wants to log that it did this. Allow writing to this location
-  // that the testrunner wants.
-  char* bloatLog = PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
-  if (bloatLog != nullptr) {
-    // |bloatLog| points to a specific file, but we actually write to a sibling
-    // of that path.
-    nsAutoCString bloatDirectoryPath =
-        nsMacUtilsImpl::GetDirectoryPath(bloatLog);
-    info.debugWriteDir = bloatDirectoryPath.get();
+  // For bloat/leak logging or when a content process dies intentionally
+  // (|NoteIntentionalCrash|) for tests, it wants to log that it did this.
+  // Allow writing to this location.
+  nsAutoCString bloatLogDirPath;
+  if (NS_SUCCEEDED(nsMacUtilsImpl::GetBloatLogDir(bloatLogDirPath))) {
+    info.debugWriteDir = bloatLogDirPath.get();
   }
 #  endif  // DEBUG
 
   info.AppendAsParams(aCachedParams);
 }
 
 // Append sandboxing command line parameters.
 void ContentParent::AppendSandboxParams(std::vector<std::string>& aArgs) {
@@ -4620,27 +4616,28 @@ mozilla::ipc::IPCResult ContentParent::R
   }
   return IPC_OK();
 }
 
 PContentPermissionRequestParent*
 ContentParent::AllocPContentPermissionRequestParent(
     const InfallibleTArray<PermissionRequest>& aRequests,
     const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
-    const bool& aIsHandlingUserInput, const TabId& aTabId) {
+    const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
+    const DOMTimeStamp& aPageLoadTimestamp, const TabId& aTabId) {
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   RefPtr<TabParent> tp =
       cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId);
   if (!tp) {
     return nullptr;
   }
 
   return nsContentPermissionUtils::CreateContentPermissionRequestParent(
       aRequests, tp->GetOwnerElement(), aPrincipal, aTopLevelPrincipal,
-      aIsHandlingUserInput, aTabId);
+      aIsHandlingUserInput, aDocumentHasUserInput, aPageLoadTimestamp, aTabId);
 }
 
 bool ContentParent::DeallocPContentPermissionRequestParent(
     PContentPermissionRequestParent* actor) {
   nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
   delete actor;
   return true;
 }
@@ -5709,17 +5706,17 @@ mozilla::ipc::IPCResult ContentParent::R
   for (auto iter = child->Group()->ContentParentsIter(); !iter.Done();
        iter.Next()) {
     nsRefPtrHashKey<ContentParent>* entry = iter.Get();
     if (entry->GetKey() == this) {
       continue;
     }
 
     Unused << entry->GetKey()->SendAttachBrowsingContext(
-      child->GetIPCInitializer());
+        child->GetIPCInitializer());
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvDetachBrowsingContext(
     BrowsingContext* aContext, bool aMoveToBFCache) {
   if (!aContext) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -495,18 +495,19 @@ class ContentParent final : public PCont
 
   mozilla::ipc::IPCResult RecvFinishShutdown();
 
   void MaybeInvokeDragSession(TabParent* aParent);
 
   PContentPermissionRequestParent* AllocPContentPermissionRequestParent(
       const InfallibleTArray<PermissionRequest>& aRequests,
       const IPC::Principal& aPrincipal,
-      const IPC::Principal& aTopLevelPrincipal, const bool& aIsTrusted,
-      const TabId& aTabId);
+      const IPC::Principal& aTopLevelPrincipal,
+      const bool& aIsHandlingUserInput, const bool& aDocumentHasUserInput,
+      const DOMTimeStamp& aPageLoadTimestamp, const TabId& aTabId);
 
   bool DeallocPContentPermissionRequestParent(
       PContentPermissionRequestParent* actor);
 
   virtual bool HandleWindowsMessages(const Message& aMsg) const override;
 
   void ForkNewProcess(bool aBlocking);
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1126,17 +1126,17 @@ parent:
      *   To identify which tab issues this request.
      *
      * NOTE: The principal is untrusted in the parent process. Only
      *       principals that can live in the content process should
      *       provided.
      */
     async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
                                     Principal aTopLevelPrincipal, bool aIsHandlingUserInput,
-                                    TabId tabId);
+                                    bool aDocumentHasUserInput, uint64_t aPageLoadTimestamp, TabId tabId);
 
     async ShutdownProfile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (ContentDeviceData aData);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -365,17 +365,18 @@ void TabParent::AddWindowListeners() {
         eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                       this, false, false);
       }
     }
   }
 }
 
 void TabParent::RemoveWindowListeners() {
-  if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
+  if (mFrameElement && mFrameElement->OwnerDoc() &&
+      mFrameElement->OwnerDoc()->GetWindow()) {
     nsCOMPtr<nsPIDOMWindowOuter> window =
         mFrameElement->OwnerDoc()->GetWindow();
     nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
     if (eventTarget) {
       eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                        this, false);
     }
   }
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -1089,18 +1089,18 @@ class VideoStreamHelper {
     this._helper = new CaptureStreamTestHelper2D(50,50);
   }
 
   async checkHasFrame(video, { offsetX, offsetY, threshold } = {}) {
     const h = this._helper;
     await h.waitForPixel(video, px => {
       let result = h.isOpaquePixelNot(px, h.black, threshold);
       info("Checking that we have a frame, got [" +
-           Array.slice(px) + "]. Ref=[" +
-           Array.slice(h.black.data) + "]. Threshold=" + threshold +
+           Array.from(px) + "]. Ref=[" +
+           Array.from(h.black.data) + "]. Threshold=" + threshold +
            ". Pass=" + result);
       return result;
     }, { offsetX, offsetY });
   }
 
   async checkVideoPlaying(video, { offsetX = 10, offsetY = 10,
                                    threshold = 16,
                                  } = {}) {
@@ -1108,17 +1108,17 @@ class VideoStreamHelper {
     await this.checkHasFrame(video, { offsetX, offsetY, threshold });
     let startPixel = {
       data: h.getPixel(video, offsetX, offsetY),
       name: "startcolor",
     };
     await h.waitForPixel(video, px => {
       let result = h.isPixelNot(px, startPixel, threshold);
       info("Checking playing, [" +
-           Array.slice(px) + "] vs [" + Array.slice(startPixel.data) +
+           Array.from(px) + "] vs [" + Array.from(startPixel.data) +
            "]. Threshold=" + threshold + " Pass=" + result);
       return result;
     }, { offsetX, offsetY });
   }
 
   async checkVideoPaused(video, { offsetX = 10, offsetY = 10,
                                   threshold = 16, time = 5000,
                                 }={}) {
@@ -1127,17 +1127,17 @@ class VideoStreamHelper {
     let startPixel = {
       data: h.getPixel(video, offsetX, offsetY),
       name: "startcolor",
     };
     try {
       await h.waitForPixel(video, px => {
           let result = h.isOpaquePixelNot(px, startPixel, threshold);
           info("Checking paused, [" +
-               Array.slice(px) + "] vs [" + Array.slice(startPixel.data) +
+               Array.from(px) + "] vs [" + Array.from(startPixel.data) +
                "]. Threshold=" + threshold + " Pass=" + result);
           return result;
         }, { offsetX, offsetY, cancel: wait(time, "timeout") });
       ok(false, "Frame changed within " + time/1000 + " seconds");
     } catch (e) {
       is(e, "timeout", "Frame shouldn't change for " + time/1000 + " seconds");
     }
   }
--- a/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -36,17 +36,17 @@
         let x = offsetX + dx + internalX;
         let y = offsetY + dy + internalY;
         info("Checking screen coordinate (" + [x,y] + ") of total resolution "
           + video.videoWidth + "x" + video.videoHeight
           + " against " + color.name + ".");
         await helper.waitForPixel(video, px => {
           let result = helper.isPixel(px, color, 16);
           info("Checking pixel against " + color.name + ". Got ["
-            + Array.slice(px) + "] (" + (result ? "YES" : "NO") + ")");
+            + Array.from(px) + "] (" + (result ? "YES" : "NO") + ")");
           return result;
         }, {offsetX: x, offsetY: y});
       }
     };
 
     let screenSizeSq = Math.min(video.videoWidth, video.videoHeight);
 
     info("Waiting for upper left quadrant to become " + upleft.name);
--- a/dom/push/Push.jsm
+++ b/dom/push/Push.jsm
@@ -177,21 +177,23 @@ Push.prototype = {
       options: [],
       QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
     };
     let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     typeArray.appendElement(type);
 
     // create a nsIContentPermissionRequest
     let request = {
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
       types: typeArray,
       principal: this._principal,
       isHandlingUserInput,
+      userHadInteractedWithDocument: this._window.document.userHasInteracted,
+      documentDOMContentLoadedTimestamp: this._window.performance.timing.domContentLoadedEventEnd,
       topLevelPrincipal: this._topLevelPrincipal,
-      QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionRequest]),
       allow: allowCallback,
       cancel: cancelCallback,
       window: this._window,
     };
 
     // Using askPermission from nsIDOMWindowUtils that takes care of the
     // remoting if needed.
     let windowUtils = this._window.windowUtils;
--- a/dom/security/CSPEvalChecker.cpp
+++ b/dom/security/CSPEvalChecker.cpp
@@ -5,32 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CSPEvalChecker.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/ErrorResult.h"
 #include "nsGlobalWindowInner.h"
 #include "mozilla/dom/Document.h"
+#include "nsContentSecurityManager.h"
+#include "nsContentUtils.h"
 #include "nsCOMPtr.h"
 #include "nsJSUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace {
 
+// We use the subjectPrincipal to assert that eval() is never
+// executed in system privileged context.
 nsresult CheckInternal(nsIContentSecurityPolicy* aCSP,
                        nsICSPEventListener* aCSPEventListener,
+                       nsIPrincipal* aSubjectPrincipal,
                        const nsAString& aExpression,
                        const nsAString& aFileNameString, uint32_t aLineNum,
                        uint32_t aColumnNum, bool* aAllowed) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aAllowed);
 
+#if defined(DEBUG) && !defined(ANDROID)
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(aSubjectPrincipal,
+                                                              cx);
+#endif
+
   // The value is set at any "return", but better to have a default value here.
   *aAllowed = false;
 
   if (!aCSP) {
     *aAllowed = true;
     return NS_OK;
   }
 
@@ -64,17 +75,18 @@ class WorkerCSPCheckRunnable final : pub
         mFileNameString(aFileNameString),
         mLineNum(aLineNum),
         mColumnNum(aColumnNum),
         mEvalAllowed(false) {}
 
   bool MainThreadRun() override {
     mResult = CheckInternal(
         mWorkerPrivate->GetCSP(), mWorkerPrivate->CSPEventListener(),
-        mExpression, mFileNameString, mLineNum, mColumnNum, &mEvalAllowed);
+        mWorkerPrivate->GetLoadingPrincipal(), mExpression, mFileNameString,
+        mLineNum, mColumnNum, &mEvalAllowed);
     return true;
   }
 
   nsresult GetResult(bool* aAllowed) {
     MOZ_ASSERT(aAllowed);
     *aAllowed = mEvalAllowed;
     return mResult;
   }
@@ -123,18 +135,18 @@ nsresult CSPEvalChecker::CheckForWindow(
   uint32_t columnNum = 0;
   nsAutoString fileNameString;
   if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum,
                                      &columnNum)) {
     fileNameString.AssignLiteral("unknown");
   }
 
   rv = CheckInternal(csp, nullptr /* no CSPEventListener for window */,
-                     aExpression, fileNameString, lineNum, columnNum,
-                     aAllowEval);
+                     doc->NodePrincipal(), aExpression, fileNameString, lineNum,
+                     columnNum, aAllowEval);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     *aAllowEval = false;
     return rv;
   }
 
   return NS_OK;
 }
 
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -16,16 +16,17 @@
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
 #include "nsIURIFixup.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIRedirectHistoryEntry.h"
 
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/Components.h"
 #include "mozilla/Logging.h"
 #include "xpcpublic.h"
 
 NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager,
@@ -155,16 +156,61 @@ bool nsContentSecurityManager::AllowInse
   nsContentUtils::ReportToConsole(
       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DATA_URI_BLOCKED"), doc,
       nsContentUtils::eSECURITY_PROPERTIES, "BlockSubresourceRedirectToData",
       params, ArrayLength(params));
   return false;
 }
 
 /* static */
+void nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(
+    nsIPrincipal* subjectPrincipal, JSContext* cx) {
+  if (!subjectPrincipal->IsSystemPrincipal()) {
+    return;
+  }
+
+  if (Preferences::GetBool("security.allow_eval_with_system_principal")) {
+    return;
+  }
+
+  static StaticAutoPtr<nsTArray<nsCString>> sUrisAllowEval;
+  JS::AutoFilename scriptFilename;
+  if (JS::DescribeScriptedCaller(cx, &scriptFilename)) {
+    if (!sUrisAllowEval) {
+      sUrisAllowEval = new nsTArray<nsCString>();
+      nsAutoCString urisAllowEval;
+      Preferences::GetCString("security.uris_using_eval_with_system_principal",
+                              urisAllowEval);
+      for (const nsACString& filenameString : urisAllowEval.Split(',')) {
+        sUrisAllowEval->AppendElement(filenameString);
+      }
+      ClearOnShutdown(&sUrisAllowEval);
+    }
+
+    nsAutoCString fileName;
+    fileName = nsAutoCString(scriptFilename.get());
+    // Extract file name alone if scriptFilename contains line number
+    // separated by multiple space delimiters in few cases.
+    int32_t fileNameIndex = fileName.FindChar(' ');
+    if (fileNameIndex != -1) {
+      fileName = Substring(fileName, 0, fileNameIndex);
+    }
+    ToLowerCase(fileName);
+
+    for (auto& uriEntry : *sUrisAllowEval) {
+      if (StringEndsWith(fileName, uriEntry)) {
+        return;
+      }
+    }
+  }
+
+  MOZ_ASSERT(false, "do not use eval with system privileges");
+}
+
+/* static */
 nsresult nsContentSecurityManager::CheckFTPSubresourceLoad(
     nsIChannel* aChannel) {
   // We dissallow using FTP resources as a subresource almost everywhere.
   // The only valid way to use FTP resources is loading it as
   // a top level document.
   if (!mozilla::net::nsIOService::BlockFTPSubresources()) {
     return NS_OK;
   }
--- a/dom/security/nsContentSecurityManager.h
+++ b/dom/security/nsContentSecurityManager.h
@@ -33,16 +33,19 @@ class nsContentSecurityManager : public 
   nsContentSecurityManager() {}
 
   static nsresult doContentSecurityCheck(
       nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener);
 
   static bool AllowTopLevelNavigationToDataURI(nsIChannel* aChannel);
   static bool AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel);
 
+  static void AssertEvalNotUsingSystemPrincipal(nsIPrincipal* subjectPrincipal,
+                                                JSContext* cx);
+
  private:
   static nsresult CheckChannel(nsIChannel* aChannel);
   static nsresult CheckFTPSubresourceLoad(nsIChannel* aChannel);
 
   virtual ~nsContentSecurityManager() {}
 };
 
 #endif /* nsContentSecurityManager_h___ */
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -33,17 +33,17 @@ function spawnWithObserver(browser, obse
     // initialization can be yeilded on before yeilding on the conclusion of the
     // test.
     "content._promise = new Promise(_resolve => {",
     // These are variables which are used by the test runner to communicate
     // state to the observer.
     "  let gLevel, gArgs, gStyle;",
     "  let expect = function(level) {",
     "    gLevel = level;",
-    "    gArgs = Array.slice(arguments, 1);",
+    "    gArgs = Array.prototype.slice.call(arguments, 1);",
     "  }",
     // To ease the transition to the new format, content.window is avaliable as gWindow
     // in the content.
     "  let gWindow = content.window;",
     // This method is called rather than _resolve such that the observer is removed
     // before exiting the test
     "  let resolve = () => {",
     "    Services.obs.removeObserver(ConsoleObserver, 'console-api-log-event');",
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -82,16 +82,17 @@ skip-if = (toolkit == 'android') # Disab
 subsuite = clipboard
 [test_bug1208217.html]
 [test_bug1313753.html]
 [test_bug1434273.html]
 [test_clientRects.html]
 [test_clipboard_disallowed.html]
 [test_clipboard_events.html]
 subsuite = clipboard
+skip-if = headless # bug 1403542
 [test_consoleAPI.html]
 [test_contentViewer_overrideDPPX.html]
 [test_CCW_optimization.html]
 [test_datatransfer_disallowed.html]
 [test_devicePixelRatio_with_zoom.html]
 [test_DOMMatrix.html]
 [test_domWindowUtils.html]
 [test_domWindowUtils_scrollbarSize.html]
--- a/dom/tests/mochitest/general/test_clipboard_events.html
+++ b/dom/tests/mochitest/general/test_clipboard_events.html
@@ -920,32 +920,35 @@ add_task(async function test_paste_event
   auxclickFired = false;
   document.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
   synthesizeMouseAtCenter(noneditableDiv, {button: 1});
   is(pasteEventCount, 1,
      "Even if 'mouseup' event is consumed, 'paste' event should be fired");
 
   pasteEventCount = 0;
   auxclickFired = false;
-  document.addEventListener("click", (event) => { event.preventDefault(); }, {once: true, capture: true});
+  document.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true, capture: true});
   synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  ok(auxclickFired, "'auxclickFired' fired");
   is(pasteEventCount, 0,
-     "If 'click' event is consumed at capturing phase at the document node, 'paste' event should be not be fired");
+     "If 'auxclick' event is consumed at capturing phase at the document node, 'paste' event should not be fired");
 
   pasteEventCount = 0;
   auxclickFired = false;
-  noneditableDiv.addEventListener("click", (event) => { event.preventDefault(); }, {once: true});
+  noneditableDiv.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
   synthesizeMouseAtCenter(noneditableDiv, {button: 1});
-  is(pasteEventCount, 1,
-     "Even if 'click' event listener is added to the click event target, 'paste' event should be fired");
+  ok(auxclickFired, "'auxclick' fired");
+  is(pasteEventCount, 0,
+     "If 'auxclick' event listener is added to the click event target, 'paste' event should not be fired");
 
   pasteEventCount = 0;
   auxclickFired = false;
   document.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
   synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  ok(auxclickFired, "'auxclick' fired");
   is(pasteEventCount, 0,
      "If 'auxclick' event is consumed, 'paste' event should be not be fired");
 
   document.removeEventListener("auxclick", onAuxClick);
   document.removeEventListener("paste", onPaste);
   contenteditableContainer.innerHTML = "";
 });
 
--- a/dom/tests/mochitest/general/test_domWindowUtils.html
+++ b/dom/tests/mochitest/general/test_domWindowUtils.html
@@ -5,17 +5,17 @@
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
   <style>
     html, body, div {
       padding: 0;
       margin: 0;
     }
-    
+
     div.test {
       position: absolute;
       height: 10px;
       width: 10px;
     }
   </style>
 </head>
 
@@ -72,17 +72,17 @@ async function testElementFromPoint() {
     [[0, 100000, true, false], [[0, 100000], offscreen], htmldoc],
     [[0, 100000, true, true], [[0, 100000], offscreen], offscreen],
   ];
 
   for (let i = 0; i < testData.length; ++i) {
     let [x, y, ignoreScroll, flushLayout] = testData[i][0];
     let moveData = testData[i][1];
     let expected = testData[i][2];
-    
+
     if (moveData) {
       let moveEl = moveData[1];
       let [moveX, moveY] = moveData[0];
 
       moveEl.style.left = moveX + "px";
       moveEl.style.top = moveY + "px";
     }
     let found = SpecialPowers.unwrap(domWindowUtils.elementFromPoint(
@@ -102,16 +102,21 @@ async function testHandlingUserInput() {
      "isHandlingUserInput should return false if nothing is happening");
 
   var data = [
     {
       eventName: "click",
       result: true,
     },
     {
+      eventName: "auxclick",
+      button: 1,
+      result: true,
+    },
+    {
       eventName: "mousemove",
       result: false,
     },
     {
       eventName: "mouseup",
       result: true,
     },
     {
@@ -123,33 +128,35 @@ async function testHandlingUserInput() {
       result: true,
     },
     {
       eventName: "keyup",
       result: true,
     },
   ];
 
-  for (let i=0; i<data.length; ++i) {
+  for (const {eventName, result, button} of data) {
     let eventPromise = new Promise(resolve => {
-      document.addEventListener(data[i].eventName, function() {
-        is(domWindowUtils.isHandlingUserInput, data[i].result,
-           "isHandlingUserInput should be " + data[i].result);
+      document.addEventListener(eventName, function() {
+        is(domWindowUtils.isHandlingUserInput, result,
+           `isHandlingUserInput should be ${result} for ${eventName}`);
 
         SimpleTest.executeSoon(resolve);
       }, {once: true});
     });
 
     SimpleTest.executeSoon(function() {
-      if (data[i].eventName == "click") {
+      if (eventName == "click") {
         synthesizeMouseAtCenter(document.body, {});
-      } else if (data[i].eventName.indexOf("key") == 0) {
-        synthesizeKey("VK_A", { type: data[i].eventName });
+      } else if (eventName == "auxclick" && button) {
+        synthesizeMouseAtCenter(document.body, { button });
+      } else if (eventName.startsWith("key")) {
+        synthesizeKey("VK_A", { type: eventName });
       } else {
-        synthesizeMouseAtCenter(document.body, { type: data[i].eventName });
+        synthesizeMouseAtCenter(document.body, { type: eventName });
       }
     });
 
     await eventPromise;
   }
 },
 ];
 
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -2,16 +2,22 @@ var scrollbarWidth = 17, scrollbarHeight
 
 function testElements(baseid, callback)
 {
   scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width");
 
   var elements = $(baseid).getElementsByTagName("*");
   for (var t = 0; t < elements.length; t++) {
     var element = elements[t];
+
+    // Ignore presentational content inside menus
+    if (element.closest("menu") && element.closest("[aria-hidden=true]")) {
+      continue;
+    }
+
     testElement(element);
   }
 
   var nonappended = document.createElement("div");
   nonappended.id = "nonappended";
   nonappended.setAttribute("_offsetParent", "null");
   testElement(nonappended);
 
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -113,17 +113,17 @@ interface Document : Node {
   Attr createAttributeNS(DOMString? namespace, DOMString name);
 };
 
 // https://html.spec.whatwg.org/multipage/dom.html#the-document-object
 partial interface Document {
   [PutForwards=href, Unforgeable] readonly attribute Location? location;
   //(HTML only)         attribute DOMString domain;
   readonly attribute DOMString referrer;
-  //(HTML only)         attribute DOMString cookie;
+  [Throws] attribute DOMString cookie;
   readonly attribute DOMString lastModified;
   readonly attribute DOMString readyState;
 
   // DOM tree accessors
   //(Not proxy yet)getter object (DOMString name);
   [CEReactions, SetterThrows, Pure]
            attribute DOMString title;
   [CEReactions, Pure]
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -3,18 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [OverrideBuiltins]
 interface HTMLDocument : Document {
            [SetterThrows]
            attribute DOMString domain;
-           [Throws]
-           attribute DOMString cookie;
   // DOM tree accessors
   [Throws]
   getter object (DOMString name);
 
   // dynamic markup insertion
   [CEReactions, Throws]
   Document open(optional DOMString type, optional DOMString replace = ""); // type is ignored
   [CEReactions, Throws]
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1978,20 +1978,17 @@ XMLHttpRequestMainThread::OnStartRequest
       mResponseXML->ForceEnableXULXBL();
     }
 
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
     bool isCrossSite = false;
     isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
 
     if (isCrossSite) {
-      nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
-      if (htmlDoc) {
-        htmlDoc->DisableCookieAccess();
-      }
+      mResponseXML->DisableCookieAccess();
     }
 
     nsCOMPtr<nsIStreamListener> listener;
     nsCOMPtr<nsILoadGroup> loadGroup;
     channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
     // suppress <parsererror> nodes on XML document parse failure, but only
     // for non-privileged code (including Web Extensions). See bug 289714.
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -661,31 +661,34 @@ class OpenRunnable final : public Worker
   Optional<nsAString> mUser;
   nsString mUserStr;
   Optional<nsAString> mPassword;
   nsString mPasswordStr;
   bool mBackgroundRequest;
   bool mWithCredentials;
   uint32_t mTimeout;
   XMLHttpRequestResponseType mResponseType;
+  const nsString mMimeTypeOverride;
 
  public:
   OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsACString& aMethod, const nsAString& aURL,
                const Optional<nsAString>& aUser,
                const Optional<nsAString>& aPassword, bool aBackgroundRequest,
                bool aWithCredentials, uint32_t aTimeout,
-               XMLHttpRequestResponseType aResponseType)
+               XMLHttpRequestResponseType aResponseType,
+               const nsString& aMimeTypeOverride)
       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
         mMethod(aMethod),
         mURL(aURL),
         mBackgroundRequest(aBackgroundRequest),
         mWithCredentials(aWithCredentials),
         mTimeout(aTimeout),
-        mResponseType(aResponseType) {
+        mResponseType(aResponseType),
+        mMimeTypeOverride(aMimeTypeOverride) {
     if (aUser.WasPassed()) {
       mUserStr = aUser.Value();
       mUser = &mUserStr;
     }
     if (aPassword.WasPassed()) {
       mPasswordStr = aPassword.Value();
       mPassword = &mPasswordStr;
     }
@@ -1284,16 +1287,23 @@ nsresult OpenRunnable::MainThreadRunInte
 
   if (mTimeout) {
     mProxy->mXHR->SetTimeout(mTimeout, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
   }
 
+  if (!mMimeTypeOverride.IsVoid()) {
+    mProxy->mXHR->OverrideMimeType(mMimeTypeOverride, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+  }
+
   MOZ_ASSERT(!mProxy->mInOpen);
   mProxy->mInOpen = true;
 
   mProxy->mXHR->Open(
       mMethod, mURL, true, mUser.WasPassed() ? mUser.Value() : VoidString(),
       mPassword.WasPassed() ? mPassword.Value() : VoidString(), rv);
 
   MOZ_ASSERT(mProxy->mInOpen);
@@ -1419,17 +1429,18 @@ void SendRunnable::RunOnMainThread(Error
 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate),
       mResponseType(XMLHttpRequestResponseType::_empty),
       mTimeout(0),
       mBackgroundRequest(false),
       mWithCredentials(false),
       mCanceled(false),
       mMozAnon(false),
-      mMozSystem(false) {
+      mMozSystem(false),
+      mMimeTypeOverride(VoidString()) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   mozilla::HoldJSObjects(this);
 }
 
 XMLHttpRequestWorker::~XMLHttpRequestWorker() {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
@@ -1742,37 +1753,39 @@ void XMLHttpRequestWorker::Open(const ns
                                 ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
     return;
   }
 
+  bool alsoOverrideMimeType = false;
   if (mProxy) {
     MaybeDispatchPrematureAbortEvents(aRv);
     if (aRv.Failed()) {
       return;
     }
   } else {
     Maybe<ClientInfo> clientInfo(mWorkerPrivate->GetClientInfo());
     if (clientInfo.isNothing()) {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
     mProxy = new Proxy(this, clientInfo.ref(), mWorkerPrivate->GetController(),
                        mMozAnon, mMozSystem);
+    alsoOverrideMimeType = true;
   }
 
   mProxy->mOuterEventStreamId++;
 
   RefPtr<OpenRunnable> runnable = new OpenRunnable(
       mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
-      mBackgroundRequest, mWithCredentials, mTimeout, mResponseType);
-
+      mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
+      alsoOverrideMimeType ? mMimeTypeOverride : VoidString());
   ++mProxy->mOpenCount;
   runnable->Dispatch(Canceling, aRv);
   if (aRv.Failed()) {
     if (mProxy && !--mProxy->mOpenCount) {
       ReleaseProxy();
     }
 
     return;
@@ -2120,30 +2133,30 @@ void XMLHttpRequestWorker::OverrideMimeT
                                             ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
     return;
   }
 
-  // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
-  // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
-  // non-racy way until the XHR state machine actually runs on this thread
-  // (bug 671047). For now we're going to let this work only if the Send()
-  // method has not been called, unless the send has been aborted.
-  if (!mProxy || (SendInProgress() &&
-                  (mProxy->mSeenLoadStart || mStateData.mReadyState > 1))) {
+  // We're supposed to throw if the state is LOADING or DONE.
+  if (mStateData.mReadyState == XMLHttpRequest_Binding::LOADING ||
+      mStateData.mReadyState == XMLHttpRequest_Binding::DONE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  RefPtr<OverrideMimeTypeRunnable> runnable =
-      new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
-  runnable->Dispatch(Canceling, aRv);
+  mMimeTypeOverride = aMimeType;
+
+  if (mProxy) {
+    RefPtr<OverrideMimeTypeRunnable> runnable =
+        new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
+    runnable->Dispatch(Canceling, aRv);
+  }
 }
 
 void XMLHttpRequestWorker::SetResponseType(
     XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -58,16 +58,18 @@ class XMLHttpRequestWorker final : publi
 
   bool mBackgroundRequest;
   bool mWithCredentials;
   bool mCanceled;
 
   bool mMozAnon;
   bool mMozSystem;
 
+  nsString mMimeTypeOverride;
+
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(XMLHttpRequestWorker,
                                                          XMLHttpRequest)
 
   static already_AddRefed<XMLHttpRequest> Construct(
       const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
       ErrorResult& aRv);
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -165,16 +165,18 @@ nsresult EditorEventListener::InstallToE
   //     However, if we do so, all click handlers in any frames and frontend
   //     code need to check if it's editable.  It makes easier create new bugs.
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("mousedown"),
                                TrustedEventsAtCapture());
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("mouseup"),
                                TrustedEventsAtCapture());
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("click"),
                                TrustedEventsAtCapture());
+  elmP->AddEventListenerByType(this, NS_LITERAL_STRING("auxclick"),
+                               TrustedEventsAtSystemGroupCapture());
   // Focus event doesn't bubble so adding the listener to capturing phase as
   // system event group.
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("blur"),
                                TrustedEventsAtSystemGroupCapture());
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("focus"),
                                TrustedEventsAtSystemGroupCapture());
   elmP->AddEventListenerByType(this, NS_LITERAL_STRING("text"),
                                TrustedEventsAtSystemGroupBubble());
@@ -237,16 +239,18 @@ void EditorEventListener::UninstallFromE
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("drop"),
                                   TrustedEventsAtSystemGroupBubble());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("mousedown"),
                                   TrustedEventsAtCapture());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("mouseup"),
                                   TrustedEventsAtCapture());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("click"),
                                   TrustedEventsAtCapture());
+  elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("auxclick"),
+                                  TrustedEventsAtSystemGroupCapture());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("blur"),
                                   TrustedEventsAtSystemGroupCapture());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("focus"),
                                   TrustedEventsAtSystemGroupCapture());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("text"),
                                   TrustedEventsAtSystemGroupBubble());
   elmP->RemoveEventListenerByType(this, NS_LITERAL_STRING("compositionstart"),
                                   TrustedEventsAtSystemGroupBubble());
@@ -393,16 +397,25 @@ EditorEventListener::HandleEvent(Event* 
         return NS_OK;
       }
       RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseUp(mouseEvent);
     }
     // click
     case eMouseClick: {
       WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
+      // Don't handle non-primary click events
+      if (widgetMouseEvent->button != WidgetMouseEventBase::eLeftButton) {
+        return NS_OK;
+      }
+      MOZ_FALLTHROUGH;
+    }
+    // auxclick
+    case eMouseAuxClick: {
+      WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
       if (NS_WARN_IF(!widgetMouseEvent)) {
         return NS_OK;
       }
       // If the preceding mousedown event or mouseup event was consumed,
       // editor shouldn't handle this click event.
       if (mMouseDownOrUpConsumedByIME) {
         mMouseDownOrUpConsumedByIME = false;
         widgetMouseEvent->PreventDefault();
@@ -645,19 +658,19 @@ nsresult EditorEventListener::MouseClick
   MOZ_ASSERT(!aMouseClickEvent->DefaultPrevented());
   nsEventStatus status = nsEventStatus_eIgnore;
   RefPtr<EventStateManager> esm = presContext->EventStateManager();
   DebugOnly<nsresult> rv = esm->HandleMiddleClickPaste(
       presShell, aMouseClickEvent, &status, textEditor);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "Failed to paste for the middle button click");
   if (status == nsEventStatus_eConsumeNoDefault) {
-    // Prevent the event from propagating up to be possibly handled
-    // again by the containing window:
-    aMouseClickEvent->StopImmediatePropagation();
+    // We no longer need to StopImmediatePropagation here since
+    // ClickHandlerChild.jsm checks for and ignores editables, so won't
+    // re-handle the event
     aMouseClickEvent->PreventDefault();
   }
   return NS_OK;
 }
 
 bool EditorEventListener::NotifyIMEOfMouseButtonEvent(
     WidgetMouseEvent* aMouseEvent) {
   MOZ_ASSERT(aMouseEvent);
--- a/editor/libeditor/tests/test_bug674770-1.html
+++ b/editor/libeditor/tests/test_bug674770-1.html
@@ -50,17 +50,17 @@ function startTests() {
 
   addEventListener("storage", function(e) {
     is(e.key, "clicked", currentTest.description + "Key should always be 'clicked'");
     is(e.newValue, "true", currentTest.description + "Value should always be 'true'");
     ok(currentTest.linkShouldWork, currentTest.description + "The click operation on the link " + (currentTest.linkShouldWork ? "should work" : "shouldn't work"));
     SimpleTest.executeSoon(runNextTest);
   }, false);
 
-  SpecialPowers.addSystemEventListener(window, "click", function(aEvent) {
+  SpecialPowers.addSystemEventListener(window, "auxclick", function(aEvent) {
     // When the click event should cause default action, e.g., opening the link,
     // the event shouldn't have been consumed except the link handler.
     // However, in e10s mode, it's not consumed during propagating the event but
     // in non-e10s mode, it's consumed during the propagation.  Therefore,
     // we cannot check defaultPrevented value when the link should work as is
     // if there is no way to detect if it's running in e10s mode or not.
     // So, let's skip checking Event.defaultPrevented value when the link should
     // work.  In such case, we should receive "storage" event later.
--- a/editor/libeditor/tests/test_middle_click_paste.html
+++ b/editor/libeditor/tests/test_middle_click_paste.html
@@ -159,22 +159,22 @@ async function doTextareaTests(aTextarea
   }
   aTextarea.addEventListener("paste", pasteEventLogger);
 
   await copyPlaintext("abc");
   aTextarea.focus();
   document.body.addEventListener("click", (event) => { event.preventDefault(); }, {capture: true, once: true});
   inputEvents = [];
   synthesizeMouseAtCenter(aTextarea, {button: 1});
-  is(aTextarea.value, "",
-     "If 'click' event is consumed at capturing phase of the <body>, paste should be canceled");
-  is(pasteEventCount, 0,
-     "If 'click' event is consumed at capturing phase of the <body>, 'paste' event should not be fired");
-  is(inputEvents.length, 0,
-     'No "input" event should be fired when the "click" event is canceled');
+  is(aTextarea.value, "abc",
+     "If 'click' event is consumed at capturing phase of the <body>, paste should not be canceled");
+  is(pasteEventCount, 1,
+     "If 'click' event is consumed at capturing phase of the <body>, 'paste' event should still be fired");
+  is(inputEvents.length, 1,
+     '"input" event should be fired when the "click" event is canceled');
   aTextarea.value = "";
 
   await copyPlaintext("abc");
   aTextarea.focus();
   aTextarea.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
   pasteEventCount = 0;
   inputEvents = [];
   synthesizeMouseAtCenter(aTextarea, {button: 1});
@@ -189,36 +189,36 @@ async function doTextareaTests(aTextarea
 
   await copyPlaintext("abc");
   aTextarea.focus();
   aTextarea.addEventListener("click", (event) => { event.preventDefault(); }, {once: true});
   pasteEventCount = 0;
   inputEvents = [];
   synthesizeMouseAtCenter(aTextarea, {button: 1});
   is(aTextarea.value, "abc",
-     "Even if 'click' event handler is added to the <textarea>, paste should not be canceled");
+     "If 'click' event handler is added to the <textarea>, paste should not be canceled");
   is(pasteEventCount, 1,
-     "Even if 'click' event handler is added to the <textarea>, 'paste' event should be fired once");
+     "If 'click' event handler is added to the <textarea>, 'paste' event should be fired once");
   is(inputEvents.length, 1,
      'One "input" event should be fired even if "click" event is canceled in bubbling phase');
   checkInputEvent(inputEvents[0], "insertFromPaste", "abc", null, 'even if "click" event is canceled in bubbling phase');
   aTextarea.value = "";
 
   await copyPlaintext("abc");
   aTextarea.focus();
   aTextarea.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
   pasteEventCount = 0;
   inputEvents = [];
   synthesizeMouseAtCenter(aTextarea, {button: 1});
-  todo_is(aTextarea.value, "",
-          "If 'auxclick' event is consumed, paste should be canceled");
-  todo_is(pasteEventCount, 0,
-          "If 'auxclick' event is consumed, 'paste' event should not be fired once");
-  todo_is(inputEvents.length, 0,
-          'No "input" event should be fired if "auxclick" event is canceled');
+  is(aTextarea.value, "",
+     "If 'auxclick' event is consumed, paste should be canceled");
+  is(pasteEventCount, 0,
+     "If 'auxclick' event is consumed, 'paste' event should not be fired once");
+  is(inputEvents.length, 0,
+     'No "input" event should be fired if "auxclick" event is canceled');
   aTextarea.value = "";
 
   aTextarea.removeEventListener("paste", pasteEventLogger);
   aTextarea.removeEventListener("input", onInput);
 }
 
 async function doContenteditableTests(aEditableDiv) {
   let inputEvents = [];
@@ -246,22 +246,22 @@ async function doContenteditableTests(aE
   }
   aEditableDiv.addEventListener("paste", pasteEventLogger);
 
   await copyPlaintext("abc");
   aEditableDiv.focus();
   window.addEventListener("click", (event) => { event.preventDefault(); }, {capture: true, once: true});
   inputEvents = [];
   synthesizeMouseAtCenter(aEditableDiv, {button: 1});
-  is(aEditableDiv.innerHTML, "",
-     "If 'click' event is consumed at capturing phase of the window, paste should be canceled");
-  is(pasteEventCount, 0,
+  is(aEditableDiv.innerHTML, "abc",
+     "If 'click' event is consumed at capturing phase of the window, paste should not be canceled");
+  is(pasteEventCount, 1,
      "If 'click' event is consumed at capturing phase of the window, 'paste' event should be fired once");
-  is(inputEvents.length, 0,
-     'No "input" event should be fired when the "click" event is canceled (contenteditable)');
+  is(inputEvents.length, 1,
+     '"input" event should still be fired when the "click" event is canceled (contenteditable)');
   aEditableDiv.innerHTML = "";
 
   await copyPlaintext("abc");
   aEditableDiv.focus();
   aEditableDiv.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
   pasteEventCount = 0;
   inputEvents = [];
   synthesizeMouseAtCenter(aEditableDiv, {button: 1});
@@ -292,22 +292,22 @@ async function doContenteditableTests(aE
   aEditableDiv.innerHTML = "";
 
   await copyPlaintext("abc");
   aEditableDiv.focus();
   aEditableDiv.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
   pasteEventCount = 0;
   inputEvents = [];
   synthesizeMouseAtCenter(aEditableDiv, {button: 1});
-  todo_is(aEditableDiv.innerHTML, "",
-          "If 'auxclick' event is consumed, paste should be canceled");
-  todo_is(pasteEventCount, 0,
-          "If 'auxclick' event is consumed, 'paste' event should not be fired");
-  todo_is(inputEvents.length, 0,
-          'No "input" event should be fired if "auxclick" event is canceled (contenteditable)');
+  is(aEditableDiv.innerHTML, "",
+     "If 'auxclick' event is consumed, paste should be canceled");
+  is(pasteEventCount, 0,
+     "If 'auxclick' event is consumed, 'paste' event should not be fired");
+  is(inputEvents.length, 0,
+     'No "input" event should be fired if "auxclick" event is canceled (contenteditable)');
   aEditableDiv.innerHTML = "";
 
   // If clipboard event is disabled, InputEvent.dataTransfer should have only empty string.
   await SpecialPowers.pushPrefEnv({"set": [["dom.event.clipboardevents.enabled", false]]});
   await copyPlaintext("abc");
   aEditableDiv.focus();
   pasteEventCount = 0;
   inputEvents = [];
@@ -378,18 +378,18 @@ async function doNestedEditorTests(aEdit
      "Clicking in <textarea> in an editable <div> should paste the clipboard text into the <textarea>");
   is(aEditableDiv.innerHTML, '<p id="p">foo</p><textarea id="textarea"></textarea>',
      "Pasting in the <textarea> shouldn't be handled by the HTMLEditor");
 
   textarea.value = "";
   textarea.readOnly = true;
   pasteTarget = null;
   synthesizeMouseAtCenter(textarea, {button: 1});
-  todo_is(pasteTarget, textarea,
-          "Target of 'paste' event should be the clicked <textarea> even if it's read-only");
+  is(pasteTarget, textarea,
+     "Target of 'paste' event should be the clicked <textarea> even if it's read-only");
   is(textarea.value, "",
      "Clicking in read-only <textarea> in an editable <div> should not paste the clipboard text into the read-only <textarea>");
   // HTMLEditor thinks that read-only <textarea> is not modifiable.
   // Therefore, HTMLEditor does not paste the text.
   is(aEditableDiv.innerHTML, '<p id="p">foo</p><textarea id="textarea" readonly=""></textarea>',
      "Clicking in read-only <textarea> shouldn't cause pasting the clipboard text into its parent HTMLEditor");
 
   textarea.value = "";
@@ -420,18 +420,18 @@ async function doAfterRemoveOfClickedEle
   let pasteTarget = null;
   document.addEventListener("paste", (aEvent) => { pasteTarget = aEvent.target; }, {once: true});
   document.addEventListener("auxclick", (aEvent) => {
     is(aEvent.target.getAttribute("id"), "span",
        "Target of auxclick event should be the <span> element");
     span.parentElement.removeChild(span);
   }, {once: true});
   synthesizeMouseAtCenter(span, {button: 1});
-  todo_is(pasteTarget.getAttribute("id"), "p",
-          "Target of 'paste' event should be the <p> element since <span> has gone");
+  is(pasteTarget.getAttribute("id"), "p",
+     "Target of 'paste' event should be the <p> element since <span> has gone");
   // XXX Currently, pasted to start of the <p> because EventStateManager
   //     do not recompute event target frame.
   todo_is(aEditableDiv.innerHTML, '<p id="p">fooCLIPBOARD TEXT</p>',
           "Clipbpard text should looks like replacing the <span> element");
   aEditableDiv.innerHTML = "";
 }
 
 async function doTests() {
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -11,22 +11,23 @@
 
 // nsPermission Implementation
 
 NS_IMPL_CLASSINFO(nsPermission, nullptr, 0, {0})
 NS_IMPL_ISUPPORTS_CI(nsPermission, nsIPermission)
 
 nsPermission::nsPermission(nsIPrincipal* aPrincipal, const nsACString& aType,
                            uint32_t aCapability, uint32_t aExpireType,
-                           int64_t aExpireTime)
+                           int64_t aExpireTime, int64_t aModificationTime)
     : mPrincipal(aPrincipal),
       mType(aType),
       mCapability(aCapability),
       mExpireType(aExpireType),
-      mExpireTime(aExpireTime) {}
+      mExpireTime(aExpireTime),
+      mModificationTime(aModificationTime) {}
 
 already_AddRefed<nsIPrincipal> nsPermission::ClonePrincipalForPermission(
     nsIPrincipal* aPrincipal) {
   MOZ_ASSERT(aPrincipal);
 
   mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef();
   attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
                         mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
@@ -37,29 +38,28 @@ already_AddRefed<nsIPrincipal> nsPermiss
 
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 }
 
-already_AddRefed<nsPermission> nsPermission::Create(nsIPrincipal* aPrincipal,
-                                                    const nsACString& aType,
-                                                    uint32_t aCapability,
-                                                    uint32_t aExpireType,
-                                                    int64_t aExpireTime) {
+already_AddRefed<nsPermission> nsPermission::Create(
+    nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aCapability,
+    uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime) {
   NS_ENSURE_TRUE(aPrincipal, nullptr);
 
   nsCOMPtr<nsIPrincipal> principal =
       nsPermission::ClonePrincipalForPermission(aPrincipal);
   NS_ENSURE_TRUE(principal, nullptr);
 
   RefPtr<nsPermission> permission =
-      new nsPermission(principal, aType, aCapability, aExpireType, aExpireTime);
+      new nsPermission(principal, aType, aCapability, aExpireType, aExpireTime,
+                       aModificationTime);
   return permission.forget();
 }
 
 NS_IMETHODIMP
 nsPermission::GetPrincipal(nsIPrincipal** aPrincipal) {
   nsCOMPtr<nsIPrincipal> copy = mPrincipal;
   copy.forget(aPrincipal);
   return NS_OK;
@@ -85,16 +85,22 @@ nsPermission::GetExpireType(uint32_t* aE
 
 NS_IMETHODIMP
 nsPermission::GetExpireTime(int64_t* aExpireTime) {
   *aExpireTime = mExpireTime;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPermission::GetModificationTime(int64_t* aModificationTime) {
+  *aModificationTime = mModificationTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost,
                       bool* aMatches) {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aMatches);
 
   *aMatches = false;
 
   nsCOMPtr<nsIPrincipal> principal =
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -12,34 +12,34 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsPermission : public nsIPermission {
  public:
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSION
 
-  static already_AddRefed<nsPermission> Create(nsIPrincipal* aPrincipal,
-                                               const nsACString& aType,
-                                               uint32_t aCapability,
-                                               uint32_t aExpireType,
-                                               int64_t aExpireTime);
+  static already_AddRefed<nsPermission> Create(
+      nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aCapability,
+      uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime);
 
   // This method creates a new nsIPrincipal with a stripped OriginAttributes (no
   // userContextId, and no FirstPartyDomain) and a codebase equal to the origin
   // of 'aPrincipal'.
   static already_AddRefed<nsIPrincipal> ClonePrincipalForPermission(
       nsIPrincipal* aPrincipal);
 
  protected:
   nsPermission(nsIPrincipal* aPrincipal, const nsACString& aType,
-               uint32_t aCapability, uint32_t aExpireType, int64_t aExpireTime);
+               uint32_t aCapability, uint32_t aExpireType, int64_t aExpireTime,
+               int64_t aModificationTime);
 
   virtual ~nsPermission(){};
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mType;
   uint32_t mCapability;
   uint32_t mExpireType;
   int64_t mExpireTime;
+  int64_t mModificationTime;
 };
 
 #endif  // nsPermission_h__
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1870,17 +1870,17 @@ nsresult nsPermissionManager::AddInterna
       if (aDBOperation == eWriteToDB && IsPersistentExpire(aExpireType)) {
         UpdateDB(op, mStmtInsert, id, origin, aType, aPermission, aExpireType,
                  aExpireTime, aModificationTime);
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
                                       aPermission, aExpireType, aExpireTime,
-                                      u"added");
+                                      aModificationTime, u"added");
       }
 
       break;
     }
 
     case eOperationRemoving: {
       PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
       id = oldPermissionEntry.mID;
@@ -1908,17 +1908,17 @@ nsresult nsPermissionManager::AddInterna
         // parameters.
         UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
                  nsIPermissionManager::EXPIRE_NEVER, 0, 0);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(
             aPrincipal, mTypeArray[typeIndex], oldPermissionEntry.mPermission,
             oldPermissionEntry.mExpireType, oldPermissionEntry.mExpireTime,
-            u"deleted");
+            oldPermissionEntry.mModificationTime, u"deleted");
       }
 
       // If there are no more permissions stored for that entry, clear it.
       if (entry->GetPermissions().IsEmpty()) {
         mPermissionTable.RemoveEntry(entry);
       }
 
       break;
@@ -1965,17 +1965,17 @@ nsresult nsPermissionManager::AddInterna
         // expireType/expireTime/modificationTime here. We pass dummy values for
         // all other parameters.
         UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
                  aPermission, aExpireType, aExpireTime, aModificationTime);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
                                       aPermission, aExpireType, aExpireTime,
-                                      u"changed");
+                                      aModificationTime, u"changed");
       }
 
       break;
     }
     case eOperationReplacingDefault: {
       // this is handling the case when we have an existing permission
       // entry that was created as a "default" (and thus isn't in the DB) with
       // an explicit permission (that may include UNKNOWN_ACTION.)
@@ -2014,17 +2014,17 @@ nsresult nsPermissionManager::AddInterna
       if (aDBOperation == eWriteToDB && IsPersistentExpire(aExpireType)) {
         UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission,
                  aExpireType, aExpireTime, aModificationTime);
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex],
                                       aPermission, aExpireType, aExpireTime,
-                                      u"changed");
+                                      aModificationTime, u"changed");
       }
 
     } break;
   }
 
   return NS_OK;
 }
 
@@ -2357,19 +2357,19 @@ nsPermissionManager::GetPermissionObject
   }
 
   nsCOMPtr<nsIPrincipal> principal;
   nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
                                        getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PermissionEntry& perm = entry->GetPermissions()[idx];
-  nsCOMPtr<nsIPermission> r =
-      nsPermission::Create(principal, mTypeArray[perm.mType], perm.mPermission,
-                           perm.mExpireType, perm.mExpireTime);
+  nsCOMPtr<nsIPermission> r = nsPermission::Create(
+      principal, mTypeArray[perm.mType], perm.mPermission, perm.mExpireType,
+      perm.mExpireTime, perm.mModificationTime);
   if (NS_WARN_IF(!r)) {
     return NS_ERROR_FAILURE;
   }
   r.forget(aResult);
   return NS_OK;
 }
 
 nsresult nsPermissionManager::CommonTestPermissionInternal(
@@ -2604,17 +2604,18 @@ NS_IMETHODIMP nsPermissionManager::GetAl
       nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
                                            getter_AddRefs(principal));
       if (NS_FAILED(rv)) {
         continue;
       }
 
       RefPtr<nsIPermission> permission = nsPermission::Create(
           principal, mTypeArray[permEntry.mType], permEntry.mPermission,
-          permEntry.mExpireType, permEntry.mExpireTime);
+          permEntry.mExpireType, permEntry.mExpireTime,
+          permEntry.mModificationTime);
       if (NS_WARN_IF(!permission)) {
         continue;
       }
       aResult.AppendElement(std::move(permission));
     }
   }
 
   return NS_OK;
@@ -2650,17 +2651,18 @@ nsPermissionManager::GetAllForPrincipal(
     for (const auto& permEntry : entry->GetPermissions()) {
       // Only return custom permissions
       if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
         continue;
       }
 
       nsCOMPtr<nsIPermission> permission = nsPermission::Create(
           aPrincipal, mTypeArray[permEntry.mType], permEntry.mPermission,
-          permEntry.mExpireType, permEntry.mExpireTime);
+          permEntry.mExpireType, permEntry.mExpireTime,
+          permEntry.mModificationTime);
       if (NS_WARN_IF(!permission)) {
         continue;
       }
       array.AppendObject(permission);
     }
   }
 
   return NS_NewArrayEnumerator(aEnum, array, NS_GET_IID(nsIPermission));
@@ -2764,19 +2766,21 @@ nsresult nsPermissionManager::RemoveAllF
 
   return NS_OK;
 }
 
 // wrapper function for mangling (host,type,perm,expireType,expireTime)
 // set into an nsIPermission.
 void nsPermissionManager::NotifyObserversWithPermission(
     nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission,
-    uint32_t aExpireType, int64_t aExpireTime, const char16_t* aData) {
-  nsCOMPtr<nsIPermission> permission = nsPermission::Create(
-      aPrincipal, aType, aPermission, aExpireType, aExpireTime);
+    uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime,
+    const char16_t* aData) {
+  nsCOMPtr<nsIPermission> permission =
+      nsPermission::Create(aPrincipal, aType, aPermission, aExpireType,
+                           aExpireTime, aModificationTime);
   if (permission) NotifyObservers(permission, aData);
 }
 
 // notify observers that the permission list changed. there are four possible
 // values for aData:
 // "deleted" means a permission was deleted. aPermission is the deleted
 // permission. "added"   means a permission was added. aPermission is the added
 // permission. "changed" means a permission was altered. aPermission is the new
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -468,16 +468,17 @@ class nsPermissionManager final : public
   nsresult Import();
   nsresult ImportDefaults();
   nsresult _DoImport(nsIInputStream* inputStream, mozIStorageConnection* aConn);
   nsresult Read();
   void NotifyObserversWithPermission(nsIPrincipal* aPrincipal,
                                      const nsACString& aType,
                                      uint32_t aPermission, uint32_t aExpireType,
                                      int64_t aExpireTime,
+                                     int64_t aModificationTime,
                                      const char16_t* aData);
   void NotifyObservers(nsIPermission* aPermission, const char16_t* aData);
 
   // Finalize all statements, close the DB and null it.
   // if aRebuildOnSuccess, reinitialize database
   void CloseDB(bool aRebuildOnSuccess = false);
 
   nsresult RemoveAllInternal(bool aNotifyObservers);
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -470,17 +470,18 @@ bool ScaledFontDWrite::GetFontInstanceDa
   return true;
 }
 
 bool ScaledFontDWrite::GetWRFontInstanceOptions(
     Maybe<wr::FontInstanceOptions>* aOutOptions,
     Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
     std::vector<FontVariation>* aOutVariations) {
   wr::FontInstanceOptions options;
-  options.render_mode = wr::ToFontRenderMode(GetDefaultAAMode());
+  bool useSubpixel = !mParams || mParams->GetClearTypeLevel() != 0.0f;
+  options.render_mode = wr::ToFontRenderMode(GetDefaultAAMode(), useSubpixel);
   options.flags = wr::FontInstanceFlags{0};
   if (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) {
     options.flags |= wr::FontInstanceFlags_SYNTHETIC_BOLD;
   }
   if (UseEmbeddedBitmaps()) {
     options.flags |= wr::FontInstanceFlags_EMBEDDED_BITMAPS;
   }
   if (ForceGDIMode()) {
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -797,19 +797,23 @@ Maybe<wr::WrSpatialId> DisplayListBuilde
 }
 
 void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
   WRDL_LOG("PopStackingContext\n", mWrState);
   wr_dp_pop_stacking_context(mWrState, aIsReferenceFrame);
 }
 
 wr::WrClipChainId DisplayListBuilder::DefineClipChain(
-    const nsTArray<wr::WrClipId>& aClips) {
+    const nsTArray<wr::WrClipId>& aClips, const wr::WrClipChainId* aParent) {
+  const uint64_t* parent = nullptr;
+  if (aParent && aParent->id != wr::ROOT_CLIP_CHAIN) {
+    parent = &aParent->id;
+  }
   uint64_t clipchainId = wr_dp_define_clipchain(
-      mWrState, nullptr, aClips.Elements(), aClips.Length());
+      mWrState, parent, aClips.Elements(), aClips.Length());
   WRDL_LOG("DefineClipChain id=%" PRIu64 " clips=%zu\n", mWrState, clipchainId,
            aClips.Length());
   return wr::WrClipChainId{clipchainId};
 }
 
 wr::WrClipId DisplayListBuilder::DefineClip(
     const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
     const nsTArray<wr::ComplexClipRegion>* aComplex,
@@ -1146,19 +1150,19 @@ void DisplayListBuilder::PopAllShadows()
 void DisplayListBuilder::SuspendClipLeafMerging() {
   if (mClipChainLeaf) {
     // No one should reinitialize mClipChainLeaf while we're suspended
     MOZ_ASSERT(!mSuspendedClipChainLeaf);
 
     mSuspendedClipChainLeaf = mClipChainLeaf;
     mSuspendedSpaceAndClipChain = Some(mCurrentSpaceAndClipChain);
 
-    // Clip is implicitly parented by mCurrentSpaceAndClipChain
+    wr::WrClipChainId currentClipChainId{mCurrentSpaceAndClipChain.clip_chain};
     auto clipId = DefineClip(Nothing(), *mClipChainLeaf);
-    auto clipChainId = DefineClipChain({ clipId });
+    auto clipChainId = DefineClipChain({clipId}, &currentClipChainId);
 
     mCurrentSpaceAndClipChain.clip_chain = clipChainId.id;
     mClipChainLeaf = Nothing();
   }
 }
 
 void DisplayListBuilder::ResumeClipLeafMerging() {
   if (mSuspendedClipChainLeaf) {
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -387,17 +387,18 @@ class DisplayListBuilder final {
     mSubBuilders[aRenderRoot]->mSendSubBuilderDisplayList = true;
   }
 
   Maybe<wr::WrSpatialId> PushStackingContext(
       const StackingContextParams& aParams, const wr::LayoutRect& aBounds,
       const wr::RasterSpace& aRasterSpace);
   void PopStackingContext(bool aIsReferenceFrame);
 
-  wr::WrClipChainId DefineClipChain(const nsTArray<wr::WrClipId>& aClips);
+  wr::WrClipChainId DefineClipChain(const nsTArray<wr::WrClipId>& aClips,
+                                    const wr::WrClipChainId* aParent = nullptr);
 
   wr::WrClipId DefineClip(
       const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
       const nsTArray<wr::ComplexClipRegion>* aComplex = nullptr,
       const wr::ImageMask* aMask = nullptr);
 
   wr::WrSpatialId DefineStickyFrame(const wr::LayoutRect& aContentRect,
                                     const float* aTopMargin,
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -4633,16 +4633,35 @@ impl Renderer {
     ) {
         let _gm = self.gpu_profile.start_marker("tile frame draw");
 
         if frame.passes.is_empty() {
             frame.has_been_rendered = true;
             return;
         }
 
+        // TODO(gw): This is a hack / workaround for a resizing glitch. What
+        //           happens is that the framebuffer rect / content origin are
+        //           determined during frame building, rather than at render
+        //           time (which is what used to happen). This means that the
+        //           framebuffer rect/origin can be wrong by the time a frame
+        //           is drawn, if resizing is occurring. This hack just makes
+        //           the framebuffer rect/origin be hard coded to the current
+        //           framebuffer size at render time. It seems like this probably
+        //           breaks some assumptions elsewhere, but it seems to fix the
+        //           bug and I haven't noticed any other issues so far. We will
+        //           need to investigate this further and make a "proper" fix.
+        if let Some(framebuffer_size) = framebuffer_size {
+            frame.framebuffer_rect = FramebufferIntRect::new(
+                FramebufferIntPoint::zero(),
+                framebuffer_size,
+            );
+            frame.content_origin = DeviceIntPoint::zero();
+        }
+
         self.device.disable_depth_write();
         self.set_blend(false, FramebufferKind::Other);
         self.device.disable_stencil();
 
         self.bind_frame_data(frame);
 
         for (pass_index, pass) in frame.passes.iter_mut().enumerate() {
             let _gm = self.gpu_profile.start_marker(&format!("pass {}", pass_index));
--- a/js/src/jit-test/tests/auto-regress/bug499169.js
+++ b/js/src/jit-test/tests/auto-regress/bug499169.js
@@ -26,17 +26,17 @@ var Native = function(k) {
     for (var h in a) {
         new Native({
             initialize: a[h],
         });
     }
 } ());
 Array.alias("forEach", "each");
 function $merge() {
-    var a = Array.slice(arguments);
+    var a = Array.prototype.slice.call(arguments);
     a.unshift({});
     return $mixin.apply(null, a);
 }
 function $mixin(e) {
     for (var d = 1, a = arguments.length; d < a; d++) {
         var b = arguments[d];
         for (var c in b) {
             var g = b[c],
--- a/js/src/jit-test/tests/basic/exception-column-number.js
+++ b/js/src/jit-test/tests/basic/exception-column-number.js
@@ -1,10 +1,10 @@
 try {
-    Array.indexOf();
+    Array.from();
 } catch (e) {
     assertEq(e.columnNumber, 11);
     // Filter the filename from the stack, since we have no clue what
     // to expect there.  Search for ':' from the end, because the file
     // path may contain ':' in it.
     var lastColon = e.stack.lastIndexOf(':');
     var afterPath = e.stack.lastIndexOf(':', lastColon - 1);
     assertEq(e.stack.substring(afterPath), ":2:11\n");
--- a/js/src/jit-test/tests/collections/Map-surfaces-2.js
+++ b/js/src/jit-test/tests/collections/Map-surfaces-2.js
@@ -1,15 +1,14 @@
 // Map methods throw when passed a this-value that isn't a Map.
 
 load(libdir + "asserts.js");
 
-function testcase(obj, fn) {
+function testcase(obj, fn, ...args) {
     assertEq(typeof fn, "function");
-    var args = Array.slice(arguments, 2);
     assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError);
 }
 
 var Map_size_getter = Object.getOwnPropertyDescriptor(Map.prototype, "size").get;
 
 function test(obj) {
     testcase(obj, Map.prototype.get, "x");
     testcase(obj, Map.prototype.has, "x");
--- a/js/src/jit-test/tests/collections/Set-surfaces-2.js
+++ b/js/src/jit-test/tests/collections/Set-surfaces-2.js
@@ -1,15 +1,14 @@
 // Set methods throw when passed a this-value that isn't a Set.
 
 load(libdir + "asserts.js");
 
-function testcase(obj, fn) {
+function testcase(obj, fn, ...args) {
     assertEq(typeof fn, "function");
-    var args = Array.slice(arguments, 2);
     assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError);
 }
 
 var Set_size_getter = Object.getOwnPropertyDescriptor(Set.prototype, "size").get;
 
 function test(obj) {
     testcase(obj, Set.prototype.has, 12);
     testcase(obj, Set.prototype.add, 12);
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -296,16 +296,18 @@ void MacroAssembler::mul64(const Registe
 
 void MacroAssembler::neg64(Register64 reg) {
   as_subu(ScratchRegister, zero, reg.low);
   as_sltu(ScratchRegister, reg.low, ScratchRegister);
   as_subu(reg.high, zero, reg.high);
   as_subu(reg.high, reg.high, ScratchRegister);
 }
 
+void MacroAssembler::negPtr(Register reg) { as_subu(reg, zero, reg); }
+
 void MacroAssembler::mulBy3(Register src, Register dest) {
   MOZ_ASSERT(src != ScratchRegister);
   as_addu(ScratchRegister, src, src);
   as_addu(dest, ScratchRegister, src);
 }
 
 void MacroAssembler::inc64(AbsoluteAddress dest) {
   ma_li(ScratchRegister, Imm32((int32_t)dest.addr));
@@ -809,16 +811,28 @@ void MacroAssembler::branchTestStringTru
   ma_b(scratch2, Imm32(0), label, b ? NotEqual : Equal);
 }
 
 void MacroAssembler::branchTestSymbol(Condition cond, const ValueOperand& value,
                                       Label* label) {
   branchTestSymbol(cond, value.typeReg(), label);
 }
 
+void MacroAssembler::branchTestBigInt(Condition cond, Register tag,
+                                      Label* label) {
+  branchTestBigIntImpl(cond, tag, label);
+}
+
+void MacroAssembler::branchTestBigInt(Condition cond, const BaseIndex& address,
+                                      Label* label) {
+  SecondScratchRegisterScope scratch2(*this);
+  splitTag(value, scratch2);
+  branchTestBigInt(cond, scratch2, label);
+}
+
 void MacroAssembler::branchTestBigInt(Condition cond, const ValueOperand& value,
                                       Label* label) {
   branchTestBigInt(cond, value.typeReg(), label);
 }
 
 void MacroAssembler::branchTestBigIntTruthy(bool b, const ValueOperand& value,
                                             Label* label) {
   Register bi = value.payloadReg();
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -16,16 +16,17 @@
 #ifdef JS_ION_PERF
 #  include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/Realm.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/SharedICHelpers-inl.h"
+#include "jit/VMFunctionList-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 static_assert(sizeof(uintptr_t) == sizeof(uint32_t), "Not 64-bit clean.");
 
 struct EnterJITRegs {
   double f30;
@@ -667,21 +668,22 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunction& f) {
-  MOZ_ASSERT(functionWrappers_);
+                                   const VMFunctionData& f, void* nativeFun,
+                                   uint32_t* wrapperOffset) {
+  *wrapperOffset = startTrampolineCode(masm);
 
-  uint32_t wrapperOffset = startTrampolineCode(masm);
-
+  // Avoid conflicts with argument registers while discarding the result after
+  // the function call.
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
   static_assert(
       (Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
       "Wrapper register set should be a superset of Volatile register set.");
 
   // The context is the first argument; a0 is the first argument register.
   Register cxreg = a0;
@@ -762,34 +764,34 @@ bool JitRuntime::generateVMWrapper(JSCon
   masm.passABIArg(cxreg);
 
   size_t argDisp = 0;
   size_t doubleArgDisp = 0;
 
   // Copy any arguments.
   for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
     switch (f.argProperties(explicitArg)) {
-      case VMFunction::WordByValue:
+      case VMFunctionData::WordByValue:
         masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
         argDisp += sizeof(uint32_t);
         break;
-      case VMFunction::DoubleByValue:
+      case VMFunctionData::DoubleByValue:
         // Values should be passed by reference, not by value, so we
         // assert that the argument is a double-precision float.
         MOZ_ASSERT(f.argPassedInFloatReg(explicitArg));
         masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE);
         argDisp += sizeof(double);
         break;
-      case VMFunction::WordByRef:
+      case VMFunctionData::WordByRef:
         masm.passABIArg(
             MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
             MoveOp::GENERAL);
         argDisp += sizeof(uint32_t);
         break;
-      case VMFunction::DoubleByRef:
+      case VMFunctionData::DoubleByRef:
         // Copy double sized argument to aligned place.
         masm.ma_ldc1WordAligned(ScratchDoubleReg, argsBase, argDisp);
         masm.as_sdc1(ScratchDoubleReg, doubleArgs, doubleArgDisp);
         masm.passABIArg(MoveOperand(doubleArgs, doubleArgDisp,
                                     MoveOperand::EFFECTIVE_ADDRESS),
                         MoveOp::GENERAL);
         doubleArgDisp += sizeof(double);
         argDisp += sizeof(double);
@@ -802,17 +804,17 @@ bool JitRuntime::generateVMWrapper(JSCon
 
   // Copy the implicit outparam, if any.
   if (f.outParam != Type_Void) {
     masm.passABIArg(
         MoveOperand(doubleArgs, outParamOffset, MoveOperand::EFFECTIVE_ADDRESS),
         MoveOp::GENERAL);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // Test for failure.
   switch (f.failType()) {
@@ -873,17 +875,17 @@ bool JitRuntime::generateVMWrapper(JSCon
   masm.restoreStackPointer();
   masm.setFramePushed(framePushedBeforeAlignStack);
 
   masm.leaveExitFrame();
   masm.retn(Imm32(sizeof(ExitFrameLayout) +
                   f.explicitStackSlots() * sizeof(uintptr_t) +
                   f.extraValuesToPop * sizeof(Value)));
 
-  return functionWrappers_->putNew(&f, wrapperOffset);
+  return true;
 }
 
 uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,
                                         MIRType type) {
   uint32_t offset = startTrampolineCode(masm);
 
   MOZ_ASSERT(PreBarrierReg == a1);
   Register temp1 = a0;
@@ -928,38 +930,35 @@ uint32_t JitRuntime::generatePreBarrier(
   masm.pop(temp3);
   masm.pop(temp2);
   masm.pop(temp1);
   masm.abiret();
 
   return offset;
 }
 
-typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
-static const VMFunction HandleDebugTrapInfo =
-    FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
-
 JitCode* JitRuntime::generateDebugTrapHandler(JSContext* cx) {
   StackMacroAssembler masm(cx);
 
   Register scratch1 = t0;
   Register scratch2 = t1;
 
   // Load BaselineFrame pointer in scratch1.
   masm.movePtr(s5, scratch1);
   masm.subPtr(Imm32(BaselineFrame::Size()), scratch1);
 
   // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
   // the stub frame has a nullptr ICStub pointer, since this pointer is
   // marked during GC.
   masm.movePtr(ImmPtr(nullptr), ICStubReg);
   EmitBaselineEnterStubFrame(masm, scratch2);
 
-  TrampolinePtr code =
-      cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, uint8_t*, bool*);
+  VMFunctionId id = VMFunctionToId<Fn, jit::HandleDebugTrap>::id;
+  TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
 
   masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
   masm.storePtr(ra, Address(StackPointer, sizeof(uintptr_t)));
   masm.storePtr(scratch1, Address(StackPointer, 0));
 
   EmitBaselineCallVM(code, masm);
 
   EmitBaselineLeaveStubFrame(masm);
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -50,16 +50,23 @@ void MacroAssembler::move16To64SignExten
   move16SignExtend(dest.reg, dest.reg);
 }
 
 void MacroAssembler::move32To64SignExtend(Register src, Register64 dest) {
   ma_sll(dest.reg, src, Imm32(0));
 }
 
 // ===============================================================
+// Load instructions
+
+void MacroAssembler::load32SignExtendToPtr(const Address& src, Register dest) {
+  loadPtr(src, dest);
+}
+
+// ===============================================================
 // Logical instructions
 
 void MacroAssembler::andPtr(Register src, Register dest) { ma_and(dest, src); }
 
 void MacroAssembler::andPtr(Imm32 imm, Register dest) { ma_and(dest, imm); }
 
 void MacroAssembler::and64(Imm64 imm, Register64 dest) {
   ma_li(ScratchRegister, ImmWord(imm.value));
@@ -255,16 +262,18 @@ void MacroAssembler::inc64(AbsoluteAddre
   ma_li(ScratchRegister, ImmWord(uintptr_t(dest.addr)));
   as_ld(SecondScratchReg, ScratchRegister, 0);
   as_daddiu(SecondScratchReg, SecondScratchReg, 1);
   as_sd(SecondScratchReg, ScratchRegister, 0);
 }
 
 void MacroAssembler::neg64(Register64 reg) { as_dsubu(reg.reg, zero, reg.reg); }
 
+void MacroAssembler::negPtr(Register reg) { as_dsubu(reg, zero, reg); }
+
 // ===============================================================
 // Shift functions
 
 void MacroAssembler::lshiftPtr(Imm32 imm, Register dest) {
   MOZ_ASSERT(0 <= imm.value && imm.value < 64);
   ma_dsll(dest, dest, imm);
 }
 
@@ -541,16 +550,30 @@ void MacroAssembler::branchTestStringTru
 
 void MacroAssembler::branchTestSymbol(Condition cond, const ValueOperand& value,
                                       Label* label) {
   SecondScratchRegisterScope scratch2(*this);
   splitTag(value, scratch2);
   branchTestSymbol(cond, scratch2, label);
 }
 
+void MacroAssembler::branchTestBigInt(Condition cond, Register tag,
+                                      Label* label) {
+  MOZ_ASSERT(cond == Equal || cond == NotEqual);
+  ma_b(tag, ImmTag(JSVAL_TAG_BIGINT), label, cond);
+}
+
+void MacroAssembler::branchTestBigInt(Condition cond, const BaseIndex& address,
+                                      Label* label) {
+  SecondScratchRegisterScope scratch2(*this);
+  computeEffectiveAddress(address, scratch2);
+  splitTag(scratch2, scratch2);
+  branchTestBigInt(cond, scratch2, label);
+}
+
 void MacroAssembler::branchTestBigInt(Condition cond, const ValueOperand& value,
                                       Label* label) {
   SecondScratchRegisterScope scratch2(*this);
   splitTag(value, scratch2);
   branchTestBigInt(cond, scratch2, label);
 }
 
 void MacroAssembler::branchTestBigIntTruthy(bool b, const ValueOperand& value,
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -16,16 +16,17 @@
 #ifdef JS_ION_PERF
 #  include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/Realm.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/SharedICHelpers-inl.h"
+#include "jit/VMFunctionList-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
 static const LiveRegisterSet AllRegs =
     LiveRegisterSet(GeneralRegisterSet(Registers::AllMask),
@@ -644,21 +645,22 @@ JitRuntime::BailoutTable JitRuntime::gen
 void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
                                         Label* bailoutTail) {
   bailoutHandlerOffset_ = startTrampolineCode(masm);
 
   GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail);
 }
 
 bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
-                                   const VMFunction& f) {
-  MOZ_ASSERT(functionWrappers_);
+                                   const VMFunctionData& f, void* nativeFun,
+                                   uint32_t* wrapperOffset) {
+  *wrapperOffset = startTrampolineCode(masm);
 
-  uint32_t wrapperOffset = startTrampolineCode(masm);
-
+  // Avoid conflicts with argument registers while discarding the result after
+  // the function call.
   AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
   static_assert(
       (Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
       "Wrapper register set should be a superset of Volatile register set.");
 
   // The context is the first argument; a0 is the first argument register.
   Register cxreg = a0;
@@ -729,43 +731,43 @@ bool JitRuntime::generateVMWrapper(JSCon
   masm.setupUnalignedABICall(regs.getAny());
   masm.passABIArg(cxreg);
 
   size_t argDisp = 0;
 
   // Copy any arguments.
   for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
     switch (f.argProperties(explicitArg)) {
-      case VMFunction::WordByValue:
+      case VMFunctionData::WordByValue:
         if (f.argPassedInFloatReg(explicitArg)) {
           masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE);
         } else {
           masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
         }
         argDisp += sizeof(void*);
         break;
-      case VMFunction::WordByRef:
+      case VMFunctionData::WordByRef:
         masm.passABIArg(
             MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
             MoveOp::GENERAL);
         argDisp += sizeof(void*);
         break;
-      case VMFunction::DoubleByValue:
-      case VMFunction::DoubleByRef:
+      case VMFunctionData::DoubleByValue:
+      case VMFunctionData::DoubleByRef:
         MOZ_CRASH("NYI: MIPS64 callVM should not be used with 128bits values.");
         break;
     }
   }
 
   // Copy the implicit outparam, if any.
   if (InvalidReg != outReg) {
     masm.passABIArg(outReg);
   }
 
-  masm.callWithABI(f.wrapped, MoveOp::GENERAL,
+  masm.callWithABI(nativeFun, MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   if (!generateTLExitVM(masm, f)) {
     return false;
   }
 
   // Test for failure.
   switch (f.failType()) {
@@ -823,17 +825,17 @@ bool JitRuntime::generateVMWrapper(JSCon
       break;
   }
 
   masm.leaveExitFrame();
   masm.retn(Imm32(sizeof(ExitFrameLayout) +
                   f.explicitStackSlots() * sizeof(void*) +
                   f.extraValuesToPop * sizeof(Value)));
 
-  return functionWrappers_->putNew(&f, wrapperOffset);
+  return true;
 }
 
 uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,
                                         MIRType type) {
   uint32_t offset = startTrampolineCode(masm);
 
   MOZ_ASSERT(PreBarrierReg == a1);
   Register temp1 = a0;
@@ -878,38 +880,35 @@ uint32_t JitRuntime::generatePreBarrier(
   masm.pop(temp3);
   masm.pop(temp2);
   masm.pop(temp1);
   masm.abiret();
 
   return offset;
 }
 
-typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
-static const VMFunction HandleDebugTrapInfo =
-    FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
-
 JitCode* JitRuntime::generateDebugTrapHandler(JSContext* cx) {
   StackMacroAssembler masm(cx);
 
   Register scratch1 = t0;
   Register scratch2 = t1;
 
   // Load BaselineFrame pointer in scratch1.
   masm.movePtr(s5, scratch1);
   masm.subPtr(Imm32(BaselineFrame::Size()), scratch1);
 
   // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
   // the stub frame has a nullptr ICStub pointer, since this pointer is
   // marked during GC.
   masm.movePtr(ImmPtr(nullptr), ICStubReg);
   EmitBaselineEnterStubFrame(masm, scratch2);
 
-  TrampolinePtr code =
-      cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
+  using Fn = bool (*)(JSContext*, BaselineFrame*, uint8_t*, bool*);
+  VMFunctionId id = VMFunctionToId<Fn, jit::HandleDebugTrap>::id;
+  TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
 
   masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
   masm.storePtr(ra, Address(StackPointer, sizeof(uintptr_t)));
   masm.storePtr(scratch1, Address(StackPointer, 0));
 
   EmitBaselineCallVM(code, masm);
 
   EmitBaselineLeaveStubFrame(masm);
--- a/js/src/tests/non262/Array/regress-474529.js
+++ b/js/src/tests/non262/Array/regress-474529.js
@@ -24,17 +24,17 @@ function test()
     function timeit(N, buildArrayString) {
       return Function("N",
                       "var d1 = +new Date;" +
                       "while (N--) var x = " + buildArrayString +
                       "; return +new Date - d1")(N);
     }
 
     function reportResults(size, N, literalMs, newArrayMs, arrayMs) {
-      print(Array.join(arguments, "\t"));
+      print(Array.prototype.join.call(arguments, "\t"));
     }
 
     var repetitions = [ 9000, 7000, 4000, 2000, 2000, 2000, 800, 800, 800, 300, 100, 100 ]
       for (var zeros = "0, ", i = 0; i < repetitions.length; ++i) {
         var N = repetitions[i];
         reportResults((1 << i) + 1, N,
                       timeit(N, "[" + zeros + " 0 ]"),
                       timeit(N, "new Array(" + zeros + " 0)"),
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
+#include "MobileViewportManager.h"
 #include <algorithm>
 
 #ifdef XP_WIN
 #  include "winuser.h"
 #endif
 
 #include "gfxContext.h"
 #include "gfxPrefs.h"
@@ -832,17 +833,19 @@ PresShell::PresShell()
       mNextPaintCompressed(false),
       mHasCSSBackgroundColor(false),
       mIsLastChromeOnlyEscapeKeyConsumed(false),
       mHasReceivedPaintMessage(false),
       mIsLastKeyDownCanceled(false),
       mHasHandledUserInput(false),
       mForceDispatchKeyPressEventsForNonPrintableKeys(false),
       mForceUseLegacyKeyCodeAndCharCodeValues(false),
-      mInitializedWithKeyPressEventDispatchingBlacklist(false) {
+      mInitializedWithKeyPressEventDispatchingBlacklist(false),
+      mForceUseLegacyNonPrimaryDispatch(false),
+      mInitializedWithClickEventDispatchingBlacklist(false) {
   MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
 
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = MakeUnique<ReflowCountMgr>();
   mReflowCountMgr->SetPresContext(mPresContext);
   mReflowCountMgr->SetPresShell(this);
 #endif
   mLastOSWake = mLoadBegin = TimeStamp::Now();
@@ -1453,23 +1456,23 @@ void nsIPresShell::UpdatePreferenceStyle
   }
 
   RemovePreferenceStyles();
 
   // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
   // it to be modifiable from devtools and similar, see bugs 1239336 and
   // 1436782. I think it conceptually should be a user sheet, and could be
   // without too much trouble I'd think.
-  StyleSet()->AppendStyleSheet(SheetType::Agent, newPrefSheet);
+  StyleSet()->AppendStyleSheet(StyleOrigin::UserAgent, newPrefSheet);
   mPrefStyleSheet = newPrefSheet;
 }
 
 void nsIPresShell::RemovePreferenceStyles() {
   if (mPrefStyleSheet) {
-    StyleSet()->RemoveStyleSheet(SheetType::Agent, mPrefStyleSheet);
+    StyleSet()->RemoveStyleSheet(StyleOrigin::UserAgent, mPrefStyleSheet);
     mPrefStyleSheet = nullptr;
   }
 }
 
 void nsIPresShell::AddUserSheet(StyleSheet* aSheet) {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
   // ordering. We want this new sheet to come after all the existing stylesheet
   // service sheets (which are at the start), but before other user sheets; see
@@ -1486,52 +1489,52 @@ void nsIPresShell::AddUserSheet(StyleShe
   MOZ_ASSERT(aSheet);
   MOZ_ASSERT(userSheets.LastElement() == aSheet);
 
   size_t index = userSheets.Length() - 1;
 
   // Assert that all of userSheets (except for the last, new element) matches up
   // with what's in the style set.
   for (size_t i = 0; i < index; ++i) {
-    MOZ_ASSERT(StyleSet()->StyleSheetAt(SheetType::User, i) == userSheets[i]);
-  }
-
-  if (index == static_cast<size_t>(StyleSet()->SheetCount(SheetType::User))) {
-    StyleSet()->AppendStyleSheet(SheetType::User, aSheet);
+    MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User, i) == userSheets[i]);
+  }
+
+  if (index == static_cast<size_t>(StyleSet()->SheetCount(StyleOrigin::User))) {
+    StyleSet()->AppendStyleSheet(StyleOrigin::User, aSheet);
   } else {
-    StyleSheet* ref = StyleSet()->StyleSheetAt(SheetType::User, index);
-    StyleSet()->InsertStyleSheetBefore(SheetType::User, aSheet, ref);
+    StyleSheet* ref = StyleSet()->SheetAt(StyleOrigin::User, index);
+    StyleSet()->InsertStyleSheetBefore(StyleOrigin::User, aSheet, ref);
   }
 
   mDocument->ApplicableStylesChanged();
 }
 
 void nsIPresShell::AddAgentSheet(StyleSheet* aSheet) {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does
   // wrt ordering.
-  StyleSet()->AppendStyleSheet(SheetType::Agent, aSheet);
+  StyleSet()->AppendStyleSheet(StyleOrigin::UserAgent, aSheet);
   mDocument->ApplicableStylesChanged();
 }
 
 void nsIPresShell::AddAuthorSheet(StyleSheet* aSheet) {
   // Document specific "additional" Author sheets should be stronger than the
   // ones added with the StyleSheetService.
   StyleSheet* firstAuthorSheet = mDocument->GetFirstAdditionalAuthorSheet();
   if (firstAuthorSheet) {
-    StyleSet()->InsertStyleSheetBefore(SheetType::Doc, aSheet,
+    StyleSet()->InsertStyleSheetBefore(StyleOrigin::Author, aSheet,
                                        firstAuthorSheet);
   } else {
-    StyleSet()->AppendStyleSheet(SheetType::Doc, aSheet);
+    StyleSet()->AppendStyleSheet(StyleOrigin::Author, aSheet);
   }
 
   mDocument->ApplicableStylesChanged();
 }
 
-void nsIPresShell::RemoveSheet(SheetType aType, StyleSheet* aSheet) {
-  StyleSet()->RemoveStyleSheet(aType, aSheet);
+void nsIPresShell::RemoveSheet(StyleOrigin aOrigin, StyleSheet* aSheet) {
+  StyleSet()->RemoveStyleSheet(aOrigin, aSheet);
   mDocument->ApplicableStylesChanged();
 }
 
 NS_IMETHODIMP
 PresShell::SetDisplaySelection(int16_t aToggle) {
   RefPtr<nsFrameSelection> frameSelection = mSelection;
   frameSelection->SetDisplaySelection(aToggle);
   return NS_OK;
@@ -8179,16 +8182,36 @@ nsresult PresShell::EventHandler::Dispat
                                             "use_legacy_keycode_and_charcode");
       }
       if (mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys) {
         aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
       }
       if (mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues) {
         aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
       }
+    } else if (aEvent->mMessage == eMouseUp) {
+      // Historically Firefox has dispatched click events for non-primary
+      // buttons, but only on window and document (and inside input/textarea),
+      // not on elements in general. The UI events spec forbids click (and
+      // dblclick) for non-primary mouse buttons, and specifies auxclick
+      // instead. In case of some websites that rely on non-primary click to
+      // prevent new tab etc. and don't have auxclick code to do the same, we
+      // need to revert to the historial non-standard behaviour
+      if (!mPresShell->mInitializedWithClickEventDispatchingBlacklist) {
+        mPresShell->mInitializedWithClickEventDispatchingBlacklist = true;
+        nsCOMPtr<nsIURI> uri =
+            GetDocumentURIToCompareWithBlacklist(*mPresShell);
+        mPresShell->mForceUseLegacyNonPrimaryDispatch =
+            nsContentUtils::IsURIInPrefList(
+                uri,
+                "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch");
+      }
+      if (mPresShell->mForceUseLegacyNonPrimaryDispatch) {
+        aEvent->AsMouseEvent()->mUseLegacyNonPrimaryDispatch = true;
+      }
     }
 
     if (aEvent->mClass == eCompositionEventClass) {
       IMEStateManager::DispatchCompositionEvent(
           eventTarget, GetPresContext(), TabParent::GetFocused(),
           aEvent->AsCompositionEvent(), aEventStatus, eventCBPtr);
     } else {
       EventDispatcher::Dispatch(eventTarget, GetPresContext(), aEvent, nullptr,
@@ -8785,41 +8808,44 @@ void PresShell::RespectDisplayportSuppre
 }
 
 bool PresShell::IsDisplayportSuppressed() {
   return sDisplayPortSuppressionRespected && mActiveSuppressDisplayport > 0;
 }
 
 nsresult PresShell::GetAgentStyleSheets(nsTArray<RefPtr<StyleSheet>>& aSheets) {
   aSheets.Clear();
-  int32_t sheetCount = StyleSet()->SheetCount(SheetType::Agent);
+  int32_t sheetCount = StyleSet()->SheetCount(StyleOrigin::UserAgent);
 
   if (!aSheets.SetCapacity(sheetCount, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   for (int32_t i = 0; i < sheetCount; ++i) {
-    StyleSheet* sheet = StyleSet()->StyleSheetAt(SheetType::Agent, i);
+    StyleSheet* sheet = StyleSet()->SheetAt(StyleOrigin::UserAgent, i);
     aSheets.AppendElement(sheet);
   }
 
   return NS_OK;
 }
 
 nsresult PresShell::SetAgentStyleSheets(
     const nsTArray<RefPtr<StyleSheet>>& aSheets) {
-  return StyleSet()->ReplaceSheets(SheetType::Agent, aSheets);
+  StyleSet()->ReplaceSheets(StyleOrigin::UserAgent, aSheets);
+  return NS_OK;
 }
 
 nsresult PresShell::AddOverrideStyleSheet(StyleSheet* aSheet) {
-  return StyleSet()->AppendStyleSheet(SheetType::Override, aSheet);
+  StyleSet()->AppendStyleSheet(aSheet->GetOrigin(), aSheet);
+  return NS_OK;
 }
 
 nsresult PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet) {
-  return StyleSet()->RemoveStyleSheet(SheetType::Override, aSheet);
+  StyleSet()->RemoveStyleSheet(aSheet->GetOrigin(), aSheet);
+  return NS_OK;
 }
 
 static void FreezeElement(nsISupports* aSupports, void* /* unused */) {
   nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
   if (olc) {
     olc->StopPluginInstance();
   }
 }
@@ -9952,19 +9978,19 @@ void PresShell::ListComputedStyles(FILE*
     nsIFrame* rootElementFrame = rootElement->GetPrimaryFrame();
     if (rootElementFrame) {
       rootElementFrame->Style()->List(out, aIndent);
     }
   }
 }
 
 void PresShell::ListStyleSheets(FILE* out, int32_t aIndent) {
-  int32_t sheetCount = StyleSet()->SheetCount(SheetType::Doc);
+  int32_t sheetCount = StyleSet()->SheetCount(StyleOrigin::Author);
   for (int32_t i = 0; i < sheetCount; ++i) {
-    StyleSet()->StyleSheetAt(SheetType::Doc, i)->List(out, aIndent);
+    StyleSet()->SheetAt(StyleOrigin::Author, i)->List(out, aIndent);
     fputs("\n", out);
   }
 }
 #endif
 
 //=============================================================
 //=============================================================
 //-- Debug Reflow Counts
@@ -10839,28 +10865,28 @@ void nsIPresShell::SyncWindowProperties(
   nsIFrame* frame = aView->GetFrame();
   if (frame && mPresContext) {
     // CreateReferenceRenderingContext can return nullptr
     RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
     nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, rcx, 0);
   }
 }
 
-static SheetType ToSheetType(uint32_t aServiceSheetType) {
+static StyleOrigin ToOrigin(uint32_t aServiceSheetType) {
   switch (aServiceSheetType) {
     case nsIStyleSheetService::AGENT_SHEET:
-      return SheetType::Agent;
+      return StyleOrigin::UserAgent;
       break;
     case nsIStyleSheetService::USER_SHEET:
-      return SheetType::User;
+      return StyleOrigin::User;
       break;
     default:
       MOZ_FALLTHROUGH_ASSERT("unexpected aSheetType value");
     case nsIStyleSheetService::AUTHOR_SHEET:
-      return SheetType::Doc;
+      return StyleOrigin::Author;
   }
 }
 
 nsresult nsIPresShell::HasRuleProcessorUsedByMultipleStyleSets(
     uint32_t aSheetType, bool* aRetVal) {
   *aRetVal = false;
   return NS_OK;
 }
@@ -10880,17 +10906,17 @@ void nsIPresShell::NotifyStyleSheetServi
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
       break;
   }
 }
 
 void nsIPresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
                                                        uint32_t aSheetType) {
-  RemoveSheet(ToSheetType(aSheetType), aSheet);
+  RemoveSheet(ToOrigin(aSheetType), aSheet);
 }
 
 nsIContent* PresShell::EventHandler::GetOverrideClickTarget(
     WidgetGUIEvent* aGUIEvent, nsIFrame* aFrame) {
   if (aGUIEvent->mMessage != eMouseUp) {
     return nullptr;
   }
 
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -1424,16 +1424,21 @@ class PresShell final : public nsIPresSh
   // value is zero to the other value or not.  When this is set to true, we
   // should keep using legacy keyCode and charCode values (i.e., one of them
   // is always 0).
   bool mForceUseLegacyKeyCodeAndCharCodeValues : 1;
   // Whether mForceDispatchKeyPressEventsForNonPrintableKeys and
   // mForceUseLegacyKeyCodeAndCharCodeValues are initialized.
   bool mInitializedWithKeyPressEventDispatchingBlacklist : 1;
 
+  // Whether we should dispatch click events for non-primary mouse buttons.
+  bool mForceUseLegacyNonPrimaryDispatch : 1;
+  // Whether mForceUseLegacyNonPrimaryDispatch is initialised.
+  bool mInitializedWithClickEventDispatchingBlacklist : 1;
+
   static bool sDisableNonTestMouseEvents;
 
   TimeStamp mLastOSWake;
 
   static bool sProcessInteractable;
 };
 
 }  // namespace mozilla
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -10,16 +10,17 @@
 #define nsIPresShell_h___
 
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/FlushType.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ScrollTypes.h"
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/ServoStyleConsts.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "FrameMetrics.h"
 #include "GeckoProfiler.h"
 #include "gfxPoint.h"
 #include "nsTHashtable.h"
@@ -1788,17 +1789,17 @@ class nsIPresShell : public nsStubDocume
 
   /**
    * Methods to handle changes to user and UA sheet lists that we get
    * notified about.
    */
   void AddUserSheet(mozilla::StyleSheet*);
   void AddAgentSheet(mozilla::StyleSheet*);
   void AddAuthorSheet(mozilla::StyleSheet*);
-  void RemoveSheet(mozilla::SheetType, mozilla::StyleSheet*);
+  void RemoveSheet(mozilla::StyleOrigin, mozilla::StyleSheet*);
   void RemovePreferenceStyles();
 
   void WillDoReflow();
 
   // This data is stored as a content property (nsGkAtoms::scrolling) on
   // mContentToScrollTo when we have a pending ScrollIntoView.
   struct ScrollIntoViewData {
     ScrollAxis mContentScrollVAxis;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5857,17 +5857,17 @@ void nsTextFrame::PaintOneShadow(const P
   nscolor shadowColor =
       aShadowDetails->mColor.CalcColor(aParams.foregroundColor);
 
   if (auto* textDrawer = aParams.context->GetTextDrawer()) {
     wr::Shadow wrShadow;
 
     // Gecko already inflates the bounding rect of text shadows,
     // so tell WR not to inflate again.
-    wrShadow.should_inflate = true;
+    wrShadow.should_inflate = false;
 
     wrShadow.offset = {
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)};
 
     wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(blurRadius);
     wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
 
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -468,27 +468,18 @@ class nsDisplayVideo : public nsDisplayI
     // help us. Hence we can ignore the return value from PushImage.
     LayoutDeviceRect rect(destGFXRect.x, destGFXRect.y, destGFXRect.width,
                           destGFXRect.height);
     aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources,
                                          aSc, rect, rect);
     return true;
   }
 
-  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
-                           bool* aSnap) const override {
-    *aSnap = false;
-
-    HTMLVideoElement* element =
-        static_cast<HTMLVideoElement*>(Frame()->GetContent());
-    if (element->HasAlpha()) {
-      return nsRegion();
-    }
-    return GetBounds(aBuilder, aSnap);
-  }
+  // For opaque videos, we will want to override GetOpaqueRegion here.
+  // This is tracked by bug 1545498.
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override {
     *aSnap = true;
     nsIFrame* f = Frame();
     return f->GetContentRectRelativeToSelf() + ToReferenceFrame();
   }
 
--- a/layout/inspector/InspectorUtils.cpp
+++ b/layout/inspector/InspectorUtils.cpp
@@ -60,23 +60,23 @@ void InspectorUtils::GetAllStyleSheets(G
                                        nsTArray<RefPtr<StyleSheet>>& aResult) {
   // Get the agent, then user and finally xbl sheets in the style set.
   PresShell* presShell = aDocument.GetPresShell();
 
   if (presShell) {
     ServoStyleSet* styleSet = presShell->StyleSet();
 
     if (!aDocumentOnly) {
-      SheetType sheetType = SheetType::Agent;
-      for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
-        aResult.AppendElement(styleSet->StyleSheetAt(sheetType, i));
-      }
-      sheetType = SheetType::User;
-      for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
-        aResult.AppendElement(styleSet->StyleSheetAt(sheetType, i));
+      const StyleOrigin kOrigins[] = {StyleOrigin::UserAgent,
+                                      StyleOrigin::User};
+      for (const auto origin : kOrigins) {
+        for (size_t i = 0, count = styleSet->SheetCount(origin); i < count;
+             i++) {
+          aResult.AppendElement(styleSet->SheetAt(origin, i));
+        }
       }
     }
 
     AutoTArray<StyleSheet*, 32> xblSheetArray;
     styleSet->AppendAllNonDocumentAuthorSheets(xblSheetArray);
 
     // The XBL stylesheet array will quite often be full of duplicates. Cope:
     //
--- a/layout/inspector/ServoStyleRuleMap.cpp
+++ b/layout/inspector/ServoStyleRuleMap.cpp
@@ -21,22 +21,18 @@
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 void ServoStyleRuleMap::EnsureTable(ServoStyleSet& aStyleSet) {
   if (!IsEmpty()) {
     return;
   }
-  aStyleSet.EnumerateStyleSheetArrays(
-      [this](const nsTArray<RefPtr<StyleSheet>>& aArray) {
-        for (auto& sheet : aArray) {
-          FillTableFromStyleSheet(*sheet);
-        }
-      });
+  aStyleSet.EnumerateStyleSheets(
+      [&](StyleSheet& aSheet) { FillTableFromStyleSheet(aSheet); });
 }
 
 void ServoStyleRuleMap::EnsureTable(nsXBLPrototypeResources& aXBLResources) {
   if (!IsEmpty() || !aXBLResources.GetServoStyles()) {
     return;
   }
   for (auto index : IntegerRange(aXBLResources.SheetCount())) {
     FillTableFromStyleSheet(*aXBLResources.StyleSheetAt(index));
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8962,20 +8962,26 @@ void nsDisplayText::Paint(nsDisplayListB
 bool nsDisplayText::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   auto* f = static_cast<nsTextFrame*>(mFrame);
   auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
 
-  nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
+  // FIXME: the webrender backend is having a lot of snapping issues, and
+  // having it do inflation for us is causing problems. For now, we pass
+  // down the wrong bounds. Try to turn this back on when things are in
+  // better shape.
+  //
+  // nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
   // Bug 748228
-  bounds.Inflate(appUnitsPerDevPixel);
-
+  // bounds.Inflate(appUnitsPerDevPixel);
+
+  nsRect bounds = mBounds;
   if (bounds.IsEmpty()) {
     return true;
   }
 
   gfx::Point deviceOffset =
       LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
           .ToUnknownPoint();
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1544895-ref.html
@@ -0,0 +1,42 @@
+<html reftest-async-scroll>
+<head>
+<style>
+body {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100%;
+}
+.header {
+    background-color: white;
+    flex: none;
+}
+.content {
+    flex: auto;
+    overflow-y: scroll;
+}
+.inner-content {
+    height: 2000px;
+}
+.shadowy {
+    text-shadow: 0px 0px 1px green;
+}
+.hidden {
+    visibility: hidden;
+}
+</style>
+</head>
+<body>
+<div class="header"> Wow, This is a Great Header! </div>
+<div class="content"
+     reftest-displayport-x="0" reftest-displayport-y="0"
+     reftest-displayport-w="800" reftest-displayport-h="1075"
+     reftest-async-scroll-y="75">
+    <div class="inner-content">
+        <p class="hidden">Wow, This is Great Hidden Content!</p>
+        <p class="hidden">Wow, This is Great Hidden Content!</p>
+        <p>Wow, This is Great Visible Content!</p>
+    </div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1544895.html
@@ -0,0 +1,39 @@
+<html reftest-async-scroll>
+<head>
+<style>
+body {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100%;
+}
+.header {
+    background-color: white;
+    flex: none;
+}
+.content {
+    flex: auto;
+    overflow-y: scroll;
+}
+.inner-content {
+    height: 2000px;
+}
+.shadowy {
+    text-shadow: 0px 0px 1px green;
+}
+</style>
+</head>
+<body>
+<div class="header"> Wow, This is a Great Header! </div>
+<div class="content"
+     reftest-displayport-x="0" reftest-displayport-y="0"
+     reftest-displayport-w="800" reftest-displayport-h="1075"
+     reftest-async-scroll-y="75">
+    <div class="inner-content">
+        <p>Wow, This is Great Content!</p>
+        <p class="shadowy">Wow, This is Great Shadowy Content!</p>
+        <p>Wow, This is Great Visible Content!</p>
+    </div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2099,11 +2099,12 @@ fuzzy(0-1,0-625) == 1466638-1.html 14666
 == 1483649-1.xul 1483649-1-ref.xul
 test-pref(layout.css.contain.enabled,true) == 1483946.html 1483946-ref.html
 test-pref(layout.css.visited_links_enabled,false) == 1488155.html 1488155-ref.html
 == 1492660-1.html 1492660-1-ref.html
 pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
 pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
 == 1509425-1.html 1509425-1-ref.html
 == 1511570.html 1511570-ref.html
-fuzzy-if(!webrender,1-5,66-547) == 1529992-1.html 1529992-1-ref.html
+fuzzy-if(!webrender,1-5,66-547) fails-if(webrender) == 1529992-1.html 1529992-1-ref.html
 fuzzy-if(!webrender,0-6,0-34) fails-if(webrender) == 1529992-2.html 1529992-2-ref.html
 == 1535040-1.html 1535040-1-ref.html
+skip-if(!asyncPan) == 1544895.html 1544895-ref.html
--- a/layout/reftests/floats/orthogonal-floats-1a.html
+++ b/layout/reftests/floats/orthogonal-floats-1a.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <html>
  <head>
   <title>Bug 1141867 - Contiguous right-floating boxes with vertical writing mode</title>
-  <!-- based on testcases from Gérard Talbot, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141867 -->
+  <!-- based on testcases from Gerard Talbot, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141867 -->
   <meta charset="utf-8">
   <style type="text/css">
   body {
     margin: 8px;
   }
   p {
     clear: both;
   }
@@ -31,25 +31,24 @@
     top: 8px;
     z-index: -1;
   }
   </style>
  </head>
 
  <body>
 
-  <div class="floated-right">abcde</div>
-
-  <div class="floated-right">fghijk</div>
-
-  <div class="floated-right">lmnopq</div>
-
-  <div class="floated-right">rstuv</div>
-
-  <div class="floated-right">wxxz!</div>
-    <!-- bug 1364714, replace 'y' with 'x' to avoid descender possibly projecting -->
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
 
   <div id="reference-overlapped-red"></div>
 
   <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
 
  </body>
 </html>
--- a/layout/reftests/floats/orthogonal-floats-1b.html
+++ b/layout/reftests/floats/orthogonal-floats-1b.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <html>
  <head>
   <title>Bug 1141867 - Contiguous right-floating boxes with vertical writing mode</title>
-  <!-- based on testcases from Gérard Talbot, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141867 -->
+  <!-- based on testcases from Gerard Talbot, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141867 -->
   <meta charset="utf-8">
   <style type="text/css">
   body {
     margin: 8px;
   }
   p {
     clear: both;
   }
@@ -30,25 +30,25 @@
     right: 8px;
     top: 8px;
     z-index: -1;
   }
   </style>
  </head>
 
  <body>
-
-  <div class="floated-right">abcde</div>
-
-  <div class="floated-right">fghijk</div>
-
-  <div class="floated-right">lmnopq</div>
-
-  <div class="floated-right">rstuv</div>
-
-  <div class="floated-right">wxyz!</div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
+
+  <div class="floated-right"></div>
 
   <div id="reference-overlapped-red"></div>
 
   <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
 
  </body>
 </html>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -139,16 +139,18 @@ fuzzy-if(d2d||skiaContent,0-1,0-10000) =
 == dynamic-inner-svg-01.svg pass.svg
 == dynamic-link-style-01.svg pass.svg
 == dynamic-marker-01.svg pass.svg
 == dynamic-marker-02.svg dynamic-marker-02-ref.svg
 == dynamic-marker-03.svg pass.svg
 == dynamic-mask-01.svg pass.svg
 == dynamic-mask-contents-01.svg pass.svg
 == dynamic-mask-pre-effects-bbox.html dynamic-mask-pre-effects-bbox-ref.html
+== test_bug1323962.html test_bug1323962-ref.html
+== test_bug1323962-2.html test_bug1323962-2-ref.html
 == dynamic-opacity-property-01.svg pass.svg
 == dynamic-pattern-01.svg pass.svg
 == dynamic-pattern-02.svg pass.svg
 == dynamic-pattern-contents-01.svg pass.svg
 == dynamic-pattern-contents-02.svg pass.svg
 == dynamic-rect-01.svg dynamic-rect-01-ref.svg
 fuzzy-if(d2d&&layersGPUAccelerated,0-3,0-1200) == dynamic-rect-02.svg dynamic-rect-02-ref.svg # bug 776038 for Win7, Win8
 == dynamic-rect-03.svg dynamic-rect-03-ref.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962-2-ref.html
@@ -0,0 +1,9 @@
+<svg width="200" height="200">
+  <defs>
+    <pattern id="Pattern" x="0" y="0" width=".25" height=".25">
+      <rect id="rec" x="0" y="0" width="30" height="30" fill="skyblue"/>
+    </pattern>
+  </defs>
+
+  <rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962-2.html
@@ -0,0 +1,14 @@
+<style>
+#rec {
+  transform: scale(.6,.6);
+}
+</style>
+<svg width="200" height="200">
+  <defs>
+    <pattern id="Pattern" x="0" y="0" width=".25" height=".25">
+      <rect id="rec" x="0" y="0" width="50" height="50" fill="skyblue"/>
+    </pattern>
+  </defs>
+
+  <rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962-ref.html
@@ -0,0 +1,8 @@
+<svg style="width: 500px; height: 500px; border: 1px solid green;">
+  <defs>
+    <mask id="mask">
+      <rect id="square" x="200px" y="250px" width="100px" height="150px" fill="#ffffff" />
+    </mask>
+  </defs>
+  <rect mask="url(#mask)" width="500px" height="500px" fill="red" />
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962.html
@@ -0,0 +1,13 @@
+<style>
+  #square{
+    transform: translate(100px, 100px) scale(2,3);
+  }
+</style>
+<svg style="width: 500px; height: 500px; border: 1px solid green;">
+  <defs>
+    <mask id="mask">
+      <rect id="square" x="50px" y="50px" width="50px" height="50px" fill="#ffffff" />
+    </mask>
+  </defs>
+  <rect mask="url(#mask)" width="500px" height="500px" fill="red" />
+</svg>
--- a/layout/reftests/text-shadow/reftest.list
+++ b/layout/reftests/text-shadow/reftest.list
@@ -1,16 +1,16 @@
 == 723669.html 723669-ref.html
 
 == basic.xul basic-ref.xul
 random-if(Android) == basic-negcoord.xul basic-negcoord-ref.xul
 != blur.xul blur-notref.xul
 == color-inherit.xul color-inherit-ref.xul
 == multiple-noblur.xul multiple-noblur-ref.xul
-fuzzy-if(webrender&&gtkWidget,128-128,160-160) == blur-opacity.html blur-opacity-ref.html
+== blur-opacity.html blur-opacity-ref.html
 
 == basic.html basic-ref.html
 == basic-negcoord.html basic-negcoord-ref.html
 == basic-opacity.html basic-opacity-ref.html
 != blur.html blur-notref.html
 == color-inherit.html color-inherit-ref.html
 == color-parserorder.html color-parserorder-ref.html
 == decorations-multiple-zorder.html decorations-multiple-zorder-ref.html
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -448,17 +448,17 @@ void FontFaceSet::Add(FontFace& aFontFac
   for (const FontFaceRecord& rec : mNonRuleFaces) {
     MOZ_ASSERT(rec.mFontFace != &aFontFace,
                "FontFace should not occur in mNonRuleFaces twice");
   }
 #endif
 
   FontFaceRecord* rec = mNonRuleFaces.AppendElement();
   rec->mFontFace = &aFontFace;
-  rec->mSheetType = SheetType::Unknown;  // unused for mNonRuleFaces
+  rec->mOrigin = Nothing();
   rec->mLoadEventShouldFire =
       aFontFace.Status() == FontFaceLoadStatus::Unloaded ||
       aFontFace.Status() == FontFaceLoadStatus::Loading;
 
   mNonRuleFacesDirty = true;
   MarkUserFontSetDirty();
   mHasLoadingFontFacesIsDirty = true;
   CheckLoadingStarted();
@@ -721,17 +721,17 @@ bool FontFaceSet::UpdateRules(const nsTA
     if (!handledRules.EnsureInserted(rule)) {
       // rule was already present in the hashtable
       continue;
     }
     RefPtr<FontFace> f = ruleFaceMap.Get(rule);
     if (!f.get()) {
       f = FontFace::CreateForRule(GetParentObject(), this, rule);
     }
-    InsertRuleFontFace(f, aRules[i].mSheetType, oldRecords, modified);
+    InsertRuleFontFace(f, aRules[i].mOrigin, oldRecords, modified);
   }
 
   for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
     // Do the same for the non rule backed FontFace objects.
     InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
   }
 
   // Remove any residual families that have no font entries (i.e., they were
@@ -811,28 +811,29 @@ void FontFaceSet::InsertNonRuleFontFace(
 
   nsAtomCString family(fontFamily);
 
   // Just create a new font entry if we haven't got one already.
   if (!aFontFace->GetUserFontEntry()) {
     // XXX Should we be checking mUserFontSet->mLocalRulesUsed like
     // InsertRuleFontFace does?
     RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
-        family, aFontFace, SheetType::Doc);
+        family, aFontFace, StyleOrigin::Author);
     if (!entry) {
       return;
     }
     aFontFace->SetUserFontEntry(entry);
   }