merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 07 Sep 2017 23:59:58 +0200
changeset 379572 b4c1ad9565ee
parent 379517 64bf417d1bdf (current diff)
parent 379571 52433c05f306 (diff)
child 379588 e5e14cc4b4a8
child 379638 6dda26ce5591
push id32456
push userarchaeopteryx@coole-files.de
push date2017-09-07 22:00 +0000
treeherdermozilla-central@b4c1ad9565ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
b4c1ad9565ee / 57.0a1 / 20170907220212 / files
nightly linux64
b4c1ad9565ee / 57.0a1 / 20170907220212 / files
nightly mac
b4c1ad9565ee / 57.0a1 / 20170907220212 / files
nightly win32
b4c1ad9565ee / 57.0a1 / 20170907220212 / files
nightly win64
b4c1ad9565ee / 57.0a1 / 20170907220212 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 3DvwIgw2ORU
browser/app/profile/firefox.js
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/test/tabs/browser_accessibility_indicator.js
browser/themes/linux/browser.css
browser/themes/shared/browser.inc.css
browser/themes/shared/icons/accessibility-active.svg
browser/themes/shared/icons/accessibility.svg
browser/themes/shared/icons/private-browsing.svg
build/valgrind/cross-architecture.sup
dom/indexedDB/test/bfcache_iframe1.html
dom/indexedDB/test/bfcache_iframe2.html
dom/ipc/ProcessHangMonitor.h
dom/media/webspeech/synth/test/file_bfcache_frame.html
dom/media/webspeech/synth/test/file_bfcache_frame2.html
dom/workers/test/WorkerDebugger_frozen_iframe1.html
dom/workers/test/WorkerDebugger_frozen_iframe2.html
dom/workers/test/suspend_iframe.html
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
layout/style/ServoBindings.toml
security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
servo/components/style/build_gecko.rs
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
toolkit/system/osxproxy/ProxyUtils.mm
toolkit/system/windowsproxy/ProxyUtils.cpp
toolkit/xre/nsGDKErrorHandler.cpp
widget/windows/IMMHandler.cpp
xpcom/io/nsLocalFileWin.cpp
xpcom/string/moz.build
xpcom/string/nsAString.h
xpcom/string/nsDependentString.cpp
xpcom/string/nsDependentString.h
xpcom/string/nsDependentSubstring.cpp
xpcom/string/nsDependentSubstring.h
xpcom/string/nsLiteralString.h
xpcom/string/nsPromiseFlatString.cpp
xpcom/string/nsPromiseFlatString.h
xpcom/string/nsString.cpp
xpcom/string/nsString.h
xpcom/string/nsStringComparator.cpp
xpcom/string/nsStringFwd.h
xpcom/string/nsStringIterator.h
xpcom/string/nsStringObsolete.cpp
xpcom/string/nsSubstring.cpp
xpcom/string/nsSubstringTuple.cpp
xpcom/string/nsSubstringTuple.h
xpcom/string/nsTDependentString.cpp
xpcom/string/nsTDependentString.h
xpcom/string/nsTDependentSubstring.cpp
xpcom/string/nsTDependentSubstring.h
xpcom/string/nsTLiteralString.h
xpcom/string/nsTPromiseFlatString.cpp
xpcom/string/nsTPromiseFlatString.h
xpcom/string/nsTString.cpp
xpcom/string/nsTString.h
xpcom/string/nsTStringComparator.cpp
xpcom/string/nsTStringObsolete.cpp
xpcom/string/nsTSubstring.cpp
xpcom/string/nsTSubstring.h
xpcom/string/nsTSubstringTuple.cpp
xpcom/string/nsTSubstringTuple.h
--- 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();
 
@@ -8016,73 +8013,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);
 }