Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Sat, 14 Jul 2018 00:52:51 +0300
changeset 426577 b79457b703d9b51cf3e38199f4e154d21bc6fc97
parent 426523 e37eeb019cf8739366d6438d75e3630952d0c412 (current diff)
parent 426576 4c9aa8e48d61555d84847adf67b41b4e846f7ddd (diff)
child 426589 703da935b1e0a6611dedaca1e7dfa63c018fbb2b
child 426627 0f509352f6a4f396452658e645335d46af7b3aed
push id34275
push usernerli@mozilla.com
push dateFri, 13 Jul 2018 21:53:18 +0000
treeherdermozilla-central@b79457b703d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
b79457b703d9 / 63.0a1 / 20180713220354 / files
nightly linux64
b79457b703d9 / 63.0a1 / 20180713220354 / files
nightly mac
b79457b703d9 / 63.0a1 / 20180713220354 / files
nightly win32
b79457b703d9 / 63.0a1 / 20180713220354 / files
nightly win64
b79457b703d9 / 63.0a1 / 20180713220354 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/media/webaudio/AudioEventTimeline.h
dom/media/webaudio/AudioParam.h
dom/media/webaudio/gtest/TestAudioEventTimeline.cpp
dom/webidl/AudioParam.webidl
testing/web-platform/meta/webaudio/the-audio-api/the-audioparam-interface/event-insertion.html.ini
testing/web-platform/tests/webaudio/resources/audionodeoptions.js
testing/web-platform/tests/webaudio/resources/audit.js
testing/web-platform/tests/webaudio/resources/start-stop-exceptions.js
testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html
testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html
testing/web-platform/tests/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
testing/web-platform/tests/webaudio/the-audio-api/the-audionode-interface/audionode.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-exceptional-values.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
testing/web-platform/tests/webaudio/the-audio-api/the-audioworklet-interface/audioworkletnode-constructor-options.https.html
testing/web-platform/tests/webaudio/the-audio-api/the-biquadfilternode-interface/biquad-basic.html
testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelaylimit.html
testing/web-platform/tests/webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html
testing/web-platform/tests/webaudio/the-audio-api/the-iirfilternode-interface/ctor-iirfilter.html
testing/web-platform/tests/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html
testing/web-platform/tests/webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html
testing/web-platform/tests/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html
testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -844,21 +844,16 @@ toolbarspring {
     image-rendering: -moz-crisp-edges;
   }
   /* Synced Tabs sidebar */
   html|*.tabs-container html|*.item-tabs-list html|*.item-icon-container {
     image-rendering: -moz-crisp-edges;
   }
 }
 
-#editBMPanel_tagsSelector {
-  /* override default listbox width from xul.css */
-  width: auto;
-}
-
 menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
   display: none;
 }
 menuitem.spell-suggestion {
   font-weight: bold;
 }
 
 /* Hide extension toolbars that neglected to set the proper class */
--- a/browser/base/content/webext-panels.xul
+++ b/browser/base/content/webext-panels.xul
@@ -1,15 +1,16 @@
 <?xml version="1.0"?>
 
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 
 <!DOCTYPE page [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
 %textcontextDTD;
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -742,46 +742,42 @@ var gEditItemOverlay = {
   },
 
   _rebuildTagsSelectorList() {
     let tagsSelector = this._element("tagsSelector");
     let tagsSelectorRow = this._element("tagsSelectorRow");
     if (tagsSelectorRow.collapsed)
       return;
 
-    // Save the current scroll position and restore it after the rebuild.
-    let firstIndex = tagsSelector.getIndexOfFirstVisibleRow();
     let selectedIndex = tagsSelector.selectedIndex;
     let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
                                          : null;
 
     while (tagsSelector.hasChildNodes()) {
       tagsSelector.removeChild(tagsSelector.lastChild);
     }
 
     let tagsInField = this._getTagsArrayFromTagsInputField();
     let allTags = PlacesUtils.tagging.allTags;
-    for (let tag of allTags) {
-      let elt = document.createElement("listitem");
-      elt.setAttribute("type", "checkbox");
-      elt.setAttribute("label", tag);
+    let fragment = document.createDocumentFragment();
+    for (var i = 0; i < allTags.length; i++) {
+      let tag = allTags[i];
+      let elt = document.createElement("richlistitem");
+      elt.appendChild(document.createElement("image"));
+      let label = document.createElement("label");
+      label.setAttribute("value", tag);
+      elt.appendChild(label);
       if (tagsInField.includes(tag))
         elt.setAttribute("checked", "true");
-      tagsSelector.appendChild(elt);
+      fragment.appendChild(elt);
       if (selectedTag === tag)
-        selectedIndex = tagsSelector.getIndexOfItem(elt);
+        selectedIndex = i;
     }
+    tagsSelector.appendChild(fragment);
 
-    // Restore position.
-    // The listbox allows to scroll only if the required offset doesn't
-    // overflow its capacity, thus need to adjust the index for removals.
-    firstIndex =
-      Math.min(firstIndex,
-               tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
-    tagsSelector.scrollToIndex(firstIndex);
     if (selectedIndex >= 0 && tagsSelector.itemCount > 0) {
       selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1);
       tagsSelector.selectedIndex = selectedIndex;
       tagsSelector.ensureIndexIsVisible(selectedIndex);
     }
   },
 
   toggleTagsSelector() {
@@ -791,22 +787,27 @@ var gEditItemOverlay = {
     if (tagsSelectorRow.collapsed) {
       expander.className = "expander-up";
       expander.setAttribute("tooltiptext",
                             expander.getAttribute("tooltiptextup"));
       tagsSelectorRow.collapsed = false;
       this._rebuildTagsSelectorList();
 
       // This is a no-op if we've added the listener.
-      tagsSelector.addEventListener("CheckboxStateChange", this);
+      tagsSelector.addEventListener("mousedown", this);
+      tagsSelector.addEventListener("keypress", this);
     } else {
       expander.className = "expander-down";
       expander.setAttribute("tooltiptext",
                             expander.getAttribute("tooltiptextdown"));
       tagsSelectorRow.collapsed = true;
+
+      // This is a no-op if we've removed the listener.
+      tagsSelector.removeEventListener("mousedown", this);
+      tagsSelector.removeEventListener("keypress", this);
     }
   },
 
   /**
    * Splits "tagsField" element value, returning an array of valid tag strings.
    *
    * @return Array of tag strings found in the field value.
    */
@@ -840,42 +841,62 @@ var gEditItemOverlay = {
     this._folderTree.selectItems([ip.guid]);
     PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
     this._folderTree.selectItems([guid]);
     this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
                                   this._folderTree.columns.getFirstColumn());
   },
 
   // EventListener
-  handleEvent(aEvent) {
-    switch (aEvent.type) {
-    case "CheckboxStateChange":
-      // Update the tags field when items are checked/unchecked in the listbox
-      let tags = this._getTagsArrayFromTagsInputField();
-      let tagCheckbox = aEvent.target;
-
-      let curTagIndex = tags.indexOf(tagCheckbox.label);
-      let tagsSelector = this._element("tagsSelector");
-      tagsSelector.selectedItem = tagCheckbox;
-
-      if (tagCheckbox.checked) {
-        if (curTagIndex == -1)
-          tags.push(tagCheckbox.label);
-      } else if (curTagIndex != -1) {
-        tags.splice(curTagIndex, 1);
+  handleEvent(event) {
+    switch (event.type) {
+    case "mousedown":
+      if (event.button == 0) {
+        // Make sure the event is triggered on an item and not the empty space.
+        let item = event.target.closest("richlistbox,richlistitem");
+        if (item.localName == "richlistitem") {
+          this.toggleItemCheckbox(item);
+        }
       }
-      this._element("tagsField").value = tags.join(", ");
-      this._updateTags();
+      break;
+    case "keypress":
+      if (event.key == " ") {
+        let item = event.target.currentItem;
+        if (item) {
+          this.toggleItemCheckbox(item);
+        }
+      }
       break;
     case "unload":
       this.uninitPanel(false);
       break;
     }
   },
 
+  toggleItemCheckbox(item) {
+    // Update the tags field when items are checked/unchecked in the listbox
+    let tags = this._getTagsArrayFromTagsInputField();
+
+    let curTagIndex = tags.indexOf(item.label);
+    let tagsSelector = this._element("tagsSelector");
+    tagsSelector.selectedItem = item;
+
+    if (!item.hasAttribute("checked")) {
+      item.setAttribute("checked", "true");
+      if (curTagIndex == -1)
+        tags.push(item.label);
+    } else {
+      item.removeAttribute("checked");
+      if (curTagIndex != -1)
+        tags.splice(curTagIndex, 1);
+    }
+    this._element("tagsField").value = tags.join(", ");
+    this._updateTags();
+  },
+
   _initTagsField() {
     let tags;
     if (this._paneInfo.isURI)
       tags = PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
     else if (this._paneInfo.bulkTagging)
       tags = this._getCommonTags();
     else
       throw new Error("_promiseTagsStr called unexpectedly");
--- a/browser/components/places/content/editBookmarkPanel.inc.xul
+++ b/browser/components/places/content/editBookmarkPanel.inc.xul
@@ -102,19 +102,20 @@
                 tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                 tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                 tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
                 oncommand="gEditItemOverlay.toggleTagsSelector();"/>
       </hbox>
     </vbox>
 
     <vbox id="editBMPanel_tagsSelectorRow"
-         collapsed="true">
-      <listbox id="editBMPanel_tagsSelector"
-               height="150"/>
+          collapsed="true">
+      <richlistbox id="editBMPanel_tagsSelector"
+                   styled="true"
+                   height="150"/>
     </vbox>
 
     <vbox id="editBMPanel_keywordRow"
           collapsed="true">
       <label value="&editBookmarkOverlay.keyword.label;"
              accesskey="&editBookmarkOverlay.keyword.accesskey;"
              control="editBMPanel_keywordField"/>
       <textbox id="editBMPanel_keywordField"
--- a/browser/components/places/tests/browser/browser_bug631374_tags_selector_scroll.js
+++ b/browser/components/places/tests/browser/browser_bug631374_tags_selector_scroll.js
@@ -1,15 +1,24 @@
  /**
   * This test checks that editing tags doesn't scroll the tags selector
   * listbox to wrong positions.
   */
 
 const TEST_URL = "about:buildconfig";
 
+function scrolledIntoView(item, parentItem) {
+  let itemRect = item.getBoundingClientRect();
+  let parentItemRect = parentItem.getBoundingClientRect();
+  let pointInView = y => parentItemRect.top < y && y < parentItemRect.bottom;
+
+  // Partially visible items are also considered visible.
+  return pointInView(itemRect.top) || pointInView(itemRect.bottom);
+}
+
 add_task(async function() {
   await PlacesUtils.bookmarks.eraseEverything();
   let tags = ["a", "b", "c", "d", "e", "f", "g",
               "h", "i", "l", "m", "n", "o", "p"];
 
   // Add a bookmark and tag it.
   let uri1 = Services.io.newURI(TEST_URL);
   let bm1 = await PlacesUtils.bookmarks.insert({
@@ -58,76 +67,70 @@ add_task(async function() {
 
   // Go by two so there is some untouched tag in the middle.
   for (let i = 8; i < tags.length; i += 2) {
     tagsSelector.selectedIndex = i;
     let listItem = tagsSelector.selectedItem;
     isnot(listItem, null, "Valid listItem found");
 
     tagsSelector.ensureElementIsVisible(listItem);
-    let visibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
+    let scrollTop = tagsSelector.scrollTop;
 
-    ok(listItem.checked, "Item is checked " + i);
+    ok(listItem.hasAttribute("checked"), "Item is checked " + i);
     let selectedTag = listItem.label;
 
     // Uncheck the tag.
     let promiseNotification = PlacesTestUtils.waitForNotification(
       "onItemChanged", (id, property) => property == "tags");
-    listItem.checked = false;
+    EventUtils.synthesizeMouseAtCenter(listItem.firstChild, {});
     await promiseNotification;
-    is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
-       "Scroll position did not change");
+    is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");
 
     // The listbox is rebuilt, so we have to get the new element.
     let newItem = tagsSelector.selectedItem;
     isnot(newItem, null, "Valid new listItem found");
-    ok(!newItem.checked, "New listItem is unchecked " + i);
+    ok(!newItem.hasAttribute("checked"), "New listItem is unchecked " + i);
     is(newItem.label, selectedTag, "Correct tag is still selected");
 
     // Check the tag.
     promiseNotification = PlacesTestUtils.waitForNotification(
       "onItemChanged", (id, property) => property == "tags");
-    newItem.checked = true;
+    EventUtils.synthesizeMouseAtCenter(newItem.firstChild, {});
     await promiseNotification;
-    is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
-       "Scroll position did not change");
+    is(scrollTop, tagsSelector.scrollTop, "Scroll position did not change");
   }
 
   // Remove the second bookmark, then nuke some of the tags.
   await PlacesUtils.bookmarks.remove(bm2);
 
   // Doing this backwords tests more interesting paths.
   for (let i = tags.length - 1; i >= 0 ; i -= 2) {
     tagsSelector.selectedIndex = i;
     let listItem = tagsSelector.selectedItem;
     isnot(listItem, null, "Valid listItem found");
 
     tagsSelector.ensureElementIsVisible(listItem);
-    let firstVisibleTag = tags[tagsSelector.getIndexOfFirstVisibleRow()];
+    let items = [...tagsSelector.children];
+    let topTag = items.find(e => scrolledIntoView(e, tagsSelector)).label;
 
-    ok(listItem.checked, "Item is checked " + i);
+    ok(listItem.hasAttribute("checked"), "Item is checked " + i);
 
     // Uncheck the tag.
     let promiseNotification = PlacesTestUtils.waitForNotification(
       "onItemChanged", (id, property) => property == "tags");
-    listItem.checked = false;
+    EventUtils.synthesizeMouseAtCenter(listItem.firstChild, {});
     await promiseNotification;
 
-    // Ensure the first visible tag is still visible in the list.
-    let firstVisibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
-    let lastVisibleIndex = firstVisibleIndex + tagsSelector.getNumberOfVisibleRows() - 1;
-    let expectedTagIndex = tags.indexOf(firstVisibleTag);
-    ok(expectedTagIndex >= firstVisibleIndex &&
-       expectedTagIndex <= lastVisibleIndex,
-       "Scroll position is correct");
+    // The listbox is rebuilt, so we have to get the new element.
+    let topItem = [...tagsSelector.children].find(e => e.label == topTag);
+    ok(scrolledIntoView(topItem, tagsSelector), "Scroll position is correct");
 
-    // The listbox is rebuilt, so we have to get the new element.
     let newItem = tagsSelector.selectedItem;
     isnot(newItem, null, "Valid new listItem found");
-    ok(newItem.checked, "New listItem is checked " + i);
+    ok(newItem.hasAttribute("checked"), "New listItem is checked " + i);
     is(tagsSelector.selectedItem.label,
        tags[Math.min(i + 1, tags.length - 2)],
        "The next tag is now selected");
   }
 
   let hiddenPromise = promisePopupHidden(bookmarkPanel);
   let doneButton = document.getElementById("editBookmarkPanelDoneButton");
   doneButton.click();
--- a/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js
+++ b/browser/components/places/tests/browser/browser_editBookmark_tags_liveUpdate.js
@@ -4,17 +4,17 @@ function checkTagsSelector(aAvailableTag
   is(PlacesUtils.tagging.allTags.length, aAvailableTags.length,
      "tagging service is in sync.");
   let tagsSelector = document.getElementById("editBMPanel_tagsSelector");
   let children = tagsSelector.childNodes;
   is(children.length, aAvailableTags.length,
       "Found expected number of tags in the tags selector");
 
   Array.prototype.forEach.call(children, function(aChild) {
-    let tag = aChild.getAttribute("label");
+    let tag = aChild.querySelector("label").getAttribute("value");
     ok(true, "Found tag '" + tag + "' in the selector");
     ok(aAvailableTags.includes(tag), "Found expected tag");
     let checked = aChild.getAttribute("checked") == "true";
     is(checked, aCheckedTags.includes(tag),
        "Tag is correctly marked");
   });
 }
 
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
@@ -18,24 +18,16 @@ add_task(async function() {
 
     // Test checkbox
     let checkbox = doc.getElementById("checkbox");
     checkbox.focus();
     EventUtils.sendString(" ");
     ok(checkbox.checked, "Checkbox is checked");
     await checkPageScrolling(container, "checkbox");
 
-    // Test listbox
-    let listbox = doc.getElementById("listbox");
-    let listitem = doc.getElementById("listitem");
-    listbox.focus();
-    EventUtils.sendString(" ");
-    ok(listitem.selected, "Listitem is selected");
-    await checkPageScrolling(container, "listbox");
-
     // Test radio
     let radiogroup = doc.getElementById("radiogroup");
     radiogroup.focus();
     EventUtils.sendString(" ");
     await checkPageScrolling(container, "radio");
   });
 
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:preferences#search" }, async function(browser) {
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
@@ -10,23 +10,16 @@
     <hbox>
       <button id="button" label="button" />
     </hbox>
 
     <hbox>
       <checkbox id="checkbox" label="checkbox" />
     </hbox>
 
-    <hbox style="height: 50px;">
-      <listbox id="listbox">
-        <listitem id="listitem" label="listitem" />
-        <listitem label="listitem" />
-      </listbox>
-    </hbox>
-
     <hbox>
       <radiogroup id="radiogroup">
         <radio id="radio" label="radio" />
       </radiogroup>
     </hbox>
   </vbox>
 </vbox>
 
--- a/browser/components/preferences/languages.js
+++ b/browser/components/preferences/languages.js
@@ -17,39 +17,34 @@ Preferences.addAll([
 
 var gLanguagesDialog = {
 
   _availableLanguagesList: [],
   _acceptLanguages: { },
 
   _selectedItemID: null,
 
-  init() {
-    if (!this._availableLanguagesList.length)
-      this._loadAvailableLanguages();
-  },
+  onLoad() {
+    Preferences.get("intl.accept_languages").on("change",
+      () => this._readAcceptLanguages().catch(Cu.reportError));
 
-  // Ugly hack used to trigger extra reflow in order to work around XUL bug 1194844;
-  // see bug 1194346.
-  forceReflow() {
-    this._activeLanguages.style.fontKerning = "none";
-    setTimeout(() => {
-      this._activeLanguages.style.removeProperty("font-kerning");
-    }, 0);
+    if (!this._availableLanguagesList.length) {
+      document.mozSubdialogReady = this._loadAvailableLanguages();
+    }
   },
 
   get _activeLanguages() {
     return document.getElementById("activeLanguages");
   },
 
   get _availableLanguages() {
     return document.getElementById("availableLanguages");
   },
 
-  _loadAvailableLanguages() {
+  async _loadAvailableLanguages() {
     // This is a parser for: resource://gre/res/language.properties
     // The file is formatted like so:
     // ab[-cd].accept=true|false
     //  ab = language
     //  cd = region
     var bundleAccepted    = document.getElementById("bundleAccepted");
 
     function LocaleInfo(aLocaleName, aLocaleCode, aIsVisible) {
@@ -80,17 +75,18 @@ var gLanguagesDialog = {
     for (let i in localeCodes) {
       let isVisible = localeValues[i] == "true" &&
         (!(localeCodes[i] in this._acceptLanguages) || !this._acceptLanguages[localeCodes[i]]);
 
       let li = new LocaleInfo(localeNames[i], localeCodes[i], isVisible);
       this._availableLanguagesList.push(li);
     }
 
-    this._buildAvailableLanguageList();
+    await this._buildAvailableLanguageList();
+    await this._readAcceptLanguages();
   },
 
   async _buildAvailableLanguageList() {
     var availableLanguagesPopup = document.getElementById("availableLanguagesPopup");
     while (availableLanguagesPopup.hasChildNodes())
       availableLanguagesPopup.firstChild.remove();
 
     let frag = document.createDocumentFragment();
@@ -127,33 +123,35 @@ var gLanguagesDialog = {
     // Re-append items in the correct order:
     items.forEach(item => frag.appendChild(item));
 
     availableLanguagesPopup.appendChild(frag);
 
     this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("placeholder"));
   },
 
-  async readAcceptLanguages() {
+  async _readAcceptLanguages() {
     while (this._activeLanguages.hasChildNodes())
       this._activeLanguages.firstChild.remove();
 
     var selectedIndex = 0;
     var preference = Preferences.get("intl.accept_languages");
     if (preference.value == "")
-      return undefined;
+      return;
     var languages = preference.value.toLowerCase().split(/\s*,\s*/);
     for (var i = 0; i < languages.length; ++i) {
-      var listitem = document.createElement("listitem");
+      var listitem = document.createElement("richlistitem");
+      var label = document.createElement("label");
+      listitem.appendChild(label);
       listitem.id = languages[i];
       if (languages[i] == this._selectedItemID)
         selectedIndex = i;
       this._activeLanguages.appendChild(listitem);
       var localeName = this._getLocaleName(languages[i]);
-      document.l10n.setAttributes(listitem, "languages-code-format", {
+      document.l10n.setAttributes(label, "languages-active-code-format", {
         locale: localeName,
         code: languages[i],
       });
 
       // Hash this language as an "Active" language so we don't
       // show it in the list that can be added.
       this._acceptLanguages[languages[i]] = true;
     }
@@ -166,22 +164,16 @@ var gLanguagesDialog = {
     if (this._activeLanguages.childNodes.length > 0) {
       this._activeLanguages.ensureIndexIsVisible(selectedIndex);
       this._activeLanguages.selectedIndex = selectedIndex;
     }
 
     // Update states of accept-language list and buttons according to
     // privacy.resistFingerprinting and privacy.spoof_english.
     this.readSpoofEnglish();
-
-    return undefined;
-  },
-
-  writeAcceptLanguages() {
-    return undefined;
   },
 
   onAvailableLanguageSelect() {
     var availableLanguages = this._availableLanguages;
     var addButton = document.getElementById("addButton");
     addButton.disabled = availableLanguages.disabled ||
                          availableLanguages.selectedIndex < 0;
 
@@ -205,17 +197,17 @@ var gLanguagesDialog = {
       arrayOfPrefs.unshift(selectedID);
       preference.value = arrayOfPrefs.join(",");
     }
 
     this._acceptLanguages[selectedID] = true;
     this._availableLanguages.selectedItem = null;
 
     // Rebuild the available list with the added item removed...
-    this._buildAvailableLanguageList();
+    this._buildAvailableLanguageList().catch(Cu.reportError);
   },
 
   removeLanguage() {
     // Build the new preference value string.
     var languagesArray = [];
     for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
       var item = this._activeLanguages.childNodes[i];
       if (!item.selected)
@@ -232,17 +224,17 @@ var gLanguagesDialog = {
     selectItem = selectItem ? selectItem.id : null;
 
     this._selectedItemID = selectItem;
 
     // Update the preference and force a UI rebuild
     var preference = Preferences.get("intl.accept_languages");
     preference.value = string;
 
-    this._buildAvailableLanguageList();
+    this._buildAvailableLanguageList().catch(Cu.reportError);
   },
 
   _getLocaleName(localeCode) {
     if (!this._availableLanguagesList.length)
       this._loadAvailableLanguages();
     for (var i = 0; i < this._availableLanguagesList.length; ++i) {
       if (localeCode == this._availableLanguagesList[i].code)
         return this._availableLanguagesList[i].name;
@@ -345,13 +337,8 @@ var gLanguagesDialog = {
       return false;
     }
   },
 
   writeSpoofEnglish() {
     return document.getElementById("spoofEnglish").checked ? 2 : 1;
   }
 };
-
-// These focus and resize handlers hack around XUL bug 1194844
-// by triggering extra reflow (see bug 1194346).
-window.addEventListener("focus", () => gLanguagesDialog.forceReflow());
-window.addEventListener("resize", () => gLanguagesDialog.forceReflow());
--- a/browser/components/preferences/languages.xul
+++ b/browser/components/preferences/languages.xul
@@ -10,17 +10,17 @@
 
 <dialog id="LanguagesDialog" type="child" class="prefwindow"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         data-l10n-id="languages-window"
         data-l10n-attrs="title, style"
         buttons="accept,cancel,help"
         persist="lastSelected screenX screenY"
         role="dialog"
-        onload="gLanguagesDialog.init();"
+        onload="gLanguagesDialog.onLoad();"
         helpTopic="prefs-languages"
         ondialoghelp="openPrefsHelp()">
 
   <link rel="localization" href="browser/preferences/languages.ftl"/>
   <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
 
   <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
@@ -44,21 +44,19 @@
               onsynctopreference="return gLanguagesDialog.writeSpoofEnglish();"/>
     <grid flex="1">
       <columns>
         <column flex="1"/>
         <column/>
       </columns>
       <rows>
         <row flex="1">
-          <listbox id="activeLanguages" flex="1" rows="6"
-                    seltype="multiple" onselect="gLanguagesDialog.onLanguageSelect();"
-                    preference="intl.accept_languages"
-                    onsyncfrompreference="return gLanguagesDialog.readAcceptLanguages();"
-                    onsynctopreference="return gLanguagesDialog.writeAcceptLanguages();"/>
+          <richlistbox id="activeLanguages" flex="1" height="200"
+                       seltype="multiple"
+                       onselect="gLanguagesDialog.onLanguageSelect();"/>
           <vbox>
             <button id="up" class="up" oncommand="gLanguagesDialog.moveUp();" disabled="true"
                     data-l10n-id="languages-customize-moveup"
                     preference="pref.browser.language.disable_button.up"/>
             <button id="down" class="down" oncommand="gLanguagesDialog.moveDown();" disabled="true"
                     data-l10n-id="languages-customize-movedown"
                     preference="pref.browser.language.disable_button.down"/>
             <button id="remove" oncommand="gLanguagesDialog.removeLanguage();" disabled="true"
--- a/browser/locales/en-US/browser/preferences/languages.ftl
+++ b/browser/locales/en-US/browser/preferences/languages.ftl
@@ -40,8 +40,11 @@ languages-customize-add =
 #   Icelandic [is]
 #   Spanish (Chile) [es-CL]
 #
 # Variables:
 #   $locale (String) - A name of the locale (for example: "Icelandic", "Spanish (Chile)")
 #   $code (String) - Locale code of the locale (for example: "is", "es-CL")
 languages-code-format =
     .label = { $locale } [{ $code }]
+
+languages-active-code-format =
+    .value = { languages-code-format.label }
--- a/browser/themes/linux/places/editBookmark.css
+++ b/browser/themes/linux/places/editBookmark.css
@@ -53,16 +53,24 @@
  * leaving only the comment column visible. This is
  * so that only the tag being edited is shown in the
  * popup.
  */
 #editBMPanel_tagsField #treecolAutoCompleteValue {
   visibility: collapse;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0 2px;
+  min-width: 13px;
+  min-height: 13px;
+}
+
 
 /* Bookmark panel dropdown menu items */
 #editBMPanel_folderMenuList[selectedIndex="0"],
 #editBMPanel_toolbarFolderItem {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg") !important;
 }
 
 #editBMPanel_folderMenuList[selectedIndex="1"],
--- a/browser/themes/osx/places/editBookmark.css
+++ b/browser/themes/osx/places/editBookmark.css
@@ -55,16 +55,30 @@
  * leaving only the comment column visible. This is
  * so that only the tag being edited is shown in the
  * popup.
  */
 #editBMPanel_tagsField #treecolAutoCompleteValue {
   visibility: collapse;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0px 2px;
+  border: 1px solid -moz-DialogText;
+  min-width: 13px;
+  min-height: 13px;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
+#editBMPanel_tagsSelector > richlistitem[checked="true"] > image {
+  background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
 
 /* ----- BOOKMARK PANEL DROPDOWN MENU ITEMS ----- */
 
 #editBMPanel_folderMenuList[selectedIndex="0"],
 #editBMPanel_toolbarFolderItem {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg") !important;
 }
 
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -204,16 +204,20 @@ button > hbox > label {
   width: 30px;
   margin-inline-start: 5px;
 }
 
 #getStarted {
   font-size: 90%;
 }
 
+#activeLanguages > richlistitem {
+  padding: 0.3em;
+}
+
 #downloadFolder {
   margin-inline-start: 0;
   padding-inline-start: 30px;
   background-repeat: no-repeat;
   background-size: 16px;
   background-position: center left 8px;
 }
 
--- a/browser/themes/windows/places/editBookmark.css
+++ b/browser/themes/windows/places/editBookmark.css
@@ -58,16 +58,26 @@
  * leaving only the comment column visible. This is
  * so that only the tag being edited is shown in the
  * popup.
  */
 #editBMPanel_tagsField #treecolAutoCompleteValue {
   visibility: collapse;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0px 2px;
+  border: 1px solid -moz-DialogText;
+  min-width: 13px;
+  min-height: 13px;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
 
 /* ::::: bookmark panel dropdown icons ::::: */
 
 #editBMPanel_folderMenuList[selectedIndex="0"],
 #editBMPanel_toolbarFolderItem {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg") !important;
 }
 
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -374,17 +374,17 @@ ElementEditor.prototype = {
     name.classList.add("attr-name");
     name.classList.add("theme-fg-color2");
     inner.appendChild(name);
 
     inner.appendChild(this.doc.createTextNode('="'));
 
     const val = this.doc.createElement("span");
     val.classList.add("attr-value");
-    val.classList.add("theme-fg-color6");
+    val.classList.add("theme-fg-color4");
     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/shared/dom-node-preview.js
+++ b/devtools/client/inspector/shared/dom-node-preview.js
@@ -105,27 +105,27 @@ DomNodePreview.prototype = {
         textContent: "id"
       });
       this.idEl.appendChild(document.createTextNode("=\""));
     } else {
       createNode({
         parent: this.idEl,
         nodeType: "span",
         attributes: {
-          "class": "theme-fg-color6"
+          "class": "theme-fg-color4"
         },
         textContent: "#"
       });
     }
 
     createNode({
       parent: this.idEl,
       nodeType: "span",
       attributes: {
-        "class": "attribute-value theme-fg-color6"
+        "class": "attribute-value theme-fg-color4"
       }
     });
 
     if (!this.options.compact) {
       this.idEl.appendChild(document.createTextNode("\""));
     }
 
     // Class attribute container.
@@ -144,27 +144,27 @@ DomNodePreview.prototype = {
         textContent: "class"
       });
       this.classEl.appendChild(document.createTextNode("=\""));
     } else {
       createNode({
         parent: this.classEl,
         nodeType: "span",
         attributes: {
-          "class": "theme-fg-color6"
+          "class": "theme-fg-color4"
         },
         textContent: "."
       });
     }
 
     createNode({
       parent: this.classEl,
       nodeType: "span",
       attributes: {
-        "class": "attribute-value theme-fg-color6"
+        "class": "attribute-value theme-fg-color4"
       }
     });
 
     if (!this.options.compact) {
       this.classEl.appendChild(document.createTextNode("\""));
       this.previewEl.appendChild(document.createTextNode(">"));
     }
 
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -126,34 +126,32 @@ body {
 .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-color6 {
+.theme-fg-color4 {
   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;
 }
 
 .CodeMirror-Tern-completion-string:before,
 .CodeMirror-Tern-completion-fn:before {
   background-color: #b26b47;
 }
 
-.theme-fg-color7,
 .cm-s-mozilla .cm-atom,
 .cm-s-mozilla .cm-quote,
 .cm-s-mozilla .cm-error,
 .variable-or-property .token-boolean,
 .variable-or-property .token-domnode,
 .variable-or-property[exception] > .title > .name {
   color: var(--theme-highlight-red);
 }
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -118,34 +118,29 @@ body {
 .variables-view-property > .title > .name {
   color: var(--theme-highlight-blue);
 }
 
 .CodeMirror-Tern-completion-array:before {
   background-color: var(--theme-highlight-bluegrey);
 }
 
-.theme-fg-color4 {
-  color: var(--theme-highlight-orange);
-}
-
-.theme-fg-color6,
+.theme-fg-color4,
 .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,
 .CodeMirror-Tern-completion-fn:before {
   background-color: hsl(24,85%,39%);
 }
 
-.theme-fg-color7,
 .cm-s-mozilla .cm-atom,
 .cm-s-mozilla .cm-quote,
 .cm-s-mozilla .cm-error,
 .variable-or-property .token-boolean,
 .variable-or-property .token-domnode,
 .variable-or-property[exception] > .title > .name {
   color: var(--theme-highlight-red);
 }
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -380,18 +380,16 @@ ul.children + .tag-line::before {
    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-color6,
-.theme-selected ~ .editor .theme-fg-color7,
 .theme-selected ~ .editor .close::before {
   color: var(--theme-selection-color);
 }
 
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -24,16 +24,17 @@
 #include "Layers.h"
 #include "nsAppRunner.h"
 // nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h.
 #include "nsNPAPIPluginInstance.h"
 #include "gfxDrawable.h"
 #include "gfxPrefs.h"
 #include "ImageOps.h"
 #include "mozAutoDocUpdate.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/AutoTimelineMarker.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
@@ -8865,31 +8866,20 @@ nsContentUtils::StorageDisabledByAntiTra
   }
 
   // Let's check if this is a 3rd party context.
   if (!IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
     return false;
   }
 
   if (aWindow) {
-    nsCOMPtr<nsIHttpChannel> httpChannel;
-    nsIDocument* document = aWindow->GetExtantDoc();
-    if (document) {
-      httpChannel = do_QueryInterface(document->GetChannel());
-    }
-
-    // If this is not a tracking resource, nothing is disabled.
-    if (!httpChannel || !httpChannel->GetIsTrackingResource()) {
-      return false;
-    }
-
-    // Maybe we want to grant this origin.
     nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
     if (documentURI &&
-        nsGlobalWindowInner::Cast(aWindow)->IsFirstPartyStorageAccessGrantedFor(documentURI)) {
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aWindow,
+                                                                documentURI)) {
       return false;
     }
 
     return true;
   }
 
   // aChannel and aWindow are mutually exclusive.
   MOZ_ASSERT(aChannel);
@@ -8905,23 +8895,18 @@ nsContentUtils::StorageDisabledByAntiTra
   }
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = httpChannel->GetURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
-  nsCOMPtr<nsILoadInfo> loadInfo;
-  rv = aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  return !loadInfo->IsFirstPartyStorageAccessGrantedFor(uri);
+  return AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                 uri);
 }
 
 // static, private
 nsContentUtils::StorageAccess
 nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
                                                    nsPIDOMWindowInner* aWindow,
                                                    nsIURI* aURI,
                                                    nsIChannel* aChannel)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7,16 +7,17 @@
 /*
  * Base class for all our document implementations.
  */
 
 #include "AudioChannelService.h"
 #include "nsDocument.h"
 #include "nsIDocumentInlines.h"
 #include "mozilla/AnimationComparator.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/CSSEnabledState.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/IntegerRange.h"
@@ -12449,17 +12450,18 @@ nsIDocument::MaybeAllowStorageForOpener(
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  nsGlobalWindowInner::Cast(openerInner)->AddFirstPartyStorageAccessGrantedFor(origin);
+  AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin,
+                                                           openerInner);
 }
 
 bool
 nsIDocument::HasBeenUserGestureActivated()
 {
   if (mUserGestureActivated) {
     return true;
   }
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -31,17 +31,16 @@
 #include "mozilla/dom/StorageUtils.h"
 #include "mozilla/dom/Timeout.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "mozilla/dom/WindowOrientationObserver.h"
 #endif
-#include "mozilla/StaticPrefs.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
 #include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
@@ -329,20 +328,16 @@ using mozilla::dom::cache::CacheStorage;
 #define MAX_SUCCESSIVE_DIALOG_COUNT 5
 
 // Idle fuzz time upper limit
 #define MAX_IDLE_FUZZ_TIME_MS 90000
 
 // Min idle notification time in seconds.
 #define MIN_IDLE_NOTIFICATION_TIME_S 1
 
-// Anti-tracking permission expiration
-#define ANTITRACKING_EXPIRATION 2592000000 // 30 days.
-#define ANTITRACKING_PERM_KEY "3rdPartyStorage"
-
 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
 
 static bool                 gIdleObserversAPIFuzzTimeDisabled = false;
 static FILE                *gDumpFile                         = nullptr;
 
 nsGlobalWindowInner::InnerWindowByIdTable *nsGlobalWindowInner::sInnerWindowsById = nullptr;
 
 bool nsGlobalWindowInner::sDragServiceDisabled = false;
@@ -917,18 +912,17 @@ nsGlobalWindowInner::nsGlobalWindowInner
     mSerial(0),
     mIdleRequestCallbackCounter(1),
     mIdleRequestExecutor(nullptr),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
     mObservingDidRefresh(false),
     mIteratingDocumentFlushedResolvers(false),
     mCanSkipCCGeneration(0),
-    mBeforeUnloadListenerCount(0),
-    mStorageGrantedOriginPopulated(false)
+    mBeforeUnloadListenerCount(0)
 {
   mIsInnerWindow = true;
 
   AssertIsOnMainThread();
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
@@ -1236,17 +1230,16 @@ nsGlobalWindowInner::FreeInnerObjects()
   }
 
   if (mIndexedDB) {
     mIndexedDB->DisconnectFromWindow(this);
     mIndexedDB = nullptr;
   }
 
   UnlinkHostObjectURIs();
-  ReleaseFirstPartyStorageAccessGrantedOrigins();
 
   NotifyWindowIDDestroyed("inner-window-destroyed");
 
   CleanupCachedXBLHandlers();
 
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     mAudioContexts[i]->Shutdown();
   }
@@ -1560,17 +1553,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
 
   tmp->UnlinkHostObjectURIs();
-  tmp->ReleaseFirstPartyStorageAccessGrantedOrigins();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
 
   // Here the IdleRequest list would've been unlinked, but we rely on
   // that IdleRequest objects have been traced and will remove
   // themselves while unlinking.
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
@@ -8057,299 +8049,16 @@ nsGlobalWindowInner::GetRegionalPrefsLoc
   AutoTArray<nsCString, 10> rpLocales;
   mozilla::intl::LocaleService::GetInstance()->GetRegionalPrefsLocales(rpLocales);
 
   for (const auto& loc : rpLocales) {
     aLocales.AppendElement(NS_ConvertUTF8toUTF16(loc));
   }
 }
 
-void
-nsGlobalWindowInner::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
-                                                          bool aOverwritten)
-{
-  MOZ_ASSERT(StaticPrefs::privacy_restrict3rdpartystorage_enabled());
-
-  if (aOverwritten) {
-    SaveFirstPartyStorageAccessGrantedFor(aOrigin);
-  }
-
-  for (StorageGrantedOrigin& data : mStorageGrantedOrigins) {
-    if (data.mOrigin == aOrigin) {
-      data.mOverwritten = aOverwritten;
-      return;
-    }
-  }
-
-  bool wasAllowed =
-    nsContentUtils::StorageDisabledByAntiTracking(this, nullptr, nullptr);
-
-  StorageGrantedOrigin* data = mStorageGrantedOrigins.AppendElement();
-  data->mOrigin = aOrigin;
-  data->mOverwritten = aOverwritten;
-
-  if (!wasAllowed &&
-      nsContentUtils::StorageDisabledByAntiTracking(this, nullptr, nullptr)) {
-    PropagateFirstPartyStorageAccessGrantedToWorkers(this);
-  }
-
-  // Let's store the origin in the loadInfo as well.
-  if (mDoc) {
-    nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
-    if (channel) {
-      nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
-      if (loadInfo) {
-        loadInfo->AddFirstPartyStorageAccessGrantedFor(aOrigin);
-      }
-    }
-  }
-}
-
-void
-nsGlobalWindowInner::GetFirstPartyStorageAccessGrantedOrigins(nsTArray<nsString>& aOrigin)
-{
-  aOrigin.Clear();
-
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
-    return;
-  }
-
-  MaybeRestoreFirstPartyStorageAccessGrantedOrigins();
-  for (const StorageGrantedOrigin& data : mStorageGrantedOrigins) {
-    aOrigin.AppendElement(data.mOrigin);
-  }
-}
-
-bool
-nsGlobalWindowInner::IsFirstPartyStorageAccessGrantedFor(nsIURI* aURI)
-{
-  MOZ_ASSERT(aURI);
-
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
-    return true;
-  }
-
-  MaybeRestoreFirstPartyStorageAccessGrantedOrigins();
-
-  if (mStorageGrantedOrigins.IsEmpty()) {
-    return false;
-  }
-
-  nsAutoString origin;
-  nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  for (const StorageGrantedOrigin& data : mStorageGrantedOrigins) {
-    if (data.mOrigin.Equals(origin)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void
-nsGlobalWindowInner::ReleaseFirstPartyStorageAccessGrantedOrigins()
-{
-  mStorageGrantedOriginPopulated = false;
-  mStorageGrantedOrigins.Clear();
-
-}
-
-namespace mozilla {
-namespace dom {
-
-extern void
-SendFirstPartyStorageAccessGrantedForOriginToParentProcess(nsIPrincipal* aPrincipal,
-                                                           const nsACString& aParentOrigin,
-                                                           const nsACString& aGrantedOrigin);
-
-} // namespace dom
-} // namespace mozilla
-
-void
-nsGlobalWindowInner::SaveFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin)
-{
-  MOZ_ASSERT(StaticPrefs::privacy_restrict3rdpartystorage_enabled());
-
-  // Now we need the principal and the origin of the parent window.
-  nsIPrincipal* parentPrincipal = GetTopLevelStorageAreaPrincipal();
-  if (NS_WARN_IF(!parentPrincipal)) {
-    return;
-  }
-
-  nsAutoCString parentOrigin;
-  nsresult rv = parentPrincipal->GetOriginNoSuffix(parentOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  // Let's take the principal and the origin of the current window.
-  nsIPrincipal* principal = GetPrincipal();
-  if (NS_WARN_IF(!principal)) {
-    return;
-  }
-
-  NS_ConvertUTF16toUTF8 grantedOrigin(aOrigin);
-
-  if (XRE_IsParentProcess()) {
-    SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(principal,
-                                                               parentOrigin,
-                                                               grantedOrigin);
-    return;
-  }
-
-  // We have this external function because ContentChild includes windows.h and
-  // for this reason it cannot be included here.
-  SendFirstPartyStorageAccessGrantedForOriginToParentProcess(principal,
-                                                             parentOrigin,
-                                                             grantedOrigin);
-}
-
-/* static */ void
-nsGlobalWindowInner::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
-                                                                                const nsCString& aParentOrigin,
-                                                                                const nsCString& aGrantedOrigin)
-{
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  if (NS_WARN_IF(!aPrincipal)) {
-    // The child process is sending something wrong. Let's ignore it.
-    return;
-  }
-
-  nsAutoCString origin;
-  nsresult rv = aPrincipal->GetOriginNoSuffix(origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
-  if (NS_WARN_IF(!pm)) {
-    return;
-  }
-
-  int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + ANTITRACKING_EXPIRATION;
-
-  // We store a permission for the 3rd party principal, to know that we grant
-  // the storage permission when loaded by the current parent origin.
-  nsAutoCString type;
-  if (origin == aGrantedOrigin) {
-    type = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s", aParentOrigin.get());
-  } else {
-    type = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s^%s", aParentOrigin.get(),
-                           aGrantedOrigin.get());
-  }
-
-  rv = pm->AddFromPrincipal(aPrincipal, type.get(),
-                            nsIPermissionManager::ALLOW_ACTION,
-                            nsIPermissionManager::EXPIRE_TIME, when);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-}
-
-void
-nsGlobalWindowInner::MaybeRestoreFirstPartyStorageAccessGrantedOrigins()
-{
-  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
-    return;
-  }
-
-  if (mStorageGrantedOriginPopulated) {
-    return;
-  }
-
-  mStorageGrantedOriginPopulated = true;
-
-  // Now we need the principal and the origin of the parent window.
-  nsIPrincipal* parentPrincipal = GetTopLevelStorageAreaPrincipal();
-  if (!parentPrincipal) {
-    // No parent window.
-    return;
-  }
-
-  nsAutoCString parentOrigin;
-  nsresult rv = parentPrincipal->GetOriginNoSuffix(parentOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  // Let's take the principal and the origin of the current window.
-  nsIPrincipal* principal = GetPrincipal();
-  if (NS_WARN_IF(!principal)) {
-    return;
-  }
-
-  nsAutoCString origin;
-  rv = principal->GetOriginNoSuffix(origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
-  if (NS_WARN_IF(!pm)) {
-    return;
-  }
-
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  rv = pm->GetAllForPrincipal(principal, getter_AddRefs(enumerator));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  bool more = false;
-  nsCOMPtr<nsISupports> iter;
-  nsCOMPtr<nsIPermission> perm;
-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) {
-    rv = enumerator->GetNext(getter_AddRefs(iter));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    perm = do_QueryInterface(iter);
-    if (NS_WARN_IF(!perm)) {
-      return;
-    }
-
-    nsAutoCString type;
-    rv = perm->GetType(type);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    if (!StringBeginsWith(type, NS_LITERAL_CSTRING(ANTITRACKING_PERM_KEY "^"))) {
-      continue;
-    }
-
-    nsCCharSeparatedTokenizer token(type, '^');
-    MOZ_ASSERT(token.hasMoreTokens());
-    auto value = token.nextToken();
-    MOZ_ASSERT(value.EqualsLiteral(ANTITRACKING_PERM_KEY));
-
-    nsAutoCString originA;
-    if (token.hasMoreTokens()) {
-      originA = token.nextToken();
-    }
-
-    // This permission was granted for another top-level window.
-    if (originA != parentOrigin) {
-      continue;
-    }
-
-    nsAutoCString originB;
-    if (token.hasMoreTokens()) {
-      originB = token.nextToken();
-    }
-
-    AddFirstPartyStorageAccessGrantedFor(NS_ConvertUTF8toUTF16(originB.IsEmpty() ? origin : originB),
-                                         false /* no overwrite */);
-  }
-}
-
 IntlUtils*
 nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError)
 {
   if (!mIntlUtils) {
     mIntlUtils = new IntlUtils(this);
   }
 
   return mIntlUtils;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -712,30 +712,16 @@ public:
   GetPaintWorklet(mozilla::ErrorResult& aRv);
 
   void
   GetRegionalPrefsLocales(nsTArray<nsString>& aLocales);
 
   mozilla::dom::IntlUtils*
   GetIntlUtils(mozilla::ErrorResult& aRv);
 
-  void
-  AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin, bool aOverwritten = true);
-
-  void
-  GetFirstPartyStorageAccessGrantedOrigins(nsTArray<nsString>& aOrigins);
-
-  bool
-  IsFirstPartyStorageAccessGrantedFor(nsIURI* aURI);
-
-  static void
-  SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
-                                                             const nsCString& aParentOrigin,
-                                                             const nsCString& aGrantedOrigin);
-
 public:
   void Alert(nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   void Alert(const nsAString& aMessage,
              nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage,
                nsIPrincipal& aSubjectPrincipal,
@@ -1068,25 +1054,16 @@ protected:
                      mozilla::dom::CallerType aCallerType,
                      mozilla::ErrorResult& aError);
   int32_t GetOuterHeight(mozilla::dom::CallerType aCallerType,
                          mozilla::ErrorResult& aError);
   void SetOuterHeight(int32_t aOuterHeight,
                       mozilla::dom::CallerType aCallerType,
                       mozilla::ErrorResult& aError);
 
-  void
-  ReleaseFirstPartyStorageAccessGrantedOrigins();
-
-  void
-  SaveFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin);
-
-  void
-  MaybeRestoreFirstPartyStorageAccessGrantedOrigins();
-
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
   nsCOMPtr<nsITimer> mIdleTimer;
 
   // Idle fuzz time added to idle timer callbacks.
   uint32_t mIdleFuzzFactor;
@@ -1124,19 +1101,16 @@ protected:
   nsresult ExecutionReady();
 
   // Inner windows only.
   nsresult DefineArgumentsProperty(nsIArray *aArguments);
 
   // Get the parent, returns null if this is a toplevel window
   nsPIDOMWindowOuter* GetParentInternal();
 
-  // Get the parent principal, returns null if this is a toplevel window.
-  nsIPrincipal* GetTopLevelStorageAreaPrincipal();
-
 public:
   // popup tracking
   bool IsPopupSpamWindow();
 
 private:
   // A type that methods called by CallOnChildren can return.  If Stop
   // is returned then CallOnChildren will stop calling further children.
   // If Continue is returned then CallOnChildren will keep calling further
@@ -1244,16 +1218,19 @@ public:
 
   // See PromiseWindowProxy.h for an explanation.
   void AddPendingPromise(mozilla::dom::Promise* aPromise);
   void RemovePendingPromise(mozilla::dom::Promise* aPromise);
 
 public:
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() override;
 
+  // Get the parent principal, returns null if this is a toplevel window.
+  nsIPrincipal* GetTopLevelStorageAreaPrincipal();
+
 protected:
   static void NotifyDOMWindowDestroyed(nsGlobalWindowInner* aWindow);
   void NotifyWindowIDDestroyed(const char* aTopic);
 
   static void NotifyDOMWindowFrozen(nsGlobalWindowInner* aWindow);
   static void NotifyDOMWindowThawed(nsGlobalWindowInner* aWindow);
 
   virtual void UpdateParentTarget() override;
@@ -1497,23 +1474,16 @@ protected:
   RefPtr<mozilla::dom::IntlUtils> mIntlUtils;
 
   mozilla::UniquePtr<mozilla::dom::ClientSource> mClientSource;
 
   nsTArray<RefPtr<mozilla::dom::Promise>> mPendingPromises;
 
   nsTArray<mozilla::UniquePtr<PromiseDocumentFlushedResolver>> mDocumentFlushedResolvers;
 
-  struct StorageGrantedOrigin {
-    nsString mOrigin;
-    bool mOverwritten;
-  };
-  nsTArray<StorageGrantedOrigin> mStorageGrantedOrigins;
-  bool mStorageGrantedOriginPopulated;
-
   static InnerWindowByIdTable* sInnerWindowsById;
 
   // Members in the mChromeFields member should only be used in chrome windows.
   // All accesses to this field should be guarded by a check of mIsChrome.
   struct ChromeFields {
     ChromeFields()
       : mGroupMessageManagers(1)
     {}
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -12,16 +12,17 @@
 
 // Local Includes
 #include "Navigator.h"
 #include "nsContentSecurityManager.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/StorageNotifierService.h"
@@ -7073,17 +7074,17 @@ nsGlobalWindowOuter::MaybeAllowStorageFo
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  inner->AddFirstPartyStorageAccessGrantedFor(origin, true);
+  AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(origin, inner);
 }
 
 //*****************************************************************************
 // nsGlobalWindowOuter: Helper Functions
 //*****************************************************************************
 
 already_AddRefed<nsIDocShellTreeOwner>
 nsGlobalWindowOuter::GetTreeOwner()
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3301,35 +3301,16 @@ NextWindowID()
   uint64_t windowID = ++gNextWindowID;
 
   MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
   uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);
 
   return (processBits << kWindowIDWindowBits) | windowBits;
 }
 
-// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
-// can't include ContentChild.h since it includes windows.h.
-void
-SendFirstPartyStorageAccessGrantedForOriginToParentProcess(nsIPrincipal* aPrincipal,
-                                                           const nsACString& aParentOrigin,
-                                                           const nsACString& aGrantedOrigin)
-{
-  MOZ_ASSERT(!XRE_IsParentProcess());
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-
-  // This is not really secure, because here we have the content process sending
-  // the request of storing a permission.
-  Unused << cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(aPrincipal),
-                                                            nsCString(aParentOrigin),
-                                                            nsCString(aGrantedOrigin));
-}
-
 mozilla::ipc::IPCResult
 ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                                     const uint32_t& aAction)
 {
   nsCOMPtr<nsIDragService> dragService =
     do_GetService("@mozilla.org/widget/dragservice;1");
   if (dragService) {
     dragService->StartDragSession();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -858,17 +858,12 @@ private:
 #endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 uint64_t
 NextWindowID();
 
-void
-SendFirstPartyStorageAccessGrantedForOriginToParentProcess(nsIPrincipal* aPrincipal,
-                                                           const nsACString& aParentOrigin,
-                                                           const nsACString& aGrantedOrigin);
-
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ContentChild_h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -24,16 +24,17 @@
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #include "mozilla/a11y/AccessibleWrap.h"
 #include "mozilla/a11y/Compatibility.h"
 #endif
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientOpenWindowOpActors.h"
@@ -112,17 +113,16 @@
 #include "nsCOMPtr.h"
 #include "nsChromeRegistryChrome.h"
 #include "nsConsoleMessage.h"
 #include "nsConsoleService.h"
 #include "nsContentUtils.h"
 #include "nsDebugImpl.h"
 #include "nsFrameLoader.h"
 #include "nsFrameMessageManager.h"
-#include "nsGlobalWindowInner.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIClipboard.h"
 #include "nsICookie.h"
 #include "nsContentPermissionHelper.h"
 #include "nsIContentProcess.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDocShellTreeOwner.h"
@@ -5755,17 +5755,17 @@ ContentParent::RecvBHRThreadHang(const H
     nsCOMPtr<nsIHangDetails> hangDetails =
       new nsHangDetails(HangDetails(aDetails));
     obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aPrincipal,
-                                                           const nsCString& aParentOrigin,
+ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
+                                                           const nsCString& aTrackingOrigin,
                                                            const nsCString& aGrantedOrigin)
 {
-  nsGlobalWindowInner::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(aPrincipal,
-                                                                                  aParentOrigin,
-                                                                                  aGrantedOrigin);
+  AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(aParentPrincipal,
+                                                                                 aTrackingOrigin,
+                                                                                 aGrantedOrigin);
   return IPC_OK();
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1220,18 +1220,18 @@ public:
     nsTArray<ChildEventData>&& events) override;
   virtual mozilla::ipc::IPCResult RecvRecordDiscardedData(
     const DiscardedData& aDiscardedData) override;
 
   virtual mozilla::ipc::IPCResult RecvBHRThreadHang(
     const HangDetails& aHangDetails) override;
 
   virtual mozilla::ipc::IPCResult
-  RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aPrincipal,
-                                              const nsCString& aParentOrigin,
+  RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aParentPrincipal,
+                                              const nsCString& aTrackingOrigin,
                                               const nsCString& aGrantedOrigin) override;
 
   // Notify the ContentChild to enable the input event prioritization when
   // initializing.
   void MaybeEnableRemoteInputEventQueue();
 
 public:
   void SendGetFilesResponseAndForget(const nsID& aID,
--- a/dom/ipc/MemMapSnapshot.cpp
+++ b/dom/ipc/MemMapSnapshot.cpp
@@ -113,29 +113,17 @@ MemMapSnapshot::Freeze(AutoMemMap& aMem)
   // since we open and share a read-only file descriptor, and then delete the
   // file. But it doesn't hurt, either.
   chmod(mPath.get(), 0400);
 
   nsCOMPtr<nsIFile> file;
   MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false,
                                 getter_AddRefs(file)));
 
-  auto result = aMem.init(file);
-#ifdef XP_LINUX
-  // On Linux automation runs, every few hundred thousand calls, our attempt to
-  // stat the file that we just successfully opened fails with EBADF (bug
-  // 1472889). Presumably this is a race with a background thread that double
-  // closes a file, but is difficult to diagnose, so work around it by making a
-  // second mapping attempt if the first one fails.
-  if (!result.isOk()) {
-    aMem.reset();
-    result = aMem.init(file);
-  }
-#endif
-  return result;
+  return aMem.init(file);
 }
 
 #else
 #  error "Unsupported build configuration"
 #endif
 
 } // namespace ipc
 } // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1155,21 +1155,21 @@ parent:
 
     async MaybeReloadPlugins();
 
     async BHRThreadHang(HangDetails aHangDetails);
 
     async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
 
     /*
-     * A 3rd party context (aPrincipal) has received the permission granted to
-     * have access to aGrantedOrigin when loaded by aParentOrigin.
+     * A 3rd party tracking origin (aTrackingOrigin) has received the permission
+     * granted to have access to aGrantedOrigin when loaded by aParentPrincipal.
      */
-    async FirstPartyStorageAccessGrantedForOrigin(Principal aPrincipal,
-                                                  nsCString aParentOrigin,
+    async FirstPartyStorageAccessGrantedForOrigin(Principal aParentPrincipal,
+                                                  nsCString aTrackingOrigin,
                                                   nsCString aGrantedOrigin);
 
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1076,20 +1076,16 @@ TabChild::DestroyWindow()
         mPuppetWidget->Destroy();
     }
 
     if (mRemoteFrame) {
         mRemoteFrame->Destroy();
         mRemoteFrame = nullptr;
     }
 
-    if (mApzcTreeManager) {
-      mApzcTreeManager->Destroy();
-      mApzcTreeManager = nullptr;
-    }
 
     if (mLayersId.IsValid()) {
       StaticMutexAutoLock lock(sTabChildrenMutex);
 
       MOZ_ASSERT(sTabChildren);
       sTabChildren->Remove(uint64_t(mLayersId));
       if (!sTabChildren->Count()) {
         delete sTabChildren;
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/MessagePortTimelineMarker.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/TimelineMarker.h"
 #include "mozilla/Unused.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsPresContext.h"
 #include "SharedMessagePortMessage.h"
 
@@ -248,25 +249,25 @@ MessagePort::UnshippedEntangle(MessagePo
   MOZ_ASSERT(!mUnshippedEntangledPort);
 
   mUnshippedEntangledPort = aEntangledPort;
 }
 
 void
 MessagePort::Initialize(const nsID& aUUID,
                         const nsID& aDestinationUUID,
-                        uint32_t aSequenceID, bool mNeutered,
+                        uint32_t aSequenceID, bool aNeutered,
                         ErrorResult& aRv)
 {
   MOZ_ASSERT(mIdentifier);
   mIdentifier->uuid() = aUUID;
   mIdentifier->destinationUuid() = aDestinationUUID;
   mIdentifier->sequenceId() = aSequenceID;
 
-  if (mNeutered) {
+  if (aNeutered) {
     // If this port is neutered we don't want to keep it alive artificially nor
     // we want to add listeners or WorkerRefs.
     mState = eStateDisentangled;
     return;
   }
 
   if (mState == eStateEntangling) {
     if (!ConnectToPBackground()) {
@@ -503,21 +504,20 @@ MessagePort::CloseInternal(bool aSoftly)
 {
   // If we have some messages to send but we don't want a 'soft' close, we have
   // to flush them now.
   if (!aSoftly) {
     mMessages.Clear();
   }
 
   if (mState == eStateUnshippedEntangled) {
-    MOZ_ASSERT(mUnshippedEntangledPort);
+    MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
 
     // This avoids loops.
     RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
-    MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
 
     mState = eStateDisentangledForClose;
     port->CloseInternal(aSoftly);
 
     UpdateMustKeepAlive();
     return;
   }
 
@@ -738,24 +738,24 @@ MessagePort::CloneAndDisentangle(Message
   aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
   aIdentifier.neutered() = false;
 
   // We have to entangle first.
   if (mState == eStateUnshippedEntangled) {
     MOZ_ASSERT(mUnshippedEntangledPort);
     MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
 
+    RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
+
     // Disconnect the entangled port and connect it to PBackground.
-    if (!mUnshippedEntangledPort->ConnectToPBackground()) {
+    if (!port->ConnectToPBackground()) {
       // We are probably shutting down. We cannot proceed.
       return;
     }
 
-    mUnshippedEntangledPort = nullptr;
-
     // In this case, we don't need to be connected to the PBackground service.
     if (mMessages.IsEmpty()) {
       aIdentifier.sequenceId() = mIdentifier->sequenceId();
 
       mState = eStateDisentangled;
       UpdateMustKeepAlive();
       return;
     }
@@ -795,17 +795,21 @@ MessagePort::Closed()
   }
 
   UpdateMustKeepAlive();
 }
 
 bool
 MessagePort::ConnectToPBackground()
 {
-  mState = eStateEntangling;
+  RefPtr<MessagePort> self = this;
+  auto raii = MakeScopeExit([self] {
+    self->mState = eStateDisentangled;
+    self->UpdateMustKeepAlive();
+  });
 
   mozilla::ipc::PBackgroundChild* actorChild =
     mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!actorChild)) {
     return false;
   }
 
   PMessagePortChild* actor =
@@ -815,16 +819,19 @@ MessagePort::ConnectToPBackground()
   if (NS_WARN_IF(!actor)) {
     return false;
   }
 
   mActor = static_cast<MessagePortChild*>(actor);
   MOZ_ASSERT(mActor);
 
   mActor->SetPort(this);
+  mState = eStateEntangling;
+
+  raii.release();
   return true;
 }
 
 void
 MessagePort::UpdateMustKeepAlive()
 {
   if (mState >= eStateDisentangled &&
       mMessages.IsEmpty() &&
@@ -842,20 +849,17 @@ MessagePort::UpdateMustKeepAlive()
     mIsKeptAlive = true;
     AddRef();
   }
 }
 
 void
 MessagePort::DisconnectFromOwner()
 {
-  if (mIsKeptAlive) {
-    CloseForced();
-  }
-
+  CloseForced();
   DOMEventTargetHelper::DisconnectFromOwner();
 }
 
 void
 MessagePort::RemoveDocFromBFCache()
 {
   if (!NS_IsMainThread()) {
     return;
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -135,17 +135,17 @@ private:
   };
 
   explicit MessagePort(nsIGlobalObject* aGlobal, State aState);
   ~MessagePort();
 
   void DisconnectFromOwner() override;
 
   void Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
-                  uint32_t aSequenceID, bool mNeutered, ErrorResult& aRv);
+                  uint32_t aSequenceID, bool aNeutered, ErrorResult& aRv);
 
   bool ConnectToPBackground();
 
   // Dispatch events from the Message Queue using a nsRunnable.
   void Dispatch();
 
   void DispatchError();
 
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -225,29 +225,27 @@ enum ExtendableEventResult {
 class ExtendableEventCallback {
 public:
   virtual void
   FinishedWithResult(ExtendableEventResult aResult) = 0;
 
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 };
 
-class KeepAliveHandler final : public WorkerHolder
-                             , public ExtendableEvent::ExtensionsHandler
+class KeepAliveHandler final : public ExtendableEvent::ExtensionsHandler
                              , public PromiseNativeHandler
 {
   // This class manages lifetime extensions added by calling WaitUntil()
   // or RespondWith(). We allow new extensions as long as we still hold
   // |mKeepAliveToken|. Once the last promise was settled, we queue a microtask
   // which releases the token and prevents further extensions. By doing this,
   // we give other pending microtasks a chance to continue adding extensions.
 
+  RefPtr<StrongWorkerRef> mWorkerRef;
   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-  WorkerPrivate* MOZ_NON_OWNING_REF mWorkerPrivate;
-  bool mWorkerHolderAdded;
 
   // We start holding a self reference when the first extension promise is
   // added. As far as I can tell, the only case where this is useful is when
   // we're waiting indefinitely on a promise that's no longer reachable
   // and will never be settled.
   // The cycle is broken when the last promise was settled or when the
   // worker is shutting down.
   RefPtr<KeepAliveHandler> mSelfRef;
@@ -261,36 +259,42 @@ class KeepAliveHandler final : public Wo
   // any of them were rejected.
   bool mRejected;
 
 public:
   NS_DECL_ISUPPORTS
 
   explicit KeepAliveHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
                             ExtendableEventCallback* aCallback)
-    : WorkerHolder("KeepAliveHolder")
-    , mKeepAliveToken(aKeepAliveToken)
-    , mWorkerPrivate(GetCurrentThreadWorkerPrivate())
-    , mWorkerHolderAdded(false)
+    : mKeepAliveToken(aKeepAliveToken)
     , mCallback(aCallback)
     , mPendingPromisesCount(0)
     , mRejected(false)
   {
     MOZ_ASSERT(mKeepAliveToken);
-    MOZ_ASSERT(mWorkerPrivate);
   }
 
   bool
-  UseWorkerHolder()
+  Init()
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(!mWorkerHolderAdded);
-    mWorkerHolderAdded = HoldWorker(mWorkerPrivate, Canceling);
-    return mWorkerHolderAdded;
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
+
+    RefPtr<KeepAliveHandler> self = this;
+    mWorkerRef =
+      StrongWorkerRef::Create(GetCurrentThreadWorkerPrivate(),
+                              "KeepAliveHandler",
+                              [self]() {
+        self->MaybeCleanup();
+      });
+
+    if (NS_WARN_IF(!mWorkerRef)) {
+      return false;
+    }
+
+    return true;
   }
 
   bool
   WaitOnPromise(Promise& aPromise) override
   {
     if (!mKeepAliveToken) {
       MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
       return false;
@@ -313,34 +317,20 @@ public:
   }
 
   void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     RemovePromise(Rejected);
   }
 
-  bool
-  Notify(WorkerStatus aStatus) override
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    if (aStatus < Canceling) {
-      return true;
-    }
-
-    MaybeCleanup();
-    return true;
-  }
-
   void
   MaybeDone()
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
 
     if (mPendingPromisesCount || !mKeepAliveToken) {
       return;
     }
     if (mCallback) {
       mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
     }
 
@@ -351,25 +341,23 @@ private:
   ~KeepAliveHandler()
   {
     MaybeCleanup();
   }
 
   void
   MaybeCleanup()
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
+
     if (!mKeepAliveToken) {
       return;
     }
-    if (mWorkerHolderAdded) {
-      ReleaseWorker();
-    }
 
+    mWorkerRef = nullptr;
     mKeepAliveToken = nullptr;
     mSelfRef = nullptr;
   }
 
   class MaybeDoneRunner : public MicroTaskRunnable
   {
   public:
     explicit MaybeDoneRunner(KeepAliveHandler* aHandler) : mHandler(aHandler) {}
@@ -379,18 +367,17 @@ private:
     }
 
     RefPtr<KeepAliveHandler> mHandler;
   };
 
   void
   RemovePromise(ExtendableEventResult aResult)
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
     MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
 
     // Note: mSelfRef and mKeepAliveToken can be nullptr here
     //       if MaybeCleanup() was called just before a promise
     //       settled.  This can happen, for example, if the
     //       worker thread is being terminated for running too
     //       long, browser shutdown, etc.
 
@@ -466,17 +453,17 @@ public:
   {
     MOZ_ASSERT(aWorkerScope);
     MOZ_ASSERT(aEvent);
     nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
     WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
 
     RefPtr<KeepAliveHandler> keepAliveHandler =
       new KeepAliveHandler(mKeepAliveToken, aCallback);
-    if (NS_WARN_IF(!keepAliveHandler->UseWorkerHolder())) {
+    if (NS_WARN_IF(!keepAliveHandler->Init())) {
       return NS_ERROR_FAILURE;
     }
 
     // This must always be set *before* dispatching the event, otherwise
     // waitUntil calls will fail.
     aEvent->SetKeepAliveHandler(keepAliveHandler);
 
     ErrorResult result;
@@ -666,110 +653,90 @@ private:
 
 };
 
 /*
  * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
  * termination during the execution of life cycle events. It is responsible
  * with advancing the job queue for install/activate tasks.
  */
-class LifeCycleEventWatcher final : public ExtendableEventCallback,
-                                    public WorkerHolder
+class LifeCycleEventWatcher final : public ExtendableEventCallback
 {
-  WorkerPrivate* mWorkerPrivate;
+  RefPtr<StrongWorkerRef> mWorkerRef;
   RefPtr<LifeCycleEventCallback> mCallback;
-  bool mDone;
 
   ~LifeCycleEventWatcher()
   {
-    if (mDone) {
-      return;
-    }
-
-    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
     // XXXcatalinb: If all the promises passed to waitUntil go out of scope,
     // the resulting Promise.all will be cycle collected and it will drop its
     // native handlers (including this object). Instead of waiting for a timeout
     // we report the failure now.
     ReportResult(false);
   }
 
 public:
   NS_INLINE_DECL_REFCOUNTING(LifeCycleEventWatcher, override)
 
-  LifeCycleEventWatcher(WorkerPrivate* aWorkerPrivate,
-                        LifeCycleEventCallback* aCallback)
-    : WorkerHolder("LifeCycleEventWatcher")
-    , mWorkerPrivate(aWorkerPrivate)
-    , mCallback(aCallback)
-    , mDone(false)
+  explicit LifeCycleEventWatcher(LifeCycleEventCallback* aCallback)
+    : mCallback(aCallback)
   {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
   }
 
   bool
   Init()
   {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
 
     // We need to listen for worker termination in case the event handler
     // never completes or never resolves the waitUntil promise. There are
     // two possible scenarios:
     // 1. The keepAlive token expires and the worker is terminated, in which
     //    case the registration/update promise will be rejected
     // 2. A new service worker is registered which will terminate the current
     //    installing worker.
-    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate, Canceling))) {
-      NS_WARNING("LifeCycleEventWatcher failed to add feature.");
-      ReportResult(false);
+    RefPtr<LifeCycleEventWatcher> self = this;
+    mWorkerRef =
+      StrongWorkerRef::Create(workerPrivate, "LifeCycleEventWatcher",
+                              [self]() {
+      self->ReportResult(false);
+    });
+    if (NS_WARN_IF(!mWorkerRef)) {
+      mCallback->SetResult(false);
+      nsresult rv = workerPrivate->DispatchToMainThread(mCallback);
+      Unused << NS_WARN_IF(NS_FAILED(rv));
       return false;
     }
 
     return true;
   }
 
-  bool
-  Notify(WorkerStatus aStatus) override
-  {
-    if (aStatus < Canceling) {
-      return true;
-    }
-
-    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
-    ReportResult(false);
-
-    return true;
-  }
-
   void
   ReportResult(bool aResult)
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
 
-    if (mDone) {
+    if (!mWorkerRef) {
       return;
     }
-    mDone = true;
 
     mCallback->SetResult(aResult);
-    nsresult rv = mWorkerPrivate->DispatchToMainThread(mCallback);
+    nsresult rv = mWorkerRef->Private()->DispatchToMainThread(mCallback);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       MOZ_CRASH("Failed to dispatch life cycle event handler.");
     }
 
-    ReleaseWorker();
+    mWorkerRef = nullptr;
   }
 
   void
   FinishedWithResult(ExtendableEventResult aResult) override
   {
-    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
     ReportResult(aResult == Resolved);
 
     // Note, all WaitUntil() rejections are reported to client consoles
     // by the WaitUntilHandler in ServiceWorkerEvents.  This ensures that
     // errors in non-lifecycle events like FetchEvent and PushEvent are
     // reported properly.
   }
 };
@@ -793,18 +760,17 @@ LifecycleEventWorkerRunnable::DispatchLi
     MOZ_CRASH("Unexpected lifecycle event");
   }
 
   event->SetTrusted(true);
 
   // It is important to initialize the watcher before actually dispatching
   // the event in order to catch worker termination while the event handler
   // is still executing. This can happen with infinite loops, for example.
-  RefPtr<LifeCycleEventWatcher> watcher =
-    new LifeCycleEventWatcher(aWorkerPrivate, mCallback);
+  RefPtr<LifeCycleEventWatcher> watcher = new LifeCycleEventWatcher(mCallback);
 
   if (!watcher->Init()) {
     return true;
   }
 
   nsresult rv = DispatchExtendableEventOnWorkerScope(aCx,
                                                      aWorkerPrivate->GlobalScope(),
                                                      event,
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,15 +1,31 @@
+Overview of changes leading to 1.8.3
+Wednesday, July 11, 2018
+====================================
+- A couple of Indic / USE bug fixes.
+- Disable vectorization, as it was causing unaligned access bus error on
+  certain 32bit architectures.
+
+
+Overview of changes leading to 1.8.2
+Tuesday, July 3, 2018
+====================================
+- Fix infinite loop in Khmer shaper.
+- Improve hb_blob_create_from_file() for streams.
+
+
 Overview of changes leading to 1.8.1
 Tuesday, June 12, 2018
 ====================================
 - Fix hb-version.h file generation; last two releases went out with wrong ones.
 - Add correctness bug in hb_set_t operations, introduced in 1.7.7.
 - Remove HB_SUBSET_BUILTIN build option.  Not necessary.
 
+
 Overview of changes leading to 1.8.0
 Tuesday, June 5, 2018
 ====================================
 - Update to Unicode 11.0.0.
 
 
 Overview of changes leading to 1.7.7
 Tuesday, June 5, 2018
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,15 +1,17 @@
-gfx/harfbuzz status as of 2018-06-12:
+This directory contains the HarfBuzz source from the upstream repo:
+https://github.com/harfbuzz/harfbuzz
 
-This directory contains the HarfBuzz source from the 'master' branch of
-https://github.com/behdad/harfbuzz.
-
-Current version: 1.8.1
+Current version: 1.8.3 [commit 2b76767bf572364d3d647cdd139f2044a7ad06b2]
 
 UPDATING:
 
 Our in-tree copy of HarfBuzz does not depend on any generated files from the
 upstream build system. Therefore, it should be sufficient to simply overwrite
 the in-tree files one the updated ones from upstream to perform updates.
 
+To simplify this, the in-tree copy can be updated by running
+  sh update.sh
+from within the gfx/harfbuzz directory.
+
 If the collection of source files changes, manual updates to moz.build may be
 needed as we don't use the upstream makefiles.
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.1],
+        [1.8.3],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
@@ -73,17 +73,17 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl]
 	if test "x$enable_gtk_doc" = xyes; then
 		have_gtk_doc=true
 	fi
 ], [
 	AM_CONDITIONAL([ENABLE_GTK_DOC], false)
 ])
 
 # Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l posix_memalign)
 
 save_libs="$LIBS"
 LIBS="$LIBS -lm"
 AC_CHECK_FUNCS([round], ,[AC_CHECK_DECLS([round], , ,[#include <math.h>])])
 LIBS="$save_libs"
 
 AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h stdbool.h)
 
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -272,23 +272,23 @@ CLEANFILES += $(pkgconfig_DATA)
 
 DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
 if HAVE_GOBJECT
 DEF_FILES += harfbuzz-gobject.def
 endif
 check: $(DEF_FILES) # For check-symbols.sh
 CLEANFILES += $(DEF_FILES)
 harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-subset.def: $(HB_SUBSET_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-icu.def: $(HB_ICU_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-gobject.def: $(HB_GOBJECT_headers)
-	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py "$@"
+	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	gen-use-table.py \
 	gen-def.py \
 	$(NULL)
--- a/gfx/harfbuzz/src/gen-def.py
+++ b/gfx/harfbuzz/src/gen-def.py
@@ -1,19 +1,25 @@
 #!/usr/bin/env python
 
 from __future__ import print_function, division, absolute_import
 
 import io, os, re, sys
 
+if len (sys.argv) < 3:
+	sys.exit("usage: gen-def.py harfbuzz.def hb.h [hb-blob.h hb-buffer.h ...]")
+
+output_file = sys.argv[1]
+header_paths = sys.argv[2:]
+
 headers_content = []
-for h in os.environ["headers"].split (' '):
+for h in header_paths:
 	if h.endswith (".h"):
 		with io.open (h, encoding='utf-8') as f: headers_content.append (f.read ())
 
 result = """EXPORTS
 %s
 LIBRARY lib%s-0.dll""" % (
 	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))),
-	sys.argv[1].replace ('.def', '')
+	output_file.replace ('.def', '')
 )
 
-with open (sys.argv[1], "w") as f: f.write (result)
+with open (output_file, "w") as f: f.write (result)
--- a/gfx/harfbuzz/src/gen-use-table.py
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -42,16 +42,19 @@ for i, f in enumerate (files):
 		values[i][t] = values[i].get (t, 0) + end - start + 1
 
 defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
 
 # TODO Characters that are not in Unicode Indic files, but used in USE
 data[0][0x034F] = defaults[0]
 data[0][0x2060] = defaults[0]
 data[0][0x20F0] = defaults[0]
+# TODO https://github.com/roozbehp/unicode-data/issues/9
+data[0][0x11C44] = 'Consonant_Placeholder'
+data[0][0x11C45] = 'Consonant_Placeholder'
 for u in range (0xFE00, 0xFE0F + 1):
 	data[0][u] = defaults[0]
 
 # Merge data into one dict:
 for i,v in enumerate (defaults):
 	values[i][v] = values[i].get (v, 0) + 1
 combined = {}
 for i,d in enumerate (data):
@@ -160,17 +163,17 @@ def is_BASE(U, UISC, UGC):
 			Tone_Letter,
 			Vowel_Independent #SPEC-DRAFT
 			] or
 		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
 					Consonant_Subjoined, Vowel, Vowel_Dependent]))
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45]) or
+		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
 	return UISC == Brahmi_Joining_Number
 def is_BASE_OTHER(U, UISC, UGC):
 	if UISC == Consonant_Placeholder: return True #SPEC-DRAFT
 	#SPEC-DRAFT return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
 	return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
@@ -339,16 +342,19 @@ def map_to_use(data):
 
 		# TODO: In USE's override list but not in Unicode 11.0
 		if U == 0x103C: UIPC = Left
 
 		# TODO: These are not in USE's override list that we have, nor are they in Unicode 11.0
 		if 0xA926 <= U <= 0xA92A: UIPC = Top
 		if U == 0x111CA: UIPC = Bottom
 		if U == 0x11300: UIPC = Top
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
+		if U == 0x11302: UIPC = Top
+		if U == 0x1133C: UIPC = Bottom
 		if U == 0x1171E: UIPC = Left # Correct?!
 		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
--- a/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
@@ -167,29 +167,29 @@ struct trak
     hb_buffer_t *buffer = c->buffer;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
     {
       const TrackData &trackData = this+horizData;
       float tracking = trackData.get_tracking (this, ptem);
       hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
       foreach_grapheme (buffer, start, end)
       {
-	/* TODO This is wrong. */
+	buffer->pos[start].x_offset += advance_to_add;
 	buffer->pos[start].x_advance += advance_to_add;
 	buffer->pos[end].x_advance += advance_to_add;
       }
     }
     else
     {
       const TrackData &trackData = this+vertData;
       float tracking = trackData.get_tracking (this, ptem);
       hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
       foreach_grapheme (buffer, start, end)
       {
-	/* TODO This is wrong. */
+	buffer->pos[start].y_offset += advance_to_add;
 	buffer->pos[start].y_advance += advance_to_add;
 	buffer->pos[end].y_advance += advance_to_add;
       }
     }
 
     return_trace (true);
   }
 
--- a/gfx/harfbuzz/src/hb-blob-private.hh
+++ b/gfx/harfbuzz/src/hb-blob-private.hh
@@ -26,18 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_BLOB_PRIVATE_HH
 #define HB_BLOB_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-object-private.hh"
-
 
 /*
  * hb_blob_t
  */
 
 struct hb_blob_t
 {
   inline void fini_shallow (void)
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -26,17 +26,16 @@
  */
 
 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
 #ifndef _POSIX_C_SOURCE
 #define _POSIX_C_SOURCE 200809L
 #endif
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-blob-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <sys/mman.h>
 #endif /* HAVE_SYS_MMAN_H */
@@ -484,127 +483,161 @@ hb_blob_t::try_make_writable (void)
 #ifdef HAVE_MMAP
 # include <sys/types.h>
 # include <sys/stat.h>
 # include <fcntl.h>
 #endif
 
 #if defined(_WIN32) || defined(__CYGWIN__)
 # include <windows.h>
-#endif
-
-#ifndef _O_BINARY
-# define _O_BINARY 0
+#else
+# ifndef _O_BINARY
+#  define _O_BINARY 0
+# endif
 #endif
 
 #ifndef MAP_NORESERVE
 # define MAP_NORESERVE 0
 #endif
 
 struct hb_mapped_file_t
 {
   char *contents;
   unsigned long length;
 #if defined(_WIN32) || defined(__CYGWIN__)
   HANDLE mapping;
 #endif
 };
 
+#if (defined(HAVE_MMAP) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
 static void
 _hb_mapped_file_destroy (hb_mapped_file_t *file)
 {
 #ifdef HAVE_MMAP
   munmap (file->contents, file->length);
 #elif defined(_WIN32) || defined(__CYGWIN__)
   UnmapViewOfFile (file->contents);
   CloseHandle (file->mapping);
 #else
-  free (file->contents);
+  assert (0); // If we don't have mmap we shouldn't reach here
 #endif
 
   free (file);
 }
+#endif
 
 /**
  * hb_blob_create_from_file:
  * @file_name: font filename.
  *
  * Returns: A hb_blob_t pointer with the content of the file
  *
  * Since: 1.7.7
  **/
 hb_blob_t *
 hb_blob_create_from_file (const char *file_name)
 {
-  // Adopted from glib's gmappedfile.c with Matthias Clasen and
-  // Allison Lortie permission but changed a lot to suit our need.
-  bool writable = false;
-  hb_memory_mode_t mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+  /* Adopted from glib's gmappedfile.c with Matthias Clasen and
+     Allison Lortie permission but changed a lot to suit our need. */
+#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)
   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
   if (unlikely (!file)) return hb_blob_get_empty ();
 
-#ifdef HAVE_MMAP
-  int fd = open (file_name, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
-# define CLOSE close
+  int fd = open (file_name, O_RDONLY | _O_BINARY, 0);
   if (unlikely (fd == -1)) goto fail_without_close;
 
   struct stat st;
   if (unlikely (fstat (fd, &st) == -1)) goto fail;
 
-  // See https://github.com/GNOME/glib/blob/f9faac7/glib/gmappedfile.c#L139-L142
-  if (unlikely (st.st_size == 0 && S_ISREG (st.st_mode))) goto fail;
-
   file->length = (unsigned long) st.st_size;
-  file->contents = (char *) mmap (nullptr, file->length,
-				  writable ? PROT_READ|PROT_WRITE : PROT_READ,
+  file->contents = (char *) mmap (nullptr, file->length, PROT_READ,
 				  MAP_PRIVATE | MAP_NORESERVE, fd, 0);
 
   if (unlikely (file->contents == MAP_FAILED)) goto fail;
 
-#elif defined(_WIN32) || defined(__CYGWIN__)
-  HANDLE fd = CreateFile (file_name,
-			  writable ? GENERIC_READ|GENERIC_WRITE : GENERIC_READ,
-			  FILE_SHARE_READ, nullptr, OPEN_EXISTING,
-			  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr);
-# define CLOSE CloseHandle
+  close (fd);
+
+  return hb_blob_create (file->contents, file->length,
+			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
+			 (hb_destroy_func_t) _hb_mapped_file_destroy);
+
+fail:
+  close (fd);
+fail_without_close:
+  free (file);
+
+#elif (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
+  hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
+  if (unlikely (!file)) return hb_blob_get_empty ();
+
+  HANDLE fd;
+  unsigned int size = strlen (file_name) + 1;
+  wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
+  if (unlikely (wchar_file_name == nullptr)) goto fail_without_close;
+  mbstowcs (wchar_file_name, file_name, size);
+  fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
+		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
+		    nullptr);
+  free (wchar_file_name);
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
   file->length = (unsigned long) GetFileSize (fd, nullptr);
-  file->mapping = CreateFileMapping (fd, nullptr,
-				     writable ? PAGE_WRITECOPY : PAGE_READONLY,
-				     0, 0, nullptr);
+  file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
   if (unlikely (file->mapping == nullptr)) goto fail;
 
-  file->contents = (char *) MapViewOfFile (file->mapping,
-					   writable ? FILE_MAP_COPY : FILE_MAP_READ,
-					   0, 0, 0);
+  file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
   if (unlikely (file->contents == nullptr)) goto fail;
 
-#else
-  mm = HB_MEMORY_MODE_WRITABLE;
-
-  FILE *fd = fopen (file_name, "rb");
-# define CLOSE fclose
-  if (unlikely (!fd)) goto fail_without_close;
+  CloseHandle (fd);
+  return hb_blob_create (file->contents, file->length,
+			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
+			 (hb_destroy_func_t) _hb_mapped_file_destroy);
 
-  fseek (fd, 0, SEEK_END);
-  file->length = ftell (fd);
-  rewind (fd);
-  file->contents = (char *) malloc (file->length);
-  if (unlikely (!file->contents)) goto fail;
-
-  if (unlikely (fread (file->contents, 1, file->length, fd) != file->length))
-    goto fail;
+fail:
+  CloseHandle (fd);
+fail_without_close:
+  free (file);
 
 #endif
 
-  CLOSE (fd);
-  return hb_blob_create (file->contents, file->length, mm, (void *) file,
-			 (hb_destroy_func_t) _hb_mapped_file_destroy);
+  /* The following tries to read a file without knowing its size beforehand
+     It's used as a fallback for systems without mmap or to read from pipes */
+  unsigned long len = 0, allocated = BUFSIZ * 16;
+  char *data = (char *) malloc (allocated);
+  if (unlikely (data == nullptr)) return hb_blob_get_empty ();
+
+  FILE *fp = fopen (file_name, "rb");
+  if (unlikely (fp == nullptr)) goto fread_fail_without_close;
 
-fail:
-  CLOSE (fd);
-#undef CLOSE
-fail_without_close:
-  free (file);
+  while (!feof (fp))
+  {
+    if (allocated - len < BUFSIZ)
+    {
+      allocated *= 2;
+      /* Don't allocate and go more than ~536MB, our mmap reader still
+	 can cover files like that but lets limit our fallback reader */
+      if (unlikely (allocated > (2 << 28))) goto fread_fail;
+      char *new_data = (char *) realloc (data, allocated);
+      if (unlikely (new_data == nullptr)) goto fread_fail;
+      data = new_data;
+    }
+
+    unsigned long addition = fread (data + len, 1, allocated - len, fp);
+
+    int err = ferror (fp);
+#ifdef EINTR // armcc doesn't have it
+    if (unlikely (err == EINTR)) continue;
+#endif
+    if (unlikely (err)) goto fread_fail;
+
+    len += addition;
+  }
+
+  return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data,
+                         (hb_destroy_func_t) free);
+
+fread_fail:
+  fclose (fp);
+fread_fail_without_close:
+  free (data);
   return hb_blob_get_empty ();
 }
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -26,17 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_BUFFER_PRIVATE_HH
 #define HB_BUFFER_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
 
 #ifndef HB_BUFFER_MAX_LEN_FACTOR
 #define HB_BUFFER_MAX_LEN_FACTOR 32
 #endif
 #ifndef HB_BUFFER_MAX_LEN_MIN
 #define HB_BUFFER_MAX_LEN_MIN 8192
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -119,24 +119,24 @@ hb_buffer_t::enlarge (unsigned int size)
     return false;
   }
 
   unsigned int new_allocated = allocated;
   hb_glyph_position_t *new_pos = nullptr;
   hb_glyph_info_t *new_info = nullptr;
   bool separate_out = out_info != info;
 
-  if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
+  if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0]))))
     goto done;
 
   while (size >= new_allocated)
     new_allocated += (new_allocated >> 1) + 32;
 
   static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
-  if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
+  if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]))))
     goto done;
 
   new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
   new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
 
 done:
   if (unlikely (!new_pos || !new_info))
     successful = false;
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -23,18 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-mutex-private.hh"
-#include "hb-object-private.hh"
 
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
 #include <xlocale.h>
 #endif
 
 
 /* hb_options_t */
@@ -540,16 +538,17 @@ hb_script_get_horizontal_direction (hb_s
     case HB_SCRIPT_OLD_SOGDIAN:
     case HB_SCRIPT_SOGDIAN:
 
       return HB_DIRECTION_RTL;
 
 
     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
     case HB_SCRIPT_OLD_ITALIC:
+    case HB_SCRIPT_RUNIC:
 
       return HB_DIRECTION_INVALID;
   }
 
   return HB_DIRECTION_LTR;
 }
 
 
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -24,17 +24,16 @@
  *
  * Mozilla Author(s): Jonathan Kew
  * Google Author(s): Behdad Esfahbod
  */
 
 #define HB_SHAPER coretext
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-coretext.h"
 #include <math.h>
 
 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
 
--- a/gfx/harfbuzz/src/hb-debug.hh
+++ b/gfx/harfbuzz/src/hb-debug.hh
@@ -23,16 +23,17 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_DEBUG_HH
 #define HB_DEBUG_HH
 
 #include "hb-private.hh"
+#include "hb-dsalgs.hh"
 
 
 #ifndef HB_DEBUG
 #define HB_DEBUG 0
 #endif
 
 static inline bool
 _hb_debug (unsigned int level,
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -18,17 +18,16 @@
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #define HB_SHAPER directwrite
 #include "hb-shaper-impl-private.hh"
 
 #include <DWrite_1.h>
 
 #include "hb-directwrite.h"
 
 
--- a/gfx/harfbuzz/src/hb-dsalgs.hh
+++ b/gfx/harfbuzz/src/hb-dsalgs.hh
@@ -25,16 +25,253 @@
  */
 
 #ifndef HB_DSALGS_HH
 #define HB_DSALGS_HH
 
 #include "hb-private.hh"
 
 
+/* Void! For when we need a expression-type of void. */
+typedef const struct _hb_void_t *hb_void_t;
+#define HB_VOID ((const _hb_void_t *) nullptr)
+
+
+/*
+ * Bithacks.
+ */
+
+/* Return the number of 1 bits in v. */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_popcount (T v)
+{
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_popcount (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_popcountl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_popcountll (v);
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "HACKMEM 169" */
+    uint32_t y;
+    y = (v >> 1) &033333333333;
+    y = v - y - ((y >>1) & 033333333333);
+    return (((y + (y >> 3)) & 030707070707) % 077);
+  }
+
+  if (sizeof (T) == 8)
+  {
+    unsigned int shift = 32;
+    return hb_popcount<uint32_t> ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift));
+  }
+
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return hb_popcount<uint64_t> ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift));
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* Returns the number of bits needed to store number */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_bit_storage (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return sizeof (unsigned int) * 8 - __builtin_clz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanReverse (&where, v);
+    return 1 + where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanReverse64 (&where, v);
+    return 1 + where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
+    const unsigned int S[] = {1, 2, 4, 8, 16};
+    unsigned int r = 0;
+    for (int i = 4; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
+    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
+    unsigned int r = 0;
+    for (int i = 5; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (v >> shift) ? hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift :
+			  hb_bit_storage<uint64_t> ((uint64_t) v);
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* Returns the number of zero bits in the least significant side of v */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_ctz (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_ctz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_ctzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_ctzll (v);
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanForward (&where, v);
+    return where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanForward64 (&where, v);
+    return where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    unsigned int c = 32;
+    v &= - (int32_t) v;
+    if (v) c--;
+    if (v & 0x0000FFFF) c -= 16;
+    if (v & 0x00FF00FF) c -= 8;
+    if (v & 0x0F0F0F0F) c -= 4;
+    if (v & 0x33333333) c -= 2;
+    if (v & 0x55555555) c -= 1;
+    return c;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    unsigned int c = 64;
+    v &= - (int64_t) (v);
+    if (v) c--;
+    if (v & 0x00000000FFFFFFFFULL) c -= 32;
+    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
+    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
+    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
+    if (v & 0x3333333333333333ULL) c -= 2;
+    if (v & 0x5555555555555555ULL) c -= 1;
+    return c;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (uint64_t) v ? hb_bit_storage<uint64_t> ((uint64_t) v) :
+			  hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift;
+  }
+
+  assert (0);
+  return 0; /* Shut up stupid compiler. */
+}
+
+
+/*
+ * Tiny stuff.
+ */
+
+#undef MIN
+template <typename Type>
+static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
+
+#undef MAX
+template <typename Type>
+static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
+
+static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
+{ return (a + (b - 1)) / b; }
+
+
+#undef  ARRAY_LENGTH
+template <typename Type, unsigned int n>
+static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
+/* A const version, but does not detect erratically being called on pointers. */
+#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
+
+static inline bool
+hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
+{
+  return (size > 0) && (count >= ((unsigned int) -1) / size);
+}
+
+static inline unsigned int
+hb_ceil_to_4 (unsigned int v)
+{
+  return ((v - 1) | 3) + 1;
+}
+
+
+/*
+ * Sort and search.
+ */
+
 static inline void *
 hb_bsearch_r (const void *key, const void *base,
 	      size_t nmemb, size_t size,
 	      int (*compar)(const void *_key, const void *_item, void *_arg),
 	      void *arg)
 {
   int min = 0, max = (int) nmemb - 1;
   while (min <= max)
@@ -48,17 +285,16 @@ hb_bsearch_r (const void *key, const voi
       min = mid + 1;
     else
       return (void *) p;
   }
   return nullptr;
 }
 
 
-
 /* From https://github.com/noporpoise/sort_r */
 
 /* Isaac Turner 29 April 2014 Public Domain */
 
 /*
 
 hb_sort_r function to be exported.
 
@@ -153,9 +389,476 @@ static inline void sort_r_simple(void *b
 
 static inline void hb_sort_r(void *base, size_t nel, size_t width,
 			     int (*compar)(const void *_a, const void *_b, void *_arg),
 			     void *arg)
 {
     sort_r_simple(base, nel, width, compar, arg);
 }
 
+
+template <typename T, typename T2> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
+{
+  for (unsigned int i = 1; i < len; i++)
+  {
+    unsigned int j = i;
+    while (j && compar (&array[j - 1], &array[i]) > 0)
+      j--;
+    if (i == j)
+      continue;
+    /* Move item i to occupy place for item j, shift what's in between. */
+    {
+      T t = array[i];
+      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
+      array[j] = t;
+    }
+    if (array2)
+    {
+      T2 t = array2[i];
+      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2));
+      array2[j] = t;
+    }
+  }
+}
+
+template <typename T> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
+{
+  hb_stable_sort (array, len, compar, (int *) nullptr);
+}
+
+static inline hb_bool_t
+hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
+{
+  /* Pain because we don't know whether s is nul-terminated. */
+  char buf[64];
+  len = MIN (ARRAY_LENGTH (buf) - 1, len);
+  strncpy (buf, s, len);
+  buf[len] = '\0';
+
+  char *end;
+  errno = 0;
+  unsigned long v = strtoul (buf, &end, base);
+  if (errno) return false;
+  if (*end) return false;
+  *out = v;
+  return true;
+}
+
+
+#define HB_VECTOR_INIT {0, 0, false, nullptr}
+template <typename Type, unsigned int StaticSize=8>
+struct hb_vector_t
+{
+  unsigned int len;
+  unsigned int allocated;
+  bool successful;
+  Type *arrayZ;
+  Type static_array[StaticSize];
+
+  void init (void)
+  {
+    len = 0;
+    allocated = ARRAY_LENGTH (static_array);
+    successful = true;
+    arrayZ = static_array;
+  }
+
+  inline Type& operator [] (unsigned int i)
+  {
+    if (unlikely (i >= len))
+      return Crap (Type);
+    return arrayZ[i];
+  }
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len))
+      return Null(Type);
+    return arrayZ[i];
+  }
+
+  inline Type *push (void)
+  {
+    if (unlikely (!resize (len + 1)))
+      return &Crap(Type);
+    return &arrayZ[len - 1];
+  }
+  inline Type *push (const Type& v)
+  {
+    Type *p = push ();
+    *p = v;
+    return p;
+  }
+
+  /* Allocate for size but don't adjust len. */
+  inline bool alloc (unsigned int size)
+  {
+    if (unlikely (!successful))
+      return false;
+
+    if (likely (size <= allocated))
+      return true;
+
+    /* Reallocate */
+
+    unsigned int new_allocated = allocated;
+    while (size >= new_allocated)
+      new_allocated += (new_allocated >> 1) + 8;
+
+    Type *new_array = nullptr;
+
+    if (arrayZ == static_array)
+    {
+      new_array = (Type *) calloc (new_allocated, sizeof (Type));
+      if (new_array)
+        memcpy (new_array, arrayZ, len * sizeof (Type));
+    }
+    else
+    {
+      bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
+      if (likely (!overflows))
+        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
+    }
+
+    if (unlikely (!new_array))
+    {
+      successful = false;
+      return false;
+    }
+
+    arrayZ = new_array;
+    allocated = new_allocated;
+
+    return true;
+  }
+
+  inline bool resize (int size_)
+  {
+    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+    if (!alloc (size))
+      return false;
+
+    if (size > len)
+      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
+
+    len = size;
+    return true;
+  }
+
+  inline void pop (void)
+  {
+    if (!len) return;
+    len--;
+  }
+
+  inline void remove (unsigned int i)
+  {
+     if (unlikely (i >= len))
+       return;
+     memmove (static_cast<void *> (&arrayZ[i]),
+	      static_cast<void *> (&arrayZ[i + 1]),
+	      (len - i - 1) * sizeof (Type));
+     len--;
+  }
+
+  inline void shrink (int size_)
+  {
+    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+     if (size < len)
+       len = size;
+  }
+
+  template <typename T>
+  inline Type *find (T v) {
+    for (unsigned int i = 0; i < len; i++)
+      if (arrayZ[i] == v)
+	return &arrayZ[i];
+    return nullptr;
+  }
+  template <typename T>
+  inline const Type *find (T v) const {
+    for (unsigned int i = 0; i < len; i++)
+      if (arrayZ[i] == v)
+	return &arrayZ[i];
+    return nullptr;
+  }
+
+  inline void qsort (int (*cmp)(const void*, const void*))
+  {
+    ::qsort (arrayZ, len, sizeof (Type), cmp);
+  }
+
+  inline void qsort (void)
+  {
+    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
+  }
+
+  inline void qsort (unsigned int start, unsigned int end)
+  {
+    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
+  }
+
+  template <typename T>
+  inline Type *lsearch (const T &x)
+  {
+    for (unsigned int i = 0; i < len; i++)
+      if (0 == this->arrayZ[i].cmp (&x))
+	return &arrayZ[i];
+    return nullptr;
+  }
+
+  template <typename T>
+  inline Type *bsearch (const T &x)
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+  }
+  template <typename T>
+  inline const Type *bsearch (const T &x) const
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+  }
+  template <typename T>
+  inline bool bfind (const T &x, unsigned int *i) const
+  {
+    int min = 0, max = (int) this->len - 1;
+    while (min <= max)
+    {
+      int mid = (min + max) / 2;
+      int c = this->arrayZ[mid].cmp (&x);
+      if (c < 0)
+        max = mid - 1;
+      else if (c > 0)
+        min = mid + 1;
+      else
+      {
+        *i = mid;
+	return true;
+      }
+    }
+    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
+      max++;
+    *i = max;
+    return false;
+  }
+
+  inline void fini (void)
+  {
+    if (arrayZ != static_array)
+      free (arrayZ);
+    arrayZ = nullptr;
+    allocated = len = 0;
+  }
+};
+
+
+#define HB_LOCKABLE_SET_INIT {HB_VECTOR_INIT}
+template <typename item_t, typename lock_t>
+struct hb_lockable_set_t
+{
+  hb_vector_t <item_t, 1> items;
+
+  inline void init (void) { items.init (); }
+
+  template <typename T>
+  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      if (replace) {
+	item_t old = *item;
+	*item = v;
+	l.unlock ();
+	old.fini ();
+      }
+      else {
+        item = nullptr;
+	l.unlock ();
+      }
+    } else {
+      item = items.push (v);
+      l.unlock ();
+    }
+    return item;
+  }
+
+  template <typename T>
+  inline void remove (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      item_t old = *item;
+      *item = items[items.len - 1];
+      items.pop ();
+      l.unlock ();
+      old.fini ();
+    } else {
+      l.unlock ();
+    }
+  }
+
+  template <typename T>
+  inline bool find (T v, item_t *i, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item)
+      *i = *item;
+    l.unlock ();
+    return !!item;
+  }
+
+  template <typename T>
+  inline item_t *find_or_insert (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (!item) {
+      item = items.push (v);
+    }
+    l.unlock ();
+    return item;
+  }
+
+  inline void fini (lock_t &l)
+  {
+    if (!items.len) {
+      /* No need for locking. */
+      items.fini ();
+      return;
+    }
+    l.lock ();
+    while (items.len) {
+      item_t old = items[items.len - 1];
+	items.pop ();
+	l.unlock ();
+	old.fini ();
+	l.lock ();
+    }
+    items.fini ();
+    l.unlock ();
+  }
+
+};
+
+
+template <typename Type>
+struct hb_auto_t : Type
+{
+  hb_auto_t (void) { Type::init (); }
+  ~hb_auto_t (void) { Type::fini (); }
+  private: /* Hide */
+  void init (void) {}
+  void fini (void) {}
+};
+
+struct hb_bytes_t
+{
+  inline hb_bytes_t (void) : bytes (nullptr), len (0) {}
+  inline hb_bytes_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
+
+  inline int cmp (const hb_bytes_t &a) const
+  {
+    if (len != a.len)
+      return (int) a.len - (int) len;
+
+    return memcmp (a.bytes, bytes, len);
+  }
+  static inline int cmp (const void *pa, const void *pb)
+  {
+    hb_bytes_t *a = (hb_bytes_t *) pa;
+    hb_bytes_t *b = (hb_bytes_t *) pb;
+    return b->cmp (*a);
+  }
+
+  const char *bytes;
+  unsigned int len;
+};
+
+
+struct HbOpOr
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
+};
+struct HbOpAnd
+{
+  static const bool passthru_left = false;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
+};
+struct HbOpMinus
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
+};
+struct HbOpXor
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
+};
+
+
+/* Compiler-assisted vectorization. */
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
+ * using vectorized operations if HB_VECTOR_SIZE is set to **bit** numbers (eg 128).
+ * Define that to 0 to disable. */
+template <typename elt_t, unsigned int byte_size>
+struct hb_vector_size_t
+{
+  elt_t& operator [] (unsigned int i) { return u.v[i]; }
+  const elt_t& operator [] (unsigned int i) const { return u.v[i]; }
+
+  template <class Op>
+  inline hb_vector_size_t process (const hb_vector_size_t &o) const
+  {
+    hb_vector_size_t r;
+#if HB_VECTOR_SIZE
+    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
+	Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]);
+    else
+#endif
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
+	Op::process (r.u.v[i], u.v[i], o.u.v[i]);
+    return r;
+  }
+  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  { return process<HbOpOr> (o); }
+  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  { return process<HbOpAnd> (o); }
+  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  { return process<HbOpXor> (o); }
+  inline hb_vector_size_t operator ~ () const
+  {
+    hb_vector_size_t r;
+#if HB_VECTOR_SIZE && 0
+    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
+      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
+	r.u.vec[i] = ~u.vec[i];
+    else
+#endif
+    for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
+      r.u.v[i] = ~u.v[i];
+    return r;
+  }
+
+  private:
+  static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
+  union {
+    elt_t v[byte_size / sizeof (elt_t)];
+#if HB_VECTOR_SIZE
+    typedef unsigned long vec_t __attribute__((vector_size (HB_VECTOR_SIZE / 8)));
+    vec_t vec[byte_size / sizeof (vec_t)];
+#endif
+  } u;
+};
+
+
 #endif /* HB_DSALGS_HH */
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FACE_PRIVATE_HH
 #define HB_FACE_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 
 
 /*
  * hb_face_t
  */
 
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -26,17 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_FONT_PRIVATE_HH
 #define HB_FONT_PRIVATE_HH
 
 #include "hb-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-face-private.hh"
 #include "hb-shaper-private.hh"
 
 
 
 /*
  * hb_font_funcs_t
  */
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -23,17 +23,16 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 
 #include "hb-ft.h"
 
 #include "hb-font-private.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_TRUETYPE_TABLES_H
--- a/gfx/harfbuzz/src/hb-map-private.hh
+++ b/gfx/harfbuzz/src/hb-map-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_MAP_PRIVATE_HH
 #define HB_MAP_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 template <typename T>
 inline uint32_t Hash (const T &v)
 {
   /* Knuth's multiplicative method: */
   return (uint32_t) v * 2654435761u;
 }
@@ -84,17 +83,17 @@ struct hb_map_t
     hb_object_fini (this);
     fini_shallow ();
   }
 
   inline bool resize (void)
   {
     if (unlikely (!successful)) return false;
 
-    unsigned int power = _hb_bit_storage (population * 2 + 8);
+    unsigned int power = hb_bit_storage (population * 2 + 8);
     unsigned int new_size = 1u << power;
     item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t));
     if (unlikely (!new_items))
     {
       successful = false;
       return false;
     }
     memset (new_items, 0xFF, (size_t) new_size * sizeof (item_t));
--- a/gfx/harfbuzz/src/hb-object-private.hh
+++ b/gfx/harfbuzz/src/hb-object-private.hh
@@ -28,18 +28,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OBJECT_PRIVATE_HH
 #define HB_OBJECT_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
-
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
 
 /* reference_count */
 
 #define HB_REFERENCE_COUNT_INERT_VALUE 0
 #define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -281,57 +281,251 @@ struct TTCHeader
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion<>version;	/* Version of the TTC Header (1.0 or 2.0),
 				 * 0x00010000u or 0x00020000u */
   }			header;
   TTCHeaderVersion1	version1;
   } u;
 };
 
+/*
+ * Mac Resource Fork
+ */
+
+struct ResourceRefItem
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    // actual data sanitization is done on ResourceForkHeader sanitizer
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  HBINT16	id;		/* Resource ID, is really should be signed? */
+  HBINT16	nameOffset;	/* Offset from beginning of resource name list
+				 * to resource name, minus means there is no */
+  HBUINT8	attr;		/* Resource attributes */
+  HBUINT24	dataOffset;	/* Offset from beginning of resource data to
+				 * data for this resource */
+  HBUINT32	reserved;	/* Reserved for handle to resource */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct ResourceTypeItem
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    // RefList sanitization is done on ResourceMap sanitizer
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  inline unsigned int get_resource_count () const
+  {
+    return numRes + 1;
+  }
+
+  inline bool is_sfnt () const
+  {
+    return type == HB_TAG ('s','f','n','t');
+  }
+
+  inline const ResourceRefItem& get_ref_item (const void *base,
+					      unsigned int i) const
+  {
+    return (base+refList)[i];
+  }
+
+  protected:
+  Tag		type;		/* Resource type */
+  HBUINT16	numRes;		/* Number of resource this type in map minus 1 */
+  OffsetTo<UnsizedArrayOf<ResourceRefItem> >
+		refList;	/* Offset from beginning of resource type list
+				 * to reference list for this type */
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct ResourceMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    for (unsigned int i = 0; i < get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = get_type (i);
+      if (unlikely (!type.sanitize (c)))
+        return_trace (false);
+      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
+	if (unlikely (!get_ref_item (type, j).sanitize (c)))
+	  return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  inline const ResourceTypeItem& get_type (unsigned int i) const
+  {
+    // Why offset from the second byte of the object? I'm not sure
+    return ((&reserved[2])+typeList)[i];
+  }
+
+  inline unsigned int get_types_count () const
+  {
+    return nTypes + 1;
+  }
+
+  inline const ResourceRefItem &get_ref_item (const ResourceTypeItem &type,
+					      unsigned int i) const
+  {
+    return type.get_ref_item (&(this+typeList), i);
+  }
+
+  inline const PString& get_name (const ResourceRefItem &item,
+				  unsigned int i) const
+  {
+    if (item.nameOffset == -1)
+      return Null (PString);
+
+    return StructAtOffset<PString> (this, nameList + item.nameOffset);
+  }
+
+  protected:
+  HBUINT8	reserved[16];	/* Reserved for copy of resource header */
+  LOffsetTo<ResourceMap>
+		reserved1;	/* Reserved for handle to next resource map */
+  HBUINT16	reserved2;	/* Reserved for file reference number */
+  HBUINT16	attr;		/* Resource fork attribute */
+  OffsetTo<UnsizedArrayOf<ResourceTypeItem> >
+		typeList;	/* Offset from beginning of map to
+				 * resource type list */
+  HBUINT16	nameList;	/* Offset from beginning of map to
+				 * resource name list */
+  HBUINT16	nTypes;		/* Number of types in the map minus 1 */
+  public:
+  DEFINE_SIZE_STATIC (30);
+};
+
+struct ResourceForkHeader
+{
+  inline unsigned int get_face_count () const
+  {
+    const ResourceMap &resource_map = this+map;
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      if (type.is_sfnt ())
+	return type.get_resource_count ();
+    }
+    return 0;
+  }
+
+  inline const LArrayOf<HBUINT8>& get_data (const ResourceTypeItem& type,
+					    unsigned int idx) const
+  {
+    const ResourceMap &resource_map = this+map;
+    unsigned int offset = dataOffset;
+    offset += resource_map.get_ref_item (type, idx).dataOffset;
+    return StructAtOffset<LArrayOf<HBUINT8> > (this, offset);
+  }
+
+  inline const OpenTypeFontFace& get_face (unsigned int idx) const
+  {
+    const ResourceMap &resource_map = this+map;
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      if (type.is_sfnt () && idx < type.get_resource_count ())
+	return (OpenTypeFontFace&) get_data (type, idx).arrayZ;
+    }
+    return Null (OpenTypeFontFace);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+
+    const ResourceMap &resource_map = this+map;
+    if (unlikely (!resource_map.sanitize (c)))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    {
+      const ResourceTypeItem& type = resource_map.get_type (i);
+      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
+      {
+        const LArrayOf<HBUINT8>& data = get_data (type, j);
+	if (unlikely (!(data.sanitize (c) &&
+			((OpenTypeFontFace&) data.arrayZ).sanitize (c))))
+	  return_trace (false);
+      }
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	dataOffset;	/* Offset from beginning of resource fork
+				 * to resource data */
+  LOffsetTo<ResourceMap>
+		map;		/* Offset from beginning of resource fork
+				 * to resource map */
+  HBUINT32	dataLen;	/* Length of resource data */
+  HBUINT32	mapLen;		/* Length of resource map */
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
 
 /*
  * OpenType Font File
  */
 
 struct OpenTypeFontFile
 {
   static const hb_tag_t tableTag	= HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
 
   enum {
     CFFTag		= HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
     TrueTypeTag	= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
     TTCTag		= HB_TAG ('t','t','c','f'), /* TrueType Collection */
+    DFontTag		= HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
     TrueTag		= HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
     Typ1Tag		= HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
   };
 
   inline hb_tag_t get_tag (void) const { return u.tag; }
 
   inline unsigned int get_face_count (void) const
   {
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return 1;
     case TTCTag:	return u.ttcHeader.get_face_count ();
+//    case DFontTag:	return u.rfHeader.get_face_count ();
     default:		return 0;
     }
   }
   inline const OpenTypeFontFace& get_face (unsigned int i) const
   {
     switch (u.tag) {
     /* Note: for non-collection SFNT data we ignore index.  This is because
      * Apple dfont container is a container of SFNT's.  So each SFNT is a
      * non-TTC, but the index is more than zero. */
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return u.fontFace;
     case TTCTag:	return u.ttcHeader.get_face (i);
+//    case DFontTag:	return u.rfHeader.get_face (i);
     default:		return Null(OpenTypeFontFace);
     }
   }
 
   inline bool serialize_single (hb_serialize_context_t *c,
 				hb_tag_t sfnt_tag,
 			        Supplier<hb_tag_t> &tags,
 			        Supplier<hb_blob_t *> &blobs,
@@ -348,25 +542,27 @@ struct OpenTypeFontFile
     TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return_trace (u.fontFace.sanitize (c));
     case TTCTag:	return_trace (u.ttcHeader.sanitize (c));
+//    case DFontTag:	return_trace (u.rfHeader.sanitize (c));
     default:		return_trace (true);
     }
   }
 
   protected:
   union {
   Tag			tag;		/* 4-byte identifier. */
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
+  ResourceForkHeader	rfHeader;
   } u;
   public:
   DEFINE_SIZE_UNION (4, tag);
 };
 
 
 } /* namespace OT */
 
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-blob-private.hh"
 #include "hb-face-private.hh"
 
 
 namespace OT {
 
 
 
@@ -225,17 +224,17 @@ struct hb_sanitize_context_t :
        ok ? "OK" : "OUT-OF-RANGE");
 
     return likely (ok);
   }
 
   inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
   {
     const char *p = (const char *) base;
-    bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
+    bool overflows = hb_unsigned_mul_overflows (len, record_size);
     unsigned int array_size = record_size * len;
     bool ok = !overflows && this->check_range (base, array_size);
 
     DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
        "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
        p, p + (record_size * len), record_size, len, (unsigned int) array_size,
        this->start, this->end,
        overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
@@ -1028,16 +1027,17 @@ struct ArrayOf
 
   public:
   LenType len;
   Type arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
 template <typename Type> struct LArrayOf : ArrayOf<Type, HBUINT32> {};
+typedef ArrayOf<HBUINT8, HBUINT8> PString;
 
 /* Array of Offset's */
 template <typename Type, typename OffsetType=HBUINT16>
 struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
 
 /* Array of offsets relative to the beginning of the array itself. */
 template <typename Type>
 struct OffsetListOf : OffsetArrayOf<Type>
@@ -1172,17 +1172,17 @@ struct BinSearchHeader
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   inline void set (unsigned int v)
   {
     len.set (v);
     assert (len == v);
-    entrySelector.set (MAX (1u, _hb_bit_storage (v)) - 1);
+    entrySelector.set (MAX (1u, hb_bit_storage (v)) - 1);
     searchRange.set (16 * (1u << entrySelector));
     rangeShift.set (v * 16 > searchRange
 		    ? 16 * v - searchRange
 		    : 0);
   }
 
   protected:
   HBUINT16	len;
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -84,17 +84,17 @@ struct CmapSubtableFormat4
     TRACE_SERIALIZE (this);
 
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
     this->format.set (4);
     this->length.set (get_sub_table_size (segments));
 
     this->segCountX2.set (segments.len * 2);
-    this->entrySelector.set (MAX (1u, _hb_bit_storage (segments.len)) - 1);
+    this->entrySelector.set (MAX (1u, hb_bit_storage (segments.len)) - 1);
     this->searchRange.set (2 * (1u << this->entrySelector));
     this->rangeShift.set (segments.len * 2 > this->searchRange
                           ? 2 * segments.len - this->searchRange
                           : 0);
 
     HBUINT16 *end_count = c->allocate_size<HBUINT16> (HBUINT16::static_size * segments.len);
     c->allocate_size<HBUINT16> (HBUINT16::static_size); // 2 bytes of padding.
     HBUINT16 *start_count = c->allocate_size<HBUINT16> (HBUINT16::static_size * segments.len);
--- a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh
@@ -194,17 +194,17 @@ struct hdmx
 
     return result;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && version == 0 &&
-		  !_hb_unsigned_int_mul_overflows (num_records, size_device_record) &&
+		  !hb_unsigned_mul_overflows (num_records, size_device_record) &&
 		  size_device_record >= DeviceRecord::min_size &&
 		  c->check_range (this, get_size()));
   }
 
   protected:
   HBUINT16	version;		/* Table version number (0) */
   HBUINT16	num_records;		/* Number of device records. */
   HBUINT32	size_device_record;	/* Size of a device record, 32-bit aligned. */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-ot-layout-private.hh"
 #include "hb-open-type-private.hh"
 #include "hb-set-private.hh"
 
 
 #ifndef HB_MAX_NESTING_LEVEL
 #define HB_MAX_NESTING_LEVEL	6
 #endif
@@ -827,40 +826,53 @@ struct CoverageFormat2
   /* Older compilers need this to be public. */
   struct Iter
   {
     inline void init (const CoverageFormat2 &c_)
     {
       c = &c_;
       coverage = 0;
       i = 0;
-      j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
+      j = c->rangeRecord.len ? c->rangeRecord[0].start : 0;
+      if (unlikely (c->rangeRecord[0].start > c->rangeRecord[0].end))
+      {
+        /* Broken table. Skip. */
+        i = c->rangeRecord.len;
+      }
     }
     inline bool more (void) { return i < c->rangeRecord.len; }
     inline void next (void)
     {
       if (j >= c->rangeRecord[i].end)
       {
         i++;
 	if (more ())
 	{
+	  hb_codepoint_t old = j;
 	  j = c->rangeRecord[i].start;
+	  if (unlikely (j <= old))
+	  {
+	    /* Broken table. Skip. Important to avoid DoS. */
+	   i = c->rangeRecord.len;
+	   return;
+	  }
 	  coverage = c->rangeRecord[i].value;
 	}
 	return;
       }
       coverage++;
       j++;
     }
     inline hb_codepoint_t get_glyph (void) { return j; }
     inline unsigned int get_coverage (void) { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
-    unsigned int i, j, coverage;
+    unsigned int i, coverage;
+    hb_codepoint_t j;
   };
   private:
 
   protected:
   HBUINT16	coverageFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID. rangeCount entries
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -94,17 +94,17 @@ struct ValueFormat : HBUINT16
 					 * horizontal advance--measured from
 					 * beginning of PosTable (may be NULL) */
   Offset	yAdvDevice;		/* Offset to Device table for vertical
 					 * advance--measured from beginning of
 					 * PosTable (may be NULL) */
 #endif
 
   inline unsigned int get_len (void) const
-  { return _hb_popcount ((unsigned int) *this); }
+  { return hb_popcount ((unsigned int) *this); }
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
   void apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
@@ -369,17 +369,17 @@ struct AnchorMatrix
     *found = !matrixZ[row * cols + col].is_null ();
     return this+matrixZ[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
-    if (unlikely (_hb_unsigned_int_mul_overflows (rows, cols))) return_trace (false);
+    if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
     unsigned int count = rows * cols;
     if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (!matrixZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
   }
 
   HBUINT16	rows;			/* Number of rows */
@@ -1069,20 +1069,23 @@ struct MarkBasePosFormat1
     /* Now we search backwards for a non-mark glyph */
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return_trace (false);
       /* We only want to attach to the first of a MultipleSubst sequence.
        * https://github.com/harfbuzz/harfbuzz/issues/740
-       * Reject others. */
+       * Reject others...
+       * ...but stop if we find a mark in the MultipleSubst sequence:
+       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
       if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
 	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
 	  (skippy_iter.idx == 0 ||
+	   _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
 	   ))
 	break;
       skippy_iter.reject ();
     } while (1);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -506,17 +506,17 @@ struct AlternateSubstFormat1
     const AlternateSet &alt_set = this+alternateSet[index];
 
     if (unlikely (!alt_set.len)) return_trace (false);
 
     hb_mask_t glyph_mask = c->buffer->cur().mask;
     hb_mask_t lookup_mask = c->lookup_mask;
 
     /* Note: This breaks badly if two features enabled this lookup together. */
-    unsigned int shift = _hb_ctz (lookup_mask);
+    unsigned int shift = hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
 
     if (unlikely (alt_index > alt_set.len || alt_index == 0)) return_trace (false);
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -25,17 +25,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-buffer-private.hh"
 #include "hb-map-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-set-private.hh"
 
 
 namespace OT {
 
@@ -383,17 +382,17 @@ struct hb_ot_apply_context_t :
 	if (skip == matcher_t::SKIP_NO)
 	  return false;
       }
       return false;
     }
     inline bool prev (void)
     {
       assert (num_items > 0);
-      while (idx >= num_items)
+      while (idx > num_items - 1)
       {
 	idx--;
 	const hb_glyph_info_t &info = c->buffer->out_info[idx];
 
 	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
 	if (unlikely (skip == matcher_t::SKIP_YES))
 	  continue;
 
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -140,17 +140,17 @@ void hb_ot_map_builder_t::add_pause (uns
 
 void
 hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
   static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
   unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
-  unsigned int global_bit_shift = _hb_popcount (HB_GLYPH_FLAG_DEFINED);
+  unsigned int global_bit_shift = hb_popcount (HB_GLYPH_FLAG_DEFINED);
 
   m.global_mask = global_bit_mask;
 
   unsigned int required_feature_index[2];
   hb_tag_t required_feature_tag[2];
   /* We default to applying required feature in stage 0.  If the required
    * feature has a tag that is known to the shaper, we apply required feature
    * in the stage for that tag.
@@ -204,17 +204,17 @@ hb_ot_map_builder_t::compile (hb_ot_map_
 
     unsigned int bits_needed;
 
     if ((info->flags & F_GLOBAL) && info->max_value == 1)
       /* Uses the global bit */
       bits_needed = 0;
     else
       /* Limit to 8 bits per feature. */
-      bits_needed = MIN(8u, _hb_bit_storage (info->max_value));
+      bits_needed = MIN(8u, hb_bit_storage (info->max_value));
 
     if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
       continue; /* Feature disabled, or not enough bits. */
 
 
     hb_bool_t found = false;
     unsigned int feature_index[2];
     for (unsigned int table_index = 0; table_index < 2; table_index++)
--- a/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
+++ b/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Garret Rieger
  */
 
 #ifndef HB_OT_OS2_UNICODE_RANGES_HH
 #define HB_OT_OS2_UNICODE_RANGES_HH
 
 #include "hb-private.hh"
-#include "hb-dsalgs.hh"
 
 namespace OT {
 
 struct Range {
   hb_codepoint_t start;
   hb_codepoint_t end;
   unsigned int bit;
 };
--- a/gfx/harfbuzz/src/hb-ot-post-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-post-table.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_POST_TABLE_HH
 #define HB_OT_POST_TABLE_HH
 
 #include "hb-open-type-private.hh"
-#include "hb-dsalgs.hh"
 #include "hb-subset-plan.hh"
 
 #define HB_STRING_ARRAY_NAME format1_names
 #define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh"
 #include "hb-string-array.hh"
 #undef HB_STRING_ARRAY_LIST
 #undef HB_STRING_ARRAY_NAME
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-ot-shape-complex-arabic-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -663,31 +663,38 @@ initial_reordering_consonant_syllable (c
   if (has_reph)
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant.
    *
    * Reports suggest that in some scripts Uniscribe does this only if there
    * is *not* a Halant after last consonant already (eg. Kannada), while it
-   * does it unconditionally in other scripts (eg. Malayalam).  We don't
-   * currently know about other scripts, so we single out Malayalam for now.
+   * does it unconditionally in other scripts (eg. Malayalam, Bengali).  We
+   * don't currently know about other scripts, so we whitelist Malayalam and
+   * Bengali for now.
    *
    * Kannada test case:
    * U+0C9A,U+0CCD,U+0C9A,U+0CCD
    * With some versions of Lohit Kannada.
    * https://bugs.freedesktop.org/show_bug.cgi?id=59118
    *
    * Malayalam test case:
    * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
    * With lohit-ttf-20121122/Lohit-Malayalam.ttf
+   *
+   * Bengali test case
+   * U+0998,U+09CD,U+09AF,U+09CD
+   * With Windows XP vrinda.ttf
+   * https://github.com/harfbuzz/harfbuzz/issues/1073
    */
   if (indic_plan->is_old_spec)
   {
-    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM;
+    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM &&
+				   buffer->props.script != HB_SCRIPT_BENGALI;
     for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H)
       {
         unsigned int j;
         for (j = end - 1; j > i; j--)
 	  if (is_consonant (info[j]) ||
 	      (disallow_double_halants && info[j].indic_category() == OT_H))
 	    break;
@@ -1115,42 +1122,79 @@ final_reordering_syllable (const hb_ot_s
   /*   o Reorder matras:
    *
    *     If a pre-base matra character had been reordered before applying basic
    *     features, the glyph can be moved closer to the main consonant based on
    *     whether half-forms had been formed. Actual position for the matra is
    *     defined as “after last standalone halant glyph, after initial matra
    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
    *     halant, position is moved after it.
+   *
+   * IMPLEMENTATION NOTES:
+   *
+   * It looks like the last sentence is wrong.  Testing, with Windows 7 Uniscribe
+   * and Devanagari shows that the behavior is best described as:
+   *
+   * "If ZWJ follows this halant, matra is NOT repositioned after this halant.
+   *  If ZWNJ follows this halant, position is moved after it."
+   *
+   * Test case, with Adobe Devanagari or Nirmala UI:
+   *
+   *   U+091F,U+094D,U+200C,U+092F,U+093F
+   *   (Matra moves to the middle, after ZWNJ.)
+   *
+   *   U+091F,U+094D,U+200D,U+092F,U+093F
+   *   (Matra does NOT move, stays to the left.)
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1070
    */
 
   if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
   {
     /* If we lost track of base, alas, position before last thingy. */
     unsigned int new_pos = base == end ? base - 2 : base - 1;
 
     /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
      * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
      * We want to position matra after them.
      */
     if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
     {
+    search:
       while (new_pos > start &&
 	     !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H)))))
 	new_pos--;
 
       /* If we found no Halant we are done.
        * Otherwise only proceed if the Halant does
        * not belong to the Matra itself! */
       if (is_halant (info[new_pos]) &&
 	  info[new_pos].indic_position() != POS_PRE_M)
       {
+#if 0 // See comment above
 	/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
 	if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
 	  new_pos++;
+#endif
+	if (new_pos + 1 < end)
+	{
+	  /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */
+	  if (info[new_pos + 1].indic_category() == OT_ZWJ)
+	  {
+	    /* Keep searching. */
+	    if (new_pos > start)
+	    {
+	      new_pos--;
+	      goto search;
+	    }
+	  }
+	  /* -> If ZWNJ follows this halant, position is moved after it. */
+	  if (info[new_pos + 1].indic_category() == OT_ZWNJ)
+	    new_pos++;
+	}
       }
       else
         new_pos = start; /* No move. */
     }
 
     if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
     {
       /* Now go see if there's actually any matras... */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
@@ -367,32 +367,35 @@ initial_reordering_consonant_syllable (c
     base = end;
     for (unsigned int i = start; i < end; i++)
       if (info[i].khmer_position() == POS_BASE_C)
       {
 	base = i;
 	break;
       }
 
-    /* Note!  syllable() is a one-byte field. */
-    for (unsigned int i = base; i < end; i++)
-      if (info[i].syllable() != 255)
-      {
-	unsigned int max = i;
-	unsigned int j = start + info[i].syllable();
-	while (j != i)
+    if (unlikely (end - start >= 127))
+      buffer->merge_clusters (start, end);
+    else
+      /* Note!  syllable() is a one-byte field. */
+      for (unsigned int i = base; i < end; i++)
+	if (info[i].syllable() != 255)
 	{
-	  max = MAX (max, j);
-	  unsigned int next = start + info[j].syllable();
-	  info[j].syllable() = 255; /* So we don't process j later again. */
-	  j = next;
+	  unsigned int max = i;
+	  unsigned int j = start + info[i].syllable();
+	  while (j != i)
+	  {
+	    max = MAX (max, j);
+	    unsigned int next = start + info[j].syllable();
+	    info[j].syllable() = 255; /* So we don't process j later again. */
+	    j = next;
+	  }
+	  if (i != max)
+	    buffer->merge_clusters (i, max + 1);
 	}
-	if (i != max)
-	  buffer->merge_clusters (i, max + 1);
-      }
 
     /* Put syllable back in. */
     for (unsigned int i = start; i < end; i++)
       info[i].syllable() = syllable;
   }
 
   /* Setup masks now */
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -555,17 +555,17 @@ static const USE_TABLE_ELEMENT_TYPE use_
   /* 112B0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 112C0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 112D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,
   /* 112E0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, CMBlw,  VBlw,     O,     O,     O,     O,     O,
   /* 112F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
   /* Grantha */
 
-  /* 11300 */ VMAbv, VMAbv, VMPst, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,
+  /* 11300 */ VMAbv, VMAbv, VMAbv, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     B,
   /* 11310 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11320 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 11330 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O, CMBlw, CMBlw,     B,  VPst,  VPst,
   /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
   /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     B,     B,
   /* 11360 */     B,     B,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
   /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
 
@@ -668,17 +668,17 @@ static const USE_TABLE_ELEMENT_TYPE use_
 
 
   /* Bhaiksuki */
 
   /* 11C00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 11C10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,
   /* 11C30 */  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,     O,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMAbv, VMPst,     H,
-  /* 11C40 */     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11C40 */     B,     O,     O,     O,    GB,    GB,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11C50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,
 
   /* Marchen */
 
   /* 11C70 */     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11C90 */     O,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -824,22 +824,22 @@ hb_propagate_flags (hb_buffer_t *buffer)
 
 /* Pull it all together! */
 
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
   c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
-  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
+  if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
   {
     c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
 			      (unsigned) HB_BUFFER_MAX_LEN_MIN);
   }
-  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
+  if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
   {
     c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
 			      (unsigned) HB_BUFFER_MAX_OPS_MIN);
   }
 
   bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan);
   //c->fallback_substitute     = disable_otl || !hb_ot_layout_has_substitution (c->face);
   c->fallback_positioning    = disable_otl || !hb_ot_layout_has_positioning (c->face);
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -53,56 +53,84 @@
 
 #if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
 #include <intrin.h>
 #endif
 
 #define HB_PASTE1(a,b) a##b
 #define HB_PASTE(a,b) HB_PASTE1(a,b)
 
+
 /* Compile-time custom allocator support. */
 
 #if defined(hb_malloc_impl) \
  && defined(hb_calloc_impl) \
  && defined(hb_realloc_impl) \
  && defined(hb_free_impl)
 extern "C" void* hb_malloc_impl(size_t size);
 extern "C" void* hb_calloc_impl(size_t nmemb, size_t size);
 extern "C" void* hb_realloc_impl(void *ptr, size_t size);
 extern "C" void  hb_free_impl(void *ptr);
 #define malloc hb_malloc_impl
 #define calloc hb_calloc_impl
 #define realloc hb_realloc_impl
 #define free hb_free_impl
+
+#if defined(hb_memalign_impl)
+extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size);
+#define posix_memalign hb_memalign_impl
+#else
+#undef HAVE_POSIX_MEMALIGN
+#endif
+
 #endif
 
 
 /* Compiler attributes */
 
 
+template <typename T>
+struct _hb_alignof
+{
+  struct s
+  {
+    char c;
+    T t;
+  };
+  static constexpr unsigned int value = offsetof (s, t);
+};
+
 #if __cplusplus < 201103L
 
 #ifndef nullptr
 #define nullptr NULL
 #endif
 
+#ifndef constexpr
+#define constexpr const
+#endif
+
 // Static assertions
 #ifndef static_assert
 #define static_assert(e, msg) \
 	HB_UNUSED typedef int HB_PASTE(static_assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1]
 #endif // static_assert
 
 #ifdef __GNUC__
 #if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
 #define thread_local __thread
 #endif
 #else
 #define thread_local
 #endif
 
+#ifndef alignof
+#define alignof(x) (_hb_alignof<x>::value)
+#endif // alignof
+
 #endif // __cplusplus < 201103L
 
 #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
 #define likely(expr) (__builtin_expect (!!(expr), 1))
 #define unlikely(expr) (__builtin_expect (!!(expr), 0))
 #else
 #define likely(expr) (expr)
 #define unlikely(expr) (expr)
@@ -246,36 +274,16 @@ static int errno = 0; /* Use something b
  */
 #    define HB_USE_ATEXIT 1
 #  endif
 #endif
 #ifdef HB_NO_ATEXIT
 #  undef HB_USE_ATEXIT
 #endif
 
-/* Basics */
-
-#undef MIN
-template <typename Type>
-static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
-
-#undef MAX
-template <typename Type>
-static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
-
-static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
-{ return (a + (b - 1)) / b; }
-
-
-#undef  ARRAY_LENGTH
-template <typename Type, unsigned int n>
-static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
-/* A const version, but does not detect erratically being called on pointers. */
-#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
-
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
 template <unsigned int cond> class hb_assert_constant_t;
 template <> class hb_assert_constant_t<1> {};
 
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * (unsigned int) sizeof (hb_assert_constant_t<_cond>))
 
@@ -317,233 +325,16 @@ static_assert ((sizeof (hb_var_int_t) ==
 /* Check _assertion in a method environment */
 #define _ASSERT_POD1(_line) \
 	HB_UNUSED inline void _static_assertion_on_line_##_line (void) const \
 	{ _ASSERT_INSTANCE_POD1 (_line, *this); /* Make sure it's POD. */ }
 # define _ASSERT_POD0(_line)	_ASSERT_POD1 (_line)
 # define ASSERT_POD()		_ASSERT_POD0 (__LINE__)
 
 
-
-/* Tiny functions */
-
-/*
- * Void!
- */
-typedef const struct _hb_void_t *hb_void_t;
-#define HB_VOID ((const _hb_void_t *) nullptr)
-
-/* Return the number of 1 bits in v. */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_popcount (T v)
-{
-#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return __builtin_popcount (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return __builtin_popcountl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return __builtin_popcountll (v);
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "HACKMEM 169" */
-    uint32_t y;
-    y = (v >> 1) &033333333333;
-    y = v - y - ((y >>1) & 033333333333);
-    return (((y + (y >> 3)) & 030707070707) % 077);
-  }
-
-  if (sizeof (T) == 8)
-  {
-    unsigned int shift = 32;
-    return _hb_popcount<uint32_t> ((uint32_t) v) + _hb_popcount ((uint32_t) (v >> shift));
-  }
-
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return _hb_popcount<uint64_t> ((uint64_t) v) + _hb_popcount ((uint64_t) (v >> shift));
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-/* Returns the number of bits needed to store number */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_bit_storage (T v)
-{
-  if (unlikely (!v)) return 0;
-
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return sizeof (unsigned int) * 8 - __builtin_clz (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
-#endif
-
-#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
-  if (sizeof (T) <= sizeof (unsigned int))
-  {
-    unsigned long where;
-    _BitScanReverse (&where, v);
-    return 1 + where;
-  }
-# if _WIN64
-  if (sizeof (T) <= 8)
-  {
-    unsigned long where;
-    _BitScanReverse64 (&where, v);
-    return 1 + where;
-  }
-# endif
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "bithacks" */
-    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
-    const unsigned int S[] = {1, 2, 4, 8, 16};
-    unsigned int r = 0;
-    for (int i = 4; i >= 0; i--)
-      if (v & b[i])
-      {
-	v >>= S[i];
-	r |= S[i];
-      }
-    return r + 1;
-  }
-  if (sizeof (T) <= 8)
-  {
-    /* "bithacks" */
-    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
-    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
-    unsigned int r = 0;
-    for (int i = 5; i >= 0; i--)
-      if (v & b[i])
-      {
-	v >>= S[i];
-	r |= S[i];
-      }
-    return r + 1;
-  }
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return (v >> shift) ? _hb_bit_storage<uint64_t> ((uint64_t) (v >> shift)) + shift :
-			  _hb_bit_storage<uint64_t> ((uint64_t) v);
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-/* Returns the number of zero bits in the least significant side of v */
-template <typename T>
-static inline HB_CONST_FUNC unsigned int
-_hb_ctz (T v)
-{
-  if (unlikely (!v)) return 0;
-
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  if (sizeof (T) <= sizeof (unsigned int))
-    return __builtin_ctz (v);
-
-  if (sizeof (T) <= sizeof (unsigned long))
-    return __builtin_ctzl (v);
-
-  if (sizeof (T) <= sizeof (unsigned long long))
-    return __builtin_ctzll (v);
-#endif
-
-#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__)
-  if (sizeof (T) <= sizeof (unsigned int))
-  {
-    unsigned long where;
-    _BitScanForward (&where, v);
-    return where;
-  }
-# if _WIN64
-  if (sizeof (T) <= 8)
-  {
-    unsigned long where;
-    _BitScanForward64 (&where, v);
-    return where;
-  }
-# endif
-#endif
-
-  if (sizeof (T) <= 4)
-  {
-    /* "bithacks" */
-    unsigned int c = 32;
-    v &= - (int32_t) v;
-    if (v) c--;
-    if (v & 0x0000FFFF) c -= 16;
-    if (v & 0x00FF00FF) c -= 8;
-    if (v & 0x0F0F0F0F) c -= 4;
-    if (v & 0x33333333) c -= 2;
-    if (v & 0x55555555) c -= 1;
-    return c;
-  }
-  if (sizeof (T) <= 8)
-  {
-    /* "bithacks" */
-    unsigned int c = 64;
-    v &= - (int64_t) (v);
-    if (v) c--;
-    if (v & 0x00000000FFFFFFFFULL) c -= 32;
-    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
-    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
-    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
-    if (v & 0x3333333333333333ULL) c -= 2;
-    if (v & 0x5555555555555555ULL) c -= 1;
-    return c;
-  }
-  if (sizeof (T) == 16)
-  {
-    unsigned int shift = 64;
-    return (uint64_t) v ? _hb_bit_storage<uint64_t> ((uint64_t) v) :
-			  _hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift;
-  }
-
-  assert (0);
-  return 0; /* Shut up stupid compiler. */
-}
-
-static inline bool
-_hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size)
-{
-  return (size > 0) && (count >= ((unsigned int) -1) / size);
-}
-
-static inline unsigned int
-_hb_ceil_to_4 (unsigned int v)
-{
-  return ((v - 1) | 3) + 1;
-}
-
-
-
-/*
- *
- * Utility types
- *
- */
-
 #define HB_DISALLOW_COPY_AND_ASSIGN(TypeName) \
   TypeName(const TypeName&); \
   void operator=(const TypeName&)
 
 /*
  * Static pools
  */
 
@@ -615,331 +406,16 @@ struct CrapOrNull {
 };
 template <typename Type>
 struct CrapOrNull<const Type> {
   static inline Type const & get (void) { return Null(Type); }
 };
 #define CrapOrNull(Type) CrapOrNull<Type>::get ()
 
 
-
-/* arrays and maps */
-
-
-#define HB_PREALLOCED_ARRAY_INIT {0, 0, nullptr}
-template <typename Type, unsigned int StaticSize=8>
-struct hb_vector_t
-{
-  unsigned int len;
-  unsigned int allocated;
-  bool successful;
-  Type *arrayZ;
-  Type static_array[StaticSize];
-
-  void init (void)
-  {
-    len = 0;
-    allocated = ARRAY_LENGTH (static_array);
-    successful = true;
-    arrayZ = static_array;
-  }
-
-  inline Type& operator [] (unsigned int i)
-  {
-    if (unlikely (i >= len))
-      return Crap (Type);
-    return arrayZ[i];
-  }
-  inline const Type& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len))
-      return Null(Type);
-    return arrayZ[i];
-  }
-
-  inline Type *push (void)
-  {
-    if (unlikely (!resize (len + 1)))
-      return &Crap(Type);
-    return &arrayZ[len - 1];
-  }
-  inline Type *push (const Type& v)
-  {
-    Type *p = push ();
-    *p = v;
-    return p;
-  }
-
-  /* Allocate for size but don't adjust len. */
-  inline bool alloc (unsigned int size)
-  {
-    if (unlikely (!successful))
-      return false;
-
-    if (likely (size <= allocated))
-      return true;
-
-    /* Reallocate */
-
-    unsigned int new_allocated = allocated;
-    while (size >= new_allocated)
-      new_allocated += (new_allocated >> 1) + 8;
-
-    Type *new_array = nullptr;
-
-    if (arrayZ == static_array)
-    {
-      new_array = (Type *) calloc (new_allocated, sizeof (Type));
-      if (new_array)
-        memcpy (new_array, arrayZ, len * sizeof (Type));
-    }
-    else
-    {
-      bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type));
-      if (likely (!overflows))
-        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
-    }
-
-    if (unlikely (!new_array))
-    {
-      successful = false;
-      return false;
-    }
-
-    arrayZ = new_array;
-    allocated = new_allocated;
-
-    return true;
-  }
-
-  inline bool resize (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-    if (!alloc (size))
-      return false;
-
-    if (size > len)
-      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
-
-    len = size;
-    return true;
-  }
-
-  inline void pop (void)
-  {
-    if (!len) return;
-    len--;
-  }
-
-  inline void remove (unsigned int i)
-  {
-     if (unlikely (i >= len))
-       return;
-     memmove (static_cast<void *> (&arrayZ[i]),
-	      static_cast<void *> (&arrayZ[i + 1]),
-	      (len - i - 1) * sizeof (Type));
-     len--;
-  }
-
-  inline void shrink (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-     if (size < len)
-       len = size;
-  }
-
-  template <typename T>
-  inline Type *find (T v) {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-  template <typename T>
-  inline const Type *find (T v) const {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  inline void qsort (int (*cmp)(const void*, const void*))
-  {
-    ::qsort (arrayZ, len, sizeof (Type), cmp);
-  }
-
-  inline void qsort (void)
-  {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
-  }
-
-  inline void qsort (unsigned int start, unsigned int end)
-  {
-    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
-  }
-
-  template <typename T>
-  inline Type *lsearch (const T &x)
-  {
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == this->arrayZ[i].cmp (&x))
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  template <typename T>
-  inline Type *bsearch (const T &x)
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline const Type *bsearch (const T &x) const
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline bool bfind (const T &x, unsigned int *i) const
-  {
-    int min = 0, max = (int) this->len - 1;
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = this->arrayZ[mid].cmp (&x);
-      if (c < 0)
-        max = mid - 1;
-      else if (c > 0)
-        min = mid + 1;
-      else
-      {
-        *i = mid;
-	return true;
-      }
-    }
-    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
-      max++;
-    *i = max;
-    return false;
-  }
-
-  inline void fini (void)
-  {
-    if (arrayZ != static_array)
-      free (arrayZ);
-    arrayZ = nullptr;
-    allocated = len = 0;
-  }
-};
-
-template <typename Type>
-struct hb_auto_t : Type
-{
-  hb_auto_t (void) { Type::init (); }
-  ~hb_auto_t (void) { Type::fini (); }
-  private: /* Hide */
-  void init (void) {}
-  void fini (void) {}
-};
-template <typename Type>
-struct hb_auto_array_t : hb_auto_t <hb_vector_t <Type> > {};
-
-
-#define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT}
-template <typename item_t, typename lock_t>
-struct hb_lockable_set_t
-{
-  hb_vector_t <item_t, 1> items;
-
-  inline void init (void) { items.init (); }
-
-  template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      if (replace) {
-	item_t old = *item;
-	*item = v;
-	l.unlock ();
-	old.fini ();
-      }
-      else {
-        item = nullptr;
-	l.unlock ();
-      }
-    } else {
-      item = items.push (v);
-      l.unlock ();
-    }
-    return item;
-  }
-
-  template <typename T>
-  inline void remove (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      item_t old = *item;
-      *item = items[items.len - 1];
-      items.pop ();
-      l.unlock ();
-      old.fini ();
-    } else {
-      l.unlock ();
-    }
-  }
-
-  template <typename T>
-  inline bool find (T v, item_t *i, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item)
-      *i = *item;
-    l.unlock ();
-    return !!item;
-  }
-
-  template <typename T>
-  inline item_t *find_or_insert (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (!item) {
-      item = items.push (v);
-    }
-    l.unlock ();
-    return item;
-  }
-
-  inline void fini (lock_t &l)
-  {
-    if (!items.len) {
-      /* No need for locking. */
-      items.fini ();
-      return;
-    }
-    l.lock ();
-    while (items.len) {
-      item_t old = items[items.len - 1];
-	items.pop ();
-	l.unlock ();
-	old.fini ();
-	l.lock ();
-    }
-    items.fini ();
-    l.unlock ();
-  }
-
-};
-
-
 /* ASCII tag/character handling */
 
 static inline bool ISALPHA (unsigned char c)
 { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
 static inline bool ISALNUM (unsigned char c)
 { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
 static inline bool ISSPACE (unsigned char c)
 { return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
@@ -1022,154 +498,38 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, 
  * For example, for testing "x ∈ {x1, x2, x3}" use:
  * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
 #define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned int)(x) < 32) + (1U << (unsigned int)(x)))
 #define FLAG_UNSAFE(x) ((unsigned int)(x) < 32 ? (1U << (unsigned int)(x)) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
-template <typename T, typename T2> static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
-{
-  for (unsigned int i = 1; i < len; i++)
-  {
-    unsigned int j = i;
-    while (j && compar (&array[j - 1], &array[i]) > 0)
-      j--;
-    if (i == j)
-      continue;
-    /* Move item i to occupy place for item j, shift what's in between. */
-    {
-      T t = array[i];
-      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
-      array[j] = t;
-    }
-    if (array2)
-    {
-      T2 t = array2[i];
-      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2));
-      array2[j] = t;
-    }
-  }
-}
-
-template <typename T> static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
-{
-  hb_stable_sort (array, len, compar, (int *) nullptr);
-}
-
-static inline hb_bool_t
-hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
-{
-  /* Pain because we don't know whether s is nul-terminated. */
-  char buf[64];
-  len = MIN (ARRAY_LENGTH (buf) - 1, len);
-  strncpy (buf, s, len);
-  buf[len] = '\0';
-
-  char *end;
-  errno = 0;
-  unsigned long v = strtoul (buf, &end, base);
-  if (errno) return false;
-  if (*end) return false;
-  *out = v;
-  return true;
-}
-
-
-/* Vectorization */
-
-struct HbOpOr
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
-};
-struct HbOpAnd
-{
-  static const bool passthru_left = false;
-  static const bool passthru_right = false;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
-};
-struct HbOpMinus
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = false;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
-};
-struct HbOpXor
-{
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
-  template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
-};
-
 
 /* Compiler-assisted vectorization. */
 
-/* The `vector_size' attribute was introduced in gcc 3.1. */
-#if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
-#define HB_VECTOR_SIZE 128
-#elif !defined(HB_VECTOR_SIZE)
-#define HB_VECTOR_SIZE 0
+/*
+ * Disable vectorization for now.  To correctly use them, we should
+ * use posix_memalign() to allocate them.  Otherwise, can cause
+ * misaligned access.
+ *
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=860184
+ */
+#if !defined(HB_VECTOR_SIZE)
+#  define HB_VECTOR_SIZE 0
 #endif
 
-/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))). */
-template <typename elt_t, unsigned int byte_size>
-struct hb_vector_size_t
-{
-  elt_t& operator [] (unsigned int i) { return u.v[i]; }
-  const elt_t& operator [] (unsigned int i) const { return u.v[i]; }
-
-  template <class Op>
-  inline hb_vector_size_t process (const hb_vector_size_t &o) const
-  {
-    hb_vector_size_t r;
-#if HB_VECTOR_SIZE
-    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
-	Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]);
-    else
+/* The `vector_size' attribute was introduced in gcc 3.1. */
+#if !defined(HB_VECTOR_SIZE)
+#  if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
+#    define HB_VECTOR_SIZE 128
+#  else
+#    define HB_VECTOR_SIZE 0
+#  endif
 #endif
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
-	Op::process (r.u.v[i], u.v[i], o.u.v[i]);
-    return r;
-  }
-  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
-  { return process<HbOpOr> (o); }
-  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
-  { return process<HbOpAnd> (o); }
-  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
-  { return process<HbOpXor> (o); }
-  inline hb_vector_size_t operator ~ () const
-  {
-    hb_vector_size_t r;
-#if HB_VECTOR_SIZE && 0
-    if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE)
-      for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++)
-	r.u.vec[i] = ~u.vec[i];
-    else
-#endif
-    for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++)
-      r.u.v[i] = ~u.v[i];
-    return r;
-  }
-
-  private:
-  static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
-  union {
-    elt_t v[byte_size / sizeof (elt_t)];
-#if HB_VECTOR_SIZE
-    typedef unsigned long vec_t __attribute__((vector_size (HB_VECTOR_SIZE / 8)));
-    vec_t vec[byte_size / sizeof (vec_t)];
-#endif
-  } u;
-};
 
 
 /* Global runtime options. */
 
 struct hb_options_t
 {
   unsigned int initialized : 1;
   unsigned int uniscribe_bug_compatible : 1;
@@ -1194,48 +554,57 @@ hb_options (void)
 
   return _hb_options.opts;
 }
 
 /* Size signifying variable-sized array */
 #define VAR 1
 
 
-/* String type. */
-
-struct hb_bytes_t
-{
-  inline hb_bytes_t (void) : bytes (nullptr), len (0) {}
-  inline hb_bytes_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
-
-  inline int cmp (const hb_bytes_t &a) const
-  {
-    if (len != a.len)
-      return (int) a.len - (int) len;
-
-    return memcmp (a.bytes, bytes, len);
-  }
-  static inline int cmp (const void *pa, const void *pb)
-  {
-    hb_bytes_t *a = (hb_bytes_t *) pa;
-    hb_bytes_t *b = (hb_bytes_t *) pb;
-    return b->cmp (*a);
-  }
-
-  const char *bytes;
-  unsigned int len;
-};
-
-
 /* fallback for round() */
-#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND)
 static inline double
-round (double x)
+_hb_round (double x)
 {
   if (x >= 0)
     return floor (x + 0.5);
   else
     return ceil (x - 0.5);
 }
+#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND)
+#define round(x) _hb_round(x)
 #endif
 
 
+/* fallback for posix_memalign() */
+static inline int
+_hb_memalign(void **memptr, size_t alignment, size_t size)
+{
+  if (unlikely (0 != (alignment & (alignment - 1)) ||
+		!alignment ||
+		0 != (alignment & (sizeof (void *) - 1))))
+    return EINVAL;
+
+  char *p = (char *) malloc (size + alignment - 1);
+  if (unlikely (!p))
+    return ENOMEM;
+
+  size_t off = (size_t) p & (alignment - 1);
+  if (off)
+    p += alignment - off;
+
+  *memptr = (void *) p;
+
+  return 0;
+}
+#if !defined(posix_memalign) && !defined(HAVE_POSIX_MEMALIGN)
+#define posix_memalign _hb_memalign
+#endif
+
+
+/* Headers we include for everyone.  Keep sorted.  They express dependency amongst
+ * themselves, but no other file should include them.*/
+#include "hb-atomic-private.hh"
+#include "hb-debug.hh"
+#include "hb-dsalgs.hh"
+#include "hb-mutex-private.hh"
+#include "hb-object-private.hh"
+
 #endif /* HB_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-set-private.hh
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SET_PRIVATE_HH
 #define HB_SET_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 /*
  * hb_set_t
  */
 
 /* TODO Keep a free-list so we can free pages that are completely zeroed.  At that
  * point maybe also use a sentinel value for "all-1" pages? */
@@ -89,17 +88,17 @@ struct hb_set_t
     {
       return 0 == memcmp (&v, &other->v, sizeof (v));
     }
 
     inline unsigned int get_population (void) const
     {
       unsigned int pop = 0;
       for (unsigned int i = 0; i < len (); i++)
-        pop += _hb_popcount (v[i]);
+        pop += hb_popcount (v[i]);
       return pop;
     }
 
     inline bool next (hb_codepoint_t *codepoint) const
     {
       unsigned int m = (*codepoint + 1) & MASK;
       if (!m)
       {
@@ -156,18 +155,18 @@ struct hb_set_t
 	  return i * ELT_BITS + elt_get_max (v[i]);
       return 0;
     }
 
     typedef unsigned long long elt_t;
     static const unsigned int PAGE_BITS = 512;
     static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
 
-    static inline unsigned int elt_get_min (const elt_t &elt) { return _hb_ctz (elt); }
-    static inline unsigned int elt_get_max (const elt_t &elt) { return _hb_bit_storage (elt) - 1; }
+    static inline unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
+    static inline unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; }
 
     typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
 
     static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
     static const unsigned int ELT_MASK = ELT_BITS - 1;
     static const unsigned int BITS = sizeof (vector_t) * 8;
     static const unsigned int MASK = BITS - 1;
     static_assert (PAGE_BITS == BITS, "");
@@ -400,16 +399,17 @@ struct hb_set_t
     return true;
   }
 
   inline bool is_subset (const hb_set_t *larger_set) const
   {
     if (get_population () > larger_set->get_population ())
       return false;
 
+    /* TODO Optimize to use pages. */
     hb_codepoint_t c = INVALID;
     while (next (&c))
       if (!larger_set->has (c))
         return false;
 
     return true;
   }
 
--- a/gfx/harfbuzz/src/hb-shape-plan-private.hh
+++ b/gfx/harfbuzz/src/hb-shape-plan-private.hh
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_SHAPE_PLAN_PRIVATE_HH
 #define HB_SHAPE_PLAN_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 
 
 struct hb_shape_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 
 
 static void
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
--- a/gfx/harfbuzz/src/hb-subset-input.cc
+++ b/gfx/harfbuzz/src/hb-subset-input.cc
@@ -19,17 +19,16 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
-#include "hb-object-private.hh"
 #include "hb-subset-private.hh"
 #include "hb-set-private.hh"
 
 /**
  * hb_subset_input_create_or_fail:
  *
  * Return value: New subset input.
  *
--- a/gfx/harfbuzz/src/hb-subset-plan.hh
+++ b/gfx/harfbuzz/src/hb-subset-plan.hh
@@ -27,17 +27,16 @@
 #ifndef HB_SUBSET_PLAN_HH
 #define HB_SUBSET_PLAN_HH
 
 #include "hb-private.hh"
 
 #include "hb-subset.h"
 #include "hb-subset-private.hh"
 
-#include "hb-object-private.hh"
 #include "hb-map-private.hh"
 
 struct hb_subset_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t drop_hints;
--- a/gfx/harfbuzz/src/hb-subset.cc
+++ b/gfx/harfbuzz/src/hb-subset.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 #include "hb-open-type-private.hh"
 
 #include "hb-subset-glyf.hh"
 #include "hb-subset-private.hh"
 #include "hb-subset-plan.hh"
 
 #include "hb-open-file-private.hh"
 #include "hb-ot-cmap-table.hh"
@@ -149,17 +148,17 @@ static void
 static hb_blob_t *
 _hb_subset_face_data_reference_blob (hb_subset_face_data_t *data)
 {
 
   unsigned int table_count = data->tables.len;
   unsigned int face_length = table_count * 16 + 12;
 
   for (unsigned int i = 0; i < table_count; i++)
-    face_length += _hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
+    face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
 
   char *buf = (char *) malloc (face_length);
   if (unlikely (!buf))
     return nullptr;
 
   OT::hb_serialize_context_t c (buf, face_length);
   OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
 
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -27,17 +27,16 @@
  * Codethink Author(s): Ryan Lortie
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_UNICODE_PRIVATE_HH
 #define HB_UNICODE_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-object-private.hh"
 
 
 extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256];
 
 /*
  * hb_unicode_funcs_t
  */
 
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -20,17 +20,16 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
-#include "hb-debug.hh"
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
 
 #include <windows.h>
 #include <usp10.h>
 #include <rpc.h>
 
 #include "hb-uniscribe.h"
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 8
-#define HB_VERSION_MICRO 1
+#define HB_VERSION_MICRO 3
 
-#define HB_VERSION_STRING "1.8.1"
+#define HB_VERSION_STRING "1.8.3"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,
--- a/gfx/harfbuzz/src/hb-warning.cc
+++ b/gfx/harfbuzz/src/hb-warning.cc
@@ -19,19 +19,17 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-atomic-private.hh"
-#include "hb-mutex-private.hh"
-
+#include "hb-private.hh"
 
 #if defined(HB_ATOMIC_INT_NIL)
 #error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe"
 #error "Check hb-atomic-private.hh for possible resolutions."
 #endif
 
 #if defined(HB_MUTEX_IMPL_NIL)
 #error "Could not find any system to define mutex macros, library WILL NOT be thread-safe"
--- a/gfx/harfbuzz/src/main.cc
+++ b/gfx/harfbuzz/src/main.cc
@@ -73,16 +73,19 @@ main (int argc, char **argv)
     printf ("TrueType Collection of OpenType fonts\n");
     break;
   case OpenTypeFontFile::TrueTag:
     printf ("Obsolete Apple TrueType font\n");
     break;
   case OpenTypeFontFile::Typ1Tag:
     printf ("Obsolete Apple Type1 font in SFNT container\n");
     break;
+  case OpenTypeFontFile::DFontTag:
+    printf ("DFont Mac Resource Fork\n");
+    break;
   default:
     printf ("Unknown font format\n");
     break;
   }
 
   int num_fonts = ot.get_face_count ();
   printf ("%d font(s) found in file\n", num_fonts);
   for (int n_font = 0; n_font < num_fonts; n_font++) {
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/update.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Script to update the mozilla in-tree copy of the HarfBuzz library.
+# Run this within the /gfx/harfbuzz directory of the source tree.
+
+MY_TEMP_DIR=`mktemp -d -t harfbuzz_update.XXXXXX` || exit 1
+
+VERSION=1.8.3
+
+git clone https://github.com/harfbuzz/harfbuzz ${MY_TEMP_DIR}/harfbuzz
+git -C ${MY_TEMP_DIR}/harfbuzz checkout ${VERSION}
+
+COMMIT=$(git -C ${MY_TEMP_DIR}/harfbuzz rev-parse HEAD)
+perl -p -i -e "s/(\d+\.)(\d+\.)(\d+)/${VERSION}/" README-mozilla;
+perl -p -i -e "s/\[commit [0-9a-f]{40}\]/[commit ${COMMIT}]/" README-mozilla;
+
+FILES="AUTHORS autogen.sh configure.ac COPYING git.mk harfbuzz.doap Makefile.am NEWS README src THANKS TODO"
+
+for f in $FILES; do
+	rm -rf $f
+	mv ${MY_TEMP_DIR}/harfbuzz/$f $f
+done
+rm -rf src/hb-ucdn
+rm -rf ${MY_TEMP_DIR}
+
+hg revert -r . src/moz.build
+hg addremove
+
+echo "###"
+echo "### Updated HarfBuzz to $COMMIT."
+echo "### Remember to verify and commit the changes to source control!"
+echo "###"
--- a/gfx/layers/apz/public/IAPZCTreeManager.h
+++ b/gfx/layers/apz/public/IAPZCTreeManager.h
@@ -131,18 +131,16 @@ public:
    * Returns an APZInputBridge interface that can be used to send input
    * events to APZ in a synchronous manner. This will always be non-null, and
    * the returned object's lifetime will match the lifetime of this
    * IAPZCTreeManager implementation.
    * It is only valid to call this function in the UI process.
    */
   virtual APZInputBridge* InputBridge() = 0;
 
-  virtual void Destroy() {}
-
 protected:
 
   // Discourage destruction outside of decref
 
   virtual ~IAPZCTreeManager() { }
 };
 
 } // namespace layers
--- a/gfx/layers/ipc/APZCTreeManagerChild.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -46,17 +46,16 @@ APZCTreeManagerChild::SetInputBridge(APZ
 void
 APZCTreeManagerChild::Destroy()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mInputBridge) {
     mInputBridge->Destroy();
     mInputBridge = nullptr;
   }
-  Send__delete__(this);
 }
 
 void
 APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap)
 {
   SendSetKeyboardMap(aKeyboardMap);
 }
 
--- a/gfx/layers/ipc/APZCTreeManagerChild.h
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -21,18 +21,17 @@ class APZCTreeManagerChild
   : public IAPZCTreeManager
   , public PAPZCTreeManagerChild
 {
 public:
   APZCTreeManagerChild();
 
   void SetCompositorSession(RemoteCompositorSession* aSession);
   void SetInputBridge(APZInputBridgeChild* aInputBridge);
-
-  void Destroy() override;
+  void Destroy();
 
   void
   SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
 
   void
   ZoomToRect(
           const ScrollableLayerGuid& aGuid,
           const CSSRect& aRect,
--- a/gfx/layers/ipc/PAPZCTreeManager.ipdl
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -30,21 +30,16 @@ namespace layers {
 /**
  * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
  * lives on the PCompositorBridge protocol which either connects to the compositor
  * thread in the main process, or to the compositor thread in the gpu processs.
  *
  * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
  * lives in the main thread of the main or the content process. APZCTreeManagerParent
  * and APZCTreeManagerChild implement this protocol.
- *
- * There is one instance of PAPZCTreeManager for each top-level window, plus one
- * instance for each tab. The child instances are managed by the RemoteCompositorSession
- * and TabChild classes, respectively. The parent instances are held in the
- * compositor's sIndirectLayerTrees structure.
  */
 protocol PAPZCTreeManager
 {
 manager PCompositorBridge;
 
 parent:
 
   // These messages correspond to the methods
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/1464243.html
@@ -0,0 +1,13 @@
+<style>
+:not(basefont) {
+  box-shadow: 0 0 8px -moz-win-mediatext;
+  transform: scaley(56);
+}
+.cl {
+  padding-top: 16vw;
+}
+</style>
+<menu>
+<menu class="cl"></menu>
+<menu style="-webkit-perspective: 1px">
+<menu>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -159,10 +159,11 @@ load 1242822.html
 load 1248222.html
 load 1278305.html
 load 1308394.html
 load 1317403-1.html # bug 1331533
 load 1325159-1.html
 load 1331683.html
 skip-if(Android) pref(dom.disable_open_during_load,false) load 1343666.html
 load 1408078-1.html
+load 1464243.html
 load 1467847-1.html
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -44,16 +44,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 
 #if defined(XP_WIN)
 #include "gfxWindowsPlatform.h"
 #elif defined(XP_MACOSX)
 #include "gfxPlatformMac.h"
 #include "gfxQuartzSurface.h"
+#include "nsCocoaFeatures.h"
 #elif defined(MOZ_WIDGET_GTK)
 #include "gfxPlatformGtk.h"
 #elif defined(ANDROID)
 #include "gfxAndroidPlatform.h"
 #endif
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
@@ -2715,16 +2716,23 @@ gfxPlatform::InitOMTPConfig()
     true,
     Preferences::GetBool("layers.omtp.enabled", false, PrefValueKind::Default));
 
   if (mContentBackend == BackendType::CAIRO) {
     omtp.ForceDisable(FeatureStatus::Broken, "OMTP is not supported when using cairo",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_PREF"));
   }
 
+#ifdef XP_MACOSX
+  if (!nsCocoaFeatures::OnYosemiteOrLater()) {
+    omtp.ForceDisable(FeatureStatus::Blocked, "OMTP blocked before OSX 10.10",
+                      NS_LITERAL_CSTRING("FEATURE_FAILURE_OMTP_OSX_MAVERICKS"));
+  }
+#endif
+
   if (InSafeMode()) {
     omtp.ForceDisable(FeatureStatus::Blocked, "OMTP blocked by safe-mode",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
   } else if (gfxPrefs::TileEdgePaddingEnabled()) {
     omtp.ForceDisable(FeatureStatus::Blocked, "OMTP does not yet support tiling with edge padding",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_OMTP_TILING"));
   }
 
--- a/image/ImageCacheKey.cpp
+++ b/image/ImageCacheKey.cpp
@@ -5,19 +5,21 @@
 
 #include "ImageCacheKey.h"
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Move.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsString.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 namespace image {
@@ -123,32 +125,51 @@ ImageCacheKey::SchemeIs(const char* aSch
 {
   bool matches = false;
   return NS_SUCCEEDED(mURI->SchemeIs(aScheme, &matches)) && matches;
 }
 
 /* static */ void*
 ImageCacheKey::GetSpecialCaseDocumentToken(nsIDocument* aDocument, nsIURI* aURI)
 {
+  if (!aDocument) {
+    return nullptr;
+  }
+
   // For controlled documents, we cast the pointer into a void* to avoid
   // dereferencing it (since we only use it for comparisons).
-  void* pointer = nullptr;
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (aDocument && swm) {
-    ErrorResult rv;
-    if (aDocument->GetController().isSome()) {
-      pointer = aDocument;
-    }
+  if (swm && aDocument->GetController().isSome()) {
+    return aDocument;
+  }
+
+  // We want to have a unique image cache if the anti-tracking feature is
+  // enabled for 3rd party resources.
+  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled() ||
+      !nsContentUtils::IsThirdPartyWindowOrChannel(aDocument->GetInnerWindow(),
+                                                   nullptr, aURI)) {
+    return nullptr;
   }
 
-  // If this document has been marked as tracker, let's use its address to make
-  // a unique cache key.
-  if (!pointer && aDocument &&
-      nsContentUtils::StorageDisabledByAntiTracking(aDocument->GetInnerWindow(),
-                                                    nullptr, aURI)) {
-    pointer = aDocument;
+  // If the window is 3rd party resource, let's see if the first party storage
+  // access is granted for this image.
+  if (nsContentUtils::IsTrackingResourceWindow(aDocument->GetInnerWindow())) {
+    return nsContentUtils::StorageDisabledByAntiTracking(aDocument->GetInnerWindow(),
+                                                         nullptr, aURI)
+             ? aDocument : nullptr;
   }
 
-  return pointer;
+  // Another scenario is if this image is a 3rd party resource loaded by a
+  // first party context. In this case, we should check if the nsIChannel has
+  // been marked as tracking resource, but we don't have the channel yet at
+  // this point.  The best approach here is to be conservative: if we are sure
+  // that the permission is granted, let's return a nullptr. Otherwise, let's
+  // make a unique image cache.
+  if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(aDocument->GetInnerWindow(),
+                                                                    aURI)) {
+    return aDocument;
+  }
+
+  return nullptr;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/intl/l10n/DOMLocalization.jsm
+++ b/intl/l10n/DOMLocalization.jsm
@@ -53,20 +53,22 @@ const LOCALIZABLE_ATTRIBUTES = {
     optgroup: ["label"],
     option: ["label"],
     track: ["label"],
     img: ["alt"],
     textarea: ["placeholder"],
     th: ["abbr"]
   },
   "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
+    description: ["value"],
     global: [
       "accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label"
     ],
     key: ["key", "keycode"],
+    label: ["value"],
     textbox: ["placeholder"],
     toolbarbutton: ["tooltiptext"],
   }
 };
 
 
 /**
  * Translate an element.
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -334,16 +334,25 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
         getter_AddRefs(sandboxedLoadingPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = PrincipalToPrincipalInfo(sandboxedLoadingPrincipal,
                                   &sandboxedLoadingPrincipalInfoTemp);
     NS_ENSURE_SUCCESS(rv, rv);
     sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
   }
 
+  OptionalPrincipalInfo topLevelStorageAreaPrincipalInfo = mozilla::void_t();
+  if (aLoadInfo->TopLevelStorageAreaPrincipal()) {
+    PrincipalInfo topLevelStorageAreaPrincipalInfoTemp;
+    rv = PrincipalToPrincipalInfo(aLoadInfo->TopLevelStorageAreaPrincipal(),
+                                  &topLevelStorageAreaPrincipalInfoTemp);
+    NS_ENSURE_SUCCESS(rv, rv);
+    topLevelStorageAreaPrincipalInfo = topLevelStorageAreaPrincipalInfoTemp;
+  }
+
   OptionalURIParams optionalResultPrincipalURI = mozilla::void_t();
   nsCOMPtr<nsIURI> resultPrincipalURI;
   Unused << aLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
   if (resultPrincipalURI) {
     SerializeURI(resultPrincipalURI, optionalResultPrincipalURI);
   }
 
   nsTArray<RedirectHistoryEntryInfo> redirectChainIncludingInternalRedirects;
@@ -394,21 +403,21 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   }
 
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       loadingPrincipalInfo,
       triggeringPrincipalInfo,
       principalToInheritInfo,
       sandboxedLoadingPrincipalInfo,
+      topLevelStorageAreaPrincipalInfo,
       optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
-      aLoadInfo->GetFirstPartyStorageAccessGrantedOrigins(),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserUpgradeInsecureRequests(),
       aLoadInfo->GetBrowserWouldUpgradeInsecureRequests(),
       aLoadInfo->GetVerifySignedContent(),
       aLoadInfo->GetEnforceSRI(),
       aLoadInfo->GetForceAllowDataURI(),
       aLoadInfo->GetAllowInsecureRedirectToDataURI(),
       aLoadInfo->GetSkipContentPolicyCheckForWebRequest(),
@@ -473,16 +482,23 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
 
   nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal;
   if (loadInfoArgs.sandboxedLoadingPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
     sandboxedLoadingPrincipal =
       PrincipalInfoToPrincipal(loadInfoArgs.sandboxedLoadingPrincipalInfo(), &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsCOMPtr<nsIPrincipal> topLevelStorageAreaPrincipal;
+  if (loadInfoArgs.topLevelStorageAreaPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
+    topLevelStorageAreaPrincipal =
+      PrincipalInfoToPrincipal(loadInfoArgs.topLevelStorageAreaPrincipalInfo(), &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   nsCOMPtr<nsIURI> resultPrincipalURI;
   if (loadInfoArgs.resultPrincipalURI().type() != OptionalURIParams::Tvoid_t) {
     resultPrincipalURI = DeserializeURI(loadInfoArgs.resultPrincipalURI());
     NS_ENSURE_TRUE(resultPrincipalURI, NS_ERROR_UNEXPECTED);
   }
 
   RedirectHistoryArray redirectChainIncludingInternalRedirects;
   for (const RedirectHistoryEntryInfo& entryInfo :
@@ -539,25 +555,25 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
       loadInfoArgs.controller().get_IPCServiceWorkerDescriptor()));
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(loadingPrincipal,
                           triggeringPrincipal,
                           principalToInherit,
                           sandboxedLoadingPrincipal,
+                          topLevelStorageAreaPrincipal,
                           resultPrincipalURI,
                           clientInfo,
                           reservedClientInfo,
                           initialClientInfo,
                           controller,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           static_cast<LoadTainting>(loadInfoArgs.tainting()),
-                          loadInfoArgs.firstPartyStorageAccessGrantedOrigins(),
                           loadInfoArgs.upgradeInsecureRequests(),
                           loadInfoArgs.browserUpgradeInsecureRequests(),
                           loadInfoArgs.browserWouldUpgradeInsecureRequests(),
                           loadInfoArgs.verifySignedContent(),
                           loadInfoArgs.enforceSRI(),
                           loadInfoArgs.forceAllowDataURI(),
                           loadInfoArgs.allowInsecureRedirectToDataURI(),
                           loadInfoArgs.skipContentPolicyCheckForWebRequest(),
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -768,30 +768,25 @@ ModuleObject::create(JSContext* cx)
     RootedObject proto(cx, GlobalObject::getOrCreateModulePrototype(cx, cx->global()));
     if (!proto)
         return nullptr;
 
     RootedModuleObject self(cx, NewObjectWithGivenProto<ModuleObject>(cx, proto));
     if (!self)
         return nullptr;
 
-    Zone* zone = cx->zone();
-    IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
-    if (!bindings) {
-        ReportOutOfMemory(cx);
+    IndirectBindingMap* bindings = cx->new_<IndirectBindingMap>();
+    if (!bindings)
         return nullptr;
-    }
 
     self->initReservedSlot(ImportBindingsSlot, PrivateValue(bindings));
 
-    FunctionDeclarationVector* funDecls = zone->new_<FunctionDeclarationVector>(zone);
-    if (!funDecls) {
-        ReportOutOfMemory(cx);
+    FunctionDeclarationVector* funDecls = cx->new_<FunctionDeclarationVector>(cx->zone());
+    if (!funDecls)
         return nullptr;
-    }
 
     self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
     return self;
 }
 
 /* static */ void
 ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
 {
@@ -1128,22 +1123,19 @@ ModuleObject::execute(JSContext* cx, Han
 }
 
 /* static */ ModuleNamespaceObject*
 ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports)
 {
     MOZ_ASSERT(!self->namespace_());
     MOZ_ASSERT(exports->is<ArrayObject>());
 
-    Zone* zone = cx->zone();
-    auto bindings = zone->make_unique<IndirectBindingMap>();
-    if (!bindings) {
-        ReportOutOfMemory(cx);
+    auto bindings = cx->make_unique<IndirectBindingMap>();
+    if (!bindings)
         return nullptr;
-    }
 
     auto ns = ModuleNamespaceObject::create(cx, self, exports, std::move(bindings));
     if (!ns)
         return nullptr;
 
     self->initReservedSlot(NamespaceSlot, ObjectValue(*ns));
     return ns;
 }
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -7417,17 +7417,17 @@ CClosure::Create(JSContext* cx,
     }
 
     // With the exception of void, the FunctionType constructor ensures that
     // the return type has a defined size.
     MOZ_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
 
     // Allocate a buffer for the return value.
     size_t rvSize = CType::GetSize(fninfo->mReturnType);
-    errResult = result->zone()->make_pod_array<uint8_t>(rvSize);
+    errResult = cx->make_pod_array<uint8_t>(rvSize);
     if (!errResult)
       return nullptr;
 
     // Do the value conversion. This might fail, in which case we throw.
     if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, errResult.get(),
                          ConversionType::Return, nullptr, typeObj))
       return nullptr;
   }
@@ -7709,22 +7709,19 @@ CData::Create(JSContext* cx,
     return nullptr;
 
   char* data;
   if (!ownResult) {
     data = static_cast<char*>(source);
   } else {
     // Initialize our own buffer.
     size_t size = CType::GetSize(typeObj);
-    data = dataObj->zone()->pod_malloc<char>(size);
-    if (!data) {
-      // Report a catchable allocation error.
-      JS_ReportAllocationOverflow(cx);
+    data = cx->pod_malloc<char>(size);
+    if (!data)
       return nullptr;
-    }
 
     if (!source)
       memset(data, 0, size);
     else
       memcpy(data, source, size);
   }
 
   *buffer.get() = data;
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -106,17 +106,17 @@ GCRuntime::tryNewNurseryObject(JSContext
 
 template <AllowGC allowGC>
 JSObject*
 GCRuntime::tryNewTenuredObject(JSContext* cx, AllocKind kind, size_t thingSize,
                                size_t nDynamicSlots)
 {
     HeapSlot* slots = nullptr;
     if (nDynamicSlots) {
-        slots = cx->zone()->pod_malloc<HeapSlot>(nDynamicSlots);
+        slots = cx->maybe_pod_malloc<HeapSlot>(nDynamicSlots);
         if (MOZ_UNLIKELY(!slots)) {
             if (allowGC)
                 ReportOutOfMemory(cx);
             return nullptr;
         }
         Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
     }
 
--- a/js/src/gc/Nursery-inl.h
+++ b/js/src/gc/Nursery-inl.h
@@ -110,17 +110,17 @@ AllocateObjectBuffer(JSContext* cx, uint
     return buffer;
 }
 
 template <typename T>
 static inline T*
 AllocateObjectBuffer(JSContext* cx, JSObject* obj, uint32_t count)
 {
     if (cx->helperThread())
-        return cx->zone()->pod_malloc<T>(count);
+        return cx->pod_malloc<T>(count);
     size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
     T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(obj, nbytes));
     if (!buffer)
         ReportOutOfMemory(cx);
     return buffer;
 }
 
 // If this returns null then the old buffer will be left alone.
--- a/js/src/gc/WeakMapPtr.cpp
+++ b/js/src/gc/WeakMapPtr.cpp
@@ -60,17 +60,17 @@ JS::WeakMapPtr<K, V>::destroy()
 }
 
 template <typename K, typename V>
 bool
 JS::WeakMapPtr<K, V>::init(JSContext* cx)
 {
     MOZ_ASSERT(!initialized());
     typename WeakMapDetails::Utils<K, V>::PtrType map =
-        cx->zone()->new_<typename WeakMapDetails::Utils<K,V>::Type>(cx);
+        cx->new_<typename WeakMapDetails::Utils<K,V>::Type>(cx);
     if (!map || !map->init())
         return false;
     ptr = map;
     return true;
 }
 
 template <typename K, typename V>
 void
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -705,38 +705,16 @@ class Zone : public JS::shadow::Zone,
     }
     void setKeepShapeTables(bool b) {
         keepShapeTables_ = b;
     }
 
     // Delete an empty compartment after its contents have been merged.
     void deleteEmptyCompartment(JS::Compartment* comp);
 
-    /*
-     * This variation of calloc will call the large-allocation-failure callback
-     * on OOM and retry the allocation.
-     */
-    template <typename T>
-    T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
-        T* p = pod_calloc<T>(numElems, arena);
-        if (MOZ_LIKELY(!!p))
-            return p;
-        size_t bytes;
-        if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
-            reportAllocationOverflow();
-            return nullptr;
-        }
-        JSRuntime* rt = runtimeFromMainThread();
-        p = static_cast<T*>(rt->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
-        if (!p)
-            return nullptr;
-        updateMallocCounter(bytes);
-        return p;
-    }
-
     // Non-zero if the storage underlying any typed object in this zone might
     // be detached. This is stored in Zone because IC stubs bake in a pointer
     // to this field and Baseline IC code is shared across realms within a
     // Zone. Furthermore, it's not entirely clear if this flag is ever set to
     // a non-zero value since bug 1458011.
     uint32_t detachedTypedObjects = 0;
 
   private:
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -909,17 +909,17 @@ IonScript::New(JSContext* cx, IonCompila
                    paddedBailoutSize +
                    paddedConstantsSize +
                    paddedSafepointIndicesSize +
                    paddedOsiIndicesSize +
                    paddedICEntriesSize +
                    paddedRuntimeSize +
                    paddedSafepointSize +
                    paddedSharedStubSize;
-    IonScript* script = cx->zone()->pod_malloc_with_extra<IonScript, uint8_t>(bytes);
+    IonScript* script = cx->pod_malloc_with_extra<IonScript, uint8_t>(bytes);
     if (!script)
         return nullptr;
     new (script) IonScript(compilationId);
 
     uint32_t offsetCursor = sizeof(IonScript);
 
     script->runtimeData_ = offsetCursor;
     script->runtimeSize_ = runtimeSize;
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -713,17 +713,17 @@ CodeGeneratorShared::createNativeToBytec
         }
 
         // Otherwise, we must have reached the top without finding any siblings.
         MOZ_ASSERT(tree->isOutermostCaller());
         break;
     }
 
     // Allocate array for list.
-    JSScript** data = cx->zone()->pod_malloc<JSScript*>(scriptList.length());
+    JSScript** data = cx->pod_malloc<JSScript*>(scriptList.length());
     if (!data)
         return false;
 
     for (uint32_t i = 0; i < scriptList.length(); i++)
         data[i] = scriptList[i];
 
     // Success.
     nativeToBytecodeScriptListLength_ = scriptList.length();
@@ -760,17 +760,17 @@ CodeGeneratorShared::generateCompactNati
         js_free(nativeToBytecodeScriptList_);
         return false;
     }
 
     MOZ_ASSERT(tableOffset > 0);
     MOZ_ASSERT(numRegions > 0);
 
     // Writer is done, copy it to sized buffer.
-    uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
+    uint8_t* data = cx->pod_malloc<uint8_t>(writer.length());
     if (!data) {
         js_free(nativeToBytecodeScriptList_);
         return false;
     }
 
     memcpy(data, writer.buffer(), writer.length());
     nativeToBytecodeMap_ = data;
     nativeToBytecodeMapSize_ = writer.length();
@@ -916,17 +916,17 @@ CodeGeneratorShared::generateCompactTrac
 
     MOZ_ASSERT(regionTableOffset > 0);
     MOZ_ASSERT(typesTableOffset > 0);
     MOZ_ASSERT(attemptsTableOffset > 0);
     MOZ_ASSERT(typesTableOffset > regionTableOffset);
     MOZ_ASSERT(attemptsTableOffset > typesTableOffset);
 
     // Copy over the table out of the writer's buffer.
-    uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
+    uint8_t* data = cx->pod_malloc<uint8_t>(writer.length());
     if (!data)
         return false;
 
     memcpy(data, writer.buffer(), writer.length());
     trackedOptimizationsMap_ = data;
     trackedOptimizationsMapSize_ = writer.length();
     trackedOptimizationsRegionTableOffset_ = regionTableOffset;
     trackedOptimizationsTypesTableOffset_ = typesTableOffset;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1286,26 +1286,26 @@ JS::IsProfileTimelineRecordingEnabled()
     return gProfileTimelineRecordingEnabled;
 }
 
 JS_PUBLIC_API(void*)
 JS_malloc(JSContext* cx, size_t nbytes)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
-    return static_cast<void*>(cx->zone()->pod_malloc<uint8_t>(nbytes));
+    return static_cast<void*>(cx->maybe_pod_malloc<uint8_t>(nbytes));
 }
 
 JS_PUBLIC_API(void*)
 JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
-    return static_cast<void*>(cx->zone()->pod_realloc<uint8_t>(static_cast<uint8_t*>(p), oldBytes,
-                                                                newBytes));
+    return static_cast<void*>(cx->maybe_pod_realloc<uint8_t>(static_cast<uint8_t*>(p),
+                                                             oldBytes, newBytes));
 }
 
 JS_PUBLIC_API(void)
 JS_free(JSContext* cx, void* p)
 {
     return js_free(p);
 }
 
--- a/js/src/util/StringBuffer.cpp
+++ b/js/src/util/StringBuffer.cpp
@@ -23,20 +23,19 @@ ExtractWellSized(JSContext* cx, Buffer& 
 
     CharT* buf = cb.extractOrCopyRawBuffer();
     if (!buf)
         return nullptr;
 
     /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
     MOZ_ASSERT(capacity >= length);
     if (length > Buffer::sMaxInlineStorage && capacity - length > length / 4) {
-        CharT* tmp = cx->zone()->pod_realloc<CharT>(buf, capacity, length + 1);
+        CharT* tmp = cx->pod_realloc<CharT>(buf, capacity, length + 1);
         if (!tmp) {
             js_free(buf);
-            ReportOutOfMemory(cx);
             return nullptr;
         }
         buf = tmp;
     }
 
     return buf;
 }
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -454,21 +454,18 @@ ArrayBufferObject::class_constructor(JSC
         return false;
     args.rval().setObject(*bufobj);
     return true;
 }
 
 static ArrayBufferObject::BufferContents
 AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
 {
-    uint8_t* p = cx->zone()->pod_callocCanGC<uint8_t>(nbytes,
+    uint8_t* p = cx->pod_callocCanGC<uint8_t>(nbytes,
                                                       js::ArrayBufferContentsArena);
-    if (!p)
-        ReportOutOfMemory(cx);
-
     return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
 }
 
 static void
 NoteViewBufferWasDetached(ArrayBufferViewObject* view,
                           ArrayBufferObject::BufferContents newContents,
                           JSContext* cx)
 {
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -614,22 +614,19 @@ CreatePropertyIterator(JSContext* cx, Ha
                   "NativeIterators are allocated in space for 1) themselves, "
                   "2) the properties a NativeIterator iterates (as "
                   "GCPtrFlatStrings), and 3) |numGuards| HeapReceiverGuard "
                   "objects; the additional-length calculation below assumes "
                   "this size-relationship when determining the extra space to "
                   "allocate");
 
     size_t extraCount = props.length() + numGuards * 2;
-    void* mem =
-        cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraCount);
-    if (!mem) {
-        ReportOutOfMemory(cx);
+    void* mem = cx->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraCount);
+    if (!mem)
         return nullptr;
-    }
 
     // This also registers |ni| with |propIter|.
     bool hadError = false;
     NativeIterator* ni =
         new (mem) NativeIterator(cx, propIter, objBeingIterated, props, numGuards, guardKey,
                                  &hadError);
     if (hadError)
         return nullptr;
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -166,16 +166,37 @@ struct JSContext : public JS::RootingCon
             return nullptr;
         }
         return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, this);
     }
 
     /* Clear the pending exception (if any) due to OOM. */
     void recoverFromOutOfMemory();
 
+    /*
+     * This variation of calloc will call the large-allocation-failure callback
+     * on OOM and retry the allocation.
+     */
+    template <typename T>
+    T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
+        T* p = pod_calloc<T>(numElems, arena);
+        if (MOZ_LIKELY(!!p))
+            return p;
+        size_t bytes;
+        if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
+            reportAllocationOverflow();
+            return nullptr;
+        }
+        p = static_cast<T*>(runtime()->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
+        if (!p)
+            return nullptr;
+        updateMallocCounter(bytes);
+        return p;
+    }
+
     void updateMallocCounter(size_t nbytes);
 
     void reportAllocationOverflow() {
         js::ReportAllocationOverflow(this);
     }
 
     // Accessors for immutable runtime data.
     JSAtomState& names() { return *runtime_->commonNames; }
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -1574,17 +1574,17 @@ NativeObject::fillInAfterSwap(JSContext*
         MOZ_ASSERT(!priv);
 
     if (obj->slots_) {
         js_free(obj->slots_);
         obj->slots_ = nullptr;
     }
 
     if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), obj->getClass())) {
-        obj->slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic);
+        obj->slots_ = cx->pod_malloc<HeapSlot>(ndynamic);
         if (!obj->slots_)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
     }
 
     obj->initSlotRange(0, values.begin(), values.length());
     return true;
 }
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2210,17 +2210,17 @@ ScriptSource::performXDR(XDRState<mode>*
         MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
         if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
             return xdr->fail(JS::TranscodeResult_Throw);
     }
 
     return Ok();
 }
 
-// Format and return a cx->zone()->pod_malloc'ed URL for a generated script like:
+// Format and return a cx->pod_malloc'ed URL for a generated script like:
 //   {filename} line {lineno} > {introducer}
 // For example:
 //   foo.js line 7 > eval
 // indicating code compiled by the call to 'eval' on line 7 of foo.js.
 char*
 js::FormatIntroducedFilename(JSContext* cx, const char* filename, unsigned lineno,
                              const char* introducer)
 {
@@ -2235,21 +2235,20 @@ js::FormatIntroducedFilename(JSContext* 
     size_t linenoLen = SprintfLiteral(linenoBuf, "%u", lineno);
     size_t introducerLen = strlen(introducer);
     size_t len = filenameLen                    +
                  6 /* == strlen(" line ") */    +
                  linenoLen                      +
                  3 /* == strlen(" > ") */       +
                  introducerLen                  +
                  1 /* \0 */;
-    char* formatted = cx->zone()->pod_malloc<char>(len);
-    if (!formatted) {
-        ReportOutOfMemory(cx);
+    char* formatted = cx->pod_malloc<char>(len);
+    if (!formatted)
         return nullptr;
-    }
+
     mozilla::DebugOnly<size_t> checkLen = snprintf(formatted, len, "%s line %s > %s",
                                                    filename, linenoBuf, introducer);
     MOZ_ASSERT(checkLen == len - 1);
 
     return formatted;
 }
 
 bool
@@ -2347,17 +2346,17 @@ ScriptSource::setSourceMapURL(JSContext*
  */
 
 SharedScriptData*
 js::SharedScriptData::new_(JSContext* cx, uint32_t codeLength,
                            uint32_t srcnotesLength, uint32_t natoms)
 {
     size_t dataLength = natoms * sizeof(GCPtrAtom) + codeLength + srcnotesLength;
     size_t allocLength = offsetof(SharedScriptData, data_) + dataLength;
-    auto entry = reinterpret_cast<SharedScriptData*>(cx->zone()->pod_malloc<uint8_t>(allocLength));
+    auto entry = reinterpret_cast<SharedScriptData*>(cx->pod_malloc<uint8_t>(allocLength));
     if (!entry) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     /* Diagnostic for Bug 1399373.
      * We expect bytecode is always non-empty. */
     MOZ_DIAGNOSTIC_ASSERT(codeLength > 0);
@@ -2727,40 +2726,41 @@ JSScript::initScriptName(JSContext* cx)
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 static inline uint8_t*
-AllocScriptData(JS::Zone* zone, size_t size)
+AllocScriptData(JSContext* cx, size_t size)
 {
     if (!size)
         return nullptr;
 
-    uint8_t* data = zone->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
+    uint8_t* data = cx->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
     if (!data)
         return nullptr;
     MOZ_ASSERT(size_t(data) % sizeof(Value) == 0);
     return data;
 }
 
 /* static */ bool
 JSScript::partiallyInit(JSContext* cx, HandleScript script, uint32_t nscopes,
                         uint32_t nconsts, uint32_t nobjects, uint32_t ntrynotes,
                         uint32_t nscopenotes, uint32_t nyieldoffsets, uint32_t nTypeSets)
 {
+    assertSameCompartment(cx, script);
+
     size_t size = ScriptDataSize(nscopes, nconsts, nobjects, ntrynotes,
                                  nscopenotes, nyieldoffsets);
-    script->data = AllocScriptData(script->zone(), size);
-    if (size && !script->data) {
-        ReportOutOfMemory(cx);
+    script->data = AllocScriptData(cx, size);
+    if (size && !script->data)
         return false;
-    }
+
     script->dataSize_ = size;
 
     MOZ_ASSERT(nTypeSets <= UINT16_MAX);
     script->nTypeSets_ = uint16_t(nTypeSets);
 
     uint8_t* cursor = script->data;
 
     // There must always be at least 1 scope, the body scope.
@@ -3459,21 +3459,19 @@ js::detail::CopyScript(JSContext* cx, Ha
     uint32_t nscopes   = src->scopes()->length;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
     uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
 
     /* Script data */
 
     size_t size = src->dataSize();
-    UniquePtr<uint8_t, JS::FreePolicy> data(AllocScriptData(cx->zone(), size));
-    if (size && !data) {
-        ReportOutOfMemory(cx);
+    UniquePtr<uint8_t, JS::FreePolicy> data(AllocScriptData(cx, size));
+    if (size && !data)
         return false;
-    }
 
     /* Scopes */
 
     // The passed in scopes vector contains body scopes that needed to be
     // cloned especially, depending on whether the script is a function or
     // global scope. Starting at scopes.length() means we only deal with
     // intra-body scopes.
     {
@@ -3761,21 +3759,19 @@ JSScript::destroyDebugScript(FreeOp* fop
 
 bool
 JSScript::ensureHasDebugScript(JSContext* cx)
 {
     if (bitFields_.hasDebugScript_)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
-    UniqueDebugScript debug(reinterpret_cast<DebugScript*>(zone()->pod_calloc<uint8_t>(nbytes)));
-    if (!debug) {
-        ReportOutOfMemory(cx);
+    UniqueDebugScript debug(reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
+    if (!debug)
         return false;
-    }
 
     /* Create realm's debugScriptMap if necessary. */
     if (!realm()->debugScriptMap) {
         auto map = cx->make_unique<DebugScriptMap>();
         if (!map)
             return false;
 
         if (!map->init()) {
@@ -3823,16 +3819,18 @@ JSScript::setNewStepMode(FreeOp* fop, ui
 }
 
 bool
 JSScript::incrementStepModeCount(JSContext* cx)
 {
     assertSameCompartment(cx, this);
     MOZ_ASSERT(cx->realm()->isDebuggee());
 
+    AutoRealm ar(cx, this);
+
     if (!ensureHasDebugScript(cx))
         return false;
 
     DebugScript* debug = debugScript();
     uint32_t count = debug->stepMode;
     setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
     return true;
 }
@@ -3844,28 +3842,28 @@ JSScript::decrementStepModeCount(FreeOp*
     uint32_t count = debug->stepMode;
     MOZ_ASSERT(count > 0);
     setNewStepMode(fop, count - 1);
 }
 
 BreakpointSite*
 JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
 {
+    AutoRealm ar(cx, this);
+
     if (!ensureHasDebugScript(cx))
         return nullptr;
 
     DebugScript* debug = debugScript();
     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
 
     if (!site) {
-        site = cx->zone()->new_<JSBreakpointSite>(this, pc);
-        if (!site) {
-            ReportOutOfMemory(cx);
+        site = cx->new_<JSBreakpointSite>(this, pc);
+        if (!site)
             return nullptr;
-        }
         debug->numSites++;
     }
 
     return site;
 }
 
 void
 JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc)
@@ -4257,16 +4255,18 @@ LazyScript::maybeForwardedScriptSource()
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(JSContext* cx, HandleFunction fun,
                       HandleScriptSourceObject sourceObject,
                       uint64_t packedFields, uint32_t sourceStart, uint32_t sourceEnd,
                       uint32_t toStringStart, uint32_t lineno, uint32_t column)
 {
+    assertSameCompartment(cx, fun);
+
     MOZ_ASSERT(sourceObject);
     union {
         PackedView p;
         uint64_t packed;
     };
 
     packed = packedFields;
 
@@ -4274,21 +4274,19 @@ LazyScript::CreateRaw(JSContext* cx, Han
     p.hasBeenCloned = false;
     p.treatAsRunOnce = false;
 
     size_t bytes = (p.numClosedOverBindings * sizeof(JSAtom*))
                  + (p.numInnerFunctions * sizeof(GCPtrFunction));
 
     UniquePtr<uint8_t, JS::FreePolicy> table;
     if (bytes) {
-        table.reset(fun->zone()->pod_malloc<uint8_t>(bytes));
-        if (!table) {
-            ReportOutOfMemory(cx);
+        table.reset(cx->pod_malloc<uint8_t>(bytes));
+        if (!table)
             return nullptr;
-        }
     }
 
     LazyScript* res = Allocate<LazyScript>(cx);
     if (!res)
         return nullptr;
 
     cx->realm()->scheduleDelazificationForDebugger();
 
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -1247,27 +1247,23 @@ ObjectGroup::newPlainObject(JSContext* c
         // will try to use an unboxed layout for the group.
         PreliminaryObjectArrayWithTemplate* preliminaryObjects =
             cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());
         if (!preliminaryObjects)
             return nullptr;
         group->setPreliminaryObjects(preliminaryObjects);
         preliminaryObjects->registerNewObject(obj);
 
-        auto ids = group->zone()->make_zeroed_pod_array<jsid>(nproperties);
-        if (!ids) {
-            ReportOutOfMemory(cx);
+        auto ids = cx->make_zeroed_pod_array<jsid>(nproperties);
+        if (!ids)
             return nullptr;
-        }
 
-        auto types = group->zone()->make_zeroed_pod_array<TypeSet::Type>(nproperties);
-        if (!types) {
-            ReportOutOfMemory(cx);
+        auto types = cx->make_zeroed_pod_array<TypeSet::Type>(nproperties);
+        if (!types)
             return nullptr;
-        }
 
         for (size_t i = 0; i < nproperties; i++) {
             ids[i] = properties[i].id;
             types[i] = GetValueTypeForTable(obj->getSlot(i));
             AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
         }
 
         ObjectGroupRealm::PlainObjectKey key;
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -149,21 +149,19 @@ CopyScopeData(JSContext* cx, Handle<type
     BindingName* names = data->trailingNames.start();
     uint32_t length = data->length;
     for (size_t i = 0; i < length; i++) {
         if (JSAtom* name = names[i].name())
             cx->markAtom(name);
     }
 
     size_t size = SizeOfData<typename ConcreteScope::Data>(data->length);
-    void* bytes = cx->zone()->pod_malloc<char>(size);
-    if (!bytes) {
-        ReportOutOfMemory(cx);
+    void* bytes = cx->pod_malloc<char>(size);
+    if (!bytes)
         return nullptr;
-    }
 
     auto* dataCopy = new (bytes) typename ConcreteScope::Data(*data);
 
     std::uninitialized_copy_n(names, length, dataCopy->trailingNames.start());
 
     return UniquePtr<typename ConcreteScope::Data>(dataCopy);
 }
 
@@ -194,19 +192,17 @@ PrepareScopeData(JSContext* cx, BindingI
     return true;
 }
 
 template <typename ConcreteScope>
 static UniquePtr<typename ConcreteScope::Data>
 NewEmptyScopeData(JSContext* cx, uint32_t length = 0)
 {
     size_t dataSize = SizeOfData<typename ConcreteScope::Data>(length);
-    uint8_t* bytes = cx->zone()->pod_malloc<uint8_t>(dataSize);
-    if (!bytes)
-        ReportOutOfMemory(cx);
+    uint8_t* bytes = cx->pod_malloc<uint8_t>(dataSize);
     auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes);
     if (data)
         new (data) typename ConcreteScope::Data(length);
     return UniquePtr<typename ConcreteScope::Data>(data);
 }
 
 static constexpr size_t HasAtomMask = 1;
 static constexpr size_t HasAtomShift = 1;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1552,16 +1552,17 @@ intrinsic_SetDisjointTypedElements(JSCon
 
 static bool
 intrinsic_SetOverlappingTypedElements(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
 
     Rooted<TypedArrayObject*> target(cx, &args[0].toObject().as<TypedArrayObject>());
+    assertSameCompartment(cx, target);
     MOZ_ASSERT(!target->hasDetachedBuffer(),
                "shouldn't set elements if underlying buffer is detached");
 
     uint32_t targetOffset = uint32_t(args[1].toInt32());
 
     // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all
     // variables derived from it to counsel extreme caution here.
     Rooted<TypedArrayObject*> unsafeSrcCrossCompartment(cx);
@@ -1573,17 +1574,17 @@ intrinsic_SetOverlappingTypedElements(JS
     // this method performs (for example, v8's self-hosted implementation).
     // But it seems likely deliberate overlapping transfers are rare enough
     // that it's not worth the trouble to implement one (and worry about its
     // safety/correctness!).  Make a copy and do a disjoint set from that.
     uint32_t count = unsafeSrcCrossCompartment->length();
     Scalar::Type unsafeSrcTypeCrossCompartment = unsafeSrcCrossCompartment->type();
     size_t sourceByteLen = count * TypedArrayElemSize(unsafeSrcTypeCrossCompartment);
 
-    auto copyOfSrcData = target->zone()->make_pod_array<uint8_t>(sourceByteLen);
+    auto copyOfSrcData = cx->make_pod_array<uint8_t>(sourceByteLen);
     if (!copyOfSrcData)
         return false;
 
     jit::AtomicOperations::memcpySafeWhenRacy(SharedMem<uint8_t*>::unshared(copyOfSrcData.get()),
                                               unsafeSrcCrossCompartment->viewDataEither().cast<uint8_t*>(),
                                               sourceByteLen);
 
     CopyToDisjointArray(target, targetOffset, SharedMem<void*>::unshared(copyOfSrcData.get()),
@@ -2950,17 +2951,17 @@ JSRuntime::initSelfHosting(JSContext* cx
     FillSelfHostingCompileOptions(options);
 
     RootedValue rv(cx);
 
     uint32_t srcLen = GetRawScriptsSize();
 
     const unsigned char* compressed = compressedSources;
     uint32_t compressedLen = GetCompressedSize();
-    auto src = selfHostingGlobal_->zone()->make_pod_array<char>(srcLen);
+    auto src = cx->make_pod_array<char>(srcLen);
     if (!src || !DecompressString(compressed, compressedLen,
                                   reinterpret_cast<unsigned char*>(src.get()), srcLen))
     {
         return false;
     }
 
     if (!Evaluate(cx, options, src.get(), srcLen, &rv))
         return false;
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3464,22 +3464,20 @@ JSScript::makeTypes(JSContext* cx)
 {
     MOZ_ASSERT(!types_);
     assertSameCompartment(cx, this);
 
     AutoEnterAnalysis enter(cx);
 
     unsigned count = TypeScript::NumTypeSets(this);
 
-    TypeScript* typeScript = (TypeScript*)
-        zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
-    if (!typeScript) {
-        ReportOutOfMemory(cx);
+    size_t size = TypeScript::SizeIncludingTypeArray(count);
+    auto typeScript = reinterpret_cast<TypeScript*>(cx->pod_calloc<uint8_t>(size));
+    if (!typeScript)
         return false;
-    }
 
 #ifdef JS_CRASH_DIAGNOSTICS
     {
         StackTypeSet* typeArray = typeScript->typeArray();
         for (unsigned i = 0; i < count; i++)
             typeArray[i].initMagic();
     }
 #endif
@@ -3767,21 +3765,20 @@ TypeNewScript::makeNativeVersion(JSConte
 
     nativeNewScript->function_ = newScript->function();
     nativeNewScript->templateObject_ = templateObject;
 
     Initializer* cursor = newScript->initializerList;
     while (cursor->kind != Initializer::DONE) { cursor++; }
     size_t initializerLength = cursor - newScript->initializerList + 1;
 
-    nativeNewScript->initializerList = cx->zone()->pod_calloc<Initializer>(initializerLength);
-    if (!nativeNewScript->initializerList) {
-        ReportOutOfMemory(cx);
+    nativeNewScript->initializerList = cx->pod_calloc<Initializer>(initializerLength);
+    if (!nativeNewScript->initializerList)
         return nullptr;
-    }
+
     PodCopy(nativeNewScript->initializerList, newScript->initializerList, initializerLength);
 
     return nativeNewScript.release();
 }
 
 size_t
 TypeNewScript::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -650,21 +650,19 @@ class TypedArrayObjectTemplate : public 
         MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
         allocKind = GetBackgroundAllocKind(allocKind);
         RootedObjectGroup group(cx, templateObj->group());
 
         NewObjectKind newKind = TenuredObject;
 
         UniquePtr<void, JS::FreePolicy> buf;
         if (!fitsInline && len > 0) {
-            buf.reset(cx->zone()->pod_calloc<uint8_t>(nbytes));
-            if (!buf) {
-                ReportOutOfMemory(cx);
+            buf.reset(cx->pod_calloc<uint8_t>(nbytes));
+            if (!buf)
                 return nullptr;
-            }
         }
 
         TypedArrayObject* obj = NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind);
         if (!obj)
             return nullptr;
 
         initTypedArraySlots(obj, len);
         initTypedArrayData(cx, obj, len, buf.release(), allocKind);
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -254,18 +254,21 @@ DebugState::getOrCreateBreakpointSite(JS
     WasmBreakpointSite* site;
     if (!breakpointSites_.initialized() && !breakpointSites_.init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     WasmBreakpointSiteMap::AddPtr p = breakpointSites_.lookupForAdd(offset);
     if (!p) {
-        site = cx->zone()->new_<WasmBreakpointSite>(this, offset);
-        if (!site || !breakpointSites_.add(p, offset, site)) {
+        site = cx->new_<WasmBreakpointSite>(this, offset);
+        if (!site)
+            return nullptr;
+
+        if (!breakpointSites_.add(p, offset, site)) {
             js_delete(site);
             ReportOutOfMemory(cx);
             return nullptr;
         }
     } else {
         site = p->value();
     }
     return site;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2638,17 +2638,17 @@ already_AddRefed<LayerManager> nsDisplay
       if (rootPresContext && XRE_IsContentProcess()) {
         if (aBuilder->WillComputePluginGeometry()) {
           rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this);
         }
         // This must be called even if PluginGeometryUpdates were not computed.
         rootPresContext->CollectPluginGeometryUpdates(layerManager);
       }
 
-      WebRenderLayerManager* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
+      auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
 
       nsIDocShell* docShell = presContext->GetDocShell();
       nsTArray<wr::WrFilterOp> wrFilters;
       gfx::Matrix5x4* colorMatrix = nsDocShell::Cast(docShell)->GetColorMatrix();
       if (colorMatrix) {
         wr::WrFilterOp gs = {
           wr::WrFilterOpType::ColorMatrix
         };
@@ -4290,17 +4290,17 @@ void
 nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                     const nsDisplayItemGeometry* aGeometry,
                                                     nsRegion* aInvalidRegion) const
 {
   if (!mBackgroundStyle) {
     return;
   }
 
-  const nsDisplayBackgroundGeometry* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry);
 
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   nsRect positioningArea = GetPositioningArea();
   if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
       (positioningArea.Size() != geometry->mPositioningArea.Size() &&
        RenderingMightDependOnPositioningAreaSizeChange())) {
     // Positioning area changed in a way that could cause everything to change,
@@ -4517,17 +4517,18 @@ nsDisplayThemedBackground::IsWindowActiv
   return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
 }
 
 void
 nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                      const nsDisplayItemGeometry* aGeometry,
                                                      nsRegion* aInvalidRegion) const
 {
-  const nsDisplayThemedBackgroundGeometry* geometry = static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
+  auto* geometry =
+    static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry);
 
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   nsRect positioningArea = GetPositioningArea();
   if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) {
     // Invalidate everything (both old and new painting areas).
     aInvalidRegion->Or(bounds, geometry->mBounds);
     return;
@@ -5224,17 +5225,17 @@ nsDisplayBorder::AllocateGeometry(nsDisp
   return new nsDisplayBorderGeometry(this, aBuilder);
 }
 
 void
 nsDisplayBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                            const nsDisplayItemGeometry* aGeometry,
                                            nsRegion* aInvalidRegion) const
 {
-  const nsDisplayBorderGeometry* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry);
   bool snap;
 
   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) {
     // We can probably get away with only invalidating the difference
     // between the border and padding rects, but the XUL ui at least
     // is apparently painting a background with this?
     aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
   }
@@ -5268,18 +5269,18 @@ nsDisplayBorder::CreateWebRenderCommands
                                                           aBuilder,
                                                           aResources,
                                                           aSc,
                                                           aManager,
                                                           aDisplayListBuilder);
 };
 
 void
-nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
-                       gfxContext* aCtx) {
+nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx)
+{
   nsPoint offset = ToReferenceFrame();
 
   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
                          ? PaintBorderFlags::SYNC_DECODE_IMAGES
                          : PaintBorderFlags();
 
   ImgDrawResult result =
     nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
@@ -5510,17 +5511,17 @@ nsDisplayBoxShadowOuter::CreateWebRender
   return true;
 }
 
 void
 nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                    const nsDisplayItemGeometry* aGeometry,
                                                    nsRegion* aInvalidRegion) const
 {
-  const nsDisplayBoxShadowOuterGeometry* geometry =
+  auto* geometry =
     static_cast<const nsDisplayBoxShadowOuterGeometry*>(aGeometry);
   bool snap;
   if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) ||
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
       mOpacity != geometry->mOpacity) {
     nsRegion oldShadow, newShadow;
     nscoord dontCare[8];
     bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
@@ -6275,18 +6276,17 @@ nsDisplayOpacity::ComputeVisibility(nsDi
     nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
 }
 
 void
 nsDisplayOpacity::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                             const nsDisplayItemGeometry* aGeometry,
                                             nsRegion* aInvalidRegion) const
 {
-  const nsDisplayOpacityGeometry* geometry =
-    static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplayOpacityGeometry*>(aGeometry);
 
   bool snap;
   if (mOpacity != geometry->mOpacity) {
     aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds);
   }
 }
 
 void
@@ -8892,20 +8892,20 @@ nsDisplayPerspective::CreateWebRenderCom
 nsDisplayItemGeometry*
 nsCharClipDisplayItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsCharClipGeometry(this, aBuilder);
 }
 
 void
 nsCharClipDisplayItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
-                                         const nsDisplayItemGeometry* aGeometry,
-                                         nsRegion* aInvalidRegion) const
-{
-  const nsCharClipGeometry* geometry = static_cast<const nsCharClipGeometry*>(aGeometry);
+                                                 const nsDisplayItemGeometry* aGeometry,
+                                                 nsRegion* aInvalidRegion) const
+{
+  auto* geometry = static_cast<const nsCharClipGeometry*>(aGeometry);
 
   bool snap;
   nsRect newRect = geometry->mBounds;
   nsRect oldRect = GetBounds(aBuilder, &snap);
   if (mVisIStartEdge != geometry->mVisIStartEdge ||
       mVisIEndEdge != geometry->mVisIEndEdge ||
       !oldRect.IsEqualInterior(newRect) ||
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
@@ -8970,18 +8970,17 @@ nsDisplaySVGEffects::UserSpaceOffset() c
   return nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
 }
 
 void
 nsDisplaySVGEffects::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                const nsDisplayItemGeometry* aGeometry,
                                                nsRegion* aInvalidRegion) const
 {
-  const nsDisplaySVGEffectGeometry* geometry =
-    static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry);
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
       geometry->mUserSpaceOffset != UserSpaceOffset() ||
       !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
     // Filter and mask output can depend on the location of the frame's user
     // space and on the frame's BBox. We need to invalidate if either of these
     // change relative to the reference frame.
@@ -9272,18 +9271,17 @@ bool nsDisplayMask::ComputeVisibility(ns
 void
 nsDisplayMask::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) const
 {
   nsDisplaySVGEffects::ComputeInvalidationRegion(aBuilder, aGeometry,
                                                  aInvalidRegion);
 
-  const nsDisplayMaskGeometry* geometry =
-    static_cast<const nsDisplayMaskGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplayMaskGeometry*>(aGeometry);
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
 
   if (mFrame->StyleEffects()->mOpacity != geometry->mOpacity ||
       mHandleOpacity != geometry->mHandleOpacity) {
     aInvalidRegion->Or(*aInvalidRegion, bounds);
   }
 
@@ -9545,18 +9543,17 @@ nsDisplayFilter::ComputeVisibility(nsDis
 void
 nsDisplayFilter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                            const nsDisplayItemGeometry* aGeometry,
                                            nsRegion* aInvalidRegion) const
 {
   nsDisplaySVGEffects::ComputeInvalidationRegion(aBuilder, aGeometry,
                                                  aInvalidRegion);
 
-  const nsDisplayFilterGeometry* geometry =
-    static_cast<const nsDisplayFilterGeometry*>(aGeometry);
+  auto* geometry = static_cast<const nsDisplayFilterGeometry*>(aGeometry);
 
   if (aBuilder->ShouldSyncDecodeImages() &&
       geometry->ShouldInvalidateToSyncDecodeImages()) {
     bool snap;
     nsRect bounds = GetBounds(aBuilder, &snap);
     aInvalidRegion->Or(*aInvalidRegion, bounds);
   }
 }
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -186,23 +186,21 @@ Gecko_DestroyAnonymousContentList(nsTArr
 const nsTArray<RefPtr<nsINode>>*
 Gecko_GetAssignedNodes(RawGeckoElementBorrowed aElement)
 {
   MOZ_ASSERT(HTMLSlotElement::FromNode(aElement));
   return &static_cast<const HTMLSlotElement*>(aElement)->AssignedNodes();
 }
 
 void
-Gecko_ComputedStyle_Init(
-    mozilla::ComputedStyle* aStyle,
-    const mozilla::ComputedStyle* aParentContext,
-    RawGeckoPresContextBorrowed aPresContext,
-    const ServoComputedData* aValues,
-    mozilla::CSSPseudoElementType aPseudoType,
-    nsAtom* aPseudoTag)
+Gecko_ComputedStyle_Init(mozilla::ComputedStyle* aStyle,
+                         RawGeckoPresContextBorrowed aPresContext,
+                         const ServoComputedData* aValues,
+                         mozilla::CSSPseudoElementType aPseudoType,
+                         nsAtom* aPseudoTag)
 {
   auto* presContext = const_cast<nsPresContext*>(aPresContext);
   new (KnownNotNull, aStyle) mozilla::ComputedStyle(
       presContext, aPseudoTag, aPseudoType,
       ServoComputedDataForgotten(aValues));
 }
 
 ServoComputedData::ServoComputedData(
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -143,19 +143,20 @@ bool Gecko_IsSignificantChild(RawGeckoNo
 RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed element, bool is_before);
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed element);
 const nsTArray<RefPtr<nsINode>>* Gecko_GetAssignedNodes(RawGeckoElementBorrowed element);
 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);
 
 void Gecko_ComputedStyle_Init(mozilla::ComputedStyle* context,
-                              ComputedStyleBorrowedOrNull parent_context,
-                              RawGeckoPresContextBorrowed pres_context, ServoComputedDataBorrowed values,
-                              mozilla::CSSPseudoElementType pseudo_type, nsAtom* pseudo_tag);
+                              RawGeckoPresContextBorrowed pres_context,
+                              ServoComputedDataBorrowed values,
+                              mozilla::CSSPseudoElementType pseudo_type,
+                              nsAtom* pseudo_tag);
 void Gecko_ComputedStyle_Destroy(mozilla::ComputedStyle* context);
 
 // By default, Servo walks the DOM by traversing the siblings of the DOM-view
 // first child. This generally works, but misses anonymous children, which we
 // want to traverse during styling. To support these cases, we create an
 // optional stack-allocated iterator in aIterator for nodes that need it.
 void Gecko_ConstructStyleChildrenIterator(RawGeckoElementBorrowed aElement,
                                           RawGeckoStyleChildrenIteratorBorrowedMut aIterator);
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1115,13 +1115,20 @@ VARCACHE_PREF(
 //---------------------------------------------------------------------------
 
 VARCACHE_PREF(
   "privacy.restrict3rdpartystorage.enabled",
    privacy_restrict3rdpartystorage_enabled,
   RelaxedAtomicBool, false
 )
 
+// Anti-tracking permission expiration
+VARCACHE_PREF(
+  "privacy.restrict3rdpartystorage.expiration",
+   privacy_restrict3rdpartystorage_expiration,
+  uint32_t, 2592000000 // 30 days
+)
+
 //---------------------------------------------------------------------------
 // End of prefs
 //---------------------------------------------------------------------------
 
 // clang-format on
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -149,17 +149,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
       mOuterWindowID = contextOuter->WindowID();
       nsCOMPtr<nsPIDOMWindowOuter> parent = contextOuter->GetScriptableParent();
       mParentOuterWindowID = parent ? parent->WindowID() : mOuterWindowID;
       mTopOuterWindowID = FindTopOuterWindowID(contextOuter);
 
       nsGlobalWindowInner* innerWindow =
         nsGlobalWindowInner::Cast(contextOuter->GetCurrentInnerWindow());
       if (innerWindow) {
-        innerWindow->GetFirstPartyStorageAccessGrantedOrigins(mFirstPartyStorageAccessGrantedOrigins);
+        mTopLevelStorageAreaPrincipal =
+          innerWindow->GetTopLevelStorageAreaPrincipal();
       }
     }
 
     mInnerWindowID = aLoadingContext->OwnerDoc()->InnerWindowID();
     mAncestorPrincipals = aLoadingContext->OwnerDoc()->AncestorPrincipals();
     mAncestorOuterWindowIDs = aLoadingContext->OwnerDoc()->AncestorOuterWindowIDs();
     MOZ_DIAGNOSTIC_ASSERT(mAncestorPrincipals.Length() == mAncestorOuterWindowIDs.Length());
 
@@ -338,17 +339,18 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   // with the hidden window.
   nsCOMPtr<nsPIDOMWindowOuter> parent = aOuterWindow->GetScriptableParent();
   mParentOuterWindowID = parent ? parent->WindowID() : 0;
   mTopOuterWindowID = FindTopOuterWindowID(aOuterWindow);
 
   nsGlobalWindowInner* innerWindow =
     nsGlobalWindowInner::Cast(aOuterWindow->GetCurrentInnerWindow());
   if (innerWindow) {
-    innerWindow->GetFirstPartyStorageAccessGrantedOrigins(mFirstPartyStorageAccessGrantedOrigins);
+    mTopLevelStorageAreaPrincipal =
+      innerWindow->GetTopLevelStorageAreaPrincipal();
   }
 
   // get the docshell from the outerwindow, and then get the originattributes
   nsCOMPtr<nsIDocShell> docShell = aOuterWindow->GetDocShell();
   MOZ_ASSERT(docShell);
   mOriginAttributes = nsDocShell::Cast(docShell)->GetOriginAttributes();
   mAncestorPrincipals = nsDocShell::Cast(docShell)->AncestorPrincipals();
   mAncestorOuterWindowIDs = nsDocShell::Cast(docShell)->AncestorOuterWindowIDs();
@@ -362,16 +364,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
 #endif
 }
 
 LoadInfo::LoadInfo(const LoadInfo& rhs)
   : mLoadingPrincipal(rhs.mLoadingPrincipal)
   , mTriggeringPrincipal(rhs.mTriggeringPrincipal)
   , mPrincipalToInherit(rhs.mPrincipalToInherit)
   , mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
+  , mTopLevelStorageAreaPrincipal(rhs.mTopLevelStorageAreaPrincipal)
   , mResultPrincipalURI(rhs.mResultPrincipalURI)
   , mClientInfo(rhs.mClientInfo)
   // mReservedClientSource must be handled specially during redirect
   // mReservedClientInfo must be handled specially during redirect
   // mInitialClientInfo must be handled specially during redirect
   , mController(rhs.mController)
   , mPerformanceStorage(rhs.mPerformanceStorage)
   , mLoadingContext(rhs.mLoadingContext)
@@ -412,25 +415,25 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mServiceWorkerTaintingSynthesized(false)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsIPrincipal* aSandboxedLoadingPrincipal,
+                   nsIPrincipal* aTopLevelStorageAreaPrincipal,
                    nsIURI* aResultPrincipalURI,
                    const Maybe<ClientInfo>& aClientInfo,
                    const Maybe<ClientInfo>& aReservedClientInfo,
                    const Maybe<ClientInfo>& aInitialClientInfo,
                    const Maybe<ServiceWorkerDescriptor>& aController,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
                    LoadTainting aTainting,
-                   const nsTArray<nsString>& aFirstPartyStorageAccessGrantedOrigins,
                    bool aUpgradeInsecureRequests,
                    bool aBrowserUpgradeInsecureRequests,
                    bool aBrowserWouldUpgradeInsecureRequests,
                    bool aVerifySignedContent,
                    bool aEnforceSRI,
                    bool aForceAllowDataURI,
                    bool aAllowInsecureRedirectToDataURI,
                    bool aSkipContentPolicyCheckForWebRequest,
@@ -452,25 +455,25 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    const nsTArray<nsCString>& aCorsUnsafeHeaders,
                    bool aForcePreflight,
                    bool aIsPreflight,
                    bool aLoadTriggeredFromExternal,
                    bool aServiceWorkerTaintingSynthesized)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(aPrincipalToInherit)
+  , mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal)
   , mResultPrincipalURI(aResultPrincipalURI)
   , mClientInfo(aClientInfo)
   , mReservedClientInfo(aReservedClientInfo)
   , mInitialClientInfo(aInitialClientInfo)
   , mController(aController)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
-  , mFirstPartyStorageAccessGrantedOrigins(aFirstPartyStorageAccessGrantedOrigins)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mBrowserUpgradeInsecureRequests(aBrowserUpgradeInsecureRequests)
   , mBrowserWouldUpgradeInsecureRequests(aBrowserWouldUpgradeInsecureRequests)
   , mVerifySignedContent(aVerifySignedContent)
   , mEnforceSRI(aEnforceSRI)
   , mForceAllowDataURI(aForceAllowDataURI)
   , mAllowInsecureRedirectToDataURI(aAllowInsecureRedirectToDataURI)
   , mSkipContentPolicyCheckForWebRequest(aSkipContentPolicyCheckForWebRequest)
@@ -635,16 +638,29 @@ LoadInfo::GetSandboxedLoadingPrincipal(n
   MOZ_ASSERT(mSandboxedLoadingPrincipal);
 
   nsCOMPtr<nsIPrincipal> copy(mSandboxedLoadingPrincipal);
   copy.forget(aPrincipal);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetTopLevelStorageAreaPrincipal(nsIPrincipal** aTopLevelStorageAreaPrincipal)
+{
+  NS_IF_ADDREF(*aTopLevelStorageAreaPrincipal = mTopLevelStorageAreaPrincipal);
+  return NS_OK;
+}
+
+nsIPrincipal*
+LoadInfo::TopLevelStorageAreaPrincipal()
+{
+  return mTopLevelStorageAreaPrincipal;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetLoadingDocument(nsIDocument** aResult)
 {
   nsCOMPtr<nsINode> node = do_QueryReferent(mLoadingContext);
   if (node) {
     nsCOMPtr<nsIDocument> context = node->OwnerDoc();
     context.forget(aResult);
   }
   return NS_OK;
@@ -1401,42 +1417,10 @@ LoadInfo::SetPerformanceStorage(Performa
 }
 
 PerformanceStorage*
 LoadInfo::GetPerformanceStorage()
 {
   return mPerformanceStorage;
 }
 
-const nsTArray<nsString>&
-LoadInfo::GetFirstPartyStorageAccessGrantedOrigins()
-{
-  return mFirstPartyStorageAccessGrantedOrigins;
-}
-
-bool
-LoadInfo::IsFirstPartyStorageAccessGrantedFor(nsIURI* aURI)
-{
-  MOZ_ASSERT(aURI);
-
-  if (mFirstPartyStorageAccessGrantedOrigins.IsEmpty()) {
-    return false;
-  }
-
-  nsAutoString origin;
-  nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  return mFirstPartyStorageAccessGrantedOrigins.Contains(origin);
-}
-
-void
-LoadInfo::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin)
-{
-  if (!mFirstPartyStorageAccessGrantedOrigins.Contains(aOrigin)) {
-    mFirstPartyStorageAccessGrantedOrigins.AppendElement(aOrigin);
-  }
-}
-
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -92,25 +92,25 @@ private:
   // private constructor that is only allowed to be called from within
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsIPrincipal* aPrincipalToInherit,
            nsIPrincipal* aSandboxedLoadingPrincipal,
+           nsIPrincipal* aTopLevelStorageAreaPrincipal,
            nsIURI* aResultPrincipalURI,
            const Maybe<mozilla::dom::ClientInfo>& aClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aReservedClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aInitialClientInfo,
            const Maybe<mozilla::dom::ServiceWorkerDescriptor>& aController,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            LoadTainting aTainting,
-           const nsTArray<nsString>& aFirstPartyStorageAccessGrantedOrigins,
            bool aUpgradeInsecureRequests,
            bool aBrowserUpgradeInsecureRequests,
            bool aBrowserWouldUpgradeInsecureRequests,
            bool aVerifySignedContent,
            bool aEnforceSRI,
            bool aForceAllowDataURI,
            bool aAllowInsecureRedirectToDataURI,
            bool aSkipContentPolicyCheckForWebRequest,
@@ -155,31 +155,31 @@ private:
   friend class mozilla::dom::XMLHttpRequestMainThread;
 
   // if you add a member, please also update the copy constructor and consider if
   // it should be merged from parent channel through ParentLoadInfoForwarderArgs.
   nsCOMPtr<nsIPrincipal>           mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal>           mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal>           mPrincipalToInherit;
   nsCOMPtr<nsIPrincipal>           mSandboxedLoadingPrincipal;
+  nsCOMPtr<nsIPrincipal>           mTopLevelStorageAreaPrincipal;
   nsCOMPtr<nsIURI>                 mResultPrincipalURI;
 
   Maybe<mozilla::dom::ClientInfo>               mClientInfo;
   UniquePtr<mozilla::dom::ClientSource>         mReservedClientSource;
   Maybe<mozilla::dom::ClientInfo>               mReservedClientInfo;
   Maybe<mozilla::dom::ClientInfo>               mInitialClientInfo;
   Maybe<mozilla::dom::ServiceWorkerDescriptor>  mController;
   RefPtr<mozilla::dom::PerformanceStorage>      mPerformanceStorage;
 
   nsWeakPtr                        mLoadingContext;
   nsWeakPtr                        mContextForTopLevelLoad;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
   LoadTainting                     mTainting;
-  nsTArray<nsString>               mFirstPartyStorageAccessGrantedOrigins;
   bool                             mUpgradeInsecureRequests;
   bool                             mBrowserUpgradeInsecureRequests;
   bool                             mBrowserWouldUpgradeInsecureRequests;
   bool                             mVerifySignedContent;
   bool                             mEnforceSRI;
   bool                             mForceAllowDataURI;
   bool                             mAllowInsecureRedirectToDataURI;
   bool                             mSkipContentPolicyCheckForWebRequest;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -873,16 +873,29 @@ interface nsILoadInfo : nsISupports
    * Returns the null principal of the resulting resource if the SEC_SANDBOXED
    * flag is set.  Otherwise returns null.  This is used by
    * GetChannelResultPrincipal() to ensure that the same null principal object
    * is returned every time.
    */
   [noscript] readonly attribute nsIPrincipal sandboxedLoadingPrincipal;
 
   /**
+   * Return the top-level storage area principal, which is the principal of
+   * the top-level window if it's not a 3rd party context, non tracking
+   * resource.
+   */
+  [noscript] readonly attribute nsIPrincipal topLevelStorageAreaPrincipal;
+
+  /**
+   * A C++-friendly version of topLevelStorageAreaPrincipal.
+   */
+  [noscript, notxpcom, nostdcall, binaryname(TopLevelStorageAreaPrincipal)]
+  nsIPrincipal binaryTopLevelStorageAreaPrincipal();
+
+  /**
    * Note which client (i.e. global) initiated this network request.  All
    * nsGlobalWindow and WorkerPrivate can be converted to a ClientInfo to
    * be set here.  While this is being added to support service worker
    * FetchEvent, it can also be used to communicate other information about
    * the source global context in the future.
    */
   [noscript, nostdcall, notxpcom]
   void SetClientInfo(in const_ClientInfoRef aClientInfo);
@@ -1015,20 +1028,9 @@ interface nsILoadInfo : nsISupports
     * exact tainting level of the Response passed to FetchEvent.respondWith().
     * This method allows us to override the tainting level in that case.
     *
     * NOTE: This should not be used outside of service worker code! Use
     *       nsILoadInfo::MaybeIncreaseTainting() instead.
    */
   [noscript, nostdcall, notxpcom]
   void SynthesizeServiceWorkerTainting(in LoadTainting aTainting);
-
-  /**
-   * This is the origin that has access storage granted also if 3rd party and
-   * in the tracking protection list.
-   */
-  [noscript, notxpcom, nostdcall]
-  StringArrayRef getFirstPartyStorageAccessGrantedOrigins();
-  [noscript, notxpcom, nostdcall]
-  bool isFirstPartyStorageAccessGrantedFor(in nsIURI aURI);
-  [noscript, notxpcom, nostdcall]
-  void addFirstPartyStorageAccessGrantedFor(in AString aOrigin);
 };
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/NeckoChannelParams.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/SystemGroup.h"
 #include "nsCookie.h"
@@ -177,24 +178,26 @@ CookieServiceChild::TrackCookieLoad(nsIC
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
   if (RequireThirdPartyCheck()) {
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   }
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                uri)) {
+      firstPartyStorageAccessGranted = true;
+    }
   }
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
-    if (loadInfo->IsFirstPartyStorageAccessGrantedFor(uri)) {
-      firstPartyStorageAccessGranted = true;
-    }
   }
   URIParams uriParams;
   SerializeURI(uri, uriParams);
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, uri);
   SendPrepareCookieList(uriParams, isForeign, isTrackingResource,
                         firstPartyStorageAccessGranted, isSafeTopLevelNav,
                         isSameSiteForeign, attrs);
@@ -568,24 +571,25 @@ CookieServiceChild::GetCookieStringInter
   }
 
   // Asynchronously call the parent.
   bool isForeign = true;
   if (RequireThirdPartyCheck())
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
+  bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
-  }
-
-  bool firstPartyStorageAccessGranted = false;
-  if (loadInfo->IsFirstPartyStorageAccessGrantedFor(aHostURI)) {
-    firstPartyStorageAccessGranted = true;
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                aHostURI)) {
+      firstPartyStorageAccessGranted = true;
+    }
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
 
   nsAutoCString result;
   if (!mIPCSync) {
     GetCookieStringFromCookieHashTable(aHostURI, isForeign, isTrackingResource,
@@ -624,43 +628,45 @@ CookieServiceChild::SetCookieStringInter
     return NS_OK;
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
   if (RequireThirdPartyCheck())
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
+  bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                aHostURI)) {
+      firstPartyStorageAccessGranted = true;
+    }
   }
 
   nsDependentCString cookieString(aCookieString);
   nsDependentCString stringServerTime;
   if (aServerTime)
     stringServerTime.Rebind(aServerTime);
 
   URIParams hostURIParams;
   SerializeURI(aHostURI, hostURIParams);
 
   nsCOMPtr<nsIURI> channelURI;
   aChannel->GetURI(getter_AddRefs(channelURI));
   URIParams channelURIParams;
   SerializeURI(channelURI, channelURIParams);
 
-  bool firstPartyStorageAccessGranted = false;
   mozilla::OriginAttributes attrs;
   if (aChannel) {
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
     if (loadInfo) {
       attrs = loadInfo->GetOriginAttributes();
-      if (loadInfo->IsFirstPartyStorageAccessGrantedFor(aHostURI)) {
-        firstPartyStorageAccessGranted = true;
-      }
     }
   }
 
   // Asynchronously call the parent.
   if (mIPCOpen) {
     SendSetCookieString(hostURIParams, channelURIParams,
                         isForeign, isTrackingResource,
                         firstPartyStorageAccessGranted, cookieString,
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -152,24 +152,25 @@ CookieServiceParent::TrackCookieLoad(nsI
 
   // Send matching cookies to Child.
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
   thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   bool isForeign = true;
   thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
 
   bool isTrackingResource = false;
+  bool storageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
-  }
-
-  bool storageAccessGranted = false;
-  if (loadInfo && loadInfo->IsFirstPartyStorageAccessGrantedFor(uri)) {
-    storageAccessGranted = true;
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                uri)) {
+      storageAccessGranted = true;
+    }
   }
 
   nsTArray<nsCookie*> foundCookieList;
   mCookieService->GetCookiesForURI(uri, isForeign, isTrackingResource,
                                    storageAccessGranted, isSafeTopLevelNav,
                                    aIsSameSiteForeign, false, attrs,
                                    foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Printf.h"
 #include "mozilla/Unused.h"
 
 #include "mozilla/net/CookieServiceChild.h"
@@ -2035,29 +2036,30 @@ nsCookieService::GetCookieStringCommon(n
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookie);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
   mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
+  bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
+
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                aHostURI)) {
+      firstPartyStorageAccessGranted = true;
+    }
   }
 
   OriginAttributes attrs;
-  bool firstPartyStorageAccessGranted = false;
   if (aChannel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-    if (loadInfo && loadInfo->IsFirstPartyStorageAccessGrantedFor(aHostURI)) {
-      firstPartyStorageAccessGranted = true;
-    }
-
     NS_GetOriginAttributes(aChannel, attrs);
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
   nsAutoCString result;
   GetCookieStringInternal(aHostURI, isForeign, isTrackingResource,
                           firstPartyStorageAccessGranted, isSafeTopLevelNav,
@@ -2141,29 +2143,31 @@ nsCookieService::SetCookieStringCommon(n
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookieHeader);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
   mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isTrackingResource = false;
+  bool firstPartyStorageAccessGranted = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
+
+    if (isForeign && isTrackingResource &&
+        AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
+                                                                aHostURI)) {
+      firstPartyStorageAccessGranted = true;
+    }
   }
 
   OriginAttributes attrs;
-  bool firstPartyStorageAccessGranted = false;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
-    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
-    if (loadInfo && loadInfo->IsFirstPartyStorageAccessGrantedFor(aHostURI)) {
-      firstPartyStorageAccessGranted = true;
-    }
   }
 
   nsDependentCString cookieString(aCookieHeader);
   nsDependentCString serverTime(aServerTime ? aServerTime : "");
   SetCookieStringInternal(aHostURI, isForeign, isTrackingResource,
                           firstPartyStorageAccessGranted, cookieString,
                           serverTime, aFromHttp, attrs, aChannel);
   return NS_OK;
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -37,21 +37,21 @@ struct RedirectHistoryEntryInfo
 };
 
 struct LoadInfoArgs
 {
   OptionalPrincipalInfo       requestingPrincipalInfo;
   PrincipalInfo               triggeringPrincipalInfo;
   OptionalPrincipalInfo       principalToInheritInfo;
   OptionalPrincipalInfo       sandboxedLoadingPrincipalInfo;
+  OptionalPrincipalInfo       topLevelStorageAreaPrincipalInfo;
   OptionalURIParams           resultPrincipalURI;
   uint32_t                    securityFlags;
   uint32_t                    contentPolicyType;
   uint32_t                    tainting;
-  nsString[]                  firstPartyStorageAccessGrantedOrigins;
   bool                        upgradeInsecureRequests;
   bool                        browserUpgradeInsecureRequests;
   bool                        browserWouldUpgradeInsecureRequests;
   bool                        verifySignedContent;
   bool                        enforceSRI;
   bool                        forceAllowDataURI;
   bool                        allowInsecureRedirectToDataURI;
   bool                        skipContentPolicyCheckForWebRequest;
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-247bf1dc3121
+53c2ee896c57
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ b/security/nss/automation/taskcluster/docker/setup.sh
@@ -17,16 +17,17 @@ apt_packages+=('curl')
 apt_packages+=('npm')
 apt_packages+=('git')
 apt_packages+=('golang-1.6')
 apt_packages+=('libxml2-utils')
 apt_packages+=('locales')
 apt_packages+=('ninja-build')
 apt_packages+=('pkg-config')
 apt_packages+=('zlib1g-dev')
+apt_packages+=('cmake')
 
 # 32-bit builds
 apt_packages+=('lib32z1-dev')
 apt_packages+=('gcc-multilib')
 apt_packages+=('g++-multilib')
 
 # ct-verif and sanitizers
 apt_packages+=('valgrind')
--- a/security/nss/cmd/fipstest/fipstest.c
+++ b/security/nss/cmd/fipstest/fipstest.c
@@ -2331,31 +2331,31 @@ sha_get_hashType(int hashbits)
             break;
         default:
             break;
     }
     return hashType;
 }
 
 HASH_HashType
-hash_string_to_hashType(const char * src)
+hash_string_to_hashType(const char *src)
 {
     HASH_HashType shaAlg = HASH_AlgNULL;
     if (strncmp(src, "SHA-1", 5) == 0) {
-         shaAlg = HASH_AlgSHA1;
+        shaAlg = HASH_AlgSHA1;
     } else if (strncmp(src, "SHA-224", 7) == 0) {
         shaAlg = HASH_AlgSHA224;
     } else if (strncmp(src, "SHA-256", 7) == 0) {
         shaAlg = HASH_AlgSHA256;
     } else if (strncmp(src, "SHA-384", 7) == 0) {
         shaAlg = HASH_AlgSHA384;
     } else if (strncmp(src, "SHA-512", 7) == 0) {
         shaAlg = HASH_AlgSHA512;
     } else if (strncmp(src, "SHA1", 4) == 0) {
-         shaAlg = HASH_AlgSHA1;
+        shaAlg = HASH_AlgSHA1;
     } else if (strncmp(src, "SHA224", 6) == 0) {
         shaAlg = HASH_AlgSHA224;
     } else if (strncmp(src, "SHA256", 6) == 0) {
         shaAlg = HASH_AlgSHA256;
     } else if (strncmp(src, "SHA384", 6) == 0) {
         shaAlg = HASH_AlgSHA384;
     } else if (strncmp(src, "SHA512", 6) == 0) {
         shaAlg = HASH_AlgSHA512;
@@ -2652,17 +2652,17 @@ ecdsa_siggen_test(char *reqfn)
             src += 2; /* skip the hyphen */
             *dst++ = *src++;
             *dst++ = *src++;
             *dst++ = *src++;
             *dst = '\0';
             src++; /* skip the comma */
             /* set the SHA Algorithm */
             shaAlg = hash_string_to_hashType(src);
-            if (shaAlg == HASH_AlgNULL){
+            if (shaAlg == HASH_AlgNULL) {
                 fprintf(ecdsaresp, "ERROR: Unable to find SHAAlg type");
                 goto loser;
             }
             if (ecparams != NULL) {
                 PORT_FreeArena(ecparams->arena, PR_FALSE);
                 ecparams = NULL;
             }
             encodedparams = getECParams(curve);
@@ -2972,25 +2972,25 @@ loser:
  * reqfn is the pathname of the REQUEST file.
  *
  * The output RESPONSE file is written to stdout.
  */
 #define MAX_ECC_PARAMS 256
 void
 ecdh_functional(char *reqfn, PRBool response)
 {
-    char buf[256];   /* holds one line from the input REQUEST file.
+    char buf[256];  /* holds one line from the input REQUEST file.
                          * needs to be large enough to hold the longest
                          * line "Qx = <144 hex digits>\n".
                          */
     FILE *ecdhreq;  /* input stream from the REQUEST file */
     FILE *ecdhresp; /* output stream to the RESPONSE file */
-    char curve[16];  /* "nistxddd" */
+    char curve[16]; /* "nistxddd" */
     unsigned char hashBuf[HASH_LENGTH_MAX];
-    ECParams *ecparams[MAX_ECC_PARAMS] = {NULL};
+    ECParams *ecparams[MAX_ECC_PARAMS] = { NULL };
     ECPrivateKey *ecpriv = NULL;
     ECParams *current_ecparams = NULL;
     SECItem pubkey;
     SECItem ZZ;
     unsigned int i;
     unsigned int len = 0;
     unsigned int uit_len = 0;
     int current_curve = -1;
@@ -3021,92 +3021,95 @@ ecdh_functional(char *reqfn, PRBool resp
 
                 if ((current_curve < 0) || (current_curve > MAX_ECC_PARAMS)) {
                     fprintf(stderr, "No curve type defined\n");
                     goto loser;
                 }
 
                 src = &buf[1];
                 /* skip passed the colon */
-                while (*src && *src != ':') src++;
+                while (*src && *src != ':')
+                    src++;
                 if (*src != ':') {
                     fprintf(stderr,
-                        "No colon in curve selected statement\n%s", buf);
+                            "No colon in curve selected statement\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 dst = &curve[4];
                 *dst++ = tolower(*src);
                 src += 2; /* skip the hyphen */
                 *dst++ = *src++;
                 *dst++ = *src++;
                 *dst++ = *src++;
                 *dst = '\0';
                 if (ecparams[current_curve] != NULL) {
                     PORT_FreeArena(ecparams[current_curve]->arena, PR_FALSE);
                     ecparams[current_curve] = NULL;
                 }
                 encodedparams = getECParams(curve);
                 if (encodedparams == NULL) {
                     fprintf(stderr, "Unknown curve %s.", curve);
                     goto loser;
                 }
-                if (EC_DecodeParams(encodedparams, &ecparams[current_curve])
-                                                        != SECSuccess) {
+                if (EC_DecodeParams(encodedparams, &ecparams[current_curve]) != SECSuccess) {
                     fprintf(stderr, "Curve %s not supported.\n", curve);
                     goto loser;
                 }
                 SECITEM_FreeItem(encodedparams, PR_TRUE);
                 fputs(buf, ecdhresp);
                 continue;
             }
             /* [Ex - SHAxxx] */
             if (buf[1] == 'E' && buf[3] == ' ') {
                 const char *src;
                 current_curve = buf[2] - 'A';
                 if ((current_curve < 0) || (current_curve > 256)) {
                     fprintf(stderr, "bad curve type defined (%c)\n", buf[2]);
                     goto loser;
                 }
-                    current_ecparams = ecparams[current_curve];
+                current_ecparams = ecparams[current_curve];
                 if (current_ecparams == NULL) {
                     fprintf(stderr, "no curve defined for type %c defined\n",
                             buf[2]);
                     goto loser;
                 }
                 /* skip passed the colon */
                 src = &buf[1];
-                while (*src && *src != '-') src++;
+                while (*src && *src != '-')
+                    src++;
                 if (*src != '-') {
                     fprintf(stderr,
-                        "No data in curve selected statement\n%s",buf);
+                            "No data in curve selected statement\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 hash = hash_string_to_hashType(src);
-                if (hash == HASH_AlgNULL){
+                if (hash == HASH_AlgNULL) {
                     fprintf(ecdhresp, "ERROR: Unable to find SHAAlg type");
                     goto loser;
                 }
                 fputs(buf, ecdhresp);
                 continue;
             }
             fputs(buf, ecdhresp);
             continue;
         }
         /* COUNT = ... */
         if (strncmp(buf, "COUNT", 5) == 0) {
             fputs(buf, ecdhresp);
             if (current_ecparams == NULL) {
                 fprintf(stderr, "no curve defined for type %c defined\n",
-                            buf[2]);
+                        buf[2]);
                 goto loser;
             }
             len = (current_ecparams->fieldID.size + 7) >> 3;
             if (pubkey.data != NULL) {
                 PORT_Free(pubkey.data);
                 pubkey.data = NULL;
             }
             SECITEM_AllocItem(NULL, &pubkey, EC_GetPointSize(current_ecparams));
@@ -3135,59 +3138,59 @@ ecdh_functional(char *reqfn, PRBool resp
             }
             from_hex_str(&pubkey.data[1 + len], len, &buf[i]);
             if (current_ecparams == NULL) {
                 fprintf(stderr, "no curve defined\n");
                 goto loser;
             }
             /* validate CAVS public key */
             if (EC_ValidatePublicKey(current_ecparams, &pubkey) != SECSuccess) {
-                fprintf(stderr,"BAD key detected\n");
+                fprintf(stderr, "BAD key detected\n");
                 goto loser;
             }
 
             /* generate ECC key pair */
             if (EC_NewKey(current_ecparams, &ecpriv) != SECSuccess) {
-                fprintf(stderr,"Failed to generate new key\n");
+                fprintf(stderr, "Failed to generate new key\n");
                 goto loser;
             }
             /* validate UIT generated public key */
             if (EC_ValidatePublicKey(current_ecparams, &ecpriv->publicValue) !=
-                    SECSuccess) {
-                fprintf(stderr,"generate key did not validate\n");
+                SECSuccess) {
+                fprintf(stderr, "generate key did not validate\n");
                 goto loser;
             }
             /* output UIT public key */
             uit_len = ecpriv->publicValue.len;
             if (uit_len % 2 == 0) {
-                fprintf(stderr,"generate key had invalid public value len\n");
+                fprintf(stderr, "generate key had invalid public value len\n");
                 goto loser;
             }
             uit_len = (uit_len - 1) / 2;
             if (ecpriv->publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
-                fprintf(stderr,"generate key was compressed\n");
+                fprintf(stderr, "generate key was compressed\n");
                 goto loser;
             }
             fputs("QeIUTx = ", ecdhresp);
             to_hex_str(buf, &ecpriv->publicValue.data[1], uit_len);
             fputs(buf, ecdhresp);
             fputc('\n', ecdhresp);
             fputs("QeIUTy = ", ecdhresp);
             to_hex_str(buf, &ecpriv->publicValue.data[1 + uit_len], uit_len);
             fputs(buf, ecdhresp);
             fputc('\n', ecdhresp);
             /* ECDH */
-            if (ECDH_Derive(&pubkey,current_ecparams, &ecpriv->privateValue,
-                        PR_FALSE, &ZZ) != SECSuccess) {
-                fprintf(stderr,"Derive failed\n");
+            if (ECDH_Derive(&pubkey, current_ecparams, &ecpriv->privateValue,
+                            PR_FALSE, &ZZ) != SECSuccess) {
+                fprintf(stderr, "Derive failed\n");
                 goto loser;
             }
             /* output hash of ZZ */
-            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess ) {
-                fprintf(stderr,"hash of derived key failed\n");
+            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) {
+                fprintf(stderr, "hash of derived key failed\n");
                 goto loser;
             }
             SECITEM_FreeItem(&ZZ, PR_FALSE);
             fputs("HashZZ = ", ecdhresp);
             to_hex_str(buf, hashBuf, fips_hashLen(hash));
             fputs(buf, ecdhresp);
             fputc('\n', ecdhresp);
             fputc('\n', ecdhresp);
@@ -3195,50 +3198,50 @@ ecdh_functional(char *reqfn, PRBool resp
             ecpriv = NULL;
             continue;
         }
     }
 loser:
     if (ecpriv != NULL) {
         PORT_FreeArena(ecpriv->ecParams.arena, PR_TRUE);
     }
-    for (i=0; i < MAX_ECC_PARAMS; i++) {
+    for (i = 0; i < MAX_ECC_PARAMS; i++) {
         if (ecparams[i] != NULL) {
             PORT_FreeArena(ecparams[i]->arena, PR_FALSE);
             ecparams[i] = NULL;
         }
     }
     if (pubkey.data != NULL) {
         PORT_Free(pubkey.data);
     }
     fclose(ecdhreq);
 }
 
-#define MATCH_OPENSSL 1 
+#define MATCH_OPENSSL 1
 /*
  * Perform the ECDH Validity Test.
  *
  * reqfn is the pathname of the REQUEST file.
  *
  * The output RESPONSE file is written to stdout.
  */
 void
 ecdh_verify(char *reqfn, PRBool response)
 {
-    char buf[256];   /* holds one line from the input REQUEST file.
+    char buf[256];  /* holds one line from the input REQUEST file.
                          * needs to be large enough to hold the longest
                          * line "Qx = <144 hex digits>\n".
                          */
     FILE *ecdhreq;  /* input stream from the REQUEST file */
     FILE *ecdhresp; /* output stream to the RESPONSE file */
-    char curve[16];  /* "nistxddd" */
+    char curve[16]; /* "nistxddd" */
     unsigned char hashBuf[HASH_LENGTH_MAX];
     unsigned char cavsHashBuf[HASH_LENGTH_MAX];
     unsigned char private_data[MAX_ECKEY_LEN];
-    ECParams *ecparams[MAX_ECC_PARAMS] =  {NULL};
+    ECParams *ecparams[MAX_ECC_PARAMS] = { NULL };
     ECParams *current_ecparams = NULL;
     SECItem pubkey;
     SECItem ZZ;
     SECItem private_value;
     unsigned int i;
     unsigned int len = 0;
     int current_curve = -1;
     HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */
@@ -3268,92 +3271,95 @@ ecdh_verify(char *reqfn, PRBool response
 
                 if ((current_curve < 0) || (current_curve > MAX_ECC_PARAMS)) {
                     fprintf(stderr, "No curve type defined\n");
                     goto loser;
                 }
 
                 src = &buf[1];
                 /* skip passed the colon */
-                while (*src && *src != ':') src++;
+                while (*src && *src != ':')
+                    src++;
                 if (*src != ':') {
                     fprintf(stderr,
-                        "No colon in curve selected statement\n%s", buf);
+                            "No colon in curve selected statement\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 dst = &curve[4];
                 *dst++ = tolower(*src);
                 src += 2; /* skip the hyphen */
                 *dst++ = *src++;
                 *dst++ = *src++;
                 *dst++ = *src++;
                 *dst = '\0';
                 if (ecparams[current_curve] != NULL) {
                     PORT_FreeArena(ecparams[current_curve]->arena, PR_FALSE);
                     ecparams[current_curve] = NULL;
                 }
                 encodedparams = getECParams(curve);
                 if (encodedparams == NULL) {
                     fprintf(stderr, "Unknown curve %s.\n", curve);
                     goto loser;
                 }
-                if (EC_DecodeParams(encodedparams, &ecparams[current_curve])
-                                                        != SECSuccess) {
+                if (EC_DecodeParams(encodedparams, &ecparams[current_curve]) != SECSuccess) {
                     fprintf(stderr, "Curve %s not supported.\n", curve);
                     goto loser;
                 }
                 SECITEM_FreeItem(encodedparams, PR_TRUE);
                 fputs(buf, ecdhresp);
                 continue;
             }
             /* [Ex - SHAxxx] */
             if (buf[1] == 'E' && buf[3] == ' ') {
                 const char *src;
                 current_curve = buf[2] - 'A';
                 if ((current_curve < 0) || (current_curve > 256)) {
                     fprintf(stderr, "bad curve type defined (%c)\n", buf[2]);
                     goto loser;
                 }
-                    current_ecparams = ecparams[current_curve];
+                current_ecparams = ecparams[current_curve];
                 if (current_ecparams == NULL) {
                     fprintf(stderr, "no curve defined for type %c defined\n",
                             buf[2]);
                     goto loser;
                 }
                 /* skip passed the colon */
                 src = &buf[1];
-                while (*src && *src != '-') src++;
+                while (*src && *src != '-')
+                    src++;
                 if (*src != '-') {
                     fprintf(stderr,
-                        "No data in curve selected statement\n%s",buf);
+                            "No data in curve selected statement\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 hash = hash_string_to_hashType(src);
-                if (hash == HASH_AlgNULL){
+                if (hash == HASH_AlgNULL) {
                     fprintf(ecdhresp, "ERROR: Unable to find SHAAlg type");
                     goto loser;
                 }
                 fputs(buf, ecdhresp);
                 continue;
             }
             fputs(buf, ecdhresp);
             continue;
         }
         /* COUNT = ... */
         if (strncmp(buf, "COUNT", 5) == 0) {
             fputs(buf, ecdhresp);
             if (current_ecparams == NULL) {
                 fprintf(stderr, "no curve defined for type %c defined\n",
-                            buf[2]);
+                        buf[2]);
                 goto loser;
             }
             len = (current_ecparams->fieldID.size + 7) >> 3;
             if (pubkey.data != NULL) {
                 PORT_Free(pubkey.data);
                 pubkey.data = NULL;
             }
             SECITEM_AllocItem(NULL, &pubkey, EC_GetPointSize(current_ecparams));
@@ -3411,65 +3417,65 @@ ecdh_verify(char *reqfn, PRBool response
             from_hex_str(cavsHashBuf, fips_hashLen(hash), &buf[i]);
             if (current_ecparams == NULL) {
                 fprintf(stderr, "no curve defined for type defined\n");
                 goto loser;
             }
             /* validate CAVS public key */
             if (EC_ValidatePublicKey(current_ecparams, &pubkey) != SECSuccess) {
 #ifdef MATCH_OPENSSL
-		fprintf(ecdhresp, "Result = F\n");
+                fprintf(ecdhresp, "Result = F\n");
 #else
-		fprintf(ecdhresp, "Result = F # key didn't validate\n");
+                fprintf(ecdhresp, "Result = F # key didn't validate\n");
 #endif
                 continue;
             }
 
             /* ECDH */
             if (ECDH_Derive(&pubkey, current_ecparams, &private_value,
-                        PR_FALSE, &ZZ) != SECSuccess) {
-                fprintf(stderr,"Derive failed\n");
+                            PR_FALSE, &ZZ) != SECSuccess) {
+                fprintf(stderr, "Derive failed\n");
                 goto loser;
             }
-            /* output  ZZ */
+/* output  ZZ */
 #ifndef MATCH_OPENSSL
             fputs("Z = ", ecdhresp);
             to_hex_str(buf, ZZ.data, ZZ.len);
             fputs(buf, ecdhresp);
             fputc('\n', ecdhresp);
 #endif
 
-            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess ) {
-                fprintf(stderr,"hash of derived key failed\n");
+            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) {
+                fprintf(stderr, "hash of derived key failed\n");
                 goto loser;
             }
             SECITEM_FreeItem(&ZZ, PR_FALSE);
 #ifndef MATCH_NIST
             fputs("IUTHashZZ = ", ecdhresp);
             to_hex_str(buf, hashBuf, fips_hashLen(hash));
             fputs(buf, ecdhresp);
             fputc('\n', ecdhresp);
 #endif
             if (memcmp(hashBuf, cavsHashBuf, fips_hashLen(hash)) != 0) {
 #ifdef MATCH_OPENSSL
-		fprintf(ecdhresp, "Result = F\n");
+                fprintf(ecdhresp, "Result = F\n");
 #else
-		fprintf(ecdhresp, "Result = F # hash doesn't match\n");
+                fprintf(ecdhresp, "Result = F # hash doesn't match\n");
 #endif
             } else {
-		fprintf(ecdhresp, "Result = P\n");
+                fprintf(ecdhresp, "Result = P\n");
             }
 #ifndef MATCH_OPENSSL
             fputc('\n', ecdhresp);
 #endif
             continue;
         }
     }
 loser:
-    for (i=0; i < MAX_ECC_PARAMS; i++) {
+    for (i = 0; i < MAX_ECC_PARAMS; i++) {
         if (ecparams[i] != NULL) {
             PORT_FreeArena(ecparams[i]->arena, PR_FALSE);
             ecparams[i] = NULL;
         }
     }
     if (pubkey.data != NULL) {
         PORT_Free(pubkey.data);
     }
@@ -3482,29 +3488,29 @@ loser:
  * reqfn is the pathname of the REQUEST file.
  *
  * The output RESPONSE file is written to stdout.
  */
 #define MAX_ECC_PARAMS 256
 void
 dh_functional(char *reqfn, PRBool response)
 {
-    char buf[1024];   /* holds one line from the input REQUEST file.
+    char buf[1024]; /* holds one line from the input REQUEST file.
                          * needs to be large enough to hold the longest
                          * line "YephCAVS = <512 hex digits>\n".
                          */
-    FILE *dhreq;  /* input stream from the REQUEST file */
-    FILE *dhresp; /* output stream to the RESPONSE file */
+    FILE *dhreq;    /* input stream from the REQUEST file */
+    FILE *dhresp;   /* output stream to the RESPONSE file */
     unsigned char hashBuf[HASH_LENGTH_MAX];
     DSAPrivateKey *dsapriv = NULL;
     PQGParams pqg = { 0 };
-    unsigned char pubkeydata[DSA_MAX_P_BITS/8];
+    unsigned char pubkeydata[DSA_MAX_P_BITS / 8];
     SECItem pubkey;
     SECItem ZZ;
-    unsigned int i,j;
+    unsigned int i, j;
     unsigned int pgySize;
     HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */
 
     dhreq = fopen(reqfn, "r");
     dhresp = stdout;
     while (fgets(buf, sizeof buf, dhreq) != NULL) {
         /* a comment or blank line */
         if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') {
@@ -3512,26 +3518,28 @@ dh_functional(char *reqfn, PRBool respon
             continue;
         }
         if (buf[0] == '[') {
             /* [Fx - SHAxxx] */
             if (buf[1] == 'F' && buf[3] == ' ') {
                 const char *src;
                 /* skip passed the colon */
                 src = &buf[1];
-                while (*src && *src != '-') src++;
+                while (*src && *src != '-')
+                    src++;
                 if (*src != '-') {
-                    fprintf(stderr, "No hash specified\n%s",buf);
+                    fprintf(stderr, "No hash specified\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 hash = hash_string_to_hashType(src);
-                if (hash == HASH_AlgNULL){
+                if (hash == HASH_AlgNULL) {
                     fprintf(dhresp, "ERROR: Unable to find SHAAlg type");
                     goto loser;
                 }
                 /* clear the PQG parameters */
                 if (pqg.prime.data) { /* P */
                     SECITEM_ZfreeItem(&pqg.prime, PR_FALSE);
                 }
                 if (pqg.subPrime.data) { /* Q */
@@ -3541,17 +3549,17 @@ dh_functional(char *reqfn, PRBool respon
                     SECITEM_ZfreeItem(&pqg.base, PR_FALSE);
                 }
                 pgySize = DSA_MAX_P_BITS / 8; /* change if more key sizes are supported in CAVS */
                 SECITEM_AllocItem(NULL, &pqg.prime, pgySize);
                 SECITEM_AllocItem(NULL, &pqg.base, pgySize);
                 pqg.prime.len = pqg.base.len = pgySize;
 
                 /* set q to the max allows */
-                SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS/ 8);
+                SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS / 8);
                 pqg.subPrime.len = DSA_MAX_Q_BITS / 8;
                 fputs(buf, dhresp);
                 continue;
             }
             fputs(buf, dhresp);
             continue;
         }
         if (buf[0] == 'P') {
@@ -3622,36 +3630,36 @@ dh_functional(char *reqfn, PRBool respon
             }
             from_hex_str(pubkeydata, pqg.prime.len, &buf[i]);
             pubkey.data = pubkeydata;
             pubkey.len = pqg.prime.len;
 
             /* generate FCC key pair, nist uses pqg rather then pg,
              * so use DSA to generate the key */
             if (DSA_NewKey(&pqg, &dsapriv) != SECSuccess) {
-                fprintf(stderr,"Failed to generate new key\n");
+                fprintf(stderr, "Failed to generate new key\n");
                 goto loser;
             }
             fputs("XephemIUT = ", dhresp);
             to_hex_str(buf, dsapriv->privateValue.data, dsapriv->privateValue.len);
             fputs(buf, dhresp);
             fputc('\n', dhresp);
             fputs("YephemIUT = ", dhresp);
             to_hex_str(buf, dsapriv->publicValue.data, dsapriv->publicValue.len);
             fputs(buf, dhresp);
             fputc('\n', dhresp);
             /* DH */
-            if (DH_Derive(&pubkey,&pqg.prime, &dsapriv->privateValue,
-                        &ZZ, pqg.prime.len) != SECSuccess) {
-                fprintf(stderr,"Derive failed\n");
+            if (DH_Derive(&pubkey, &pqg.prime, &dsapriv->privateValue,
+                          &ZZ, pqg.prime.len) != SECSuccess) {
+                fprintf(stderr, "Derive failed\n");
                 goto loser;
             }
             /* output hash of ZZ */
-            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess ) {
-                fprintf(stderr,"hash of derived key failed\n");
+            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) {
+                fprintf(stderr, "hash of derived key failed\n");
                 goto loser;
             }
             SECITEM_FreeItem(&ZZ, PR_FALSE);
             fputs("HashZZ = ", dhresp);
             to_hex_str(buf, hashBuf, fips_hashLen(hash));
             fputs(buf, dhresp);
             fputc('\n', dhresp);
             fputc('\n', dhresp);
@@ -3662,42 +3670,42 @@ dh_functional(char *reqfn, PRBool respon
     }
 loser:
     if (dsapriv != NULL) {
         PORT_FreeArena(dsapriv->params.arena, PR_TRUE);
     }
     fclose(dhreq);
 }
 
-#define MATCH_OPENSSL 1 
+#define MATCH_OPENSSL 1
 /*
  * Perform the DH Validity Test.
  *
  * reqfn is the pathname of the REQUEST file.
  *
  * The output RESPONSE file is written to stdout.
  */
 void
 dh_verify(char *reqfn, PRBool response)
 {
-    char buf[1024];   /* holds one line from the input REQUEST file.
+    char buf[1024]; /* holds one line from the input REQUEST file.
                          * needs to be large enough to hold the longest
                          * line "YephCAVS = <512 hex digits>\n".
                          */
-    FILE *dhreq;  /* input stream from the REQUEST file */
-    FILE *dhresp; /* output stream to the RESPONSE file */
+    FILE *dhreq;    /* input stream from the REQUEST file */
+    FILE *dhresp;   /* output stream to the RESPONSE file */
     unsigned char hashBuf[HASH_LENGTH_MAX];
     unsigned char cavsHashBuf[HASH_LENGTH_MAX];
     PQGParams pqg = { 0 };
-    unsigned char pubkeydata[DSA_MAX_P_BITS/8];
-    unsigned char privkeydata[DSA_MAX_P_BITS/8];
+    unsigned char pubkeydata[DSA_MAX_P_BITS / 8];
+    unsigned char privkeydata[DSA_MAX_P_BITS / 8];
     SECItem pubkey;
     SECItem privkey;
     SECItem ZZ;
-    unsigned int i,j;
+    unsigned int i, j;
     unsigned int pgySize;
     HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */
 
     dhreq = fopen(reqfn, "r");
     dhresp = stdout;
     while (fgets(buf, sizeof buf, dhreq) != NULL) {
         /* a comment or blank line */
         if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') {
@@ -3705,26 +3713,28 @@ dh_verify(char *reqfn, PRBool response)
             continue;
         }
         if (buf[0] == '[') {
             /* [Fx - SHAxxx] */
             if (buf[1] == 'F' && buf[3] == ' ') {
                 const char *src;
                 /* skip passed the colon */
                 src = &buf[1];
-                while (*src && *src != '-') src++;
+                while (*src && *src != '-')
+                    src++;
                 if (*src != '-') {
-                    fprintf(stderr, "No hash specified\n%s",buf);
+                    fprintf(stderr, "No hash specified\n%s", buf);
                     goto loser;
                 }
                 src++;
                 /* skip to the first non-space */
-                while (*src && *src == ' ') src++;
+                while (*src && *src == ' ')
+                    src++;
                 hash = hash_string_to_hashType(src);
-                if (hash == HASH_AlgNULL){
+                if (hash == HASH_AlgNULL) {
                     fprintf(dhresp, "ERROR: Unable to find SHAAlg type");
                     goto loser;
                 }
                 /* clear the PQG parameters */
                 if (pqg.prime.data) { /* P */
                     SECITEM_ZfreeItem(&pqg.prime, PR_FALSE);
                 }
                 if (pqg.subPrime.data) { /* Q */
@@ -3734,17 +3744,17 @@ dh_verify(char *reqfn, PRBool response)
                     SECITEM_ZfreeItem(&pqg.base, PR_FALSE);
                 }
                 pgySize = DSA_MAX_P_BITS / 8; /* change if more key sizes are supported in CAVS */
                 SECITEM_AllocItem(NULL, &pqg.prime, pgySize);
                 SECITEM_AllocItem(NULL, &pqg.base, pgySize);
                 pqg.prime.len = pqg.base.len = pgySize;
 
                 /* set q to the max allows */
-                SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS/ 8);
+                SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS / 8);
                 pqg.subPrime.len = DSA_MAX_Q_BITS / 8;
                 fputs(buf, dhresp);
                 continue;
             }
             fputs(buf, dhresp);
             continue;
         }
         if (buf[0] == 'P') {
@@ -3839,43 +3849,43 @@ dh_verify(char *reqfn, PRBool response)
         if (strncmp(buf, "CAVSHashZZ", 10) == 0) {
             fputs(buf, dhresp);
             i = 10;
             while (isspace(buf[i]) || buf[i] == '=') {
                 i++;
             }
             from_hex_str(cavsHashBuf, fips_hashLen(hash), &buf[i]);
             /* do the DH operation*/
-            if (DH_Derive(&pubkey,&pqg.prime, &privkey,
-                        &ZZ, pqg.prime.len) != SECSuccess) {
-                fprintf(stderr,"Derive failed\n");
+            if (DH_Derive(&pubkey, &pqg.prime, &privkey,
+                          &ZZ, pqg.prime.len) != SECSuccess) {
+                fprintf(stderr, "Derive failed\n");
                 goto loser;
             }
-            /* output  ZZ */
+/* output  ZZ */
 #ifndef MATCH_OPENSSL
             fputs("Z = ", dhresp);
             to_hex_str(buf, ZZ.data, ZZ.len);
             fputs(buf, dhresp);
             fputc('\n', dhresp);
 #endif
-            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess ) {
-                fprintf(stderr,"hash of derived key failed\n");
+            if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) {
+                fprintf(stderr, "hash of derived key failed\n");
                 goto loser;
             }
             SECITEM_FreeItem(&ZZ, PR_FALSE);
 #ifndef MATCH_NIST_
             fputs("IUTHashZZ = ", dhresp);
             to_hex_str(buf, hashBuf, fips_hashLen(hash));
             fputs(buf, dhresp);
             fputc('\n', dhresp);
 #endif
             if (memcmp(hashBuf, cavsHashBuf, fips_hashLen(hash)) != 0) {
-		fprintf(dhresp, "Result = F\n");
+                fprintf(dhresp, "Result = F\n");
             } else {
-		fprintf(dhresp, "Result = P\n");
+                fprintf(dhresp, "Result = P\n");
             }
 #ifndef MATCH_OPENSSL
             fputc('\n', dhresp);
 #endif
             continue;
         }
     }
 loser:
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/pk11wrap/pk11pars.c
+++ b/security/nss/lib/pk11wrap/pk11pars.c
@@ -397,17 +397,17 @@ static const optionFreeDef freeOptList[]
     { CIPHER_NAME("RSA-MIN"), NSS_RSA_MIN_KEY_SIZE },
     { CIPHER_NAME("DH-MIN"), NSS_DH_MIN_KEY_SIZE },
     { CIPHER_NAME("DSA-MIN"), NSS_DSA_MIN_KEY_SIZE },
     /* constraints on SSL Protocols */
     { CIPHER_NAME("TLS-VERSION-MIN"), NSS_TLS_VERSION_MIN_POLICY },
     { CIPHER_NAME("TLS-VERSION-MAX"), NSS_TLS_VERSION_MAX_POLICY },
     /* constraints on DTLS Protocols */
     { CIPHER_NAME("DTLS-VERSION-MIN"), NSS_DTLS_VERSION_MIN_POLICY },
-    { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MIN_POLICY }
+    { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MAX_POLICY }
 };
 
 static const policyFlagDef policyFlagList[] = {
     { CIPHER_NAME("SSL"), NSS_USE_ALG_IN_SSL },
     { CIPHER_NAME("SSL-KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX },
     /* add other key exhanges in the future */
     { CIPHER_NAME("KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX },
     { CIPHER_NAME("CERT-SIGNATURE"), NSS_USE_ALG_IN_CERT_SIGNATURE },
--- a/security/nss/mach
+++ b/security/nss/mach
@@ -242,17 +242,17 @@ def parse_arguments():
         help="Specify files or directories to run clang-format on",
         action=cfAction)
 
     parser_test = subparsers.add_parser(
         'tests', help='Run tests through tests/all.sh.')
     tests = [
         "cipher", "lowhash", "chains", "cert", "dbtests", "tools", "fips",
         "sdr", "crmf", "smime", "ssl", "ocsp", "merge", "pkits", "ec",
-        "gtests", "ssl_gtests", "bogo"
+        "gtests", "ssl_gtests", "bogo", "interop"
     ]
     parser_test.add_argument(
         'test', choices=tests, help="Available tests", action=testAction)
 
     parser_cov = subparsers.add_parser(
         'coverage', help='Generate coverage report')
     cov_modules = ["ssl_gtests"]
     parser_cov.add_argument(
--- a/security/nss/tests/interop/interop.sh
+++ b/security/nss/tests/interop/interop.sh
@@ -19,28 +19,36 @@ interop_init()
     cd ../common
     . ./init.sh
   fi
 
   mkdir -p "${HOSTDIR}/interop"
   cd "${HOSTDIR}/interop"
   INTEROP=${INTEROP:=tls_interop}
   if [ ! -d "$INTEROP" ]; then
-    git clone -q https://github.com/ttaubert/tls-interop "$INTEROP"
-    git -C "$INTEROP" checkout -q d07b28ac32b390dea1c9bcca5c56716247d23e5e
+    git clone -q https://github.com/jallmann/tls-interop "$INTEROP"
+    git -C "$INTEROP" checkout -q befbbde4e9e3ff78a6f90dc3170755bddcb1a2ef
   fi
   INTEROP=$(cd "$INTEROP";pwd -P)
 
   # We use the BoringSSL keyfiles
   BORING=${BORING:=boringssl}
   if [ ! -d "$BORING" ]; then
     git clone -q https://boringssl.googlesource.com/boringssl "$BORING"
-    git -C "$BORING" checkout -q ea80f9d5df4c302de391e999395e1c87f9c786b3
+    git -C "$BORING" checkout -q 3815720cf31339b1739f8ad1c21205105412c6b5
   fi
   BORING=$(cd "$BORING";pwd -P)
+  mkdir "$BORING/build"
+  cd "$BORING/build"
+  
+  # Build boring explicitly with gcc because it fails on builds where 
+  # CC=clang-5.0, for example asan-builds.
+  export CC=gcc
+  cmake ..
+  make
 
   SCRIPTNAME="interop.sh"
   html_head "interop test"
 }
 
 interop_cleanup()
 {
   html "</TABLE><BR>"
@@ -49,28 +57,29 @@ interop_cleanup()
 }
 
 # Function so we can easily add other stacks
 interop_run()
 {
   test_name=$1
   client=$2
   server=$3
+  client_writes_first=$4
 
   (cd "$INTEROP";
-   cargo run -- --client "$client" --server "$server" --rootdir "$BORING"/ssl/test/runner/ --test-cases cases.json) 2>interop-${test_name}.errors | tee interop-${test_name}.log
+   cargo run -- --client "$client" --server "$server" --rootdir "$BORING"/ssl/test/runner/ --test-cases cases.json $client_writes_first ) 2>interop-${test_name}.errors | tee interop-${test_name}.log
   RESULT=${PIPESTATUS[0]}
   html_msg "${RESULT}" 0 "Interop" "Run successfully"
   if [ $RESULT -ne 0 ]; then
     cat interop-${test_name}.errors
     cat interop-${test_name}.log
   fi
   grep -i 'FAILED\|Assertion failure' interop-${test_name}.errors
   html_msg $? 1 "Interop" "No failures"
 }
 
-cd "$(dirname "$0")"
-SOURCE_DIR="$PWD"/../..
 interop_init
 NSS_SHIM="$BINDIR"/nss_bogo_shim
 BORING_SHIM="$BORING"/build/ssl/test/bssl_shim
 interop_run "nss_nss" ${NSS_SHIM} ${NSS_SHIM}
+interop_run "bssl_nss" ${BORING_SHIM} ${NSS_SHIM}
+interop_run "nss_bssl" ${NSS_SHIM} ${BORING_SHIM} "--client-writes-first"
 interop_cleanup
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -78,17 +78,16 @@ pub mod style_structs {
 pub type ComputedValuesInner = ::gecko_bindings::structs::ServoComputedData;
 
 #[repr(C)]
 pub struct ComputedValues(::gecko_bindings::structs::mozilla::ComputedStyle);
 
 impl ComputedValues {
     pub fn new(
         device: &Device,
-        parent: Option<<&ComputedValues>,
         pseudo: Option<<&PseudoElement>,
         custom_properties: Option<Arc<CustomPropertiesMap>>,
         writing_mode: WritingMode,
         flags: ComputedValueFlags,
         rules: Option<StrongRuleNode>,
         visited_style: Option<Arc<ComputedValues>>,
         % for style_struct in data.style_structs:
         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
@@ -100,32 +99,31 @@ impl ComputedValues {
             flags,
             rules,
             visited_style,
             % for style_struct in data.style_structs:
             ${style_struct.ident},
             % endfor
         ).to_outer(
             device.pres_context(),
-            parent,
             pseudo.map(|p| p.pseudo_info())
         )
     }
 
     pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
         ComputedValuesInner::new(
             /* custom_properties = */ None,
             /* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious
             ComputedValueFlags::empty(),
             /* rules = */ None,
             /* visited_style = */ None,
             % for style_struct in data.style_structs:
             style_structs::${style_struct.name}::default(pres_context),
             % endfor
-        ).to_outer(pres_context, None, None)
+        ).to_outer(pres_context, None)
     }
 
     pub fn pseudo(&self) -> Option<PseudoElement> {
         let atom = (self.0).mPseudoTag.mRawPtr;
         if atom.is_null() {
             return None;
         }
 
@@ -190,17 +188,16 @@ impl Clone for ComputedValuesInner {
             flags: self.flags.clone(),
             rules: self.rules.clone(),
             visited_style: self.visited_style.clone(),
         }
     }
 }
 
 type PseudoInfo = (*mut structs::nsAtom, structs::CSSPseudoElementType);
-type ParentComputedStyleInfo<'a> = Option< &'a ComputedValues>;
 
 impl ComputedValuesInner {
     pub fn new(custom_properties: Option<Arc<CustomPropertiesMap>>,
                writing_mode: WritingMode,
                flags: ComputedValueFlags,
                rules: Option<StrongRuleNode>,
                visited_style: Option<Arc<ComputedValues>>,
                % for style_struct in data.style_structs:
@@ -217,40 +214,42 @@ impl ComputedValuesInner {
             ${style_struct.gecko_name}: Arc::into_raw_offset(${style_struct.ident}),
             % endfor
         }
     }
 
     fn to_outer(
         self,
         pres_context: RawGeckoPresContextBorrowed,
-        parent: ParentComputedStyleInfo,
         info: Option<PseudoInfo>
     ) -> Arc<ComputedValues> {
         let (tag, ty) = if let Some(info) = info {
             info
         } else {
             (ptr::null_mut(), structs::CSSPseudoElementType::NotPseudo)
         };
 
-        unsafe { self.to_outer_helper(pres_context, parent, ty, tag) }
+        unsafe { self.to_outer_helper(pres_context, ty, tag) }
     }
 
     unsafe fn to_outer_helper(
         self,
         pres_context: bindings::RawGeckoPresContextBorrowed,
-        parent: ParentComputedStyleInfo,
         pseudo_ty: structs::CSSPseudoElementType,
         pseudo_tag: *mut structs::nsAtom
     ) -> Arc<ComputedValues> {
         let arc = {
             let arc: Arc<ComputedValues> = Arc::new(uninitialized());
-            bindings::Gecko_ComputedStyle_Init(&arc.0 as *const _ as *mut _,
-                                                   parent, pres_context,
-                                                   &self, pseudo_ty, pseudo_tag);
+            bindings::Gecko_ComputedStyle_Init(
+                &arc.0 as *const _ as *mut _,
+                pres_context,
+                &self,
+                pseudo_ty,
+                pseudo_tag
+            );
             // We're simulating a move by having C++ do a memcpy and then forgetting
             // it on this end.
             forget(self);
             arc
         };
         arc
     }
 }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2715,17 +2715,16 @@ impl ComputedValues {
     }
 }
 
 #[cfg(feature = "servo")]
 impl ComputedValues {
     /// Create a new refcounted `ComputedValues`
     pub fn new(
         _: &Device,
-        _: Option<<&ComputedValues>,
         _: Option<<&PseudoElement>,
         custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
         writing_mode: WritingMode,
         flags: ComputedValueFlags,
         rules: Option<StrongRuleNode>,
         visited_style: Option<Arc<ComputedValues>>,
         % for style_struct in data.active_style_structs():
         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
@@ -3123,20 +3122,16 @@ pub struct StyleBuilder<'a> {
     /// The style we're inheriting from for properties that don't inherit from
     /// ::first-line.  This is the same as inherited_style, unless
     /// inherited_style is a ::first-line style.
     inherited_style_ignoring_first_line: &'a ComputedValues,
 
     /// The style we're getting reset structs from.
     reset_style: &'a ComputedValues,
 
-    /// The style we're inheriting from explicitly, or none if we're the root of
-    /// a subtree.
-    parent_style: Option<<&'a ComputedValues>,
-
     /// The rule node representing the ordered list of rules matched for this
     /// node.
     pub rules: Option<StrongRuleNode>,
 
     custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
 
     /// The pseudo-element this style will represent.
     pub pseudo: Option<<&'a PseudoElement>,
@@ -3194,17 +3189,16 @@ impl<'a> StyleBuilder<'a> {
 
         let mut flags = inherited_style.flags.inherited();
         if cascade_flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
             flags.insert(ComputedValueFlags::IS_STYLE_IF_VISITED);
         }
 
         StyleBuilder {
             device,
-            parent_style,
             inherited_style,
             inherited_style_ignoring_first_line,
             reset_style,
             pseudo,
             rules,
             modified_reset: false,
             custom_properties,
             writing_mode: inherited_style.writing_mode,
@@ -3238,17 +3232,16 @@ impl<'a> StyleBuilder<'a> {
     ) -> Self {
         let reset_style = device.default_computed_values();
         let inherited_style = parent_style.unwrap_or(reset_style);
         #[cfg(feature = "gecko")]
         debug_assert!(parent_style.is_none() ||
                       parent_style.unwrap().pseudo() != Some(PseudoElement::FirstLine));
         StyleBuilder {
             device,
-            parent_style,
             inherited_style,
             // None of our callers pass in ::first-line parent styles.
             inherited_style_ignoring_first_line: inherited_style,
             reset_style,
             pseudo: None,
             modified_reset: false,
             rules: None,
             custom_properties: style_to_derive_from.custom_properties().cloned(),
@@ -3480,17 +3473,16 @@ impl<'a> StyleBuilder<'a> {
     fn modified_reset(&self) -> bool {
         self.modified_reset
     }
 
     /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
     pub fn build(self) -> Arc<ComputedValues> {
         ComputedValues::new(
             self.device,
-            self.parent_style,
             self.pseudo,
             self.custom_properties,
             self.writing_mode,
             self.flags,
             self.rules,
             self.visited_style,
             % for style_struct in data.active_style_structs():
             self.${style_struct.ident}.build(),
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1170,17 +1170,16 @@ impl Stylist {
 
         let rule_hash_target = element.rule_hash_target();
 
         debug!(
             "Determining if style is shareable: pseudo: {}",
             pseudo_element.is_some()
         );
 
-        let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
         let matches_user_rules = rule_hash_target.matches_user_and_author_rules();
         let matches_author_rules =
             matches_user_rules && self.author_styles_enabled == AuthorStylesEnabled::Yes;
 
         // Normal user-agent rules.
         if let Some(map) = self.cascade_data
             .user_agent
             .cascade_data
@@ -1215,45 +1214,49 @@ impl Stylist {
                     context,
                     flags_setter,
                     CascadeLevel::UserNormal,
                     0,
                 );
             }
         }
 
-        if pseudo_element.is_none() && !only_default_rules {
+        if rule_inclusion == RuleInclusion::DefaultOnly {
+            return;
+        }
+
+        if pseudo_element.is_none() {
             // Presentational hints.
             //
             // These go before author rules, but after user rules, see:
             // https://drafts.csswg.org/css-cascade/#preshint
             let length_before_preshints = applicable_declarations.len();
             element.synthesize_presentational_hints_for_legacy_attributes(
                 context.visited_handling(),
                 applicable_declarations,
             );
-            if applicable_declarations.len() != length_before_preshints {
-                if cfg!(debug_assertions) {
+            if cfg!(debug_assertions) {
+                if applicable_declarations.len() != length_before_preshints {
                     for declaration in &applicable_declarations[length_before_preshints..] {
                         assert_eq!(declaration.level(), CascadeLevel::PresHints);
                     }
                 }
             }
         }
 
         let mut match_document_author_rules = matches_author_rules;
         let mut shadow_cascade_order = 0;
 
         // XBL / Shadow DOM rules, which are author rules too.
         //
         // TODO(emilio): Cascade order here is wrong for Shadow DOM. In
         // particular, normally document rules override ::slotted() rules, but
         // for !important it should be the other way around. So probably we need
         // to add some sort of AuthorScoped cascade level or something.
-        if matches_author_rules && !only_default_rules {
+        if matches_author_rules {
             if let Some(shadow) = rule_hash_target.shadow_root() {
                 if let Some(map) = shadow.style_data().host_rules(pseudo_element) {
                     context.with_shadow_host(Some(rule_hash_target), |context| {
                         map.get_all_matching_rules(
                             element,
                             rule_hash_target,
                             applicable_declarations,
                             context,
@@ -1310,20 +1313,18 @@ impl Stylist {
                     });
                     shadow_cascade_order += 1;
                 }
 
                 match_document_author_rules = false;
             }
         }
 
-        // FIXME(emilio): It looks very wrong to match XBL rules even for
-        // getDefaultComputedStyle!
-        //
-        // Also, this doesn't account for the author_styles_enabled stuff.
+        // FIXME(emilio): This doesn't account for the author_styles_enabled
+        // stuff...
         let cut_xbl_binding_inheritance =
             element.each_xbl_cascade_data(|cascade_data, quirks_mode| {
                 if let Some(map) = cascade_data.normal_rules(pseudo_element) {
                     // NOTE(emilio): This is needed because the XBL stylist may
                     // think it has a different quirks mode than the document.
                     let mut matching_context = MatchingContext::new(
                         context.matching_mode(),
                         context.bloom_filter,
@@ -1344,72 +1345,68 @@ impl Stylist {
                         CascadeLevel::SameTreeAuthorNormal,
                         shadow_cascade_order,
                     );
                 }
             });
 
         match_document_author_rules &= !cut_xbl_binding_inheritance;
 
-        if match_document_author_rules && !only_default_rules {
+        if match_document_author_rules {
             // Author normal rules.
             if let Some(map) = self.cascade_data.author.normal_rules(pseudo_element) {
                 map.get_all_matching_rules(
                     element,
                     rule_hash_target,
                     applicable_declarations,
                     context,
                     flags_setter,
                     CascadeLevel::SameTreeAuthorNormal,
                     shadow_cascade_order,
                 );
             }
         }
 
-        if !only_default_rules {
-            // Style attribute ("Normal override declarations").
-            if let Some(sa) = style_attribute {
-                applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
-                    sa.clone_arc(),
-                    CascadeLevel::StyleAttributeNormal,
-                ));
-            }
+        // Style attribute ("Normal override declarations").
+        if let Some(sa) = style_attribute {
+            applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
+                sa.clone_arc(),
+                CascadeLevel::StyleAttributeNormal,
+            ));
+        }
 
-            // Declarations from SVG SMIL animation elements.
-            if let Some(so) = smil_override {
-                applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
-                    so.clone_arc(),
-                    CascadeLevel::SMILOverride,
-                ));
-            }
+        // Declarations from SVG SMIL animation elements.
+        if let Some(so) = smil_override {
+            applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
+                so.clone_arc(),
+                CascadeLevel::SMILOverride,
+            ));
+        }
 
-            // The animations sheet (CSS animations, script-generated
-            // animations, and CSS transitions that are no longer tied to CSS
-            // markup).
-            if let Some(anim) = animation_rules.0 {
-                applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
-                    anim.clone(),
-                    CascadeLevel::Animations,
-                ));
-            }
+        // The animations sheet (CSS animations, script-generated
+        // animations, and CSS transitions that are no longer tied to CSS
+        // markup).
+        if let Some(anim) = animation_rules.0 {
+            applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
+                anim.clone(),
+                CascadeLevel::Animations,
+            ));
         }
 
         //
         // !important rules are handled during rule tree insertion.
         //
 
-        if !only_default_rules {
-            // The transitions sheet (CSS transitions that are tied to CSS
-            // markup).
-            if let Some(anim) = animation_rules.1 {
-                applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
-                    anim.clone(),
-                    CascadeLevel::Transitions,
-                ));
-            }
+        // The transitions sheet (CSS transitions that are tied to CSS
+        // markup).
+        if let Some(anim) = animation_rules.1 {
+            applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
+                anim.clone(),
+                CascadeLevel::Transitions,
+            ));
         }
     }
 
     /// Given an id, returns whether there might be any rules for that id in any
     /// of our rule maps.
     #[inline]
     pub fn may_have_rules_for_id<E>(&self, id: &WeakAtom, element: E) -> bool
     where
--- a/taskcluster/ci/test/misc.yml
+++ b/taskcluster/ci/test/misc.yml
@@ -28,17 +28,18 @@ geckoview-junit:
     treeherder-symbol: gv-junit
     instance-size: xlarge
     loopback-video: true
     e10s: true
     target: geckoview-androidTest.apk
     max-run-time: 3600
     chunks:
         by-test-platform:
-            android-em-4.3-arm7-api-16/debug: 3
+            android-em-4.3-arm7-api-16/debug: 4
+            android-em-4.3-arm7-api-16/opt: 2
             default: 1
     mozharness:
         script: android_emulator_unittest.py
         config:
             by-test-platform:
                 android-em-4.2-x86/opt:
                     - android/android_common.py
                     - android/androidx86.py
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AntiTrackingCommon.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/StaticPrefs.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindowInner.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrincipal.h"
+#include "nsIURI.h"
+#include "nsPIDOMWindow.h"
+#include "prtime.h"
+
+#define ANTITRACKING_PERM_KEY "3rdPartyStorage"
+
+using namespace mozilla;
+using mozilla::dom::ContentChild;
+
+namespace {
+
+bool
+GetParentPrincipalAndTrackingOrigin(nsPIDOMWindowInner* a3rdPartyTrackingWindow,
+                                    nsIPrincipal** aParentPrincipal,
+                                    nsACString& aTrackingOrigin)
+{
+#ifdef DEBUG
+  MOZ_ASSERT(nsContentUtils::IsThirdPartyWindowOrChannel(a3rdPartyTrackingWindow,
+                                                         nullptr, nullptr));
+  MOZ_ASSERT(nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow));
+#endif
+
+  nsGlobalWindowInner* innerWindow =
+    nsGlobalWindowInner::Cast(a3rdPartyTrackingWindow);
+
+  // Now we need the principal and the origin of the parent window.
+  nsCOMPtr<nsIPrincipal> parentPrincipal =
+    innerWindow->GetTopLevelStorageAreaPrincipal();
+  if (NS_WARN_IF(!parentPrincipal)) {
+    return false;
+  }
+
+  // Let's take the principal and the origin of the tracker.
+  nsIPrincipal* trackingPrincipal = innerWindow->GetPrincipal();
+  if (NS_WARN_IF(!trackingPrincipal)) {
+    return false;
+  }
+
+  nsresult rv = trackingPrincipal->GetOriginNoSuffix(aTrackingOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  parentPrincipal.forget(aParentPrincipal);
+  return true;
+};
+
+void
+CreatePermissionKey(const nsCString& aTrackingOrigin,
+                    const nsCString& aGrantedOrigin,
+                    nsACString& aPermissionKey)
+{
+  if (aTrackingOrigin == aGrantedOrigin) {
+    aPermissionKey = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s",
+                                     aTrackingOrigin.get());
+    return;
+  }
+
+  aPermissionKey = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s^%s",
+                                   aTrackingOrigin.get(),
+                                   aGrantedOrigin.get());
+}
+
+} // anonymous
+
+/* static */ void
+AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
+                                                         nsPIDOMWindowInner* a3rdPartyTrackingWindow)
+{
+  MOZ_ASSERT(a3rdPartyTrackingWindow);
+
+  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+    return;
+  }
+
+  nsCOMPtr<nsIPrincipal> parentPrincipal;
+  nsAutoCString trackingOrigin;
+  if (!GetParentPrincipalAndTrackingOrigin(a3rdPartyTrackingWindow,
+                                           getter_AddRefs(parentPrincipal),
+                                           trackingOrigin)) {
+    return;
+  }
+
+  NS_ConvertUTF16toUTF8 grantedOrigin(aOrigin);
+
+  if (XRE_IsParentProcess()) {
+    SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(parentPrincipal,
+                                                               trackingOrigin,
+                                                               grantedOrigin);
+    return;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+
+  // This is not really secure, because here we have the content process sending
+  // the request of storing a permission.
+  Unused << cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(parentPrincipal),
+                                                            trackingOrigin,
+                                                            grantedOrigin);
+}
+
+/* static */ void
+AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aParentPrincipal,
+                                                                               const nsCString& aTrackingOrigin,
+                                                                               const nsCString& aGrantedOrigin)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  if (NS_WARN_IF(!aParentPrincipal)) {
+    // The child process is sending something wrong. Let's ignore it.
+    return;
+  }
+
+  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
+  if (NS_WARN_IF(!pm)) {
+    return;
+  }
+
+  uint32_t expirationTime =
+    StaticPrefs::privacy_restrict3rdpartystorage_expiration();
+  int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + expirationTime;
+
+  nsAutoCString type;
+  CreatePermissionKey(aTrackingOrigin, aGrantedOrigin, type);
+
+  nsresult rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
+                                     nsIPermissionManager::ALLOW_ACTION,
+                                     nsIPermissionManager::EXPIRE_TIME, when);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+bool
+AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* a3rdPartyTrackingWindow,
+                                                        nsIURI* aURI)
+{
+  MOZ_ASSERT(a3rdPartyTrackingWindow);
+  MOZ_ASSERT(aURI);
+
+  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+    return true;
+  }
+
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(a3rdPartyTrackingWindow,
+                                                   nullptr, aURI) ||
+      !nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrincipal> parentPrincipal;
+  nsAutoCString trackingOrigin;
+  if (!GetParentPrincipalAndTrackingOrigin(a3rdPartyTrackingWindow,
+                                           getter_AddRefs(parentPrincipal),
+                                           trackingOrigin)) {
+    return false;
+  }
+
+  nsAutoString origin;
+  nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoCString type;
+  CreatePermissionKey(trackingOrigin, NS_ConvertUTF16toUTF8(origin), type);
+
+  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
+  if (NS_WARN_IF(!pm)) {
+    return false;
+  }
+
+  uint32_t result = 0;
+  rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return result == nsIPermissionManager::ALLOW_ACTION;
+}
+
+bool
+AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel,
+                                                        nsIURI* aURI)
+{
+  MOZ_ASSERT(aURI);
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aChannel->GetIsTrackingResource());
+
+#ifdef DEBUG
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+  bool is3rdPartyContext = false;
+  thirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &is3rdPartyContext);
+  MOZ_ASSERT(is3rdPartyContext);
+#endif
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (!loadInfo) {
+    return true;
+  }
+
+  nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal();
+  if (!parentPrincipal) {
+    parentPrincipal = loadInfo->LoadingPrincipal();
+    if (NS_WARN_IF(!parentPrincipal)) {
+      // Why we are here?!?
+      return true;
+    }
+  }
+
+  nsCOMPtr<nsIURI> trackingURI;
+  nsresult rv = aChannel->GetURI(getter_AddRefs(trackingURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  nsAutoString trackingOrigin;
+  rv = nsContentUtils::GetUTFOrigin(trackingURI, trackingOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoString origin;
+  rv = nsContentUtils::GetUTFOrigin(aURI, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoCString type;
+  CreatePermissionKey(NS_ConvertUTF16toUTF8(trackingOrigin),
+                      NS_ConvertUTF16toUTF8(origin), type);
+
+  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
+  if (NS_WARN_IF(!pm)) {
+    return false;
+  }
+
+  uint32_t result = 0;
+  rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return result == nsIPermissionManager::ALLOW_ACTION;
+}
+
+/* static */ bool
+AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aFirstPartyWindow,
+                                                             nsIURI* aURI)
+{
+  MOZ_ASSERT(aFirstPartyWindow);
+  MOZ_ASSERT(!nsContentUtils::IsTrackingResourceWindow(aFirstPartyWindow));
+  MOZ_ASSERT(aURI);
+
+  if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
+    return true;
+  }
+
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(aFirstPartyWindow,
+                                                   nullptr, aURI)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrincipal> parentPrincipal =
+    nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetPrincipal();
+  if (NS_WARN_IF(!parentPrincipal)) {
+    return false;
+  }
+
+  nsAutoString origin;
+  nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  NS_ConvertUTF16toUTF8 utf8Origin(origin);
+
+  nsAutoCString type;
+  CreatePermissionKey(utf8Origin, utf8Origin, type);
+
+  nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
+  if (NS_WARN_IF(!pm)) {
+    return false;
+  }
+
+  uint32_t result = 0;
+  rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return result == nsIPermissionManager::ALLOW_ACTION;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_antitrackingservice_h
+#define mozilla_antitrackingservice_h
+
+#include "nsString.h"
+
+class nsIHttpChannel;
+class nsIPrincipal;
+class nsIURI;
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class AntiTrackingCommon final
+{
+public:
+  // This method returns true if the URI has first party storage access when
+  // loaded inside the passed 3rd party context tracking resource window.
+  // If the window is first party context, please use
+  // MaybeIsFirstPartyStorageAccessGrantedFor();
+  static bool
+  IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* a3rdPartyTrackingWindow,
+                                      nsIURI* aURI);
+
+  // Note: you should use IsFirstPartyStorageAccessGrantedFor() passing the
+  // nsIHttpChannel! Use this method _only_ if the channel is not available.
+  // For first party window, it's impossible to know if the aURI is a tracking
+  // resource synchronously, so here we return the best guest: if we are sure
+  // that the permission is granted for the origin of aURI, this method returns
+  // true, otherwise false.
+  static bool
+  MaybeIsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aFirstPartyWindow,
+                                           nsIURI* aURI);
+
+  // This can be called only if the a3rdPartyTrackingChannel is _really_ a 3rd
+  // party context and marked as tracking resource.
+  // It returns true if the URI has access to the first party storage.
+  static bool
+  IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* a3rdPartyTrackingChannel,
+                                      nsIURI* aURI);
+
+  // Grant the permission for aOrigin to have access to the first party storage
+  // when loaded in that particular 3rd party context.
+  static void
+  AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
+                                       nsPIDOMWindowInner* a3rdPartyTrackingWindow);
+
+  // For IPC only.
+  static void
+  SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
+                                                             const nsCString& aParentOrigin,
+                                                             const nsCString& aGrantedOrigin);
+
+};
+
+} // namespace mozilla
+
+#endif // mozilla_antitrackingservice_h
--- a/toolkit/components/antitracking/moz.build
+++ b/toolkit/components/antitracking/moz.build
@@ -2,9 +2,21 @@
 # vim: set filetype=python:
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'DOM: Security')
 
+EXPORTS.mozilla = [
+    'AntiTrackingCommon.h',
+]
+
+UNIFIED_SOURCES += [
+    'AntiTrackingCommon.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -13,8 +13,10 @@ support-files =
 [browser_blockingCookies.js]
 support-files = server.sjs
 [browser_blockingIndexedDb.js]
 [browser_blockingStorage.js]
 [browser_blockingWorkers.js]
 [browser_blockingMessaging.js]
 [browser_imageCache.js]
 support-files = image.sjs
+[browser_subResources.js]
+support-files = subResources.sjs
--- a/toolkit/components/antitracking/test/browser/browser_imageCache.js
+++ b/toolkit/components/antitracking/test/browser/browser_imageCache.js
@@ -26,17 +26,22 @@ AntiTracking.runTest("Image cache - shou
     await new Promise(resolve => { img.onload = resolve; });
     ok(true, "Image 3 loaded");
 
     img = document.createElement("img");
     document.body.appendChild(img);
     img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs",
     await new Promise(resolve => { img.onload = resolve; });
     ok(true, "Image 4 loaded");
-  });
+  },
+  null, // cleanup function
+  null, // no extra prefs
+  false, // no window open test
+  false // no user-interaction test
+);
 
 // We still want to see just 2 requests.
 add_task(async _ => {
   await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs?result")
     .then(r => r.text())
     .then(text => {
       is(text, 2, "The image should be loaded correctly.");
     });
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_subResources.js
@@ -0,0 +1,168 @@
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+add_task(async function() {
+  info("Starting subResources test");
+
+  await SpecialPowers.flushPrefEnv();
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["privacy.restrict3rdpartystorage.enabled", true],
+    ["privacy.trackingprotection.enabled", false],
+    ["privacy.trackingprotection.pbmode.enabled", false],
+    ["privacy.trackingprotection.annotate_channels", true],
+  ]});
+
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  info("Creating a new tab");
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  info("Loading tracking scripts and tracking images");
+  await ContentTask.spawn(browser, null, async function() {
+    // Let's load the script twice here.
+    {
+      let src = content.document.createElement("script");
+      let p = new content.Promise(resolve => { src.onload = resolve; });
+      content.document.body.appendChild(src);
+      src.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=script";
+      await p;
+    }
+    {
+      let src = content.document.createElement("script");
+      let p = new content.Promise(resolve => { src.onload = resolve; });
+      content.document.body.appendChild(src);
+      src.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=script";
+      await p;
+    }
+
+    // Let's load an image twice here.
+    {
+      let img = content.document.createElement("img");
+      let p = new content.Promise(resolve => { img.onload = resolve; });
+      content.document.body.appendChild(img);
+      img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=image";
+      await p;
+    }
+    {
+      let img = content.document.createElement("img");
+      let p = new content.Promise(resolve => { img.onload = resolve; });
+      content.document.body.appendChild(img);
+      img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=image";
+      await p;
+    }
+  });
+
+  await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?result&what=image")
+    .then(r => r.text())
+    .then(text => {
+      is(text, 0, "Cookies received for images");
+    });
+
+  await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?result&what=script")
+    .then(r => r.text())
+    .then(text => {
+      is(text, 0, "Cookies received for scripts");
+    });
+
+  info("Creating a 3rd party content");
+  await ContentTask.spawn(browser,
+                          { page: TEST_3RD_PARTY_PAGE_WO,
+                            blockingCallback: (async _ => {}).toString(),
+                            nonBlockingCallback: (async _ => {}).toString(),
+                          },
+                          async function(obj) {
+    await new content.Promise(resolve => {
+      let ifr = content.document.createElement("iframe");
+      ifr.onload = function() {
+        info("Sending code to the 3rd party content");
+        ifr.contentWindow.postMessage(obj, "*");
+      };
+
+      content.addEventListener("message", function msg(event) {
+        if (event.data.type == "finish") {
+          content.removeEventListener("message", msg);
+          resolve();
+          return;
+        }
+
+        if (event.data.type == "ok") {
+          ok(event.data.what, event.data.msg);
+          return;
+        }
+
+        if (event.data.type == "info") {
+          info(event.data.msg);
+          return;
+        }
+
+        ok(false, "Unknown message");
+      });
+
+      content.document.body.appendChild(ifr);
+      ifr.src = obj.page;
+    });
+  });
+
+  info("Loading tracking scripts and tracking images again");
+  await ContentTask.spawn(browser, null, async function() {
+    // Let's load the script twice here.
+    {
+      let src = content.document.createElement("script");
+      let p = new content.Promise(resolve => { src.onload = resolve; });
+      content.document.body.appendChild(src);
+      src.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=script";
+      await p;
+    }
+    {
+      let src = content.document.createElement("script");
+      let p = new content.Promise(resolve => { src.onload = resolve; });
+      content.document.body.appendChild(src);
+      src.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=script";
+      await p;
+    }
+
+    // Let's load an image twice here.
+    {
+      let img = content.document.createElement("img");
+      let p = new content.Promise(resolve => { img.onload = resolve; });
+      content.document.body.appendChild(img);
+      img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=image";
+      await p;
+    }
+    {
+      let img = content.document.createElement("img");
+      let p = new content.Promise(resolve => { img.onload = resolve; });
+      content.document.body.appendChild(img);
+      img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?what=image";
+      await p;
+    }
+  });
+
+  await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?result&what=image")
+    .then(r => r.text())
+    .then(text => {
+      is(text, 1, "One cookie received for images.");
+    });
+
+  await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/subResources.sjs?result&what=script")
+    .then(r => r.text())
+    .then(text => {
+      is(text, 1, "One cookie received received for scripts.");
+    });
+
+  info("Removing the tab");
+  BrowserTestUtils.removeTab(tab);
+
+  UrlClassifierTestUtils.cleanupTestTrackers();
+});
+
+add_task(async function() {
+  info("Cleaning up.");
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+  });
+});
+
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -7,33 +7,37 @@ const TEST_TOP_PAGE = TEST_DOMAIN + TEST
 const TEST_POPUP_PAGE = TEST_DOMAIN + TEST_PATH + "popup.html";
 const TEST_3RD_PARTY_PAGE = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdParty.html";
 const TEST_3RD_PARTY_PAGE_WO = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyWO.html";
 const TEST_3RD_PARTY_PAGE_UI = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyUI.html";
 
 let {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
 
 this.AntiTracking = {
-  runTest(name, callbackTracking, callbackNonTracking, cleanupFunction, extraPrefs) {
+  runTest(name, callbackTracking, callbackNonTracking, cleanupFunction, extraPrefs, windowOpenTest = true, userInteractionTest = true) {
     // Here we want to test that a 3rd party context is simply blocked.
     this._createTask(name, true, callbackTracking, extraPrefs);
     this._createCleanupTask(cleanupFunction);
 
     if (callbackNonTracking) {
       // Here we want to test that a 3rd party context is not blocked if pref is off.
       this._createTask(name, false, callbackNonTracking);
       this._createCleanupTask(cleanupFunction);
 
       // Permission granted when there is a window.open()
-      this._createWindowOpenTask(name, callbackTracking, callbackNonTracking, extraPrefs);
-      this._createCleanupTask(cleanupFunction);
+      if (windowOpenTest) {
+        this._createWindowOpenTask(name, callbackTracking, callbackNonTracking, extraPrefs);
+        this._createCleanupTask(cleanupFunction);
+      }
 
       // Permission granted when there is user-interaction.
-      this._createUserInteractionTask(name, callbackTracking, callbackNonTracking, extraPrefs);
-      this._createCleanupTask(cleanupFunction);
+      if (userInteractionTest) {
+        this._createUserInteractionTask(name, callbackTracking, callbackNonTracking, extraPrefs);
+        this._createCleanupTask(cleanupFunction);
+      }
     }
   },
 
   async _setupTest(blocking, extraPrefs) {
     await SpecialPowers.flushPrefEnv();
     await SpecialPowers.pushPrefEnv({"set": [
       ["privacy.restrict3rdpartystorage.enabled", blocking],
       ["privacy.trackingprotection.enabled", false],
--- a/toolkit/components/antitracking/test/browser/image.sjs
+++ b/toolkit/components/antitracking/test/browser/image.sjs
@@ -8,13 +8,13 @@ function handleRequest(aRequest, aRespon
 
   if (aRequest.queryString.includes("result")) {
     aResponse.write(getState("hints") || 0);
     setState("hints", "0");
   } else {
     let hints = parseInt(getState("hints") || 0) + 1;
     setState("hints", hints.toString());
 
-    aResponse.setHeader("Cache-Control", "max-age=10000", false);
+    aResponse.setHeader("Set-Cookie", "foopy=1");
     aResponse.setHeader("Content-Type", "image/png", false);
     aResponse.write(IMAGE);
  }
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/subResources.sjs
@@ -0,0 +1,31 @@
+// A 1x1 PNG image.
+// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+                   "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+  let key = aRequest.queryString.includes("what=script") ? "script" : "image";
+
+  if (aRequest.queryString.includes("result")) {
+    aResponse.write(getState(key) || 0);
+    setState(key, "0");
+    return;
+  }
+
+  if (aRequest.hasHeader('Cookie')) {
+    let hints = parseInt(getState(key) || 0) + 1;
+    setState(key, hints.toString());
+  }
+
+  aResponse.setHeader("Set-Cookie", "foopy=1");
+
+  if (key == "script") {
+    aResponse.setHeader("Content-Type", "text/javascript", false);
+    aResponse.write("42;");
+  } else {
+    aResponse.setHeader("Content-Type", "image/png", false);
+    aResponse.write(IMAGE);
+  }
+}
--- a/toolkit/components/remotebrowserutils/RemoteWebNavigation.js
+++ b/toolkit/components/remotebrowserutils/RemoteWebNavigation.js
@@ -75,19 +75,20 @@ RemoteWebNavigation.prototype = {
     // We know the url is going to be loaded, let's start requesting network
     // connection before the content process asks.
     // Note that we might have already setup the speculative connection in some
     // cases, especially when the url is from location bar or its popup menu.
     if (aURI.startsWith("http:") || aURI.startsWith("https:")) {
       try {
         let uri = makeURI(aURI);
         let principal = aTriggeringPrincipal;
-        // We usually have a aTriggeringPrincipal assigned, but in case we don't
-        // have one, create it with OA inferred from the current context.
-        if (!principal) {
+        // We usually have a aTriggeringPrincipal assigned, but in case we
+        // don't have one or if it's a SystemPrincipal, let's create it with OA
+        // inferred from the current context.
+        if (!principal || principal.isSystemPrincipal) {
           let attrs = {
             userContextId: this._browser.getAttribute("usercontextid") || 0,
             privateBrowsingId: PrivateBrowsingUtils.isBrowserPrivate(this._browser) ? 1 : 0
           };
           principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);
         }
         Services.io.speculativeConnect2(uri, principal, null);
       } catch (ex) {
--- a/toolkit/content/components.css
+++ b/toolkit/content/components.css
@@ -6,10 +6,9 @@
    ==
    == THESE STYLES HAVE BEEN MOVED TO "widgets.css".
    ==
    == This file exists to allow debugging regressions more easily using
    == just artifact builds, and will be removed in bug 1470873.
    ==
    ======================================================================= */
 
-@import url("chrome://global/skin/menu.css");
 
--- a/toolkit/content/preferencesBindings.js
+++ b/toolkit/content/preferencesBindings.js
@@ -434,17 +434,16 @@ const Preferences = window.Preferences =
     }
 
     isElementEditable(aElement) {
       switch (aElement.localName) {
       case "checkbox":
       case "colorpicker":
       case "radiogroup":
       case "textbox":
-      case "listbox":
       case "menulist":
         return true;
       }
       return false;
     }
 
     updateElements() {
       if (!this.id)
--- a/toolkit/content/widgets.css
+++ b/toolkit/content/widgets.css
@@ -7,14 +7,15 @@
    ======================================================================= */
 
 @import url("chrome://global/content/autocomplete.css");
 @import url("chrome://global/skin/autocomplete.css");
 @import url("chrome://formautofill-shared/skin/autocomplete-item.css");
 @import url("chrome://formautofill/skin/autocomplete-item.css");
 @import url("chrome://global/skin/dropmarker.css");
 @import url("chrome://global/skin/groupbox.css");
+@import url("chrome://global/skin/menu.css");
 @import url("chrome://global/skin/menulist.css");
 @import url("chrome://global/skin/notification.css");
 @import url("chrome://global/skin/richlistbox.css");
 @import url("chrome://global/skin/splitter.css");
 @import url("chrome://global/skin/toolbar.css");
 @import url("chrome://global/skin/wizard.css");
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -103,17 +103,17 @@ xul|groupbox {
   margin: 0;
   padding: 0;
 }
 
 xul|groupbox > xul|caption {
   padding: 4px 0;
 }
 
-xul|groupbox xul|label:not(.menu-accel):not(.menu-text):not(.menulist-label):not(.indent):not(.learnMore):not(.tail-with-learn-more),
+xul|groupbox xul|label:not(.menu-accel):not(.menu-iconic-accel):not(.menu-text):not(.menu-iconic-text):not(.menulist-label):not(.indent):not(.learnMore):not(.tail-with-learn-more),
 xul|groupbox xul|description {
   /* !important needed to override toolkit !important rule */
   margin-inline-start: 0 !important;
   margin-inline-end: 0 !important;
 }
 
 /* tabpanels and tabs */
 
@@ -222,25 +222,20 @@ xul|menulist[open="true"]:not([disabled=
   background-color: var(--in-content-box-background-active);
 }
 
 html|button:disabled,
 html|select:disabled,
 html|*.numberbox-input:disabled::-moz-number-spin-box,
 xul|button[disabled="true"],
 xul|colorpicker[type="button"][disabled="true"],
-xul|menulist[disabled="true"],
-xul|listbox[disabled="true"] {
+xul|menulist[disabled="true"] {
   opacity: 0.5;
 }
 
-xul|listbox[disabled="true"] xul|listitem:hover {
-  background-color: transparent;
-}
-
 *|button.primary {
   background-color: var(--in-content-primary-button-background);
   border-color: transparent;
   color: var(--in-content-selected-text);
 }
 
 html|button.primary:enabled:hover,
 xul|button.primary:not([disabled="true"]):hover {
@@ -744,44 +739,40 @@ xul|*.radio-label-box {
   line-height: 1.3em;
   margin: 0;
   -moz-user-select: none;
 }
 
 /* List boxes */
 
 html|select[size][multiple],
-xul|richlistbox,
-xul|listbox {
+xul|richlistbox {
   -moz-appearance: none;
   margin-inline-start: 0;
   background-color: var(--in-content-box-background);
   border: 1px solid var(--in-content-box-border-color);
   border-radius: 2px;
   color: var(--in-content-text-color);
 }
 
 html|select[size][multiple] > html|option,
-xul|treechildren::-moz-tree-row,
-xul|listbox xul|listitem {
+xul|treechildren::-moz-tree-row {
   padding: 0.3em;
   margin: 0;
   border: none;
   border-radius: 0;
   background-image: none;
 }
 
 html|select[size][multiple] > html|option:hover,
-xul|treechildren::-moz-tree-row(hover),