Merge mozilla-central to inbound. a=merge CLOSED TREE
authorOana Pop Rus <opoprus@mozilla.com>
Tue, 22 Jan 2019 18:39:16 +0200
changeset 514903 96d9416e177cb1067d52340f2ced0596e3770e88
parent 514902 e2775f83185e13bc4eb050b8182fd59d00ca7867 (current diff)
parent 514845 af02238bdfe7b2de724e2bfc073736e095cafaac (diff)
child 514904 d1c7d5975a884c86a3c31bdd19049878494d5572
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.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 inbound. a=merge CLOSED TREE
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -83,16 +83,26 @@ class ClickHandlerChild extends ActorChi
           let isPrivateWin = ownerDoc.nodePrincipal.originAttributes.privateBrowsingId > 0;
           sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false, isPrivateWin);
           json.allowMixedContent = true;
         } catch (e) {}
       }
       json.originPrincipal = ownerDoc.nodePrincipal;
       json.triggeringPrincipal = ownerDoc.nodePrincipal;
 
+      // If a link element is clicked with middle button, user wants to open
+      // the link somewhere rather than pasting clipboard content.  Therefore,
+      // when it's clicked with middle button, we should prevent multiple
+      // actions here to avoid leaking clipboard content unexpectedly.
+      // Note that whether the link will work actually or not does not matter
+      // because in this case, user does not intent to paste clipboard content.
+      if (event.button === 1) {
+        event.preventMultipleActions();
+      }
+
       this.mm.sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
       this.mm.sendAsyncMessage("Content:Click", json);
     }
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -55,16 +55,19 @@ support-files = file_new_tab_page.html
 [browser_new_tab_in_privileged_process_pref.js]
 skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_new_web_tab_in_file_process_pref.js]
 skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_newwindow_tabstrip_overflow.js]
 [browser_open_newtab_start_observer_notification.js]
 [browser_opened_file_tab_navigated_to_web.js]
 [browser_overflowScroll.js]
+[browser_paste_event_at_middle_click_on_link.js]
+subsuite = clipboard
+support-files = file_anchor_elements.html
 [browser_pinnedTabs_clickOpen.js]
 [browser_pinnedTabs_closeByKeyboard.js]
 [browser_pinnedTabs.js]
 [browser_positional_attributes.js]
 skip-if = (verify && (os == 'win' || os == 'mac'))
 [browser_preloadedBrowser_zoom.js]
 [browser_reload_deleted_file.js]
 skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug 1421183, disabled on Linux/OSX for leaked windows
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_paste_event_at_middle_click_on_link.js
@@ -0,0 +1,74 @@
+"use strict";
+
+add_task(async function doCheckPasteEventAtMiddleClickOnAnchorElement() {
+  await SpecialPowers.pushPrefEnv({set: [
+    ["browser.tabs.opentabfor.middleclick", true],
+    ["middlemouse.paste", true],
+    ["middlemouse.contentLoadURL", false],
+    ["general.autoScroll", false],
+  ]});
+
+  await new Promise((resolve, reject) => {
+    SimpleTest.waitForClipboard("Text in the clipboard", () => {
+      Cc["@mozilla.org/widget/clipboardhelper;1"]
+        .getService(Ci.nsIClipboardHelper)
+        .copyString("Text in the clipboard");
+    }, resolve, () => {
+      ok(false, "Clipboard copy failed");
+      reject();
+    });
+  });
+
+  is(gBrowser.tabs.length, 1, "Number of tabs should be 1 at starting this test #1");
+
+  let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
+  pageURL = `${pageURL}file_anchor_elements.html`;
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageURL);
+
+  let pasteEventCount = 0;
+  BrowserTestUtils.addContentEventListener(gBrowser.selectedBrowser, "paste", () => { ++pasteEventCount; });
+
+  // Click the usual link.
+  ok(true, "Clicking on usual link...");
+  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/#a_with_href", true);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#a_with_href",
+                                                 {button: 1}, gBrowser.selectedBrowser);
+  let openTabForUsualLink = await newTabPromise;
+  is(openTabForUsualLink.linkedBrowser.currentURI.spec, "http://example.com/#a_with_href",
+     "Middle click should open site to correct url at clicking on usual link");
+  is(pasteEventCount, 0, "paste event should be suppressed when clicking on usual link");
+
+  // Click the link in editing host.
+  is(gBrowser.tabs.length, 3, "Number of tabs should be 3 at starting this test #2");
+  ok(true, "Clicking on editable link...");
+  await BrowserTestUtils.synthesizeMouseAtCenter("#editable_a_with_href",
+                                                 {button: 1}, gBrowser.selectedBrowser);
+  await TestUtils.waitForCondition(() => pasteEventCount >= 1,
+                                   "Waiting for paste event caused by clicking on editable link");
+  is(pasteEventCount, 1, "paste event should be suppressed when clicking on editable link");
+  is(gBrowser.tabs.length, 3, "Clicking on editable link shouldn't open new tab");
+
+  // Click the link in non-editable area in editing host.
+  ok(true, "Clicking on non-editable link in an editing host...");
+  newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/#non-editable_a_with_href", true);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#non-editable_a_with_href",
+                                                 {button: 1}, gBrowser.selectedBrowser);
+  let openTabForNonEditableLink = await newTabPromise;
+  is(openTabForNonEditableLink.linkedBrowser.currentURI.spec, "http://example.com/#non-editable_a_with_href",
+     "Middle click should open site to correct url at clicking on non-editable link in an editing host.");
+  is(pasteEventCount, 1, "paste event should be suppressed when clicking on non-editable link in an editing host");
+
+  // Click the <a> element without href attribute.
+  is(gBrowser.tabs.length, 4, "Number of tabs should be 4 at starting this test #3");
+  ok(true, "Clicking on anchor element without href...");
+  await BrowserTestUtils.synthesizeMouseAtCenter("#a_with_name",
+                                                 {button: 1}, gBrowser.selectedBrowser);
+  await TestUtils.waitForCondition(() => pasteEventCount >= 2,
+                                   "Waiting for paste event caused by clicking on anchor element without href");
+  is(pasteEventCount, 2, "paste event should be suppressed when clicking on anchor element without href");
+  is(gBrowser.tabs.length, 4, "Clicking on anchor element without href shouldn't open new tab");
+
+  BrowserTestUtils.removeTab(tab);
+  BrowserTestUtils.removeTab(openTabForUsualLink);
+  BrowserTestUtils.removeTab(openTabForNonEditableLink);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/file_anchor_elements.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <title>Testing whether paste event is fired at middle click on anchor elements</title>
+</head>
+<body>
+  <p>Here is an <a id="a_with_href" href="http://example.com/#a_with_href">anchor element</a></p>
+  <p contenteditable>Here is an <a id="editable_a_with_href" href="http://example.com/#editable_a_with_href">editable anchor element</a></p>
+  <p contenteditable>Here is <span contenteditable="false"><a id="non-editable_a_with_href" href="http://example.com/#non-editable_a_with_href">non-editable anchor element</a></span>
+  <p>Here is an <a id="a_with_name" name="a_with_name">anchor element without href</a></p>
+</body>
+</html>
\ No newline at end of file
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -49,16 +49,17 @@ skip-if = (os == "win" && ccov) # Bug 15
 [browser_aboutdebugging_routes.js]
 [browser_aboutdebugging_runtime_connection-prompt.js]
 [browser_aboutdebugging_runtime_usbclient_closed.js]
 [browser_aboutdebugging_select_network_runtime.js]
 [browser_aboutdebugging_select_page_with_serviceworker.js]
 [browser_aboutdebugging_serviceworker_multie10s.js]
 [browser_aboutdebugging_serviceworker_push.js]
 [browser_aboutdebugging_serviceworker_pushservice_url.js]
+[browser_aboutdebugging_serviceworker_runtime-page.js]
 [browser_aboutdebugging_sidebar_network_runtimes.js]
 [browser_aboutdebugging_sidebar_usb_runtime.js]
 [browser_aboutdebugging_sidebar_usb_runtime_connect.js]
 [browser_aboutdebugging_sidebar_usb_runtime_refresh.js]
 [browser_aboutdebugging_sidebar_usb_runtime_select.js]
 [browser_aboutdebugging_sidebar_usb_status.js]
 skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
 [browser_aboutdebugging_sidebar_usb_unknown_runtime.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_serviceworker_runtime-page.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from helper-serviceworker.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-serviceworker.js", this);
+/* import-globals-from helper-collapsibilities.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-collapsibilities.js", this);
+
+const SW_TAB_URL = URL_ROOT + "resources/service-workers/push-sw.html";
+const SW_URL = URL_ROOT + "resources/service-workers/push-sw.js";
+
+// This is a migration from:
+// https://searchfox.org/mozilla-central/source/devtools/client/aboutdebugging/test/browser_service_workers.js
+
+/**
+ * Test that service workers appear and dissapear from the runtime page when they
+ * are registered / unregistered.
+ */
+add_task(async function() {
+  prepareCollapsibilitiesTest();
+  await enableServiceWorkerDebugging();
+  const { document, tab, window } = await openAboutDebugging();
+  const store = window.AboutDebugging.store;
+
+  await selectThisFirefoxPage(document, store);
+
+  // check that SW list is empty
+  info("Check that the SW pane is empty");
+  let swPane = getDebugTargetPane("Service Workers", document);
+  ok(!swPane.querySelector(".js-debug-target-item"),
+    "SW list is empty");
+
+  // open a tab and register service worker
+  info("Register a service worker");
+  const swTab = await addTab(SW_TAB_URL);
+  // check that service worker is rendered
+  info("Wait for sw to appear in the debug pane list");
+  await waitUntil(() => {
+    swPane = getDebugTargetPane("Service Workers", document);
+    return swPane.querySelectorAll(".js-debug-target-item").length > 0;
+  });
+  swPane = getDebugTargetPane("Service Workers", document);
+  ok(swPane.querySelectorAll(".js-debug-target-item").length === 1,
+    "Service worker list has one element");
+  ok(swPane.querySelector(".js-debug-target-item").textContent.includes(SW_URL),
+    "Service worker list is the one we registered");
+
+  // unregister the service worker
+  info("Unregister service worker");
+  await unregisterServiceWorker(swTab);
+  // check that service worker is not rendered anymore
+  info("Wait for service worker to disappear");
+  await waitUntil(() => {
+    swPane = getDebugTargetPane("Service Workers", document);
+    return swPane.querySelectorAll(".js-debug-target-item").length === 0;
+  });
+
+  info("Remove tabs");
+  await removeTab(swTab);
+  await removeTab(tab);
+});
--- a/devtools/client/aboutdebugging-new/test/browser/head.js
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -125,16 +125,28 @@ function waitForDispatch(store, type) {
       run: (dispatch, getState, action) => {
         resolve(action);
       },
     });
   });
 }
 
 /**
+ * Navigate to "This Firefox"
+ */
+async function selectThisFirefoxPage(doc, store) {
+  info("Select This Firefox page");
+  doc.location.hash = "#/runtime/this-firefox";
+  info("Wait for requests to settle");
+  await waitForRequestsToSettle(store);
+  info("Wait for runtime page to be rendered");
+  await waitUntil(() => doc.querySelector(".js-runtime-page"));
+}
+
+/**
  * Navigate to the Connect page. Resolves when the Connect page is rendered.
  */
 async function selectConnectPage(doc) {
   const sidebarItems = doc.querySelectorAll(".js-sidebar-item");
   const connectSidebarItem = [...sidebarItems].find(element => {
     return element.textContent === "Connect";
   });
   ok(connectSidebarItem, "Sidebar contains a Connect item");
--- a/devtools/client/inspector/changes/components/CSSDeclaration.js
+++ b/devtools/client/inspector/changes/components/CSSDeclaration.js
@@ -22,17 +22,17 @@ class CSSDeclaration extends PureCompone
       className: "",
     };
   }
 
   render() {
     const { property, value, className } = this.props;
 
     return dom.div({ className: `declaration ${className}` },
-      dom.span({ className: "declaration-name theme-fg-color5"}, property),
+      dom.span({ className: "declaration-name theme-fg-color3"}, property),
       ":",
       dom.span({ className: "declaration-value theme-fg-color1"}, value),
       ";"
     );
   }
 }
 
 module.exports = CSSDeclaration;
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -970,17 +970,17 @@ PropertyView.prototype = {
     // Build the twisty expand/collapse
     this.matchedExpander = doc.createElementNS(HTML_NS, "div");
     this.matchedExpander.className = "computed-expander theme-twisty";
     this.matchedExpander.addEventListener("click", this.onMatchedToggle);
     nameContainer.appendChild(this.matchedExpander);
 
     // Build the style name element
     this.nameNode = doc.createElementNS(HTML_NS, "span");
-    this.nameNode.classList.add("computed-property-name", "theme-fg-color5");
+    this.nameNode.classList.add("computed-property-name", "theme-fg-color3");
     // Reset its tabindex attribute otherwise, if an ellipsis is applied
     // it will be reachable via TABing
     this.nameNode.setAttribute("tabindex", "");
     // Avoid english text (css properties) from being altered
     // by RTL mode
     this.nameNode.setAttribute("dir", "ltr");
     this.nameNode.textContent = this.nameNode.title = this.name;
     // Make it hand over the focus to the container
--- a/devtools/client/inspector/layout/components/ComputedProperty.js
+++ b/devtools/client/inspector/layout/components/ComputedProperty.js
@@ -81,17 +81,17 @@ class ComputedProperty extends PureCompo
           tabIndex: "0",
           ref: container => {
             this.container = container;
           },
         },
         dom.div({ className: "computed-property-name-container" },
           dom.div(
             {
-              className: "computed-property-name theme-fg-color5",
+              className: "computed-property-name theme-fg-color3",
               tabIndex: "",
               title: name,
               onClick: this.onFocus,
             },
             name
           )
         ),
         dom.div({ className: "computed-property-value-container" },
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -437,24 +437,24 @@ ElementEditor.prototype = {
 
     const inner = this.doc.createElement("span");
     inner.classList.add("editable");
     inner.setAttribute("tabindex", this.container.canFocus ? "0" : "-1");
     attr.appendChild(inner);
 
     const name = this.doc.createElement("span");
     name.classList.add("attr-name");
-    name.classList.add("theme-fg-color2");
+    name.classList.add("theme-fg-color1");
     inner.appendChild(name);
 
     inner.appendChild(this.doc.createTextNode('="'));
 
     const val = this.doc.createElement("span");
     val.classList.add("attr-value");
-    val.classList.add("theme-fg-color4");
+    val.classList.add("theme-fg-color2");
     inner.appendChild(val);
 
     inner.appendChild(this.doc.createTextNode('"'));
 
     // Double quotes need to be handled specially to prevent DOMParser failing.
     // name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
     // name="v'a"l'u"e" when editing -> name="v'a&quot;l'u&quot;e"
     let editValueDisplayed = attribute.value || "";
--- a/devtools/client/inspector/markup/views/read-only-editor.js
+++ b/devtools/client/inspector/markup/views/read-only-editor.js
@@ -10,17 +10,17 @@ const nodeConstants = require("devtools/
  * Creates an editor for non-editable nodes.
  */
 function ReadOnlyEditor(container, node) {
   this.container = container;
   this.markup = this.container.markup;
   this.buildMarkup();
 
   if (node.isPseudoElement) {
-    this.tag.classList.add("theme-fg-color5");
+    this.tag.classList.add("theme-fg-color3");
     this.tag.textContent = node.isBeforePseudoElement ? "::before" : "::after";
   } else if (node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE) {
     this.elt.classList.add("comment", "doctype");
     this.tag.textContent = node.doctypeString;
   } else if (node.isShadowRoot) {
     this.tag.textContent = `#shadow-root (${node.shadowRootMode})`;
   } else {
     this.tag.textContent = node.nodeName;
--- a/devtools/client/inspector/rules/components/Declaration.js
+++ b/devtools/client/inspector/rules/components/Declaration.js
@@ -62,17 +62,17 @@ class Declaration extends PureComponent 
           return (
             dom.li(
               {
                 key: `${name}${value}`,
                 className: "ruleview-computed" +
                            (isOverridden ? " ruleview-overridden" : ""),
               },
               dom.span({ className: "ruleview-namecontainer" },
-                dom.span({ className: "ruleview-propertyname theme-fg-color5" }, name),
+                dom.span({ className: "ruleview-propertyname theme-fg-color3" }, name),
                 ": "
               ),
               dom.span({ className: "ruleview-propertyvaluecontainer" },
                 dom.span({ className: "ruleview-propertyvalue theme-fg-color1" }, value),
                 ";"
               )
             )
           );
@@ -98,17 +98,17 @@ class Declaration extends PureComponent 
         overriddenComputedProperties.map(({ name, value }) => {
           return (
             dom.li(
               {
                 key: `${name}${value}`,
                 className: "ruleview-overridden-item ruleview-overridden",
               },
               dom.span({ className: "ruleview-namecontainer" },
-                dom.span({ className: "ruleview-propertyname theme-fg-color5" }, name),
+                dom.span({ className: "ruleview-propertyname theme-fg-color3" }, name),
                 ": "
               ),
               dom.span({ className: "ruleview-propertyvaluecontainer" },
                 dom.span({ className: "ruleview-propertyvalue theme-fg-color1" }, value),
                 ";"
               )
             )
           );
@@ -139,17 +139,17 @@ class Declaration extends PureComponent 
         dom.div({ className: "ruleview-propertycontainer" },
           dom.div({
             className: "ruleview-enableproperty theme-checkbox" +
                         (isEnabled ? " checked" : ""),
             onClick: this.onToggleDeclarationClick,
             tabIndex: -1,
           }),
           dom.span({ className: "ruleview-namecontainer" },
-            dom.span({ className: "ruleview-propertyname theme-fg-color5" }, name),
+            dom.span({ className: "ruleview-propertyname theme-fg-color3" }, name),
             ": "
           ),
           dom.span({
             className: "ruleview-expander theme-twisty" +
                        (this.state.isComputedListExpanded ? " open" : ""),
             onClick: this.onComputedExpanderClick,
             style: { display: computedProperties.length ? "inline-block" : "none" },
           }),
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -137,17 +137,17 @@ TextPropertyEditor.prototype = {
 
     this.nameContainer = createChild(this.container, "span", {
       class: "ruleview-namecontainer",
     });
 
     // Property name, editable when focused.  Property name
     // is committed when the editor is unfocused.
     this.nameSpan = createChild(this.nameContainer, "span", {
-      class: "ruleview-propertyname theme-fg-color5",
+      class: "ruleview-propertyname theme-fg-color3",
       tabindex: this.ruleEditor.isEditable ? "0" : "-1",
     });
 
     appendText(this.nameContainer, ": ");
 
     // Click to expand the computed properties of the text property.
     this.expander = createChild(this.container, "span", {
       class: "ruleview-expander theme-twisty",
@@ -704,17 +704,17 @@ TextPropertyEditor.prototype = {
       li.classList.add("ruleview-overridden");
     }
 
     const nameContainer = createChild(li, "span", {
       class: "ruleview-namecontainer",
     });
 
     createChild(nameContainer, "span", {
-      class: "ruleview-propertyname theme-fg-color5",
+      class: "ruleview-propertyname theme-fg-color3",
       textContent: computed.name,
     });
     appendText(nameContainer, ": ");
 
     const outputParser = this.ruleView._outputParser;
     const frag = outputParser.parseCssProperty(
       computed.name, computed.value, {
         colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -274,16 +274,19 @@ pref("devtools.webconsole.timestampMessa
 pref("devtools.webconsole.sidebarToggle", true);
 #else
 pref("devtools.webconsole.sidebarToggle", false);
 #endif
 
 // Enable CodeMirror in the JsTerm
 pref("devtools.webconsole.jsterm.codeMirror", true);
 
+// Enable editor mode in the console.
+pref("devtools.webconsole.input.editor", false);
+
 // Disable the new performance recording panel by default
 pref("devtools.performance.new-panel-enabled", false);
 
 // Enable client-side mapping service for source maps
 pref("devtools.source-map.client-service.enabled", true);
 
 // The number of lines that are displayed in the web console.
 pref("devtools.hud.loglimit", 10000);
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -79,17 +79,16 @@ body {
   color: #6B89FF;
 }
 
 .CodeMirror-Tern-completion-number:before {
   background-color: #5c9966;
 }
 
 .theme-fg-color1,
-.theme-fg-color2,
 .cm-s-mozilla .cm-attribute,
 .cm-s-mozilla .cm-builtin,
 .cm-s-mozilla .cm-keyword,
 .variables-view-variable > .title > .name {
   color: var(--theme-highlight-red);
 }
 
 .cm-s-mozilla .cm-def,
@@ -105,30 +104,29 @@ body {
   color: var(--theme-highlight-green);
 }
 
 .CodeMirror-Tern-completion-object:before {
   background-color: #3689b2;
 }
 
 .theme-fg-color3,
-.theme-fg-color5,
 .cm-s-mozilla .cm-tag,
 .cm-s-mozilla .cm-header,
 .cm-s-mozilla .cm-bracket,
 .cm-s-mozilla .cm-qualifier,
 .variables-view-property > .title > .name {
   color: var(--theme-highlight-blue);
 }
 
 .CodeMirror-Tern-completion-array:before {
   background-color: var(--theme-highlight-bluegrey);
 }
 
-.theme-fg-color4 {
+.theme-fg-color2 {
   color: var(--theme-highlight-purple);
 }
 
 .cm-s-mozilla .cm-string,
 .cm-s-mozilla .cm-string-2,
 .variable-or-property .token-string,
 .CodeMirror-Tern-farg {
   color: #6B89FF;
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -79,17 +79,16 @@ body {
   color: var(--theme-highlight-purple);
 }
 
 .CodeMirror-Tern-completion-number:before {
   background-color: hsl(72,100%,27%);
 }
 
 .theme-fg-color1,
-.theme-fg-color2,
 .cm-s-mozilla .cm-attribute,
 .cm-s-mozilla .cm-builtin,
 .cm-s-mozilla .cm-keyword,
 .variables-view-variable > .title > .name {
   color: var(--theme-highlight-red);
 }
 
 .cm-s-mozilla .cm-def,
@@ -105,30 +104,29 @@ body {
   color: var(--theme-highlight-green);
 }
 
 .CodeMirror-Tern-completion-object:before {
   background-color: hsl(208,56%,40%);
 }
 
 .theme-fg-color3,
-.theme-fg-color5,
 .cm-s-mozilla .cm-tag,
 .cm-s-mozilla .cm-header,
 .cm-s-mozilla .cm-bracket,
 .cm-s-mozilla .cm-qualifier,
 .variables-view-property > .title > .name {
   color: var(--theme-highlight-blue);
 }
 
 .CodeMirror-Tern-completion-array:before {
   background-color: var(--theme-highlight-bluegrey);
 }
 
-.theme-fg-color4,
+.theme-fg-color2,
 .cm-s-mozilla .cm-string,
 .cm-s-mozilla .cm-string-2,
 .variable-or-property .token-string,
 .CodeMirror-Tern-farg {
   color: var(--theme-highlight-purple);
 }
 
 .CodeMirror-Tern-completion-string:before,
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -387,18 +387,16 @@ ul.children + .tag-line::before {
 
 /* Selected nodes in the tree should have light selected text.
    theme-selected doesn't work in this case since the text is a
    sibling of the class, not a child. */
 .theme-selected ~ .editor,
 .theme-selected ~ .editor.comment,
 .theme-selected ~ .editor .theme-fg-color1,
 .theme-selected ~ .editor .theme-fg-color2,
-.theme-selected ~ .editor .theme-fg-color3,
-.theme-selected ~ .editor .theme-fg-color4,
-.theme-selected ~ .editor .theme-fg-color5 {
+.theme-selected ~ .editor .theme-fg-color3 {
   color: var(--theme-selection-color);
 }
 
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -2944,17 +2944,21 @@ void ScopedContentTraversal::Prev() {
     StyleChildrenIterator iter(parent, false /* aStartAtBeginning */);
     last = iter.GetPreviousChild();
   }
 
   // If parent is mOwner and no previous sibling remains, END traversal
   SetCurrent(parent == mOwner ? nullptr : parent);
 }
 
-nsIContent* nsFocusManager::FindOwner(nsIContent* aContent) {
+/**
+ * Returns scope owner of aContent.
+ * A scope owner is either a document root, shadow host, or slot.
+ */
+static nsIContent* FindOwner(nsIContent* aContent) {
   nsIContent* currentContent = aContent;
   while (currentContent) {
     nsIContent* parent = currentContent->GetFlattenedTreeParent();
     if (!parent) {
       // Document root
       Document* doc = currentContent->GetUncomposedDoc();
       if (doc && doc->GetRootElement() == currentContent) {
         return currentContent;
@@ -2969,18 +2973,24 @@ nsIContent* nsFocusManager::FindOwner(ns
     }
 
     currentContent = parent;
   }
 
   return nullptr;
 }
 
-int32_t nsFocusManager::HostOrSlotTabIndexValue(nsIContent* aContent,
-                                                bool* aIsFocusable) {
+/**
+ * Host and Slot elements need to be handled as if they had tabindex 0 even
+ * when they don't have the attribute. This is a helper method to get the
+ * right value for focus navigation. If aIsFocusable is passed, it is set to
+ * true if the element itself is focusable.
+ */
+static int32_t HostOrSlotTabIndexValue(const nsIContent* aContent,
+                                       bool* aIsFocusable = nullptr) {
   MOZ_ASSERT(IsHostOrSlot(aContent));
 
   if (aIsFocusable) {
     *aIsFocusable = false;
     nsIFrame* frame = aContent->GetPrimaryFrame();
     if (frame) {
       int32_t tabIndex;
       frame->IsFocusable(&tabIndex, 0);
@@ -3000,20 +3010,19 @@ int32_t nsFocusManager::HostOrSlotTabInd
 
   return -1;
 }
 
 nsIContent* nsFocusManager::GetNextTabbableContentInScope(
     nsIContent* aOwner, nsIContent* aStartContent,
     nsIContent* aOriginalStartContent, bool aForward, int32_t aCurrentTabIndex,
     bool aIgnoreTabIndex, bool aForDocumentNavigation, bool aSkipOwner) {
-  // Return shadow host at first for forward navigation if its tabindex
-  // is non-negative
-  bool skipOwner = aSkipOwner || !aOwner->GetShadowRoot();
-  if (!skipOwner && (aForward && aOwner == aStartContent)) {
+  MOZ_ASSERT(IsHostOrSlot(aOwner), "Scope owner should be host or slot");
+
+  if (!aSkipOwner && (aForward && aOwner == aStartContent)) {
     int32_t tabIndex = -1;
     nsIFrame* frame = aOwner->GetPrimaryFrame();
     if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
       return aOwner;
     }
   }
 
   //
@@ -3105,38 +3114,39 @@ nsIContent* nsFocusManager::GetNextTabba
       break;
     }
 
     // Continue looking for next highest priority tabindex
     aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
     contentTraversal.Reset();
   }
 
-  // Return shadow host at last for backward navigation if its tabindex
+  // Return scope owner at last for backward navigation if its tabindex
   // is non-negative
-  if (!skipOwner && !aForward) {
+  if (!aSkipOwner && !aForward) {
     int32_t tabIndex = -1;
     nsIFrame* frame = aOwner->GetPrimaryFrame();
     if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
       return aOwner;
     }
   }
 
   return nullptr;
 }
 
 nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
-    nsIContent** aStartContent, nsIContent* aOriginalStartContent,
-    bool aForward, int32_t* aCurrentTabIndex, bool aIgnoreTabIndex,
-    bool aForDocumentNavigation) {
+    nsIContent* aStartOwner, nsIContent** aStartContent,
+    nsIContent* aOriginalStartContent, bool aForward, int32_t* aCurrentTabIndex,
+    bool aIgnoreTabIndex, bool aForDocumentNavigation) {
+  MOZ_ASSERT(aStartOwner == FindOwner(*aStartContent),
+             "aStartOWner should be the scope owner of aStartContent");
+  MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot");
+
+  nsIContent* owner = aStartOwner;
   nsIContent* startContent = *aStartContent;
-  nsIContent* owner = FindOwner(startContent);
-  MOZ_ASSERT(owner, "focus navigation scope owner not in document");
-  MOZ_ASSERT(IsHostOrSlot(owner), "scope owner should be host or slot");
-
   while (IsHostOrSlot(owner)) {
     int32_t tabIndex = 0;
     if (IsHostOrSlot(startContent)) {
       tabIndex = HostOrSlotTabIndexValue(startContent);
     } else {
       startContent->IsFocusable(&tabIndex);
     }
     nsIContent* contentToFocus = GetNextTabbableContentInScope(
@@ -3153,43 +3163,46 @@ nsIContent* nsFocusManager::GetNextTabba
   // If not found in shadow DOM, search from the top level shadow host in light
   // DOM
   *aStartContent = startContent;
   *aCurrentTabIndex = HostOrSlotTabIndexValue(startContent);
 
   return nullptr;
 }
 
-static nsIContent* GetTopLevelHost(nsIContent* aContent) {
-  nsIContent* topLevelhost = nullptr;
+static nsIContent* GetTopLevelScopeOwner(nsIContent* aContent) {
+  nsIContent* topLevelScopeOwner = nullptr;
   while (aContent) {
     if (HTMLSlotElement* slot = aContent->GetAssignedSlot()) {
       aContent = slot;
     } else if (ShadowRoot* shadowRoot = aContent->GetContainingShadow()) {
       aContent = shadowRoot->Host();
-      topLevelhost = aContent;
+      topLevelScopeOwner = aContent;
     } else {
+      if (HTMLSlotElement::FromNode(aContent)) {
+        topLevelScopeOwner = aContent;
+      }
       aContent = aContent->GetParent();
     }
   }
 
-  return topLevelhost;
+  return topLevelScopeOwner;
 }
 
 nsresult nsFocusManager::GetNextTabbableContent(
     nsIPresShell* aPresShell, nsIContent* aRootContent,
     nsIContent* aOriginalStartContent, nsIContent* aStartContent, bool aForward,
     int32_t aCurrentTabIndex, bool aIgnoreTabIndex, bool aForDocumentNavigation,
     nsIContent** aResultContent) {
   *aResultContent = nullptr;
 
   nsCOMPtr<nsIContent> startContent = aStartContent;
   if (!startContent) return NS_OK;
 
-  nsIContent* currentTopLevelHost = GetTopLevelHost(aStartContent);
+  nsIContent* currentTopLevelScopeOwner = GetTopLevelScopeOwner(aStartContent);
 
   LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
   LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
 
   // If aStartContent is a shadow host or slot in forward navigation,
   // search in scope owned by aStartContent
   if (aForward && IsHostOrSlot(aStartContent)) {
     nsIContent* contentToFocus = GetNextTabbableContentInScope(
@@ -3204,18 +3217,18 @@ nsresult nsFocusManager::GetNextTabbable
 
   // If aStartContent is not in a scope owned by the root element
   // (i.e. aStartContent is already in shadow DOM),
   // search from scope including aStartContent
   nsIContent* rootElement = aRootContent->OwnerDoc()->GetRootElement();
   nsIContent* owner = FindOwner(aStartContent);
   if (owner && rootElement != owner) {
     nsIContent* contentToFocus = GetNextTabbableContentInAncestorScopes(
-        &aStartContent, aOriginalStartContent, aForward, &aCurrentTabIndex,
-        aIgnoreTabIndex, aForDocumentNavigation);
+        owner, &aStartContent, aOriginalStartContent, aForward,
+        &aCurrentTabIndex, aIgnoreTabIndex, aForDocumentNavigation);
     if (contentToFocus) {
       NS_ADDREF(*aResultContent = contentToFocus);
       return NS_OK;
     }
   }
 
   // If we reach here, it means no next tabbable content in shadow DOM.
   // We need to continue searching in light DOM, starting at the top level
@@ -3300,41 +3313,41 @@ nsresult nsFocusManager::GetNextTabbable
         }
 
         frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
       }
     }
 
     // Walk frames to find something tabbable matching aCurrentTabIndex
     while (frame) {
-      // Try to find the topmost Shadow DOM host, since we want to
-      // skip Shadow DOM in frame traversal.
+      // Try to find the topmost scope owner, since we want to skip the node
+      // that is not owned by document in frame traversal.
       nsIContent* currentContent = frame->GetContent();
-      nsIContent* oldTopLevelHost = currentTopLevelHost;
-      if (oldTopLevelHost != currentContent) {
-        currentTopLevelHost = GetTopLevelHost(currentContent);
+      nsIContent* oldTopLevelScopeOwner = currentTopLevelScopeOwner;
+      if (oldTopLevelScopeOwner != currentContent) {
+        currentTopLevelScopeOwner = GetTopLevelScopeOwner(currentContent);
       } else {
-        currentTopLevelHost = currentContent;
+        currentTopLevelScopeOwner = currentContent;
       }
-      if (currentTopLevelHost) {
-        if (currentTopLevelHost == oldTopLevelHost) {
-          // We're within Shadow DOM, continue.
+      if (currentTopLevelScopeOwner) {
+        if (currentTopLevelScopeOwner == oldTopLevelScopeOwner) {
+          // We're within non-document scope, continue.
           do {
             if (aForward) {
               frameTraversal->Next();
             } else {
               frameTraversal->Prev();
             }
             frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
             // For the usage of GetPrevContinuation, see the comment
             // at the end of while (frame) loop.
           } while (frame && frame->GetPrevContinuation());
           continue;
         }
-        currentContent = currentTopLevelHost;
+        currentContent = currentTopLevelScopeOwner;
       }
 
       // For document navigation, check if this element is an open panel. Since
       // panels aren't focusable (tabIndex would be -1), we'll just assume that
       // for document navigation, the tabIndex is 0.
       if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
           currentContent->IsXULElement(nsGkAtoms::panel)) {
         nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -416,31 +416,16 @@ class nsFocusManager final : public nsIF
    * beginning (or end) of the starting document.
    */
   nsresult DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
                                        nsIContent* aStart, int32_t aType,
                                        bool aNoParentTraversal,
                                        nsIContent** aNextContent);
 
   /**
-   * Returns scope owner of aContent.
-   * A scope owner is either a document root, shadow host, or slot.
-   */
-  nsIContent* FindOwner(nsIContent* aContent);
-
-  /**
-   * Host and Slot elements need to be handled as if they had tabindex 0 even
-   * when they don't have the attribute. This is a helper method to get the
-   * right value for focus navigation. If aIsFocusable is passed, it is set to
-   * true if the element itself is focusable.
-   */
-  int32_t HostOrSlotTabIndexValue(nsIContent* aContent,
-                                  bool* aIsFocusable = nullptr);
-
-  /**
    * Retrieve the next tabbable element in scope owned by aOwner, using
    * focusability and tabindex to determine the tab order.
    *
    * aOwner is the owner of scope to search in.
    *
    * aStartContent is the starting point for this call of this method.
    *
    * aOriginalStartContent is the initial starting point for sequential
@@ -470,16 +455,18 @@ class nsFocusManager final : public nsIF
       int32_t aCurrentTabIndex, bool aIgnoreTabIndex,
       bool aForDocumentNavigation, bool aSkipOwner);
 
   /**
    * Retrieve the next tabbable element in scope including aStartContent
    * and the scope's ancestor scopes, using focusability and tabindex to
    * determine the tab order.
    *
+   * aStartOwner is the scope owner of the aStartContent.
+   *
    * aStartContent an in/out paremeter. It as input is the starting point
    * for this call of this method; as output it is the shadow host in
    * light DOM if the next tabbable element is not found in shadow DOM,
    * in order to continue searching in light DOM.
    *
    * aOriginalStartContent is the initial starting point for sequential
    * navigation.
    *
@@ -497,18 +484,19 @@ class nsFocusManager final : public nsIF
    * documents.
    *
    * NOTE:
    *   Consider the method searches upwards in all shadow host- or slot-rooted
    *   flattened subtrees that contains aStartContent as non-root, except
    *   the flattened subtree rooted at shadow host in light DOM.
    */
   nsIContent* GetNextTabbableContentInAncestorScopes(
-      nsIContent** aStartContent, nsIContent* aOriginalStartContent,
-      bool aForward, int32_t* aCurrentTabIndex, bool aIgnoreTabIndex,
+      nsIContent* aStartOwner, nsIContent** aStartContent,
+      nsIContent* aOriginalStartContent, bool aForward,
+      int32_t* aCurrentTabIndex, bool aIgnoreTabIndex,
       bool aForDocumentNavigation);
 
   /**
    * Retrieve the next tabbable element within a document, using focusability
    * and tabindex to determine the tab order. The element is returned in
    * aResultContent.
    *
    * aRootContent is the root node -- nodes above this will not be examined.
--- a/dom/base/test/file_bug1453693.html
+++ b/dom/base/test/file_bug1453693.html
@@ -528,26 +528,148 @@
         synthesizeKey("KEY_Tab", {shiftKey: true});
         opener.is(document.activeElement, document.body.firstChild,
                   "body's first child should have focus.");
 
         host0.remove();
         host1.remove();
       }
 
+      function testTabbingThroughSlotInLightDOM() {
+        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
+
+        var input0 = document.createElement("input");
+        input0.onfocus = focusLogger;
+        document.body.appendChild(input0);
+
+        var slot1 = document.createElement("slot");
+        document.body.appendChild(slot1);
+
+        var input10 = document.createElement("input");
+        input10.onfocus = focusLogger;
+        slot1.appendChild(input10);
+
+        var input11 = document.createElement("input");
+        input11.onfocus = focusLogger;
+        slot1.appendChild(input11);
+
+        var input2 = document.createElement("input");
+        input2.onfocus = focusLogger;
+        document.body.appendChild(input2);
+
+        document.body.offsetLeft;
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input0, "Should have focused input element. (1)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input2, "Should have focused input element. (4)");
+
+        // Backwards
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, input0, "Should have focused input element. (7)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(document.activeElement, document.body.firstChild,
+                  "body's first child should have focus.");
+
+        input0.remove();
+        slot1.remove();
+        input2.remove();
+      }
+
+      function testTabbingThroughFocusableSlotInLightDOM() {
+        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
+
+        var slot0 = document.createElement("slot");
+        slot0.tabIndex = 0;
+        slot0.setAttribute("style", "display: inline;");
+        slot0.onfocus = focusLogger;
+        document.body.appendChild(slot0);
+
+        var slot00 = document.createElement("slot");
+        slot00.tabIndex = 0;
+        slot00.setAttribute("style", "display: inline;");
+        slot00.onfocus = focusLogger;
+        slot0.appendChild(slot00);
+
+        var input000 = document.createElement("input");
+        input000.onfocus = focusLogger;
+        slot00.appendChild(input000);
+
+        var input01 = document.createElement("input");
+        input01.onfocus = focusLogger;
+        slot0.appendChild(input01);
+
+        var input1 = document.createElement("input");
+        input1.onfocus = focusLogger;
+        document.body.appendChild(input1);
+
+        document.body.offsetLeft;
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)");
+
+        synthesizeKey("KEY_Tab");
+        opener.is(lastFocusTarget, input1, "Should have focused input element. (5)");
+
+        // Backwards
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)");
+
+        synthesizeKey("KEY_Tab", {shiftKey: true});
+        opener.is(document.activeElement, document.body.firstChild,
+                  "body's first child should have focus.");
+
+        slot0.remove();
+        input1.remove();
+      }
+
       function runTest() {
 
         testTabbingThroughShadowDOMWithTabIndexes();
         testTabbingThroughSimpleShadowDOM();
         testTabbingThroughNestedShadowDOM();
         testTabbingThroughDisplayContentsHost();
         testTabbingThroughLightDOMShadowDOMLightDOM();
         testFocusableHost();
         testShiftTabbingThroughFocusableHost();
         testTabbingThroughNestedSlot();
+        testTabbingThroughSlotInLightDOM();
+        testTabbingThroughFocusableSlotInLightDOM();
 
         opener.didRunTests();
         window.close();
       }
 
       function init() {
         SimpleTest.waitForFocus(runTest);
       }
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -216,16 +216,20 @@ class Event : public nsISupports, public
   bool DefaultPreventedByChrome() const {
     return mEvent->mFlags.mDefaultPreventedByChrome;
   }
 
   bool DefaultPreventedByContent() const {
     return mEvent->mFlags.mDefaultPreventedByContent;
   }
 
+  void PreventMultipleActions() {
+    mEvent->mFlags.mMultipleActionsPrevented = true;
+  }
+
   bool MultipleActionsPrevented() const {
     return mEvent->mFlags.mMultipleActionsPrevented;
   }
 
   bool ReturnValue(CallerType aCallerType) const;
 
   void SetReturnValue(bool aReturnValue, CallerType aCallerType);
 
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -76,16 +76,17 @@ partial interface Event {
    * are retargeted to their parent node when they happen over text nodes (bug
    * 185889), and in that case .target will show the parent and
    * .explicitOriginalTarget will show the text node.
    * .explicitOriginalTarget differs from .originalTarget in that it will never
    * contain anonymous content.
    */
   readonly attribute EventTarget? explicitOriginalTarget;
   [ChromeOnly] readonly attribute EventTarget? composedTarget;
+  [ChromeOnly] void preventMultipleActions();
   [ChromeOnly] readonly attribute boolean multipleActionsPrevented;
   [ChromeOnly] readonly attribute boolean isSynthesized;
 };
 
 dictionary EventInit {
   boolean bubbles = false;
   boolean cancelable = false;
   boolean composed = false;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1026,16 +1026,20 @@ void HttpChannelChild::MaybeDivertOnStop
 
 void HttpChannelChild::OnStopRequest(
     const nsresult& channelStatus, const ResourceTimingStruct& timing,
     const nsHttpHeaderArray& aResponseTrailers) {
   LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
        static_cast<uint32_t>(channelStatus)));
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (mOnStopRequestCalled && !mIPCOpen) {
+    return;
+  }
+
   if (mDivertingToParent) {
     MOZ_RELEASE_ASSERT(
         !mFlushedForDiversion,
         "Should not be processing any more callbacks from parent!");
 
     SendDivertOnStopRequest(channelStatus);
     return;
   }
@@ -1182,39 +1186,42 @@ void HttpChannelChild::CollectOMTTelemet
 void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
                                        nsresult aChannelStatus,
                                        nsISupports* aContext) {
   AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
   LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mIsPending);
 
-  // NB: We use aChannelStatus here instead of mStatus because if there was an
-  // nsCORSListenerProxy on this request, it will override the tracking
-  // protection's return value.
-  if (aChannelStatus == NS_ERROR_TRACKING_URI ||
-      aChannelStatus == NS_ERROR_MALWARE_URI ||
-      aChannelStatus == NS_ERROR_UNWANTED_URI ||
-      aChannelStatus == NS_ERROR_BLOCKED_URI ||
-      aChannelStatus == NS_ERROR_HARMFUL_URI ||
-      aChannelStatus == NS_ERROR_PHISHING_URI) {
-    nsCString list, provider, fullhash;
-
-    nsresult rv = GetMatchedList(list);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    rv = GetMatchedProvider(provider);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    rv = GetMatchedFullHash(fullhash);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list, provider,
-                                           fullhash);
-  }
+  auto checkForBlockedContent = [&]() {
+    // NB: We use aChannelStatus here instead of mStatus because if there was an
+    // nsCORSListenerProxy on this request, it will override the tracking
+    // protection's return value.
+    if (aChannelStatus == NS_ERROR_TRACKING_URI ||
+        aChannelStatus == NS_ERROR_MALWARE_URI ||
+        aChannelStatus == NS_ERROR_UNWANTED_URI ||
+        aChannelStatus == NS_ERROR_BLOCKED_URI ||
+        aChannelStatus == NS_ERROR_HARMFUL_URI ||
+        aChannelStatus == NS_ERROR_PHISHING_URI) {
+      nsCString list, provider, fullhash;
+
+      nsresult rv = GetMatchedList(list);
+      NS_ENSURE_SUCCESS_VOID(rv);
+
+      rv = GetMatchedProvider(provider);
+      NS_ENSURE_SUCCESS_VOID(rv);
+
+      rv = GetMatchedFullHash(fullhash);
+      NS_ENSURE_SUCCESS_VOID(rv);
+
+      UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
+                                             provider, fullhash);
+    }
+  };
+  checkForBlockedContent();
 
   MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
 
   // In theory mListener should not be null, but in practice sometimes it is.
   MOZ_ASSERT(mListener);
   if (mListener) {
     mListener->OnStopRequest(aRequest, aContext, mStatus);
   }
@@ -3779,16 +3786,25 @@ mozilla::ipc::IPCResult HttpChannelChild
 
 void HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
   MOZ_ASSERT(NS_IsMainThread());
 
   // OnStartRequest might be dropped if IPDL is destroyed abnormally
   // and BackgroundChild might have pending IPC messages.
   // Clean up BackgroundChild at this time to prevent memleak.
   if (aWhy != Deletion) {
+    // The actor tree might get destroyed before we get the OnStopRequest.
+    // So if we didn't get it, we send it here in order to prevent any leaks.
+    // Ocasionally we will get the OnStopRequest message after this, in which
+    // case we just ignore it as we've already cleared the listener.
+    if (!mOnStopRequestCalled) {
+      DoPreOnStopRequest(NS_ERROR_ABORT);
+      DoOnStopRequest(this, NS_ERROR_ABORT, mListenerContext);
+    }
+
     CleanupBackgroundChannel();
   }
 }
 
 mozilla::ipc::IPCResult HttpChannelChild::RecvLogBlockedCORSRequest(
     const nsString& aMessage, const nsCString& aCategory) {
   Unused << LogBlockedCORSRequest(aMessage, aCategory);
   return IPC_OK();
--- a/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py
+++ b/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py
@@ -248,17 +248,17 @@ class TbplFormatter(BaseFormatter):
 
             return failure_line + info_line
 
         sections = ["TEST-%s" % data['status'], test_id]
         if duration_msg:
             sections.append(duration_msg)
         rv.append(' | '.join(sections) + '\n')
         if screenshot_msg:
-            rv.append(screenshot_msg[1:])
+            rv.append(screenshot_msg[1:] + "\n")
         return "".join(rv)
 
     def suite_end(self, data):
         start_time = self.suite_start_time
         time = int((data["time"] - start_time) / 1000)
 
         return "SUITE-END | took %is\n" % time
 
--- a/testing/raptor/raptor/playback/mitmproxy.py
+++ b/testing/raptor/raptor/playback/mitmproxy.py
@@ -391,16 +391,17 @@ class MitmproxyAndroid(Mitmproxy):
            `certutil -N -d sql:<path to profile> --empty-password`
         2. Import the mitmproxy certificate into the database, i.e.:
            `certutil -A -d sql:<path to profile> -n "some nickname" -t TC,, -a -i <path to CA.pem>`
         """
         self.CERTUTIL_SLEEP = 10
         if self.config['run_local']:
             # when running locally, it is found in the Firefox desktop build (..obj../dist/bin)
             self.certutil = os.path.join(self.config['obj_path'], 'dist', 'bin')
+            os.environ['LD_LIBRARY_PATH'] = self.certutil
         else:
             # must download certutil inside hostutils via tooltool; use this manifest:
             # mozilla-central/testing/config/tooltool-manifests/linux64/hostutils.manifest
             # after it will be found here inside the worker/bitbar container:
             # /builds/worker/workspace/build/hostutils/host-utils-66.0a1.en-US.linux-x86_64
             LOG.info("downloading certutil binary (hostutils)")
 
             # get path to the hostutils tooltool manifest; was set earlier in
--- a/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
+++ b/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
@@ -1,2 +1,1 @@
 lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, EntrySlotOrCreate, Realloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::alloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceMainThread::CreateNavigationTimingEntry, mozilla::net::nsStandardURL::TemplatedMutator]
-leak-threshold: [tab:358400]
--- a/widget/windows/nsDataObj.cpp
+++ b/widget/windows/nsDataObj.cpp
@@ -856,17 +856,16 @@ nsDataObj::GetDib(const nsACString& inFl
   }
 
   // We don't want the file header.
   src += BFH_LENGTH;
   size -= BFH_LENGTH;
 
   HGLOBAL glob = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);
   if (!glob) {
-    DWORD err = ::GetLastError();
     return E_FAIL;
   }
 
   char* dst = (char*)::GlobalLock(glob);
   ::CopyMemory(dst, src, size);
   ::GlobalUnlock(glob);
 
   aSTG.hGlobal = glob;