Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Sat, 14 Jul 2018 01:22:25 +0300
changeset 426589 703da935b1e0
parent 426588 91cd5bee61ef (current diff)
parent 426577 b79457b703d9 (diff)
child 426590 b3eb58b5214a
push id34276
push userncsoregi@mozilla.com
push dateSat, 14 Jul 2018 09:41:08 +0000
treeherdermozilla-central@04dd259d71db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- 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),
-xul|listbox xul|listitem:hover {
+xul|treechildren::-moz-tree-row(hover) {
   background-color: var(--in-content-item-hover);
 }
 
-xul|treechildren::-moz-tree-row(selected),
-xul|listbox xul|listitem[selected="true"] {
+xul|treechildren::-moz-tree-row(selected) {
   background-color: var(--in-content-item-selected);
   color: var(--in-content-selected-text);
 }
 
 /* Trees */
 
 xul|tree {
   -moz-appearance: none;
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -49,18 +49,17 @@ html|input[type="checkbox"]:-moz-focusri
 *|button.primary:focus {
   outline: 1px dotted;
   outline-offset: -3px;
 }
 
 /* Use a 2px border so that selected row highlight is still visible behind
     an existing high-contrast border that uses the background color */
 @media (-moz-windows-default-theme: 0) {
-  xul|treechildren::-moz-tree-row(selected),
-  xul|listbox xul|listitem[selected="true"] {
+  xul|treechildren::-moz-tree-row(selected) {
      border: 2px dotted Highlight;
   }
 }
 
 html|button {
   /* XUL button min-width */
   min-width: 6.3em;
 }
--- a/toolkit/themes/windows/global/menu.css
+++ b/toolkit/themes/windows/global/menu.css
@@ -85,18 +85,18 @@ menucaption > .menu-iconic-text {
   min-width: 1.45em;
 }
 
 .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
-menu.menu-iconic > .menu-iconic-left,
-menuitem.menuitem-iconic > .menu-iconic-left {
+.menu-iconic > .menu-iconic-left,
+.menuitem-iconic > .menu-iconic-left {
   -moz-appearance: menuimage;
   padding-top: 2px;
 }
 
 /* ..... menu arrow box ..... */
 
 .menu-right {
   -moz-appearance: menuarrow;
--- a/widget/LSBUtils.cpp
+++ b/widget/LSBUtils.cpp
@@ -3,16 +3,17 @@
  * 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 "LSBUtils.h"
 
 #include <unistd.h>
 #include "base/process_util.h"
+#include "mozilla/FileUtils.h"
 
 namespace mozilla {
 namespace widget {
 namespace lsb {
 
 static const char* gLsbReleasePath = "/usr/bin/lsb_release";
 
 bool
@@ -42,37 +43,33 @@ GetLSBRelease(nsACString& aDistributor,
   bool ok = base::LaunchApp(argv, options, &process);
   close(pipefd[1]);
   if (!ok) {
     NS_WARNING("Failed to spawn lsb_release!");
     close(pipefd[0]);
     return false;
   }
 
-  FILE* stream = fdopen(pipefd[0], "r");
+  ScopedCloseFile stream(fdopen(pipefd[0], "r"));
   if (!stream) {
     NS_WARNING("Could not wrap fd!");
     close(pipefd[0]);
     return false;
   }
 
   char dist[256], desc[256], release[256], codename[256];
   if (fscanf(stream, "Distributor ID:\t%255[^\n]\n"
                      "Description:\t%255[^\n]\n"
                      "Release:\t%255[^\n]\n"
                      "Codename:\t%255[^\n]\n",
              dist, desc, release, codename) != 4)
   {
     NS_WARNING("Failed to parse lsb_release!");
-    fclose(stream);
-    close(pipefd[0]);
     return false;
   }
-  fclose(stream);
-  close(pipefd[0]);
 
   aDistributor.Assign(dist);
   aDescription.Assign(desc);
   aRelease.Assign(release);
   aCodename.Assign(codename);
   return true;
 }