Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
authorRazvan Maries <rmaries@mozilla.com>
Wed, 05 Dec 2018 00:01:48 +0200
changeset 508618 8860aa6f15ed24ca1f1ae58a6876eb8e7bc6d061
parent 508617 9883f547e28008de9ee595492c8732d9cdc4e294 (current diff)
parent 508557 0a65dda20ade2c24bf42fb22bfd7169b320b732e (diff)
child 508619 84a17995db6f2ff316d14c65b0cf8743425c2173
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.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. a=merge on a CLOSED TREE
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,4 +1,9 @@
 # Bug 1204606 - Reformat of dom/media r=jya
 # https://hg.mozilla.org/mozilla-central/rev/0ceae9db9ec0be18daa1a279511ad305723185d4
 abd6d77c618998827e5ffc3dab12f1a34d6ed03d # cinnabar
 804b8b8883ba2a6795b0fcf65ebcb18306b6416b # gecko-dev
+
+# Bug 1511181 - Reformat everything to the Google coding style r=ehsan
+# https://hg.mozilla.org/mozilla-central/rev/6f3709b3878117466168c40affa7bca0b60cf75b
+0e0308d10a5fd4a8dcf0601978776342a2abf2df # cinnabar
+265e6721798a455604328ed5262f430cfcc37c2f # gecko-dev
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1913,16 +1913,17 @@ dependencies = [
 name = "procedural-masquerade"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "profiler_helper"
 version = "0.1.0"
 dependencies = [
+ "goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "object 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "pulse"
 version = "0.2.0"
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -28,17 +28,17 @@
           data-category="paneGeneral"
           hidden="true">
   <label><html:h2 data-l10n-id="startup-header"/></label>
 
 #ifdef MOZ_DEV_EDITION
   <vbox id="separateProfileBox">
     <checkbox id="separateProfileMode"
               data-l10n-id="separate-profile-mode"/>
-    <hbox id="sync-dev-edition-root" lign="center" class="indent" hidden="true">
+    <hbox id="sync-dev-edition-root" align="center" class="indent" hidden="true">
       <label id="useFirefoxSync" data-l10n-id="use-firefox-sync"/>
       <deck id="getStarted">
         <label class="text-link" data-l10n-id="get-started-not-logged-in"/>
         <label class="text-link" data-l10n-id="get-started-configured"/>
       </deck>
     </hbox>
   </vbox>
 #endif
--- a/devtools/client/inspector/changes/reducers/changes.js
+++ b/devtools/client/inspector/changes/reducers/changes.js
@@ -211,28 +211,40 @@ const reducers = {
       }
     }
 
     if (change.remove && change.remove.length) {
       for (const decl of change.remove) {
         // Find the position of any added declaration which matches the incoming
         // declaration to be removed.
         const addIndex = rule.add.findIndex(addDecl => {
-          return addDecl.index === decl.index;
+          return addDecl.index === decl.index &&
+                 addDecl.property === decl.property &&
+                 addDecl.value === decl.value;
+        });
+
+        // Find the position of any removed declaration which matches the incoming
+        // declaration to be removed. It's possible to get duplicate remove operations
+        // when, for example, disabling a declaration then deleting it.
+        const removeIndex = rule.remove.findIndex(removeDecl => {
+          return removeDecl.index === decl.index &&
+                 removeDecl.property === decl.property &&
+                 removeDecl.value === decl.value;
         });
 
         // Track the remove operation only if the property was not previously introduced
         // by an add operation. This ensures repeated changes of the same property
-        // register as a single remove operation of its original value.
-        if (addIndex < 0) {
+        // register as a single remove operation of its original value. Avoid tracking the
+        // remove declaration if already tracked (happens on disable followed by delete).
+        if (addIndex < 0 && removeIndex < 0) {
           rule.remove.push(decl);
         }
 
         // Delete any previous add operation which would be canceled out by this remove.
-        if (rule.add[addIndex] && rule.add[addIndex].value === decl.value) {
+        if (rule.add[addIndex]) {
           rule.add.splice(addIndex, 1);
         }
 
         // Update the indexes of previously tracked declarations which follow this removed
         // one so future tracking continues to point to the right declarations.
         if (changeType === "declaration-remove") {
           rule.add = rule.add.map((addDecl => {
             if (addDecl.index > decl.index) {
@@ -253,28 +265,29 @@ const reducers = {
       }
     }
 
     if (change.add && change.add.length) {
       for (const decl of change.add) {
         // Find the position of any removed declaration which matches the incoming
         // declaration to be added.
         const removeIndex = rule.remove.findIndex(removeDecl => {
-          return removeDecl.index === decl.index;
+          return removeDecl.index === decl.index &&
+                 removeDecl.value === decl.value &&
+                 removeDecl.property === decl.property;
         });
 
         // Find the position of any added declaration which matches the incoming
         // declaration to be added in case we need to replace it.
         const addIndex = rule.add.findIndex(addDecl => {
-          return addDecl.index === decl.index;
+          return addDecl.index === decl.index &&
+                 addDecl.property === decl.property;
         });
 
-        if (rule.remove[removeIndex] &&
-            rule.remove[removeIndex].value === decl.value &&
-            rule.remove[removeIndex].property === decl.property) {
+        if (rule.remove[removeIndex]) {
           // Delete any previous remove operation which would be canceled out by this add.
           rule.remove.splice(removeIndex, 1);
         } else if (rule.add[addIndex]) {
           // Replace previous add operation for declaration at this index.
           rule.add.splice(addIndex, 1, decl);
         } else {
           // Track new add operation.
           rule.add.push(decl);
--- a/devtools/client/inspector/changes/test/browser.ini
+++ b/devtools/client/inspector/changes/test/browser.ini
@@ -11,11 +11,12 @@ support-files =
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_changes_declaration_disable.js]
 [browser_changes_declaration_duplicate.js]
 [browser_changes_declaration_edit_value.js]
 [browser_changes_declaration_remove_ahead.js]
+[browser_changes_declaration_remove_disabled.js]
 [browser_changes_declaration_remove.js]
 [browser_changes_declaration_rename.js]
 [browser_changes_rule_selector.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/test/browser_changes_declaration_remove_disabled.js
@@ -0,0 +1,99 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that disabling a CSS declaration and then removing it from the Rule view
+// is tracked as removed only once. Toggling leftover declarations should not introduce
+// duplicate changes.
+
+const TEST_URI = `
+  <style type='text/css'>
+    div {
+      color: red;
+      background: black;
+    }
+  </style>
+  <div></div>
+`;
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, view: ruleView } = await openRuleView();
+  const { document: doc, store } = selectChangesView(inspector);
+
+  await selectNode("div", inspector);
+  const rule = getRuleViewRuleEditor(ruleView, 1).rule;
+
+  info("Using the second declaration");
+  await testRemoveValue(ruleView, store, doc, rule.textProps[1]);
+  info("Using the first declaration");
+  await testToggle(ruleView, store, doc, rule.textProps[0]);
+  info("Using the first declaration");
+  await testRemoveName(ruleView, store, doc, rule.textProps[0]);
+});
+
+async function testRemoveValue(ruleView, store, doc, prop) {
+  info("Test removing disabled declaration by clearing its property value.");
+  let onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Disable the declaration");
+  await togglePropStatus(ruleView, prop);
+  info("Wait for change to be tracked");
+  await onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Remove the disabled declaration by clearing its value");
+  await setProperty(ruleView, prop, null);
+  await onTrackChange;
+
+  const removeDecl = getRemovedDeclarations(doc);
+  is(removeDecl.length, 1, "Only one declaration tracked as removed");
+}
+
+async function testToggle(ruleView, store, doc, prop) {
+  info("Test toggling leftover declaration off and on will not track extra changes.");
+  let onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Disable the declaration");
+  await togglePropStatus(ruleView, prop);
+  await onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Re-enable the declaration");
+  await togglePropStatus(ruleView, prop);
+  await onTrackChange;
+
+  const removeDecl = getRemovedDeclarations(doc);
+  is(removeDecl.length, 1, "Still just one declaration tracked as removed");
+}
+
+async function testRemoveName(ruleView, store, doc, prop) {
+  info("Test removing disabled declaration by clearing its property name.");
+  let onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Disable the declaration");
+  await togglePropStatus(ruleView, prop);
+  await onTrackChange;
+
+  onTrackChange = waitUntilAction(store, "TRACK_CHANGE");
+  info("Remove the disabled declaration by clearing its name");
+  await removeProperty(ruleView, prop);
+  await onTrackChange;
+
+  info(`Expecting two declarations removed:
+  - one removed by its value in the other test
+  - one removed by its name from this test
+  `);
+
+  const removeDecl = getRemovedDeclarations(doc);
+  is(removeDecl.length, 2, "Two declarations tracked as removed");
+  is(removeDecl[0].property, "background", "First declaration name correct");
+  is(removeDecl[0].value, "black", "First declaration value correct");
+  is(removeDecl[1].property, "color", "Second declaration name correct");
+  is(removeDecl[1].value, "red", "Second declaration value correct");
+}
--- a/devtools/client/inspector/flexbox/test/browser.ini
+++ b/devtools/client/inspector/flexbox/test/browser.ini
@@ -44,10 +44,11 @@ support-files =
 [browser_flexbox_sizing_info_for_different_writing_modes.js]
 [browser_flexbox_sizing_info_for_pseudos.js]
 [browser_flexbox_sizing_info_for_text_nodes.js]
 [browser_flexbox_sizing_info_has_correct_sections.js]
 [browser_flexbox_sizing_info_matches_properties_with_!important.js]
 [browser_flexbox_sizing_info_updates_on_change.js]
 [browser_flexbox_sizing_wanted_to_grow_but_was_clamped.js]
 [browser_flexbox_text_nodes_are_listed.js]
+[browser_flexbox_text_nodes_are_not_inlined.js]
 [browser_flexbox_toggle_flexbox_highlighter_01.js]
 [browser_flexbox_toggle_flexbox_highlighter_02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_not_inlined.js
@@ -0,0 +1,40 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that single child text nodes that are also flex items can be selected in the
+// flexbox inspector.
+// This means that they are not inlined like normal single child text nodes, since
+// selecting them in the flexbox inspector also means selecting them in the markup view.
+
+const TEST_URI = URL_ROOT + "doc_flexbox_text_nodes.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  // Select the flex container in the inspector.
+  const onItemsListRendered = waitForDOM(doc, ".layout-flexbox-wrapper .flex-item-list");
+  await selectNode(".container.single-child", inspector);
+  const [flexItemList] = await onItemsListRendered;
+
+  const items = [...flexItemList.querySelectorAll("button .objectBox")];
+  is(items.length, 1, "There is 1 item displayed in the list");
+  is(items[0].textContent, "#text", "The item in the list is a text node");
+
+  info("Click on the item to select it");
+  const onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
+  items[0].closest("button").click();
+  const [flexOutlineContainer] = await onFlexItemOutlineRendered;
+  ok(flexOutlineContainer,
+     "The flex outline is displayed for a single child short text node too");
+
+  ok(inspector.selection.isTextNode(),
+     "The current inspector selection is the text node");
+
+  const markupContainer = inspector.markup.getContainer(inspector.selection.nodeFront);
+  is(markupContainer.elt.textContent, "short text", "This is the right text node");
+});
--- a/devtools/client/inspector/flexbox/test/doc_flexbox_text_nodes.html
+++ b/devtools/client/inspector/flexbox/test/doc_flexbox_text_nodes.html
@@ -12,8 +12,9 @@
   align-self: stretch;
 }
 </style>
 <div class="container">
   A text node will be wrapped into an anonymous block container
   <div></div>
   Here is yet another text node
 </div>
+<div class="container single-child">short text</div>
--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -15,27 +15,16 @@ const DATA = [
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
     value: null,
     extra: {
       oldpanel: "layoutview",
-      newpanel: "changesview",
-    },
-  },
-  {
-    timestamp: null,
-    category: "devtools.main",
-    method: "sidepanel_changed",
-    object: "inspector",
-    value: null,
-    extra: {
-      oldpanel: "changesview",
       newpanel: "animationinspector",
     },
   },
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
@@ -70,27 +59,16 @@ const DATA = [
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
     value: null,
     extra: {
       oldpanel: "computedview",
-      newpanel: "changesview",
-    },
-  },
-  {
-    timestamp: null,
-    category: "devtools.main",
-    method: "sidepanel_changed",
-    object: "inspector",
-    value: null,
-    extra: {
-      oldpanel: "changesview",
       newpanel: "animationinspector",
     },
   },
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
@@ -123,19 +101,16 @@ const DATA = [
     },
   },
 ];
 
 add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
-  // Ensure the Changes panel is enabled before running the tests.
-  await pushPref("devtools.inspector.changes.enabled", true);
-
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   await addTab(TEST_URI);
   startTelemetry();
 
   const target = await TargetFactory.forTab(gBrowser.selectedTab);
@@ -150,17 +125,17 @@ add_task(async function() {
   gBrowser.removeCurrentTab();
 });
 
 function testSidebar(toolbox) {
   info("Testing sidebar");
 
   const inspector = toolbox.getCurrentPanel();
   let sidebarTools = ["computedview", "layoutview", "fontinspector",
-                      "animationinspector", "changesview"];
+                      "animationinspector"];
 
   // Concatenate the array with itself so that we can open each tool twice.
   sidebarTools = [...sidebarTools, ...sidebarTools];
 
   return new Promise(resolve => {
     // See TOOL_DELAY for why we need setTimeout here
     setTimeout(function selectSidebarTab() {
       const tool = sidebarTools.pop();
@@ -179,21 +154,19 @@ function testSidebar(toolbox) {
 function checkResults() {
   // For help generating these tests use generateTelemetryTests("DEVTOOLS_")
   // here.
   checkTelemetry("DEVTOOLS_INSPECTOR_OPENED_COUNT", "", {0: 1, 1: 0}, "array");
   checkTelemetry("DEVTOOLS_RULEVIEW_OPENED_COUNT", "", {0: 1, 1: 0}, "array");
   checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
   checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", {0: 3, 1: 0}, "array");
   checkTelemetry("DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
-  checkTelemetry("devtools.changesview.opened_count", "", 2, "scalar");
   checkTelemetry("DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
   checkTelemetry("DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
   checkTelemetry("DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS", "", null, "hasentries");
-  checkTelemetry("DEVTOOLS_CHANGESVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
 }
 
 function checkEventTelemetry() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
                                                   event[2] === "sidepanel_changed" &&
                                                   event[3] === "inspector" &&
                                                   event[4] === null
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -30,16 +30,17 @@ loader.lazyRequireGetter(this, "nodeDocu
 loader.lazyRequireGetter(this, "standardTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);
 
 loader.lazyRequireGetter(this, "CustomElementWatcher", "devtools/server/actors/inspector/custom-element-watcher", true);
 loader.lazyRequireGetter(this, "DocumentWalker", "devtools/server/actors/inspector/document-walker", true);
 loader.lazyRequireGetter(this, "SKIP_TO_SIBLING", "devtools/server/actors/inspector/document-walker", true);
 loader.lazyRequireGetter(this, "NodeActor", "devtools/server/actors/inspector/node", true);
 loader.lazyRequireGetter(this, "NodeListActor", "devtools/server/actors/inspector/node", true);
 loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
+loader.lazyRequireGetter(this, "findFlexOrGridParentContainerForNode", "devtools/server/actors/layout", true);
 loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
 loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
 loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
 
 loader.lazyServiceGetter(this, "eventListenerService",
   "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
 
 loader.lazyRequireGetter(this, "ChromeUtils");
@@ -504,57 +505,63 @@ var WalkerActor = protocol.ActorClassWit
   },
 
   /**
    * If the given NodeActor only has a single text node as a child with a text
    * content small enough to be inlined, return that child's NodeActor.
    *
    * @param NodeActor node
    */
-  inlineTextChild: function(node) {
+  inlineTextChild: function({ rawNode }) {
     // Quick checks to prevent creating a new walker if possible.
-    if (isBeforePseudoElement(node.rawNode) ||
-        isAfterPseudoElement(node.rawNode) ||
-        isShadowHost(node.rawNode) ||
-        node.rawNode.nodeType != Node.ELEMENT_NODE ||
-        node.rawNode.children.length > 0) {
+    if (isBeforePseudoElement(rawNode) ||
+        isAfterPseudoElement(rawNode) ||
+        isShadowHost(rawNode) ||
+        rawNode.nodeType != Node.ELEMENT_NODE ||
+        rawNode.children.length > 0) {
       return undefined;
     }
 
     let walker;
     try {
       // By default always try to use an anonymous walker. Even for DirectShadowHostChild,
       // children should be available through an anonymous walker (unless the child is not
       // slotted, see catch block).
-      walker = this.getDocumentWalker(node.rawNode);
+      walker = this.getDocumentWalker(rawNode);
     } catch (e) {
       // Using an anonymous walker might throw, for instance on unslotted shadow host
       // children.
-      walker = this.getNonAnonymousWalker(node.rawNode);
+      walker = this.getNonAnonymousWalker(rawNode);
     }
 
     const firstChild = walker.firstChild();
 
     // Bail out if:
     // - more than one child
     // - unique child is not a text node
     // - unique child is a text node, but is too long to be inlined
     // - we are a slot -> these are always represented on their own lines with
     //                    a link to the original node.
+    // - we are a flex item -> these are always shown on their own lines so they can be
+    //                         selected by the flexbox inspector.
     const isAssignedSlot =
       !!firstChild &&
-      node.rawNode.nodeName === "SLOT" &&
+      rawNode.nodeName === "SLOT" &&
       isDirectShadowHostChild(firstChild);
 
+    const isFlexItem =
+      !!firstChild &&
+      findFlexOrGridParentContainerForNode(firstChild, "flex", this);
+
     if (!firstChild ||
         walker.nextSibling() ||
         firstChild.nodeType !== Node.TEXT_NODE ||
         firstChild.nodeValue.length > gValueSummaryLength ||
-        isAssignedSlot
-        ) {
+        isAssignedSlot ||
+        isFlexItem) {
       return undefined;
     }
 
     return this._ref(firstChild);
   },
 
   /**
    * Mark a node as 'retained'.
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -84,17 +84,18 @@ const FlexboxActor = ActorClassWithSpec(
 
     return form;
   },
 
   /**
    * Returns an array of FlexItemActor objects for all the flex item elements contained
    * in the flex container element.
    *
-   * @return {Array} An array of FlexItemActor objects.
+   * @return {Array}
+   *         An array of FlexItemActor objects.
    */
   getFlexItems() {
     if (isNodeDead(this.containerEl)) {
       return [];
     }
 
     const flex = this.containerEl.getAsFlexContainer();
     if (!flex) {
@@ -330,128 +331,99 @@ const LayoutActor = ActorClassWithSpec(l
    * selected node. The current node can be a grid/flex container or grid/flex item.
    * If it is a grid/flex item, returns the parent grid/flex container. Otherwise, returns
    * null if the current or parent node is not a grid/flex container.
    *
    * @param  {Node|NodeActor} node
    *         The node to start iterating at.
    * @param  {String} type
    *         Can be "grid" or "flex", the display type we are searching for.
-   * @param  {Node|null} container
-   *         The container of the current node.
-   * @return {GridActor|FlexboxActor|null} The GridActor or FlexboxActor of the
-   * grid/flex container of the give node. Otherwise, returns null.
+   * @return {GridActor|FlexboxActor|null}
+   *         The GridActor or FlexboxActor of the grid/flex container of the given node.
+   *         Otherwise, returns null.
    */
-  getCurrentDisplay(node, type, container) {
+  getCurrentDisplay(node, type) {
     if (isNodeDead(node)) {
       return null;
     }
 
     // Given node can either be a Node or a NodeActor.
     if (node.rawNode) {
       node = node.rawNode;
     }
 
-    const treeWalker = this.walker.getDocumentWalker(node, SHOW_ELEMENT);
-    let currentNode = treeWalker.currentNode;
-    let displayType = this.walker.getNode(currentNode).displayType;
+    const flexType = type === "flex";
+    const gridType = type === "grid";
+    const displayType = this.walker.getNode(node).displayType;
 
     // If the node is an element, check first if it is itself a flex or a grid.
-    if (currentNode.nodeType === currentNode.ELEMENT_NODE) {
+    if (node.nodeType === node.ELEMENT_NODE) {
       if (!displayType) {
         return null;
       }
 
-      if (type == "flex") {
-        // If only the current node is being considered when finding its display, then
-        // return it as only a flex container.
-        if ((displayType == "inline-flex" || displayType == "flex") &&
-              !container) {
-          return new FlexboxActor(this, currentNode);
-
-        // If considering the current node's container, then we are trying to determine
-        // the current node's flex item status.
-        } else if (container) {
-          if (this.isNodeFlexItemInContainer(currentNode, container)) {
-            return new FlexboxActor(this, container);
-          }
-        }
-      } else if (type == "grid" &&
-                 (displayType == "inline-grid" || displayType == "grid")) {
-        return new GridActor(this, currentNode);
+      if (flexType && displayType.includes("flex")) {
+        return new FlexboxActor(this, node);
+      } else if (gridType && displayType.includes("grid")) {
+        return new GridActor(this, node);
       }
     }
 
     // Otherwise, check if this is a flex/grid item or the parent node is a flex/grid
     // container.
     // Note that text nodes that are children of flex/grid containers are wrapped in
     // anonymous containers, so even if their displayType getter returns null we still
     // want to walk up the chain to find their container.
-    while ((currentNode = treeWalker.parentNode())) {
-      if (!currentNode) {
-        break;
-      }
-
-      displayType = this.walker.getNode(currentNode).displayType;
-
-      if (type == "flex" &&
-          (displayType == "inline-flex" || displayType == "flex")) {
-        if (this.isNodeFlexItemInContainer(node, currentNode)) {
-          return new FlexboxActor(this, currentNode);
-        }
-      } else if (type == "grid" &&
-                 (displayType == "inline-grid" || displayType == "grid")) {
-        return new GridActor(this, currentNode);
-      } else if (displayType == "contents") {
-        // Continue walking up the tree since the parent node is a content element.
-        continue;
-      }
-
-      break;
+    const container = findFlexOrGridParentContainerForNode(node, type, this.walker);
+    if (container && flexType) {
+      return new FlexboxActor(this, container);
+    }
+    if (container && gridType) {
+      return new GridActor(this, container);
     }
 
     return null;
   },
 
   /**
-   * Returns the grid container found by iterating on the given selected node. The current
-   * node can be a grid container or grid item. If it is a grid item, returns the parent
-   * grid container. Otherwise, return null if the current or parent node is not a grid
+   * Returns the grid container for a given selected node.
+   * The node itself can be a container, but if not, walk up the DOM to find its
    * container.
+   * Returns null if no container can be found.
    *
    * @param  {Node|NodeActor} node
    *         The node to start iterating at.
-   * @return {GridActor|null} The GridActor of the grid container of the given node.
-   * Otherwise, returns null.
+   * @return {GridActor|null}
+   *         The GridActor of the grid container of the given node. Otherwise, returns
+   *         null.
    */
   getCurrentGrid(node) {
     return this.getCurrentDisplay(node, "grid");
   },
 
   /**
-   * Returns the flex container found by iterating on the given selected node. The current
-   * node can be a flex container or flex item. If it is a flex item, returns the parent
-   * flex container. Otherwise, return null if the current or parent node is not a flex
+   * Returns the flex container for a given selected node.
+   * The node itself can be a container, but if not, walk up the DOM to find its
    * container.
+   * Returns null if no container can be found.
    *
    * @param  {Node|NodeActor} node
    *         The node to start iterating at.
    * @param  {Boolean|null} onlyLookAtParents
-   *         Whether or not to only consider the parent node of the given node.
-   * @return {FlexboxActor|null} The FlexboxActor of the flex container of the given node.
-   * Otherwise, returns null.
+   *         If true, skip the passed node and only start looking at its parent and up.
+   * @return {FlexboxActor|null}
+   *         The FlexboxActor of the flex container of the given node. Otherwise, returns
+   *         null.
    */
   getCurrentFlexbox(node, onlyLookAtParents) {
-    let container = null;
-
     if (onlyLookAtParents) {
-      container = node.rawNode.parentNode;
+      node = node.rawNode.parentNode;
     }
 
-    return this.getCurrentDisplay(node, "flex", container);
+    return this.getCurrentDisplay(node, "flex");
   },
 
   /**
    * Returns an array of GridActor objects for all the grid elements contained in the
    * given root node.
    *
    * @param  {Node|NodeActor} node
    *         The root node for grid elements
@@ -477,47 +449,99 @@ const LayoutActor = ActorClassWithSpec(l
 
     const frames = node.querySelectorAll("iframe, frame");
     for (const frame of frames) {
       gridActors = gridActors.concat(this.getGrids(frame.contentDocument));
     }
 
     return gridActors;
   },
-
-  /**
-   * Returns whether or not the given node is actually considered a flex item by its
-   * container.
-   *
-   * @param  {Node|NodeActor} supposedItem
-   *         The node that might be a flex item of its container.
-   * @param  {Node} container
-   *         The node's container.
-   * @return {Boolean} Whether or not the node we are looking at is a flex item of its
-   * container.
-   */
-  isNodeFlexItemInContainer(supposedItem, container) {
-    const containerDisplayType = this.walker.getNode(container).displayType;
-
-    if (containerDisplayType == "inline-flex" || containerDisplayType == "flex") {
-      const containerFlex = container.getAsFlexContainer();
-
-      for (const line of containerFlex.getLines()) {
-        for (const item of line.getItems()) {
-          if (item.node === supposedItem) {
-            return true;
-          }
-        }
-      }
-    }
-
-    return false;
-  },
 });
 
 function isNodeDead(node) {
   return !node || (node.rawNode && Cu.isDeadWrapper(node.rawNode));
 }
 
+/**
+ * If the provided node is a grid of flex item, then return its parent grid or flex
+ * container.
+ *
+ * @param  {DOMNode} node
+ *         The node that is supposedly a grid or flex item.
+ * @param  {String} type
+ *         The type of container/item to look for: "flex" or "grid".
+ * @param  {WalkerActor} walkerActor
+ *         The current instance of WalkerActor.
+ * @return {DOMNode|null}
+ *         The parent grid or flex container if found, null otherwise.
+ */
+function findFlexOrGridParentContainerForNode(node, type, walker) {
+  const treeWalker = walker.getDocumentWalker(node, SHOW_ELEMENT);
+  let currentNode = treeWalker.currentNode;
+
+  const flexType = type === "flex";
+  const gridType = type === "grid";
+
+  try {
+    while ((currentNode = treeWalker.parentNode())) {
+      if (!currentNode) {
+        break;
+      }
+
+      const displayType = walker.getNode(currentNode).displayType;
+      if (!displayType) {
+        break;
+      }
+
+      if (flexType && displayType.includes("flex")) {
+        if (isNodeAFlexItemInContainer(node, currentNode, walker)) {
+          return currentNode;
+        }
+      } else if (gridType && displayType.includes("grid")) {
+        return currentNode;
+      } else if (displayType === "contents") {
+        // Continue walking up the tree since the parent node is a content element.
+        continue;
+      }
+
+      break;
+    }
+  } catch (e) {
+    // Getting the parentNode can fail when the supplied node is in shadow DOM.
+  }
+
+  return null;
+}
+
+/**
+ * Returns whether or not the given node is actually considered a flex item by its
+ * container.
+ *
+ * @param  {Node|NodeActor} supposedItem
+ *         The node that might be a flex item of its container.
+ * @param  {Node} container
+ *         The node's container.
+ * @return {Boolean}
+ *         Whether or not the node we are looking at is a flex item of its container.
+ */
+function isNodeAFlexItemInContainer(supposedItem, container, walker) {
+  const containerDisplayType = walker.getNode(container).displayType;
+
+  if (containerDisplayType.includes("flex")) {
+    const containerFlex = container.getAsFlexContainer();
+
+    for (const line of containerFlex.getLines()) {
+      for (const item of line.getItems()) {
+        if (item.node === supposedItem) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+exports.findFlexOrGridParentContainerForNode = findFlexOrGridParentContainerForNode;
 exports.FlexboxActor = FlexboxActor;
 exports.FlexItemActor = FlexItemActor;
 exports.GridActor = GridActor;
 exports.LayoutActor = LayoutActor;
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -30,19 +30,17 @@ support-files =
   setBreakpoint-on-line-with-no-offsets.js
   setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
   addons/web-extension/manifest.json
   addons/web-extension-upgrade/manifest.json
   addons/web-extension2/manifest.json
 
 [test_addon_events.js]
 [test_addon_reload.js]
-skip-if = verify # verify mode causes AddonRepository shutdown errors
 [test_addons_actor.js]
-skip-if = (verify && !debug && (os == 'win'))
 [test_animation_name.js]
 [test_animation_type.js]
 [test_actor-registry-actor.js]
 [test_nesting-01.js]
 [test_nesting-02.js]
 [test_nesting-03.js]
 [test_forwardingprefix.js]
 [test_getyoungestframe.js]
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-a1758f0fc17a71c8d53387be5e9da42495ee11ae
+add1538234a214a371e471bff67734afd7c3ca95
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -581,17 +581,17 @@ impl AlphaBatchBuilder {
 
                 let batch_key = BatchKey {
                     blend_mode: BlendMode::PremultipliedDestOut,
                     kind: BatchKind::Brush(BrushBatchKind::Solid),
                     textures: BatchTextures::no_texture(),
                 };
 
                 let instance = PrimitiveInstanceData::from(BrushInstance {
-                    segment_index: 0,
+                    segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: 0,
                 });
 
                 self.batch_list.push_single_instance(
@@ -857,17 +857,17 @@ impl AlphaBatchBuilder {
 
                 let batch_key = BatchKey {
                     blend_mode,
                     kind: BatchKind::Brush(batch_kind),
                     textures: textures,
                 };
 
                 let instance = PrimitiveInstanceData::from(BrushInstance {
-                    segment_index: 0,
+                    segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: segment_user_data,
                 });
 
                 self.batch_list.push_single_instance(
@@ -1102,17 +1102,17 @@ impl AlphaBatchBuilder {
                                         let prim_header_index = prim_headers.push(&prim_header, z_id, [
                                             ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
                                             RasterizationSpace::Screen as i32,
                                             get_shader_opacity(1.0),
                                         ]);
 
                                         let instance = BrushInstance {
                                             prim_header_index,
-                                            segment_index: 0,
+                                            segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags: BrushFlags::empty(),
                                             clip_task_address,
                                             user_data: uv_rect_address.as_int(),
                                         };
 
                                         self.batch_list.push_single_instance(
                                             key,
@@ -1184,26 +1184,26 @@ impl AlphaBatchBuilder {
                                             ShaderColorMode::Alpha as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
                                             RasterizationSpace::Screen as i32,
                                             get_shader_opacity(1.0),
                                         ]);
 
                                         let shadow_instance = BrushInstance {
                                             prim_header_index: shadow_prim_header_index,
                                             clip_task_address,
-                                            segment_index: 0,
+                                            segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags: BrushFlags::empty(),
                                             user_data: shadow_uv_rect_address,
                                         };
 
                                         let content_instance = BrushInstance {
                                             prim_header_index: content_prim_header_index,
                                             clip_task_address,
-                                            segment_index: 0,
+                                            segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags: BrushFlags::empty(),
                                             user_data: content_uv_rect_address,
                                         };
 
                                         self.batch_list.push_single_instance(
                                             shadow_key,
                                             bounding_rect,
@@ -1278,17 +1278,17 @@ impl AlphaBatchBuilder {
                                             uv_rect_address.as_int(),
                                             filter_mode,
                                             user_data,
                                         ]);
 
                                         let instance = BrushInstance {
                                             prim_header_index,
                                             clip_task_address,
-                                            segment_index: 0,
+                                            segment_index: INVALID_SEGMENT_INDEX,
                                             edge_flags: EdgeAaSegmentMask::empty(),
                                             brush_flags: BrushFlags::empty(),
                                             user_data: 0,
                                         };
 
                                         self.batch_list.push_single_instance(
                                             key,
                                             bounding_rect,
@@ -1323,17 +1323,17 @@ impl AlphaBatchBuilder {
                                     mode as u32 as i32,
                                     backdrop_task_address.0 as i32,
                                     source_task_address.0 as i32,
                                 ]);
 
                                 let instance = BrushInstance {
                                     prim_header_index,
                                     clip_task_address,
-                                    segment_index: 0,
+                                    segment_index: INVALID_SEGMENT_INDEX,
                                     edge_flags: EdgeAaSegmentMask::empty(),
                                     brush_flags: BrushFlags::empty(),
                                     user_data: 0,
                                 };
 
                                 self.batch_list.push_single_instance(
                                     key,
                                     bounding_rect,
@@ -1363,17 +1363,17 @@ impl AlphaBatchBuilder {
                                     ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
                                     RasterizationSpace::Screen as i32,
                                     get_shader_opacity(1.0),
                                 ]);
 
                                 let instance = BrushInstance {
                                     prim_header_index,
                                     clip_task_address,
-                                    segment_index: 0,
+                                    segment_index: INVALID_SEGMENT_INDEX,
                                     edge_flags: EdgeAaSegmentMask::empty(),
                                     brush_flags: BrushFlags::empty(),
                                     user_data: uv_rect_address,
                                 };
 
                                 self.batch_list.push_single_instance(
                                     key,
                                     bounding_rect,
--- a/gfx/wr/webrender/src/prim_store.rs
+++ b/gfx/wr/webrender/src/prim_store.rs
@@ -1012,48 +1012,48 @@ impl From<PrimitiveKey> for PrimitiveTem
     }
 }
 
 impl PrimitiveTemplateKind {
     /// Write any GPU blocks for the primitive template to the given request object.
     fn write_prim_gpu_blocks(
         &self,
         request: &mut GpuDataRequest,
-        prim_rect: LayoutRect,
+        prim_size: LayoutSize,
     ) {
         match *self {
             PrimitiveTemplateKind::Clear => {
                 // Opaque black with operator dest out
                 request.push(PremultipliedColorF::BLACK);
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 request.push(color.premultiplied());
             }
             PrimitiveTemplateKind::NormalBorder { .. } => {
                 // Border primitives currently used for
                 // image borders, and run through the
                 // normal brush_image shader.
                 request.push(PremultipliedColorF::WHITE);
                 request.push(PremultipliedColorF::WHITE);
                 request.push([
-                    prim_rect.size.width,
-                    prim_rect.size.height,
+                    prim_size.width,
+                    prim_size.height,
                     0.0,
                     0.0,
                 ]);
             }
             PrimitiveTemplateKind::ImageBorder { .. } => {
                 // Border primitives currently used for
                 // image borders, and run through the
                 // normal brush_image shader.
                 request.push(PremultipliedColorF::WHITE);
                 request.push(PremultipliedColorF::WHITE);
                 request.push([
-                    prim_rect.size.width,
-                    prim_rect.size.height,
+                    prim_size.width,
+                    prim_size.height,
                     0.0,
                     0.0,
                 ]);
             }
             PrimitiveTemplateKind::LineDecoration { ref cache_key, ref color } => {
                 match cache_key {
                     Some(cache_key) => {
                         request.push(color.premultiplied());
@@ -1160,25 +1160,18 @@ impl PrimitiveTemplateKind {
             }
             PrimitiveTemplateKind::Unused => {}
         }
     }
 
     fn write_segment_gpu_blocks(
         &self,
         request: &mut GpuDataRequest,
-        prim_rect: LayoutRect,
     ) {
         match *self {
-            PrimitiveTemplateKind::Clear => {
-                request.write_segment(
-                    prim_rect,
-                    [0.0; 4],
-                );
-            }
             PrimitiveTemplateKind::NormalBorder { ref template, .. } => {
                 for segment in &template.brush_segments {
                     // has to match VECS_PER_SEGMENT
                     request.write_segment(
                         segment.local_rect,
                         segment.extra_data,
                     );
                 }
@@ -1187,32 +1180,28 @@ impl PrimitiveTemplateKind {
                 for segment in brush_segments {
                     // has to match VECS_PER_SEGMENT
                     request.write_segment(
                         segment.local_rect,
                         segment.extra_data,
                     );
                 }
             }
-            PrimitiveTemplateKind::LineDecoration { .. } => {
-                request.write_segment(
-                    prim_rect,
-                    [0.0; 4],
-                );
-            }
             PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } |
             PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => {
                 for segment in brush_segments {
                     // has to match VECS_PER_SEGMENT
                     request.write_segment(
                         segment.local_rect,
                         segment.extra_data,
                     );
                 }
             }
+            PrimitiveTemplateKind::Clear |
+            PrimitiveTemplateKind::LineDecoration { .. } |
             PrimitiveTemplateKind::Image { .. } |
             PrimitiveTemplateKind::Rectangle { .. } |
             PrimitiveTemplateKind::TextRun { .. } |
             PrimitiveTemplateKind::YuvImage { .. } |
             PrimitiveTemplateKind::Unused => {}
         }
     }
 }
@@ -1228,18 +1217,21 @@ impl PrimitiveTemplate {
         //           code shouldn't depend on current surface state. This is due to a
         //           limitation in how render task caching works. We should fix this by
         //           allowing render task caching to assign to surfaces implicitly
         //           during pass allocation.
         surface_index: SurfaceIndex,
         frame_state: &mut FrameBuildingState,
     ) {
         if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-            self.kind.write_prim_gpu_blocks(&mut request, self.prim_rect);
-            self.kind.write_segment_gpu_blocks(&mut request, self.prim_rect);
+            self.kind.write_prim_gpu_blocks(
+                &mut request,
+                self.prim_rect.size,
+            );
+            self.kind.write_segment_gpu_blocks(&mut request);
         }
 
         self.opacity = match self.kind {
             PrimitiveTemplateKind::Clear => {
                 PrimitiveOpacity::translucent()
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 PrimitiveOpacity::from_alpha(color.a)
@@ -3092,20 +3084,16 @@ impl PrimitiveStore {
                     request.push(PremultipliedColorF::WHITE);
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
                         -1.0,       // -ve means use prim rect for stretch size
                         0.0,
                         0.0,
                         0.0,
                     ]);
-                    request.write_segment(
-                        pic.local_rect,
-                        [0.0; 4],
-                    );
                 }
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::Rectangle { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
             PrimitiveInstanceKind::YuvImage { .. } |
@@ -3599,17 +3587,20 @@ impl PrimitiveStore {
 
         debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID);
         if segment_instance_index != SegmentInstanceIndex::UNUSED {
             let segment_instance = &mut scratch.segment_instances[segment_instance_index];
 
             if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) {
                 let segments = &scratch.segments[segment_instance.segments_range];
 
-                prim_data.kind.write_prim_gpu_blocks(&mut request, prim_data.prim_rect);
+                prim_data.kind.write_prim_gpu_blocks(
+                    &mut request,
+                    prim_data.prim_rect.size,
+                );
 
                 for segment in segments {
                     request.write_segment(
                         segment.local_rect,
                         [0.0; 4],
                     );
                 }
             }
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -375,19 +375,19 @@ class Bootstrapper(object):
             print(STYLO_NODEJS_DIRECTORY_MESSAGE.format(statedir=state_dir))
             sys.exit(1)
 
         if not have_clone:
             print(STYLE_NODEJS_REQUIRES_CLONE)
             sys.exit(1)
 
         self.instance.state_dir = state_dir
-        self.instance.ensure_clang_static_analysis_package(checkout_root)
         self.instance.ensure_stylo_packages(state_dir, checkout_root)
         self.instance.ensure_node_packages(state_dir, checkout_root)
+        self.instance.ensure_clang_static_analysis_package(checkout_root)
 
     def check_telemetry_opt_in(self, state_dir):
         # We can't prompt the user.
         if self.instance.no_interactive:
             return
         # Don't prompt if the user already has a setting for this value.
         if self.mach_context is not None and 'telemetry' in self.mach_context.settings.build:
             return
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -880,20 +880,25 @@ bool AntiTrackingCommon::IsFirstPartySto
   }
 
   if (behavior == nsICookieService::BEHAVIOR_REJECT) {
     LOG(("The cookie behavior pref mandates rejecting all cookies!"));
     *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL;
     return false;
   }
 
-  // Let's check if this is a 3rd party context.
-  if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
-    LOG(("Our window isn't a third-party window"));
-    return true;
+  // As a performance optimization, we only perform this check for
+  // BEHAVIOR_REJECT_FOREIGN and BEHAVIOR_LIMIT_FOREIGN.  For
+  // BEHAVIOR_REJECT_TRACKER, third-partiness is implicily checked later below.
+  if (behavior != nsICookieService::BEHAVIOR_REJECT_TRACKER) {
+    // Let's check if this is a 3rd party context.
+    if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
+      LOG(("Our window isn't a third-party window"));
+      return true;
+    }
   }
 
   if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
       behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
     // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
     // simply rejecting the request to use the storage. In the future, if we
     // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
     // for non-cookie storage types, this may change.
@@ -904,16 +909,30 @@ bool AntiTrackingCommon::IsFirstPartySto
 
   MOZ_ASSERT(behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
 
   if (!nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) {
     LOG(("Our window isn't a third-party tracking window"));
     return true;
   }
 
+#ifdef DEBUG
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+  if (thirdPartyUtil) {
+    bool thirdParty = false;
+    nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
+                                                     aURI, &thirdParty);
+    // The result of this assertion depends on whether IsThirdPartyWindow
+    // succeeds, because otherwise IsThirdPartyWindowOrChannel artificially
+    // fails.
+    MOZ_ASSERT(nsContentUtils::IsThirdPartyWindowOrChannel(
+                   aWindow, nullptr, aURI) == NS_SUCCEEDED(rv));
+  }
+#endif
+
   nsCOMPtr<nsIPrincipal> parentPrincipal;
   nsCOMPtr<nsIURI> parentPrincipalURI;
   nsCOMPtr<nsIURI> trackingURI;
   nsAutoCString trackingOrigin;
   if (!GetParentPrincipalAndTrackingOrigin(
           nsGlobalWindowInner::Cast(aWindow), getter_AddRefs(parentPrincipal),
           trackingOrigin, getter_AddRefs(trackingURI), nullptr)) {
     LOG(("Failed to obtain the parent principal and the tracking origin"));
--- a/toolkit/locales/en-US/toolkit/about/aboutNetworking.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutNetworking.ftl
@@ -1,61 +1,61 @@
-# 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/.
-
-title = About Networking
-warning = This is very experimental. Do not use without adult supervision.
-show-next-time-checkbox = Show this warning next time
-ok = OK
-http = HTTP
-sockets = Sockets
-dns = DNS
-websockets = WebSockets
-refresh = Refresh
-auto-refresh = Autorefresh every 3 seconds
-hostname = Hostname
-port = Port
-http2 = HTTP/2
-ssl = SSL
-active = Active
-idle = Idle
-host = Host
-tcp = TCP
-sent = Sent
-received = Received
-family = Family
-trr = TRR
-addresses = Addresses
-expires = Expires (Seconds)
-messages-sent = Messages Sent
-messages-received = Messages Received
-bytes-sent = Bytes Sent
-bytes-received = Bytes Received
-logging = Logging
-log-tutorial =
-    See <a data-l10n-name="logging">HTTP Logging</a>
-    for instructions on how to use this tool.
-current-log-file = Current Log File:
-current-log-modules = Current Log Modules:
-set-log-file = Set Log File
-set-log-modules = Set Log Modules
-start-logging = Start Logging
-stop-logging = Stop Logging
-dns-lookup = DNS Lookup
-dns-lookup-button = Resolve
-dns-domain = Domain:
-dns-lookup-table-column = IPs
-rcwn = RCWN Stats
-rcwn-status = RCWN Status
-rcwn-cache-won-count = Cache won count
-rcwn-net-won-count = Net won count
-total-network-requests = Total network request count
-rcwn-operation = Cache Operation
-rcwn-perf-open = Open
-rcwn-perf-read = Read
-rcwn-perf-write = Write
-rcwn-perf-entry-open = Entry Open
-rcwn-avg-short = Short Average
-rcwn-avg-long = Long Average
-rcwn-std-dev-long = Long Standard Deviation
-rcwn-cache-slow = Cache slow count
-rcwn-cache-not-slow = Cache not slow count
+# 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/.
+
+title = About Networking
+warning = This is very experimental. Do not use without adult supervision.
+show-next-time-checkbox = Show this warning next time
+ok = OK
+http = HTTP
+sockets = Sockets
+dns = DNS
+websockets = WebSockets
+refresh = Refresh
+auto-refresh = Autorefresh every 3 seconds
+hostname = Hostname
+port = Port
+http2 = HTTP/2
+ssl = SSL
+active = Active
+idle = Idle
+host = Host
+tcp = TCP
+sent = Sent
+received = Received
+family = Family
+trr = TRR
+addresses = Addresses
+expires = Expires (Seconds)
+messages-sent = Messages Sent
+messages-received = Messages Received
+bytes-sent = Bytes Sent
+bytes-received = Bytes Received
+logging = Logging
+log-tutorial =
+    See <a data-l10n-name="logging">HTTP Logging</a>
+    for instructions on how to use this tool.
+current-log-file = Current Log File:
+current-log-modules = Current Log Modules:
+set-log-file = Set Log File
+set-log-modules = Set Log Modules
+start-logging = Start Logging
+stop-logging = Stop Logging
+dns-lookup = DNS Lookup
+dns-lookup-button = Resolve
+dns-domain = Domain:
+dns-lookup-table-column = IPs
+rcwn = RCWN Stats
+rcwn-status = RCWN Status
+rcwn-cache-won-count = Cache won count
+rcwn-net-won-count = Net won count
+total-network-requests = Total network request count
+rcwn-operation = Cache Operation
+rcwn-perf-open = Open
+rcwn-perf-read = Read
+rcwn-perf-write = Write
+rcwn-perf-entry-open = Entry Open
+rcwn-avg-short = Short Average
+rcwn-avg-long = Long Average
+rcwn-std-dev-long = Long Standard Deviation
+rcwn-cache-slow = Cache slow count
+rcwn-cache-not-slow = Cache not slow count
--- a/tools/profiler/rust-helper/Cargo.toml
+++ b/tools/profiler/rust-helper/Cargo.toml
@@ -7,14 +7,22 @@ authors = ["Markus Stange <mstange@thema
 memmap = "0.6.2"
 
 [dependencies.object]
 version = "0.10.0"
 optional = true
 default-features = false
 features = ["std"]
 
+[dependencies.goblin]
+optional = true
+# The version and features of goblin need to match what's in object's Cargo.toml,
+# because we really want object's goblin and not another instance of goblin.
+version = "0.0.17"
+features = ["endian_fd", "elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "archive"]
+default-features = false
+
 [dependencies.thin-vec]
 version = "0.1.0"
 features = ["gecko-ffi"]
 
 [features]
-parse_elf = ["object"]
+parse_elf = ["object", "goblin"]
--- a/tools/profiler/rust-helper/src/elf.rs
+++ b/tools/profiler/rust-helper/src/elf.rs
@@ -1,24 +1,93 @@
 use compact_symbol_table::CompactSymbolTable;
 use object::{ElfFile, Object, SymbolKind, Uuid};
 use std::collections::HashMap;
+use std::cmp;
+use goblin::elf;
+
+const UUID_SIZE: usize = 16;
+const PAGE_SIZE: usize = 4096;
 
 fn get_symbol_map<'a, 'b, T>(object_file: &'b T) -> HashMap<u32, &'a str>
 where
   T: Object<'a, 'b>,
 {
   object_file
     .dynamic_symbols()
     .chain(object_file.symbols())
     .filter(|symbol| symbol.kind() == SymbolKind::Text)
     .filter_map(|symbol| symbol.name().map(|name| (symbol.address() as u32, name)))
     .collect()
 }
 
 pub fn get_compact_symbol_table(buffer: &[u8], breakpad_id: &str) -> Option<CompactSymbolTable> {
   let elf_file = ElfFile::parse(buffer).ok()?;
-  let elf_id = Uuid::from_bytes(elf_file.build_id()?).ok()?;
+  let elf_id = get_elf_id(&elf_file, buffer)?;
   if format!("{:X}0", elf_id.simple()) != breakpad_id {
     return None;
   }
   return Some(CompactSymbolTable::from_map(get_symbol_map(&elf_file)));
 }
+
+fn create_elf_id(identifier: &[u8], little_endian: bool) -> Option<Uuid> {
+  // Make sure that we have exactly UUID_SIZE bytes available
+  let mut data = [0 as u8; UUID_SIZE];
+  let len = cmp::min(identifier.len(), UUID_SIZE);
+  data[0..len].copy_from_slice(&identifier[0..len]);
+
+  if little_endian {
+    // The file ELF file targets a little endian architecture. Convert to
+    // network byte order (big endian) to match the Breakpad processor's
+    // expectations. For big endian object files, this is not needed.
+    data[0..4].reverse(); // uuid field 1
+    data[4..6].reverse(); // uuid field 2
+    data[6..8].reverse(); // uuid field 3
+  }
+
+  Uuid::from_bytes(&data).ok()
+}
+
+/// Tries to obtain the object identifier of an ELF object.
+///
+/// As opposed to Mach-O, ELF does not specify a unique ID for object files in
+/// its header. Compilers and linkers usually add either `SHT_NOTE` sections or
+/// `PT_NOTE` program header elements for this purpose. If one of these notes
+/// is present, ElfFile's build_id() method will find it.
+///
+/// If neither of the above are present, this function will hash the first page
+/// of the `.text` section (program code). This matches what the Breakpad
+/// processor does.
+///
+/// If all of the above fails, this function will return `None`.
+pub fn get_elf_id(elf_file: &ElfFile, data: &[u8]) -> Option<Uuid> {
+  if let Some(identifier) = elf_file.build_id() {
+    return create_elf_id(identifier, elf_file.elf().little_endian);
+  }
+
+  // We were not able to locate the build ID, so fall back to hashing the
+  // first page of the ".text" (program code) section. This algorithm XORs
+  // 16-byte chunks directly into a UUID buffer.
+  if let Some(section_data) = find_text_section(elf_file.elf(), data) {
+    let mut hash = [0; UUID_SIZE];
+    for i in 0..cmp::min(section_data.len(), PAGE_SIZE) {
+      hash[i % UUID_SIZE] ^= section_data[i];
+    }
+
+    return create_elf_id(&hash, elf_file.elf().little_endian);
+  }
+
+  None
+}
+
+/// Returns a reference to the data of the the .text section in an ELF binary.
+fn find_text_section<'elf, 'data>(
+  elf: &'elf elf::Elf,
+  data: &'data [u8],
+) -> Option<&'data[u8]> {
+  elf.section_headers.iter().find_map(|header| {
+    match (header.sh_type, elf.shdr_strtab.get(header.sh_name)) {
+      (elf::section_header::SHT_PROGBITS, Some(Ok(".text"))) =>
+        Some(&data[header.sh_offset as usize..][..header.sh_size as usize]),
+      _ => None
+    }
+  })
+}
--- a/tools/profiler/rust-helper/src/lib.rs
+++ b/tools/profiler/rust-helper/src/lib.rs
@@ -1,13 +1,15 @@
 extern crate memmap;
 extern crate thin_vec;
 
 #[cfg(feature = "parse_elf")]
 extern crate object;
+#[cfg(feature = "parse_elf")]
+extern crate goblin;
 
 mod compact_symbol_table;
 
 #[cfg(feature = "parse_elf")]
 mod elf;
 
 #[cfg(feature = "parse_elf")]
 use memmap::MmapOptions;
--- a/xpcom/base/nsMacUtilsImpl.cpp
+++ b/xpcom/base/nsMacUtilsImpl.cpp
@@ -123,47 +123,49 @@ nsMacUtilsImpl::GetIsTranslated(bool* aI
   // translated.
   *aIsTranslated = false;
 #endif
 
   return NS_OK;
 }
 
 #if defined(MOZ_CONTENT_SANDBOX)
-// Get the path to the .app directory for the parent process. When executing
-// in the child process, this is the outer .app (such as Firefox.app) and not
-// the inner .app containing the child process executable.
+// Get the path to the .app directory (aka bundle) for the parent process.
+// When executing in the child process, this is the outer .app (such as
+// Firefox.app) and not the inner .app containing the child process
+// executable. We don't rely on the actual .app extension to allow for the
+// bundle being renamed.
 bool nsMacUtilsImpl::GetAppPath(nsCString& aAppPath) {
   nsAutoCString appPath;
   nsAutoCString appBinaryPath(
       (CommandLine::ForCurrentProcess()->argv()[0]).c_str());
 
-  auto pattern = NS_LITERAL_CSTRING(".app/Contents/MacOS/");
+  // The binary path resides within the .app dir in Contents/MacOS,
+  // e.g., Firefox.app/Contents/MacOS/firefox. Search backwards in
+  // the binary path for the end of .app path.
+  auto pattern = NS_LITERAL_CSTRING("/Contents/MacOS/");
   nsAutoCString::const_iterator start, end;
   appBinaryPath.BeginReading(start);
   appBinaryPath.EndReading(end);
   if (RFindInReadable(pattern, start, end)) {
     end = start;
     appBinaryPath.BeginReading(start);
 
-    // If we're executing in a child process, get the
-    // parent .app by searching right-to-left once more.
+    // If we're executing in a child process, get the parent .app path
+    // by searching backwards once more. The child executable resides
+    // in Firefox.app/Contents/MacOS/plugin-container/Contents/MacOS.
     if (!XRE_IsParentProcess()) {
       if (RFindInReadable(pattern, start, end)) {
         end = start;
         appBinaryPath.BeginReading(start);
       } else {
         return false;
       }
     }
 
-    ++end;
-    ++end;
-    ++end;
-    ++end;
     appPath.Assign(Substring(start, end));
   } else {
     return false;
   }
 
   nsCOMPtr<nsIFile> app;
   nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath), true,
                                 getter_AddRefs(app));