merge mozilla-central to autoland. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Fri, 08 Sep 2017 00:05:35 +0200
changeset 429072 e5e14cc4b4a8c12399b728610fa52e5bb7888cbb
parent 429071 5e167360b1f93acf1962e782a1f9e8a9ea7819f3 (current diff)
parent 429056 b4c1ad9565ee9d00d96501c4a83083daf25c1413 (diff)
child 429073 2e5bb7e3c040c34b75aec7958dfc9d74d3d287ff
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.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. r=merge a=merge
browser/base/content/browser.js
browser/base/content/test/tabs/browser_accessibility_indicator.js
browser/themes/shared/icons/accessibility-active.svg
browser/themes/shared/icons/accessibility.svg
browser/themes/shared/icons/private-browsing.svg
testing/web-platform/meta/2dcontext/imagebitmap/createImageBitmap-drawImage.html.ini
testing/web-platform/meta/app-uri/appURI_test.html.ini
testing/web-platform/meta/css/css3-color/t424-hsl-parsing-f.xht.ini
testing/web-platform/meta/css/css3-color/t425-hsla-parsing-f.xht.ini
testing/web-platform/meta/fetch/api/abort/general.html.ini
testing/web-platform/meta/fonts/variations/variable-box-font.html.ini
testing/web-platform/meta/fonts/variations/variable-gpos-m2b.html.ini
testing/web-platform/meta/fonts/variations/variable-gsub.html.ini
testing/web-platform/meta/html/browsers/browsing-the-web/scroll-to-fragid/scroll-frag-percent-encoded.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-003.html.ini
testing/web-platform/meta/html/user-interaction/focus/tabindex-focus-flag.html.ini
testing/web-platform/meta/image-decodes/image-decode-iframe.html.ini
testing/web-platform/meta/image-decodes/image-decode-path-changes.html.ini
testing/web-platform/meta/image-decodes/image-decode.html.ini
testing/web-platform/meta/payment-request/payment-request-id.https.html.ini
testing/web-platform/meta/service-workers/service-worker/register-link-element.https.html.ini
testing/web-platform/meta/service-workers/service-worker/registration.https.html.ini
testing/web-platform/meta/webdriver/tests/cookies/cookies.py.ini
testing/web-platform/meta/webdriver/tests/state/get_element_rect.py.ini
testing/web-platform/meta/webmessaging/Channel_postMessage_ports_readonly_array.htm.ini
testing/web-platform/meta/workers/postMessage_ports_readonly_array.htm.ini
testing/web-platform/tests/app-uri/OWNERS
testing/web-platform/tests/app-uri/README.md
testing/web-platform/tests/app-uri/appURI_test.html
testing/web-platform/tests/app-uri/resources/ExamPLE/mmY/index.html
testing/web-platform/tests/app-uri/resources/ExamPLE/mmY/sth.txt
testing/web-platform/tests/app-uri/resources/ExamPLE/{mY}/z...z/index.html
testing/web-platform/tests/app-uri/resources/ImaGes/{{a}}/Test_1/$a/sth34!.png
testing/web-platform/tests/app-uri/resources/ImaGes/~t/{!a}/corrupted_file.png
testing/web-platform/tests/app-uri/resources/ImaGes/~t/{!a}/~sth.png
testing/web-platform/tests/app-uri/resources/icons/w3c-128.png
testing/web-platform/tests/app-uri/resources/icons/w3c-16.png
testing/web-platform/tests/app-uri/resources/icons/w3c-48.png
testing/web-platform/tests/css-values/unset-value-storage.html
testing/web-platform/tests/css/css-tables-3/visibility-collapse-col-004.html
testing/web-platform/tests/css/css-ui-3/nav-dir-001.html
testing/web-platform/tests/css/css-ui-3/nav-dir-002.html
testing/web-platform/tests/css/css-ui-3/nav-dir-003.html
testing/web-platform/tests/css/css-ui-3/nav-dir-004.html
testing/web-platform/tests/css/css-ui-3/nav-dir-005.html
testing/web-platform/tests/css/css-ui-3/nav-dir-missing-1.html
testing/web-platform/tests/css/css-ui-3/nav-dir-missing-2.html
testing/web-platform/tests/css/css-ui-3/nav-dir-missing-3.html
testing/web-platform/tests/css/css-ui-3/nav-dir-missing-4.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-001.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-002.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-003.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-004.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-005.html
testing/web-platform/tests/css/css-ui-3/nav-dir-target-006.html
testing/web-platform/tests/css/css-ui-3/nav-down-000.html
testing/web-platform/tests/css/css-ui-3/nav-down-001.html
testing/web-platform/tests/css/css-ui-3/nav-down-002.html
testing/web-platform/tests/css/css-ui-3/nav-down-003.html
testing/web-platform/tests/css/css-ui-3/nav-down-004.html
testing/web-platform/tests/css/css-ui-3/nav-down-005.html
testing/web-platform/tests/css/css-ui-3/nav-down-006.html
testing/web-platform/tests/css/css-ui-3/nav-down-007.html
testing/web-platform/tests/css/css-ui-3/nav-down-008.html
testing/web-platform/tests/css/css-ui-3/nav-down-009.html
testing/web-platform/tests/css/css-ui-3/nav-down-010.html
testing/web-platform/tests/css/css-ui-3/nav-down-011.html
testing/web-platform/tests/css/css-ui-3/nav-down-012.html
testing/web-platform/tests/css/css-ui-3/nav-down-013.html
testing/web-platform/tests/css/css-ui-3/nav-down-014.html
testing/web-platform/tests/css/css-ui-3/nav-down-015.html
testing/web-platform/tests/css/css-ui-3/nav-down-016.html
testing/web-platform/tests/css/css-ui-3/nav-left-000.html
testing/web-platform/tests/css/css-ui-3/nav-left-001.html
testing/web-platform/tests/css/css-ui-3/nav-left-002.html
testing/web-platform/tests/css/css-ui-3/nav-left-003.html
testing/web-platform/tests/css/css-ui-3/nav-left-004.html
testing/web-platform/tests/css/css-ui-3/nav-left-005.html
testing/web-platform/tests/css/css-ui-3/nav-left-006.html
testing/web-platform/tests/css/css-ui-3/nav-left-007.html
testing/web-platform/tests/css/css-ui-3/nav-left-008.html
testing/web-platform/tests/css/css-ui-3/nav-left-009.html
testing/web-platform/tests/css/css-ui-3/nav-left-010.html
testing/web-platform/tests/css/css-ui-3/nav-left-011.html
testing/web-platform/tests/css/css-ui-3/nav-left-012.html
testing/web-platform/tests/css/css-ui-3/nav-left-013.html
testing/web-platform/tests/css/css-ui-3/nav-left-014.html
testing/web-platform/tests/css/css-ui-3/nav-left-015.html
testing/web-platform/tests/css/css-ui-3/nav-left-016.html
testing/web-platform/tests/css/css-ui-3/nav-right-000.html
testing/web-platform/tests/css/css-ui-3/nav-right-001.html
testing/web-platform/tests/css/css-ui-3/nav-right-002.html
testing/web-platform/tests/css/css-ui-3/nav-right-003.html
testing/web-platform/tests/css/css-ui-3/nav-right-004.html
testing/web-platform/tests/css/css-ui-3/nav-right-005.html
testing/web-platform/tests/css/css-ui-3/nav-right-006.html
testing/web-platform/tests/css/css-ui-3/nav-right-007.html
testing/web-platform/tests/css/css-ui-3/nav-right-008.html
testing/web-platform/tests/css/css-ui-3/nav-right-009.html
testing/web-platform/tests/css/css-ui-3/nav-right-010.html
testing/web-platform/tests/css/css-ui-3/nav-right-011.html
testing/web-platform/tests/css/css-ui-3/nav-right-012.html
testing/web-platform/tests/css/css-ui-3/nav-right-013.html
testing/web-platform/tests/css/css-ui-3/nav-right-014.html
testing/web-platform/tests/css/css-ui-3/nav-right-015.html
testing/web-platform/tests/css/css-ui-3/nav-right-016.html
testing/web-platform/tests/css/css-ui-3/nav-up-000.html
testing/web-platform/tests/css/css-ui-3/nav-up-001.html
testing/web-platform/tests/css/css-ui-3/nav-up-002.html
testing/web-platform/tests/css/css-ui-3/nav-up-003.html
testing/web-platform/tests/css/css-ui-3/nav-up-004.html
testing/web-platform/tests/css/css-ui-3/nav-up-005.html
testing/web-platform/tests/css/css-ui-3/nav-up-006.html
testing/web-platform/tests/css/css-ui-3/nav-up-007.html
testing/web-platform/tests/css/css-ui-3/nav-up-008.html
testing/web-platform/tests/css/css-ui-3/nav-up-009.html
testing/web-platform/tests/css/css-ui-3/nav-up-010.html
testing/web-platform/tests/css/css-ui-3/nav-up-011.html
testing/web-platform/tests/css/css-ui-3/nav-up-012.html
testing/web-platform/tests/css/css-ui-3/nav-up-013.html
testing/web-platform/tests/css/css-ui-3/nav-up-014.html
testing/web-platform/tests/css/css-ui-3/nav-up-015.html
testing/web-platform/tests/css/css-ui-3/nav-up-016.html
testing/web-platform/tests/css/css-ui-3/support/nav-dir-target-001-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-dir-target-002-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-dir-target-003-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-dir-target-004-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-dir-target-005-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-down-009-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-down-010-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-down-011-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-down-012-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-down-013-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-left-009-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-left-010-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-left-011-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-left-012-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-left-013-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-right-009-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-right-010-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-right-011-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-right-012-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-right-013-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-up-009-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-up-010-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-up-011-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-up-012-frame.html
testing/web-platform/tests/css/css-ui-3/support/nav-up-013-frame.html
testing/web-platform/tests/css/css-values-3/attr-invalid-type-003.html
testing/web-platform/tests/fetch/api/abort/general.html
testing/web-platform/tests/fetch/api/abort/general.js
testing/web-platform/tests/fonts/variations/resources/variabletest_box.ttf
testing/web-platform/tests/fonts/variations/variable-box-font-ref.html
testing/web-platform/tests/fonts/variations/variable-box-font.html
testing/web-platform/tests/fonts/variations/variable-gpos-m2b-ref.html
testing/web-platform/tests/fonts/variations/variable-gpos-m2b.html
testing/web-platform/tests/fonts/variations/variable-gsub-ref.html
testing/web-platform/tests/fonts/variations/variable-gsub.html
testing/web-platform/tests/fullscreen/api/element-ready-check-enabled-flag-not-set-manual.html
testing/web-platform/tests/html/user-interaction/focus/tabindex-focus-flag.html
testing/web-platform/tests/image-decodes/image-decode-iframe.html
testing/web-platform/tests/image-decodes/image-decode-path-changes.html
testing/web-platform/tests/image-decodes/image-decode.html
testing/web-platform/tests/payment-request/payment-request-id.https.html
testing/web-platform/tests/service-workers/service-worker/register-link-element.https.html
testing/web-platform/tests/service-workers/service-worker/registration.https.html
testing/web-platform/tests/viewport/OWNERS
testing/web-platform/tests/webdriver/tests/state/get_element_rect.py
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -667,20 +667,16 @@ pref("network.protocol-handler.expose.ne
 pref("network.protocol-handler.expose.snews", false);
 pref("network.protocol-handler.expose.nntp", false);
 
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.flashBar", 1);
 
-// Accessibility indicator preferences such as support URL, enabled flag.
-pref("accessibility.support.url", "https://support.mozilla.org/%LOCALE%/kb/accessibility-services");
-pref("accessibility.indicator.enabled", true);
-
 // Tracks when accessibility is loaded into the previous session.
 pref("accessibility.loadedInLastSession", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 // Should plugins that are hidden show the infobar UI?
 pref("plugins.show_infobar", false);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -283,18 +283,17 @@ toolbarpaletteitem {
 %else
 /* On non-OSX, these should be start-aligned */
 #titlebar-buttonbox-container {
   -moz-box-align: start;
 }
 %endif
 
 %if !defined(MOZ_WIDGET_GTK)
-#TabsToolbar > .private-browsing-indicator,
-#TabsToolbar > .accessibility-indicator {
+#TabsToolbar > .private-browsing-indicator {
   -moz-box-ordinal-group: 1000;
 }
 %endif
 
 %ifdef XP_WIN
 #main-window[sizemode="maximized"] #titlebar-buttonbox {
   -moz-appearance: -moz-window-button-box-maximized;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1357,17 +1357,16 @@ var gBrowserInit = {
       gURLBar.setAttribute("enablehistory", "false");
     }
 
     // Misc. inits.
     TabletModeUpdater.init();
     CombinedStopReload.init();
     gPrivateBrowsingUI.init();
     BrowserPageActions.init();
-    gAccessibilityServiceIndicator.init();
 
     if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
         window.matchMedia("(-moz-windows-default-theme)").matches) {
       let windowFrameColor = new Color(...Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
                                             .Windows8WindowFrameColor.get());
       // Default to black for foreground text.
       if (!windowFrameColor.isContrastRatioAcceptable(new Color(0, 0, 0))) {
         document.documentElement.setAttribute("darkwindowframe", "true");
@@ -1844,18 +1843,16 @@ var gBrowserInit = {
     CompactTheme.uninit();
 
     TrackingProtection.uninit();
 
     CaptivePortalWatcher.uninit();
 
     SidebarUI.uninit();
 
-    gAccessibilityServiceIndicator.uninit();
-
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features)
         Win7Features.onCloseWindow();
 
@@ -8026,73 +8023,16 @@ function getTabModalPromptBox(aWindow) {
   return null;
 }
 
 /* DEPRECATED */
 function getBrowser() {
   return gBrowser;
 }
 
-const gAccessibilityServiceIndicator = {
-  init() {
-    // Pref to enable accessibility service indicator.
-    gPrefService.addObserver("accessibility.indicator.enabled", this);
-    // Accessibility service init/shutdown event.
-    Services.obs.addObserver(this, "a11y-init-or-shutdown");
-    this.update(Services.appinfo.accessibilityEnabled);
-  },
-
-  update(accessibilityEnabled = false) {
-    if (this.enabled && accessibilityEnabled) {
-      this._active = true;
-      document.documentElement.setAttribute("accessibilitymode", "true");
-      [...document.querySelectorAll(".accessibility-indicator")].forEach(
-        indicator => ["click", "keypress"].forEach(type =>
-          indicator.addEventListener(type, this)));
-      TabsInTitlebar.updateAppearance(true);
-    } else if (this._active) {
-      this._active = false;
-      document.documentElement.removeAttribute("accessibilitymode");
-      [...document.querySelectorAll(".accessibility-indicator")].forEach(
-        indicator => ["click", "keypress"].forEach(type =>
-          indicator.removeEventListener(type, this)));
-      TabsInTitlebar.updateAppearance(true);
-    }
-  },
-
-  observe(subject, topic, data) {
-    if (topic == "nsPref:changed" && data === "accessibility.indicator.enabled") {
-      this.update(Services.appinfo.accessibilityEnabled);
-    } else if (topic === "a11y-init-or-shutdown") {
-      // When "a11y-init-or-shutdown" event is fired, "1" indicates that
-      // accessibility service is started and "0" that it is shut down.
-      this.update(data === "1");
-    }
-  },
-
-  get enabled() {
-    return gPrefService.getBoolPref("accessibility.indicator.enabled");
-  },
-
-  handleEvent({ key, type }) {
-    if ((type === "keypress" && [" ", "Enter"].includes(key)) ||
-         type === "click") {
-      let a11yServicesSupportURL =
-        Services.urlFormatter.formatURLPref("accessibility.support.url");
-      gBrowser.selectedTab = gBrowser.addTab(a11yServicesSupportURL);
-    }
-  },
-
-  uninit() {
-    gPrefService.removeObserver("accessibility.indicator.enabled", this);
-    Services.obs.removeObserver(this, "a11y-init-or-shutdown");
-    this.update();
-  }
-};
-
 var gPrivateBrowsingUI = {
   init: function PBUI_init() {
     // Do nothing for normal windows
     if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
       return;
     }
 
     // Disable the Clear Recent History... menu item when in PB mode
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -586,30 +586,30 @@
   <box id="appMenu-viewCache" hidden="true"/>
 
 #ifdef CAN_DRAW_IN_TITLEBAR
 <vbox id="titlebar">
   <hbox id="titlebar-content">
     <spacer id="titlebar-spacer" flex="1"/>
     <hbox id="titlebar-buttonbox-container">
 #ifdef XP_WIN
-      <button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;" aria-live="polite"/>
-      <hbox class="private-browsing-indicator"/>
+      <hbox id="private-browsing-indicator-titlebar">
+        <hbox class="private-browsing-indicator"/>
+      </hbox>
 #endif
       <hbox id="titlebar-buttonbox">
         <toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
       </hbox>
     </hbox>
 #ifdef XP_MACOSX
     <!-- OS X does not natively support RTL for its titlebar items, so we prevent this secondary
          buttonbox from reversing order in RTL by forcing an LTR direction. -->
     <hbox id="titlebar-secondary-buttonbox" dir="ltr">
-      <button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;" aria-live="polite"/>
       <hbox class="private-browsing-indicator"/>
       <hbox id="titlebar-fullscreen-button"/>
     </hbox>
 #endif
   </hbox>
 </vbox>
 #endif
 
@@ -646,22 +646,18 @@
              customizable="true"
              mode="icons"
              iconsize="small"
              aria-label="&tabsToolbar.label;"
              context="toolbar-context-menu"
              collapsed="true">
 
 #if defined(MOZ_WIDGET_GTK)
-      <hbox class="private-browsing-indicator"
+      <hbox id="private-browsing-indicator"
             skipintoolbarset="true"/>
-      <button class="accessibility-indicator"
-              tooltiptext="&accessibilityIndicator.tooltip;"
-              aria-live="polite"
-              skipintoolbarset="true"/>
 #endif
 
       <tabs id="tabbrowser-tabs"
             class="tabbrowser-tabs"
             tabbrowser="content"
             flex="1"
             setfocus="false"
             tooltip="tabbrowser-tab-tooltip"
@@ -699,18 +695,16 @@
                 label="&newUserContext.label;">
             <menupopup id="alltabs_containersMenuTab" />
           </menu>
           <menuseparator id="alltabs-popup-separator-2"/>
         </menupopup>
       </toolbarbutton>
 
 #if !defined(MOZ_WIDGET_GTK)
-      <button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;"
-              aria-live="polite" skipintoolbarset="true"/>
       <hbox class="private-browsing-indicator" skipintoolbarset="true"/>
 #endif
 #ifdef CAN_DRAW_IN_TITLEBAR
       <hbox class="titlebar-placeholder" type="caption-buttons"
             id="titlebar-placeholder-on-TabsToolbar-for-captions-buttons" persist="width"
 #ifndef XP_MACOSX
             ordinal="1000"
 #endif
--- a/browser/base/content/test/alerts/browser.ini
+++ b/browser/base/content/test/alerts/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   head.js
   file_dom_notifications.html
 
 [browser_notification_close.js]
 skip-if = os == 'win' # Bug 1227785
 [browser_notification_do_not_disturb.js]
+skip-if = (os == 'win' && bits == 32) # Bug 1352791
 [browser_notification_open_settings.js]
 [browser_notification_remove_permission.js]
 [browser_notification_replace.js]
 [browser_notification_tab_switching.js]
--- a/browser/base/content/test/tabcrashed/browser.ini
+++ b/browser/base/content/test/tabcrashed/browser.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 skip-if = (!e10s || !crashreporter)
 support-files =
   head.js
 
 [browser_autoSubmitRequest.js]
 [browser_clearEmail.js]
 [browser_noPermanentKey.js]
-skip-if = (os == "linux" && bits == 32 && debug) # Bug 1383315
+skip-if = (os == "linux" && debug) # Bug 1383315
 [browser_showForm.js]
 [browser_shown.js]
 [browser_withoutDump.js]
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -1,15 +1,14 @@
 [DEFAULT]
 support-files =
   dummy_page.html
   test_bug1358314.html
 
 [browser_abandonment_telemetry.js]
-[browser_accessibility_indicator.js]
 [browser_allow_process_switches_despite_related_browser.js]
 [browser_contextmenu_openlink_after_tabnavigated.js]
 [browser_isLocalAboutURI.js]
 [browser_tabCloseProbes.js]
 [browser_tabSpinnerProbe.js]
 skip-if = !e10s # Tab spinner is e10s only.
 [browser_tabSwitchPrintPreview.js]
 skip-if = os == 'mac'
deleted file mode 100644
--- a/browser/base/content/test/tabs/browser_accessibility_indicator.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-const A11Y_INDICATOR_ENABLED_PREF = "accessibility.indicator.enabled";
-
-/**
- * Test various pref and UI properties based on whether the accessibility
- * indicator is enabled and the accessibility service is initialized.
- * @param  {Object}  win      browser window to check the indicator in.
- * @param  {Boolean} enabled  pref flag for accessibility indicator.
- * @param  {Boolean} active   whether accessibility service is started or not.
- */
-function testIndicatorState(win, enabled, active) {
-  is(Services.prefs.getBoolPref(A11Y_INDICATOR_ENABLED_PREF), enabled,
-    `Indicator is ${enabled ? "enabled" : "disabled"}.`);
-  is(Services.appinfo.accessibilityEnabled, active,
-    `Accessibility service is ${active ? "enabled" : "disabled"}.`);
-
-  let visible = enabled && active;
-  is(win.document.documentElement.hasAttribute("accessibilitymode"), visible,
-    `accessibilitymode flag is ${visible ? "set" : "unset"}.`);
-
-  // Browser UI has 2 indicators in markup for OSX and Windows but only 1 is
-  // shown depending on whether the titlebar is enabled.
-  let expectedVisibleCount = visible ? 1 : 0;
-  let visibleCount = 0;
-  [...win.document.querySelectorAll(".accessibility-indicator")].forEach(indicator =>
-    win.getComputedStyle(indicator).getPropertyValue("display") !== "none" &&
-    visibleCount++);
-  is(expectedVisibleCount, visibleCount,
-    `Indicator is ${visible ? "visible" : "invisible"}.`);
-}
-
-/**
- * Instantiate accessibility service and wait for event associated with its
- * startup, if necessary.
- */
-async function initAccessibilityService() {
-  let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
-    Ci.nsIAccessibilityService);
-
-  if (!Services.appinfo.accessibilityEnabled) {
-    await new Promise(resolve => {
-      let observe = (subject, topic, data) => {
-        Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
-        // "1" indicates that the accessibility service is initialized.
-        data === "1" && resolve();
-      };
-      Services.obs.addObserver(observe, "a11y-init-or-shutdown");
-    })
-  }
-
-  return accService;
-}
-
-/**
- * If accessibility service is not yet disabled, wait for the event associated
- * with its shutdown.
- */
-async function shutdownAccessibilityService() {
-  if (Services.appinfo.accessibilityEnabled) {
-    await new Promise(resolve => {
-      let observe = (subject, topic, data) => {
-        Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
-        // "1" indicates that the accessibility service is shutdown.
-        data === "0" && resolve();
-      };
-      Services.obs.addObserver(observe, "a11y-init-or-shutdown");
-    })
-  }
-}
-
-/**
- * Force garbage collection.
- */
-function forceGC() {
-  SpecialPowers.gc();
-  SpecialPowers.forceShrinkingGC();
-  SpecialPowers.forceCC();
-}
-
-add_task(async function test_accessibility_indicator() {
-  info("Test default accessibility indicator state.");
-  let newWin = await BrowserTestUtils.openNewBrowserWindow();
-  testIndicatorState(window, true, false);
-  testIndicatorState(newWin, true, false);
-
-  info("Enable accessibility and ensure the indicator is shown in all windows.");
-  let accService = await initAccessibilityService(); // eslint-disable-line no-unused-vars
-  testIndicatorState(window, true, true);
-  testIndicatorState(newWin, true, true);
-
-  info("Open a new window and ensure the indicator is shown there by default.");
-  let dynamicWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-  testIndicatorState(dynamicWin, true, true);
-  await BrowserTestUtils.closeWindow(dynamicWin);
-
-  info("Preff off accessibility indicator.");
-  Services.prefs.setBoolPref(A11Y_INDICATOR_ENABLED_PREF, false);
-  testIndicatorState(window, false, true);
-  testIndicatorState(newWin, false, true);
-  dynamicWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-  testIndicatorState(dynamicWin, false, true);
-
-  info("Preff on accessibility indicator.");
-  Services.prefs.setBoolPref(A11Y_INDICATOR_ENABLED_PREF, true);
-  testIndicatorState(window, true, true);
-  testIndicatorState(newWin, true, true);
-  testIndicatorState(dynamicWin, true, true);
-
-  info("Disable accessibility and ensure the indicator is hidden in all windows.");
-  accService = undefined;
-  forceGC();
-  await shutdownAccessibilityService();
-  testIndicatorState(window, true, false);
-  testIndicatorState(newWin, true, false);
-  testIndicatorState(dynamicWin, true, false);
-
-  Services.prefs.clearUserPref(A11Y_INDICATOR_ENABLED_PREF);
-  await BrowserTestUtils.closeWindow(newWin);
-  await BrowserTestUtils.closeWindow(dynamicWin);
-});
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -1642,17 +1642,18 @@ var gPrivacyPane = {
       case 0: // access allowed
         checkbox.checked = false;
         break;
     }
   },
 
   _initA11yString() {
     let a11yLearnMoreLink =
-      Services.urlFormatter.formatURLPref("accessibility.support.url");
+      Services.urlFormatter.formatURLPref("app.support.baseURL") +
+      "accessibility";
     document.getElementById("a11yLearnMoreLink")
       .setAttribute("href", a11yLearnMoreLink);
   },
 
   updateA11yPrefs(checked) {
     Services.prefs.setIntPref("accessibility.force_disabled", checked ? 1 : 0);
   }
 };
--- a/browser/components/resistfingerprinting/test/browser/browser_navigator.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_navigator.js
@@ -8,17 +8,17 @@ const { classes: Cc, Constructor: CC, in
 const TEST_PATH = "http://example.net/browser/browser/" +
                   "components/resistfingerprinting/test/browser/"
 
 var spoofedUserAgent;
 
 const SPOOFED_APPNAME        = "Netscape";
 const SPOOFED_APPVERSION     = "5.0 (Windows)";
 const SPOOFED_PLATFORM       = "Win64";
-const SPOOFED_OSCPU          = "Windows NT 6.1";
+const SPOOFED_OSCPU          = "Windows NT 6.1; Win64; x64";
 const SPOOFED_BUILDID        = "20100101";
 const SPOOFED_HW_CONCURRENCY = 2;
 
 const CONST_APPCODENAME = "Mozilla";
 const CONST_PRODUCT     = "Gecko";
 const CONST_PRODUCTSUB  = "20100101";
 const CONST_VENDOR      = "";
 const CONST_VENDORSUB   = "";
@@ -87,17 +87,17 @@ async function testWorkerNavigator() {
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set":
     [["privacy.resistFingerprinting", true]]
   });
 
   let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
   let appVersion = parseInt(appInfo.version);
   let spoofedVersion = appVersion - ((appVersion - 3) % 7);
-  spoofedUserAgent = `Mozilla/5.0 (Windows NT 6.1; rv:${spoofedVersion}.0) Gecko/20100101 Firefox/${spoofedVersion}.0`;
+  spoofedUserAgent = `Mozilla/5.0 (${SPOOFED_OSCPU}; rv:${spoofedVersion}.0) Gecko/20100101 Firefox/${spoofedVersion}.0`;
 });
 
 add_task(async function runNavigatorTest() {
   await testNavigator();
 });
 
 add_task(async function runWorkerNavigatorTest() {
   await testWorkerNavigator();
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -976,14 +976,8 @@ you can use these alternative items. Oth
 <!ENTITY pageActionButton.tooltip "Page actions">
 <!ENTITY pageAction.addToUrlbar.label "Add to Address Bar">
 <!ENTITY pageAction.removeFromUrlbar.label "Remove from Address Bar">
 
 <!ENTITY pageAction.sendTabToDevice.label "Send Tab to Device">
 <!ENTITY sendToDevice.syncNotReady.label "Syncing Devices…">
 
 <!ENTITY libraryButton.tooltip "View history, saved bookmarks, and more">
-
-<!-- LOCALIZATION NOTE: (accessibilityIndicator.tooltip): This is used to
-     display a tooltip for accessibility indicator in toolbar/tabbar. It is also
-     used as a textual label for the indicator used by assistive technology
-     users. -->
-<!ENTITY accessibilityIndicator.tooltip "Accessibility Features Enabled">
--- a/browser/modules/WindowsJumpLists.jsm
+++ b/browser/modules/WindowsJumpLists.jsm
@@ -511,28 +511,28 @@ this.WinTaskbarJumpList =
 
   /**
    * Notification handlers
    */
 
   notify: function WTBJL_notify(aTimer) {
     // Add idle observer on the first notification so it doesn't hit startup.
     this._updateIdleObserver();
-    this.update();
+    Services.tm.idleDispatchToMainThread(() => { this.update(); });
   },
 
   observe: function WTBJL_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "nsPref:changed":
         if (this._enabled == true && !_prefs.getBoolPref(PREF_TASKBAR_ENABLED))
           this._deleteActiveJumpList();
         this._refreshPrefs();
         this._updateTimer();
         this._updateIdleObserver();
-        this.update();
+        Services.tm.idleDispatchToMainThread(() => { this.update(); });
       break;
 
       case "profile-before-change":
         this._shutdown();
       break;
 
       case "browser:purge-session-history":
         this.update();
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -720,16 +720,22 @@ html|span.ac-emphasize-text-url {
 }
 
 /* Customization mode */
 
 %include ../shared/customizableui/customizeMode.inc.css
 
 /* End customization mode */
 
+
+#main-window[privatebrowsingmode=temporary] #private-browsing-indicator {
+  background: url("chrome://browser/skin/privatebrowsing-mask.png") center no-repeat;
+  width: 40px;
+}
+
 %include ../shared/UITour.inc.css
 
 #UITourHighlight {
   /* Below are some fixes for people without an X compositor on Linux.
      This is why we can't have nice things: */
   /* Animations don't repaint properly without an X compositor. */
   animation-name: none !important;
   /* Opacity rounds to 0 or 1 on Linux without an X compositor, making the
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -17,16 +17,17 @@ browser.jar:
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
+  skin/classic/browser/privatebrowsing-mask.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
 * skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9eaf3aec7e71bd4d7f3e3d91b47fa3717a17a947
GIT binary patch
literal 1355
zc$@)C1+@B!P)<h;3K|Lk000e1NJLTq000^Q000;W1^@s6<bv(R000FNNkl<Zc-rlj
zTTGK@7{{4{4#uo&DJ{nyILX06!KoAmmV#165Tu17P?*ReD!2i22WQxbY|5M*i+BQ5
zP$^K<Q3rycPDG{`o7ogWG+WH>+)Q@0E&lhs&DR^3-7d?ri(lUFd;b6D`R9G}w)q?Y
z{3HAagBC9FTtF;jYgu581A}{aCSD`l2_dnBaD2hzO1Lo(=XK`hG2K4fk*(~-WwWeI
z&zX6wK`aQDr%Ja~=lfSr6bJ74m3Wrpx2t!VC@z@v#l#|!K;f&G?`SIuc>f90nKxBh
zR3+qx$+%D+AtFyc%V0rnh5ql%<oi|L$&{5HGsw#?6a~EZtbTnL21;&V@`FdjJdR{r
zK)!joaCL-$9}-~ntGZ*-_hI7Q8K&F3_1aEQ?^BvFpp$LyDDbcT?oI#Q^K26=$b*E*
zi`7#&th3-$-UzN$&*FN;V~iEdVOT$dVciU_md>L!-HdE``8B<4`%qKbSzO=w2qW1K
zFq}1mvBEigMLiQ$574z`7)SJju<o8lj=bVFW!TP9lrAZ@c4S*IUOI=dqFG$tdLO4U
zM{#)Ld7RE1$E7Xb!>YfBk=*+@Z5Ts`&Wg*%2e_1V&!%<jCT!l5)HAx3LbuK0O35rb
z4VRE1Eg7K<F$;1}@hv#lvdN6TtSRi*bRkdv*6$gT;@edJGE-Xm4ZZ)-lx)U@&3A2F
z)UowjG;907H1gbKUh4Up_3qbn!mPiEwhRk4`W6n_kIpUGC%3yXtp{6V<<He#x>FLj
z6$Yv+iBKZaPZ+&bCoTQKq_v=T?KI92>{%}@`^jIZ+D4uz&a3owi_+7KPByD)(-~-e
z^3Jdf7PORv<~r?B99Y+dB=79AR10Gw;mYBw5NRsQm9^+uJBgm8Nw8<CRg)$mUL{^3
z1Vo6&JFENCqz-(t{x}l6^d@!-9hf0XysmWD>RJpKZ=y!iO4~Kwit$X_5#k=N@)U#z
z3HhO6UIJxSrlk0Jr+NZi>Z>pYy$v5hWR8fxJWR}21O~guMYEn1ALEUh#8&j>-#}$d
z9bOk}cd`r?<mAkggr<5MpPn|1Vjy=K#|#%xsoID9;9bzmOQ4sPAUCiadsZ}~ebq37
zo?DO?RAJZ2TVd-tntdJ?n$eRpjx?WwUtE{U6)b}Vy+pW2i#N3IO>D$T?Kn<oMsYUd
zI{NdbFjzPZi}4oBhDn%K4WU_i(S~W|5UVkbd6?JM(?Pvm$ycyf(+HI~<v6?5EQ19x
zoSk{n@FY*&T#dQ~Z826H3?0CMATtgGno+;34-FB6wt9VRH$IE)fk`!lLkbJin1?|<
z{b-~5#|dp%>#2L}!jna^*(`&F*mouH;)kb`;pdu!R<y6Nq9M|P&lUY>UU?o3nqE4Z
zIwW|c&n9?eJgkha!{Nj)&=@s{gUkDINNKV4e3W2fA<ra&sBG?XEU*XJaU{e(d}WSW
zyy?fvxP924d;-lWy<j&o(IfM>2%);2JSxW}UT>^)UuW5&`Vb9iXVA31m%JUQimwCp
zK9L9_^T_8(IB~PNAP!Ctx$vaX3SoS6v`Bk1PLwjI5@~PA1S`r&3m}$qf11G|Zn3qo
zq78S+^N>7K;led7t~_}RX<}j#H}kJSYH_N4XFS4}2qhxeP_Fr9?wA*FzZ-APqhu@B
z!{cT+{B2OYD8&Xh%!wQ9Oe`iAvib`iN6zEItxjCs;YI%cX8899{RvW$k=wCC>_q?o
N002ovPDHLkV1hb=mlFU0
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -48,39 +48,31 @@
 
 #main-window {
   -moz-appearance: none;
   background-color: #eeeeee;
 }
 
 /** Begin titlebar **/
 
-#titlebar-content {
-  /* Ensure the the content part of the titlebar does not shrink. */
-  min-height: inherit;
-}
-
 #titlebar-buttonbox > .titlebar-button {
   display: none;
 }
 
 /* NB: these would be margin-inline-start/end if it wasn't for the fact that OS X
  * doesn't reverse the order of the items in the titlebar in RTL mode. */
 .titlebar-placeholder[type="caption-buttons"],
 #titlebar-buttonbox {
   margin-left: 7px;
 }
 
-.titlebar-placeholder[type="fullscreen-button"] {
-  margin-right: 4px;
-}
-
+.titlebar-placeholder[type="fullscreen-button"],
 #titlebar-secondary-buttonbox {
-  align-items: center;
-  display: flex;
+  margin-right: 7px;
+  margin-left: 7px;
 }
 
 #main-window:not(:-moz-lwtheme) > #titlebar {
   -moz-appearance: -moz-window-titlebar;
 }
 
 #main-window:not([tabsintitlebar]) > #titlebar {
   height: 22px; /* The native titlebar on OS X is 22px tall. */
@@ -1227,32 +1219,77 @@ html|*.addon-webext-perm-list {
 }
 
 /* Customization mode */
 
 %include ../shared/customizableui/customizeMode.inc.css
 
 /* End customization mode */
 
-/* Private browsing and accessibility indicators */
+.private-browsing-indicator {
+  background-image: url("chrome://browser/skin/privatebrowsing-mask.png");
+  background-repeat: no-repeat;
+  background-size: 100% auto;
+  width: 38px;
+  height: 28px;
+  /**
+   * The private browsing mask graphic has a 3px flare at the top. The distance
+   * we want between the mask and items on either side is 7px, so we use 4px,
+   * since the other 3px is accounted for by the empty space on either side.
+   */
+  margin-left: 4px;
+  margin-right: 4px;
+}
 
-:root[accessibilitymode][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .accessibility-indicator,
-:root[privatebrowsingmode=temporary][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .private-browsing-indicator,
-:root[accessibilitymode]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .accessibility-indicator,
-:root[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
+#titlebar-secondary-buttonbox > .private-browsing-indicator {
+  position: relative;
+}
+
+#main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
+  background-image: url("chrome://browser/skin/privatebrowsing-mask-short.png");
+  height: 20px;
+}
+
+#main-window:not([privatebrowsingmode=temporary]) .private-browsing-indicator,
+#main-window[privatebrowsingmode=temporary][inFullscreen] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator,
+#main-window[privatebrowsingmode=temporary]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .private-browsing-indicator {
   display: none;
 }
 
-#TabsToolbar > .private-browsing-indicator:-moz-locale-dir(rtl),
-#TabsToolbar > .accessibility-indicator:-moz-locale-dir(rtl) {
+@media (min-resolution: 2dppx) {
+  .private-browsing-indicator {
+    background-image: url("chrome://browser/skin/privatebrowsing-mask@2x.png");
+  }
+  #main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
+    background-image: url("chrome://browser/skin/privatebrowsing-mask-short@2x.png");
+  }
+}
+
+#TabsToolbar > .private-browsing-indicator {
+  /* We offset by 38px for mask graphic, plus 4px to account for the
+   * margin-left, which sums to 42px.
+   */
+  margin-right: -42px;
+}
+
+#main-window[privatebrowsingmode=temporary] .titlebar-placeholder[type="fullscreen-button"],
+#main-window[privatebrowsingmode=temporary] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
+  margin-left: 0px;
+}
+
+#main-window[privatebrowsingmode=temporary][inFullscreen] .titlebar-placeholder[type="fullscreen-button"] {
+  /* Override display:none for .titlebar-placeholder in fullscreen so we can have consistent
+     position and padding for the private browsing indicator. */
+  display: -moz-box;
+}
+
+#TabsToolbar > .private-browsing-indicator:-moz-locale-dir(rtl) {
   -moz-box-ordinal-group: 0;
 }
 
-/* End private browsing and accessibility indicators */
-
 %include ../shared/UITour.inc.css
 
 #UITourTooltipDescription {
   font-size: 1.18rem;
   line-height: 2rem;
 }
 
 #UITourTooltipClose {
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -22,16 +22,20 @@ browser.jar:
   skin/classic/browser/panel-expander-closed.png
   skin/classic/browser/panel-expander-closed@2x.png
   skin/classic/browser/panel-expander-open.png
   skin/classic/browser/panel-expander-open@2x.png
   skin/classic/browser/panel-plus-sign.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/page-livemarks@2x.png
   skin/classic/browser/pageInfo.css
+  skin/classic/browser/privatebrowsing-mask.png
+  skin/classic/browser/privatebrowsing-mask@2x.png
+  skin/classic/browser/privatebrowsing-mask-short.png
+  skin/classic/browser/privatebrowsing-mask-short@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/toolbarbutton-dropmarker@2x.png
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
 * skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..92f60e29f9fd71ed62101b7f7f22ea37423387a0
GIT binary patch
literal 1074
zc$@(;1kL-2P)<h;3K|Lk000e1NJLTq001Tc000yS1^@s6;g3%U000B|Nkl<Zc-qB}
z-%pcw7{xVNvLAoIl4%444X~-(j2g@wIAl{|7I(FnESb1nnPFTI#snP{iYY%Tr4)+3
zv;u9BQY}zdX$!=}mSPLCAVtJ>RX~^&5MjMEIr~0u;<9Y-7q12vAKq`z^E~GqHq3?R
zv&}MRd(&~pOIy}39Nz`j1zXlV=#O{5q%MDiBH0@Nyv<0_pYAjZ2VJln9$FWKG$)#K
zY)0xrov0Y`Mrdd{Q+=QV<}du~0%eB=r;bn;>h!AEMn;TszXjJa?n+`h<b@&K&G3j{
z-N8OIq;^C5=`A#-^@_UjP(SIU(Tv`I-?Xr=iOqTbluaF|%fE|ls@T^C<7fSn2&p}=
z7y3|>ZbidaHr!Hp5pvI9{^2s*x)Ic-b&CIWk7^KeV@u-9&UUoNx}eUm!cja(9$bIQ
zrVf(D6nk3Hoa&ZDonphda|#QOqF8wNC*}exc=p>1%-&nV+~_jAEdktN1tWoF%nmQn
zjCRD`g^4J}U6bU&^`~rZv5UJ6(0_7UlBzux^f!%S+PjG9fkiyMyND;A2z^Cu#{Zn4
zx2R3B*^KWw?TT7|^C)?6{VAJUtn|ZX2r0diRPHvTx7LsF?Fe+)o%ngL8o9~UD9gNp
z`yFBY-t$c4erFhpV|tu@zZ&QEso8Fac+*!mL>^p!%H|d;-l>CjuS1f`kIcA!p$`-G
z8MstFl%zJ|mmLD~l53#(_8P`*Perufw<9;HTAaxa2<TQ0&<(roB@eDYWpj&NNEV>^
z*e*%suIs2gYCzz|B*qv)=PUwUPcUo_V#E^0Nb6&am>y%)%Cz>NI19OEF=m|-Z>o;A
zkO$YFvbn_y617n6>XJmc(~82x%jhb(gCTtozAF>(H$K9kj={cuLLiu~6MxeVn+I`I
z?x9=q;QCWGw^;r*4U`{PIov>5k`ehk)Nm+#@M<2wqYA)N6Tm?I1GMIJqb<(`Z=I;a
z&7d7;xu0(6mOQxrl+7)cyR{Y-Nfs{UNo^?Fu18Ly5_KoqVJq_BrpyER5do)T%aI+c
zK=s!~*h{_QtUk-ab}!Kl-LC3Q+5BR$Dk!$Mt;)r1CKSi(@l#wa&csyU$CxUd+gcA<
z!d0eUMSh%ybdkI`HSN;PYkeu3TkOo1O2`uoC`&M{2HP>BB)$c*_$HB3_AX-@@k%dd
z(3^H&?`HsIOBEwFor;x5&&LRm#kHU$&afs>7G<7_kw>Wmb@@-R!puvKAKp_Se{&rQ
zwlpv<|IajtS(I7yWzDa=VsE{eU-`V?aFy%i=ECU7ch6%jpe)K<imYS+cvGqv@eU)M
s@eSj|8bMj<l>N^x^oPgBwSbrZ7l%*`5<vo2aR2}S07*qoM6N<$f`P3K_W%F@
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ec1cf7452ef6e0251dad9113e8119793bfed4b6f
GIT binary patch
literal 2639
zc$@)G3b6HwP)<h;3K|Lk000e1NJLTq002w?001Zm1^@s6RPr2W000UWNkl<ZcwX(8
zTZ~=RdB=Zit-a4aGjq;d&Da=h<BoA+8*rQe=3Xa&(;-Q3N<kF*)HbP<K2Qk>rB&3b
zt=g9m<t33?<*ltm)CwXfq=h&!7+<*9#DMVyk0<tI2JnpMdgfgA-fMle_7ZbC@@V9<
z<)?A}qfbXOXRX=m`{}!_ttQ82{^Iz)q4giUHhkyIx$3>8u+wyf-L#nwj1EwWt4bnZ
zT}sW)Gm}hHNoH-=`u2TW@7s8A%e@2Vl!!b61VHfC^T#&6@o&eTC`nIOcd>`a6ww$I
zR|hpga9A3)&UAf>ikhC;`6pYyf7|bF8wOG!4S;8XE@0!uFPvES-IxFI@ZZLf1m&=s
zP=zRUl>ieVk%YNqn)8jZ^ufRR{L>ei{0cY$3<EO(kN^|FBrtOOAKbj_?f*KtH$Gi|
zz=kOXjmB^_$YHUh#I{cFXG?o`{^2bj0(*h;fCC5?t%q^ojhhF5Ys;J8A9^6QDS-)5
z$khQ%gw&=qY=hee2Mz#lcqTZ!2rhvPliR<%`M`nioy3a8Ig8f%>Hy|!=5y*&c0Bai
z{lH`s*3!TPaGlqb&N=6BF2yN9<f@4<@3V81dfP`lEMysA3z`$>5G#lhgw}uw9<ovj
zg}UUs)TJb@K_W3uELv$oRit1dj9T24omgTQXLZH$>DmIJjtF$H<o_3OD5YEO?&8Gz
z>|DkGFD?g|bK>~_)`}7rQ{14KIa4JvZ5ae7F8h1k)hORs;D*~*vEg%T=vrN(I$LMx
z&?qOL8>3h+5t;%(2DU1*tb4GR&wO!!&i)dW3sug(^D!fb#+e#Qh;)0lUZY4QrQvGy
zZ7x$e9aGQ|t!S~oxK@DSqoirAsFqf_>rZdulQS1MwP%#dM=?=%$lBX_*s^OI-Pe}!
zXA%HP-R*22+{}%4t>=}0evA63kEFO%;en^NvuZ={!WnwjbkZ}}#g4ChhN;mRj_ny{
z?CmLR)zW@l#OHozJ^dTH`QInsB@7F+BJb<+85Jy_&*F{NfTcEN^5a>y?i$Ffc_1J5
zY2D$h1M7cR3J4U%j)({U`Zj*@)N7<Q;j7=?Nzd9YlA1-*W}0I_8%<Y#nY$j_j(VJ?
z1cnZeWM^<jXhBVXB?mA>$WV#5RtLbzu^Ey|3PzzcC<o38E|m<@B}%~6Ers@w`~LD4
z;%Y+I>N4?cvgCS;MQMc&6e>^vhyYf&FglHh<T$rOaF7R>bIzM;y@nye%)}gN!)B0L
zQ92v5dP5(gpfTfNEvuMC(nc{iHIImo;+AWe^VTvjK|lZ}h!$Fr5=EnuU=x=`R3?S*
zni{ON%+J;6=xoOr<2#cG2$>pb0*Xub8ijL~%1o7xa+}}lShQCHMSU(tiE4cuasoom
z8s@~)##^gO8)*j1r4FS%sI#X>IkM*%zdm{%QiUM&4Rmtr?rm(|G2kOYz@;>$e7!pL
z-Wd-6&oRb7oJOG#Bdoi*pB?vaVcn*+D7248G&n(_mW!0-v&!WFCeGysCYG$Q+r*nP
z@T0$fhICv}GG%(yDs<>ajd(cbl^?#zM_-xX?!j9#$W4*es6($D=E$=rv%S5+HE5-?
zNSk`M&I|ACgWiO1{M{deQy78jbOW&=j(or*Pf9vIok=WC46Uj<S;wXp8@nu(1|QqS
zEB|tcC@s>aJ22WHB3NT7=pygFbee6q-AJjUqgitmG-?S)o<Bivcnw9<j=^NWQk9}z
zl{AdG|M7cqi6GViLTxri6SiD?y{`^O4q#$Iv_tULinJJJ&Q-7ti%oz;Shx9F9{q<w
z4*&NDj2)gt*A1Y&i9<UW9XijZEgKOl02%0Wr#^yMkkFA3lLCSu1<RTp-F)F|chFI8
zr#6-NJ=!rdQlZb4T0XbG-8jUOCnW_Z-dgQ+BB0Zdv!}*bd)=CB|9mPGy9?a?M>|jt
z!~9H*`h0xR5=@{83WkC%kk-<L2vZ8yUcZK~e`gRITWspGwH@6NrS1;EH-`D~7?(J-
zQH-2BkD50~s9HX^!}}|_YM3|=P35Rz2n8bEef0!u9$ihEScol2EhX$Q1d%2TL&BZ{
z<sQ87V?<K7GzBKoboG>2BoX5I6dU_b@n8swhNG{Ypg<8Et=BNT&mt}lFu~Wbd|fi2
z!1(bAM$eAX-`~Gb%+rPkqO`&rARq$1OR`8dby)*ZK6^p%(Qhwp4xfyj=luIK^oQ3j
zf5ln8ZX=Q>!mOXavL7Y1DkFxH?qc76zRmA_b2pNx%+Y+ZETas(0IxptCMDI0aY3Fl
zd&t&Xj=Q958kW{2L_}1l3+#DrANT+Mz1X^27MfE2s-pu92-eSWAj`uFBg5X84j_|;
zVo(GGCzck~^fy;ZDp_=U&m*G(O1i|<iAjF(;(qSieFtKFIxXe6DQZr9aEc>8e?J3p
z_d~a`ZvFMx#?on`jlV~}`qKkU{Bo9_urGV2Uz#hzys4Zi>2z~=&8t8n%DR(@qtm=N
z@iXpu=yuAb&Mb1oH62eS;nf%RGk$EEcGCq2umAWkH|?I~rdzgR8@Aa!HCYtSRp!|L
z>|4}F5;}vP>{()2G*0BIVKxsiJZ*8FcwT7~5p9$WRLACc@n3&I-=;3E8(2fRT*3r~
z$%!dW9vVhhLb`%p-{q9=w8vi_X5{2YY}$SU?Hz3y=($RTvD2S0{^1nux=h*hWY5s#
zxIbR0_hBLq77D*3zd$QRQ6jWJs6ysX*Ex1-2*Cni2q>x!in@)6B1Rd2>{Dw^_3S+F
z3?E;pr5Z!1L%LNjkrXLNgju*t<_9JS<Q*%VNYfDV3lLCfLW*b=5E5avT?{}K2nf-l
z5m6ot0TJz4T}n)X6Ndtn(gYF^s1PFoN)&GCx|M5i&Hn;X{6$oh<4Q;Yffx@3w~*>e
zG#ZWaDYeLPgF;DxfPm(Dg+ik}*u{JD%lj)iFW*U;k_MJg`Im@@;>t;(G89dDOmf-l
z0UQ9r6_N6TdMcLG+LnL`K%(L_ad8mo2qy-`%9e=Wup}<V21y++xg1~)pxAHD)<3Qd
zM6QT7291Uli%oDi5}Qz$8ii~0C|>SzfF*z%*m?cI_kMC_z+e#PD5z+m&a518f_PZO
z(qLXH7j2fCb`Biyut+Wkm<6hT{`4cyKk@oM{r2p{)VefHD5`b>9jr)TsY|I#m8z<s
z``pMko__cRpt{s0RxLf~Q2@4oaB%4E?>_dUziND9*B5l;=OWRdR|qiw`U)$S*foe&
zn~^8~<Ildg_0EC)z)>K+Twn@l19n$uYkfcX*1v!0_<`X&YZq#3Z9=4EWdIW;n2<(W
zU)$K0yEY#D)(;;4d3$Hu1>j|%2FT?BYeFjn_W-M4<u^)T0(c#m1EjUV6$F^|v>5=_
x1D$|jWyyL!IR^{@Da%N;c3J(6G%e{j`akW6UkWIyRSW<C002ovPDHLkV1i#V_h<kB
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..586d3d9f0a4bda4175ea75f7c3725fa607875e7c
GIT binary patch
literal 918
zc$@*218Mw;P)<h;3K|Lk000e1NJLTq001Tc000~a1^@s64cyIT000ABNkl<Zc-qC0
zSx8i27={yqmisOmOszC6TSP2dtVGL<=r)X~teYYdLdr^W&D^rdG|^GZIcYAVGc{Az
zsA#K}Xci_ep;H>Bspz;nD!iTVgo5dRtc&IhJTUY9|NlJ?%p&^ahZn1qKKd!0Z!Pqv
z>m@<w`vLcTo=;JW5Cvcv&-VL%kMq;rOHgp2MiSEZ7K8KGWm5|^O~);6t@(a1X}4Cg
zFmkt4w|i3yHBE<E@K`16QbXlcEeYhN25R{;OeWu1fV}-xl0a^1m^bX!b`^3x-%A3y
znPE3w^YGB4QWD6`42#{Gi!ArIl0a@|Sd2>!xIM2Wf!xfnXy<HXy1iubCaMV!z28E8
zv<fw;2J|!zVyxeUMokB@Jb8ipD|}LZ!${9Ko$+^Cq4udl&Y=oa#(xprO>Smbq|*ar
z>?mW>S=Wcj(W!Z9(RZRr*TFicx1zhr%?yj!tVEh?DU+u1c1-XR!ZbXIk-l;IJ};yF
zrdhw6`L|-b$;}K4+jtKtE_x<)x>ii^K}As&h~C8^*f9;MM>ObdHPT$5*I-1(aV>&3
zrXkvmL(z>2VP0Qk5Zz5~W?0CEbR;_$F{#M-4Aamg8lJU*J5(mjZaZn9zWjpb0@{$*
zkUOb`v&@5~sMmg_IlUF#O>Smbkb?q=PEVL9kLxi$I5C5<eHh(M0~q)*GBX?cIf|a<
z0Xmy?mph>s-A!(0Sm3%8By7?$iFM3J-Ln>q4NUwK^(6*+E4rK9%&;rA$&fof5+!Qe
z1GIhU#F)`Ei|WD#eAG72nYVxJ!i{a*f_ur$3=3G3g!uJ^qQp6<5VBc;=kb*=yzdZD
zp5gxrTLn)lG{NO)tL+lz#qxIu*`lB~3+^R1Gwh;GBI4{-3leLehv2p82w0sc5MigB
zISaMDhfBZbbcgQ#)lPD=!>kh!W0xlh<YtEXTgwq;n<EM2W`<o@bqkSevL%7s%rHM$
z9KvllMA&4R6>^@J#m)_*4-L;+T^|m#R3Ow!i7+e9ERc(w<faB{nG+V|E!Ur08I1th
zU0k(H;Yt0^qzEo@h9182lohtr*fY5Qs#ko8uPkWz)QW2`2jn7WfA`=%086N4zPRN)
s9z4f*PMQgFd60Wf5F^sU9I*Vq074^n>aAz0_y7O^07*qoM6N<$g0gqB7ytkO
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efb6d9b8070f646a39b04a40ee556386fb5291f6
GIT binary patch
literal 2199
zc$@*32x#|-P)<h;3K|Lk000e1NJLTq002w?001}$1^@s6S0o<%000PHNkl<ZcwX(A
zYiu0V8HS%ZGkdkyUVD?6IK)kC6B<LBK*A*<ZcAE7D=wuKRFSF{q0*MBv<RuB6_mD8
z)C#F-tBO)p;s-?)0$LiiL5d<!(wI0l2?-EPLUS{*9mmA6H(sy3*O_zb9IaN?DhBVY
za3rsv)h9>tYBcYY?|kR%j*aNq*qWp5C%<uYZ}-|*U!t|rQ)O0r!vxF<OkrUdlod*W
zBW;q-II+wKW7$!sdU0@XOKW3i%lhVRhEnPpU=?5;*xt3I{kiuZ4H2#m`y=>;AHxtB
zl!rBiLMcf~rWi>L6DJm1|G<hr-gx(l(|`@UYXKhs=YXXXzmQ6*t*<?G@K@4rS|M*W
z9>arSl!-8<$f_*yOpL+QIs3Mst^R#Z*8hMHfv5l^Fa#U|Ucc#{&z+1c3+>4`X{2&c
zNXi9;)R`}4zv$rRudh4-yq<G21V}+1Byb+szhYxkM^>iwT2_<~3Uua+*)KW(_UD{P
za##Qb(xNLjUft38%rPA0Xyd{Vlmk*ZI<q}d*M0S~`+#U())c}NAgkt8^hzmFO6fvT
z4&?JnDXFXt_T^-$sfFbsuFxwQm3x%~lQRQxa!f~<R7!#ZrK?PYDRP-5fGkEBsDdg3
zrjytDg!P=SSx#YQ7s3i&<v?NHVwYsOcGwIp$+;z2#=@8ZVRORfmZXcJPZ<j1G7bIO
z=1QU`$G8;pmWOCqA7SB|8bS;Gq(-vzwh#0Ei+yC`Hj@#S!Lqv+(s<hfDzES}em>2>
z!3*@fF~sn(OJ9F}ONjGF<1^b~Qc09lC_^HV6w8f|G-+WccYMU?*(8A)FN@bjXxdmu
zbwiNJR0d31?pwr?4fEOk`1^U$fryt4TdpB;b%-2zLl!lS6|{WwN=8m6II&}ZGo4W~
zW46BT)psu>+*rY@4<4del+>AZ_8IT6__-v@?pUa+fGJx(SnK7>Pp#n9hYn#U9R2T_
zr4>`YKm3^>H~g?k*JOJ34Wg8q;bTRK2iM_&3oKy)#o`NlXnkTi*>Rijge*nzobSI-
zqA-dVMu{CZc0L6PSJYiwjYi>$WHeKfFx<|t$yF?wcy`LfcG_Vqnxrxk!19_DgOhQH
z4=1Ut4Pg09E=4+?b#u@8tnO!+9{TP+d^@Sht&P4`&)WmM^^c>R*w=@Xl^DXHzGXf)
zf9qOWzO<B&<5*h{oiB88=Dk6EEnx|o*Imh)dzZ6hbv>p>kVm55Q!F~Kyi7C9PRq%%
z_{)9&Vz@U3fMFO2!$eBSnXW-z`T1^Izq^WCA6m_&X#d+`c0Y3vjRB@%B5gS#u~%2P
zKE%V@?nRgafYeClGR-g~x?HsP_)roQc|mtRehb?kYr~WF<1+*Khr=0}X3QRC?{n|4
z>ds}<EDAB1#7K%g&mO{O`UzQ8c!duDg(B@F$#_!S^^5fgAwZ!?L{qNrIhWV%3{yyz
z`-!4hhEB$?vks;u0BE@G3LbpnYrOsZDL&kDj?B0XfMs|Id*%_(#yE3ikZ^q^lj(W)
zBm7PURi1GEz5qM{ljhbYu79ABs`_BQqm)z(or)urhhoWRb|VZ_Zl`oV!@3XjbJe=V
z<e;+7&*wK^L+ejl7#mEHj%D?L79N2oXi~K(Kp~Ac)N}vyUm=@tkXec86$B$bDz6A2
zOoKvt-Web}=HL|;#ggA4aUm=}01aIfi-m{%FCXEmwTmf0FVRq4K-VW7YP|su^O`+V
zB%~1DeCY_LvRn=Q{6;5B{l*oA8+tni>3*x9rEBV$0rUoPs;!@hr%s2T>2F+178Woo
z`1h7Je)QtKc!M53O|mJQZNL66K_lcwR$=DLBg|*`89qD8KcCpermY(h!r;?DDfE9#
ztT(|tZ{1}QhD51cA#qE!73SC*r`h)8F7El|t)F<ZKk&*CcK`K&-d*q5+)B%aMm`S5
zmTdoR8(lk3<YY<QQkd8M!4-vD1SZv1Er)j=CweBvJx_j-1x>X8h373D{K&3ua`4jT
zo!tJDwXFZnDlWzO4=?ba-?ZtSG3<$8V!7?E(hsgVQH8=>FU@MRmfo&&JiX~<nr~Xn
zvRfLcSyZJvZs7P39WNXvK9nG6RuVF+0Y%InVdtMZIM#le>+fDp)%*&5wi`Jc=Xggi
z$M^N1B!sOR{3769R$*qh=djW{ERPNpvE~!AqZ46!bWKJe2zzSriy#^d!d5M#wxIi9
zzpg?o!$Y-Iqfh?@Axr8oa6R<VRl2ZzK)vOsWlEQ$Y!pfduQWpl!z>JbvD<WqCgnJe
zaxgG34HKUTOq4$#v~<YlbvN{<Jn5H?*YM#reH1WdaF5}|V|u6Y{?a=PSC84|{<fm@
zU9t=)`Rkps6^5%BR>s2AObDA3HhG(+EXe1TxDaMb8%H>kK3f^gZj{dIC<jM6MF~>?
zHW@oBvzE_rkeKD53#l9?G9yJcg$6YpVG_U}v3gR&$tIuh>l3&@%;GLnNF-Thlafii
z5wjl+ayr7YfNWmV(Al-)RFj1XW(cqFYH0?>EE1<sIawK*Vcdz6a7J0Nrnw_0D?1%w
z4v_f%)=k?V>Dcmx_{fFDj<WF?0W4u*AZA-{>3N-2NfI)SsFr;{*t+ReAOScOU`(-v
zdV!TkJ5S#7yZfJgG#zysyuydTL>RiuX3^Y|NF4oq)K_cvJ@)+f{?M|vc^_~X$V^w5
z0R({C6Qjw<pMU)K4IjMOy*4?LtaogDvq_j~kq(3dXP4jH*!l3&-`*Xp3Je3U0ZBkj
zQ&?VF2v`ryD~odl3<2#x98g6*$VCC8IonM@15gE+%r2Z<&R*aYU{gq8hfSY31Vu5&
Z{{a}F|3zXu!;b&}002ovPDHLkV1oNq7h3=T
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -65,48 +65,8 @@
 #library-animatable-box {
   display: none;
 }
 
 #library-animatable-box[animate] {
   display: -moz-box;
 }
 
-/* Private browsing and accessibility indicators */
-
-.accessibility-indicator,
-.private-browsing-indicator {
-  background-repeat: no-repeat;
-  background-size: 100% auto;
-  background-position: center;
-  width: 24px;
-  height: 24px;
-  margin-left: 8px;
-  margin-right: 8px;
-}
-
-.accessibility-indicator {
-  background-image: url("chrome://browser/skin/accessibility.svg");
-  -moz-user-focus: normal;
-  /* Clear default button styling */
-  -moz-appearance: none;
-  margin-top: unset;
-  margin-bottom: unset;
-  min-width: unset;
-  color: unset;
-  text-shadow: unset;
-}
-
-.accessibility-indicator:-moz-any(:hover, :active, :focus, :-moz-focusring) {
-  background-image: url("chrome://browser/skin/accessibility-active.svg");
-  outline: 0;
-}
-
-.private-browsing-indicator {
-  background-image: url("chrome://browser/skin/private-browsing.svg");
-}
-
-:root:not([accessibilitymode]) .accessibility-indicator,
-:root:not([privatebrowsingmode=temporary]) .private-browsing-indicator {
-  display: none;
-}
-
-/* End private browsing and accessibility indicators */
deleted file mode 100644
--- a/browser/themes/shared/icons/accessibility-active.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px">
-<path fill="#008EA4" fill-opacity="0.9886" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0  C24,18.6,18.6,24,12,24z"/>
-<g>
-	<circle fill="#FFFFFF" cx="12" cy="6" r="2"/>
-	<path fill="#FFFFFF" d="M18.1,8.5h-3.6l0,0h-5l0,0H6c-0.6,0-1,0.4-1,1s0.4,1,1,1h3.5v0.6l0,0v7.8c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1   v-4.1h1v4.1c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1v-4.1l0,0v-4.5H18c0.6,0,1-0.4,1-1C19,8.8,18.6,8.5,18.1,8.5z"/>
-</g>
-</svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/themes/shared/icons/accessibility.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px">
-<path fill="#00C8D7" fill-opacity="0.9886" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0  C24,18.6,18.6,24,12,24z"/>
-<g>
-	<circle fill="#FFFFFF" cx="12" cy="6" r="2"/>
-	<path fill="#FFFFFF" d="M18.1,8.5h-3.6l0,0h-5l0,0H6c-0.6,0-1,0.4-1,1c0,0.6,0.4,1,1,1h3.5v0.6l0,0v7.8c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1
-  v-4.1h1v4.1c0,0.6,0.4,1.1,1,1.1s1-0.5,1-1.1v-4.1l0,0v-4.5H18c0.6,0,1-0.4,1-1C19,8.8,18.6,8.5,18.1,8.5z"/>
-</g>
-</svg>
deleted file mode 100644
--- a/browser/themes/shared/icons/private-browsing.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px">
-<path fill="#8000D7" d="M12,24L12,24C5.4,24,0,18.6,0,12l0,0C0,5.4,5.4,0,12,0l0,0c6.6,0,12,5.4,12,12l0,0
-	C24,18.6,18.6,24,12,24z"/>
-<path fill="#FFFFFF" d="M15.4,11c-1-0.1-1.9,0.5-2.1,1.5c0,0.3,1.2,0.7,2.3,0.7s2.1-0.7,2.1-0.9C17.7,12.1,17,11.1,15.4,11
-	L15.4,11z M8.6,11c-1.6,0.1-2.3,1-2.3,1.3c0,0.2,1.1,0.9,2.1,0.9c1,0,2.3-0.4,2.3-0.7C10.5,11.5,9.5,10.9,8.6,11L8.6,11z M16.4,16.5
-	c-1.7,0-2.8-2-4.4-2c-1.6,0-2.8,2-4.4,2c-2.1,0-3.6-1.9-3.6-5.3c0-2.1,0.6-2.7,3.3-2.7s3.4,1.1,4.7,1.1s2.1-1.1,4.7-1.1
-	S20,9.2,20,11.2C20,14.6,18.5,16.5,16.4,16.5L16.4,16.5z"/>
-</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -107,18 +107,16 @@
   skin/classic/browser/preferences/in-content/sync-devices.svg (../shared/incontentprefs/sync-devices.svg)
   skin/classic/browser/preferences/in-content/sync.svg         (../shared/incontentprefs/sync.svg)
 * skin/classic/browser/preferences/in-content/containers.css   (../shared/incontentprefs/containers.css)
 * skin/classic/browser/preferences/containers.css              (../shared/preferences/containers.css)
   skin/classic/browser/fxa/default-avatar.svg                  (../shared/fxa/default-avatar.svg)
   skin/classic/browser/fxa/sync-illustration.svg               (../shared/fxa/sync-illustration.svg)
 
 
-  skin/classic/browser/accessibility.svg              (../shared/icons/accessibility.svg)
-  skin/classic/browser/accessibility-active.svg       (../shared/icons/accessibility-active.svg)
   skin/classic/browser/arrow-left.svg                 (../shared/icons/arrow-left.svg)
   skin/classic/browser/back.svg                       (../shared/icons/back.svg)
   skin/classic/browser/back-12.svg                    (../shared/icons/back-12.svg)
   skin/classic/browser/bookmark.svg                   (../shared/icons/bookmark.svg)
   skin/classic/browser/bookmark-animation.svg         (../shared/icons/bookmark-animation.svg)
   skin/classic/browser/bookmark-hollow.svg            (../shared/icons/bookmark-hollow.svg)
   skin/classic/browser/bookmark-star-on-tray.svg      (../shared/icons/bookmark-star-on-tray.svg)
   skin/classic/browser/characterEncoding.svg          (../shared/icons/characterEncoding.svg)
@@ -150,17 +148,16 @@
   skin/classic/browser/link.svg                       (../shared/icons/link.svg)
   skin/classic/browser/mail.svg                       (../shared/icons/mail.svg)
   skin/classic/browser/menu.svg                       (../shared/icons/menu.svg)
   skin/classic/browser/new-tab.svg                    (../shared/icons/new-tab.svg)
   skin/classic/browser/new-window.svg                 (../shared/icons/new-window.svg)
   skin/classic/browser/open.svg                       (../shared/icons/open.svg)
   skin/classic/browser/page-action.svg                (../shared/icons/page-action.svg)
   skin/classic/browser/print.svg                      (../shared/icons/print.svg)
-  skin/classic/browser/private-browsing.svg           (../shared/icons/private-browsing.svg)
   skin/classic/browser/privateBrowsing.svg            (../shared/icons/privateBrowsing.svg)
   skin/classic/browser/restore-session.svg            (../shared/icons/restore-session.svg)
   skin/classic/browser/quit.svg                       (../shared/icons/quit.svg)
   skin/classic/browser/reload.svg                     (../shared/icons/reload.svg)
   skin/classic/browser/reload-to-stop.svg             (../shared/icons/reload-to-stop.svg)
   skin/classic/browser/save.svg                       (../shared/icons/save.svg)
   skin/classic/browser/settings.svg                   (../shared/icons/settings.svg)
   skin/classic/browser/sidebars.svg                   (../shared/icons/sidebars.svg)
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -229,22 +229,16 @@
  * window state. Otherwise, elements in the navigator-toolbox, like the menubar,
  * can swallow those events. It will also place the buttons above the fog on
  * Windows 7 with Aero Glass.
  */
 #titlebar-buttonbox {
   z-index: 1;
 }
 
-#titlebar-buttonbox,
-#titlebar-buttonbox-container {
-  display: flex;
-  align-items: center;
-}
-
 .titlebar-placeholder[type="caption-buttons"] {
   margin-left: 22px; /* space needed for Aero Snap */
 }
 
 /* titlebar command buttons */
 
 #titlebar-min {
   -moz-appearance: -moz-window-button-minimize;
@@ -1082,36 +1076,99 @@ notification[value="translation"] {
 }
 
 #customization-tipPanel > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="right"] {
   margin-left: -2px;
 }
 
 /* End customization mode */
 
-/* Private browsing and accessibility indicators */
+/* Private browsing indicators */
+
+/**
+ * Currently, we have two places where we put private browsing indicators on
+ * Windows. When tabsintitlebar is enabled, we draw the indicator in the titlebar.
+ * When tabsintitlebar is disabled, we draw the indicator at the end of the
+ * tabstrip. The titlebar indicator is the fiddliest of the bunch, since we
+ * want the bottom border of the image to line up with the bottom of the window
+ * caption buttons. That's why there's so much special-casing going on in here.
+ */
+.private-browsing-indicator {
+  display: none;
+  pointer-events: none;
+}
 
-:root:-moz-any([tabsintitlebar], [inFullscreen]):not([privatebrowsingmode=temporary]) .accessibility-indicator,
-:root:-moz-any([tabsintitlebar], [inFullscreen]) .private-browsing-indicator {
-  margin-inline-end: 12px;
+#private-browsing-indicator-titlebar {
+  display: block;
+  position: absolute;
+}
+
+#main-window[privatebrowsingmode=temporary][tabsintitlebar] #private-browsing-indicator-titlebar > .private-browsing-indicator {
+  display: block;
+}
+
+#main-window[privatebrowsingmode=temporary]:-moz-any([inFullscreen],:not([tabsintitlebar])) #TabsToolbar > .private-browsing-indicator {
+  display: -moz-box;
+}
+
+#TabsToolbar > .private-browsing-indicator {
+  background: url("chrome://browser/skin/privatebrowsing-mask-tabstrip.png") no-repeat center -3px;
+  margin-inline-start: 4px;
+  width: 48px;
 }
 
-:root:not([accessibilitymode]) .private-browsing-indicator,
-.accessibility-indicator {
-  margin-inline-start: 12px;
+/* Bug 1008183: We're intentionally using the titlebar asset here for fullscreen
+ * mode, since the tabstrip "mimics" the titlebar in that case with its own
+ * min/max/close window buttons.
+ */
+#private-browsing-indicator-titlebar > .private-browsing-indicator,
+#main-window[inFullscreen] #TabsToolbar > .private-browsing-indicator {
+  background: url("chrome://browser/skin/privatebrowsing-mask-titlebar.png") no-repeat center 0px;
+  margin-inline-end: 4px;
+  width: 40px;
+  height: 20px;
+  position: relative;
+}
+
+@media (-moz-windows-classic) {
+  /**
+   * We have to use top instead of background-position in this case, otherwise
+   * the bottom of the indicator would get cut off by the bounds of the
+   * private-browsing-indicator element.
+   */
+  #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
+    top: 4px;
+  }
 }
 
-:root[accessibilitymode][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .accessibility-indicator,
-:root[privatebrowsingmode=temporary][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .private-browsing-indicator,
-:root[accessibilitymode]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .accessibility-indicator,
-:root[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
-  display: none;
+@media (-moz-os-version: windows-win7) {
+  @media (-moz-windows-glass) {
+    #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
+      top: 1px;
+    }
+    #main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
+      top: -1px;
+    }
+  }
+
+  /**
+   * This next block targets Aero Basic, which has different positioning for the
+   * window caption buttons, and therefore needs to be special-cased.
+   */
+  @media (-moz-windows-default-theme) {
+    @media (-moz-windows-compositor: 0) {
+      #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
+        background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-win7-tall.png");
+        height: 28px;
+      }
+    }
+  }
 }
 
-/* End private browsing and accessibility indicators */
+/* End private browsing indicators */
 
 %include ../shared/UITour.inc.css
 
 #UITourTooltipButtons {
   /**
    * Override the --arrowpanel-padding so the background extends
    * to the sides and bottom of the panel.
    */
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -18,16 +18,21 @@ browser.jar:
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
+  skin/classic/browser/privatebrowsing-mask-tabstrip.png
+  skin/classic/browser/privatebrowsing-mask-tabstrip-win7.png
+  skin/classic/browser/privatebrowsing-mask-titlebar.png
+  skin/classic/browser/privatebrowsing-mask-titlebar-win7.png
+  skin/classic/browser/privatebrowsing-mask-titlebar-win7-tall.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
   skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/toolbarbutton-dropdown-arrow-win7.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
@@ -84,9 +89,11 @@ browser.jar:
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/page-livemarks.png                   chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/audioFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/videoFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
 
+% override chrome://browser/skin/privatebrowsing-mask-tabstrip.png    chrome://browser/skin/privatebrowsing-mask-tabstrip-win7.png  os=WINNT osversion<=6.1
+% override chrome://browser/skin/privatebrowsing-mask-titlebar.png    chrome://browser/skin/privatebrowsing-mask-titlebar-win7.png  os=WINNT osversion<=6.1
 % override chrome://browser/skin/toolbarbutton-dropdown-arrow.png     chrome://browser/skin/toolbarbutton-dropdown-arrow-win7.png   os=WINNT osversion<=6.1
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bd5d46a76a8def4a3d8db31aed36126f5b7b062b
GIT binary patch
literal 949
zc$@*X14{gfP)<h;3K|Lk000e1NJLTq001xm001Be1^@s6RMh(%000AgNkl<Zc-rli
zNl#Nz7(n%BAOy-(h{_Cz3llfSonfOJSH`#kV$eho5K0v&ipZplwzL2(5&=OI1cg`2
zBtU0qX-f$}fljt`1YO|$NJuIvr7!8?hWL_`H22=-JLmP~_1ymp=j-6$;5-hHm8Kx5
zm_j*Cp87hI{h*NAG*U+G6ft6XVjKV$Z~`}QJn_y~Wwzh1h~Y{&sgn~u8A5*VRWX4B
z-~vwIR+Y(u2#VLcpC8>Y3J2j!I0A0q2(DfOXi+Q))Z!WdZr}*6-rhD;dNfD+0Ug8{
za0FLy_8yQSCh*0NfF5)RTTTyPqW}?sGvIiJ1ENsC6(K<eMhs8Xkvc7CP5eGZi)6_>
zX^j6m!%X5&dajisS`){n*eR2Ovgj@eH+`N@W*#`67*HC?mh#etguhf!d7>{x)9aN<
z-AnIo4lMI)#9P}Ev)!rRbBlOtL;2;7#pf|>`Bbxf$F6g^V2^ks-fFnfzx*nVw=_(j
z-FSf_$jk)CfCbPZSTdc^WZSp?+4qLzliDKbTQQ!ruM?SUd0}rA0Slm^O)fg*FC{2I
z^=;F#hhzZvuiyLj9rqr5_J%|M@1A@_yZbPtN=0TS&38b%l$*9d_z4PN%H%_<CXgAR
z6Z)s|_1f$U3{CZ|&UJ&z;yMyFsI9L0JG_mYV1}k3p?+ncbX*VXqCu54H>7%*%uI0f
z5x_zL^Mt=ZzzlQgP)7V&Wv#r%Q9lcBlf(KzzF^}hptoRDpA%fEE(mK{ti9f=lnrXl
zus+^AZ+-*;BxWK<9{~|8X?N;8;V-ophi2RF4r@Fg3(K0rX;4}nHl6F}7l6mgtyfx{
zu;+QU?lyOW9>o!pnF)>o3jm74nKH%5ngxW&usEu<Wd&7e-Y5MK1T3HgZFZqUWT=Ym
zpL?0cRcuO(j^mOG{Yr}%J?F>QbyA}<Kdx@IfYK}{JFmoNKsX9WnIp(TkK{<o5;zK?
zMQAdu?7BCl24~B?kGia&a!qb<^>Sa~__lGy>awic-2#@#oOh{43+o-pGkmhMz%k$f
zKx5qKR1u|3{y3yYnH|JX!`r1bVQiu^dG_!zdlK59fOP=<0}DyWlMFxuF~)a5G1`PX
zhBL)yJAlHHb|lRbI0KG80+=Y^CxJ7_(MLcS3P?l;F$NsZZa`sZv%D>i5a0|rdItOj
Xfp>l0?Pb^300000NkvXXu0mjfJ{+=N
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e2eba8b89e4fd2b1b9d33a5f93f6c1bbfdaf9889
GIT binary patch
literal 403
zc$@)~0c`$>P)<h;3K|Lk000e1NJLTq001xm001Be0{{R3V+so{0002GP)t-s|NsB>
z==qcrzpYBnoF&BM#P9Im_R5Oi@7wj%q2;`6*P0;1)uQH>7{Hht!M$$S;=t~lB*g93
z^rJM$!+hK0!S30t=+Bkn@!<CH;P=|C>BN29z;xN1Cd8>i%<9nc>e2GLXV#PyztWrJ
zz;oHwq~`SI_}8ZAp)kj}WYx%q-k>hVtVzzIGRWx5@#xI)*sAE-uIZg7#O22C)}`m>
z$MDOH;Oo@$<;U=`RMG3x^X}R8&644pBEy}2(rf?#00DGTPE!Ct=GbNc004(cL_t(|
zUhUE~62kxxz`*Ft5N2j(=Kp^yGp^E|Yme1$r%XbU*d++0U>KC!mR-+fSFTmr7V|17
z5EYJzs;Zy{vj$O3prrzjPFH$;7#O3|gsBP~<_nP}_#$f$M+F<8v~$7UmjFWra^SC{
x=)gP!gcpFT83RagyFZ>H{_FkuCT6}A`~U&>5GQ}a@O}UQ002ovPDHLkV1j|%*ysQN
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4a723c54e2316e978062be38a7be8eaa1a0d11f2
GIT binary patch
literal 940
zc$@*O15^BoP)<h;3K|Lk000e1NJLTq001xm001Be1^@s6RMh(%000AXNkl<Zc-rlg
z$xl;J6o(1_1yoX*5x@cn=*F-yu3Q@3nvl3aOi+RVG8+L@W(zb*feJ04fCvJp@M?uJ
zccO(-+8}dVOItdDF7O_cCWS!Wdj`^H<R-t(x#!&TeH}bp&;I_tCrZM7?^1Pf2v1C<
z028nQqbu`~0)>Ii(vB-)j7kBfGl73bpa3<uqaiA#fV9A6F-D<~7idF6lnHr(CL|!j
zs1#uOj|2jcK>RF5r2rFQfqefaDKBmoBa)&Zo~SuSC}1(ZjOF>UWv*dH4358!<c}Vx
z`?F;%#_(8U^r+xTV2h+RcA8IQ4=|m$0KoS>7Os>;wJMm`yOsP3@y4daV6ka;9U`7e
z_cWX%FAQ#)8>^q&vaH+faEFzPH)^hRDWApi6axj5s~~aW0(t(8Qf}M~hL>Ew2HEPY
zZfDo@YwxJYdzTH8PPP8Hdo*i-H;C*4CUOF?Xb>l`Tt28VJAoYAKldzKj@{#ct=fZk
z@*#~G-oW2QR=|%f<zAY`@RIA>AQN>jZUanr!H6~^qDphmyGT`bgR`nzG}@h@I+e*@
zeUrPIOe<1lUaZn|=Z|QiF6mJj;SD0YfQghqD;mN}4qDD<iSz(7_fcakPv}rWEt6g^
z=RO?RbP9B2e%{KU*U90o>}uPB`2Er!X_$o??k=(dxv04X4dJCC^uuJ`t*&L)pM~+e
z)uvl8S<UEA>#SL{4u@W8w!)ojZ~g7AWq5<gu3S<AzDOWu3L}yXdaYdAzhnj=GLe7M
z?7%G1J-~D-0?&gxr?MHfOKTFH^)Td2*Md>pvuHY8Uzh4_*$n1VY0%s0(-VlE#E2%}
zw^5Q7T&c!b(}o{vvtv!Fv$5{HTTh{tDN|@Kl<gTqTD{t2H?CM50=CGI9#*BvBe#Q`
zKn`ktgNDfDW!Rwb!MXB<B(GvsvR8>Ji_V&ZIwxuZOY^BwKt0K;NCiDrp?%|I-_Q9Q
z0jh+&!1)FlCu$O-Qh*6p01h-|(d%Vw#^_i0`l%3L0VZGrMqGi^fO0|2g??cZL&)bu
zPGIf~FaQfM5fzB5z1#LE-KSPj8PYq>3LpOVDuOR?M_@NF01Gpbn+|LqL=KoT6I;_$
z{7MC>v<gKUt$N-aLA&RH0a#o=eWDWaFhr5W<KW`Tc)BAVXXosk{`n1(UhvtS<;6z;
O0000<MNUMnLSTY#G_!92
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..835912b534b2d16ac01a52c44ff6f3548ac0032c
GIT binary patch
literal 860
zc$@)T1Ec(jP)<h;3K|Lk000e1NJLTq001Ze000yS1^@s6|3-hi0009cNkl<Zc-qy`
z+fR~V90&0J4X4gTQ&USkVt1~KwywHsb=l_DjkZ}%QS-<$vF0HhC^|t!5uKr?nY!{-
zO7IB02#Ops&p-&s(YmnbcR`5D@yd;K@x6Gz&-*^#&*0kW>1ht_MmIiqM}~z`zy!Ac
z16YF|2C`*XD5WM?U|qaG%FA33wWrR^SI2S{XVaQ8E$K`Xee>_T6dkG9e0PReR2$bP
z@29NJ1_A8C1=&(8KoG89I$j~!rl<2&%=^RY*=G6v0dBTC4IZ~tpfT3o<|%16yVvH~
zYlrrImkZ9YCfR<=J)!yyMWFgbTikgS*DEKo1tuRb76B<zEKsUqxQg9X<ME00_sPGa
z8rw4C?{%h&&e6AaxP!<RGWmejF<p3n)+!b#RV0o=IjOgK2R%o>P8^4B&qW}o!T(G(
z_1y+{5ZMA#P(a!6<`ImI&@e+u3PYbCPS-R%;O&?;EiNy_q_Mgf5BqnXMbI^+WQKmY
zX2t+LKB}<<1w=nP&d*xG0;Q7FshDe*nmlN1?W5k(bazk<U0F0+$uFEdfF3ef`uVW*
zdaDx78gd3U<RjW6=;2%Fh-`uB43LBKGncVIX-@jE*!D=c<%K{jKO9b@+Ul_Fxm@1P
zZg9IzYMTSj&c56BShxju5ZMBg4@lzReHqIbk(5NWD)9-e4F)e0<+T17z9qVYOg>;G
zdgw(Zk-SY1E|rs6+FiNPaUQWE+_1<-H?8OUJ>29hPhn~6<A;`s?o|2#qY*HD2_u?n
z5?5Z7@J@%Zm(4$QHuo-Wbh4ie?G=PKs38L-QwEdH>auLx-6D?EOigC!t4Q2SZGg!i
zn6`*rCd{Ns@r#JYjl8frO@0_%^D>IP20brz0V|GdQ9+*<_FD5Cy~9Wxmt69Ge_%y)
zr-FydPzsp-0?VS>6ddyWTn{=3g@6T_e89rkMo~-Rgt&_=p6x+@(NzWvU;&dCn8kW3
zD5gfTRLu!v5_a;JuN}8$MOP8n1_M}_xqK?v00_xp=JrtwNcEz^@FrDJIAi@Pg0|Pe
m02VNT4RGF2pbU{vyskfVIUEteAA!IC0000<MNUMnLSTZ~^p(5-
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..111dc7d04167c3767538fb6b64e34717fb259ef0
GIT binary patch
literal 370
zc$@)p0ge8NP)<h;3K|Lk000e1NJLTq001Ze000yS0ssI2a3tvL0003wNkl<ZcmZRW
zBD{Y<(5S^=G-~m#^lP&e4lg!2aisRnlOyjx-2Zld>Z2Lb2d7CKIMH_R{q3(n!NWDT
z7a5(HqjF?#-VLf*{OG{DuP=V0p{t9YU7Gy_Q~dG4cT}_Z=9(7}L%%%x@$tboOv^q#
z_zq{^TKke}7GGKT6lmgslACkXjsUqUozA?v@CjngtBar3dYzjhvVX4Hkpsmyfg;zI
zJfoV$r@HO~O}xJB`9g!^5RsiJS0NVf$+->@S)g|e;y$2as#(0o^Bg=R-(LIj;`B#g
zf`V{?3GKz1k3d1V0$>18GdtZ_1&U^Z=+?TIRL;k9RF6D8_8u)&Ut9VdS>)OA4?tzq
zFF=4<`E<{N*H=D2J^o>1$VG@CFpghe{`~Id*Rzuz&Q>}+l8ekyi$^U603#jiY0Ssy
Q{Qv*}07*qoM6N<$f_}`eLI3~&
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -918,16 +918,29 @@ Selection::FocusOffset()
 
   if (GetDirection() == eDirNext){
     return mAnchorFocusRange->EndOffset();
   }
 
   return mAnchorFocusRange->StartOffset();
 }
 
+nsIContent*
+Selection::GetChildAtAnchorOffset()
+{
+  if (!mAnchorFocusRange)
+    return nullptr;
+
+  if (GetDirection() == eDirNext) {
+    return mAnchorFocusRange->GetChildAtStartOffset();
+  }
+
+  return mAnchorFocusRange->GetChildAtEndOffset();
+}
+
 static nsresult
 CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
                     nsRange* aRange, int32_t* aCmp)
 {
   nsINode* start = aRange->GetStartContainer();
   NS_ENSURE_STATE(aCompareNode && start);
   // If the nodes that we're comparing are not in the same document,
   // assume that aCompareNode will fall at the end of the ranges.
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -166,16 +166,18 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL methods
   nsINode*     GetAnchorNode();
   uint32_t     AnchorOffset();
   nsINode*     GetFocusNode();
   uint32_t     FocusOffset();
 
+  nsIContent*  GetChildAtAnchorOffset();
+
   /*
    * IsCollapsed -- is the whole selection just one point, or unset?
    */
   bool IsCollapsed() const
   {
     uint32_t cnt = mRanges.Length();
     if (cnt == 0) {
       return true;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3011,23 +3011,24 @@ nsDocument::InitCSP(nsIChannel* aChannel
   // The document may already have some sandbox flags set (e.g. if the document
   // is an iframe with the sandbox attribute set). If we have a CSP sandbox
   // directive, intersect the CSP sandbox flags with the existing flags. This
   // corresponds to the _least_ permissive policy.
   uint32_t cspSandboxFlags = SANDBOXED_NONE;
   rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mSandboxFlags |= cspSandboxFlags;
-
   // Probably the iframe sandbox attribute already caused the creation of a
   // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
   // and no one has been created yet.
   bool needNewNullPrincipal =
     (cspSandboxFlags & SANDBOXED_ORIGIN) && !(mSandboxFlags & SANDBOXED_ORIGIN);
+
+  mSandboxFlags |= cspSandboxFlags;
+  
   if (needNewNullPrincipal) {
     principal = NullPrincipal::CreateWithInheritedAttributes(principal);
     principal->SetCsp(csp);
     SetPrincipal(principal);
   }
 
   // ----- Enforce frame-ancestor policy on any applied policies
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2391,16 +2391,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
   }
 
   if (tmp->mListenerManager) {
     tmp->mListenerManager->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
   }
 
+  tmp->UpdateTopInnerWindow();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
   if (tmp->mApplicationCache) {
@@ -2622,17 +2623,20 @@ nsGlobalWindow::SetInitialPrincipalToSub
     // NullPrincipal.
     bool isNullPrincipal;
     MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) &&
                isNullPrincipal);
 #endif
   }
 
   GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);
-  mDoc->SetIsInitialDocument(true);
+
+  if (mDoc) {
+    mDoc->SetIsInitialDocument(true);
+  }
 
   nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell();
 
   if (shell && !shell->DidInitialize()) {
     // Ensure that if someone plays with this document they will get
     // layout happening.
     nsRect r = shell->GetPresContext()->GetVisibleArea();
     shell->Initialize(r.Width(), r.Height());
@@ -4376,16 +4380,27 @@ nsPIDOMWindowInner::Thaw()
 
 void
 nsPIDOMWindowInner::SyncStateFromParentWindow()
 {
   nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
 }
 
 void
+nsGlobalWindow::UpdateTopInnerWindow()
+{
+  if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+    return;
+  }
+
+  AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+  AsInner()->UpdateUserMediaCount(-(int32_t)mNumOfActiveUserMedia);
+}
+
+void
 nsPIDOMWindowInner::AddPeerConnection()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(IsInnerWindow());
   mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections++
                   : mActivePeerConnections++;
 }
 
@@ -4514,48 +4529,52 @@ void
 nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aDelta == 0) {
     return;
   }
 
-  uint32_t& counter = mTopInnerWindow ? mTopInnerWindow->mNumOfOpenWebSockets
-                                      : mNumOfOpenWebSockets;
-
-  MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || ((aDelta + counter) < counter));
-
-  counter += aDelta;
+  if (!IsTopInnerWindow()) {
+    mTopInnerWindow->UpdateWebSocketCount(aDelta);
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(
+    aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));
+
+  mNumOfOpenWebSockets += aDelta;
 }
 
 bool
 nsPIDOMWindowInner::HasOpenWebSockets() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  return (mTopInnerWindow ? mTopInnerWindow->mNumOfOpenWebSockets
-                          : mNumOfOpenWebSockets) > 0;
+  return mNumOfOpenWebSockets ||
+         (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
 }
 
 void
 nsPIDOMWindowInner::UpdateUserMediaCount(int32_t aDelta)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aDelta == 0) {
     return;
   }
 
-  uint32_t& counter = mTopInnerWindow ? mTopInnerWindow->mNumOfActiveUserMedia
-                                      : mNumOfActiveUserMedia;
-
-  MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || ((aDelta + counter) < counter));
-
-  counter += aDelta;
+  if (!IsTopInnerWindow()) {
+    mTopInnerWindow->UpdateUserMediaCount(aDelta);
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(
+    aDelta > 0 || ((aDelta + mNumOfActiveUserMedia) < mNumOfActiveUserMedia));
+
+  mNumOfActiveUserMedia += aDelta;
 }
 
 bool
 nsPIDOMWindowInner::HasActiveUserMedia() const
 {
   return (mTopInnerWindow ? mTopInnerWindow->mNumOfActiveUserMedia
                           : mNumOfActiveUserMedia) > 0;
 }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1314,16 +1314,18 @@ public:
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
 
   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
 
   mozilla::dom::Performance* GetPerformance();
 
+  void UpdateTopInnerWindow();
+
 protected:
   // Web IDL helpers
 
   // Redefine the property called aPropName on this window object to be a value
   // property with the value aValue, much like we would do for a [Replaceable]
   // property in IDL.
   void RedefineProperty(JSContext* aCx, const char* aPropName,
                         JS::Handle<JS::Value> aValue,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1779,22 +1779,30 @@ nsJSContext::EndCycleCollectionCallback(
 }
 
 // static
 bool
 InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
 {
   nsJSContext::KillInterSliceGCRunner();
   MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
-  // We use longer budgets when timer runs since that means
-  // there hasn't been idle time recently and we may have significant amount
-  // garbage to collect.
-  int64_t budget = sActiveIntersliceGCBudget * 2;
-  if (!aDeadline.IsNull()) {
-    budget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
+  // We use longer budgets when the CC has been locked out but the CC has tried
+  // to run since that means we may have significant amount garbage to collect
+  // and better to GC in several longer slices than in a very long one.
+  int64_t budget = aDeadline.IsNull() ?
+    int64_t(sActiveIntersliceGCBudget) :
+    int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
+  if (sCCLockedOut && sCCLockedOutTime) {
+    int64_t lockedTime = PR_Now() - sCCLockedOutTime;
+    int32_t maxSliceGCBudget = sActiveIntersliceGCBudget * 10;
+    double percentOfLockedTime =
+      std::min((double)lockedTime / NS_MAX_CC_LOCKEDOUT_TIME, 1.0);
+    budget =
+      static_cast<int64_t>(
+        std::max((double)budget, percentOfLockedTime * maxSliceGCBudget));
   }
 
   TimeStamp startTimeStamp = TimeStamp::Now();
   TimeDuration duration = sGCUnnotifiedTotalTime;
   uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
   nsJSContext::GarbageCollectNow(aData ?
                                    static_cast<JS::gcreason::Reason>(reason) :
                                    JS::gcreason::INTER_SLICE_GC,
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -781,16 +781,19 @@ public:
   // window of that outer.
   inline bool IsCurrentInnerWindow() const;
 
   // Returns true if the document of this window is the active document.  This
   // is not identical to IsCurrentInnerWindow() because document.open() will
   // keep the same document active but create a new window.
   inline bool HasActiveDocument();
 
+  // Returns true if this window is the same as mTopInnerWindow
+  inline bool IsTopInnerWindow() const;
+
   bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void MuteAudioContexts();
   void UnmuteAudioContexts();
 
   bool GetAudioCaptured() const;
   nsresult SetAudioCapture(bool aCapture);
 
--- a/dom/base/nsPIDOMWindowInlines.h
+++ b/dom/base/nsPIDOMWindowInlines.h
@@ -94,16 +94,22 @@ bool
 nsPIDOMWindowInner::HasActiveDocument()
 {
   return IsCurrentInnerWindow() ||
     (mOuterWindow &&
      mOuterWindow->GetCurrentInnerWindow() &&
      mOuterWindow->GetCurrentInnerWindow()->GetDoc() == mDoc);
 }
 
+bool
+nsPIDOMWindowInner::IsTopInnerWindow() const
+{
+  return mTopInnerWindow == this;
+}
+
 template <class T>
 nsIDocShell*
 nsPIDOMWindow<T>::GetDocShell() const
 {
   if (mOuterWindow) {
     return mOuterWindow->GetDocShell();
   }
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -97,16 +97,26 @@ public:
     return static_cast<uint32_t>(mStart.Offset());
   }
 
   uint32_t EndOffset() const
   {
     return static_cast<uint32_t>(mEnd.Offset());
   }
 
+  nsIContent* GetChildAtStartOffset() const
+  {
+    return mStart.GetChildAtOffset();
+  }
+
+  nsIContent* GetChildAtEndOffset() const
+  {
+    return mEnd.GetChildAtOffset();
+  }
+
   bool IsPositioned() const
   {
     return mIsPositioned;
   }
 
   void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees)
   {
     mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees;
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -81,16 +81,18 @@ struct CacheResponse
   nsCString[] urlList;
   uint32_t status;
   nsCString statusText;
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   CacheReadStreamOrVoid body;
   IPCChannelInfo channelInfo;
   OptionalPrincipalInfo principalInfo;
+  uint32_t paddingInfo;
+  int64_t paddingSize;
 };
 
 union CacheResponseOrVoid
 {
   void_t;
   CacheResponse;
 };
 
--- a/dom/cache/DBAction.cpp
+++ b/dom/cache/DBAction.cpp
@@ -4,33 +4,62 @@
  * 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/dom/cache/DBAction.h"
 
 #include "mozilla/dom/cache/Connection.h"
 #include "mozilla/dom/cache/DBSchema.h"
 #include "mozilla/dom/cache/FileUtils.h"
+#include "mozilla/dom/cache/QuotaClient.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/net/nsFileProtocolHandler.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageService.h"
 #include "mozStorageCID.h"
 #include "nsIFile.h"
 #include "nsIURI.h"
 #include "nsIFileURL.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
 using mozilla::dom::quota::PersistenceType;
 
+namespace {
+
+nsresult
+WipeDatabase(const QuotaInfo& aQuotaInfo, nsIFile* aDBFile,
+             nsIFile* aDBDir)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aDBFile);
+  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+
+  nsresult rv = RemoveNsIFile(aQuotaInfo, aDBFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  // Note, the -wal journal file will be automatically deleted by sqlite when
+  // the new database is created.  No need to explicitly delete it here.
+
+  // Delete the morgue as well.
+  rv = BodyDeleteDir(aQuotaInfo, aDBDir);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = WipePaddingFile(aQuotaInfo, aDBDir);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
+}
+
 DBAction::DBAction(Mode aMode)
   : mMode(aMode)
 {
 }
 
 DBAction::~DBAction()
 {
 }
@@ -95,35 +124,73 @@ DBAction::RunOnTarget(Resolver* aResolve
 nsresult
 DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                          mozIStorageConnection** aConnOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aDBDir);
   MOZ_DIAGNOSTIC_ASSERT(aConnOut);
 
-  nsCOMPtr<mozIStorageConnection> conn;
-
   bool exists;
   nsresult rv = aDBDir->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (!exists) {
     if (NS_WARN_IF(mMode != Create)) {  return NS_ERROR_FILE_NOT_FOUND; }
     rv = aDBDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
+  rv = OpenDBConnection(aQuotaInfo, aDBDir, aConnOut);
+
+  return rv;
+}
+
+SyncDBAction::SyncDBAction(Mode aMode)
+  : DBAction(aMode)
+{
+}
+
+SyncDBAction::~SyncDBAction()
+{
+}
+
+void
+SyncDBAction::RunWithDBOnTarget(Resolver* aResolver,
+                                const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
+                                mozIStorageConnection* aConn)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aResolver);
+  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+
+  nsresult rv = RunSyncWithDBOnTarget(aQuotaInfo, aDBDir, aConn);
+  aResolver->Resolve(rv);
+}
+
+// static
+nsresult
+OpenDBConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
+                 mozIStorageConnection** aConnOut)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConnOut);
+
+  nsCOMPtr<mozIStorageConnection> conn;
+
   nsCOMPtr<nsIFile> dbFile;
-  rv = aDBDir->Clone(getter_AddRefs(dbFile));
+  nsresult rv = aDBDir->Clone(getter_AddRefs(dbFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = dbFile->Append(NS_LITERAL_STRING("caches.sqlite"));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  bool exists = false;
   rv = dbFile->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Use our default file:// protocol handler directly to construct the database
   // URL.  This avoids any problems if a plugin registers a custom file://
   // handler.  If such a custom handler used javascript, then we would have a
   // bad time running off the main thread here.
   RefPtr<nsFileProtocolHandler> handler = new nsFileProtocolHandler();
@@ -182,54 +249,11 @@ DBAction::OpenConnection(const QuotaInfo
   rv = db::InitializeConnection(conn);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   conn.forget(aConnOut);
 
   return rv;
 }
 
-nsresult
-DBAction::WipeDatabase(const QuotaInfo& aQuotaInfo, nsIFile* aDBFile,
-                       nsIFile* aDBDir)
-{
-  MOZ_DIAGNOSTIC_ASSERT(aDBFile);
-  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
-
-  nsresult rv = RemoveNsIFile(aQuotaInfo, aDBFile);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Note, the -wal journal file will be automatically deleted by sqlite when
-  // the new database is created.  No need to explicitly delete it here.
-
-  // Delete the morgue as well.
-  rv = BodyDeleteDir(aQuotaInfo, aDBDir);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  return rv;
-}
-
-SyncDBAction::SyncDBAction(Mode aMode)
-  : DBAction(aMode)
-{
-}
-
-SyncDBAction::~SyncDBAction()
-{
-}
-
-void
-SyncDBAction::RunWithDBOnTarget(Resolver* aResolver,
-                                const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
-                                mozIStorageConnection* aConn)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_DIAGNOSTIC_ASSERT(aResolver);
-  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
-  MOZ_DIAGNOSTIC_ASSERT(aConn);
-
-  nsresult rv = RunSyncWithDBOnTarget(aQuotaInfo, aDBDir, aConn);
-  aResolver->Resolve(rv);
-}
-
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/DBAction.h
+++ b/dom/cache/DBAction.h
@@ -13,16 +13,20 @@
 
 class mozIStorageConnection;
 class nsIFile;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+nsresult
+OpenDBConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
+                 mozIStorageConnection** aConnOut);
+
 class DBAction : public Action
 {
 protected:
   // The mode specifies whether the database should already exist or if its
   // ok to create a new database.
   enum Mode
   {
     Existing,
@@ -44,19 +48,16 @@ protected:
 private:
   virtual void
   RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
               Data* aOptionalData) override;
 
   nsresult OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aQuotaDir,
                           mozIStorageConnection** aConnOut);
 
-  nsresult WipeDatabase(const QuotaInfo& aQuotaInfo, nsIFile* aDBFile,
-                        nsIFile* aDBDir);
-
   const Mode mMode;
 };
 
 class SyncDBAction : public DBAction
 {
 protected:
   explicit SyncDBAction(Mode aMode);
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/DBSchema.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/HeadersBinding.h"
 #include "mozilla/dom/InternalHeaders.h"
+#include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
@@ -30,17 +31,17 @@
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 namespace db {
 const int32_t kFirstShippedSchemaVersion = 15;
 namespace {
 // Update this whenever the DB schema is changed.
-const int32_t kLatestSchemaVersion = 25;
+const int32_t kLatestSchemaVersion = 26;
 // ---------
 // The following constants define the SQL schema.  These are defined in the
 // same order the SQL should be executed in CreateOrMigrateSchema().  They are
 // broken out as constants for convenient use in validation and migration.
 // ---------
 // The caches table is the single source of truth about what Cache
 // objects exist for the origin.  The contents of the Cache are stored
 // in the entries table that references back to caches.
@@ -96,17 +97,18 @@ const char* const kTableEntries =
     "response_headers_guard INTEGER NOT NULL, "
     "response_body_id TEXT NULL, "
     "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
     "response_principal_info TEXT NOT NULL, "
     "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
     "request_redirect INTEGER NOT NULL, "
     "request_referrer_policy INTEGER NOT NULL, "
     "request_integrity TEXT NOT NULL, "
-    "request_url_fragment TEXT NOT NULL"
+    "request_url_fragment TEXT NOT NULL, "
+    "response_padding_size INTEGER NULL "
     // New columns must be added at the end of table to migrate and
     // validate properly.
   ")";
 // Create an index to support the QueryCache() matching algorithm.  This
 // needs to quickly find entries in a given Cache that match the request
 // URL.  The url query is separated in order to support the ignoreSearch
 // option.  Finally, we index hashes of the URL values instead of the
 // actual strings to avoid excessive disk bloat.  The index will duplicate
@@ -314,16 +316,17 @@ static nsresult QueryCache(mozIStorageCo
                            uint32_t aMaxResults = UINT32_MAX);
 static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
                                   const CacheRequest& aRequest,
                                   EntryId entryId, bool* aSuccessOut);
 static nsresult DeleteEntries(mozIStorageConnection* aConn,
                               const nsTArray<EntryId>& aEntryIdList,
                               nsTArray<nsID>& aDeletedBodyIdListOut,
                               nsTArray<IdCount>& aDeletedSecurityIdListOut,
+                              int64_t* aDeletedPaddingSizeOut,
                               uint32_t aPos=0, int32_t aLen=-1);
 static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
                                    nsICryptoHash* aCrypto,
                                    const nsACString& aData, int32_t *aIdOut);
 static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
                                    int32_t aCount);
 static nsresult DeleteSecurityInfoList(mozIStorageConnection* aConn,
                                        const nsTArray<IdCount>& aDeletedStorageIdList);
@@ -593,33 +596,38 @@ CreateCacheId(mozIStorageConnection* aCo
   rv = state->GetInt64(0, aCacheIdOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult
 DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
-              nsTArray<nsID>& aDeletedBodyIdListOut)
+              nsTArray<nsID>& aDeletedBodyIdListOut,
+              int64_t* aDeletedPaddingSizeOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   // Delete the bodies explicitly as we need to read out the body IDs
   // anyway.  These body IDs must be deleted one-by-one as content may
   // still be referencing them invidivually.
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryAll(aConn, aCacheId, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Delete the remainder of the cache using cascade semantics.
   nsCOMPtr<mozIStorageStatement> state;
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM caches WHERE id=:id;"
   ), getter_AddRefs(state));
@@ -686,16 +694,47 @@ FindOrphanedCacheIds(mozIStorageConnecti
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     aOrphanedListOut.AppendElement(cacheId);
   }
 
   return rv;
 }
 
 nsresult
+FindOverallPaddingSize(mozIStorageConnection* aConn,
+                       int64_t* aOverallPaddingSizeOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aOverallPaddingSizeOut);
+
+  nsCOMPtr<mozIStorageStatement> state;
+  nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT response_padding_size FROM entries "
+    "WHERE response_padding_size IS NOT NULL;"
+  ), getter_AddRefs(state));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  int64_t overallPaddingSize = 0;
+  bool hasMoreData = false;
+  while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
+    int64_t padding_size = 0;
+    rv = state->GetInt64(0, &padding_size);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    MOZ_DIAGNOSTIC_ASSERT(padding_size >= 0);
+    MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - padding_size >= overallPaddingSize);
+    overallPaddingSize += padding_size;
+  }
+
+  *aOverallPaddingSizeOut = overallPaddingSize;
+
+  return rv;
+}
+
+nsresult
 GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT request_body_id, response_body_id FROM entries;"
@@ -787,69 +826,79 @@ CacheMatchAll(mozIStorageConnection* aCo
 }
 
 nsresult
 CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
          const CacheRequest& aRequest,
          const nsID* aRequestBodyId,
          const CacheResponse& aResponse,
          const nsID* aResponseBodyId,
-         nsTArray<nsID>& aDeletedBodyIdListOut)
+         nsTArray<nsID>& aDeletedBodyIdListOut,
+         int64_t* aDeletedPaddingSizeOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   CacheQueryParams params(false, false, false, false,
                            NS_LITERAL_STRING(""));
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = InsertEntry(aConn, aCacheId, aRequest, aRequestBodyId, aResponse,
                    aResponseBodyId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Delete the security values after doing the insert to avoid churning
   // the security table when its not necessary.
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   return rv;
 }
 
 nsresult
 CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const CacheQueryParams& aParams,
-            nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
+            nsTArray<nsID>& aDeletedBodyIdListOut,
+            int64_t* aDeletedPaddingSizeOut, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
   MOZ_DIAGNOSTIC_ASSERT(aSuccessOut);
 
   *aSuccessOut = false;
 
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (matches.IsEmpty()) {
     return rv;
   }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
+  int64_t deletedPaddingSize = 0;
   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
-                     deletedSecurityIdList);
+                     deletedSecurityIdList, &deletedPaddingSize);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  *aDeletedPaddingSizeOut = deletedPaddingSize;
+
   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aSuccessOut = true;
 
   return rv;
 }
 
@@ -1328,63 +1377,78 @@ MatchByVaryHeader(mozIStorageConnection*
   return rv;
 }
 
 nsresult
 DeleteEntries(mozIStorageConnection* aConn,
               const nsTArray<EntryId>& aEntryIdList,
               nsTArray<nsID>& aDeletedBodyIdListOut,
               nsTArray<IdCount>& aDeletedSecurityIdListOut,
+              int64_t* aDeletedPaddingSizeOut,
               uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aDeletedPaddingSizeOut);
 
   if (aEntryIdList.IsEmpty()) {
     return NS_OK;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(aPos < aEntryIdList.Length());
 
   if (aLen < 0) {
     aLen = aEntryIdList.Length() - aPos;
   }
 
   // Sqlite limits the number of entries allowed for an IN clause,
   // so split up larger operations.
   if (aLen > kMaxEntriesPerStatement) {
+    int64_t overallDeletedPaddingSize = 0;
     uint32_t curPos = aPos;
     int32_t remaining = aLen;
     while (remaining > 0) {
+      int64_t deletedPaddingSize = 0;
       int32_t max = kMaxEntriesPerStatement;
       int32_t curLen = std::min(max, remaining);
       nsresult rv = DeleteEntries(aConn, aEntryIdList, aDeletedBodyIdListOut,
-                                  aDeletedSecurityIdListOut, curPos, curLen);
+                                  aDeletedSecurityIdListOut,
+                                  &deletedPaddingSize, curPos, curLen);
       if (NS_FAILED(rv)) { return rv; }
 
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - deletedPaddingSize >=
+                            overallDeletedPaddingSize);
+      overallDeletedPaddingSize += deletedPaddingSize;
       curPos += curLen;
       remaining -= curLen;
     }
+
+    *aDeletedPaddingSizeOut += overallDeletedPaddingSize;
     return NS_OK;
   }
 
   nsCOMPtr<mozIStorageStatement> state;
   nsAutoCString query(
-    "SELECT request_body_id, response_body_id, response_security_info_id "
+    "SELECT "
+      "request_body_id, "
+      "response_body_id, "
+      "response_security_info_id, "
+      "response_padding_size "
     "FROM entries WHERE id IN ("
   );
   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
   query.AppendLiteral(")");
 
   nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  int64_t overallPaddingSize = 0;
   bool hasMoreData = false;
   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     // extract 0 to 2 nsID structs per row
     for (uint32_t i = 0; i < 2; ++i) {
       bool isNull = false;
 
       rv = state->GetIsNull(i, &isNull);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1418,18 +1482,34 @@ DeleteEntries(mozIStorageConnection* aCo
         }
       }
 
       // Otherwise add a new entry for this ID with a count of 1
       if (!found) {
         aDeletedSecurityIdListOut.AppendElement(IdCount(securityId));
       }
     }
+
+    // It's possible to have null padding size for non-opaque response
+    rv = state->GetIsNull(3, &isNull);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    if (!isNull) {
+      int64_t paddingSize = 0;
+      rv = state->GetInt64(3, &paddingSize);
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+      MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - overallPaddingSize >= paddingSize);
+      overallPaddingSize += paddingSize;
+    }
   }
 
+  *aDeletedPaddingSizeOut = overallPaddingSize;
+
   // Dependent records removed via ON DELETE CASCADE
 
   query = NS_LITERAL_CSTRING(
     "DELETE FROM entries WHERE id IN ("
   );
   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
   query.AppendLiteral(")");
 
@@ -1665,16 +1745,17 @@ InsertEntry(mozIStorageConnection* aConn
       "request_body_id, "
       "response_type, "
       "response_status, "
       "response_status_text, "
       "response_headers_guard, "
       "response_body_id, "
       "response_security_info_id, "
       "response_principal_info, "
+      "response_padding_size, "
       "cache_id "
     ") VALUES ("
       ":request_method, "
       ":request_url_no_query, "
       ":request_url_no_query_hash, "
       ":request_url_query, "
       ":request_url_query_hash, "
       ":request_url_fragment, "
@@ -1690,16 +1771,17 @@ InsertEntry(mozIStorageConnection* aConn
       ":request_body_id, "
       ":response_type, "
       ":response_status, "
       ":response_status_text, "
       ":response_headers_guard, "
       ":response_body_id, "
       ":response_security_info_id, "
       ":response_principal_info, "
+      ":response_padding_size, "
       ":cache_id "
     ");"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_method"),
                                    aRequest.method());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1807,16 +1889,28 @@ InsertEntry(mozIStorageConnection* aConn
     cInfo.attrs().CreateSuffix(suffix);
     serializedInfo.Append(suffix);
   }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
                                    serializedInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  if (aResponse.paddingSize() == InternalResponse::UNKNOWN_PADDING_SIZE) {
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.type() != ResponseType::Opaque);
+    rv = state->BindNullByName(NS_LITERAL_CSTRING("response_padding_size"));
+  } else {
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.paddingSize() >= 0);
+    MOZ_DIAGNOSTIC_ASSERT(aResponse.type() == ResponseType::Opaque);
+
+    rv = state->BindInt64ByName(NS_LITERAL_CSTRING("response_padding_size"),
+                                aResponse.paddingSize());
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT last_insert_rowid()"
@@ -1892,17 +1986,17 @@ InsertEntry(mozIStorageConnection* aConn
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   const nsTArray<nsCString>& responseUrlList = aResponse.urlList();
   for (uint32_t i = 0; i < responseUrlList.Length(); ++i) {
     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url"),
                                      responseUrlList[i]);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-    rv = state->BindInt64ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
+    rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = state->Execute();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   return rv;
 }
@@ -1919,16 +2013,17 @@ ReadResponse(mozIStorageConnection* aCon
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "entries.response_type, "
       "entries.response_status, "
       "entries.response_status_text, "
       "entries.response_headers_guard, "
       "entries.response_body_id, "
       "entries.response_principal_info, "
+      "entries.response_padding_size, "
       "security_info.data "
     "FROM entries "
     "LEFT OUTER JOIN security_info "
     "ON entries.response_security_info_id=security_info.id "
     "WHERE entries.id=:id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1980,17 +2075,37 @@ ReadResponse(mozIStorageConnection* aCon
       NS_WARNING("Something went wrong parsing a serialized principal!");
       return NS_ERROR_FAILURE;
     }
 
     aSavedResponseOut->mValue.principalInfo() =
       mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
   }
 
-  rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
+  bool nullPadding = false;
+  rv = state->GetIsNull(6, &nullPadding);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  if (nullPadding) {
+    MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() !=
+                          ResponseType::Opaque);
+    aSavedResponseOut->mValue.paddingSize() =
+      InternalResponse::UNKNOWN_PADDING_SIZE;
+  } else {
+    MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() ==
+                          ResponseType::Opaque);
+    int64_t paddingSize = 0;
+    rv = state->GetInt64(6, &paddingSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);
+    aSavedResponseOut->mValue.paddingSize() = paddingSize;
+  }
+
+  rv = state->GetBlobAsUTF8String(7, aSavedResponseOut->mValue.channelInfo().securityInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM response_headers "
     "WHERE entry_id=:entry_id;"
@@ -2477,28 +2592,30 @@ nsresult MigrateFrom16To17(mozIStorageCo
 nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom24To25(mozIStorageConnection* aConn, bool& aRewriteSchema);
+nsresult MigrateFrom25To26(mozIStorageConnection* aConn, bool& aRewriteSchema);
 // Configure migration functions to run for the given starting version.
 Migration sMigrationList[] = {
   Migration(15, MigrateFrom15To16),
   Migration(16, MigrateFrom16To17),
   Migration(17, MigrateFrom17To18),
   Migration(18, MigrateFrom18To19),
   Migration(19, MigrateFrom19To20),
   Migration(20, MigrateFrom20To21),
   Migration(21, MigrateFrom21To22),
   Migration(22, MigrateFrom22To23),
   Migration(23, MigrateFrom23To24),
   Migration(24, MigrateFrom24To25),
+  Migration(25, MigrateFrom25To26),
 };
 uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
 nsresult
 RewriteEntriesSchema(mozIStorageConnection* aConn)
 {
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA writable_schema = ON"
   ));
@@ -3025,13 +3142,40 @@ nsresult MigrateFrom24To25(mozIStorageCo
   MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // The only change between 24 and 25 was a new nsIContentPolicy type.
   nsresult rv = aConn->SetSchemaVersion(25);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   return rv;
 }
 
+nsresult MigrateFrom25To26(mozIStorageConnection* aConn, bool& aRewriteSchema)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+
+  // Add the response_padding_size column.
+  // Note: only opaque repsonse should be non-null interger.
+  nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE entries "
+    "ADD COLUMN response_padding_size INTEGER NULL "
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE entries SET response_padding_size = 0"
+    "WHERE response_type = 4" // opaque response
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = aConn->SetSchemaVersion(26);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  aRewriteSchema = true;
+
+  return rv;
+}
+
 } // anonymous namespace
 } // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/DBSchema.h
+++ b/dom/cache/DBSchema.h
@@ -37,28 +37,33 @@ CreateOrMigrateSchema(mozIStorageConnect
 nsresult
 InitializeConnection(mozIStorageConnection* aConn);
 
 nsresult
 CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut);
 
 nsresult
 DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
-              nsTArray<nsID>& aDeletedBodyIdListOut);
+              nsTArray<nsID>& aDeletedBodyIdListOut,
+              int64_t* aDeletedPaddingSizeOut);
 
 // TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
 nsresult
 IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
                 bool* aOrphanedOut);
 
 nsresult
 FindOrphanedCacheIds(mozIStorageConnection* aConn,
                      nsTArray<CacheId>& aOrphanedListOut);
 
 nsresult
+FindOverallPaddingSize(mozIStorageConnection* aConn,
+                       int64_t* aOverallPaddingSizeOut);
+
+nsresult
 GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut);
 
 nsresult
 CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
            const CacheRequest& aRequest, const CacheQueryParams& aParams,
            bool* aFoundResponseOut, SavedResponse* aSavedResponseOut);
 
 nsresult
@@ -68,23 +73,25 @@ CacheMatchAll(mozIStorageConnection* aCo
               nsTArray<SavedResponse>& aSavedResponsesOut);
 
 nsresult
 CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
          const CacheRequest& aRequest,
          const nsID* aRequestBodyId,
          const CacheResponse& aResponse,
          const nsID* aResponseBodyId,
-         nsTArray<nsID>& aDeletedBodyIdListOut);
+         nsTArray<nsID>& aDeletedBodyIdListOut,
+         int64_t* aDeletedPaddingSizeOut);
 
 nsresult
 CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const CacheQueryParams& aParams,
             nsTArray<nsID>& aDeletedBodyIdListOut,
+            int64_t* aDeletedPaddingSizeOut,
             bool* aSuccessOut);
 
 nsresult
 CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
           const CacheRequestOrVoid& aRequestOrVoid,
           const CacheQueryParams& aParams,
           nsTArray<SavedRequest>& aSavedRequestsOut);
 
--- a/dom/cache/FileUtils.cpp
+++ b/dom/cache/FileUtils.cpp
@@ -1,49 +1,77 @@
 /* -*- 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 "mozilla/dom/cache/FileUtils.h"
 
+#include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/quota/FileStreams.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/SnappyCompressOutputStream.h"
 #include "mozilla/Unused.h"
+#include "nsIBinaryInputStream.h"
+#include "nsIBinaryOutputStream.h"
 #include "nsIFile.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
+#include "nsNetUtil.h"
 #include "nsISimpleEnumerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+#define PADDING_FILE_NAME ".padding"
+#define PADDING_TMP_FILE_NAME ".padding-tmp"
+
 using mozilla::dom::quota::FileInputStream;
 using mozilla::dom::quota::FileOutputStream;
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
 using mozilla::dom::quota::QuotaManager;
+using mozilla::dom::quota::QuotaObject;
 
 namespace {
 
+// Const variable for generate padding size.
+// XXX This will be tweaked to something more meaningful in Bug 1383656.
+const int64_t kRoundUpNumber = 20480;
+
 enum BodyFileType
 {
   BODY_FILE_FINAL,
   BODY_FILE_TMP
 };
 
 nsresult
 BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
              nsIFile** aBodyFileOut);
 
+int64_t
+RoundUp(const int64_t aX, const int64_t aY);
+
+// The alogrithm for generating padding refers to the mitigation approach in
+// https://github.com/whatwg/storage/issues/31.
+// First, generate a random number between 0 and 100kB.
+// Next, round up the sum of random number and response size to the nearest
+// 20kB.
+// Finally, the virtual padding size will be the result minus the response size.
+int64_t
+BodyGeneratePadding(const int64_t aBodyFileSize, const uint32_t aPaddingInfo);
+
+nsresult
+LockedDirectoryPaddingWrite(nsIFile* aBaseDir, DirPaddingFile aPaddingFileType,
+                            int64_t aPaddingSize);
+
 } // namespace
 
 // static
 nsresult
 BodyCreateDir(nsIFile* aBaseDir)
 {
   MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
@@ -243,16 +271,55 @@ BodyOpen(const QuotaInfo& aQuotaInfo, ns
 
   fileStream.forget(aStreamOut);
 
   return rv;
 }
 
 // static
 nsresult
+BodyMaybeUpdatePaddingSize(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
+                           const nsID& aId, const uint32_t aPaddingInfo,
+                           int64_t* aPaddingSizeOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aPaddingSizeOut);
+
+  nsCOMPtr<nsIFile> bodyFile;
+  nsresult rv =
+    BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(bodyFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  MOZ_DIAGNOSTIC_ASSERT(bodyFile);
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_DIAGNOSTIC_ASSERT(quotaManager);
+
+  int64_t fileSize = 0;
+  RefPtr<QuotaObject> quotaObject =
+    quotaManager->GetQuotaObject(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
+                                 aQuotaInfo.mOrigin, bodyFile, &fileSize);
+  MOZ_DIAGNOSTIC_ASSERT(quotaObject);
+  MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
+
+  if (*aPaddingSizeOut == InternalResponse::UNKNOWN_PADDING_SIZE) {
+    *aPaddingSizeOut = BodyGeneratePadding(fileSize, aPaddingInfo);
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(*aPaddingSizeOut >= 0);
+
+  if (!quotaObject->IncreaseSize(*aPaddingSizeOut)) {
+    return NS_ERROR_FILE_NO_DEVICE_SPACE;
+  }
+
+  return rv;
+}
+
+// static
+nsresult
 BodyDeleteFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
                 const nsTArray<nsID>& aIdList)
 {
   nsresult rv = NS_OK;
 
   for (uint32_t i = 0; i < aIdList.Length(); ++i) {
     nsCOMPtr<nsIFile> tmpFile;
     rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_TMP,
@@ -305,16 +372,72 @@ BodyIdToFile(nsIFile* aBaseDir, const ns
   }
 
   rv = (*aBodyFileOut)->Append(fileName);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
+int64_t
+RoundUp(const int64_t aX, const int64_t aY)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aX >= 0);
+  MOZ_DIAGNOSTIC_ASSERT(aY > 0);
+
+  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - ((aX - 1) / aY) * aY >= aY);
+  return aY + ((aX - 1) / aY) * aY;
+}
+
+int64_t
+BodyGeneratePadding(const int64_t aBodyFileSize, const uint32_t aPaddingInfo)
+{
+  // Generate padding
+  int64_t randomSize = static_cast<int64_t>(aPaddingInfo);
+  MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - aBodyFileSize >= randomSize);
+  randomSize += aBodyFileSize;
+
+  return RoundUp(randomSize, kRoundUpNumber) - aBodyFileSize;
+}
+
+nsresult
+LockedDirectoryPaddingWrite(nsIFile* aBaseDir, DirPaddingFile aPaddingFileType,
+                            int64_t aPaddingSize)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aPaddingSize >= 0);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aBaseDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  if (aPaddingFileType == DirPaddingFile::TMP_FILE) {
+    rv = file->Append(NS_LITERAL_STRING(PADDING_TMP_FILE_NAME));
+  } else {
+    rv = file->Append(NS_LITERAL_STRING(PADDING_FILE_NAME));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<nsIBinaryOutputStream> binaryStream =
+    do_CreateInstance("@mozilla.org/binaryoutputstream;1");
+  if (NS_WARN_IF(!binaryStream)) { return NS_ERROR_FAILURE; }
+
+  rv = binaryStream->SetOutputStream(outputStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = binaryStream->Write64(aPaddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
 } // namespace
 
 nsresult
 BodyDeleteOrphanedFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
                         nsTArray<nsID>& aKnownBodyIdList)
 {
   MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
@@ -543,21 +666,257 @@ RemoveNsIFile(const QuotaInfo& aQuotaInf
       rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
     return NS_OK;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aFile->Remove( /* recursive */ false);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  if (fileSize > 0) {
+    DecreaseUsageForQuotaInfo(aQuotaInfo, fileSize);
+  }
+
+  return rv;
+}
+
+// static
+void
+DecreaseUsageForQuotaInfo(const QuotaInfo& aQuotaInfo,
+                          const int64_t& aUpdatingSize)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aUpdatingSize > 0);
+
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_DIAGNOSTIC_ASSERT(quotaManager);
 
   quotaManager->DecreaseUsageForOrigin(PERSISTENCE_TYPE_DEFAULT,
                                        aQuotaInfo.mGroup, aQuotaInfo.mOrigin,
-                                       fileSize);
+                                       aUpdatingSize);
+}
+
+// static
+bool
+DirectoryPaddingFileExists(nsIFile* aBaseDir, DirPaddingFile aPaddingFileType)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aBaseDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
+
+  nsString fileName;
+  if (aPaddingFileType == DirPaddingFile::TMP_FILE) {
+    fileName = NS_LITERAL_STRING(PADDING_TMP_FILE_NAME);
+  } else {
+    fileName = NS_LITERAL_STRING(PADDING_FILE_NAME);
+  }
+
+  rv = file->Append(fileName);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
+
+  bool exists = false;
+  rv = file->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
+
+  return exists;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingGet(nsIFile* aBaseDir, int64_t* aPaddingSizeOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aPaddingSizeOut);
+  MOZ_DIAGNOSTIC_ASSERT(!DirectoryPaddingFileExists(aBaseDir,
+                                                    DirPaddingFile::TMP_FILE));
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aBaseDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = file->Append(NS_LITERAL_STRING(PADDING_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream =
+    do_CreateInstance("@mozilla.org/binaryinputstream;1");
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = binaryStream->SetInputStream(bufferedStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  uint64_t paddingSize = 0;
+  rv = binaryStream->Read64(&paddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  *aPaddingSizeOut = paddingSize;
+
+  return rv;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingInit(nsIFile* aBaseDir)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+
+  nsresult rv = LockedDirectoryPaddingWrite(aBaseDir, DirPaddingFile::FILE, 0);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
 
   return rv;
 }
 
+// static
+nsresult
+LockedUpdateDirectoryPaddingFile(nsIFile* aBaseDir,
+                                 mozIStorageConnection* aConn,
+                                 const int64_t aIncreaseSize,
+                                 const int64_t aDecreaseSize,
+                                 const bool aTemporaryFileExist)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aIncreaseSize >= 0);
+  MOZ_DIAGNOSTIC_ASSERT(aDecreaseSize >= 0);
+
+  int64_t currentPaddingSize = 0;
+  nsresult rv = LockedDirectoryPaddingGet(aBaseDir, &currentPaddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv)) || aTemporaryFileExist) {
+    // Fail to read padding size from the dir padding file, so try to restore.
+    if (rv != NS_ERROR_FILE_NOT_FOUND &&
+        rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
+      // Not delete the temporary padding file here, because we're going to
+      // overwrite it below anyway.
+      rv = LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE);
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+    }
+
+    // We don't need to add the aIncreaseSize or aDecreaseSize here, because
+    // it's already encompassed within the database.
+    rv = db::FindOverallPaddingSize(aConn, &currentPaddingSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  } else {
+    if (aIncreaseSize > 0) {
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - currentPaddingSize >= aIncreaseSize);
+      currentPaddingSize += aIncreaseSize;
+    }
+
+    if (aDecreaseSize > 0) {
+      MOZ_DIAGNOSTIC_ASSERT(currentPaddingSize >= aDecreaseSize);
+      currentPaddingSize -= aDecreaseSize;
+    }
+
+#ifdef DEBUG
+    int64_t paddingSizeFromDB = 0;
+    rv = db::FindOverallPaddingSize(aConn, &paddingSizeFromDB);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    MOZ_DIAGNOSTIC_ASSERT(paddingSizeFromDB == currentPaddingSize);
+#endif // DEBUG
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(currentPaddingSize >= 0);
+
+  rv = LockedDirectoryPaddingTemporaryWrite(aBaseDir, currentPaddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingTemporaryWrite(nsIFile* aBaseDir, int64_t aPaddingSize)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aPaddingSize >= 0);
+
+  nsresult rv = LockedDirectoryPaddingWrite(aBaseDir, DirPaddingFile::TMP_FILE,
+                                            aPaddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingFinalizeWrite(nsIFile* aBaseDir)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(DirectoryPaddingFileExists(aBaseDir,
+                                                   DirPaddingFile::TMP_FILE));
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aBaseDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = file->Append(NS_LITERAL_STRING(PADDING_TMP_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = file->RenameTo(nullptr, NS_LITERAL_STRING(PADDING_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingRestore(nsIFile* aBaseDir, mozIStorageConnection* aConn)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+
+  // The content of padding file is untrusted, so remove it here.
+  nsresult rv = LockedDirectoryPaddingDeleteFile(aBaseDir,
+                                                 DirPaddingFile::TMP_FILE);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  int64_t paddingSize = 0;
+  rv = db::FindOverallPaddingSize(aConn, &paddingSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  MOZ_DIAGNOSTIC_ASSERT(paddingSize >= 0);
+
+  LockedDirectoryPaddingWrite(aBaseDir, DirPaddingFile::FILE, paddingSize);
+
+  return rv;
+}
+
+// static
+nsresult
+LockedDirectoryPaddingDeleteFile(nsIFile* aBaseDir,
+                                 DirPaddingFile aPaddingFileType)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aBaseDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  if (aPaddingFileType == DirPaddingFile::TMP_FILE) {
+    rv = file->Append(NS_LITERAL_STRING(PADDING_TMP_FILE_NAME));
+  } else {
+    rv = file->Append(NS_LITERAL_STRING(PADDING_FILE_NAME));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = file->Remove( /* recursive */ false);
+  if (rv == NS_ERROR_FILE_NOT_FOUND ||
+      rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
+    return NS_OK;
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/FileUtils.h
+++ b/dom/cache/FileUtils.h
@@ -14,16 +14,22 @@
 
 struct nsID;
 class nsIFile;
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+enum DirPaddingFile
+{
+  FILE,
+  TMP_FILE
+};
+
 nsresult
 BodyCreateDir(nsIFile* aBaseDir);
 
 // Note that this function can only be used during the initialization of the
 // database.  We're unlikely to be able to delete the DB successfully past
 // that point due to the file being in use.
 nsresult
 BodyDeleteDir(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir);
@@ -43,16 +49,20 @@ BodyCancelWrite(nsIFile* aBaseDir, nsISu
 nsresult
 BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
 
 nsresult
 BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
          nsIInputStream** aStreamOut);
 
 nsresult
+BodyMaybeUpdatePaddingSize(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
+                           const nsID& aId, int64_t* aPaddingSizeOut);
+
+nsresult
 BodyDeleteFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
                 const nsTArray<nsID>& aIdList);
 
 nsresult
 BodyDeleteOrphanedFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
                         nsTArray<nsID>& aKnownBodyIdList);
 
 nsresult
@@ -65,13 +75,59 @@ bool
 MarkerFileExists(const QuotaInfo& aQuotaInfo);
 
 nsresult
 RemoveNsIFileRecursively(const QuotaInfo& aQuotaInfo, nsIFile* aFile);
 
 nsresult
 RemoveNsIFile(const QuotaInfo& aQuotaInfo, nsIFile* aFile);
 
+void
+DecreaseUsageForQuotaInfo(const QuotaInfo& aQuotaInfo,
+                          const int64_t& aUpdatingSize);
+
+/**
+ * This function is used to check if the directory padding file is existed.
+ */
+
+bool
+DirectoryPaddingFileExists(nsIFile* aBaseDir, DirPaddingFile aPaddingFileType);
+
+/**
+ *
+ * The functions below are used to read/write/delete the directory padding file
+ * after acquiring the mutex lock. The mutex lock is held by
+ * CacheQuotaClient to prevent multi-thread accessing issue. To avoid deadlock,
+ * these functions should only access by static functions in
+ * dom/cache/QuotaClient.cpp.
+ *
+ */
+
+nsresult
+LockedDirectoryPaddingGet(nsIFile* aBaseDir, int64_t* aPaddingSizeOut);
+
+nsresult
+LockedDirectoryPaddingInit(nsIFile* aBaseDir);
+
+nsresult
+LockedUpdateDirectoryPaddingFile(nsIFile* aBaseDir,
+                                 mozIStorageConnection* aConn,
+                                 const int64_t aIncreaseSize,
+                                 const int64_t aDecreaseSize,
+                                 const bool aTemporaryFileExist);
+
+nsresult
+LockedDirectoryPaddingTemporaryWrite(nsIFile* aBaseDir, int64_t aPaddingSize);
+
+nsresult
+LockedDirectoryPaddingFinalizeWrite(nsIFile* aBaseDir);
+
+nsresult
+LockedDirectoryPaddingRestore(nsIFile* aBaseDir, mozIStorageConnection* aConn);
+
+nsresult
+LockedDirectoryPaddingDeleteFile(nsIFile* aBaseDir,
+                                 DirPaddingFile aPaddingFileType);
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_FileUtils_h
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/cache/DBAction.h"
 #include "mozilla/dom/cache/DBSchema.h"
 #include "mozilla/dom/cache/FileUtils.h"
 #include "mozilla/dom/cache/ManagerId.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/SavedTypes.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/dom/cache/Types.h"
+#include "mozilla/dom/cache/QuotaClient.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozStorageHelper.h"
 #include "nsIInputStream.h"
 #include "nsID.h"
 #include "nsIFile.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsTObserverArray.h"
@@ -45,16 +46,18 @@ public:
   SetupAction()
     : SyncDBAction(DBAction::Create)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
+    MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+
     nsresult rv = BodyCreateDir(aDBDir);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     // executes in its own transaction
     rv = db::CreateOrMigrateSchema(aConn);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     // If the Context marker file exists, then the last session was
@@ -72,31 +75,56 @@ public:
       mozStorageTransaction trans(aConn, false,
                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
       // Clean up orphaned Cache objects
       AutoTArray<CacheId, 8> orphanedCacheIdList;
       nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+      int64_t overallDeletedPaddingSize = 0;
       for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
         AutoTArray<nsID, 16> deletedBodyIdList;
-        rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList);
+        int64_t deletedPaddingSize = 0;
+        rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList,
+                               &deletedPaddingSize);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
         rv = BodyDeleteFiles(aQuotaInfo, aDBDir, deletedBodyIdList);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+        if (deletedPaddingSize > 0) {
+          DecreaseUsageForQuotaInfo(aQuotaInfo, deletedPaddingSize);
+        }
+
+        MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - deletedPaddingSize >=
+                              overallDeletedPaddingSize);
+        overallDeletedPaddingSize += deletedPaddingSize;
       }
 
       // Clean up orphaned body objects
       AutoTArray<nsID, 64> knownBodyIdList;
       rv = db::GetKnownBodyIds(aConn, knownBodyIdList);
 
       rv = BodyDeleteOrphanedFiles(aQuotaInfo, aDBDir, knownBodyIdList);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+      // Commit() explicitly here, because we want to ensure the padding file
+      // has the correct content.
+      rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
+                                  overallDeletedPaddingSize,
+                                  [&trans]() mutable { return trans.Commit(); });
+      // We'll restore padding file below, so just warn here if failure happens.
+      Unused << NS_WARN_IF(NS_FAILED(rv));
+    }
+
+    if (DirectoryPaddingFileExists(aDBDir, DirPaddingFile::TMP_FILE) ||
+        !DirectoryPaddingFileExists(aDBDir, DirPaddingFile::FILE)) {
+      rv = RestorePaddingFile(aDBDir, aConn);
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     }
 
     return rv;
   }
 };
 
 // ----------------------------------------------------------------------------
 
@@ -435,47 +463,60 @@ protected:
 // a Cache object that has been orphaned.
 class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
 {
 public:
   DeleteOrphanedCacheAction(Manager* aManager, CacheId aCacheId)
     : SyncDBAction(DBAction::Existing)
     , mManager(aManager)
     , mCacheId(aCacheId)
+    , mDeletedPaddingSize(0)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
+    mQuotaInfo.emplace(aQuotaInfo);
+
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList);
+    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList,
+                                    &mDeletedPaddingSize);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-    rv = trans.Commit();
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+    rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
+                                mDeletedPaddingSize,
+                                [&trans]() mutable { return trans.Commit(); });
+    Unused << NS_WARN_IF(NS_FAILED(rv));
 
     return rv;
   }
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
   {
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
 
+    if (mDeletedPaddingSize > 0) {
+      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
+    }
+
     // ensure we release the manager on the initiating thread
     mManager = nullptr;
   }
 
 private:
   RefPtr<Manager> mManager;
   const CacheId mCacheId;
   nsTArray<nsID> mDeletedBodyIdList;
+  Maybe<QuotaInfo> mQuotaInfo;
+  // Track any pad amount associated with orphaned entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheMatchAction final : public Manager::BaseAction
 {
 public:
   CacheMatchAction(Manager* aManager, ListenerId aListenerId,
@@ -616,16 +657,18 @@ public:
     : DBAction(DBAction::Existing)
     , mManager(aManager)
     , mListenerId(aListenerId)
     , mCacheId(aCacheId)
     , mList(aPutList.Length())
     , mExpectedAsyncCopyCompletions(1)
     , mAsyncResult(NS_OK)
     , mMutex("cache::Manager::CachePutAllAction")
+    , mUpdatedPaddingSize(0)
+    , mDeletedPaddingSize(0)
   {
     MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
 
     for (uint32_t i = 0; i < aPutList.Length(); ++i) {
       Entry* entry = mList.AppendElement();
       entry->mRequest = aPutList[i].request();
@@ -676,17 +719,16 @@ private:
 
       rv = StartStreamCopy(aQuotaInfo, mList[i], ResponseStream,
                            &mExpectedAsyncCopyCompletions);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         break;
       }
     }
 
-
     // Always call OnAsyncCopyComplete() manually here.  This covers the
     // case where there is no async copying and also reports any startup
     // errors correctly.  If we hit an error, then OnAsyncCopyComplete()
     // will cancel any async copying.
     OnAsyncCopyComplete(rv);
   }
 
   // Called once for each asynchronous file copy whether it succeeds or
@@ -757,35 +799,60 @@ private:
       if (e.mRequestStream) {
         rv = BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           DoResolve(rv);
           return;
         }
       }
       if (e.mResponseStream) {
+        // Gerenate padding size for opaque response if needed.
+        if (e.mResponse.type() == ResponseType::Opaque) {
+          // It'll generate padding if we've not set it yet.
+          rv = BodyMaybeUpdatePaddingSize(mQuotaInfo.ref(), mDBDir,
+                                          e.mResponseBodyId,
+                                          e.mResponse.paddingInfo(),
+                                          &e.mResponse.paddingSize());
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            DoResolve(rv);
+            return;
+          }
+
+          MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - e.mResponse.paddingSize() >=
+                                mUpdatedPaddingSize);
+          mUpdatedPaddingSize += e.mResponse.paddingSize();
+        }
+
         rv = BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           DoResolve(rv);
           return;
         }
       }
 
+      int64_t deletedPaddingSize = 0;
       rv = db::CachePut(mConn, mCacheId, e.mRequest,
                         e.mRequestStream ? &e.mRequestBodyId : nullptr,
                         e.mResponse,
                         e.mResponseStream ? &e.mResponseBodyId : nullptr,
-                        mDeletedBodyIdList);
+                        mDeletedBodyIdList, &deletedPaddingSize);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         DoResolve(rv);
         return;
       }
+
+      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - mDeletedPaddingSize >=
+                            deletedPaddingSize);
+      mDeletedPaddingSize += deletedPaddingSize;
     }
 
-    rv = trans.Commit();
+    // Update padding file when it's necessary
+    rv = MaybeUpdatePaddingFile(mDBDir, mConn, mUpdatedPaddingSize,
+                                mDeletedPaddingSize,
+                                [&trans]() mutable { return trans.Commit(); });
     Unused << NS_WARN_IF(NS_FAILED(rv));
 
     DoResolve(rv);
   }
 
   virtual void
   CompleteOnInitiatingThread(nsresult aRv) override
   {
@@ -793,16 +860,20 @@ private:
 
     for (uint32_t i = 0; i < mList.Length(); ++i) {
       mList[i].mRequestStream = nullptr;
       mList[i].mResponseStream = nullptr;
     }
 
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
 
+    if (mDeletedPaddingSize > 0) {
+      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
+    }
+
     Listener* listener = mManager->GetListener(mListenerId);
     mManager = nullptr;
     if (listener) {
       listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
     }
   }
 
   virtual void
@@ -932,16 +1003,19 @@ private:
       MutexAutoLock lock(mMutex);
       MOZ_ASSERT(mCopyContextList.IsEmpty());
     }
 #endif
 
     // Clean up any files we might have written before hitting the error.
     if (NS_FAILED(aRv)) {
       BodyDeleteFiles(mQuotaInfo.ref(), mDBDir, mBodyIdWrittenList);
+      if (mUpdatedPaddingSize > 0) {
+        DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mUpdatedPaddingSize);
+      }
     }
 
     // Must be released on the target thread where it was opened.
     mConn = nullptr;
 
     // Drop our ref to the target thread as we are done with this thread.
     // Also makes our thread assertions catch any incorrect method calls
     // after resolve.
@@ -975,69 +1049,87 @@ private:
   // thread activity is guaranteed complete
   nsTArray<nsID> mDeletedBodyIdList;
 
   // accessed from any thread while mMutex locked
   Mutex mMutex;
   nsTArray<nsCOMPtr<nsISupports>> mCopyContextList;
 
   Maybe<QuotaInfo> mQuotaInfo;
+  // Track how much pad amount has been added for new entries so that it can be
+  // removed if an error occurs.
+  int64_t mUpdatedPaddingSize;
+  // Track any pad amount associated with overwritten entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheDeleteAction final : public Manager::BaseAction
 {
 public:
   CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
                     CacheId aCacheId, const CacheDeleteArgs& aArgs)
     : BaseAction(aManager, aListenerId)
     , mCacheId(aCacheId)
     , mArgs(aArgs)
     , mSuccess(false)
+    , mDeletedPaddingSize(0)
   { }
 
   virtual nsresult
   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                         mozIStorageConnection* aConn) override
   {
+    mQuotaInfo.emplace(aQuotaInfo);
+
     mozStorageTransaction trans(aConn, false,
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     nsresult rv = db::CacheDelete(aConn, mCacheId, mArgs.request(),
                                   mArgs.params(), mDeletedBodyIdList,
-                                  &mSuccess);
+                                  &mDeletedPaddingSize, &mSuccess);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-    rv = trans.Commit();
+    rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
+                                mDeletedPaddingSize,
+                                [&trans]() mutable { return trans.Commit(); });
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mSuccess = false;
       return rv;
     }
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
+
+    if (mDeletedPaddingSize > 0) {
+      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
+    }
+
     aListener->OnOpComplete(Move(aRv), CacheDeleteResult(mSuccess));
   }
 
   virtual bool MatchesCacheId(CacheId aCacheId) const override
   {
     return aCacheId == mCacheId;
   }
 
 private:
   const CacheId mCacheId;
   const CacheDeleteArgs mArgs;
   bool mSuccess;
   nsTArray<nsID> mDeletedBodyIdList;
+  Maybe<QuotaInfo> mQuotaInfo;
+  // Track any pad amount associated with deleted entries.
+  int64_t mDeletedPaddingSize;
 };
 
 // ----------------------------------------------------------------------------
 
 class Manager::CacheKeysAction final : public Manager::BaseAction
 {
 public:
   CacheKeysAction(Manager* aManager, ListenerId aListenerId,
@@ -1277,16 +1369,18 @@ public:
                                         &exists, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     if (!exists) {
       mCacheDeleted = false;
       return NS_OK;
     }
 
+    // Don't delete the removing padding size here, we'll delete it on
+    // DeleteOrphanedCacheAction.
     rv = db::StorageForgetCache(aConn, mNamespace, mArgs.key());
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     mCacheDeleted = true;
     return rv;
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -13,27 +13,34 @@
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 
 namespace {
 
 using mozilla::Atomic;
 using mozilla::dom::ContentParentId;
+using mozilla::dom::cache::DirPaddingFile;
 using mozilla::dom::cache::Manager;
+using mozilla::dom::cache::QuotaInfo;
+using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::Client;
 using mozilla::dom::quota::PersistenceType;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::UsageInfo;
 using mozilla::ipc::AssertIsOnBackgroundThread;
+using mozilla::MutexAutoLock;
+using mozilla::Unused;
 
 static nsresult
 GetBodyUsage(nsIFile* aDir, const Atomic<bool>& aCanceled,
              UsageInfo* aUsageInfo)
 {
+  AssertIsOnIOThread();
+
   nsCOMPtr<nsISimpleEnumerator> entries;
   nsresult rv = aDir->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool hasMore;
   while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore &&
          !aCanceled) {
     nsCOMPtr<nsISupports> entry;
@@ -60,56 +67,105 @@ GetBodyUsage(nsIFile* aDir, const Atomic
     aUsageInfo->AppendToFileUsage(fileSize);
   }
 
   return NS_OK;
 }
 
 class CacheQuotaClient final : public Client
 {
+  static CacheQuotaClient* sInstance;
+
 public:
+  CacheQuotaClient()
+  : mDirPaddingFileMutex("DOMCacheQuotaClient.mDirPaddingFileMutex")
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_DIAGNOSTIC_ASSERT(!sInstance);
+    sInstance = this;
+  }
+
+  static CacheQuotaClient*
+  Get()
+  {
+    MOZ_DIAGNOSTIC_ASSERT(sInstance);
+    return sInstance;
+  }
+
   virtual Type
   GetType() override
   {
     return DOMCACHE;
   }
 
   virtual nsresult
   InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
              const nsACString& aOrigin, const AtomicBool& aCanceled,
              UsageInfo* aUsageInfo) override
   {
+    AssertIsOnIOThread();
+
     // The QuotaManager passes a nullptr UsageInfo if there is no quota being
     // enforced against the origin.
     if (!aUsageInfo) {
       return NS_OK;
     }
 
     return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aCanceled,
                              aUsageInfo);
   }
 
   virtual nsresult
   GetUsageForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
                     const nsACString& aOrigin, const AtomicBool& aCanceled,
                     UsageInfo* aUsageInfo) override
   {
+    AssertIsOnIOThread();
     MOZ_DIAGNOSTIC_ASSERT(aUsageInfo);
 
     QuotaManager* qm = QuotaManager::Get();
     MOZ_DIAGNOSTIC_ASSERT(qm);
 
     nsCOMPtr<nsIFile> dir;
     nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
                                             getter_AddRefs(dir));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = dir->Append(NS_LITERAL_STRING(DOMCACHE_DIRECTORY_NAME));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+    int64_t paddingSize = 0;
+    {
+      // If the tempoary file still exists after locking, it means the previous
+      // action fails, so restore the padding file.
+      MutexAutoLock lock(mDirPaddingFileMutex);
+
+      if (mozilla::dom::cache::
+          DirectoryPaddingFileExists(dir, DirPaddingFile::TMP_FILE) ||
+          NS_WARN_IF(NS_FAILED(mozilla::dom::cache::
+                               LockedDirectoryPaddingGet(dir,
+                                                         &paddingSize)))) {
+        nsCOMPtr<mozIStorageConnection> conn;
+        QuotaInfo quotaInfo;
+        quotaInfo.mGroup = aGroup;
+        quotaInfo.mOrigin = aOrigin;
+        rv = mozilla::dom::cache::
+             OpenDBConnection(quotaInfo, dir, getter_AddRefs(conn));
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+        rv = mozilla::dom::cache::LockedDirectoryPaddingRestore(dir, conn);
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+        rv = mozilla::dom::cache::LockedDirectoryPaddingGet(dir, &paddingSize);
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+      }
+    }
+
+    aUsageInfo->AppendToFileUsage(paddingSize);
+
     nsCOMPtr<nsISimpleEnumerator> entries;
     rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     bool hasMore;
     while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore &&
            !aCanceled) {
       nsCOMPtr<nsISupports> entry;
@@ -151,16 +207,22 @@ public:
         rv = file->GetFileSize(&fileSize);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
         MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
         aUsageInfo->AppendToDatabaseUsage(fileSize);
         continue;
       }
 
+      // Ignore directory padding file
+      if (leafName.EqualsLiteral(PADDING_FILE_NAME) ||
+          leafName.EqualsLiteral(PADDING_TMP_FILE_NAME)) {
+        continue;
+      }
+
       NS_WARNING("Unknown Cache file found!");
     }
 
     return NS_OK;
   }
 
   virtual void
   OnOriginClearCompleted(PersistenceType aPersistenceType,
@@ -210,34 +272,240 @@ public:
   ShutdownWorkThreads() override
   {
     AssertIsOnBackgroundThread();
 
     // spins the event loop and synchronously shuts down all Managers
     Manager::ShutdownAll();
   }
 
+  nsresult
+  UpgradeStorageFrom2_0To3_0(nsIFile* aDirectory) override
+  {
+    AssertIsOnIOThread();
+    MOZ_DIAGNOSTIC_ASSERT(aDirectory);
+
+    MutexAutoLock lock(mDirPaddingFileMutex);
+
+    nsresult rv = mozilla::dom::cache::LockedDirectoryPaddingInit(aDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    return rv;
+  }
+
+  // static
+  template<typename Callable>
+  nsresult
+  MaybeUpdatePaddingFileInternal(nsIFile* aBaseDir,
+                                 mozIStorageConnection* aConn,
+                                 const int64_t aIncreaseSize,
+                                 const int64_t aDecreaseSize,
+                                 Callable aCommitHook)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+    MOZ_DIAGNOSTIC_ASSERT(aConn);
+    MOZ_DIAGNOSTIC_ASSERT(aIncreaseSize >= 0);
+    MOZ_DIAGNOSTIC_ASSERT(aDecreaseSize >= 0);
+
+    nsresult rv;
+
+    // Temporary should be removed at the end of each action. If not, it means
+    // the failure happened.
+    bool temporaryPaddingFileExist =
+      mozilla::dom::cache::DirectoryPaddingFileExists(aBaseDir,
+                                                      DirPaddingFile::TMP_FILE);
+
+    if (aIncreaseSize == aDecreaseSize && !temporaryPaddingFileExist) {
+      // Early return here, since most cache actions won't modify padding size.
+      rv = aCommitHook();
+      Unused << NS_WARN_IF(NS_FAILED(rv));
+      return rv;
+    }
+
+    {
+      MutexAutoLock lock(mDirPaddingFileMutex);
+      rv =
+        mozilla::dom::cache::
+        LockedUpdateDirectoryPaddingFile(aBaseDir, aConn, aIncreaseSize,
+                                         aDecreaseSize,
+                                         temporaryPaddingFileExist);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        mozilla::dom::cache::
+        LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::TMP_FILE);
+        return rv;
+      }
+
+      rv = aCommitHook();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        mozilla::dom::cache::
+        LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::TMP_FILE);
+        return rv;
+      }
+
+      rv = mozilla::dom::cache::LockedDirectoryPaddingFinalizeWrite(aBaseDir);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        // Force restore file next time.
+        mozilla::dom::cache::
+        LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE);
+      }
+    }
+
+    return rv;
+  }
+
+  // static
+  nsresult
+  RestorePaddingFileInternal(nsIFile* aBaseDir, mozIStorageConnection* aConn)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+    MOZ_DIAGNOSTIC_ASSERT(aConn);
+
+    MutexAutoLock lock(mDirPaddingFileMutex);
+
+    nsresult rv =
+      mozilla::dom::cache::LockedDirectoryPaddingRestore(aBaseDir, aConn);
+    Unused << NS_WARN_IF(NS_FAILED(rv));
+
+    return rv;
+  }
+
+  // static
+  nsresult
+  WipePaddingFileInternal(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+
+    MutexAutoLock lock(mDirPaddingFileMutex);
+
+    // Remove temporary file if we have one.
+    nsresult rv =
+      mozilla::dom::cache::
+      LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::TMP_FILE);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    MOZ_DIAGNOSTIC_ASSERT(mozilla::dom::cache::
+                          DirectoryPaddingFileExists(aBaseDir,
+                                                     DirPaddingFile::FILE));
+
+    int64_t paddingSize = 0;
+    rv = mozilla::dom::cache::LockedDirectoryPaddingGet(aBaseDir, &paddingSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      // If read file fail, there is nothing we can do to recover the file.
+      NS_WARNING("Cannnot read padding size from file!");
+      paddingSize = 0;
+    }
+
+    if (paddingSize > 0) {
+      mozilla::dom::cache::DecreaseUsageForQuotaInfo(aQuotaInfo, paddingSize);
+    }
+
+    rv = mozilla::dom::cache::
+         LockedDirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE);
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+    rv = mozilla::dom::cache::LockedDirectoryPaddingInit(aBaseDir);
+    Unused << NS_WARN_IF(NS_FAILED(rv));
+
+    return rv;
+  }
+
 private:
   ~CacheQuotaClient()
   {
     AssertIsOnBackgroundThread();
+    MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
+
+    sInstance = nullptr;
   }
 
-  NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient, override)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheQuotaClient, override)
+
+  // Mutex lock to protect directroy padding files. It should only be acquired
+  // in DOM Cache IO threads and Quota IO thread.
+  mozilla::Mutex mDirPaddingFileMutex;
 };
 
+// static
+CacheQuotaClient* CacheQuotaClient::sInstance = nullptr;
+
 } // namespace
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
+// static
 already_AddRefed<quota::Client> CreateQuotaClient()
 {
   AssertIsOnBackgroundThread();
 
   RefPtr<CacheQuotaClient> ref = new CacheQuotaClient();
   return ref.forget();
 }
 
+// static
+template<typename Callable>
+nsresult
+MaybeUpdatePaddingFile(nsIFile* aBaseDir,
+                       mozIStorageConnection* aConn,
+                       const int64_t aIncreaseSize,
+                       const int64_t aDecreaseSize,
+                       Callable aCommitHook)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aIncreaseSize >= 0);
+  MOZ_DIAGNOSTIC_ASSERT(aDecreaseSize >= 0);
+
+  RefPtr<CacheQuotaClient> cacheQuotaClient = CacheQuotaClient::Get();
+  MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient);
+
+  nsresult rv =
+    cacheQuotaClient->MaybeUpdatePaddingFileInternal(aBaseDir, aConn,
+                                                     aIncreaseSize,
+                                                     aDecreaseSize,
+                                                     aCommitHook);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  return rv;
+}
+
+// static
+nsresult
+RestorePaddingFile(nsIFile* aBaseDir, mozIStorageConnection* aConn)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+
+  RefPtr<CacheQuotaClient> cacheQuotaClient = CacheQuotaClient::Get();
+  MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient);
+
+  nsresult rv =
+    cacheQuotaClient->RestorePaddingFileInternal(aBaseDir, aConn);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  return rv;
+}
+
+// static
+nsresult
+WipePaddingFile(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+
+  RefPtr<CacheQuotaClient> cacheQuotaClient = CacheQuotaClient::Get();
+  MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient);
+
+  nsresult rv =
+    cacheQuotaClient->WipePaddingFileInternal(aQuotaInfo, aBaseDir);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  return rv;
+}
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/QuotaClient.h
+++ b/dom/cache/QuotaClient.h
@@ -3,22 +3,60 @@
 /* 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_dom_cache_QuotaClient_h
 #define mozilla_dom_cache_QuotaClient_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/cache/Types.h"
 #include "mozilla/dom/quota/Client.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 already_AddRefed<quota::Client>
 CreateQuotaClient();
 
+/**
+ * The following functions are used to access the directory padding file. The
+ * directory padding file lives in DOM Cache base directory
+ * (e.g. foo.com/cache/.padding). It is used to keep the current overall padding
+ * size for an origin, so that the QuotaManager doesn't need to access the
+ * database when getting quota clients' usage.
+ *
+ * For the directory padding file, it's only accessed on Quota IO thread
+ * (for getting current usage) and Cache IO threads (for tracking padding size
+ * change). Besides, the padding file is protected by a mutex lock held by
+ * CacheQuotaClient.
+ *
+ * Each padding file should only take 8 bytes (int64_t) to record the overall
+ * padding size. Besides, we use the temporary padding file to indicate if the
+ * previous action is completed successfully. If the temporary file exists, it
+ * represents that the previous action is failed and the content of padding file
+ * cannot be trusted, and we need to restore the padding file from the database.
+ */
+
+/**
+ * Note: The aCommitHook argument will be invoked while a lock is held. Callers
+ * should be careful not to pass a hook that might lock on something else and
+ * trigger a deadlock.
+ */
+template<typename Callable>
+nsresult
+MaybeUpdatePaddingFile(nsIFile* aBaseDir,
+                       mozIStorageConnection* aConn,
+                       const int64_t aIncreaseSize,
+                       const int64_t aDecreaseSize,
+                       Callable aCommitHook);
+
+nsresult
+RestorePaddingFile(nsIFile* aBaseDir, mozIStorageConnection* aConn);
+
+nsresult
+WipePaddingFile(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir);
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_QuotaClient_h
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -196,16 +196,19 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
   aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
   if (aIn.GetPrincipalInfo()) {
     aOut.principalInfo() = *aIn.GetPrincipalInfo();
   } else {
     aOut.principalInfo() = void_t();
   }
+
+  aOut.paddingInfo() = aIn.GetPaddingInfo();
+  aOut.paddingSize() = aIn.GetPaddingSize();
 }
 
 void
 TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
                            nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                            ErrorResult& aRv)
 {
   if (aIn.BodyUsed()) {
@@ -299,16 +302,18 @@ TypeUtils::ToResponse(const CacheRespons
     case ResponseType::Opaqueredirect:
       ir = ir->OpaqueRedirectResponse();
       break;
     default:
       MOZ_CRASH("Unexpected ResponseType!");
   }
   MOZ_DIAGNOSTIC_ASSERT(ir);
 
+  ir->SetPaddingSize(aIn.paddingSize());
+
   RefPtr<Response> ref = new Response(GetGlobalObject(), ir, nullptr);
   return ref.forget();
 }
 already_AddRefed<InternalRequest>
 TypeUtils::ToInternalRequest(const CacheRequest& aIn)
 {
   nsAutoCString url(aIn.urlWithoutQuery());
   url.Append(aIn.urlQuery());
--- a/dom/cache/test/mochitest/mochitest.ini
+++ b/dom/cache/test/mochitest/mochitest.ini
@@ -40,13 +40,14 @@ support-files =
 [test_cache_put_reorder.html]
 [test_cache_https.html]
 [test_cache_redirect.html]
 [test_cache_restart.html]
 [test_cache_shrink.html]
 [test_cache_orphaned_cache.html]
 [test_cache_orphaned_body.html]
 scheme=https
+[test_cache_padding.html]
 [test_cache_untrusted.html]
 [test_cache_updateUsage.html]
 [test_chrome_constructor.html]
 [test_cache_worker_gc.html]
 scheme=https
new file mode 100644
--- /dev/null
+++ b/dom/cache/test/mochitest/test_cache_padding.html
@@ -0,0 +1,197 @@
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Cache generate padding size for opaque repsonse</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="large_url_list.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+function setupTestIframe() {
+  return new Promise(function(resolve) {
+    var iframe = document.createElement("iframe");
+    iframe.src = "empty.html";
+    iframe.onload = function() {
+      window.caches = iframe.contentWindow.caches;
+      resolve();
+    };
+    document.body.appendChild(iframe);
+  });
+}
+
+function clearStorage() {
+  return new Promise(function(resolve, reject) {
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var request = qms.clearStoragesForPrincipal(principal);
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
+  });
+}
+
+function resetStorage() {
+  return new Promise(function(resolve, reject) {
+    var qms = SpecialPowers.Services.qms;
+    var request = qms.reset();
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
+  });
+}
+
+function getStorageUsage(fromMemory) {
+  return new Promise(function(resolve, reject) {
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var cb = SpecialPowers.wrapCallback(function(request) {
+      var result = request.result;
+      resolve(result.usage);
+    });
+
+    // Actually, the flag is used to distingulish getting group usage and origin
+    // usage, but we utilize this to get usage from in-memory and the disk.
+    // Default value for "fromMemory" is false.
+    qms.getUsageForPrincipal(principal, cb, !!fromMemory);
+  });
+}
+
+async function verifyUsage() {
+  // Although it returns group usage when passing true, it calculate the usage
+  // from tracking usage object (in-memory object) in QuotaManager.
+  let memoryUsage = await getStorageUsage(/* fromMemory */ true);
+  // This will returns the origin usage by re-calculating usage from directory.
+  let diskUsage = await getStorageUsage(/* fromMemory */ false);
+
+  is(memoryUsage, diskUsage,
+     "In-memory usage and disk usage should be the same.");
+  return memoryUsage;
+}
+
+async function waitForIOToComplete(cache, request) {
+  info("Wait for IO complete.");
+  // The following lines ensure we've deleted orphaned body.
+  // First, wait for cache operation delete the orphaned body.
+  await cache.match(request);
+
+  // Finally, wait for -wal file finish its job.
+  return await resetStorage();
+}
+
+function fetchOpaqueResponse(url) {
+  return fetch(url, { mode: "no-cors" });
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({
+  "set": [["dom.caches.enabled", true],
+          ["dom.caches.testing.enabled", true],
+          ["dom.quotaManager.testing", true]]
+}, async function() {
+
+  // This test is mainly to verify we only generate different padding size for
+  // the opaque response which is comming from netwrok.
+  // Besides, this test utilizes verifyUsage() to ensure Cache Acions does
+  // update thier usage/padding size to the QM, does record padding size to
+  // the directory padding file and does do above two things synchronously.
+  // So that, opaque response's size is bigger than the normal response's size
+  // and we always have the same usage bewteen from in-memory and from
+  // the file-system.
+  // Note: For the cloned and cached opaque response, the padding size shouldn't
+  // be changed. Thus, it makes the attacker harder to get the padding size.
+
+  const name = 'cachePadding';
+  const other_name = 'cachePaddingOther';
+  const cors_base = 'https://example.com/test/dom/cache/test/mochitest/';
+  const url = 'test_cache_add.js';
+
+  await setupTestIframe();
+
+  info("Stage 1: Clean storage.");
+  await clearStorage();
+
+  let cache = await caches.open(name);
+  await waitForIOToComplete(cache, url);
+  let usage1 = await verifyUsage();
+
+  info("Stage 2: Verify opaque responses have padding.");
+  cache = await caches.open(name);
+  await cache.add(url);
+  await waitForIOToComplete(cache, url);
+  let usage2 = await verifyUsage();
+  let sizeForNormalResponse = usage2 - usage1;
+
+  let opaqueResponse = await fetchOpaqueResponse(cors_base + url);
+  cache = await caches.open(name);
+  await cache.put(cors_base + url, opaqueResponse.clone());
+  await waitForIOToComplete(cache, url);
+  let usage3 = await verifyUsage();
+  let sizeForOpaqueResponse = usage3 - usage2;
+  ok(sizeForOpaqueResponse > sizeForNormalResponse,
+     "The opaque response should have larger size than the normal response.");
+
+  info("Stage 3: Verify the cloned response has the same size.");
+  cache = await caches.open(name);
+  await cache.put(cors_base + url, opaqueResponse.clone());
+  await waitForIOToComplete(cache, url);
+  let usage4 = await verifyUsage();
+  // Since we put the same request and response again, the size should be the
+  // same (DOM Cache removes the previous cached request and response)
+  ok(usage4 == usage3,
+     "We won't generate different padding for cloned response");
+
+  info("Stage 4: Verify the cached response has the same size.");
+  cache = await caches.open(name);
+  opaqueResponse = await cache.match(cors_base + url);
+  ok(opaqueResponse);
+
+  await cache.put(cors_base + url, opaqueResponse);
+  await waitForIOToComplete(cache, url);
+  let usage5 = await verifyUsage();
+  ok(usage5 == usage3,
+     "We won't generate different padding for cached response");
+
+  info("Stage 5: Verify padding size may changes in different fetch()s.");
+  let paddingSizeChange = false;
+  // Since we randomly generate padding size and rounding the overall size up,
+  // we will probably have the same size. So, fetch it multiple times.
+  for (let i = 0; i < 10; i++) {
+    opaqueResponse = await fetchOpaqueResponse(cors_base + url);
+    cache = await caches.open(name);
+    await cache.put(cors_base + url, opaqueResponse);
+    await waitForIOToComplete(cache, url);
+    let usage6  = await verifyUsage();
+    if (usage6 != usage5) {
+      paddingSizeChange = true;
+      break;
+    }
+  }
+  ok(paddingSizeChange,
+     "We should generate different padding size for fetching response");
+
+  info("Stage 6: Verify the padding is removed once on caches.delete() and " +
+       "cache.delete().");
+  // Add an opauqe response on other cache storage and then delete that storage.
+  cache = await caches.open(other_name);
+  opaqueResponse = await fetchOpaqueResponse(cors_base + url);
+  await cache.put(cors_base + url, opaqueResponse);
+  await caches.delete(other_name);
+  await caches.has(other_name);
+  // Force remove orphaned cached in the next action
+  await resetStorage();
+
+  // Delete the opauqe repsonse on current cache storage.
+  cache = await caches.open(name);
+  await cache.delete(cors_base + url);
+  await waitForIOToComplete(cache, url);
+  let usage7  = await verifyUsage();
+  ok(usage7 == usage2,
+     "The opaque response should be removed by caches.delete() and " +
+     "cache.delete()");
+
+  await SimpleTest.finish();
+});
+</script>
+</body>
+</html>
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -428,19 +428,22 @@ FetchDriver::BeginAndGetFilteredResponse
   } else {
     switch (mRequest->GetResponseTainting()) {
       case LoadTainting::Basic:
         filteredResponse = aResponse->BasicResponse();
         break;
       case LoadTainting::CORS:
         filteredResponse = aResponse->CORSResponse();
         break;
-      case LoadTainting::Opaque:
+      case LoadTainting::Opaque: {
         filteredResponse = aResponse->OpaqueResponse();
+        nsresult rv = filteredResponse->GeneratePaddingInfo();
+        if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
         break;
+      }
       default:
         MOZ_CRASH("Unexpected case");
     }
   }
 
   MOZ_ASSERT(filteredResponse);
   MOZ_ASSERT(mObserver);
   if (!ShouldCheckSRI(mRequest, filteredResponse)) {
@@ -609,16 +612,22 @@ FetchDriver::OnStartRequest(nsIRequest* 
   // the outer fetch().  In gecko, however, we serialize the Response through
   // the channel and must regenerate the tainting from the channel in the
   // interception case.
   mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
 
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
   mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
+  if (NS_WARN_IF(!mResponse)) {
+    // Fail to generate a paddingInfo for opaque response.
+    MOZ_DIAGNOSTIC_ASSERT(mResponse->Type() == ResponseType::Opaque);
+    FailWithNetworkError();
+    return rv;
+  }
 
   // From "Main Fetch" step 19: SRI-part1.
   if (ShouldCheckSRI(mRequest, mResponse) && mSRIMetadata.IsEmpty()) {
     nsIConsoleReportCollector* reporter = nullptr;
     if (mObserver) {
       reporter = mObserver->GetReporter();
     }
 
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -6,28 +6,38 @@
 
 #include "InternalResponse.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
+#include "nsIRandomGenerator.h"
 #include "nsIURI.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+// Const variable for generate padding size
+// XXX This will be tweaked to something more meaningful in Bug 1383656.
+const uint32_t kMaxRandomNumber = 102400;
+
+} // namespace
+
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
   , mBodySize(UNKNOWN_BODY_SIZE)
+  , mPaddingSize(UNKNOWN_PADDING_SIZE)
 {
 }
 
 already_AddRefed<InternalResponse>
 InternalResponse::FromIPC(const IPCInternalResponse& aIPCResponse)
 {
   if (aIPCResponse.type() == ResponseType::Error) {
     return InternalResponse::NetworkError();
@@ -137,16 +147,21 @@ InternalResponse::ToIPC(IPCInternalRespo
 }
 
 already_AddRefed<InternalResponse>
 InternalResponse::Clone(CloneType aCloneType)
 {
   RefPtr<InternalResponse> clone = CreateIncompleteCopy();
 
   clone->mHeaders = new InternalHeaders(*mHeaders);
+
+  // Make sure the clone response will have the same padding size.
+  clone->mPaddingInfo = mPaddingInfo;
+  clone->mPaddingSize = mPaddingSize;
+
   if (mWrappedResponse) {
     clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
     MOZ_ASSERT(!mBody);
     return clone.forget();
   }
 
   if (!mBody || aCloneType == eDontCloneInputStream) {
     return clone.forget();
@@ -184,16 +199,89 @@ InternalResponse::CORSResponse()
   MOZ_ASSERT(!mWrappedResponse, "Can't CORSResponse a already wrapped response");
   RefPtr<InternalResponse> cors = CreateIncompleteCopy();
   cors->mType = ResponseType::Cors;
   cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
   cors->mWrappedResponse = this;
   return cors.forget();
 }
 
+uint32_t
+InternalResponse::GetPaddingInfo()
+{
+  // If it's an opaque response, the paddingInfo should be generated only when
+  // paddingSize is unknown size.
+  // If it's not, the paddingInfo should be nothing and the paddingSize should
+  // be unknown size.
+  MOZ_DIAGNOSTIC_ASSERT((mType == ResponseType::Opaque &&
+                         mPaddingSize == UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isSome()) ||
+                        (mType == ResponseType::Opaque &&
+                         mPaddingSize != UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isNothing()) ||
+                        (mType != ResponseType::Opaque &&
+                         mPaddingSize == UNKNOWN_PADDING_SIZE &&
+                         mPaddingInfo.isNothing()));
+  return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0;
+}
+
+nsresult
+InternalResponse::GeneratePaddingInfo()
+{
+  MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque);
+  MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE);
+
+  // Utilize random generator to generator a random number
+  nsresult rv;
+  uint32_t randomNumber = 0;
+  nsCOMPtr<nsIRandomGenerator> randomGenerator =
+    do_GetService("@mozilla.org/security/random-generator;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  MOZ_DIAGNOSTIC_ASSERT(randomGenerator);
+
+  uint8_t* buffer;
+  rv = randomGenerator->GenerateRandomBytes(sizeof(randomNumber), &buffer);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  memcpy(&randomNumber, buffer, sizeof(randomNumber));
+  free(buffer);
+
+  mPaddingInfo.emplace(randomNumber % kMaxRandomNumber);
+
+  return rv;
+}
+
+int64_t
+InternalResponse::GetPaddingSize()
+{
+  // We initialize padding size to an unknown size (-1). After cached, we only
+  // pad opaque response. Opaque response's padding size might be unknown before
+  // cached.
+  MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque ||
+                        mPaddingSize == UNKNOWN_PADDING_SIZE);
+  MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE ||
+                        mPaddingSize >= 0);
+
+  return mPaddingSize;
+}
+
+void
+InternalResponse::SetPaddingSize(int64_t aPaddingSize)
+{
+  // We should only pad the opaque response.
+  MOZ_DIAGNOSTIC_ASSERT((mType == ResponseType::Opaque) !=
+                        (aPaddingSize ==
+                         InternalResponse::UNKNOWN_PADDING_SIZE));
+  MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE ||
+                        aPaddingSize >= 0);
+
+  mPaddingSize = aPaddingSize;
+}
+
 void
 InternalResponse::SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)
 {
   mPrincipalInfo = Move(aPrincipalInfo);
 }
 
 LoadTainting
 InternalResponse::GetTainting() const
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -228,16 +228,28 @@ public:
     MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
     // If body is not given, then size must be unknown.
     MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
 
     mBody = aBody;
     mBodySize = aBodySize;
   }
 
+  uint32_t
+  GetPaddingInfo();
+
+  nsresult
+  GeneratePaddingInfo();
+
+  int64_t
+  GetPaddingSize();
+
+  void
+  SetPaddingSize(int64_t aPaddingSize);
+
   void
   InitChannelInfo(nsIChannel* aChannel)
   {
     mChannelInfo.InitFromChannel(aChannel);
   }
 
   void
   InitChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
@@ -296,18 +308,23 @@ private:
   // Unless stated otherwise, it is the empty list. The current url is the last
   // element in mURLlist
   nsTArray<nsCString> mURLList;
   const uint16_t mStatus;
   const nsCString mStatusText;
   RefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
   int64_t mBodySize;
+  // It's used to passed to the CacheResponse to generate padding size. Once, we
+  // generate the padding size for resposne, we don't need it anymore.
+  Maybe<uint32_t> mPaddingInfo;
+  int64_t mPaddingSize;
 public:
   static const int64_t UNKNOWN_BODY_SIZE = -1;
+  static const int64_t UNKNOWN_PADDING_SIZE = -1;
 private:
   ChannelInfo mChannelInfo;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
 
   // For filtered responses.
   // Cache, and SW interception should always serialize/access the underlying
   // unfiltered headers and when deserializing, create an InternalResponse
   // with the unfiltered headers followed by wrapping it.
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -120,17 +120,17 @@ namespace {
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
 const uint32_t kSQLitePageSizeOverride = 512;
 
 // Major storage version. Bump for backwards-incompatible changes.
-const uint32_t kMajorStorageVersion = 2;
+const uint32_t kMajorStorageVersion = 3;
 
 // Minor storage version. Bump for backwards-compatible changes.
 const uint32_t kMinorStorageVersion = 0;
 
 // The storage version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 16 bits so the max value is
 // 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
 static_assert(kMajorStorageVersion <= 0xFFFF,
@@ -1809,16 +1809,39 @@ private:
   nsresult
   MaybeStripObsoleteOriginAttributes(const OriginProps& aOriginProps,
                                      bool* aStripped);
 
   nsresult
   ProcessOriginDirectory(const OriginProps& aOriginProps) override;
 };
 
+// XXXtt: The following class is duplicated from
+// UpgradeStorageFrom1_0To2_0Helper and it should be extracted out in
+// bug 1395102.
+class UpgradeStorageFrom2_0To3_0Helper final
+  : public StorageDirectoryHelper
+{
+public:
+  UpgradeStorageFrom2_0To3_0Helper(nsIFile* aDirectory,
+                                   bool aPersistent)
+    : StorageDirectoryHelper(aDirectory, aPersistent)
+  { }
+
+  nsresult
+  DoUpgrade();
+
+private:
+  nsresult
+  MaybeUpgradeClients(const OriginProps& aOriginProps);
+
+  nsresult
+  ProcessOriginDirectory(const OriginProps& aOriginProps) override;
+};
+
 class RestoreDirectoryMetadata2Helper final
   : public StorageDirectoryHelper
 {
 public:
   RestoreDirectoryMetadata2Helper(nsIFile* aDirectory,
                                   bool aPersistent)
     : StorageDirectoryHelper(aDirectory, aPersistent)
   { }
@@ -2924,16 +2947,65 @@ QuotaObject::Release()
 bool
 QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate)
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   MutexAutoLock lock(quotaManager->mQuotaMutex);
 
+  return LockedMaybeUpdateSize(aSize, aTruncate);
+}
+
+bool
+QuotaObject::IncreaseSize(int64_t aDelta)
+{
+  MOZ_ASSERT(aDelta >= 0);
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  AssertNoOverflow(mSize, aDelta);
+  int64_t size = mSize + aDelta;
+
+  return LockedMaybeUpdateSize(size, /* aTruncate */ false);
+}
+
+void
+QuotaObject::DisableQuotaCheck()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  mQuotaCheckDisabled = true;
+}
+
+void
+QuotaObject::EnableQuotaCheck()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  mQuotaCheckDisabled = false;
+}
+
+bool
+QuotaObject::LockedMaybeUpdateSize(int64_t aSize, bool aTruncate)
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
+
   if (mQuotaCheckDisabled) {
     return true;
   }
 
   if (mSize == aSize) {
     return true;
   }
 
@@ -3122,38 +3194,16 @@ QuotaObject::MaybeUpdateSize(int64_t aSi
   }
   quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
 
   mSize = aSize;
 
   return true;
 }
 
-void
-QuotaObject::DisableQuotaCheck()
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  MOZ_ASSERT(quotaManager);
-
-  MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-  mQuotaCheckDisabled = true;
-}
-
-void
-QuotaObject::EnableQuotaCheck()
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  MOZ_ASSERT(quotaManager);
-
-  MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-  mQuotaCheckDisabled = false;
-}
-
 /*******************************************************************************
  * Quota manager
  ******************************************************************************/
 
 QuotaManager::QuotaManager()
 : mQuotaMutex("QuotaManager.mQuotaMutex"),
   mTemporaryStorageLimit(0),
   mTemporaryStorageUsage(0),
@@ -3769,20 +3819,25 @@ QuotaManager::RemoveQuota()
 
   NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
 }
 
 already_AddRefed<QuotaObject>
 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
                              const nsACString& aGroup,
                              const nsACString& aOrigin,
-                             nsIFile* aFile)
+                             nsIFile* aFile,
+                             int64_t* aFileSizeOut /* = nullptr */)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
+  if (aFileSizeOut) {
+    *aFileSizeOut = 0;
+  }
+
   if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
     return nullptr;
   }
 
   nsString path;
   nsresult rv = aFile->GetPath(path);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
@@ -3844,35 +3899,46 @@ QuotaManager::GetQuotaObject(Persistence
       originInfo->mQuotaObjects.Put(path, quotaObject);
     }
 
     // Addref the QuotaObject and move the ownership to the result. This must
     // happen before we unlock!
     result = quotaObject->LockedAddRef();
   }
 
+  if (aFileSizeOut) {
+    *aFileSizeOut = fileSize;
+  }
+
   // The caller becomes the owner of the QuotaObject, that is, the caller is
   // is responsible to delete it when the last reference is removed.
   return result.forget();
 }
 
 already_AddRefed<QuotaObject>
 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
                              const nsACString& aGroup,
                              const nsACString& aOrigin,
-                             const nsAString& aPath)
-{
+                             const nsAString& aPath,
+                             int64_t* aFileSizeOut /* = nullptr */)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (aFileSizeOut) {
+    *aFileSizeOut = 0;
+  }
+
   nsresult rv;
   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = file->InitWithPath(aPath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
-  return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
+  return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file, aFileSizeOut);
 }
 
 Nullable<bool>
 QuotaManager::OriginPersisted(const nsACString& aGroup,
                               const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
 
@@ -4744,16 +4810,79 @@ QuotaManager::UpgradeStorageFrom1_0To2_0
   rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+nsresult
+QuotaManager::UpgradeStorageFrom2_0To3_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  // The upgrade is mainly to create a directory padding file in DOM Cache
+  // directory to record the overall padding size of an origin.
+
+  nsresult rv;
+
+  for (const PersistenceType persistenceType : kAllPersistenceTypes) {
+    nsCOMPtr<nsIFile> directory =
+      do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = directory->InitWithPath(GetStoragePath(persistenceType));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool exists;
+    rv = directory->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!exists) {
+      continue;
+    }
+
+    bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
+    RefPtr<UpgradeStorageFrom2_0To3_0Helper> helper =
+      new UpgradeStorageFrom2_0To3_0Helper(directory, persistent);
+
+    rv = helper->DoUpgrade();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+#ifdef DEBUG
+  {
+    int32_t storageVersion;
+    rv = aConnection->GetSchemaVersion(&storageVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(storageVersion == MakeStorageVersion(2, 0));
+  }
+#endif
+
+  rv = aConnection->SetSchemaVersion(MakeStorageVersion(3, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 #ifdef DEBUG
 
 void
 QuotaManager::AssertStorageIsInitialized() const
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mStorageInitialized);
 }
@@ -4892,24 +5021,26 @@ QuotaManager::EnsureStorageIsInitialized
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&storageVersion)));
       MOZ_ASSERT(storageVersion == kStorageVersion);
     } else {
       // This logic needs to change next time we change the storage!
-      static_assert(kStorageVersion == int32_t((2 << 16) + 0),
+      static_assert(kStorageVersion == int32_t((3 << 16) + 0),
                     "Upgrade function needed due to storage version increase.");
 
       while (storageVersion != kStorageVersion) {
         if (storageVersion == 0) {
           rv = UpgradeStorageFrom0_0To1_0(connection);
         } else if (storageVersion == MakeStorageVersion(1, 0)) {
           rv = UpgradeStorageFrom1_0To2_0(connection);
+        } else if (storageVersion == MakeStorageVersion(2, 0)) {
+          rv = UpgradeStorageFrom2_0To3_0(connection);
         } else {
           NS_WARNING("Unable to initialize storage, no upgrade path is "
                      "available!");
           return NS_ERROR_FAILURE;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -9263,16 +9394,226 @@ UpgradeStorageFrom1_0To2_0Helper::Proces
       return rv;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
+UpgradeStorageFrom2_0To3_0Helper::DoUpgrade()
+{
+  AssertIsOnIOThread();
+
+  DebugOnly<bool> exists;
+  MOZ_ASSERT(NS_SUCCEEDED(mDirectory->Exists(&exists)));
+  MOZ_ASSERT(exists);
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  nsresult rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
+    MOZ_ASSERT(originDir);
+
+    bool isDirectory;
+    rv = originDir->IsDirectory(&isDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!isDirectory) {
+      nsString leafName;
+      rv = originDir->GetLeafName(leafName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      // Unknown files during upgrade are allowed. Just warn if we find them.
+      if (!IsOSMetadata(leafName)) {
+        UNKNOWN_FILE_WARNING(leafName);
+      }
+      continue;
+    }
+
+    OriginProps originProps;
+    rv = originProps.Init(originDir);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // Only update DOM Cache directory for adding padding file.
+    rv = MaybeUpgradeClients(originProps);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    int64_t timestamp;
+    nsCString group;
+    nsCString origin;
+    Nullable<bool> isApp;
+    nsresult rv = GetDirectoryMetadata(originDir,
+                                       timestamp,
+                                       group,
+                                       origin,
+                                       isApp);
+    if (NS_FAILED(rv) || isApp.IsNull()) {
+      originProps.mNeedsRestore = true;
+    }
+
+    nsCString suffix;
+    rv = GetDirectoryMetadata2(originDir,
+                               timestamp,
+                               suffix,
+                               group,
+                               origin,
+                               isApp.SetValue());
+    if (NS_FAILED(rv)) {
+      originProps.mTimestamp = GetLastModifiedTime(originDir, mPersistent);
+      originProps.mNeedsRestore2 = true;
+    } else {
+      originProps.mTimestamp = timestamp;
+    }
+
+    mOriginProps.AppendElement(Move(originProps));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (mOriginProps.IsEmpty()) {
+    return NS_OK;
+  }
+
+  rv = ProcessOriginDirectories();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeStorageFrom2_0To3_0Helper::MaybeUpgradeClients(
+                                                const OriginProps& aOriginProps)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aOriginProps.mDirectory);
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  nsresult rv =
+    aOriginProps.mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    if (NS_WARN_IF(!file)) {
+      return rv;
+    }
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsString leafName;
+    rv = file->GetLeafName(leafName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!isDirectory) {
+      // Unknown files during upgrade are allowed. Just warn if we find them.
+      if (!IsOriginMetadata(leafName) &&
+          !IsTempMetadata(leafName)) {
+        UNKNOWN_FILE_WARNING(leafName);
+      }
+      continue;
+    }
+
+    Client::Type clientType;
+    rv = Client::TypeFromText(leafName, clientType);
+    if (NS_FAILED(rv)) {
+      UNKNOWN_FILE_WARNING(leafName);
+      continue;
+    }
+
+    Client* client = quotaManager->GetClient(clientType);
+    MOZ_ASSERT(client);
+
+    rv = client->UpgradeStorageFrom2_0To3_0(file);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeStorageFrom2_0To3_0Helper::ProcessOriginDirectory(
+                                                const OriginProps& aOriginProps)
+{
+  AssertIsOnIOThread();
+
+  nsresult rv;
+
+  if (aOriginProps.mNeedsRestore) {
+    rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
+                                 aOriginProps.mTimestamp,
+                                 aOriginProps.mSuffix,
+                                 aOriginProps.mGroup,
+                                 aOriginProps.mOrigin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  if (aOriginProps.mNeedsRestore2) {
+    rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
+                                  aOriginProps.mTimestamp,
+                                  /* aPersisted */ false,
+                                  aOriginProps.mSuffix,
+                                  aOriginProps.mGroup,
+                                  aOriginProps.mOrigin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
 RestoreDirectoryMetadata2Helper::RestoreMetadata2File()
 {
   AssertIsOnIOThread();
 
   nsresult rv;
 
   OriginProps originProps;
   rv = originProps.Init(mDirectory);
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -86,24 +86,30 @@ public:
     }
     else {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
-  // Methods which are called on the IO thred.
+  // Methods which are called on the IO thread.
   virtual nsresult
   UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory)
   {
     return NS_OK;
   }
 
   virtual nsresult
+  UpgradeStorageFrom2_0To3_0(nsIFile* aDirectory)
+  {
+    return NS_OK;
+  }
+
+  virtual nsresult
   InitOrigin(PersistenceType aPersistenceType,
              const nsACString& aGroup,
              const nsACString& aOrigin,
              const AtomicBool& aCanceled,
              UsageInfo* aUsageInfo) = 0;
 
   virtual nsresult
   GetUsageForOrigin(PersistenceType aPersistenceType,
@@ -114,17 +120,17 @@ public:
 
   virtual void
   OnOriginClearCompleted(PersistenceType aPersistenceType,
                          const nsACString& aOrigin) = 0;
 
   virtual void
   ReleaseIOThreadObjects() = 0;
 
-  // Methods which are called on the background thred.
+  // Methods which are called on the background thread.
   virtual void
   AbortOperations(const nsACString& aOrigin) = 0;
 
   virtual void
   AbortOperationsForProcess(ContentParentId aContentParentId) = 0;
 
   virtual void
   StartIdleMaintenance() = 0;
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -172,23 +172,25 @@ public:
     MutexAutoLock lock(mQuotaMutex);
     LockedRemoveQuotaForOrigin(aPersistenceType, aGroup, aOrigin);
   }
 
   already_AddRefed<QuotaObject>
   GetQuotaObject(PersistenceType aPersistenceType,
                  const nsACString& aGroup,
                  const nsACString& aOrigin,
-                 nsIFile* aFile);
+                 nsIFile* aFile,
+                 int64_t* aFileSizeOut = nullptr);
 
   already_AddRefed<QuotaObject>
   GetQuotaObject(PersistenceType aPersistenceType,
                  const nsACString& aGroup,
                  const nsACString& aOrigin,
-                 const nsAString& aPath);
+                 const nsAString& aPath,
+                 int64_t* aFileSizeOut = nullptr);
 
   Nullable<bool>
   OriginPersisted(const nsACString& aGroup,
                   const nsACString& aOrigin);
 
   void
   PersistOrigin(const nsACString& aGroup,
                 const nsACString& aOrigin);
@@ -470,16 +472,19 @@ private:
 
   nsresult
   UpgradeStorageFrom0_0To1_0(mozIStorageConnection* aConnection);
 
   nsresult
   UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection);
 
   nsresult
+  UpgradeStorageFrom2_0To3_0(mozIStorageConnection* aConnection);
+
+  nsresult
   InitializeRepository(PersistenceType aPersistenceType);
 
   nsresult
   InitializeOrigin(PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    int64_t aAccessTime,
                    bool aPersisted,
--- a/dom/quota/QuotaObject.h
+++ b/dom/quota/QuotaObject.h
@@ -36,16 +36,19 @@ public:
   Path() const
   {
     return mPath;
   }
 
   bool
   MaybeUpdateSize(int64_t aSize, bool aTruncate);
 
+  bool
+  IncreaseSize(int64_t aDelta);
+
   void
   DisableQuotaCheck();
 
   void
   EnableQuotaCheck();
 
 private:
   QuotaObject(OriginInfo* aOriginInfo, const nsAString& aPath, int64_t aSize)
@@ -68,16 +71,19 @@ private:
     AssertCurrentThreadOwnsQuotaMutex();
 
     ++mRefCnt;
 
     RefPtr<QuotaObject> result = dont_AddRef(this);
     return result.forget();
   }
 
+  bool
+  LockedMaybeUpdateSize(int64_t aSize, bool aTruncate);
+
   mozilla::ThreadSafeAutoRefCnt mRefCnt;
 
   OriginInfo* mOriginInfo;
   nsString mPath;
   int64_t mSize;
 
   bool mQuotaCheckDisabled;
 };
new file mode 100644
--- /dev/null
+++ b/dom/quota/test/unit/test_version3_0upgrade.js
@@ -0,0 +1,76 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function* testSteps()
+{
+  const origins = [
+    "storage/default/chrome/",
+    "storage/default/http+++www.mozilla.org/"
+  ];
+  const paddingFilePath = "cache/.padding";
+
+  info("Clearing");
+
+  clear(continueToNextStepSync);
+  yield undefined;
+
+  // The profile contains two cache storages:
+  // - storage/default/chrome/cache,
+  // - storage/default/http+++www.mozilla.org/cache
+  // The file create_cache.js in the package was run locally, specifically it
+  // was temporarily added to xpcshell.ini and then executed:
+  //   mach xpcshell-test --interactive dom/quota/test/unit/create_cache.js
+  // Note: it only creates the directory "storage/default/chrome/cache".
+  // To make it become the profile in the test, two more manual steps are
+  // needed.
+  // 1. Remove the folder "storage/temporary".
+  // 2. Copy the content under the "storage/default/chrome" to
+  //    "storage/default/http+++www.mozilla.org".
+  installPackage("version3_0upgrade_profile");
+
+  info("Checking padding file before upgrade (QM version 2.0)");
+
+  for (let origin of origins) {
+    let paddingFile = getRelativeFile(origin + paddingFilePath);
+
+    let exists = paddingFile.exists();
+    ok(!exists, "Padding file doesn't exist");
+  }
+
+  info("Initializing");
+
+  // Initialize QuotaManager to trigger upgrade the QM to version 3.0
+  let request = init(continueToNextStepSync);
+  yield undefined;
+
+  ok(request.resultCode == NS_OK, "Initialization succeeded");
+
+  info("Checking padding files after upgrade (QM version 3.0)");
+
+  for (let origin of origins) {
+    let paddingFile = getRelativeFile(origin + paddingFilePath);
+
+    let exists = paddingFile.exists();
+    ok(exists, "Padding file does exist");
+
+    info("Reading out contents of padding file");
+
+    File.createFromNsIFile(paddingFile).then(grabArgAndContinueHandler);
+    let domFile = yield undefined;
+
+    let fileReader = new FileReader();
+    fileReader.onload = continueToNextStepSync;
+    fileReader.readAsArrayBuffer(domFile);
+    yield undefined;
+
+    let paddingFileInfo = new Float64Array(fileReader.result);
+    ok(paddingFileInfo.length == 1, "Padding file does take 64 bytes.");
+    ok(paddingFileInfo[0] == 0, "Padding size does default to zero.");
+  }
+
+  finishTest();
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8c16d698ee28c2ab1cf411d0645dce57d093b80e
GIT binary patch
literal 5261
zc$^FHW@h1H0D-BEYTgV;fP+DXp|~W!C^0=%KQx4sf%)g!^^pbZ)<>3Ba5FHnykKTv
z022W?O%OmbAtf~}u{5UyVG=tjCaGgFDWjyMKwDe8yu4g5H@_+~Cnr%azbGAH0S74-
zh-0xJIin~)H<h4)BH*wB8MvIu>!BEsCkw<1xDC|HO)W`GNi0dk7pln#y%`pd7Cbn0
z;J|@ta{{N#pWStY3C+F@KCQPzfjliBRwlwe-7+JhtZZPrG0C{=Q*rRb2s}ZiOMILp
zCnjeg1tce-fP8Z9nr<8m$Pz{dD^jfilf`<)g*llesrdZ+_RiV7$1V&9J{I2Yom{u>
z$f6amlz5#kq}|@seZfni(dCv@$VA_1Cfd3D+NWRd=yjDU&t=a&s=2q|iR6xgM}ifi
zd<V1-^fEv0-%>bfS;LR4FaJ;a<(#juF`oDFM$xV2^ICZ?PAH$Z-{|yn-45Nfv?K08
zT#9K|_vq-IU0tac+qW${E6#WSdL!+7`#wLOv`<5<nE%KrbM389^QX(7yUun0Y5mFl
z>+9FwKWTg7uR&rx!-4-U@9#Cm#YA5|XYTyE@;0Z_`R#qb<Gpv6F1}-svUpFhVal$+
zyplD->K0pb+~0*uA5(XW4a??@tPNV3bMpLL<DJ1P-!56bEp@xh<=nm_Q%WBvFU|`o
z?`>8I+VIfKJNi>$?f<}?bsF}jeER*u>d}*yzC9YVanCZ&?Uzg9*z|8@Eiqo3_u}a5
zv|09_=awi+UH()kd4JCJ?(grj_io<Nv(M|wYnHy7UvH<~<~=l#w|iz$Kg(&}tt!&T
zE+2C^IM?sR{MhhI8k1&=gkD=!bbf`zD}&uno9}*_7x-(_0`c<gTkmNXR_gKHE?Tqf
zF?(8w?6VsST21!<zH0k;<(v)4B^Mq(cv3U1>iyHNkG^dy`{%Z8(xr&QuRlLttugso
zfoN^w*4@g||J$lw+g|zl=-INee^s--ipO4U(l4GDsmqgUBm6`EdGxa@obi{dJemEJ
zXaBWXDQ&Skm5=SI@$*WjKQ9t8H{5yg{ovZ}OK!Y7AJ5Ek6<wIhx;E#+({Go;zpef8
z>BHCT!mG<Nm&I2t)6(xVU+2fR>+92veseZFHEq`}i&6XUF~{QY%dP1-N3w%=*-75F
zEULNxalO~l`;Yr}PqUBWW>)<^>#^RvzX`X?XRWe|T`lX=^|aJU^!1hO-QOZB%B&2J
zuHg0IsDEer_}H8&+Pq%BzfAR9{?IX__3lT>vqub^pQl<x6+4TsUcFkc*Ftl3V{&<|
zdC0@L%KDcr+pW^nl8ipZs1&{0EqmK`>#f2E1#SPIbeP?G%N`wk`Kzq;oPxMtyytw*
zeQKGpE{o@Q;6$~pACyh?=B+TB+#CGq*_TJ{ucQ3^*{=Dhy4^h}b9dh9n67oVRJCf=
zeDaP=pL#EPb5&t&taZG{J1e%uDPei3?;SRI##S$sygqeySVnKzd*$1&%iR23UVik^
zlZxb<oTFAQ;~26~wz{HJZ}!!k1e<j^E4N-U_SIjvE^BZ2W0lEmlWNYT33+UbjrNGF
zyR_nM_|<j&Jz37v9<_hp|AP@+w&b;`nr3V7=QIU#8#GN8pIvhP|9&ngKQrh;S~&Mf
zc^;SZ{~vv;pQ{Mg^ufRD-%9!O|G^v|^Lzi9K@5Yx7617`j00aD|Fs9pUG9Hg4`wWr
z-~XQ-#7Ow{@t-`{=$GQQ|FgC2VKz&@US~G_|AGYWc#A_q3xAokuAP>1G3fNg_y4y}
zo!>eOY|4SNrRA4C+anc?JAT%&<1RR5aTnES1tpH!7JEUdMabCEf^oE994#0}3&ufP
zFq)9s)XN1jOOeV;Zc>{3Dul{Qbkp&c#;bv*uitmJ^*NAN1q>p7py|m)sfi`2@nF;R
zvWme5DXqh5(9{Wr{znXWTEG9{S{;0Qp=Fo=^COjQ!k6y$IX&7oEo!&doy(#}>eYFf
z*W`S>{QKVK<lEOAxc&DuPEYMN>EW89kf88hgn30nAIJLFzg^_|jI1B!rMq2ydM`@;
zqGs&H(>C18{T?MuPvSe8ZEw)E_cfoH^R*ePyBEy(!Ya8qxbs8Av(1kty?S<VMqTQ9
zl{^2go?YI!M@gOa{hyAPPtwk0g#P-twj*>IV@LJM)z3L&RpeGJ>!_1|EwkW=cHEPe
z?LxKpZltpxaI;{IuuSmpdC0EiJ6Gq#*Zl0uPx&AIPkSo9^y4J`Tm65G?jyo0s6oy9
zo06=lAt)-C7#R2vv4LDe&0f7eGH5qOaB=f}IjQ3r>Tz2Cl&?;pL0UpmLP|<OKtKWz
zv?SeW;9Rf69I=1i3Fo*+SNfV5K@r8sB*%=aqbmVr2{62M1ks3YFDoSKF`^x}X#yyw
zIU$*b=mL{&syd3PJy=Y|*&Qa`GI12k-eR!~-ZjQ&IIfPd1l*!6jdqMU4M*xG6K4x*
zcNp20bwt<#?i`aIR=P-G_195>iRg%dHT8(|8)}aj;kPA??W9_X)(j-w%d#lRBa$6w
z*plx>Efg<qAY=u_UNS-PQV|b{K?<)aNVi!9#pVZCY{p(E5e`$#8VO<(BP8STGGNwE
qtdRN%kD-{wHgZJe@iAZ&;;fKD9K%djHjo}ZAe_L;!0?<8!~+0-0*k=_
--- a/dom/quota/test/unit/xpcshell.ini
+++ b/dom/quota/test/unit/xpcshell.ini
@@ -11,23 +11,25 @@ support-files =
   idbSubdirUpgrade1_profile.zip
   idbSubdirUpgrade2_profile.zip
   morgueCleanup_profile.zip
   obsoleteOriginAttributes_profile.zip
   originAttributesUpgrade_profile.zip
   removeAppsUpgrade_profile.zip
   storagePersistentUpgrade_profile.zip
   tempMetadataCleanup_profile.zip
+  version3_0upgrade_profile.zip
 
 [test_basics.js]
 [test_bad_origin_directory.js]
 skip-if = release_or_beta
 [test_defaultStorageUpgrade.js]
 [test_getUsage.js]
 [test_idbSubdirUpgrade.js]
 [test_morgueCleanup.js]
 [test_obsoleteOriginAttributesUpgrade.js]
 [test_originAttributesUpgrade.js]
 [test_persist.js]
 [test_removeAppsUpgrade.js]
 [test_storagePersistentUpgrade.js]
 [test_tempMetadataCleanup.js]
 [test_unknownFiles.js]
+[test_version3_0upgrade.js]
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -684,20 +684,16 @@ AutoEntryScript::AutoEntryScript(JSObjec
                                  const char* aReason,
                                  bool aIsMainThread)
   : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
 {
 }
 
 AutoEntryScript::~AutoEntryScript()
 {
-  // GC when we pop a script entry point. This is a useful heuristic that helps
-  // us out on certain (flawed) benchmarks like sunspider, because it lets us
-  // avoid GCing during the timing loop.
-  JS_MaybeGC(cx());
 }
 
 AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
                                                             const char* aReason)
   : JS::dbg::AutoEntryMonitor(aCx)
   , mReason(aReason)
 {
 }
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_13.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+  function ok(result, desc) {
+    window.parent.postMessage({ok: result, desc: desc}, "*");
+  }
+
+  function doStuff() {
+    ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+  }
+</script>
+<script src='file_sandbox_fail.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+  I am sandboxed but with only inline "allow-scripts"
+
+ <!-- Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img13_bad&type=img/png" />
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img13a_bad&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script13_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script13a_bad&type=text/javascript'></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_allow_scripts.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+  <meta charset='utf-8'>
+  <title>Bug 1396320: Fix CSP sandbox regression for allow-scripts</title>
+  </head>
+<body>
+<script type='application/javascript'>
+  window.parent.postMessage({result: document.domain }, '*');
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_allow_scripts.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: sandbox allow-scripts;
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -177,16 +177,17 @@ support-files =
   file_sandbox_5.html
   file_sandbox_6.html
   file_sandbox_7.html
   file_sandbox_8.html
   file_sandbox_9.html
   file_sandbox_10.html
   file_sandbox_11.html
   file_sandbox_12.html
+  file_sandbox_13.html
   file_require_sri_meta.sjs
   file_require_sri_meta.js
   file_sendbeacon.html
   file_upgrade_insecure_docwrite_iframe.sjs
   file_data-uri_blocked.html
   file_data-uri_blocked.html^headers^
   file_strict_dynamic_js_url.html
   file_strict_dynamic_script_events.html
@@ -318,8 +319,12 @@ skip-if = toolkit == 'android'
 [test_data_csp_merge.html]
 [test_report_font_cache.html]
 [test_data_doc_ignore_meta_csp.html]
 [test_meta_csp_self.html]
 [test_uir_top_nav.html]
 support-files =
   file_uir_top_nav.html
   file_uir_top_nav_dummy.html
+[test_sandbox_allow_scripts.html]
+support-files =
+  file_sandbox_allow_scripts.html
+  file_sandbox_allow_scripts.html^headers^
--- a/dom/security/test/csp/test_sandbox.html
+++ b/dom/security/test/csp/test_sandbox.html
@@ -106,17 +106,17 @@ var testCases = [
     results: { img12_bad: -1, script12_bad: -1 },
     nrOKmessages: 4 // sends 4 ok message
   },
   {
     // Test 13: same as Test 5 and Test 11, but:
     // * using sandbox flag 'allow-scripts' in CSP and not as iframe attribute
     // * not using allow-same-origin in CSP (so a new NullPrincipal is created).
     csp: "default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts",
-    file: "file_sandbox_5.html",
+    file: "file_sandbox_13.html",
     results: { img13_bad: -1, img13a_bad: -1, script13_bad: -1, script13a_bad: -1 },
     nrOKmessages: 2 // sends 2 ok message
   },
 ];
 
 // a postMessage handler that is used by sandboxed iframes without
 // 'allow-same-origin' to communicate pass/fail back to this main page.
 // it expects to be called with an object like:
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_sandbox_allow_scripts.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1396320: Fix CSP sandbox regression for allow-scripts</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Load an iframe using a CSP of 'sandbox allow-scripts' and make sure
+ * the security context of the iframe is sandboxed (cross origin)
+ */
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+  is(event.data.result, "",
+  	"document.domain of sandboxed iframe should be opaque");
+  window.removeEventListener("message", receiveMessage);
+  SimpleTest.finish();
+}
+
+let testframe = document.getElementById("testframe");
+testframe.src = "file_sandbox_allow_scripts.html";
+
+</script>
+</body>
+</html>
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -2916,18 +2916,17 @@ HTMLEditor::GetCellFromRange(nsRange* aR
 
   nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer();
   if (NS_WARN_IF(!startContainer)) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t startOffset = aRange->StartOffset();
 
-  nsCOMPtr<nsINode> childNode =
-    startContainer->GetChildAt(static_cast<int32_t>(startOffset));
+  nsCOMPtr<nsINode> childNode = aRange->GetChildAtStartOffset();
   // This means selection is probably at a text node (or end of doc?)
   if (!childNode) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer();
   if (NS_WARN_IF(!endContainer)) {
     return NS_ERROR_FAILURE;
@@ -3197,18 +3196,17 @@ HTMLEditor::GetSelectedOrParentTableElem
   } else {
     nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
     if (NS_WARN_IF(!anchorNode)) {
       return NS_ERROR_FAILURE;
     }
 
     // Get child of anchor node, if exists
     if (anchorNode->HasChildNodes()) {
-      int32_t anchorOffset = selection->AnchorOffset();
-      nsINode* selectedNode = anchorNode->GetChildAt(anchorOffset);
+      nsINode* selectedNode = selection->GetChildAtAnchorOffset();
       if (!selectedNode) {
         selectedNode = anchorNode;
         // If anchor doesn't have a child, we can't be selecting a table element,
         //  so don't do the following:
       } else {
         if (selectedNode->IsHTMLElement(nsGkAtoms::td)) {
           tableOrCellElement = do_QueryInterface(selectedNode);
           aTagName = tdName;
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,8 +1,65 @@
+Overview of changes leading to 1.5.1
+Tuesday, September 5, 2017
+====================================
+
+- Fix "unsafe-to-break" in fallback shaping and other corner cases.
+  All our tests pass with --verify now, meaning unsafe-to-break API
+  works as expected.
+- Add --unicodes to hb-view / hb-shape.
+- [indic] Treat Consonant_With_Stacker as consonant.  This will need
+  further tweaking.
+- hb_buffer_diff() tweaks.
+
+
+Overview of changes leading to 1.5.0
+Wednesday, August 23, 2017
+====================================
+
+- Misc new API, for appending a buffer to another, and for comparing
+  contents of two buffers for types of differences.
+
+- New "unsafe-to-break" API.  Can be used to speed up reshaping
+  in line-breaking situations.  Essentially, after shaping, it returns
+  positions in the input string (some of the cluster boundaries) that
+  are "safe to break" in that if the text is segmented at that position
+  and two sides reshaped and concatenated, the shaping result is
+  exactly the same as shaping the text in one piece.
+
+  hb-view and hb-shape and hb-shape now take --verify, which verifies
+  the above property.
+
+  Some corner cases of the implementation are still not quite working.
+  Those will be fixed in subsequent releases.
+
+- New API:
+
+hb_buffer_append()
+
+hb_glyph_flags_t
+HB_GLYPH_FLAG_UNSAFE_TO_BREAK
+HB_GLYPH_FLAG_DEFINED
+hb_glyph_info_get_glyph_flags()
+
+HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS
+
+hb_buffer_diff_flags_t
+HB_BUFFER_DIFF_FLAG_EQUAL
+HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH
+HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH
+HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT
+HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH
+HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH
+HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH
+HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH
+hb_buffer_diff
+
+
 Overview of changes leading to 1.4.8
 Tuesday, August 8, 2017
 ====================================
 
 - Major fix to avar table handling.
 - Rename hb-shape --show-message to --trace.
 - Build fixes.
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,14 +1,14 @@
-gfx/harfbuzz status as of 2017-08-08:
+gfx/harfbuzz status as of 2017-09-05:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
-Current version: 1.4.8
+Current version: 1.5.1
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
 
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.8],
+        [1.5.1],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
--- a/gfx/harfbuzz/src/harfbuzz-icu.pc
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library ICU integration
-Version: 1.4.8
+Version: 1.5.1
 
 Requires: harfbuzz
 Requires.private: icu-uc
 Libs: -L${libdir} -lharfbuzz-icu
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/harfbuzz.pc
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library
-Version: 1.4.8
+Version: 1.5.1
 
 Libs: -L${libdir} -lharfbuzz
 Libs.private:    
 Requires.private: glib-2.0 >= 2.19.1 
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -45,23 +45,26 @@
 #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
 #endif
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
 HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
 
 enum hb_buffer_scratch_flags_t {
   HB_BUFFER_SCRATCH_FLAG_DEFAULT			= 0x00000000u,
   HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII			= 0x00000001u,
   HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES		= 0x00000002u,
   HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK		= 0x00000004u,
   HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK		= 0x00000010u,
+
   /* Reserved for complex shapers' internal use. */
   HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX2			= 0x04000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX3			= 0x08000000u,
 };
 HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
 
@@ -227,35 +230,41 @@ struct hb_buffer_t {
     for (unsigned int j = 0; j < len; j++)
       info[j].mask = mask;
   }
   inline void add_masks (hb_mask_t mask)
   {
     for (unsigned int j = 0; j < len; j++)
       info[j].mask |= mask;
   }
-  HB_INTERNAL void set_masks (hb_mask_t value,
-			      hb_mask_t mask,
-			      unsigned int cluster_start,
-			      unsigned int cluster_end);
+  HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask,
+			      unsigned int cluster_start, unsigned int cluster_end);
 
-  HB_INTERNAL void merge_clusters (unsigned int start,
-				   unsigned int end)
+  inline void merge_clusters (unsigned int start, unsigned int end)
   {
     if (end - start < 2)
       return;
     merge_clusters_impl (start, end);
   }
-  HB_INTERNAL void merge_clusters_impl (unsigned int start,
-					unsigned int end);
-  HB_INTERNAL void merge_out_clusters (unsigned int start,
-				       unsigned int end);
+  HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
   /* Merge clusters for deleting current glyph, and skip it. */
   HB_INTERNAL void delete_glyph (void);
 
+  inline void unsafe_to_break (unsigned int start,
+			       unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    unsafe_to_break_impl (start, end);
+  }
+  HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
+
+
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
   { return likely (!size || size < allocated) ? true : enlarge (size); }
 
   inline bool ensure_inplace (unsigned int size)
   { return likely (!size || size < allocated); }
@@ -277,19 +286,76 @@ struct hb_buffer_t {
       return true;
     va_list ap;
     va_start (ap, fmt);
     bool ret = message_impl (font, fmt, ap);
     va_end (ap);
     return ret;
   }
   HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
+
+  static inline void
+  set_cluster (hb_glyph_info_t &info, unsigned int cluster, unsigned int mask = 0)
+  {
+    if (info.cluster != cluster)
+    {
+      if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+	info.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      else
+	info.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+    }
+    info.cluster = cluster;
+  }
+
+  int
+  _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *info,
+				     unsigned int start, unsigned int end,
+				     unsigned int cluster) const
+  {
+    for (unsigned int i = start; i < end; i++)
+      cluster = MIN (cluster, info[i].cluster);
+    return cluster;
+  }
+  void
+  _unsafe_to_break_set_mask (hb_glyph_info_t *info,
+			     unsigned int start, unsigned int end,
+			     unsigned int cluster)
+  {
+    for (unsigned int i = start; i < end; i++)
+      if (cluster != info[i].cluster)
+      {
+	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
+	info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      }
+  }
 };
 
 
+/* Loop over clusters. Duplicated in foreach_syllable(). */
+#define foreach_cluster(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_cluster (buffer, start))
+
+static inline unsigned int
+_next_cluster (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int cluster = info[start].cluster;
+  while (++start < count && cluster == info[start].cluster)
+    ;
+
+  return start;
+}
+
+
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var))
 #define HB_BUFFER_ALLOCATE_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, allocate_var,   var ())
 #define HB_BUFFER_DEALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ())
 #define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,     var ())
 
 
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -140,30 +140,36 @@ static unsigned int
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
-		     pos[i].x_offset, pos[i].y_offset);
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
-		     pos[i].x_advance, pos[i].y_advance);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+			     pos[i].x_offset, pos[i].y_offset));
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
+			     pos[i].x_advance, pos[i].y_advance));
+    }
+
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
     }
 
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
     {
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
-        extents.x_bearing, extents.y_bearing));
+		extents.x_bearing, extents.y_bearing));
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
-        extents.width, extents.height));
+		extents.width, extents.height));
     }
 
     *p++ = '}';
 
     unsigned int l = p - b;
     if (buf_size > l)
     {
       memcpy (buf, b, l);
@@ -221,16 +227,22 @@ static unsigned int
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset));
 
       *p++ = '+';
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
       if (pos[i].y_advance)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
     }
 
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask &HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
+    }
+
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
     {
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
     }
 
     unsigned int l = p - b;
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -26,20 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-buffer-private.hh"
 #include "hb-utf-private.hh"
 
 
-#ifndef HB_DEBUG_BUFFER
-#define HB_DEBUG_BUFFER (HB_DEBUG+0)
-#endif
-
 /**
  * SECTION: hb-buffer
  * @title: Buffers
  * @short_description: Input and output buffers
  * @include: hb.h
  *
  * Buffers serve dual role in HarfBuzz; they hold the input characters that are
  * passed hb_shape(), and after shaping they hold the output glyphs.
@@ -262,17 +258,17 @@ hb_buffer_t::add (hb_codepoint_t  codepo
   hb_glyph_info_t *glyph;
 
   if (unlikely (!ensure (len + 1))) return;
 
   glyph = &info[len];
 
   memset (glyph, 0, sizeof (*glyph));
   glyph->codepoint = codepoint;
-  glyph->mask = 1;
+  glyph->mask = 0;
   glyph->cluster = cluster;
 
   len++;
 }
 
 void
 hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
 {
@@ -545,104 +541,136 @@ hb_buffer_t::reverse_clusters (void)
   reverse_range (start, i);
 }
 
 void
 hb_buffer_t::merge_clusters_impl (unsigned int start,
 				  unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+  {
+    unsafe_to_break (start, end);
     return;
+  }
 
-  uint32_t cluster = info[start].cluster;
+  unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, info[i].cluster);
 
   /* Extend end */
   while (end < len && info[end - 1].cluster == info[end].cluster)
     end++;
 
   /* Extend start */
   while (idx < start && info[start - 1].cluster == info[start].cluster)
     start--;
 
   /* If we hit the start of buffer, continue in out-buffer. */
   if (idx == start)
     for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
-      out_info[i - 1].cluster = cluster;
+      set_cluster (out_info[i - 1], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    info[i].cluster = cluster;
+    set_cluster (info[i], cluster);
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
   if (unlikely (end - start < 2))
     return;
 
-  uint32_t cluster = out_info[start].cluster;
+  unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, out_info[i].cluster);
 
   /* Extend start */
   while (start && out_info[start - 1].cluster == out_info[start].cluster)
     start--;
 
   /* Extend end */
   while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
     end++;
 
   /* If we hit the end of out-buffer, continue in buffer. */
   if (end == out_len)
     for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
-      info[i].cluster = cluster;
+      set_cluster (info[i], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    out_info[i].cluster = cluster;
+    set_cluster (out_info[i], cluster);
 }
 void
 hb_buffer_t::delete_glyph ()
 {
+  /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */
+
   unsigned int cluster = info[idx].cluster;
   if (idx + 1 < len && cluster == info[idx + 1].cluster)
   {
     /* Cluster survives; do nothing. */
     goto done;
   }
 
   if (out_len)
   {
     /* Merge cluster backward. */
     if (cluster < out_info[out_len - 1].cluster)
     {
+      unsigned int mask = info[idx].mask;
       unsigned int old_cluster = out_info[out_len - 1].cluster;
       for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
-	out_info[i - 1].cluster = cluster;
+	set_cluster (out_info[i - 1], cluster, mask);
     }
     goto done;
   }
 
   if (idx + 1 < len)
   {
     /* Merge cluster forward. */
     merge_clusters (idx, idx + 2);
     goto done;
   }
 
 done:
   skip_glyph ();
 }
 
 void
+hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
+{
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
+  _unsafe_to_break_set_mask (info, start, end, cluster);
+}
+void
+hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
+{
+  if (!have_output)
+  {
+    unsafe_to_break_impl (start, end);
+    return;
+  }
+
+  assert (start <= out_len);
+  assert (idx <= end);
+
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
+  cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
+  _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
+  _unsafe_to_break_set_mask (info, idx, end, cluster);
+}
+
+void
 hb_buffer_t::guess_segment_properties (void)
 {
   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
     for (unsigned int i = 0; i < len; i++) {
@@ -1375,16 +1403,33 @@ hb_buffer_get_glyph_positions (hb_buffer
 
   if (length)
     *length = buffer->len;
 
   return (hb_glyph_position_t *) buffer->pos;
 }
 
 /**
+ * hb_glyph_info_get_glyph_flags:
+ * @info: a #hb_glyph_info_t.
+ *
+ * Returns glyph flags encoded within a #hb_glyph_info_t.
+ *
+ * Return value:
+ * The #hb_glyph_flags_t encoded within @info.
+ *
+ * Since: 1.5.0
+ **/
+hb_glyph_flags_t
+(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info)
+{
+  return hb_glyph_info_get_glyph_flags (info);
+}
+
+/**
  * hb_buffer_reverse:
  * @buffer: an #hb_buffer_t.
  *
  * Reverses buffer contents.
  *
  * Since: 0.9.2
  **/
 void
@@ -1661,16 +1706,68 @@ hb_buffer_add_codepoints (hb_buffer_t   
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length)
 {
   hb_buffer_add_utf<hb_utf32_t<false> > (buffer, text, text_length, item_offset, item_length);
 }
 
 
+/**
+ * hb_buffer_append:
+ * @buffer: an #hb_buffer_t.
+ * @source: source #hb_buffer_t.
+ * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
+ * @end: end index into source buffer to copy.  Use (unsigned int) -1 to copy to end of buffer.
+ *
+ * Append (part of) contents of another buffer to this buffer.
+ *
+ * Since: 1.5.0
+ **/
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end)
+{
+  assert (!buffer->have_output && !source->have_output);
+  assert (buffer->have_positions == source->have_positions ||
+	  !buffer->len || !source->len);
+  assert (buffer->content_type == source->content_type ||
+	  !buffer->len || !source->len);
+
+  if (end > source->len)
+    end = source->len;
+  if (start > end)
+    start = end;
+  if (start == end)
+    return;
+
+  if (!buffer->len)
+    buffer->content_type = source->content_type;
+  if (!buffer->have_positions && source->have_positions)
+    buffer->clear_positions ();
+
+  if (buffer->len + (end - start) < buffer->len) /* Overflows. */
+  {
+    buffer->in_error = true;
+    return;
+  }
+
+  unsigned int orig_len = buffer->len;
+  hb_buffer_set_length (buffer, buffer->len + (end - start));
+  if (buffer->in_error)
+    return;
+
+  memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
+  if (buffer->have_positions)
+    memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
+}
+
+
 static int
 compare_info_codepoint (const hb_glyph_info_t *pa,
 			const hb_glyph_info_t *pb)
 {
   return (int) pb->codepoint - (int) pa->codepoint;
 }
 
 static inline void
@@ -1731,17 +1828,18 @@ normalize_glyphs_cluster (hb_buffer_t *b
  * <note>This has nothing to do with Unicode normalization.</note>
  *
  * Since: 0.9.2
  **/
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
-  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
   hb_glyph_info_t *info = buffer->info;
 
   unsigned int start = 0;
@@ -1770,16 +1868,108 @@ hb_buffer_t::sort (unsigned int start, u
     {
       hb_glyph_info_t t = info[i];
       memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t));
       info[j] = t;
     }
   }
 }
 
+
+/*
+ * Comparing buffers.
+ */
+
+/**
+ * hb_buffer_diff:
+ *
+ * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+ * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
+ * callers if just comparing two buffers is needed.
+ *
+ * Since: 1.5.0
+ **/
+hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz)
+{
+  if (buffer->content_type != reference->content_type && buffer->len && reference->len)
+    return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH;
+
+  hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL;
+  bool contains = dottedcircle_glyph != (hb_codepoint_t) -1;
+
+  unsigned int count = reference->len;
+
+  if (buffer->len != count)
+  {
+    /*
+     * we can't compare glyph-by-glyph, but we do want to know if there
+     * are .notdef or dottedcircle glyphs present in the reference buffer
+     */
+    const hb_glyph_info_t *info = reference->info;
+    unsigned int i;
+    for (i = 0; i < count; i++)
+    {
+      if (contains && info[i].codepoint == dottedcircle_glyph)
+        result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+      if (contains && info[i].codepoint == 0)
+        result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    }
+    result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
+    return hb_buffer_diff_flags_t (result);
+  }
+
+  if (!count)
+    return hb_buffer_diff_flags_t (result);
+
+  const hb_glyph_info_t *buf_info = buffer->info;
+  const hb_glyph_info_t *ref_info = reference->info;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (buf_info->codepoint != ref_info->codepoint)
+      result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
+    if (buf_info->cluster != ref_info->cluster)
+      result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
+    if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED))
+      result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
+    if (contains && ref_info->codepoint == dottedcircle_glyph)
+      result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+    if (contains && ref_info->codepoint == 0)
+      result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    buf_info++;
+    ref_info++;
+  }
+
+  if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)
+  {
+    assert (buffer->have_positions);
+    const hb_glyph_position_t *buf_pos = buffer->pos;
+    const hb_glyph_position_t *ref_pos = reference->pos;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz)
+      {
+        result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
+        break;
+      }
+      buf_pos++;
+      ref_pos++;
+    }
+  }
+
+  return result;
+}
+
+
 /*
  * Debugging.
  */
 
 /**
  * hb_buffer_set_message_func:
  * @buffer: an #hb_buffer_t.
  * @func: (closure user_data) (destroy destroy) (scope notified):
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -58,24 +58,37 @@ HB_BEGIN_DECLS
  *           allow selecting more fine-grained cluster handling.
  *
  * The #hb_glyph_info_t is the structure that holds information about the
  * glyphs and their relation to input text.
  *
  */
 typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
-  hb_mask_t      mask;
+  hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
   uint32_t       cluster;
 
   /*< private >*/
   hb_var_int_t   var1;
   hb_var_int_t   var2;
 } hb_glyph_info_t;
 
+typedef enum { /*< flags >*/
+  HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
+
+  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
+} hb_glyph_flags_t;
+
+HB_EXTERN hb_glyph_flags_t
+hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info);
+
+#define hb_glyph_info_get_glyph_flags(info) \
+	((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED))
+
+
 /**
  * hb_glyph_position_t:
  * @x_advance: how much the line advances after drawing this glyph when setting
  *             text in horizontal direction.
  * @y_advance: how much the line advances after drawing this glyph when setting
  *             text in vertical direction.
  * @x_offset: how much the glyph moves on the X-axis before drawing it, this
  *            should not affect how much the line advances.
@@ -158,16 +171,17 @@ hb_buffer_set_user_data (hb_buffer_t    
 			 void *              data,
 			 hb_destroy_func_t   destroy,
 			 hb_bool_t           replace);
 
 HB_EXTERN void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key);
 
+
 /**
  * hb_buffer_content_type_t:
  * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer.
  * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping).
  * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping).
  */
 typedef enum {
   HB_BUFFER_CONTENT_TYPE_INVALID = 0,
@@ -354,16 +368,21 @@ hb_buffer_add_latin1 (hb_buffer_t   *buf
 
 HB_EXTERN void
 hb_buffer_add_codepoints (hb_buffer_t          *buffer,
 			  const hb_codepoint_t *text,
 			  int                   text_length,
 			  unsigned int          item_offset,
 			  int                   item_length);
 
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end);
 
 HB_EXTERN hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length);
 
 HB_EXTERN unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer);
 
@@ -398,17 +417,18 @@ hb_buffer_normalize_glyphs (hb_buffer_t 
  *
  * Since: 0.9.20
  */
 typedef enum { /*< flags >*/
   HB_BUFFER_SERIALIZE_FLAG_DEFAULT		= 0x00000000u,
   HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001u,
   HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002u,
   HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
-  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u,
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS		= 0x00000010u
 } hb_buffer_serialize_flags_t;
 
 /**
  * hb_buffer_serialize_format_t:
  * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format.
  * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format.
  * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format.
  *
@@ -448,16 +468,55 @@ hb_buffer_deserialize_glyphs (hb_buffer_
 			      const char *buf,
 			      int buf_len,
 			      const char **end_ptr,
 			      hb_font_t *font,
 			      hb_buffer_serialize_format_t format);
 
 
 /*
+ * Compare buffers
+ */
+
+typedef enum { /*< flags >*/
+  HB_BUFFER_DIFF_FLAG_EQUAL			= 0x0000,
+
+  /* Buffers with different content_type cannot be meaningfully compared
+   * in any further detail. */
+  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0x0001,
+
+  /* For buffers with differing length, the per-glyph comparison is not
+   * attempted, though we do still scan reference for dottedcircle / .notdef
+   * glyphs. */
+  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0x0002,
+
+  /* We want to know if dottedcircle / .notdef glyphs are present in the
+   * reference, as we may not care so much about other differences in this
+   * case. */
+  HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT		= 0x0004,
+  HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT	= 0x0008,
+
+  /* If the buffers have the same length, we compare them glyph-by-glyph
+   * and report which aspect(s) of the glyph info/position are different. */
+  HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH	= 0x0010,
+  HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH		= 0x0020,
+  HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH	= 0x0040,
+  HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH		= 0x0080
+
+} hb_buffer_diff_flags_t;
+
+/* Compare the contents of two buffers, report types of differences. */
+hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz);
+
+
+/*
  * Debugging.
  */
 
 typedef hb_bool_t	(*hb_buffer_message_func_t)	(hb_buffer_t *buffer,
 							 hb_font_t   *font,
 							 const char  *message,
 							 void        *user_data);
 
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -1175,24 +1175,30 @@ resize_and_retry:
     hb_glyph_info_t *info = buffer->info;
     hb_glyph_position_t *pos = buffer->pos;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->x_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
+	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+
 	info++, pos++;
       }
     else
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->y_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
+	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+
 	info++, pos++;
       }
 
     /* Fix up clusters so that we never return out-of-order indices;
      * if core text has reordered glyphs, we'll merge them to the
      * beginning of the reordered cluster.  CoreText is nice enough
      * to tell us whenever it has produced nonmonotonic results...
      * Note that we assume the input clusters were nonmonotonic to
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -875,16 +875,18 @@ retry_getglyphs:
     hb_glyph_info_t *info = &buffer->info[i];
     hb_glyph_position_t *pos = &buffer->pos[i];
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
     pos->x_offset =
       x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
+
+    info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 
   if (isRightToLeft)
     hb_buffer_reverse (buffer);
 
   free (clusterMap);
   free (glyphIndices);
   free (textProperties);
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -50,20 +50,20 @@ struct hb_face_t {
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
   unsigned int index;			/* Face index in a collection, zero-based. */
   mutable unsigned int upem;		/* Units-per-EM. */
   mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
   enum dirty_t {
-    NOTHING	= 0x0000,
-    INDEX	= 0x0001,
-    UPEM	= 0x0002,
-    NUM_GLYPHS	= 0x0004,
+    DIRTY_NOTHING	= 0x0000,
+    DIRTY_INDEX		= 0x0001,
+    DIRTY_UPEM		= 0x0002,
+    DIRTY_NUM_GLYPHS	= 0x0004,
   } dirty;
 
   struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
   /* Various non-shaping data. */
   /* ... */
 
   /* Cache */
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -46,17 +46,17 @@ const hb_face_t _hb_face_nil = {
   NULL, /* reference_table_func */
   NULL, /* user_data */
   NULL, /* destroy */
 
   0,    /* index */
   1000, /* upem */
   0,    /* num_glyphs */
 
-  hb_face_t::NOTHING, /* dirty */
+  hb_face_t::DIRTY_NOTHING, /* dirty */
 
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   },
 
   NULL, /* shape_plans */
@@ -365,17 +365,17 @@ hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (face->immutable)
     return;
 
   if (face->index == index)
     return;
 
-  face->dirty |= face->INDEX;
+  face->dirty |= face->DIRTY_INDEX;
 
   face->index = index;
 }
 
 /**
  * hb_face_get_index:
  * @face: a face.
  *
@@ -405,17 +405,17 @@ hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem)
 {
   if (face->immutable)
     return;
 
   if (face->upem == upem)
     return;
 
-  face->dirty |= face->UPEM;
+  face->dirty |= face->DIRTY_UPEM;
 
   face->upem = upem;
 }
 
 /**
  * hb_face_get_upem:
  * @face: a face.
  *
@@ -454,17 +454,17 @@ hb_face_set_glyph_count (hb_face_t    *f
 			 unsigned int  glyph_count)
 {
   if (face->immutable)
     return;
 
   if (face->num_glyphs == glyph_count)
     return;
 
-  face->dirty |= face->NUM_GLYPHS;
+  face->dirty |= face->DIRTY_NUM_GLYPHS;
 
   face->num_glyphs = glyph_count;
 }
 
 /**
  * hb_face_get_glyph_count:
  * @face: a face.
  *
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -112,23 +112,23 @@ struct hb_font_t {
   unsigned int num_coords;
   int *coords;
 
   hb_font_funcs_t   *klass;
   void              *user_data;
   hb_destroy_func_t  destroy;
 
   enum dirty_t {
-    NOTHING	= 0x0000,
-    FACE	= 0x0001,
-    PARENT	= 0x0002,
-    FUNCS	= 0x0004,
-    SCALE	= 0x0008,
-    PPEM	= 0x0010,
-    VARIATIONS	= 0x0020,
+    DIRTY_NOTHING	= 0x0000,
+    DIRTY_FACE		= 0x0001,
+    DIRTY_PARENT	= 0x0002,
+    DIRTY_FUNCS		= 0x0004,
+    DIRTY_SCALE		= 0x0008,
+    DIRTY_PPEM		= 0x0010,
+    DIRTY_VARIATIONS	= 0x0020,
   } dirty;
 
   struct hb_shaper_data_t shaper_data;
 
 
   /* Convert from font-space to user-space */
   inline int dir_scale (hb_direction_t direction)
   { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -1191,17 +1191,17 @@ hb_font_get_empty (void)
 
     0, /* num_coords */
     NULL, /* coords */
 
     const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
     NULL, /* user_data */
     NULL, /* destroy */
 
-    hb_font_t::NOTHING, /* dirty */
+    hb_font_t::DIRTY_NOTHING, /* dirty */
 
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
     }
   };
 
@@ -1348,17 +1348,17 @@ hb_font_set_parent (hb_font_t *font,
     return;
 
   if (!parent)
     parent = hb_font_get_empty ();
 
   if (parent == font->parent)
     return;
 
-  font->dirty |= font->PARENT;
+  font->dirty |= font->DIRTY_PARENT;
 
   hb_font_t *old = font->parent;
 
   font->parent = hb_font_reference (parent);
 
   hb_font_destroy (old);
 }
 
@@ -1395,17 +1395,17 @@ hb_font_set_face (hb_font_t *font,
     return;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
 
   if (font->face == face)
     return;
 
-  font->dirty |= font->FACE;
+  font->dirty |= font->DIRTY_FACE;
 
   hb_face_t *old = font->face;
 
   font->face = hb_face_reference (face);
 
   hb_face_destroy (old);
 }
 
@@ -1450,17 +1450,17 @@ hb_font_set_funcs (hb_font_t         *fo
   }
 
   if (font->destroy)
     font->destroy (font->user_data);
 
   if (!klass)
     klass = hb_font_funcs_get_empty ();
 
-  font->dirty |= font->FUNCS;
+  font->dirty |= font->DIRTY_FUNCS;
 
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
   font->klass = klass;
   font->user_data = font_data;
   font->destroy = destroy;
 }
 
@@ -1510,17 +1510,17 @@ hb_font_set_scale (hb_font_t *font,
 		   int y_scale)
 {
   if (font->immutable)
     return;
 
   if (font->x_scale == x_scale && font->y_scale == y_scale)
     return;
 
-  font->dirty |= font->SCALE;
+  font->dirty |= font->DIRTY_SCALE;
 
   font->x_scale = x_scale;
   font->y_scale = y_scale;
 }
 
 /**
  * hb_font_get_scale:
  * @font: a font.
@@ -1556,17 +1556,17 @@ hb_font_set_ppem (hb_font_t *font,
 		  unsigned int y_ppem)
 {
   if (font->immutable)
     return;
 
   if (font->x_ppem == x_ppem && font->y_ppem == y_ppem)
     return;
 
-  font->dirty |= font->PPEM;
+  font->dirty |= font->DIRTY_PPEM;
 
   font->x_ppem = x_ppem;
   font->y_ppem = y_ppem;
 }
 
 /**
  * hb_font_get_ppem:
  * @font: a font.
@@ -1598,17 +1598,17 @@ static void
   if (font->num_coords == coords_length &&
       (coords_length == 0 ||
        0 == memcmp (font->coords, coords, coords_length * sizeof (coords[0]))))
   {
     free (coords);
     return;
   }
 
-  font->dirty |= font->VARIATIONS;
+  font->dirty |= font->DIRTY_VARIATIONS;
 
   free (font->coords);
 
   font->coords = coords;
   font->num_coords = coords_length;
 }
 
 /**
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -488,17 +488,17 @@ reference_table  (hb_face_t *face HB_UNU
 
   /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
   if (error)
     return NULL;
 
   buffer = (FT_Byte *) malloc (length);
-  if (buffer == NULL)
+  if (!buffer)
     return NULL;
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
   if (error)
     return NULL;
 
   return hb_blob_create ((const char *) buffer, length,
 			 HB_MEMORY_MODE_WRITABLE,
@@ -516,17 +516,17 @@ reference_table  (hb_face_t *face HB_UNU
  * Since: 0.9.2
  **/
 hb_face_t *
 hb_ft_face_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy)
 {
   hb_face_t *face;
 
-  if (ft_face->stream->read == NULL) {
+  if (!ft_face->stream->read) {
     hb_blob_t *blob;
 
     blob = hb_blob_create ((const char *) ft_face->stream->base,
 			   (unsigned int) ft_face->stream->size,
 			   HB_MEMORY_MODE_READONLY,
 			   ft_face, destroy);
     face = hb_face_create (blob, ft_face->face_index);
     hb_blob_destroy (blob);
@@ -627,19 +627,19 @@ hb_ft_font_create (FT_Face           ft_
     {
       if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
       {
 	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
 	  coords[i] = ft_coords[i] >>= 2;
 
 	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
       }
-      free (coords);
-      free (ft_coords);
     }
+    free (coords);
+    free (ft_coords);
     free (mm_var);
   }
 #endif
 
   return font;
 }
 
 /**
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -350,27 +350,28 @@ hb_bool_t
 
   for (unsigned int i = 0; i < ci; ++i)
   {
     for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
     {
       hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
       info->codepoint = gids[clusters[i].base_glyph + j];
       info->cluster = clusters[i].cluster;
+      info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
       info->var1.i32 = clusters[i].advance;     // all glyphs in the cluster get the same advance
     }
   }
   buffer->len = glyph_count;
 
   unsigned int upem = hb_face_get_upem (face);
   float xscale = (float) font->x_scale / upem;
   float yscale = (float) font->y_scale / upem;
   yscale *= yscale / xscale;
   /* Positioning. */
-  int currclus = -1;
+  unsigned int currclus = (unsigned int) -1;
   const hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
   if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
     curradvx = 0;
     for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
     {
       pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -219,17 +219,17 @@ struct hb_ot_face_glyf_accelerator_t
 struct hb_ot_face_cbdt_accelerator_t
 {
   hb_blob_t *cblc_blob;
   hb_blob_t *cbdt_blob;
   const OT::CBLC *cblc;
   const OT::CBDT *cbdt;
 
   unsigned int cbdt_len;
-  float upem;
+  unsigned int upem;
 
   inline void init (hb_face_t *face)
   {
     upem = face->get_upem();
 
     cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC));
     cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT));
     cbdt_len = hb_blob_get_length (cbdt_blob);
@@ -249,21 +249,21 @@ struct hb_ot_face_cbdt_accelerator_t
     hb_blob_destroy (this->cblc_blob);
     hb_blob_destroy (this->cbdt_blob);
   }
 
   inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   {
     unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
 
-    if (cblc == NULL)
+    if (!cblc)
       return false;  // Not a color bitmap font.
 
     const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
-    if (subtable_record == NULL)
+    if (!subtable_record || !x_ppem || !y_ppem)
       return false;
 
     if (subtable_record->get_extents (extents))
       return true;
 
     unsigned int image_offset = 0, image_length = 0, image_format = 0;
     if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
       return false;
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -936,17 +936,17 @@ struct Coverage
     switch (u.format) {
     case 1: u.format1.add_coverage (glyphs); break;
     case 2: u.format2.add_coverage (glyphs); break;
     default:                                 break;
     }
   }
 
   struct Iter {
-    Iter (void) : format (0) {};
+    Iter (void) : format (0), u () {};
     inline void init (const Coverage &c_) {
       format = c_.u.format;
       switch (format) {
       case 1: u.format1.init (c_.u.format1); return;
       case 2: u.format2.init (c_.u.format2); return;
       default:                               return;
       }
     }
@@ -977,18 +977,18 @@ struct Coverage
       case 2: return u.format2.get_coverage ();
       default:return -1;
       }
     }
 
     private:
     unsigned int format;
     union {
+    CoverageFormat2::Iter	format2; /* Put this one first since it's larger; helps shut up compiler. */
     CoverageFormat1::Iter	format1;
-    CoverageFormat2::Iter	format2;
     } u;
   };
 
   protected:
   union {
   USHORT		format;		/* Format identifier */
   CoverageFormat1	format1;
   CoverageFormat2	format2;
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -427,16 +427,17 @@ struct MarkArray : ArrayOf<MarkRecord>	/
     bool found;
     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
     /* If this subtable doesn't have an anchor for this base and this class,
      * return false such that the subsequent subtables have a chance at it. */
     if (unlikely (!found)) return_trace (false);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
+    buffer->unsafe_to_break (glyph_pos, buffer->idx);
     mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
     o.attach_type() = ATTACH_TYPE_MARK;
     o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
@@ -638,16 +639,17 @@ struct PairSet
       const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
       hb_codepoint_t mid_x = record->secondGlyph;
       if (x < mid_x)
         max = mid - 1;
       else if (x > mid_x)
         min = mid + 1;
       else
       {
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
 	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
 	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
 	return_trace (true);
       }
     }
@@ -785,16 +787,17 @@ struct PairPosFormat2
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
+    buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c, this, v, buffer->cur_pos());
     valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
       buffer->idx++;
 
@@ -924,16 +927,17 @@ struct CursivePosFormat1
     if (!skippy_iter.next ()) return_trace (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
     if (!next_record.entryAnchor) return_trace (false);
 
     unsigned int i = buffer->idx;
     unsigned int j = skippy_iter.idx;
 
+    buffer->unsafe_to_break (i, j);
     hb_position_t entry_x, entry_y, exit_x, exit_y;
     (this+this_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
     (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
 
     hb_glyph_position_t *pos = buffer->pos;
 
     hb_position_t d;
     /* Main-direction adjustment */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -1009,24 +1009,27 @@ struct ReverseChainSingleSubstFormat1
       return_trace (false); /* No chaining to this type */
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
 
+  unsigned int start_index = 0, end_index = 0;
     if (match_backtrack (c,
 			 backtrack.len, (USHORT *) backtrack.array,
-			 match_coverage, this) &&
+			 match_coverage, this,
+			 &start_index) &&
         match_lookahead (c,
 			 lookahead.len, (USHORT *) lookahead.array,
 			 match_coverage, this,
-			 1))
+			 1, &end_index))
     {
+      c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
       c->replace_glyph_inplace (substitute[index]);
       /* Note: We DON'T decrease buffer->idx.  The main loop does it
        * for us.  This is useful for preventing surprises if someone
        * calls us through a Context lookup. */
       return_trace (true);
     }
 
     return_trace (false);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -886,48 +886,54 @@ static inline bool ligate_input (hb_appl
   }
   return_trace (true);
 }
 
 static inline bool match_backtrack (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT backtrack[],
 				    match_func_t match_func,
-				    const void *match_data)
+				    const void *match_data,
+				    unsigned int *match_start)
 {
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->backtrack_len (), count);
   skippy_iter.set_match_func (match_func, match_data, backtrack);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.prev ())
       return_trace (false);
 
+  *match_start = skippy_iter.idx;
+
   return_trace (true);
 }
 
 static inline bool match_lookahead (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
-				    unsigned int offset)
+				    unsigned int offset,
+				    unsigned int *end_index)
 {
   TRACE_APPLY (NULL);
 
   hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->idx + offset - 1, count);
   skippy_iter.set_match_func (match_func, match_data, lookahead);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.next ())
       return_trace (false);
 
+  *end_index = skippy_iter.idx + 1;
+
   return_trace (true);
 }
 
 
 
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1140,20 +1146,21 @@ static inline bool context_apply_lookup 
 					 ContextApplyLookupContext &lookup_context)
 {
   unsigned int match_length = 0;
   unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data,
 		      &match_length, match_positions)
-      && apply_lookup (c,
+      && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
+	  apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct Rule
 {
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
@@ -1661,33 +1668,35 @@ static inline bool chain_context_apply_l
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
 					       const USHORT input[], /* Array of input values--start with second glyph */
 					       unsigned int lookaheadCount,
 					       const USHORT lookahead[],
 					       unsigned int lookupCount,
 					       const LookupRecord lookupRecord[],
 					       ChainContextApplyLookupContext &lookup_context)
 {
-  unsigned int match_length = 0;
+  unsigned int start_index = 0, match_length = 0, end_index = 0;
   unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data[1],
 		      &match_length, match_positions)
       && match_backtrack (c,
 			  backtrackCount, backtrack,
-			  lookup_context.funcs.match, lookup_context.match_data[0])
+			  lookup_context.funcs.match, lookup_context.match_data[0],
+			  &start_index)
       && match_lookahead (c,
 			  lookaheadCount, lookahead,
 			  lookup_context.funcs.match, lookup_context.match_data[2],
-			  match_length)
-      && apply_lookup (c,
+			  match_length, &end_index)
+      && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
+          apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct ChainRule
 {
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -192,18 +192,17 @@ HB_INTERNAL void
 #define unicode_props()		var2.u16[0]
 
 /* buffer var allocations, used during the GSUB/GPOS processing */
 #define glyph_props()		var1.u16[0] /* GDEF glyph properties */
 #define lig_props()		var1.u8[2] /* GSUB/GPOS ligature tracking */
 #define syllable()		var1.u8[3] /* GSUB/GPOS shaping boundaries */
 
 
-/* loop over syllables */
-
+/* Loop over syllables. Based on foreach_cluster(). */
 #define foreach_syllable(buffer, start, end) \
   for (unsigned int \
        _count = buffer->len, \
        start = 0, end = _count ? _next_syllable (buffer, 0) : 0; \
        start < _count; \
        start = end, end = _next_syllable (buffer, start))
 
 static inline unsigned int
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -133,17 +133,21 @@ void hb_ot_map_builder_t::add_pause (uns
   current_stage[table_index]++;
 }
 
 void
 hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
-  m.global_mask = 1;
+  ASSERT_STATIC (!(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_popcount32 (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.
    */
   unsigned int required_feature_stage[2] = {0, 0};
@@ -185,17 +189,18 @@ hb_ot_map_builder_t::compile (hb_ot_map_
 	feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]);
 	feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]);
       }
     feature_infos.shrink (j + 1);
   }
 
 
   /* Allocate bits now */
-  unsigned int next_bit = 1;
+  unsigned int next_bit = global_bit_shift + 1;
+
   for (unsigned int i = 0; i < feature_infos.len; i++)
   {
     const feature_info_t *info = &feature_infos[i];
 
     unsigned int bits_needed;
 
     if ((info->flags & F_GLOBAL) && info->max_value == 1)
       /* Uses the global bit */
@@ -244,18 +249,18 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     map->index[0] = feature_index[0];
     map->index[1] = feature_index[1];
     map->stage[0] = info->stage[0];
     map->stage[1] = info->stage[1];
     map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ);
     map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
     if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
-      map->shift = 0;
-      map->mask = 1;
+      map->shift = global_bit_shift;
+      map->mask = global_bit_mask;
     } else {
       map->shift = next_bit;
       map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit);
       next_bit += bits_needed;
       m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1u << map->shift) & map->mask;
     map->needs_fallback = !found;
@@ -282,17 +287,17 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     unsigned int last_num_lookups = 0;
     for (unsigned stage = 0; stage < current_stage[table_index]; stage++)
     {
       if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX &&
 	  required_feature_stage[table_index] == stage)
 	add_lookups (m, face, table_index,
 		     required_feature_index[table_index],
 		     variations_index,
-		     1 /* mask */);
+		     global_bit_mask);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
 	  add_lookups (m, face, table_index,
 		       m.features[i].index[table_index],
 		       variations_index,
 		       m.features[i].mask,
 		       m.features[i].auto_zwnj,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -316,17 +316,20 @@ arabic_joining (hb_buffer_t *buffer)
     if (unlikely (this_type == JOINING_TYPE_T)) {
       info[i].arabic_shaping_action() = NONE;
       continue;
     }
 
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
     if (entry->prev_action != NONE && prev != (unsigned int) -1)
+    {
       info[prev].arabic_shaping_action() = entry->prev_action;
+      buffer->unsafe_to_break (prev, i + 1);
+    }
 
     info[i].arabic_shaping_action() = entry->curr_action;
 
     prev = i;
     state = entry->next_state;
   }
 
   for (unsigned int i = 0; i < buffer->context_len[1]; i++)
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -197,16 +197,17 @@ preprocess_text_hangul (const hb_ot_shap
       /*
        * We could cache the width of the tone marks and the existence of dotted-circle,
        * but the use of the Hangul tone mark characters seems to be rare enough that
        * I didn't bother for now.
        */
       if (start < end && end == buffer->out_len)
       {
 	/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
+        buffer->unsafe_to_break_from_outbuffer (start, buffer->idx);
 	buffer->next_glyph ();
 	if (!is_zero_width_char (font, u))
 	{
 	  buffer->merge_out_clusters (start, end + 1);
 	  hb_glyph_info_t *info = buffer->out_info;
 	  hb_glyph_info_t tone = info[end];
 	  memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
 	  info[start] = tone;
@@ -253,16 +254,17 @@ preprocess_text_hangul (const hb_ot_shap
 	if (buffer->idx + 2 < count)
 	{
 	  t = buffer->cur(+2).codepoint;
 	  if (isT (t))
 	    tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */
 	  else
 	    t = 0; /* The next character was not a trailing jamo. */
 	}
+	buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2));
 
 	/* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
 	if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t)))
 	{
 	  /* Try to compose; if this succeeds, end is set to start+1. */
 	  hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex;
 	  if (font->has_glyph (s))
 	  {
@@ -317,16 +319,18 @@ preprocess_text_hangul (const hb_ot_shap
 	if (font->has_glyph (new_s))
 	{
 	  buffer->replace_glyphs (2, 1, &new_s);
 	  if (unlikely (buffer->in_error))
 	    return;
 	  end = start + 1;
 	  continue;
 	}
+	else
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       /* Otherwise, decompose if font doesn't support <LV> or <LVT>,
        * or if having non-combining <LV,T>.  Note that we already handled
        * combining <LV,T> above. */
       if (!has_glyph ||
 	  (!tindex &&
 	   buffer->idx + 1 < count &&
@@ -363,16 +367,18 @@ preprocess_text_hangul (const hb_ot_shap
 	  info[i++].hangul_shaping_feature() = LJMO;
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
 	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
 	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
+	else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       if (has_glyph)
       {
         /* We didn't decompose the S, so just advance past it. */
 	end = start + 1;
 	buffer->next_glyph ();
 	continue;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -116,17 +116,17 @@ enum indic_syllabic_category_t {
   INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER		= OT_C,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER		= OT_M, /* U+17CD only. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL		= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER		= OT_PLACEHOLDER,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	= OT_Repha,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED		= OT_X, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED		= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_N,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_Repha, /* TODO */
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_C,
   INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER		= OT_Coeng,
   INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
   INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER		= OT_X,
   INDIC_SYLLABIC_CATEGORY_NON_JOINER			= OT_ZWNJ,
   INDIC_SYLLABIC_CATEGORY_NUKTA				= OT_N,
   INDIC_SYLLABIC_CATEGORY_NUMBER			= OT_PLACEHOLDER,
   INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER			= OT_PLACEHOLDER, /* Don't care. */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -619,16 +619,18 @@ setup_masks_indic (const hb_ot_shape_pla
 }
 
 static void
 setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->indic_position();
   int b = pb->indic_position();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -292,16 +292,18 @@ setup_masks_myanmar (const hb_ot_shape_p
 }
 
 static void
 setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
 compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->myanmar_position();
   int b = pb->myanmar_position();
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -239,16 +239,17 @@ do_thai_pua_shaping (const hb_ot_shape_p
     const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt];
     const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt];
     above_state = above_edge.next_state;
     below_state = below_edge.next_state;
 
     /* At least one of the above/below actions is NOP. */
     thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action;
 
+    buffer->unsafe_to_break (base, i);
     if (action == RD)
       info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font);
     else
       info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font);
   }
 }
 
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -349,16 +349,18 @@ setup_topographical_masks (const hb_ot_s
 }
 
 static void
 setup_syllables (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
   setup_rphf_mask (plan, buffer);
   setup_topographical_masks (plan, buffer);
 }
 
 static void
 clear_substitution_flags (const hb_ot_shape_plan_t *plan,
 			  hb_font_t *font HB_UNUSED,
 			  hb_buffer_t *buffer)
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -302,16 +302,19 @@ position_mark (const hb_ot_shape_plan_t 
 static inline void
 position_around_base (const hb_ot_shape_plan_t *plan,
 		      hb_font_t *font,
 		      hb_buffer_t  *buffer,
 		      unsigned int base,
 		      unsigned int end)
 {
   hb_direction_t horiz_dir = HB_DIRECTION_INVALID;
+
+  buffer->unsafe_to_break (base, end);
+
   hb_glyph_extents_t base_extents;
   if (!font->get_glyph_extents (buffer->info[base].codepoint,
 				&base_extents))
   {
     /* If extents don't work, zero marks and go home. */
     zero_mark_advances (buffer, base + 1, end);
     return;
   }
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -270,34 +270,39 @@ hb_insert_dotted_circle (hb_buffer_t *bu
     buffer->next_glyph ();
 
   buffer->swap_buffers ();
 }
 
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
-  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
-      buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
     return;
 
   /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
   unsigned int base = 0;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
   {
     if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
 		!_hb_glyph_info_is_joiner (&info[i])))
     {
-      buffer->merge_clusters (base, i);
+      if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	buffer->merge_clusters (base, i);
+      else
+	buffer->unsafe_to_break (base, i);
       base = i;
     }
   }
-  buffer->merge_clusters (base, count);
+  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    buffer->merge_clusters (base, count);
+  else
+    buffer->unsafe_to_break (base, count);
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical:
@@ -389,16 +394,18 @@ hb_ot_shape_setup_masks_fraction (hb_ot_
 	     _hb_glyph_info_get_general_category (&info[start - 1]) ==
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         start--;
       while (end < count &&
 	     _hb_glyph_info_get_general_category (&info[end]) ==
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         end++;
 
+      buffer->unsafe_to_break (start, end);
+
       for (unsigned int j = start; j < i; j++)
         info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
       for (unsigned int j = i + 1; j < end; j++)
         info[j].mask |= post_mask;
 
       i = end - 1;
     }
@@ -504,19 +511,20 @@ hb_ot_hide_default_ignorables (hb_ot_sha
 	if (i + 1 < count && cluster == info[i + 1].cluster)
 	  continue; /* Cluster survives; do nothing. */
 
 	if (j)
 	{
 	  /* Merge cluster backward. */
 	  if (cluster < info[j - 1].cluster)
 	  {
+	    unsigned int mask = info[i].mask;
 	    unsigned int old_cluster = info[j - 1].cluster;
 	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
-	      info[k - 1].cluster = cluster;
+	      buffer->set_cluster (info[k - 1], cluster, mask);
 	  }
 	  continue;
 	}
 
 	if (i + 1 < count)
 	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
 
 	continue;
@@ -572,18 +580,16 @@ hb_synthesize_glyph_classes (hb_ot_shape
   }
 }
 
 static inline void
 hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
-  hb_ot_shape_initialize_masks (c);
-
   hb_ot_mirror_chars (c);
 
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
 
   _hb_ot_shape_normalize (c->plan, buffer, c->font);
 
   hb_ot_shape_setup_masks (c);
 
@@ -777,16 +783,41 @@ hb_ot_position (hb_ot_shape_context_t *c
   /* Visual fallback goes here. */
 
   if (c->fallback_positioning)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
+static inline void
+hb_propagate_flags (hb_buffer_t *buffer)
+{
+  /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
+   * Simplifies using them. */
+
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
+    return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_cluster (buffer, start, end)
+  {
+    unsigned int mask = 0;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+      {
+	 mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+	 break;
+      }
+    if (mask)
+      for (unsigned int i = start; i < end; i++)
+	info[i].mask |= mask;
+  }
+}
 
 /* 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;
@@ -803,33 +834,37 @@ hb_ot_shape_internal (hb_ot_shape_contex
 
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
   _hb_buffer_allocate_unicode_vars (c->buffer);
 
   c->buffer->clear_output ();
 
+  hb_ot_shape_initialize_masks (c);
   hb_set_unicode_props (c->buffer);
   hb_insert_dotted_circle (c->buffer, c->font);
+
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
 
   hb_ot_substitute (c);
   hb_ot_position (c);
 
   hb_ot_hide_default_ignorables (c);
 
   if (c->plan->shaper->postprocess_glyphs)
     c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
 
+  hb_propagate_flags (c->buffer);
+
   _hb_buffer_deallocate_unicode_vars (c->buffer);
 
   c->buffer->props.direction = c->target_direction;
 
   c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
   c->buffer->deallocate_var_all ();
 }
 
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -155,17 +155,17 @@ hb_shape_plan_create2 (hb_face_t        
     free (coords);
     free (features);
     return hb_shape_plan_get_empty ();
   }
 
   assert (props->direction != HB_DIRECTION_INVALID);
 
   hb_face_make_immutable (face);
-  shape_plan->default_shaper_list = shaper_list == NULL;
+  shape_plan->default_shaper_list = !shaper_list;
   shape_plan->face_unsafe = face;
   shape_plan->props = *props;
   shape_plan->num_user_features = num_user_features;
   shape_plan->user_features = features;
   if (num_user_features)
     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
   shape_plan->num_coords = num_coords;
   shape_plan->coords = coords;
@@ -418,17 +418,17 @@ hb_shape_plan_coords_match (const hb_sha
 
 static hb_bool_t
 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
 		       const hb_shape_plan_proposal_t *proposal)
 {
   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
 	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
 	 hb_shape_plan_coords_match (shape_plan, proposal) &&
-	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
+	 ((shape_plan->default_shaper_list && !proposal->shaper_list) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
 
 static inline hb_bool_t
 hb_non_global_user_features_present (const hb_feature_t *user_features,
 				     unsigned int        num_user_features)
 {
   while (num_user_features) {
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -100,16 +100,20 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
     decomposed[ret] = 0;
     return ret;
   }
 
 
   inline unsigned int
   modified_combining_class (hb_codepoint_t unicode)
   {
+    /* XXX This hack belongs to the Arabic shaper:
+     * Put HAMZA ABOVE in the same class as SHADDA. */
+    if (unlikely (unicode == 0x0654u)) unicode = 0x0651u;
+
     /* XXX This hack belongs to the Myanmar shaper. */
     if (unlikely (unicode == 0x1037u)) unicode = 0x103Au;
 
     /* XXX This hack belongs to the SEA shaper (for Tai Tham):
      * Reorder SAKOT to ensure it comes after any tone marks. */
     if (unlikely (unicode == 0x1A60u)) return 254;
 
     /* XXX This hack belongs to the Tibetan shaper:
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -1019,16 +1019,18 @@ retry:
   {
     hb_glyph_info_t *info = &buffer->info[i];
     hb_glyph_position_t *pos = &buffer->pos[i];
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
     pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
+
+    info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 
   if (backward)
     hb_buffer_reverse (buffer);
 
   /* Wow, done! */
   return true;
 }
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -32,20 +32,20 @@
 #define HB_VERSION_H
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
-#define HB_VERSION_MINOR 4
-#define HB_VERSION_MICRO 8
+#define HB_VERSION_MINOR 5
+#define HB_VERSION_MICRO 1
 
-#define HB_VERSION_STRING "1.4.8"
+#define HB_VERSION_STRING "1.5.1"
 
 #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/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -480,16 +480,22 @@ D3D11TextureData::Create(IntSize aSize, 
   if (srcSurf && !DeviceManagerDx::Get()->HasCrashyInitData()) {
     uploadData.pSysMem = sourceMap.mData;
     uploadData.SysMemPitch = sourceMap.mStride;
     uploadData.SysMemSlicePitch = 0; // unused
 
     uploadDataPtr = &uploadData;
   }
 
+  // See bug 1397040
+  RefPtr<ID3D10Multithread> mt;
+  device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
+
+  D3D11MTAutoEnter lock(mt.forget());
+
   RefPtr<ID3D11Texture2D> texture11;
   HRESULT hr = device->CreateTexture2D(&newDesc, uploadDataPtr, getter_AddRefs(texture11));
 
   if (FAILED(hr) || !texture11) {
     gfxCriticalNote << "[D3D11] 2 CreateTexture2D failure Size: " << aSize
       << "texture11: " << texture11 << " Code: " << gfx::hexa(hr);
     return nullptr;
   }
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -531,19 +531,25 @@ private:
 };
 
 class D3D11MTAutoEnter
 {
 public:
   explicit D3D11MTAutoEnter(already_AddRefed<ID3D10Multithread> aMT)
     : mMT(aMT)
   {
-    mMT->Enter();
+    if (mMT) {
+      mMT->Enter();
+    }
   }
-  ~D3D11MTAutoEnter() { mMT->Leave(); }
+  ~D3D11MTAutoEnter() {
+    if (mMT) {
+      mMT->Leave();
+    }
+  }
 
 private:
   RefPtr<ID3D10Multithread> mMT;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -115,47 +115,54 @@ void
 CompositorBridgeChild::AfterDestroy()
 {
   // Note that we cannot rely upon mCanSend here because we already set that to
   // false to prevent normal IPDL calls from being made after SendWillClose.
   // The only time we should not issue Send__delete__ is if the actor is already
   // destroyed, e.g. the compositor process crashed.
   if (!mActorDestroyed) {
     Send__delete__(this);
+    mActorDestroyed = true;
   }
 
   if (sCompositorBridge == this) {
     sCompositorBridge = nullptr;
   }
 }
 
 void
 CompositorBridgeChild::Destroy()
 {
   // This must not be called from the destructor!
   mTexturesWaitingRecycled.Clear();
 
+  // Destroying the layer manager may cause all sorts of things to happen, so
+  // let's make sure there is still a reference to keep this alive whatever
+  // happens.
+  RefPtr<CompositorBridgeChild> selfRef = this;
+
   if (!mCanSend) {
+    // We may have already called destroy but still have lingering references
+    // or CompositorBridgeChild::ActorDestroy was called. Ensure that we do our
+    // post destroy clean up no matter what. It is safe to call multiple times.
+    MessageLoop::current()->PostTask(NewRunnableMethod(
+      "CompositorBridgeChild::AfterDestroy",
+      selfRef, &CompositorBridgeChild::AfterDestroy));
     return;
   }
 
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
     mTexturePools[i]->Destroy();
   }
 
   if (mSectionAllocator) {
     delete mSectionAllocator;
     mSectionAllocator = nullptr;
   }
 
-  // Destroying the layer manager may cause all sorts of things to happen, so
-  // let's make sure there is still a reference to keep this alive whatever
-  // happens.
-  RefPtr<CompositorBridgeChild> selfRef = this;
-
   if (mLayerManager) {
     mLayerManager->Destroy();
     mLayerManager = nullptr;
   }
 
   AutoTArray<PLayerTransactionChild*, 16> transactions;
   ManagedPLayerTransactionChild(transactions);
   for (int i = transactions.Length() - 1; i >= 0; --i) {
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -1124,21 +1124,18 @@ AtomicsObject::initClass(JSContext* cx, 
     if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods))
         return nullptr;
     if (!DefineToStringTag(cx, Atomics, cx->names().Atomics))
         return nullptr;
 
     RootedValue AtomicsValue(cx, ObjectValue(*Atomics));
 
     // Everything is set up, install Atomics on the global object.
-    if (!DefineProperty(cx, global, cx->names().Atomics, AtomicsValue, nullptr, nullptr,
-                        JSPROP_RESOLVING))
-    {
+    if (!DefineDataProperty(cx, global, cx->names().Atomics, AtomicsValue, JSPROP_RESOLVING))
         return nullptr;
-    }
 
     global->setConstructor(JSProto_Atomics, AtomicsValue);
     return Atomics;
 }
 
 JSObject*
 js::InitAtomicsClass(JSContext* cx, HandleObject obj)
 {
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -820,21 +820,18 @@ intl_availableLocales(JSContext* cx, Cou
         if (!lang)
             return false;
         char* p;
         while ((p = strchr(lang.get(), '_')))
             *p = '-';
         a = Atomize(cx, lang.get(), strlen(lang.get()));
         if (!a)
             return false;
-        if (!DefineProperty(cx, locales, a->asPropertyName(), TrueHandleValue, nullptr, nullptr,
-                            JSPROP_ENUMERATE))
-        {
+        if (!DefineDataProperty(cx, locales, a->asPropertyName(), TrueHandleValue))
             return false;
-        }
     }
 #endif
     result.setObject(*locales);
     return true;
 }
 
 /**
  * Returns the object holding the internal properties for obj.
@@ -1065,17 +1062,17 @@ CreateCollatorPrototype(JSContext* cx, H
         return nullptr;
 
     // 10.3.2 and 10.3.3
     if (!JS_DefineProperties(cx, proto, collator_properties))
         return nullptr;
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
+    if (!DefineDataProperty(cx, Intl, cx->names().Collator, ctorValue, 0))
         return nullptr;
 
     return proto;
 }
 
 bool
 js::intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1116,17 +1113,17 @@ js::intl_availableCollations(JSContext* 
     RootedObject collations(cx, NewDenseEmptyArray(cx));
     if (!collations)
         return false;
 
     uint32_t index = 0;
 
     // The first element of the collations array must be |null| per
     // ES2017 Intl, 10.2.3 Internal Slots.
-    if (!DefineElement(cx, collations, index++, NullHandleValue))
+    if (!DefineDataElement(cx, collations, index++, NullHandleValue))
         return false;
 
     RootedValue element(cx);
     for (uint32_t i = 0; i < count; i++) {
         const char* collation = uenum_next(values, nullptr, &status);
         if (U_FAILURE(status)) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
             return false;
@@ -1139,17 +1136,17 @@ js::intl_availableCollations(JSContext* 
         if (equal(collation, "standard") || equal(collation, "search"))
             continue;
 
         // ICU returns old-style keyword values; map them to BCP 47 equivalents.
         JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
         if (!jscollation)
             return false;
         element = StringValue(jscollation);
-        if (!DefineElement(cx, collations, index++, element))
+        if (!DefineDataElement(cx, collations, index++, element))
             return false;
     }
 
     args.rval().setObject(*collations);
     return true;
 }
 
 /**
@@ -1644,23 +1641,23 @@ CreateNumberFormatPrototype(JSContext* c
         HandlePropertyName name = cx->names().formatToParts;
         if (!GlobalObject::getSelfHostedFunction(cx, cx->global(),
                                                  cx->names().NumberFormatFormatToParts,
                                                  name, 1, &ftp))
         {
             return nullptr;
         }
 
-        if (!DefineProperty(cx, proto, cx->names().formatToParts, ftp, nullptr, nullptr, 0))
+        if (!DefineDataProperty(cx, proto, cx->names().formatToParts, ftp, 0))
             return nullptr;
     }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
+    if (!DefineDataProperty(cx, Intl, cx->names().NumberFormat, ctorValue, 0))
         return nullptr;
 
     constructor.set(ctor);
     return proto;
 }
 
 bool
 js::intl_NumberFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
@@ -2303,30 +2300,30 @@ intl_FormatNumberToParts(JSContext* cx, 
 
         MOZ_ASSERT(lastEndIndex < endIndex);
 
         singlePart = NewBuiltinClassInstance<PlainObject>(cx);
         if (!singlePart)
             return false;
 
         propVal.setString(cx->names().*type);
-        if (!DefineProperty(cx, singlePart, cx->names().type, propVal))
+        if (!DefineDataProperty(cx, singlePart, cx->names().type, propVal))
             return false;
 
         JSLinearString* partSubstr =
             NewDependentString(cx, overallResult, lastEndIndex, endIndex - lastEndIndex);
         if (!partSubstr)
             return false;
 
         propVal.setString(partSubstr);
-        if (!DefineProperty(cx, singlePart, cx->names().value, propVal))
+        if (!DefineDataProperty(cx, singlePart, cx->names().value, propVal))
             return false;
 
         propVal.setObject(*singlePart);
-        if (!DefineElement(cx, partsArray, partIndex, propVal))
+        if (!DefineDataElement(cx, partsArray, partIndex, propVal))
             return false;
 
         lastEndIndex = endIndex;
         partIndex++;
     } while (true);
 
     MOZ_ASSERT(lastEndIndex == overallResult->length(),
                "result array must partition the entire string");
@@ -2524,17 +2521,17 @@ CreateDateTimeFormatPrototype(JSContext*
         return nullptr;
 
     // 12.4.2 and 12.4.3
     if (!JS_DefineProperties(cx, proto, dateTimeFormat_properties))
         return nullptr;
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
+    if (!DefineDataProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, 0))
         return nullptr;
 
     constructor.set(ctor);
     return proto;
 }
 
 bool
 js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
@@ -2611,17 +2608,17 @@ js::intl_availableCalendars(JSContext* c
         return false;
     uint32_t index = 0;
 
     // We need the default calendar for the locale as the first result.
     RootedValue element(cx);
     if (!DefaultCalendar(cx, locale, &element))
         return false;
 
-    if (!DefineElement(cx, calendars, index++, element))
+    if (!DefineDataElement(cx, calendars, index++, element))
         return false;
 
     // Now get the calendars that "would make a difference", i.e., not the default.
     UErrorCode status = U_ZERO_ERROR;
     UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
@@ -2643,27 +2640,27 @@ js::intl_availableCalendars(JSContext* c
 
         // ICU returns old-style keyword values; map them to BCP 47 equivalents
         calendar = uloc_toUnicodeLocaleType("ca", calendar);
 
         JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
         if (!jscalendar)
             return false;
         element = StringValue(jscalendar);
-        if (!DefineElement(cx, calendars, index++, element))
+        if (!DefineDataElement(cx, calendars, index++, element))
             return false;
 
         // ICU doesn't return calendar aliases, append them here.
         for (const auto& calendarAlias : calendarAliases) {
             if (equal(calendar, calendarAlias.calendar)) {
                 JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
                 if (!jscalendar)
                     return false;
                 element = StringValue(jscalendar);
-                if (!DefineElement(cx, calendars, index++, element))
+                if (!DefineDataElement(cx, calendars, index++, element))
                     return false;
             }
         }
     }
 
     args.rval().setObject(*calendars);
     return true;
 }
@@ -3380,30 +3377,30 @@ intl_FormatToPartsDateTime(JSContext* cx
     RootedValue val(cx);
 
     auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
         singlePart = NewBuiltinClassInstance<PlainObject>(cx);
         if (!singlePart)
             return false;
 
         partType = StringValue(cx->names().*type);
-        if (!DefineProperty(cx, singlePart, cx->names().type, partType))
+        if (!DefineDataProperty(cx, singlePart, cx->names().type, partType))
             return false;
 
         JSLinearString* partSubstr =
             NewDependentString(cx, overallResult, beginIndex, endIndex - beginIndex);
         if (!partSubstr)
             return false;
 
         val = StringValue(partSubstr);
-        if (!DefineProperty(cx, singlePart, cx->names().value, val))
+        if (!DefineDataProperty(cx, singlePart, cx->names().value, val))
             return false;
 
         val = ObjectValue(*singlePart);
-        if (!DefineElement(cx, partsArray, partIndex, val))
+        if (!DefineDataElement(cx, partsArray, partIndex, val))
             return false;
 
         lastEndIndex = endIndex;
         partIndex++;
         return true;
     };
 
     int32_t fieldInt, beginIndexInt, endIndexInt;
@@ -3588,17 +3585,17 @@ CreatePluralRulesPrototype(JSContext* cx
 
     if (!JS_DefineFunctions(cx, ctor, pluralRules_static_methods))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
         return nullptr;
 
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
+    if (!DefineDataProperty(cx, Intl, cx->names().PluralRules, ctorValue, 0))
         return nullptr;
 
     return proto;
 }
 
 /* static */ bool
 js::GlobalObject::addPluralRulesConstructor(JSContext* cx, HandleObject intl)
 {
@@ -3761,17 +3758,17 @@ js::intl_GetPluralCategories(JSContext* 
             break;
 
         MOZ_ASSERT(catSize >= 0);
         JSString* str = NewStringCopyN<CanGC>(cx, cat, catSize);
         if (!str)
             return false;
 
         element.setString(str);
-        if (!DefineElement(cx, res, i++, element))
+        if (!DefineDataElement(cx, res, i++, element))
             return false;
     } while (true);
 
     args.rval().setObject(*res);
     return true;
 }
 
 
@@ -3921,22 +3918,22 @@ js::intl_GetCalendarInfo(JSContext* cx, 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
         return false;
 
     RootedValue v(cx);
     int32_t firstDayOfWeek = ucal_getAttribute(cal, UCAL_FIRST_DAY_OF_WEEK);
     v.setInt32(firstDayOfWeek);
 
-    if (!DefineProperty(cx, info, cx->names().firstDayOfWeek, v))
+    if (!DefineDataProperty(cx, info, cx->names().firstDayOfWeek, v))
         return false;
 
     int32_t minDays = ucal_getAttribute(cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK);
     v.setInt32(minDays);
-    if (!DefineProperty(cx, info, cx->names().minDays, v))
+    if (!DefineDataProperty(cx, info, cx->names().minDays, v))
         return false;
 
     UCalendarWeekdayType prevDayType = ucal_getDayOfWeekType(cal, UCAL_SATURDAY, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
@@ -3974,20 +3971,20 @@ js::intl_GetCalendarInfo(JSContext* cx, 
         }
 
         prevDayType = type;
     }
 
     MOZ_ASSERT(weekendStart.isInt32());
     MOZ_ASSERT(weekendEnd.isInt32());
 
-    if (!DefineProperty(cx, info, cx->names().weekendStart, weekendStart))
+    if (!DefineDataProperty(cx, info, cx->names().weekendStart, weekendStart))
         return false;
 
-    if (!DefineProperty(cx, info, cx->names().weekendEnd, weekendEnd))
+    if (!DefineDataProperty(cx, info, cx->names().weekendEnd, weekendEnd))
         return false;
 
     args.rval().setObject(*info);
     return true;
 }
 
 static void
 ReportBadKey(JSContext* cx, const Range<const JS::Latin1Char>& range)
@@ -4301,17 +4298,17 @@ js::intl_ComputeDisplayNames(JSContext* 
                                        stablePatternChars.latin1Range())
             : ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars,
                                        stablePatternChars.twoByteRange());
         if (!displayName)
             return false;
 
         // 5.b. Append the result string to result.
         v.setString(displayName);
-        if (!DefineElement(cx, result, i, v))
+        if (!DefineDataElement(cx, result, i, v))
             return false;
     }
 
     // 6. Return result.
     args.rval().setObject(*result);
     return true;
 }
 
@@ -4324,24 +4321,24 @@ js::intl_GetLocaleInfo(JSContext* cx, un
     JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
         return false;
 
-    if (!DefineProperty(cx, info, cx->names().locale, args[0]))
+    if (!DefineDataProperty(cx, info, cx->names().locale, args[0]))
         return false;
 
     bool rtl = uloc_isRightToLeft(icuLocale(locale.ptr()));
 
     RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
 
-    if (!DefineProperty(cx, info, cx->names().direction, dir))
+    if (!DefineDataProperty(cx, info, cx->names().direction, dir))
         return false;
 
     args.rval().setObject(*info);
     return true;
 }
 
 const Class js::IntlClass = {
     js_Object_str,
@@ -4398,21 +4395,18 @@ GlobalObject::initIntlObject(JSContext* 
         return false;
     RootedObject numberFormatProto(cx), numberFormat(cx);
     numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
     if (!numberFormatProto)
         return false;
 
     // The |Intl| object is fully set up now, so define the global property.
     RootedValue intlValue(cx, ObjectValue(*intl));
-    if (!DefineProperty(cx, global, cx->names().Intl, intlValue, nullptr, nullptr,
-                        JSPROP_RESOLVING))
-    {
+    if (!DefineDataProperty(cx, global, cx->names().Intl, intlValue, JSPROP_RESOLVING))
         return false;
-    }
 
     // Now that the |Intl| object is successfully added, we can OOM-safely fill
     // in all relevant reserved global slots.
 
     // Cache the various prototypes, for use in creating instances of these
     // objects with the proper [[Prototype]] as "the original value of
     // |Intl.Collator.prototype|" and similar.  For builtin classes like
     // |String.prototype| we have |JSProto_*| that enables
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -1984,17 +1984,17 @@ PerformPromiseAll(JSContext *cx, JS::For
         }
 
         // Step h.
         { // Scope for the JSAutoCompartment we need to work with valuesArray.  We
             // mostly do this for performance; we could go ahead and do the define via
             // a cross-compartment proxy instead...
             JSAutoCompartment ac(cx, valuesArray);
             indexId = INT_TO_JSID(index);
-            if (!DefineProperty(cx, valuesArray, indexId, UndefinedHandleValue))
+            if (!DefineDataProperty(cx, valuesArray, indexId, UndefinedHandleValue))
                 return false;
         }
 
         // Step i.
         // Sadly, because someone could have overridden
         // "resolve" on the canonical Promise constructor.
         RootedValue nextPromise(cx);
         RootedValue staticResolve(cx);
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -235,15 +235,15 @@ js::InitReflect(JSContext* cx, HandleObj
 
     RootedObject reflect(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject));
     if (!reflect)
         return nullptr;
     if (!JS_DefineFunctions(cx, reflect, methods))
         return nullptr;
 
     RootedValue value(cx, ObjectValue(*reflect));
-    if (!DefineProperty(cx, obj, cx->names().Reflect, value, nullptr, nullptr, JSPROP_RESOLVING))
+    if (!DefineDataProperty(cx, obj, cx->names().Reflect, value, JSPROP_RESOLVING))
         return nullptr;
 
     obj->as<GlobalObject>().setConstructor(JSProto_Reflect, value);
 
     return reflect;
 }
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -433,17 +433,17 @@ class NodeBuilder
          * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
         RootedAtom atom(cx, Atomize(cx, name, strlen(name)));
         if (!atom)
             return false;
 
         /* Represent "no node" as null and ensure users are not exposed to magic values. */
         RootedValue optVal(cx, val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val);
-        return DefineProperty(cx, obj, atom->asPropertyName(), optVal);
+        return DefineDataProperty(cx, obj, atom->asPropertyName(), optVal);
     }
 
     MOZ_MUST_USE bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool setNodeLoc(HandleObject node, TokenPos* pos);
 
   public:
     /*
@@ -674,17 +674,17 @@ NodeBuilder::newArray(NodeVector& elts, 
         RootedValue val(cx, elts[i]);
 
         MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
 
         /* Represent "no node" as an array hole by not adding the value. */
         if (val.isMagic(JS_SERIALIZE_NO_NODE))
             continue;
 
-        if (!DefineElement(cx, array, i, val))
+        if (!DefineDataElement(cx, array, i, val))
             return false;
     }
 
     dst.setObject(*array);
     return true;
 }
 
 bool
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -556,21 +556,18 @@ GlobalObject::initSimdObject(JSContext* 
     if (!objProto)
         return false;
 
     globalSimdObject = NewObjectWithGivenProto(cx, &SimdObject::class_, objProto, SingletonObject);
     if (!globalSimdObject)
         return false;
 
     RootedValue globalSimdValue(cx, ObjectValue(*globalSimdObject));
-    if (!DefineProperty(cx, global, cx->names().SIMD, globalSimdValue, nullptr, nullptr,
-                        JSPROP_RESOLVING))
-    {
+    if (!DefineDataProperty(cx, global, cx->names().SIMD, globalSimdValue, JSPROP_RESOLVING))
         return false;
-    }
 
     global->setConstructor(JSProto_SIMD, globalSimdValue);
     return true;
 }
 
 static bool
 CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName stringRepr,
                SimdType simdType, const JSFunctionSpec* methods)
@@ -616,18 +613,18 @@ CreateSimdType(JSContext* cx, Handle<Glo
     }
 
     // Bind type descriptor to the global SIMD object
     RootedObject globalSimdObject(cx, GlobalObject::getOrCreateSimdGlobalObject(cx, global));
     MOZ_ASSERT(globalSimdObject);
 
     RootedValue typeValue(cx, ObjectValue(*typeDescr));
     if (!JS_DefineFunctions(cx, typeDescr, methods) ||
-        !DefineProperty(cx, globalSimdObject, stringRepr, typeValue, nullptr, nullptr,
-                        JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING))
+        !DefineDataProperty(cx, globalSimdObject, stringRepr, typeValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING))
     {
         return false;
     }
 
     uint32_t slot = uint32_t(typeDescr->type());
     MOZ_ASSERT(globalSimdObject->as<NativeObject>().getReservedSlot(slot).isUndefined());
     globalSimdObject->as<NativeObject>().setReservedSlot(slot, ObjectValue(*typeDescr));
     return !!typeDescr;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -550,40 +550,40 @@ const JSFunctionSpec ArrayMetaTypeDescr:
 
 bool
 js::CreateUserSizeAndAlignmentProperties(JSContext* cx, HandleTypeDescr descr)
 {
     // If data is transparent, also store the public slots.
     if (descr->transparent()) {
         // byteLength
         RootedValue typeByteLength(cx, Int32Value(AssertedCast<int32_t>(descr->size())));
-        if (!DefineProperty(cx, descr, cx->names().byteLength, typeByteLength,
-                            nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, descr, cx->names().byteLength, typeByteLength,
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
         RootedValue typeByteAlignment(cx, Int32Value(descr->alignment()));
-        if (!DefineProperty(cx, descr, cx->names().byteAlignment, typeByteAlignment,
-                            nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, descr, cx->names().byteAlignment, typeByteAlignment,
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
     } else {
         // byteLength
-        if (!DefineProperty(cx, descr, cx->names().byteLength, UndefinedHandleValue,
-                            nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, descr, cx->names().byteLength, UndefinedHandleValue,
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
-        if (!DefineProperty(cx, descr, cx->names().byteAlignment, UndefinedHandleValue,
-                            nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, descr, cx->names().byteAlignment, UndefinedHandleValue,
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
     }
 
     return true;
 }
 
@@ -608,25 +608,25 @@ ArrayMetaTypeDescr::create(JSContext* cx
     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
     obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
-    if (!DefineProperty(cx, obj, cx->names().elementType, elementTypeVal,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     RootedValue lengthValue(cx, NumberValue(length));
-    if (!DefineProperty(cx, obj, cx->names().length, lengthValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, obj, cx->names().length, lengthValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     if (!CreateUserSizeAndAlignmentProperties(cx, obj))
         return nullptr;
 
     // All arrays with the same element type have the same prototype. This
@@ -838,18 +838,18 @@ StructMetaTypeDescr::create(JSContext* c
         // Collect field name and type object
         RootedValue fieldName(cx, IdToValue(id));
         if (!fieldNames.append(fieldName))
             return nullptr;
         if (!fieldTypeObjs.append(ObjectValue(*fieldType)))
             return nullptr;
 
         // userFieldTypes[id] = typeObj
-        if (!DefineProperty(cx, userFieldTypes, id, fieldTypeObjs[i], nullptr, nullptr,
-                            JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, userFieldTypes, id, fieldTypeObjs[i],
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return nullptr;
         }
 
         // Append "f:Type" to the string repr
         if (i > 0 && !stringBuffer.append(", "))
             return nullptr;
         if (!stringBuffer.append(JSID_TO_ATOM(id)))
@@ -867,18 +867,18 @@ StructMetaTypeDescr::create(JSContext* c
             return nullptr;
         }
         MOZ_ASSERT(offset.value() >= 0);
         if (!fieldOffsets.append(Int32Value(offset.value())))
             return nullptr;
 
         // userFieldOffsets[id] = offset
         RootedValue offsetValue(cx, Int32Value(offset.value()));
-        if (!DefineProperty(cx, userFieldOffsets, id, offsetValue, nullptr, nullptr,
-                            JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
+                                JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return nullptr;
         }
 
         // Add space for this field to the total struct size.
         sizeSoFar = offset + fieldType->size();
         if (!sizeSoFar.isValid()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG);
@@ -956,24 +956,24 @@ StructMetaTypeDescr::create(JSContext* c
     }
 
     // Create data properties fieldOffsets and fieldTypes
     if (!FreezeObject(cx, userFieldOffsets))
         return nullptr;
     if (!FreezeObject(cx, userFieldTypes))
         return nullptr;
     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
-    if (!DefineProperty(cx, descr, cx->names().fieldOffsets, userFieldOffsetsValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, descr, cx->names().fieldOffsets, userFieldOffsetsValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
     RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
-    if (!DefineProperty(cx, descr, cx->names().fieldTypes, userFieldTypesValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, descr, cx->names().fieldTypes, userFieldTypesValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
         return nullptr;
 
     Rooted<TypedProto*> prototypeObj(cx);
@@ -1150,17 +1150,17 @@ DefineSimpleTypeDescr(JSContext* cx,
     // not being user accessible, but we still create one for consistency.
     Rooted<TypedProto*> proto(cx);
     proto = NewObjectWithGivenProto<TypedProto>(cx, objProto, TenuredObject);
     if (!proto)
         return false;
     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
 
     RootedValue descrValue(cx, ObjectValue(*descr));
-    if (!DefineProperty(cx, module, className, descrValue, nullptr, nullptr, 0))
+    if (!DefineDataProperty(cx, module, className, descrValue, 0))
         return false;
 
     if (!CreateTraceList(cx, descr))
         return false;
 
     if (!cx->zone()->addTypeDescrObject(cx, descr))
         return false;
 
@@ -1197,18 +1197,18 @@ DefineMetaTypeDescr(JSContext* cx,
     if (!objProto)
         return nullptr;
     RootedObject protoProto(cx);
     protoProto = NewObjectWithGivenProto<PlainObject>(cx, objProto, SingletonObject);
     if (!protoProto)
         return nullptr;
 
     RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
-    if (!DefineProperty(cx, proto, cx->names().prototype, protoProtoValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, proto, cx->names().prototype, protoProtoValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     // Create ctor itself
 
     const int constructorLength = 2;
     RootedFunction ctor(cx);
@@ -1271,41 +1271,41 @@ GlobalObject::initTypedObjectModule(JSCo
 
     RootedObject arrayType(cx);
     arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
         cx, "ArrayType", global, module, TypedObjectModuleObject::ArrayTypePrototype);
     if (!arrayType)
         return false;
 
     RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
-    if (!DefineProperty(cx, module, cx->names().ArrayType, arrayTypeValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, module, cx->names().ArrayType, arrayTypeValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return false;
     }
 
     // StructType.
 
     RootedObject structType(cx);
     structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
         cx, "StructType", global, module, TypedObjectModuleObject::StructTypePrototype);
     if (!structType)
         return false;
 
     RootedValue structTypeValue(cx, ObjectValue(*structType));
-    if (!DefineProperty(cx, module, cx->names().StructType, structTypeValue,
-                        nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!DefineDataProperty(cx, module, cx->names().StructType, structTypeValue,
+                            JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return false;
     }
 
     // Everything is setup, install module on the global object:
     RootedValue moduleValue(cx, ObjectValue(*module));
-    if (!DefineProperty(cx, global, cx->names().TypedObject, moduleValue, nullptr, nullptr,
-                        JSPROP_RESOLVING))
+    if (!DefineDataProperty(cx, global, cx->names().TypedObject, moduleValue,
+                            JSPROP_RESOLVING))
     {
         return false;
     }
 
     global->setConstructor(JSProto_TypedObject, moduleValue);
 
     return module;
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1397026.js
@@ -0,0 +1,43 @@
+function f1() {
+    var o = {};
+    var values = [];
+    for (var i = 0; i < 6; ++i) {
+        var desc = {
+            value: i,
+            writable: true,
+            configurable: true,
+            enumerable: true
+        };
+        try {
+            Object.defineProperty(o, "p", desc);
+        } catch (e) {
+        }
+        if (i === 1) {
+            Object.defineProperty(o, "p", {configurable: false});
+        }
+        values.push(o.p);
+    }
+    assertEq(values.toString(), "0,1,1,1,1,1");
+}
+f1();
+
+function f2() {
+    var o = {};
+    for (var i = 0; i < 6; ++i) {
+        var desc = {
+            value: i,
+            writable: true,
+            configurable: true,
+            enumerable: true
+        };
+        try {
+            Object.defineProperty(o, "p", desc);
+        } catch (e) {
+        }
+        assertEq(Object.getOwnPropertyDescriptor(o, "p").enumerable, true);
+        if (i > 0) {
+            Object.defineProperty(o, "p", {enumerable: false});
+        }
+    }
+}
+f2();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/array-push-multiple-frozen.js
@@ -0,0 +1,87 @@
+// |jit-test| --no-threads
+
+// This test case check's Ion ability to recover from an allocation failure in
+// the inlining of Array.prototype.push, when given multiple arguments. Note,
+// that the following are not equivalent in case of failures:
+//
+//   arr = [];
+//   arr.push(1,2,3); // OOM ---> arr == []
+//
+//   arr = [];
+//   arr.push(1);
+//   arr.push(2); // OOM --> arr == [1]
+//   arr.push(3);
+
+function canIoncompile() {
+  while (true) {
+    var i = inIon();
+    if (i)
+      return i;
+  }
+}
+
+if (!("oomAtAllocation" in this))
+  quit();
+if (canIoncompile() != true)
+  quit();
+if ("gczeal" in this)
+  gczeal(0);
+
+function pushLimits(limit, offset, arr, freeze) {
+  arr = arr || [];
+  arr.push(0,1,2,3,4,5,6,7,8,9);
+  arr.length = offset;
+  var l = arr.length;
+  var was = inIon();
+  oomAtAllocation(limit);
+  try {
+    for (var i = 0; i < 50; i++)
+      arr.push(0,1,2,3,4,5,6,7,8,9);
+    if (freeze)
+      arr.frozen();
+    for (var i = 0; i < 100; i++)
+      arr.push(0,1,2,3,4,5,6,7,8,9);
+  } catch (e) {
+    // Catch OOM.
+  }
+  resetOOMFailure();
+  assertEq(arr.length % 10, l);
+  // Check for a bailout.
+  var is = inIon();
+  return was ? is ? 1 : 2 : 0;
+}
+
+// We need this limit to be high enough to be able to OSR in the for-loop of
+// pushLimits.
+var limit = 1024 * 1024 * 1024;
+while(true) {
+  var res = pushLimits(limit, 0);
+
+  if (res == 0) {
+    limit = 1024 * 1024 * 1024;
+  } else if (res == 1) { // Started and finished in Ion.
+    if (limit == 0) // If we are not in the Jit.
+      break;
+    // We want to converge quickly to a state where the memory is limited
+    // enough to cause failures in array.prototype.push.
+    limit = (limit / 2) | 0;
+  } else if (res == 2) { // Started in Ion, and finished in Baseline.
+    if (limit < 10) {
+      // This is used to offset the OOM location, such that we can test
+      // each steps of the Array.push function, when it is jitted.
+      for (var off = 1; off < 10; off++) {
+        var arr = [];
+        try {
+          pushLimits(limit, off, arr, true);
+        } catch (e) {
+          // Cacth OOM produced while generating the error message.
+        }
+        if (arr.length > 10) assertEq(arr[arr.length - 1], 9);
+        else assertEq(arr[arr.length - 1], arr.length - 1);
+      }
+    }
+    if (limit == 1)
+      break;
+    limit--;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/array-push-multiple.js
@@ -0,0 +1,74 @@
+// |jit-test| --no-threads
+
+// This test case check's Ion ability to recover from an allocation failure in
+// the inlining of Array.prototype.push, when given multiple arguments. Note,
+// that the following are not equivalent in case of failures:
+//
+//   arr = [];
+//   arr.push(1,2,3); // OOM ---> arr == []
+//
+//   arr = [];
+//   arr.push(1);
+//   arr.push(2); // OOM --> arr == [1]
+//   arr.push(3);
+
+function canIoncompile() {
+  while (true) {
+    var i = inIon();
+    if (i)
+      return i;
+  }
+}
+
+if (!("oomAtAllocation" in this))
+  quit();
+if (canIoncompile() != true)
+  quit();
+if ("gczeal" in this)
+  gczeal(0);
+
+function pushLimits(limit, offset) {
+  var arr = [0,1,2,3,4,5,6,7,8,9];
+  arr.length = offset;
+  var l = arr.length;
+  var was = inIon();
+  oomAtAllocation(limit);
+  try {
+    for (var i = 0; i < 100; i++)
+      arr.push(0,1,2,3,4,5,6,7,8,9);
+  } catch (e) {
+    // Catch OOM.
+  }
+  resetOOMFailure();
+  assertEq(arr.length % 10, l);
+  // Check for a bailout.
+  var is = inIon();
+  return was ? is ? 1 : 2 : 0;
+}
+
+// We need this limit to be high enough to be able to OSR in the for-loop of
+// pushLimits.
+var limit = 1024 * 1024 * 1024;
+while(true) {
+  var res = pushLimits(limit, 0);
+
+  if (res == 0) {
+    limit = 1024 * 1024 * 1024;
+  } else if (res == 1) { // Started and finished in Ion.
+    if (limit == 0) // If we are not in the Jit.
+      break;
+    // We want to converge quickly to a state where the memory is limited
+    // enough to cause failures in array.prototype.push.
+    limit = (limit / 2) | 0;
+  } else if (res == 2) { // Started in Ion, and finished in Baseline.
+    if (limit < 10) {
+      // This is used to offset the OOM location, such that we can test
+      // each steps of the Array.push function, when it is jitted.
+      for (var off = 1; off < 10; off++)
+        pushLimits(limit, off);
+    }
+    if (limit == 1)
+      break;
+    limit--;
+  }
+}
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2707,32 +2707,43 @@ EmitStoreSlotAndReturn(CacheIRWriter& wr
     } else {
         size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
         writer.storeDynamicSlot(objId, offset, rhsId);
     }
     writer.returnFromIC();
 }
 
 static Shape*
-LookupShapeForSetSlot(NativeObject* obj, jsid id)
+LookupShapeForSetSlot(JSOp op, NativeObject* obj, jsid id)
 {
     Shape* shape = obj->lookupPure(id);
-    if (shape && shape->hasSlot() && shape->hasDefaultSetter() && shape->writable())
-        return shape;
-    return nullptr;
+    if (!shape || !shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable())
+        return nullptr;
+
+    // If this is an op like JSOP_INITELEM / [[DefineOwnProperty]], the
+    // property's attributes may have to be changed too, so make sure it's a
+    // simple data property.
+    if (IsPropertyInitOp(op) && (!shape->configurable() ||
+                                 !shape->enumerable() ||
+                                 !shape->hasDefaultGetter()))
+    {
+        return nullptr;
+    }
+
+    return shape;
 }
 
 static bool
-CanAttachNativeSetSlot(JSContext* cx, HandleObject obj, HandleId id,
+CanAttachNativeSetSlot(JSContext* cx, JSOp op, HandleObject obj, HandleId id,
                        bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
 {
     if (!obj->isNative())
         return false;
 
-    propShape.set(LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
+    propShape.set(LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id));
     if (!propShape)
         return false;
 
     ObjectGroup* group = JSObject::getGroup(cx, obj);
     if (!group) {
         cx->recoverFromOutOfMemory();
         return false;
     }
@@ -2750,17 +2761,17 @@ CanAttachNativeSetSlot(JSContext* cx, Ha
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                            ValOperandId rhsId)
 {
     RootedShape propShape(cx_);
-    if (!CanAttachNativeSetSlot(cx_, obj, id, isTemporarilyUnoptimizable_, &propShape))
+    if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), obj, id, isTemporarilyUnoptimizable_, &propShape))
         return false;
 
     if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
         writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
                                     typeCheckInfo_.needsTypeBarrier());
         writer.returnFromIC();
         trackAttached("MegamorphicNativeSlot");
         return true;
@@ -2794,17 +2805,17 @@ SetPropIRGenerator::tryAttachUnboxedExpa
 {
     if (!obj->is<UnboxedPlainObject>())
         return false;
 
     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
     if (!expando)
         return false;
 
-    Shape* propShape = LookupShapeForSetSlot(expando, id);
+    Shape* propShape = LookupShapeForSetSlot(JSOp(*pc_), expando, id);
     if (!propShape)
         return false;
 
     maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
     writer.guardShape(expandoId, expando->lastProperty());
 
@@ -3475,17 +3486,19 @@ SetPropIRGenerator::tryAttachDOMProxyExp
         MOZ_ASSERT(!expandoVal.isUndefined(),
                    "How did a missing expando manage to shadow things?");
         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
         MOZ_ASSERT(expandoAndGeneration);
         expandoObj = &expandoAndGeneration->expando.toObject();
     }
 
     RootedShape propShape(cx_);
-    if (CanAttachNativeSetSlot(cx_, expandoObj, id, isTemporarilyUnoptimizable_, &propShape)) {
+    if (CanAttachNativeSetSlot(cx_, JSOp(*pc_), expandoObj, id, isTemporarilyUnoptimizable_,
+                               &propShape))
+    {
         maybeEmitIdGuard(id);
         ObjOperandId expandoObjId =
             guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
 
         NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
         writer.guardGroup(expandoObjId, nativeExpandoObj->group());
         typeCheckInfo_.set(nativeExpandoObj->group(), id);
 
@@ -3616,18 +3629,21 @@ SetPropIRGenerator::tryAttachWindowProxy
     // those.
     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
 
     // Now try to do the set on the Window (the current global).
     Handle<GlobalObject*> windowObj = cx_->global();
 
     RootedShape propShape(cx_);
-    if (!CanAttachNativeSetSlot(cx_, windowObj, id, isTemporarilyUnoptimizable_, &propShape))
+    if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), windowObj, id, isTemporarilyUnoptimizable_,
+                                &propShape))
+    {
         return false;
+    }
 
     maybeEmitIdGuard(id);
 
     writer.guardClass(objId, GuardClassKind::WindowProxy);
     ObjOperandId windowObjId = writer.loadObject(windowObj);
 
     writer.guardShape(windowObjId, windowObj->lastProperty());
     writer.guardGroup(windowObjId, windowObj->group());
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -785,28 +785,31 @@ IonBuilder::inlineArrayJoin(CallInfo& ca
 
     MOZ_TRY(resumeAfter(ins));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineArrayPush(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    const uint32_t inlineArgsLimit = 10;
+    if (callInfo.argc() < 1 || callInfo.argc() > inlineArgsLimit || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
-    MDefinition* value = callInfo.getArg(0);
-    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
-                                      &obj, nullptr, &value, /* canModify = */ false))
-    {
-        trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
-        return InliningStatus_NotInlined;
+    for (uint32_t i = 0; i < callInfo.argc(); i++) {
+        MDefinition* value = callInfo.getArg(i);
+        if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
+                                          &obj, nullptr, &value, /* canModify = */ false))
+        {
+            trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
+            return InliningStatus_NotInlined;
+        }
     }
 
     if (getInlineReturnType() != MIRType::Int32)
         return InliningStatus_NotInlined;
     if (obj->type() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* thisTypes = obj->resultTypeSet();
@@ -840,34 +843,83 @@ IonBuilder::inlineArrayPush(CallInfo& ca
     if (clasp == &UnboxedArrayObject::class_) {
         unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
         if (unboxedType == JSVAL_TYPE_MAGIC)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
-        conversion == TemporaryTypeSet::MaybeConvertToDoubles)
-    {
-        MInstruction* valueDouble = MToDouble::New(alloc(), value);
-        current->add(valueDouble);
-        value = valueDouble;
-    }
+    bool toDouble =
+        conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
+        conversion == TemporaryTypeSet::MaybeConvertToDoubles;
 
     if (unboxedType == JSVAL_TYPE_MAGIC)
         obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
-    if (needsPostBarrier(value))
-        current->add(MPostWriteBarrier::New(alloc(), obj, value));
-
-    MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
-    current->add(ins);
+    // If we have more than one argument, we are splitting the function into
+    // multiple inlined calls to Array.push.
+    //
+    // Still, in case of bailouts, we have to keep the atomicity of the call, as
+    // we cannot resume within Array.push function. To do so, we register a
+    // resume point which would be captured by the upcoming MArrayPush
+    // instructions, and this resume point contains an instruction which
+    // truncates the length of the Array, to its original length in case of
+    // bailouts, and resume before the Array.push call.
+    MResumePoint* lastRp = nullptr;
+    MInstruction* truncate = nullptr;
+    if (callInfo.argc() > 1) {
+        MInstruction* elements = MElements::New(alloc(), obj);
+        MInstruction* length = MArrayLength::New(alloc(), elements);
+        truncate = MSetArrayLength::New(alloc(), obj, length);
+        truncate->setRecoveredOnBailout();
+
+        current->add(elements);
+        current->add(length);
+        current->add(truncate);
+
+        // Restore the stack, such that resume points are created with the stack
+        // as it was before the call.
+        callInfo.pushFormals(current);
+    }
+
+    MInstruction* ins = nullptr;
+    for (uint32_t i = 0; i < callInfo.argc(); i++) {
+        MDefinition* value = callInfo.getArg(i);
+        if (toDouble) {
+            MInstruction* valueDouble = MToDouble::New(alloc(), value);
+            current->add(valueDouble);
+            value = valueDouble;
+        }
+
+        if (needsPostBarrier(value))
+            current->add(MPostWriteBarrier::New(alloc(), obj, value));
+
+        ins = MArrayPush::New(alloc(), obj, value, unboxedType);
+        current->add(ins);
+
+        if (callInfo.argc() > 1) {
+            // Restore that call stack and the array length.
+            MOZ_TRY(resumeAt(ins, pc));
+            ins->resumePoint()->addStore(alloc(), truncate, lastRp);
+            lastRp = ins->resumePoint();
+        }
+    }
+
+    if (callInfo.argc() > 1) {
+        // Fix the stack to represent the state after the call execution.
+        callInfo.popFormals(current);
+    }
     current->push(ins);
 
+    if (callInfo.argc() > 1) {
+        ins = MNop::New(alloc());
+        current->add(ins);
+    }
+
     MOZ_TRY(resumeAfter(ins));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineArraySlice(CallInfo& callInfo)
 {
     if (callInfo.constructing()) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9221,16 +9221,22 @@ class MSetArrayLength
   public:
     INSTRUCTION_HEADER(SetArrayLength)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, elements), (1, index))
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::ObjectFields);
     }
+
+    // By default no, unless built as a recovered instruction.
+    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+    bool canRecoverOnBailout() const override {
+        return isRecoveredOnBailout();
+    }
 };
 
 class MGetNextEntryForIterator
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
   public:
     enum Mode {
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1683,16 +1683,48 @@ RArrayState::recover(JSContext* cx, Snap
     }
 
     result.setObject(*object);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MSetArrayLength::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    // For simplicity, we capture directly the object instead of the elements
+    // pointer.
+    MOZ_ASSERT(elements()->type() != MIRType::Elements);
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_SetArrayLength));
+    return true;
+}
+
+RSetArrayLength::RSetArrayLength(CompactBufferReader& reader)
+{
+}
+
+bool
+RSetArrayLength::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    RootedValue result(cx);
+    RootedArrayObject obj(cx, &iter.read().toObject().as<ArrayObject>());
+    RootedValue len(cx, iter.read());
+
+    RootedId id(cx, NameToId(cx->names().length));
+    ObjectOpResult error;
+    if (!ArraySetLength(cx, obj, id, JSPROP_PERMANENT, len, error))
+        return false;
+
+    result.setObject(*obj);
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MAssertRecoveredOnBailout::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     MOZ_RELEASE_ASSERT(input()->isRecoveredOnBailout() == mustBeRecovered_,
         "assertRecoveredOnBailout failed during compilation");
     writer.writeUnsigned(uint32_t(RInstruction::Recover_AssertRecoveredOnBailout));
     return true;
 }
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -105,16 +105,17 @@ namespace jit {
     _(NewIterator)                              \
     _(NewDerivedTypedObject)                    \
     _(CreateThisWithTemplate)                   \
     _(Lambda)                                   \
     _(LambdaArrow)                              \
     _(SimdBox)                                  \
     _(ObjectState)                              \
     _(ArrayState)                               \
+    _(SetArrayLength)                           \
     _(AtomicIsLockFree)                         \
     _(AssertRecoveredOnBailout)
 
 class RResumePoint;
 class SnapshotIterator;
 
 class MOZ_NON_PARAM RInstruction
 {
@@ -683,16 +684,24 @@ class RArrayState final : public RInstru
         // +1 for the array.
         // +1 for the initalized length.
         return numElements() + 2;
     }
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
+class RSetArrayLength final : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_NUM_OP_(SetArrayLength, 2)
+
+    MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
+};
+
 class RAtomicIsLockFree final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(AtomicIsLockFree, 1)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1037,18 +1037,18 @@ JS_ResolveStandardClass(JSContext* cx, H
     if (!JSID_IS_ATOM(id))
         return true;
 
     /* Check whether we're resolving 'undefined', and define it if so. */
     JSAtom* idAtom = JSID_TO_ATOM(id);
     JSAtom* undefinedAtom = cx->names().undefined;
     if (idAtom == undefinedAtom) {
         *resolved = true;
-        return DefineProperty(cx, global, id, UndefinedHandleValue, nullptr, nullptr,
-                              JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING);
+        return DefineDataProperty(cx, global, id, UndefinedHandleValue,
+                                  JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING);
     }
 
     /* Try for class constructors/prototypes named by well-known atoms. */
     stdnm = LookupStdName(cx->names(), idAtom, standard_class_names);
 
     /* Try less frequently used top-level functions and constants. */
     if (!stdnm)
         stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names);
@@ -2128,18 +2128,17 @@ JS_GetPropertyDescriptor(JSContext* cx, 
 
 static bool
 DefinePropertyByDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                            Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, desc);
-    return DefineProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(),
-                          desc.attributes(), result);
+    return DefineProperty(cx, obj, id, desc, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
                       Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
     return DefinePropertyByDescriptor(cx, obj, id, desc, result);
 }
@@ -2216,30 +2215,30 @@ DefineAccessorPropertyById(JSContext* cx
     assertSameCompartment(cx, obj, id,
                           (attrs & JSPROP_GETTER)
                           ? JS_FUNC_TO_DATA_PTR(JSObject*, getter)
                           : nullptr,
                           (attrs & JSPROP_SETTER)
                           ? JS_FUNC_TO_DATA_PTR(JSObject*, setter)
                           : nullptr);
 
-    return DefineProperty(cx, obj, id, UndefinedHandleValue, getter, setter, attrs);
+    return js::DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
 }
 
 static bool
 DefineDataPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                        unsigned attrs)
 {
     MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_PROPOP_ACCESSORS)));
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, value);
 
-    return DefineProperty(cx, obj, id, value, nullptr, nullptr, attrs);
+    return js::DefineDataProperty(cx, obj, id, value, attrs);
 }
 
 /*
  * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
  */
 static JSNativeWrapper
 NativeOpWrapper(Native native)
@@ -2526,64 +2525,64 @@ DefineDataElement(JSContext* cx, HandleO
         return false;
     return DefineDataPropertyById(cx, obj, id, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                  unsigned attrs)
 {
-    return DefineDataElement(cx, obj, index, value, attrs);
+    return ::DefineDataElement(cx, obj, index, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, Native getter, Native setter,
                  unsigned attrs)
 {
     return DefineAccessorElement(cx, obj, index, attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject valueArg,
                  unsigned attrs)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
-    return DefineDataElement(cx, obj, index, value, attrs);
+    return ::DefineDataElement(cx, obj, index, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleString valueArg,
                  unsigned attrs)
 {
     RootedValue value(cx, StringValue(valueArg));
-    return DefineDataElement(cx, obj, index, value, attrs);
+    return ::DefineDataElement(cx, obj, index, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, int32_t valueArg,
                  unsigned attrs)
 {
     Value value = Int32Value(valueArg);
-    return DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
+    return ::DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, uint32_t valueArg,
                  unsigned attrs)
 {
     Value value = NumberValue(valueArg);
-    return DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
+    return ::DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, double valueArg,
                  unsigned attrs)
 {
     Value value = NumberValue(valueArg);
-    return DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
+    return ::DefineDataElement(cx, obj, index, HandleValue::fromMarkedLocation(&value), attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -351,17 +351,17 @@ ElementAdder::append(JSContext* cx, Hand
 {
     MOZ_ASSERT(index_ < length_);
     if (resObj_) {
         DenseElementResult result =
             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
         if (result == DenseElementResult::Failure)
             return false;
         if (result == DenseElementResult::Incomplete) {
-            if (!DefineElement(cx, resObj_, index_, v))
+            if (!DefineDataElement(cx, resObj_, index_, v))
                 return false;
         }
     } else {
         vp_[index_] = v;
     }
     index_++;
     return true;
 }
@@ -489,17 +489,17 @@ GetArrayElement(JSContext* cx, HandleObj
 }
 
 static inline bool
 DefineArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue value)
 {
     RootedId id(cx);
     if (!ToId(cx, index, &id))
         return false;
-    return DefineProperty(cx, obj, id, value);
+    return DefineDataProperty(cx, obj, id, value);
 }
 
 // Set the value of the property at the given index to v.
 static inline bool
 SetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue v)
 {
     RootedId id(cx);
     if (!ToId(cx, index, &id))
@@ -634,18 +634,17 @@ array_length_getter(JSContext* cx, Handl
 static bool
 array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
                     ObjectOpResult& result)
 {
     if (!obj->is<ArrayObject>()) {
         // This array .length property was found on the prototype
         // chain. Ideally the setter should not have been called, but since
         // we're here, do an impression of SetPropertyByDefining.
-        return DefineProperty(cx, obj, cx->names().length, vp, nullptr, nullptr,
-                              JSPROP_ENUMERATE, result);
+        return DefineDataProperty(cx, obj, cx->names().length, vp, JSPROP_ENUMERATE, result);
     }
 
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     MOZ_ASSERT(arr->lengthIsWritable(),
                "setter shouldn't be called if property is non-writable");
 
     return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result);
 }
@@ -2744,17 +2743,17 @@ CopyArrayElements(JSContext* cx, HandleO
 
             if (!hole) {
                 DenseElementResult edResult = nresult->ensureDenseElements(cx, index, 1);
                 if (edResult != DenseElementResult::Success) {
                     if (edResult == DenseElementResult::Failure)
                         return false;
 
                     MOZ_ASSERT(edResult == DenseElementResult::Incomplete);
-                    if (!DefineElement(cx, nresult, index, value))
+                    if (!DefineDataElement(cx, nresult, index, value))
                         return false;
 
                     break;
                 }
                 nresult->setDenseElementWithType(cx, index, value);
             }
         }
 
@@ -3204,17 +3203,17 @@ SliceSparse(JSContext* cx, HandleObject 
     RootedValue value(cx);
     for (uint32_t index : indexes) {
         MOZ_ASSERT(begin <= index && index < end);
 
         bool hole;
         if (!HasAndGetElement(cx, obj, index, &hole, &value))
             return false;
 
-        if (!hole && !DefineElement(cx, result, index - uint32_t(begin), value))
+        if (!hole && !DefineDataElement(cx, result, index - uint32_t(begin), value))
             return false;
     }
 
     return true;
 }
 
 template <typename T, typename ArrayLength>
 static inline ArrayLength
@@ -3464,17 +3463,17 @@ array_of(JSContext* cx, unsigned argc, V
         cargs[0].setNumber(args.length());
 
         if (!Construct(cx, args.thisv(), cargs, args.thisv(), &obj))
             return false;
     }
 
     // Step 8.
     for (unsigned k = 0; k < args.length(); k++) {
-        if (!DefineElement(cx, obj, k, args[k]))
+        if (!DefineDataElement(cx, obj, k, args[k]))
             return false;
     }
 
     // Steps 9-10.
     if (!SetLengthProperty(cx, obj, args.length()))
         return false;
 
     // Step 11.
@@ -3692,31 +3691,31 @@ static bool
 array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
 {
     // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
     RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
     if (!unscopables)
         return false;
 
     RootedValue value(cx, BooleanValue(true));
-    if (!DefineProperty(cx, unscopables, cx->names().copyWithin, value) ||
-        !DefineProperty(cx, unscopables, cx->names().entries, value) ||
-        !DefineProperty(cx, unscopables, cx->names().fill, value) ||
-        !DefineProperty(cx, unscopables, cx->names().find, value) ||
-        !DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
-        !DefineProperty(cx, unscopables, cx->names().includes, value) ||
-        !DefineProperty(cx, unscopables, cx->names().keys, value) ||
-        !DefineProperty(cx, unscopables, cx->names().values, value))
+    if (!DefineDataProperty(cx, unscopables, cx->names().copyWithin, value) ||
+        !DefineDataProperty(cx, unscopables, cx->names().entries, value) ||
+        !DefineDataProperty(cx, unscopables, cx->names().fill, val