Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 16 Jul 2015 12:45:32 -0400
changeset 253159 61101cfa98d2cdcee1f6582023cdd041adb4d02b
parent 253157 63ec4e50cb90205c4b1a74e29154e23b92e2e957 (diff)
parent 253158 d8023b434e25beed6cb5d38b57d6bb27aaf888df (current diff)
child 253160 19f222fd79fe6651e313b2361c19561027c091af
child 253262 a0f4a688433d5d8ef7468f9b41227b5a93cd2308
push id62369
push userryanvm@gmail.com
push dateThu, 16 Jul 2015 16:45:58 +0000
treeherdermozilla-inbound@61101cfa98d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. a=merge CLOSED TREE
gfx/thebes/gfxWindowsPlatform.cpp
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -62,16 +62,21 @@ DocAccessibleParent::AddSubtree(ProxyAcc
   }
 
   const AccessibleData& newChild = aNewTree[aIdx];
   if (newChild.Role() > roles::LAST_ROLE) {
     NS_ERROR("invalid role");
     return 0;
   }
 
+  if (mAccessibles.Contains(newChild.ID())) {
+    NS_ERROR("ID already in use");
+    return 0;
+  }
+
   auto role = static_cast<a11y::role>(newChild.Role());
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role);
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
   ProxyCreated(newProxy, newChild.Interfaces());
 
   uint32_t accessibles = 1;
@@ -90,16 +95,18 @@ DocAccessibleParent::AddSubtree(ProxyAcc
 }
 
 bool
 DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
 {
   if (mShutdown)
     return true;
 
+  CheckDocTree();
+
   ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
   if (!rootEntry) {
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* root = rootEntry->mProxy;
   if (!root) {
@@ -231,10 +238,22 @@ DocAccessibleParent::Destroy()
   mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
   ProxyDestroyed(this);
   if (mParentDoc)
     mParentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
+void
+DocAccessibleParent::CheckDocTree() const
+{
+  size_t childDocs = mChildDocs.Length();
+  for (size_t i = 0; i < childDocs; i++) {
+    if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
+      MOZ_CRASH("document tree is broken!");
+
+    mChildDocs[i]->CheckDocTree();
+  }
+}
+
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -145,16 +145,18 @@ private:
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
+  void CheckDocTree() const;
+
   static PLDHashOperator ShutdownAccessibles(ProxyEntry* entry, void* unused);
 
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1451,17 +1451,16 @@ pref("devtools.debugger.chrome-debugging
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-timeout", 20000);
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.auto-black-box", true);
-pref("devtools.debugger.tracer", false);
 pref("devtools.debugger.workers", false);
 pref("devtools.debugger.promise", false);
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
--- a/browser/modules/PanelFrame.jsm
+++ b/browser/modules/PanelFrame.jsm
@@ -178,19 +178,17 @@ let PanelFrame = {
         // first time load, wait for load and dispatch after load
         notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
           notificationFrame.removeEventListener("load", panelBrowserOnload, true);
           initFrameShow();
         }, true);
       }
     });
 
-    // in overflow, the anchor is a normal toolbarbutton, in toolbar it is a badge button
-    let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-badge-container") ||
-                 aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon");
+    let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon");
     // Bug 849216 - open the popup asynchronously so we avoid the auto-rollup
     // handling from preventing it being opened in some cases.
     Services.tm.mainThread.dispatch(function() {
       panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
     }, Ci.nsIThread.DISPATCH_NORMAL);
 
     if (aCallback)
       aCallback(notificationFrame);
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -579,35 +579,35 @@ menuitem:not([type]):not(.menuitem-toolt
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open="true"],
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:hover:active {
   padding: 3px;
 }
 
 .findbar-button > .toolbarbutton-text,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
-:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-badge-container,
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-badge-stack,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-icon {
   -moz-margin-end: 0;
   padding: 2px 6px;
   border: 1px solid transparent;
   border-radius: 2px;
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
-:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container,
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   padding: 3px 7px;
 }
 
 /* Help SDK icons fit: */
 toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon,
-toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon {
+toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   width: 16px;
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
   /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */
   width: 32px;
 }
 
@@ -615,17 +615,17 @@ toolbarbutton[constrain-size="true"][cui
   -moz-padding-start: 7px;
   -moz-padding-end: 5px;
 }
 
 .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open]:not([disabled=true]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):not([open]):hover > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):not([open]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
-:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-container,
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-stack,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-icon {
   background: var(--toolbarbutton-hover-background);
   border-width: 1px;
   border-style: solid;
   border-color: var(--toolbarbutton-hover-bordercolor);
   box-shadow: var(--toolbarbutton-hover-boxshadow);
 }
 
@@ -633,17 +633,17 @@ toolbarbutton[constrain-size="true"][cui
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   margin-top: 4px;
   margin-bottom: 4px;
 }
 
 .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open="true"]) > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open="true"] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
-:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container,
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon {
   background: var(--toolbarbutton-active-background);
   box-shadow: var(--toolbarbutton-active-boxshadow);
   border-width: 1px;
   border-style: solid;
   border-color: var(--toolbarbutton-active-bordercolor);
   transition-duration: 10ms;
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1062,17 +1062,17 @@ toolbar .toolbarbutton-1 > .toolbarbutto
     -moz-image-region: rect(36px, 1404px, 72px, 1368px);
   }
 
   #web-apps-button@toolbarButtonPressed@ {
     -moz-image-region: rect(36px, 1440px, 72px, 1404px);
   }
 
   :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
-  :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-badge-container > .toolbarbutton-icon,
+  :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-badge-stack > .toolbarbutton-icon,
   :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
     width: 18px;
   }
 
   #add-share-provider {
     list-style-image: url(chrome://browser/skin/menuPanel-small@2x.png);
     -moz-image-region: rect(0px, 192px, 32px, 160px);
   }
@@ -1085,43 +1085,43 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 
 /* Help 16px icons fit: */
 toolbar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon {
   margin: 2px;
 }
 
 /* Help SDK icons fit: */
 toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon,
-toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon {
+toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   width: 16px;
 }
 
 #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon,
-#main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
+#main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon {
   opacity: .4;
 }
 
 @media (-moz-mac-lion-theme) {
   #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon,
-  #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
+  #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
   #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
   #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-menu-dropmarker,
   #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-dropmarker,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-icon,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-text,
-  .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-container > .toolbarbutton-icon,
+  .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-stack > .toolbarbutton-icon,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menu-dropmarker,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
     opacity: .5;
   }
 
   #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon,
-  #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
+  #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
   #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon {
     opacity: .25;
   }
 }
 
 .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   list-style-image: url(chrome://browser/skin/toolbarbutton-dropmarker.png);
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -228,24 +228,24 @@ toolbarpaletteitem[place="panel"] {
 toolbarpaletteitem[notransition].panel-customization-placeholder,
 toolbarpaletteitem[notransition][place="toolbar"],
 toolbarpaletteitem[notransition][place="palette"],
 toolbarpaletteitem[notransition][place="panel"] {
   transition: none;
 }
 
 toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
-toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-container > .toolbarbutton-icon,
+toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon,
 toolbarpaletteitem > toolbaritem.panel-wide-item,
 toolbarpaletteitem > toolbarbutton[type="menu-button"] {
   transition: transform .3s cubic-bezier(.6, 2, .75, 1.5) !important;
 }
 
 toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-icon,
-toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-container > .toolbarbutton-icon {
+toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   transform: scale(1.3);
 }
 
 toolbarpaletteitem[mousedown] > toolbaritem.panel-wide-item,
 toolbarpaletteitem[mousedown] > toolbarbutton[type="menu-button"] {
   transform: scale(1.1);
 }
 
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -307,19 +307,19 @@ toolbaritem[cui-areatype="menu-panel"][s
  * should have a min-width set so they abide by the width set above (which they do outside of
  * customize mode because they're in a flexed container) */
 toolbarpaletteitem[place="panel"]:not([haswideitem=true]) > .toolbarbutton-1 {
   min-width: 0.01px;
 }
 
 /* Help SDK buttons fit in. */
 toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-icon,
-toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
+toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
 toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon,
-toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-container > .toolbarbutton-icon {
+toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   height: 32px;
   width: 32px;
 }
 
 toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-1 > .toolbarbutton-icon {
   width: 32px;
   height: 32px;
 }
@@ -427,20 +427,20 @@ toolbaritem[cui-areatype="menu-panel"][s
 }
 
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text {
   text-align: center;
 }
 
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon,
-.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container,
+.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-stack,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-icon,
-.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-container,
+.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-stack,
 .panelUI-grid #bookmarks-toolbar-placeholder > .toolbarbutton-icon,
 .customization-palette #bookmarks-toolbar-placeholder > .toolbarbutton-icon,
 .panel-customization-placeholder-child > .toolbarbutton-icon {
   width: 32px;
   height: 32px;
   min-width: 32px;
   min-height: 32px;
   /* Explanation for the below formula (A / B - C)
@@ -456,18 +456,18 @@ toolbaritem[cui-areatype="menu-panel"][s
        which means each horizontal margin should be the half the button's width - (46/2) px.
   */
   margin: 4px calc(@menuPanelButtonWidth@ / 2 - 23px);
 }
 
 /* above we treat the container as the icon for the margins, that is so the
 /* badge itself is positioned correctly. Here we make sure that the icon itself
 /* has the minimum size we want, but no padding/margin. */
-.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbarbutton-icon,
-.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbarbutton-icon {
+.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon,
+.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   width: 32px;
   height: 32px;
   min-width: 32px;
   min-height: 32px;
   margin: 0;
   padding: 0;
 }
 
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -186,49 +186,49 @@
   #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
     -moz-image-region: rect(32px, 992px, 64px, 960px);
   }
 
   toolbaritem[sdkstylewidget="true"] > toolbarbutton {
     -moz-image-region: rect(0, 832px, 32px, 800px);
   }
 
-  #loop-button[cui-areatype="menu-panel"] > .toolbarbutton-badge-container,
-  toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > #loop-button {
     list-style-image: url(chrome://browser/skin/loop/menuPanel.png);
     -moz-image-region: rect(0, 32px, 32px, 0);
   }
 
   /* Make sure that the state icons are not shown in the customization palette. */
-  toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container {
+  toolbarpaletteitem[place="palette"] > #loop-button {
     -moz-image-region: rect(0, 32px, 32px, 0) !important;
   }
 
-  #loop-button[cui-areatype="menu-panel"][state="disabled"] > .toolbarbutton-badge-container,
-  #loop-button[cui-areatype="menu-panel"][disabled="true"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"][state="disabled"],
+  #loop-button[cui-areatype="menu-panel"][disabled="true"] {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
     -moz-image-region: rect(0, 96px, 32px, 64px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 160px, 32px, 128px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
     -moz-image-region: rect(0, 192px, 32px, 160px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 224px, 32px, 192px);
   }
 
   /* Wide panel control icons */
 
   #edit-controls@inAnyPanel@ > toolbarbutton,
   #zoom-controls@inAnyPanel@ > toolbarbutton,
   toolbarpaletteitem[place="palette"] > #edit-controls > toolbarbutton,
@@ -370,49 +370,49 @@
   toolbarpaletteitem[place="palette"] > #pocket-button {
     -moz-image-region: rect(0px, 1984px, 64px, 1920px);
   }
 
   #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
     -moz-image-region: rect(64px, 1984px, 128px, 1920px);
   }
 
-  #loop-button[cui-areatype="menu-panel"] > .toolbarbutton-badge-container,
-  toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"],
+  toolbarpaletteitem[place="palette"] > #loop-button {
     list-style-image: url(chrome://browser/skin/loop/menuPanel@2x.png);
     -moz-image-region: rect(0, 64px, 64px, 0);
   }
 
   /* Make sure that the state icons are not shown in the customization palette. */
-  toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container {
+  toolbarpaletteitem[place="palette"] > #loop-button {
     -moz-image-region: rect(0, 64px, 64px, 0) !important;
   }
 
-  #loop-button[cui-areatype="menu-panel"][state="disabled"] > .toolbarbutton-badge-container,
-  #loop-button[cui-areatype="menu-panel"][disabled="true"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"][state="disabled"],
+  #loop-button[cui-areatype="menu-panel"][disabled="true"] {
     -moz-image-region: rect(0, 128px, 64px, 64px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
     -moz-image-region: rect(0, 192px, 64px, 128px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
     -moz-image-region: rect(0, 256px, 64px, 192px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 320px, 64px, 256px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
     -moz-image-region: rect(0, 384px, 64px, 320px);
   }
 
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 448px, 64px, 384px);
   }
 
   #new-tab-button[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #new-tab-button {
     -moz-image-region: rect(0px, 1088px, 64px, 1024px);
   }
 
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -179,47 +179,47 @@ toolbar[brighttext] #sync-button[status=
 #panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #web-apps-button[cui-areatype="toolbar"] {
   -moz-image-region: rect(0, 720px, 18px, 702px);
 }
 
-#loop-button > .toolbarbutton-badge-container {
+#loop-button {
   list-style-image: url(chrome://browser/skin/loop/toolbar.png);
   -moz-image-region: rect(0, 18px, 18px, 0);
 }
 
-toolbar[brighttext] #loop-button > .toolbarbutton-badge-container {
+toolbar[brighttext] #loop-button {
   list-style-image: url(chrome://browser/skin/loop/toolbar-inverted.png);
 }
 
-#loop-button[state="disabled"] > .toolbarbutton-badge-container,
-#loop-button[disabled="true"] > .toolbarbutton-badge-container {
+#loop-button[state="disabled"],
+#loop-button[disabled="true"] {
   -moz-image-region: rect(0, 36px, 18px, 18px);
 }
 
-#loop-button:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container {
+#loop-button:not([disabled="true"])[state="error"] {
   -moz-image-region: rect(0, 54px, 18px, 36px);
 }
 
-#loop-button:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container {
+#loop-button:not([disabled="true"])[state="action"] {
   -moz-image-region: rect(0, 72px, 18px, 54px);
 }
 
-#loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+#loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
   -moz-image-region: rect(0, 90px, 18px, 72px);
 }
 
-#loop-button:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container {
+#loop-button:not([disabled="true"])[state="active"] {
   -moz-image-region: rect(0, 108px, 18px, 90px);
 }
 
-#loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+#loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
   -moz-image-region: rect(0, 126px, 18px, 108px);
 }
 
 #webide-button[cui-areatype="toolbar"] {
   -moz-image-region: rect(0, 738px, 18px, 720px);
 }
 
 #pocket-button[cui-areatype="toolbar"] {
@@ -425,43 +425,43 @@ toolbar[brighttext] #loop-button > .tool
   #pocket-button[cui-areatype="toolbar"][open] {
 %ifdef XP_MACOSX
     -moz-image-region: rect(72px, 1548px, 108px, 1512px);
 %else
     -moz-image-region: rect(36px, 1548px, 72px, 1512px);
 %endif
   }
 
-  #loop-button > .toolbarbutton-badge-container {
+  #loop-button {
     list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png");
     -moz-image-region: rect(0, 36px, 36px, 0);
   }
 
-  toolbar[brighttext] #loop-button > .toolbarbutton-badge-container {
+  toolbar[brighttext] #loop-button {
     list-style-image: url("chrome://browser/skin/loop/toolbar-inverted@2x.png");
   }
 
-  #loop-button[state="disabled"] > .toolbarbutton-badge-container,
-  #loop-button[disabled="true"] > .toolbarbutton-badge-container {
+  #loop-button[state="disabled"],
+  #loop-button[disabled="true"] {
     -moz-image-region: rect(0, 72px, 36px, 36px);
   }
 
-  #loop-button:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container {
+  #loop-button:not([disabled="true"])[state="error"] {
     -moz-image-region: rect(0, 108px, 36px, 72px);
   }
 
-  #loop-button:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container {
+  #loop-button:not([disabled="true"])[state="action"] {
     -moz-image-region: rect(0, 144px, 36px, 108px);
   }
 
-  #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 180px, 36px, 144px);
   }
 
-  #loop-button:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container {
+  #loop-button:not([disabled="true"])[state="active"] {
     -moz-image-region: rect(0, 216px, 36px, 180px);
   }
 
-  #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container {
+  #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
     -moz-image-region: rect(0, 252px, 36px, 216px);
   }
 }
 %endif
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -588,23 +588,23 @@ menuitem.bookmark-item {
 %include ../shared/menupanel.inc.css
 
 @media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) {
   :-moz-any(@primaryToolbarButtons@),
   #bookmarks-menu-button.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
     list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png");
   }
 
-  #loop-button > .toolbarbutton-badge-container {
+  #loop-button {
     list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver.png)
   }
 }
 
 @media (-moz-windows-theme: luna-silver) and (min-resolution: 1.1dppx) {
-  #loop-button > .toolbarbutton-badge-container {
+  #loop-button {
     list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver@2x.png)
   }
 }
 
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
@@ -669,30 +669,30 @@ toolbar[brighttext] .toolbarbutton-1 > .
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   -moz-padding-start: 0;
   -moz-box-align: center;
 }
 
 .findbar-button > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container,
+#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 @conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon {
   padding: var(--toolbarbutton-vertical-inner-padding) 6px;
   border: 1px solid;
   border-color: transparent;
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
 toolbarbutton[cui-areatype="toolbar"] > :-moz-any(@nestedButtons@) > .toolbarbutton-icon,
 :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])):not(:-moz-any(@nestedButtons@)) > .toolbarbutton-icon,
-:-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-badge-container > .toolbarbutton-icon,
+:-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-badge-stack > .toolbarbutton-icon,
 :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   width: 18px;
 }
 
 #nav-bar toolbarbutton[cui-areatype="toolbar"] > :-moz-any(@nestedButtons@) > .toolbarbutton-icon,
 #nav-bar :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-icon,
 #nav-bar :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
@@ -723,40 +723,40 @@ toolbarbutton[cui-areatype="toolbar"] > 
     --toolbarbutton-checkedhover-backgroundcolor: rgba(90%,90%,90%,.4);
 
     --toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
     --toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.2);
   }
   .findbar-button > .toolbarbutton-text,
   #nav-bar .toolbarbutton-1 > .toolbarbutton-icon,
   #nav-bar .toolbarbutton-1 > .toolbarbutton-text,
-  #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container,
+  #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack,
   #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
   #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
     background-color: hsla(210,32%,93%,0);
     background-origin: padding-box;
     border-radius: 2px;
     border-color: hsla(210,54%,20%,0) hsla(210,54%,20%,0) hsla(210,54%,20%,0);
     box-shadow: 0 1px hsla(0,0%,100%,0) inset,
                 0 1px hsla(210,54%,20%,0),
                 0 0 2px hsla(210,54%,20%,0);
     transition-property: background-color, border-color, box-shadow;
     transition-duration: 150ms;
   }
 }
 
 #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container,
+#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack,
 #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   padding: calc(var(--toolbarbutton-vertical-inner-padding) + 1px) 7px;
 }
 
 /* Help SDK icons fit: */
 toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon,
-toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon {
+toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   width: 16px;
 }
 
 #nav-bar toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
   /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */
   width: 32px;
 }
 
@@ -816,17 +816,17 @@ toolbarbutton[constrain-size="true"][cui
 }
 
 .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-container,
+#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack,
 @conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
   background: var(--toolbarbutton-hover-background);
   border-color: var(--toolbarbutton-hover-bordercolor);
   box-shadow: var(--toolbarbutton-hover-boxshadow);
 }
 
 @media (-moz-os-version: windows-xp),
@@ -861,33 +861,33 @@ toolbarbutton[constrain-size="true"][cui
   }
 }
 
 .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
+#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack {
   background: var(--toolbarbutton-active-background);
   border-color: var(--toolbarbutton-active-bordercolor);
   box-shadow: var(--toolbarbutton-active-boxshadow);
   transition-duration: 10ms;
 }
 
 @media (-moz-os-version: windows-xp),
        (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7) {
   /* < Win8 */
   .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
   #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon,
   #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon,
   #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
   #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
-  #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
+  #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack {
     text-shadow: none;
     transition: none;
   }
 
   #nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
     -moz-border-start-color: hsla(210,54%,20%,.35);
   }
 
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -21,17 +21,17 @@ class DomainPolicyClone;
 }
 }
 %}
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
 
-[scriptable, uuid(f4c578b8-5bac-4ba1-9582-f1140e09a3b4)]
+[scriptable, uuid(50418f5c-b0d8-42c3-ba5d-efffb6927e1c)]
 interface nsIScriptSecurityManager : nsISupports
 {
     /**
      * For each of these hooks returning NS_OK means 'let the action continue'.
      * Returning an error code means 'veto the action'. XPConnect will return
      * false to the js engine if the action is vetoed. The implementor of this
      * interface is responsible for setting a JS exception into the JSContext
      * if that is appropriate.
@@ -197,16 +197,30 @@ interface nsIScriptSecurityManager : nsI
      * Returns a unique nonce principal with |originAttributes|.
      * See nsIPrincipal.h for a description of origin attributes, and
      * SystemDictionaries.webidl for a list of origin attributes and their defaults.
      */
     [implicit_jscontext]
     nsIPrincipal createNullPrincipal(in jsval originAttributes);
 
     /**
+     * Creates an expanded principal whose capabilities are the union of the
+     * given principals. An expanded principal has an asymmetric privilege
+     * relationship with its sub-principals (that is to say, it subsumes the
+     * sub-principals, but the sub-principals do not subsume it), even if
+     * there's only one. This presents a legitimate use-case for making an
+     * expanded principal around a single sub-principal, which we do frequently.
+     *
+     * Expanded principals cannot have origin attributes themselves, but rather
+     * have them through their sub-principals - so we don't accept them here.
+     */
+    nsIPrincipal createExpandedPrincipal([array, size_is(aLength)] in nsIPrincipal aPrincipalArray,
+                                         [optional] in unsigned long aLength);
+
+    /**
      * Returns OK if aSourceURI and target have the same "origin"
      * (scheme, host, and port).
      * ReportError flag suppresses error reports for functions that
      * don't need reporting.
      */
     void checkSameOriginURI(in nsIURI aSourceURI,
                             in nsIURI aTargetURI,
                             in boolean reportError);
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=4 et sw=4 tw=80: */
+/* 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 "nsScriptSecurityManager.h"
 
 #include "mozilla/ArrayUtils.h"
 
@@ -1023,16 +1023,31 @@ nsScriptSecurityManager::CreateNullPrinc
   }
   nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create(attrs);
   NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
   prin.forget(aPrincipal);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptSecurityManager::CreateExpandedPrincipal(nsIPrincipal** aPrincipalArray, uint32_t aLength,
+                                                 nsIPrincipal** aResult)
+{
+  nsTArray<nsCOMPtr<nsIPrincipal>> principals;
+  principals.SetCapacity(aLength);
+  for (uint32_t i = 0; i < aLength; ++i) {
+    principals.AppendElement(aPrincipalArray[i]);
+  }
+
+  nsCOMPtr<nsIPrincipal> p = new nsExpandedPrincipal(principals);
+  p.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
                                                  uint32_t aAppId,
                                                  bool aInMozBrowser,
                                                  nsIPrincipal** aPrincipal)
 {
   NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
                  NS_ERROR_INVALID_ARG);
 
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -42,17 +42,17 @@ function run_test() {
   do_check_eq(exampleOrg.origin, 'http://example.org');
   checkOriginAttributes(exampleOrg);
   var exampleCom = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {});
   do_check_eq(exampleCom.origin, 'https://www.example.com:123');
   checkOriginAttributes(exampleCom);
   var nullPrin = Cu.getObjectPrincipal(new Cu.Sandbox(null));
   do_check_true(/^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(nullPrin.origin));
   checkOriginAttributes(nullPrin);
-  var ep = Cu.getObjectPrincipal(new Cu.Sandbox([exampleCom, nullPrin, exampleOrg]));
+  var ep = ssm.createExpandedPrincipal([exampleCom, nullPrin, exampleOrg]);
   checkOriginAttributes(ep);
   checkCrossOrigin(exampleCom, exampleOrg);
   checkCrossOrigin(exampleOrg, nullPrin);
 
   // nsEP origins should be in lexical order.
   do_check_eq(ep.origin, `[Expanded Principal [${exampleOrg.origin}, ${exampleCom.origin}, ${nullPrin.origin}]]`);
 
   // Make sure createCodebasePrincipal does what the rest of gecko does.
--- a/configure.in
+++ b/configure.in
@@ -471,26 +471,46 @@ esac
 
 AC_SUBST(MACOSX_DEPLOYMENT_TARGET)
 
 dnl ========================================================
 dnl Special MacOS X checks
 dnl ========================================================
 
 if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then
-  AC_MSG_CHECKING([MacOS X compatibility with rust])
-  # rustc doesn't support MacOS X 10.6 or earlier.
+  AC_MSG_CHECKING([rustc compatibility with MacOS X])
+  # Stock rustc doesn't support MacOS X 10.6 or earlier.
   # https://github.com/rust-lang/rust/issues/25342
   _MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2`
   if test "$_MACOSX_TARGET_MINOR" -lt 7; then
+    dnl Test C linkage against rust code to see if the rust
+    dnl toolchain output is compatible.
+    cat > conftest.rs <<EOF
+      [#[no_mangle]]
+      pub extern fn rusty_answer() -> u8 { 42 }
+EOF
+    ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null"
+    AC_TRY_EVAL(ac_try)
+    save_LDFLAGS=$LDFLAGS
+    LDFLAGS="$LDFLAGS conftest.a -lpthread -lm"
+    AC_TRY_LINK_FUNC([rusty_answer], [
+      AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc])
+    ], [
+      AC_MSG_RESULT([cannot link on $MACOSX_DEPLOYMENT_TARGET])
+      MOZ_RUST=
+    ])
+    LDFLAGS=$save_LDFLAGS
+    rm -rf conftest*
+  else
+    AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok])
+  fi
+  if test -z "$MOZ_RUST"; then
     AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET
       Add 'ac_add_options --enable-macos-target=10.7' (or later)
-      to mozconfig, or disable rust support.])
-  else
-    AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok])
+      to mozconfig, disable rust support, or use an alternate toolchain.])
   fi
 fi
 
 dnl ========================================================
 dnl Special win32 checks
 dnl ========================================================
 
 # Target the Windows 8.1 SDK by default
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -1310,17 +1310,19 @@ nsSHistory::RemoveDuplicate(int32_t aInd
   int32_t compareIndex = aKeepNext ? aIndex + 1 : aIndex - 1;
   nsCOMPtr<nsISHEntry> root1, root2;
   GetEntryAtIndex(aIndex, false, getter_AddRefs(root1));
   GetEntryAtIndex(compareIndex, false, getter_AddRefs(root2));
   if (IsSameTree(root1, root2)) {
     nsCOMPtr<nsISHTransaction> txToRemove, txToKeep, txNext, txPrev;
     GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove));
     GetTransactionAtIndex(compareIndex, getter_AddRefs(txToKeep));
-    NS_ENSURE_TRUE(txToRemove, false);
+    if (!txToRemove) {
+      return false;
+    }
     NS_ENSURE_TRUE(txToKeep, false);
     txToRemove->GetNext(getter_AddRefs(txNext));
     txToRemove->GetPrev(getter_AddRefs(txPrev));
     txToRemove->SetNext(nullptr);
     txToRemove->SetPrev(nullptr);
     if (aKeepNext) {
       if (txPrev) {
         txPrev->SetNext(txToKeep);
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -202,18 +202,17 @@ NS_INTERFACE_MAP_BEGIN(AudioChannelServi
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(AudioChannelService)
 NS_IMPL_RELEASE(AudioChannelService)
 
 AudioChannelService::AudioChannelService()
-  : mDisabled(false)
-  , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
+  : mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
   , mTelephonyChannel(false)
   , mContentOrNormalChannel(false)
   , mAnyChannel(false)
 {
   if (IsParentProcess()) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
@@ -233,20 +232,16 @@ AudioChannelService::AudioChannelService
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                AudioChannel aChannel)
 {
-  if (mDisabled) {
-    return;
-  }
-
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (!winData) {
     winData = new AudioChannelWindow(windowID);
     mWindows.AppendElement(winData);
   }
 
   MOZ_ASSERT(!winData->mAgents.Contains(aAgent));
@@ -267,20 +262,16 @@ AudioChannelService::RegisterAudioChanne
   }
 
   MaybeSendStatusUpdate();
 }
 
 void
 AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
 {
-  if (mDisabled) {
-    return;
-  }
-
   AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
   if (!winData) {
     return;
   }
 
   if (winData->mAgents.Contains(aAgent)) {
     int32_t channel = aAgent->AudioChannelType();
     uint64_t windowID = aAgent->WindowID();
@@ -461,18 +452,18 @@ AudioChannelService::AnyAudioChannelIsAc
   return false;
 }
 
 NS_IMETHODIMP
 AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
                              const char16_t* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
-    mDisabled = true;
     mWindows.Clear();
+    Shutdown();
   }
 
 #ifdef MOZ_WIDGET_GONK
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, "mozsettings-changed")) {
     RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
     if (!WrappedJSToDictionary(aSubject, setting)) {
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -38,21 +38,16 @@ public:
 
   /**
    * Returns the AudioChannelServce singleton.
    * If AudioChannelServce is not exist, create and return new one.
    * Only to be called from main thread.
    */
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
-  /**
-   * Shutdown the singleton.
-   */
-  static void Shutdown();
-
   static bool IsAudioChannelMutedByDefault();
 
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
   void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel);
 
@@ -131,16 +126,21 @@ public:
 
   void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
                            bool aContentOrNormalChannel, bool aAnyChannel);
 
 private:
   AudioChannelService();
   ~AudioChannelService();
 
+  /**
+   * Shutdown the singleton.
+   */
+  static void Shutdown();
+
   void MaybeSendStatusUpdate();
 
   bool ContentOrNormalChannelIsActive();
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aVisible, uint64_t aChildID);
 
@@ -199,18 +199,16 @@ private:
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows;
 
   nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren;
 
 #ifdef MOZ_WIDGET_GONK
   nsTArray<SpeakerManagerService*>  mSpeakerManager;
 #endif
 
-  bool mDisabled;
-
   nsCOMPtr<nsIRunnable> mRunnable;
 
   uint64_t mDefChannelChildID;
 
   // These boolean are used to know if we have to send an status update to the
   // service running in the main process.
   bool mTelephonyChannel;
   bool mContentOrNormalChannel;
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -712,26 +712,16 @@ nsFocusManager::WindowRaised(nsIDOMWindo
   nsCOMPtr<nsPIDOMWindow> currentWindow;
   nsCOMPtr<nsIContent> currentFocus =
     GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
 
   NS_ASSERTION(currentWindow, "window raised with no window current");
   if (!currentWindow)
     return NS_OK;
 
-  nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
-
-  nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell();
-  if (presShell) {
-    // disable selection mousedown state on activation
-    // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
-    nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
-    frameSelection->SetDragState(false);
-  }
-
   // If there is no nsIXULWindow, then this is an embedded or child process window.
   // Pass false for aWindowRaised so that commands get updated.
   nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(baseWindow));
   Focus(currentWindow, currentFocus, 0, true, false, xulWin != nullptr, true);
 
   return NS_OK;
 }
 
@@ -759,16 +749,29 @@ nsFocusManager::WindowLowered(nsIDOMWind
   }
 
   if (mActiveWindow != window)
     return NS_OK;
 
   // clear the mouse capture as the active window has changed
   nsIPresShell::SetCapturingContent(nullptr, 0);
 
+  // In addition, reset the drag state to ensure that we are no longer in
+  // drag-select mode.
+  if (mFocusedWindow) {
+    nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
+    if (docShell) {
+      nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+      if (presShell) {
+        nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
+        frameSelection->SetDragState(false);
+      }
+    }
+  }
+
   // If this is a parent or single process window, send the deactivate event.
   // Events for child process windows will be sent when ParentActivated
   // is called.
   if (XRE_IsParentProcess()) {
     ActivateOrDeactivate(window, false);
   }
 
   // keep track of the window being lowered, so that attempts to raise the
--- a/dom/base/test/chrome/cpows_child.js
+++ b/dom/base/test/chrome/cpows_child.js
@@ -12,17 +12,19 @@ const Cu = Components.utils;
     xray_test,
     symbol_test,
     compartment_test,
     regexp_test,
     postmessage_test,
     sync_test,
     async_test,
     rpc_test,
-    lifetime_test
+    lifetime_test,
+    cancel_test,
+    cancel_test2,
   ];
 
   function go() {
     if (tests.length == 0) {
       sendRpcMessage("cpows:done", {});
       return;
     }
 
@@ -261,8 +263,77 @@ function lifetime_test(finish)
   Components.utils.schedulePreciseGC(function() {
     addMessageListener("cpows:lifetime_test_3", (msg) => {
       ok(obj.wont_die.f == 2, "reverse CPOW still works");
       finish();
     });
     sendRpcMessage("cpows:lifetime_test_2");
   });
 }
+
+function cancel_test(finish)
+{
+  if (!is_remote) {
+    // No point in doing this in single-process mode.
+    finish();
+    return;
+  }
+
+  let fin1 = false, fin2 = false;
+
+  // CPOW from the parent runs f. When it sends a sync message, the
+  // CPOW is canceled. The parent starts running again immediately
+  // after the CPOW is canceled; f also continues running.
+  function f() {
+    let res = sendSyncMessage("cpows:cancel_sync_message");
+    ok(res[0] == 12, "cancel_sync_message result correct");
+    fin1 = true;
+    if (fin1 && fin2) finish();
+  }
+
+  sendAsyncMessage("cpows:cancel_test", null, {f: f});
+  addMessageListener("cpows:cancel_test_done", msg => {
+    fin2 = true;
+    if (fin1 && fin2) finish();
+  });
+}
+
+function cancel_test2(finish)
+{
+  if (!is_remote) {
+    // No point in doing this in single-process mode.
+    finish();
+    return;
+  }
+
+  let fin1 = false, fin2 = false;
+
+  // CPOW from the parent runs f. When it does a sync XHR, the
+  // CPOW is canceled. The parent starts running again immediately
+  // after the CPOW is canceled; f also continues running.
+  function f() {
+    let req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
+              createInstance(Components.interfaces.nsIXMLHttpRequest);
+    let fin = false;
+    let reqListener = () => {
+      if (req.readyState != req.DONE) {
+        return;
+      }
+      ok(req.status == 200, "XHR succeeded");
+      fin = true;
+    };
+
+    req.onload = reqListener;
+    req.open("get", "http://example.com", false);
+    req.send(null);
+
+    ok(fin == true, "XHR happened");
+
+    fin1 = true;
+    if (fin1 && fin2) finish();
+  }
+
+  sendAsyncMessage("cpows:cancel_test2", null, {f: f});
+  addMessageListener("cpows:cancel_test2_done", msg => {
+    fin2 = true;
+    if (fin1 && fin2) finish();
+  });
+}
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -359,16 +359,42 @@
       }
       ok(threw, "limited-lifetime CPOW stopped working");
       wontDie = null;
       Components.utils.schedulePreciseGC(function() {
         savedMM.sendAsyncMessage("cpows:lifetime_test_3");
       });
     }
 
+    function recvCancelTest(msg) {
+      let failed = false;
+      try {
+        msg.objects.f();
+      } catch (e if /cross-process JS call failed/.test(String(e))) {
+        failed = true;
+      }
+      ok(failed, "CPOW should fail due to cancelation");
+      msg.target.messageManager.sendAsyncMessage("cpows:cancel_test_done");
+    }
+
+    function recvCancelSyncMessage() {
+      return 12;
+    }
+
+    function recvCancelTest2(msg) {
+      let failed = false;
+      try {
+        msg.objects.f();
+      } catch (e if /cross-process JS call failed/.test(String(e))) {
+        failed = true;
+      }
+      ok(failed, "CPOW should fail due to cancelation");
+      msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done");
+    }
+
     function run_tests(type) {
       info("Running tests: " + type);
       var node = document.getElementById('cpowbrowser_' + type);
 
       test_state = type;
       test_node = node;
 
       function recvIsRemote(message) {
@@ -395,16 +421,19 @@
       if (typeof Symbol === "function") {
         mm.addMessageListener("cpows:symbol_test", recvSymbolTest);
       }
       mm.addMessageListener("cpows:compartment_test", recvCompartmentTest);
       mm.addMessageListener("cpows:regexp_test", recvRegExpTest);
       mm.addMessageListener("cpows:postmessage_test", recvPostMessageTest);
       mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1);
       mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2);
+      mm.addMessageListener("cpows:cancel_test", recvCancelTest);
+      mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage);
+      mm.addMessageListener("cpows:cancel_test2", recvCancelTest2);
       mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true);
     }
 
     function start() {
       run_tests('remote');
     }
 
     function finish() {
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1183363.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+    var ctx = canvas.getContext('2d');
+    ctx.rect(2, 6, 9, 8);
+    ctx.setTransform(1, 2, 3, 0, 4, 1);
+    setTimeout(function() {
+        ctx.moveTo(0, 1);
+        ctx.isPointInPath(0, 0, 'evenodd');
+    }, 0);
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<canvas id="canvas"></canvas>
+</body>
+</html>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -17,8 +17,9 @@ skip-if(Android||B2G) load 780392-1.html
 skip-if(Android||B2G) skip-if(gtkWidget&&isDebugBuild) load 789933-1.html # bug 833371 for B2G, bug 1155252 for linux
 load 794463-1.html
 load 802926-1.html
 load 896047-1.html
 load 896047-2.html
 load 916128-1.html
 load 934939-1.html
 load 1099143-1.html
+load 1183363.html
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -940,17 +940,17 @@ IMEStateManager::SetInputContextForChild
      GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
      NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
      GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange),
      sPresContext, sActiveTabParent.get()));
 
-  if (NS_WARN_IF(aTabParent != sActiveTabParent)) {
+  if (aTabParent != sActiveTabParent) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("ISM:    IMEStateManager::SetInputContextForChildProcess(), FAILED, "
        "because non-focused tab parent tries to set input context"));
     return;
   }
 
   if (NS_WARN_IF(!sPresContext)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIArray;
 interface nsIDocument;
 interface nsIInterceptedChannel;
 interface nsIPrincipal;
+interface nsIRunnable;
 interface nsIURI;
 
 [scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
@@ -28,17 +29,17 @@ interface nsIServiceWorkerInfo : nsISupp
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(ed1cbbf2-0400-4caa-8eb2-b09d21a94e20)]
+[scriptable, builtinclass, uuid(8d80dd18-597b-4378-b41e-768bfe48dd4f)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -88,18 +89,21 @@ interface nsIServiceWorkerManager : nsIS
    * These are only meant to be called from ServiceWorkerRegistration instances.
    */
   [noscript] nsISupports GetInstalling(in nsIDOMWindow aWindow, in DOMString aScope);
   [noscript] nsISupports GetWaiting(in nsIDOMWindow aWindow, in DOMString aScope);
   [noscript] nsISupports GetActive(in nsIDOMWindow aWindow, in DOMString aScope);
 
   /*
    * Returns a ServiceWorker.
+   *  - aLoadFailedRunnable is an optional callback that will fire on main thread if
+   *    a ServiceWorker object is returned, but later fails to load for some reason.
    */
-  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
+  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow,
+                                               in nsIRunnable aLoadFailedRunnable);
 
   /*
    * Clears ServiceWorker registrations from memory and disk for the specified
    * host.
    * - All ServiceWorker instances change their state to redundant.
    * - Existing ServiceWorker instances handling fetches will keep running.
    * - All documents will immediately stop being controlled.
    * - Unregister jobs will be queued for all registrations.
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -11,49 +11,16 @@
 namespace mozilla {
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define SINK_LOG(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 #define SINK_LOG_V(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 
-AudioSink::OnAudioEndTimeUpdateTask::OnAudioEndTimeUpdateTask(
-                                     MediaDecoderStateMachine* aStateMachine)
-  : mMutex("OnAudioEndTimeUpdateTask")
-  , mEndTime(0)
-  , mStateMachine(aStateMachine)
-{
-}
-
-NS_IMETHODIMP
-AudioSink::OnAudioEndTimeUpdateTask::Run() {
-  MutexAutoLock lock(mMutex);
-  if (mStateMachine) {
-    mStateMachine->OnAudioEndTimeUpdate(mEndTime);
-  }
-  return NS_OK;
-}
-
-void
-AudioSink::OnAudioEndTimeUpdateTask::Dispatch(int64_t aEndTime) {
-  MutexAutoLock lock(mMutex);
-  if (mStateMachine) {
-    mEndTime = aEndTime;
-    nsRefPtr<AudioSink::OnAudioEndTimeUpdateTask> runnable(this);
-    mStateMachine->TaskQueue()->Dispatch(runnable.forget());
-  }
-}
-
-void
-AudioSink::OnAudioEndTimeUpdateTask::Cancel() {
-  MutexAutoLock lock(mMutex);
-  mStateMachine = nullptr;
-}
-
 // The amount of audio frames that is used to fuzz rounding errors.
 static const int64_t AUDIO_FUZZ_FRAMES = 1;
 
 AudioSink::AudioSink(MediaDecoderStateMachine* aStateMachine,
                      int64_t aStartTime, AudioInfo aInfo, dom::AudioChannel aChannel)
   : mStateMachine(aStateMachine)
   , mStartTime(aStartTime)
   , mWritten(0)
@@ -63,17 +30,16 @@ AudioSink::AudioSink(MediaDecoderStateMa
   , mVolume(1.0)
   , mPlaybackRate(1.0)
   , mPreservesPitch(false)
   , mStopAudioThread(false)
   , mSetVolume(false)
   , mSetPlaybackRate(false)
   , mSetPreservesPitch(false)
   , mPlaying(true)
-  , mOnAudioEndTimeUpdateTask(new OnAudioEndTimeUpdateTask(aStateMachine))
 {
 }
 
 nsresult
 AudioSink::Init()
 {
   nsresult rv = NS_NewNamedThread("Media Audio",
                                   getter_AddRefs(mThread),
@@ -127,17 +93,16 @@ AudioSink::PrepareToShutdown()
     mAudioStream->Cancel();
   }
   GetReentrantMonitor().NotifyAll();
 }
 
 void
 AudioSink::Shutdown()
 {
-  mOnAudioEndTimeUpdateTask->Cancel();
   mThread->Shutdown();
   mThread = nullptr;
   MOZ_ASSERT(!mAudioStream);
 }
 
 void
 AudioSink::SetVolume(double aVolume)
 {
@@ -213,20 +178,16 @@ AudioSink::AudioLoop()
       // we pushed to the audio hardware. We must push silence into the audio
       // hardware so that the next audio chunk begins playback at the correct
       // time.
       missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
       mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
     } else {
       mWritten += PlayFromAudioQueue();
     }
-    int64_t endTime = GetEndTime();
-    if (endTime != -1) {
-      mOnAudioEndTimeUpdateTask->Dispatch(endTime);
-    }
   }
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
   if (!mStopAudioThread && mPlaying) {
     Drain();
   }
   SINK_LOG("AudioLoop complete");
   Cleanup();
@@ -422,17 +383,17 @@ AudioSink::WriteSilence(uint32_t aFrames
   buf.SetLength(numSamples);
   memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue));
   mAudioStream->Write(buf.Elements(), aFrames);
 
   StartAudioStreamPlaybackIfNeeded();
 }
 
 int64_t
-AudioSink::GetEndTime()
+AudioSink::GetEndTime() const
 {
   CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime;
   if (!playedUsecs.isValid()) {
     NS_WARNING("Int overflow calculating audio end time");
     return -1;
   }
   return playedUsecs.value();
 }
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -22,16 +22,19 @@ public:
 
   AudioSink(MediaDecoderStateMachine* aStateMachine,
             int64_t aStartTime, AudioInfo aInfo, dom::AudioChannel aChannel);
 
   nsresult Init();
 
   int64_t GetPosition();
 
+  // Thread-safe. Can be called on any thread.
+  int64_t GetEndTime() const;
+
   // Check whether we've pushed more frames to the audio hardware than it has
   // played.
   bool HasUnplayedFrames();
 
   // Tell the AudioSink to stop processing and initiate shutdown.  Must be
   // called with the decoder monitor held.
   void PrepareToShutdown();
 
@@ -85,18 +88,16 @@ private:
 
   void UpdateStreamSettings();
 
   // If we have already written enough frames to the AudioStream, start the
   // playback.
   void StartAudioStreamPlaybackIfNeeded();
   void WriteSilence(uint32_t aFrames);
 
-  int64_t GetEndTime();
-
   MediaQueue<AudioData>& AudioQueue();
 
   ReentrantMonitor& GetReentrantMonitor();
   void AssertCurrentThreadInMonitor();
   void AssertOnAudioThread();
 
   nsRefPtr<MediaDecoderStateMachine> mStateMachine;
 
@@ -119,45 +120,28 @@ private:
   // PCM frames written to the stream so far.
   Atomic<int64_t> mWritten;
 
   // Keep the last good position returned from the audio stream. Used to ensure
   // position returned by GetPosition() is mono-increasing in spite of audio
   // stream error.
   int64_t mLastGoodPosition;
 
-  AudioInfo mInfo;
+  const AudioInfo mInfo;
 
   dom::AudioChannel mChannel;
 
   double mVolume;
   double mPlaybackRate;
   bool mPreservesPitch;
 
   bool mStopAudioThread;
 
   bool mSetVolume;
   bool mSetPlaybackRate;
   bool mSetPreservesPitch;
 
   bool mPlaying;
-
-  class OnAudioEndTimeUpdateTask : public nsRunnable {
-  public:
-    explicit OnAudioEndTimeUpdateTask(MediaDecoderStateMachine* aStateMachine);
-
-    NS_IMETHOD Run() override;
-
-    void Dispatch(int64_t aEndTime);
-    void Cancel();
-
-  private:
-    Mutex mMutex;
-    int64_t mEndTime;
-    nsRefPtr<MediaDecoderStateMachine> mStateMachine;
-  };
-
-  nsRefPtr<OnAudioEndTimeUpdateTask> mOnAudioEndTimeUpdateTask;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/Intervals.h
+++ b/dom/media/Intervals.h
@@ -152,16 +152,21 @@ public:
   }
 
   bool Intersects(const SelfType& aOther) const
   {
     return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
       (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
   }
 
+  bool IntersectsStrict(const SelfType& aOther) const
+  {
+    return mStart < aOther.mEnd && aOther.mStart < mEnd;
+  }
+
   // Same as Intersects, but including the boundaries.
   bool Touches(const SelfType& aOther) const
   {
     return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
       (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
   }
 
   // Returns true if aOther is strictly to the right of this and contiguous.
@@ -303,24 +308,28 @@ public:
     this->~IntervalSet();
     new(this) IntervalSet(Move(aOther));
     return *this;
   }
 
   SelfType& operator= (const ElemType& aInterval)
   {
     mIntervals.Clear();
-    mIntervals.AppendElement(aInterval);
+    if (!aInterval.IsEmpty()) {
+      mIntervals.AppendElement(aInterval);
+    }
     return *this;
   }
 
   SelfType& operator= (ElemType&& aInterval)
   {
     mIntervals.Clear();
-    mIntervals.AppendElement(Move(aInterval));
+    if (!aInterval.IsEmpty()) {
+      mIntervals.AppendElement(Move(aInterval));
+    }
     return *this;
   }
 
   SelfType& Add(const SelfType& aIntervals)
   {
     mIntervals.AppendElements(aIntervals.mIntervals);
     Normalize();
     return *this;
@@ -458,17 +467,17 @@ public:
   // Mutate this TimeRange to be the intersection of this and aOther.
   SelfType& Intersection(const SelfType& aOther)
   {
     ContainerType intersection;
 
     const ContainerType& other = aOther.mIntervals;
     IndexType i = 0, j = 0;
     for (; i < mIntervals.Length() && j < other.Length();) {
-      if (mIntervals[i].Intersects(other[j])) {
+      if (mIntervals[i].IntersectsStrict(other[j])) {
         intersection.AppendElement(mIntervals[i].Intersection(other[j]));
       }
       if (mIntervals[i].mEnd < other[j].mEnd) {
         i++;
       } else {
         j++;
       }
     }
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -353,18 +353,18 @@ bool MediaDecoderStateMachine::HaveNextF
          (!HasVideo() || VideoQueue().GetSize() > 1);
 }
 
 int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   int64_t audioDecoded = AudioQueue().Duration();
-  if (mAudioEndTime != -1) {
-    audioDecoded += mAudioEndTime - GetMediaTime();
+  if (AudioEndTime() != -1) {
+    audioDecoded += AudioEndTime() - GetMediaTime();
   }
   return audioDecoded;
 }
 
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -1771,17 +1771,17 @@ MediaDecoderStateMachine::StartAudioThre
 int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(),
                "Should only call AudioDecodedUsecs() when we have audio");
   // The amount of audio we have decoded is the amount of audio data we've
   // already decoded and pushed to the hardware, plus the amount of audio
   // data waiting to be pushed to the hardware.
-  int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0;
+  int64_t pushed = (AudioEndTime() != -1) ? (AudioEndTime() - GetMediaTime()) : 0;
 
   // Currently for real time streams, AudioQueue().Duration() produce
   // wrong values (Bug 1114434), so we use frame counts to calculate duration.
   if (IsRealTime()) {
     return pushed + FramesToUsecs(AudioQueue().FrameCount(), mInfo.mAudio.mRate).value();
   }
   return pushed + AudioQueue().Duration();
 }
@@ -2417,32 +2417,35 @@ nsresult MediaDecoderStateMachine::RunSt
 
       if (mState != DECODER_STATE_COMPLETED) {
         // While we're presenting a frame we can change state. Whatever changed
         // our state should have scheduled another state machine run.
         NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
         return NS_OK;
       }
 
-      StopAudioThread();
-      mDecodedStream->StopPlayback();
-
       if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
           !mSentPlaybackEndedEvent)
       {
-        int64_t clockTime = std::max(mAudioEndTime, mVideoFrameEndTime);
+        int64_t clockTime = std::max(AudioEndTime(), mVideoFrameEndTime);
         clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
         UpdatePlaybackPosition(clockTime);
 
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
       }
+
+      // Stop audio sink after call to AudioEndTime() above, otherwise it will
+      // return an incorrect value due to a null mAudioSink.
+      StopAudioThread();
+      mDecodedStream->StopPlayback();
+
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 void
@@ -2728,19 +2731,19 @@ void MediaDecoderStateMachine::UpdateRen
       ScheduleStateMachineIn(USECS_PER_S);
       return;
     }
   }
 
   // Cap the current time to the larger of the audio and video end time.
   // This ensures that if we're running off the system clock, we don't
   // advance the clock to after the media end time.
-  if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) {
+  if (mVideoFrameEndTime != -1 || AudioEndTime() != -1) {
     // These will be non -1 if we've displayed a video frame, or played an audio frame.
-    int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, mAudioEndTime));
+    int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, AudioEndTime()));
     // FIXME: Bug 1091422 - chained ogg files hit this assertion.
     //MOZ_ASSERT(t >= GetMediaTime());
     if (t > GetMediaTime()) {
       UpdatePlaybackPosition(t);
     }
   }
   // Note we have to update playback position before releasing the monitor.
   // Otherwise, MediaDecoder::AddOutputStream could kick in when we are outside
@@ -3065,21 +3068,35 @@ void MediaDecoderStateMachine::QueueMeta
   AssertCurrentThreadInMonitor();
   TimedMetadata* metadata = new TimedMetadata;
   metadata->mPublishTime = aPublishTime;
   metadata->mInfo = aInfo.forget();
   metadata->mTags = aTags.forget();
   mMetadataManager.QueueMetadata(metadata);
 }
 
+int64_t
+MediaDecoderStateMachine::AudioEndTime() const
+{
+  MOZ_ASSERT(OnTaskQueue());
+  AssertCurrentThreadInMonitor();
+  if (mAudioSink) {
+    return mAudioSink->GetEndTime();
+  }
+  // Don't call this after mAudioSink becomes null since we can't distinguish
+  // "before StartAudioThread" and "after StopAudioThread".
+  MOZ_ASSERT(mAudioCaptured || !mAudioCompleted);
+  return mAudioEndTime;
+}
+
 void MediaDecoderStateMachine::OnAudioEndTimeUpdate(int64_t aAudioEndTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(aAudioEndTime >= mAudioEndTime);
+  MOZ_ASSERT(aAudioEndTime >= AudioEndTime());
   mAudioEndTime = aAudioEndTime;
 }
 
 void MediaDecoderStateMachine::OnPlaybackOffsetUpdate(int64_t aPlaybackOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aPlaybackOffset);
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -1065,16 +1065,17 @@ protected:
   Canonical<int64_t> mCurrentPosition;
 public:
   AbstractCanonical<int64_t>* CanonicalCurrentPosition() { return &mCurrentPosition; }
 protected:
   // The end time of the last audio frame that's been pushed onto the audio
   // hardware in microseconds. This will approximately be the end time of the
   // audio stream, unless another frame is pushed to the hardware.
   int64_t mAudioEndTime;
+  int64_t AudioEndTime() const;
 
   // The end time of the last decoded audio frame. This signifies the end of
   // decoded audio data. Used to check if we are low in decoded data.
   int64_t mDecodedAudioEndTime;
 
   // The presentation end time of the last video frame which has been displayed
   // in microseconds. Accessed from the state machine thread.
   int64_t mVideoFrameEndTime;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -76,34 +76,25 @@ MediaFormatReader::MediaFormatReader(Abs
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
   MOZ_COUNT_DTOR(MediaFormatReader);
-  // shutdown main thread demuxer and track demuxers.
-  if (mAudioTrackDemuxer) {
-    mAudioTrackDemuxer->BreakCycles();
-    mAudioTrackDemuxer = nullptr;
-  }
-  if (mVideoTrackDemuxer) {
-    mVideoTrackDemuxer->BreakCycles();
-    mVideoTrackDemuxer = nullptr;
-  }
-  mMainThreadDemuxer = nullptr;
 }
 
 nsRefPtr<ShutdownPromise>
 MediaFormatReader::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   mDemuxerInitRequest.DisconnectIfExists();
+  mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
   mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
   mSkipRequest.DisconnectIfExists();
 
   if (mAudio.mDecoder) {
     Flush(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
       mAudio.RejectPromise(CANCELED, __func__);
     }
@@ -139,16 +130,27 @@ MediaFormatReader::Shutdown()
     mVideo.mTaskQueue->BeginShutdown();
     mVideo.mTaskQueue->AwaitShutdownAndIdle();
     mVideo.mTaskQueue = nullptr;
   }
   MOZ_ASSERT(mVideo.mPromise.IsEmpty());
 
   mDemuxer = nullptr;
 
+  // shutdown main thread demuxer and track demuxers.
+  if (mAudioTrackDemuxer) {
+    mAudioTrackDemuxer->BreakCycles();
+    mAudioTrackDemuxer = nullptr;
+  }
+  if (mVideoTrackDemuxer) {
+    mVideoTrackDemuxer->BreakCycles();
+    mVideoTrackDemuxer = nullptr;
+  }
+  mMainThreadDemuxer = nullptr;
+
   mPlatform = nullptr;
 
   return MediaDecoderReader::Shutdown();
 }
 
 void
 MediaFormatReader::InitLayersBackendType()
 {
@@ -351,16 +353,21 @@ MediaFormatReader::OnDemuxerInitDone(nsr
   } else {
     mMainThreadDemuxer = mDemuxer->Clone();
   }
   if (!mMainThreadDemuxer) {
     mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
     NS_WARNING("Unable to clone current MediaDataDemuxer");
     return;
   }
+
+  if (!videoActive && !audioActive) {
+    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
+    return;
+  }
   if (videoActive) {
     mVideoTrackDemuxer =
       mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTrackDemuxer);
   }
   if (audioActive) {
     mAudioTrackDemuxer =
       mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
@@ -520,17 +527,18 @@ MediaFormatReader::ShouldSkip(bool aSkip
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
                                     int64_t aTimeThreshold,
                                     bool aForceDecodeAhead)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
   MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests");
-  MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists());
+  MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() ||
+                        mVideo.mTimeThreshold.isSome());
   MOZ_DIAGNOSTIC_ASSERT(!mSkipRequest.Exists(), "called mid-skipping");
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek");
   LOGV("RequestVideoData(%d, %lld)", aSkipToNextKeyframe, aTimeThreshold);
 
   if (!HasVideo()) {
     LOG("called with no video track");
     return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__);
   }
@@ -617,17 +625,18 @@ MediaFormatReader::OnVideoDemuxCompleted
   ScheduleUpdate(TrackInfo::kVideoTrack);
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MediaFormatReader::RequestAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
-  MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists());
+  MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists() ||
+                        mAudio.mTimeThreshold.isSome());
   MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise(), "No duplicate sample requests");
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek");
   LOGV("");
 
   if (!HasAudio()) {
     LOG("called with no audio track");
     return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__);
   }
@@ -714,47 +723,51 @@ MediaFormatReader::NotifyDrainComplete(T
 
 void
 MediaFormatReader::NotifyError(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("%s Decoding error", TrackTypeToStr(aTrack));
   auto& decoder = GetDecoderData(aTrack);
   decoder.mError = true;
+  decoder.mNeedDraining = true;
   ScheduleUpdate(aTrack);
 }
 
 void
 MediaFormatReader::NotifyWaitingForData(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
   decoder.mWaitingForData = true;
+  decoder.mNeedDraining = true;
   ScheduleUpdate(aTrack);
 }
 
 void
 MediaFormatReader::NotifyEndOfStream(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
   decoder.mDemuxEOS = true;
+  decoder.mNeedDraining = true;
   ScheduleUpdate(aTrack);
 }
 
 bool
 MediaFormatReader::NeedInput(DecoderData& aDecoder)
 {
   MOZ_ASSERT(OnTaskQueue());
   // We try to keep a few more compressed samples input than decoded samples
   // have been output, provided the state machine has requested we send it a
   // decoded sample. To account for H.264 streams which may require a longer
   // run of input than we input, decoders fire an "input exhausted" callback,
   // which overrides our "few more samples" threshold.
   return
+    !aDecoder.mDraining &&
     !aDecoder.mError &&
     (aDecoder.HasPromise() || aDecoder.mForceDecodeAhead) &&
     !aDecoder.mDemuxRequest.Exists() &&
     aDecoder.mOutput.IsEmpty() &&
     (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() ||
      aDecoder.mTimeThreshold.isSome() ||
      aDecoder.mForceDecodeAhead ||
      aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput < aDecoder.mDecodeAhead);
@@ -792,17 +805,16 @@ MediaFormatReader::UpdateReceivedNewData
   bool hasLastEnd;
   media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
   // Update our cached TimeRange.
   decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
   if (decoder.mTimeRanges.Length() &&
       (!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) {
     // New data was added after our previous end, we can clear the EOS flag.
     decoder.mDemuxEOS = false;
-    decoder.mDemuxEOSServiced = false;
   }
 
   if (decoder.mError) {
     return false;
   }
   if (decoder.HasWaitingPromise()) {
     MOZ_ASSERT(!decoder.HasPromise());
     LOG("We have new data. Resolving WaitingPromise");
@@ -867,16 +879,27 @@ MediaFormatReader::DecodeDemuxedSamples(
     nsRefPtr<SharedTrackInfo> info = sample->mTrackInfo;
 
     if (info && decoder.mLastStreamSourceID != info->GetID()) {
       if (samplesPending) {
         // Let existing samples complete their decoding. We'll resume later.
         return;
       }
 
+      if (decoder.mNextStreamSourceID.isNothing() ||
+          decoder.mNextStreamSourceID.ref() != info->GetID()) {
+        LOG("%s stream id has changed from:%d to:%d, draining decoder.",
+            TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
+            info->GetID());
+        decoder.mNeedDraining = true;
+        decoder.mNextStreamSourceID = Some(info->GetID());
+        DrainDecoder(aTrack);
+        return;
+      }
+
       LOG("%s stream id has changed from:%d to:%d, recreating decoder.",
           TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
           info->GetID());
       decoder.mInfo = info;
       decoder.mLastStreamSourceID = info->GetID();
       // Flush will clear our array of queued samples. So make a copy now.
       nsTArray<nsRefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
       Flush(aTrack);
@@ -940,16 +963,35 @@ MediaFormatReader::DecodeDemuxedSamples(
     samplesPending = true;
   }
 
   // We have serviced the decoder's request for more data.
   decoder.mInputExhausted = false;
 }
 
 void
+MediaFormatReader::DrainDecoder(TrackType aTrack)
+{
+  MOZ_ASSERT(OnTaskQueue());
+
+  auto& decoder = GetDecoderData(aTrack);
+  if (!decoder.mNeedDraining || decoder.mDraining) {
+    return;
+  }
+  decoder.mNeedDraining = false;
+  if (!decoder.mDecoder) {
+    return;
+  }
+  decoder.mOutputRequested = true;
+  decoder.mDecoder->Drain();
+  decoder.mDraining = true;
+  LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack));
+}
+
+void
 MediaFormatReader::Update(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (mShutdown) {
     return;
   }
 
@@ -960,43 +1002,27 @@ MediaFormatReader::Update(TrackType aTra
   auto& decoder = GetDecoderData(aTrack);
   decoder.mUpdateScheduled = false;
 
   if (UpdateReceivedNewData(aTrack)) {
     LOGV("Nothing more to do");
     return;
   }
 
-  if (decoder.HasPromise()) {
-    // Handle pending requests from the MediaDecoderStateMachine.
-    if (decoder.mError) {
-      LOG("Decoding Error");
-      decoder.RejectPromise(DECODE_ERROR, __func__);
-      return;
-    }
-    if (decoder.mWaitingForData) {
-      LOG("Waiting For Data");
-      decoder.RejectPromise(WAITING_FOR_DATA, __func__);
-    }
-  } else if (decoder.mWaitingForData) {
-    // Nothing more we can do at present.
-    LOGV("Still waiting for data.");
-    return;
-  }
-
   // Record number of frames decoded and parsed. Automatically update the
   // stats counters using the AutoNotifyDecoded stack-based class.
   AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
 
   if (aTrack == TrackInfo::kVideoTrack) {
     uint64_t delta =
       decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames;
     a.mDecoded = static_cast<uint32_t>(delta);
     mLastReportedNumDecodedFrames = decoder.mNumSamplesOutput;
   }
+
   if (decoder.HasPromise()) {
     needOutput = true;
     if (!decoder.mOutput.IsEmpty()) {
       // We have a decoded sample ready to be returned.
       nsRefPtr<MediaData> output = decoder.mOutput[0];
       decoder.mOutput.RemoveElementAt(0);
       decoder.mSizeOfQueue -= 1;
       if (decoder.mTimeThreshold.isNothing() ||
@@ -1005,26 +1031,36 @@ MediaFormatReader::Update(TrackType aTra
         decoder.mTimeThreshold.reset();
       } else {
         LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)",
              media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
              decoder.mTimeThreshold.ref().ToSeconds(),
              output->mKeyframe);
       }
     } else if (decoder.mDrainComplete) {
-      decoder.RejectPromise(END_OF_STREAM, __func__);
       decoder.mDrainComplete = false;
+      decoder.mDraining = false;
+      if (decoder.mError) {
+        LOG("Decoding Error");
+        decoder.RejectPromise(DECODE_ERROR, __func__);
+        return;
+      } else if (decoder.mDemuxEOS) {
+        decoder.RejectPromise(END_OF_STREAM, __func__);
+      } else if (decoder.mWaitingForData) {
+        LOG("Waiting For Data");
+        decoder.RejectPromise(WAITING_FOR_DATA, __func__);
+      }
+    } else if (decoder.mError && !decoder.mDecoder) {
+      decoder.RejectPromise(DECODE_ERROR, __func__);
+      return;
     }
   }
 
-  if (decoder.mDemuxEOS && !decoder.mDemuxEOSServiced) {
-    decoder.mOutputRequested = true;
-    decoder.mDecoder->Drain();
-    decoder.mDemuxEOSServiced = true;
-    LOGV("Requesting decoder to drain");
+  if (decoder.mError || decoder.mDemuxEOS || decoder.mWaitingForData) {
+    DrainDecoder(aTrack);
     return;
   }
 
   if (!NeedInput(decoder)) {
     LOGV("No need for additional input");
     return;
   }
 
@@ -1495,36 +1531,38 @@ MediaFormatReader::NotifyDemuxer(uint32_
 }
 
 void
 MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aLength);
 
-  if (!mInitDone) {
+  if (!mInitDone || mShutdown) {
     return;
   }
 
+  MOZ_ASSERT(mMainThreadDemuxer);
+
   // Queue a task to notify our main thread demuxer.
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArgs<uint32_t, int64_t>(
       mMainThreadDemuxer, &MediaDataDemuxer::NotifyDataArrived,
       aLength, aOffset);
   AbstractThread::MainThread()->Dispatch(task.forget());
 
   NotifyDemuxer(aLength, aOffset);
 }
 
 void
 MediaFormatReader::NotifyDataRemoved()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  if (!mInitDone) {
+  if (!mInitDone || mShutdown) {
     return;
   }
 
   MOZ_ASSERT(mMainThreadDemuxer);
 
   // Queue a task to notify our main thread demuxer.
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethod(
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -113,16 +113,18 @@ private:
   // Handle actions should more data be received.
   // Returns true if no more action is required.
   bool UpdateReceivedNewData(TrackType aTrack);
   // Called when new samples need to be demuxed.
   void RequestDemuxSamples(TrackType aTrack);
   // Decode any pending already demuxed samples.
   void DecodeDemuxedSamples(TrackType aTrack,
                             AbstractMediaDecoder::AutoNotifyDecoded& aA);
+  // Drain the current decoder.
+  void DrainDecoder(TrackType aTrack);
   void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
   void NotifyInputExhausted(TrackType aTrack);
   void NotifyDrainComplete(TrackType aTrack);
   void NotifyError(TrackType aTrack);
   void NotifyWaitingForData(TrackType aTrack);
   void NotifyEndOfStream(TrackType aTrack);
 
   void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
@@ -183,23 +185,24 @@ private:
                 MediaData::Type aType,
                 uint32_t aDecodeAhead)
       : mOwner(aOwner)
       , mType(aType)
       , mDecodeAhead(aDecodeAhead)
       , mForceDecodeAhead(false)
       , mUpdateScheduled(false)
       , mDemuxEOS(false)
-      , mDemuxEOSServiced(false)
       , mWaitingForData(false)
       , mReceivedNewData(false)
       , mDiscontinuity(true)
       , mOutputRequested(false)
       , mInputExhausted(false)
       , mError(false)
+      , mNeedDraining(false)
+      , mDraining(false)
       , mDrainComplete(false)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
       , mSizeOfQueue(0)
       , mLastStreamSourceID(UINT32_MAX)
     {}
 
     MediaFormatReader* mOwner;
@@ -214,17 +217,16 @@ private:
     // Callback that receives output and error notifications from the decoder.
     nsAutoPtr<DecoderCallback> mCallback;
 
     // Only accessed from reader's task queue.
     uint32_t mDecodeAhead;
     bool mForceDecodeAhead;
     bool mUpdateScheduled;
     bool mDemuxEOS;
-    bool mDemuxEOSServiced;
     bool mWaitingForData;
     bool mReceivedNewData;
     bool mDiscontinuity;
 
     // Pending seek.
     MediaPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest;
 
     // Queued demux samples waiting to be decoded.
@@ -236,16 +238,18 @@ private:
       MOZ_ASSERT(mOwner->OnTaskQueue());
       return !mWaitingPromise.IsEmpty();
     }
 
     // MediaDataDecoder handler's variables.
     bool mOutputRequested;
     bool mInputExhausted;
     bool mError;
+    bool mNeedDraining;
+    bool mDraining;
     bool mDrainComplete;
     // If set, all decoded samples prior mTimeThreshold will be dropped.
     // Used for internal seeking when a change of stream is detected.
     Maybe<media::TimeUnit> mTimeThreshold;
 
     // Decoded samples returned my mDecoder awaiting being returned to
     // state machine upon request.
     nsTArray<nsRefPtr<MediaData>> mOutput;
@@ -265,35 +269,38 @@ private:
       mTrackDemuxer->Reset();
     }
 
     void ResetState()
     {
       MOZ_ASSERT(mOwner->OnTaskQueue());
       mForceDecodeAhead = false;
       mDemuxEOS = false;
-      mDemuxEOSServiced = false;
       mWaitingForData = false;
       mReceivedNewData = false;
       mDiscontinuity = true;
       mQueuedSamples.Clear();
       mOutputRequested = false;
       mInputExhausted = false;
+      mNeedDraining = false;
+      mDraining = false;
       mDrainComplete = false;
       mTimeThreshold.reset();
       mOutput.Clear();
       mNumSamplesInput = 0;
       mNumSamplesOutput = 0;
       mSizeOfQueue = 0;
+      mNextStreamSourceID.reset();
     }
 
     // Used by the MDSM for logging purposes.
     Atomic<size_t> mSizeOfQueue;
     // Sample format monitoring.
     uint32_t mLastStreamSourceID;
+    Maybe<uint32_t> mNextStreamSourceID;
     media::TimeIntervals mTimeRanges;
     nsRefPtr<SharedTrackInfo> mInfo;
   };
 
   template<typename PromiseType>
   struct DecoderDataWithPromise : public DecoderData {
     DecoderDataWithPromise(MediaFormatReader* aOwner,
                            MediaData::Type aType,
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -10,18 +10,59 @@
 
 #include "MP4Demuxer.h"
 #include "mp4_demuxer/Index.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mp4_demuxer/MP4Metadata.h"
 #include "mp4_demuxer/ResourceStream.h"
 #include "mp4_demuxer/BufferStream.h"
 
+// Used for telemetry
+#include "mozilla/Telemetry.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
+
 namespace mozilla {
 
+// Returns true if no SPS was found and search for it should continue.
+bool
+AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata)
+{
+  mp4_demuxer::SPSData spsdata;
+  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
+    uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) |
+                          (spsdata.constraint_set1_flag ? (1 << 1) : 0) |
+                          (spsdata.constraint_set2_flag ? (1 << 2) : 0) |
+                          (spsdata.constraint_set3_flag ? (1 << 3) : 0) |
+                          (spsdata.constraint_set4_flag ? (1 << 4) : 0) |
+                          (spsdata.constraint_set5_flag ? (1 << 5) : 0);
+    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG,
+                          constraints);
+
+    // Collect profile_idc values up to 244, otherwise 0 for unknown.
+    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_PROFILE,
+                          spsdata.profile_idc <= 244 ? spsdata.profile_idc : 0);
+
+    // Make sure level_idc represents a value between levels 1 and 5.2,
+    // otherwise collect 0 for unknown level.
+    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_LEVEL,
+                          (spsdata.level_idc >= 10 && spsdata.level_idc <= 52) ?
+                          spsdata.level_idc : 0);
+
+    // max_num_ref_frames should be between 0 and 16, anything larger will
+    // be treated as invalid.
+    Telemetry::Accumulate(Telemetry::VIDEO_H264_SPS_MAX_NUM_REF_FRAMES,
+                          std::min(spsdata.max_num_ref_frames, 17u));
+
+    return false;
+  }
+
+  return true;
+}
+
 MP4Demuxer::MP4Demuxer(MediaResource* aResource)
   : mResource(aResource)
   , mStream(new mp4_demuxer::ResourceStream(aResource))
   , mInitData(new MediaByteBuffer)
 {
 }
 
 nsRefPtr<MP4Demuxer::InitPromise>
@@ -164,16 +205,27 @@ MP4TrackDemuxer::MP4TrackDemuxer(MP4Demu
   }
   mIndex = new mp4_demuxer::Index(indices,
                                   mStream,
                                   mInfo->mTrackId,
                                   mInfo->IsAudio(),
                                   &mMonitor);
   mIterator = MakeUnique<mp4_demuxer::SampleIterator>(mIndex);
   EnsureUpToDateIndex(); // Force update of index
+
+  // Collect telemetry from h264 AVCC SPS.
+  if (mInfo->GetAsVideoInfo() &&
+      (mInfo->mMimeType.EqualsLiteral("video/mp4") ||
+       mInfo->mMimeType.EqualsLiteral("video/avc"))) {
+    mNeedSPSForTelemetry =
+      AccumulateSPSTelemetry(mInfo->GetAsVideoInfo()->mExtraData);
+  } else {
+    // No SPS to be found.
+    mNeedSPSForTelemetry = false;
+  }
 }
 
 UniquePtr<TrackInfo>
 MP4TrackDemuxer::GetInfo() const
 {
   return mInfo->Clone();
 }
 
@@ -263,16 +315,22 @@ MP4TrackDemuxer::Reset()
   SetNextKeyFrameTime();
 }
 
 void
 MP4TrackDemuxer::UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples)
 {
   for (size_t i = 0; i < aSamples.Length(); i++) {
     MediaRawData* sample = aSamples[i];
+    // Collect telemetry from h264 Annex B SPS.
+    if (mNeedSPSForTelemetry && mp4_demuxer::AnnexB::HasSPS(sample)) {
+      nsRefPtr<MediaByteBuffer> extradata =
+        mp4_demuxer::AnnexB::ExtractExtraData(sample);
+      mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata);
+    }
     if (sample->mCrypto.mValid) {
       nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
       writer->mCrypto.mMode = mInfo->mCrypto.mMode;
       writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
       writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
     }
     if (mInfo->GetAsVideoInfo()) {
       sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
--- a/dom/media/fmp4/MP4Demuxer.h
+++ b/dom/media/fmp4/MP4Demuxer.h
@@ -91,16 +91,17 @@ private:
   nsRefPtr<mp4_demuxer::Index> mIndex;
   UniquePtr<mp4_demuxer::SampleIterator> mIterator;
   UniquePtr<TrackInfo> mInfo;
   nsRefPtr<mp4_demuxer::ResourceStream> mStream;
   Maybe<media::TimeUnit> mNextKeyframeTime;
   // Queued samples extracted by the demuxer, but not yet returned.
   nsRefPtr<MediaRawData> mQueuedSample;
   bool mNeedReIndex;
+  bool mNeedSPSForTelemetry;
 
   // We do not actually need a monitor, however MoofParser will assert
   // if a monitor isn't held.
   Monitor mMonitor;
 };
 
 } // namespace mozilla
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -61,50 +61,17 @@ TrackTypeToStr(TrackInfo::TrackType aTra
     return "Audio";
   case TrackInfo::kVideoTrack:
     return "Video";
   default:
     return "Unknown";
   }
 }
 
-bool
-AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata)
-{
-  SPSData spsdata;
-  if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
-   uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) |
-                         (spsdata.constraint_set1_flag ? (1 << 1) : 0) |
-                         (spsdata.constraint_set2_flag ? (1 << 2) : 0) |
-                         (spsdata.constraint_set3_flag ? (1 << 3) : 0) |
-                         (spsdata.constraint_set4_flag ? (1 << 4) : 0) |
-                         (spsdata.constraint_set5_flag ? (1 << 5) : 0);
-    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG,
-                          constraints);
-
-    // Collect profile_idc values up to 244, otherwise 0 for unknown.
-    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_PROFILE,
-                          spsdata.profile_idc <= 244 ? spsdata.profile_idc : 0);
-
-    // Make sure level_idc represents a value between levels 1 and 5.2,
-    // otherwise collect 0 for unknown level.
-    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_LEVEL,
-                          (spsdata.level_idc >= 10 && spsdata.level_idc <= 52) ?
-                          spsdata.level_idc : 0);
-
-    // max_num_ref_frames should be between 0 and 16, anything larger will
-    // be treated as invalid.
-    Telemetry::Accumulate(Telemetry::VIDEO_H264_SPS_MAX_NUM_REF_FRAMES,
-                          std::min(spsdata.max_num_ref_frames, 17u));
-
-    return true;
-  }
-
-  return false;
-}
+extern bool AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata);
 
 // MP4Demuxer wants to do various blocking reads, which cause deadlocks while
 // mDemuxerMonitor is held. This stuff should really be redesigned, but we don't
 // have time for that right now. So in order to get proper synchronization while
 // keeping behavior as similar as possible, we do the following nasty hack:
 //
 // The demuxer has a Stream object with APIs to do both blocking and non-blocking
 // reads. When it does a blocking read, MP4Stream actually redirects it to a non-
--- a/dom/media/gmp/GMPSharedMemManager.cpp
+++ b/dom/media/gmp/GMPSharedMemManager.cpp
@@ -35,19 +35,19 @@ GMPSharedMemManager::MgrAllocShmem(GMPSh
       return true;
     }
   }
 
   // Didn't find a buffer free with enough space; allocate one
   size_t pagesize = ipc::SharedMemory::SystemPageSize();
   aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size
   bool retval = Alloc(aSize, aType, aMem);
-  // The allocator (or NeedsShmem call) should never return less than we ask for...
-  MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize);
   if (retval) {
+    // The allocator (or NeedsShmem call) should never return less than we ask for...
+    MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize);
     mData->mGmpAllocated[aClass]++;
   }
   return retval;
 }
 
 bool
 GMPSharedMemManager::MgrDeallocShmem(GMPSharedMem::GMPMemoryClasses aClass, ipc::Shmem& aMem)
 {
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -141,16 +141,17 @@ void
 MediaSourceDecoder::Shutdown()
 {
   MSE_DEBUG("Shutdown");
   // Detach first so that TrackBuffers are unused on the main thread when
   // shut down on the decode task queue.
   if (mMediaSource) {
     mMediaSource->Detach();
   }
+  mDemuxer = nullptr;
 
   MediaDecoder::Shutdown();
   // Kick WaitForData out of its slumber.
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mon.NotifyAll();
 }
 
 /*static*/
@@ -287,16 +288,17 @@ MediaSourceDecoder::PrepareReaderInitial
   MOZ_ASSERT(mReader);
   GetReader()->PrepareInitialization();
 }
 
 void
 MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString)
 {
   if (mIsUsingFormatReader) {
+    mDemuxer->GetMozDebugReaderData(aString);
     return;
   }
   GetReader()->GetMozDebugReaderData(aString);
 }
 
 #ifdef MOZ_EME
 nsresult
 MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <algorithm>
 #include <limits>
 #include <stdint.h>
 
 #include "MediaSourceDemuxer.h"
 #include "SourceBufferList.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
 typedef TrackInfo::TrackType TrackType;
 using media::TimeUnit;
 using media::TimeIntervals;
 
 // Gap allowed between frames. Due to inaccuracies in determining buffer end
@@ -219,16 +220,51 @@ MediaSourceDemuxer::GetManager(TrackType
 }
 
 MediaSourceDemuxer::~MediaSourceDemuxer()
 {
   mTaskQueue->BeginShutdown();
   mTaskQueue = nullptr;
 }
 
+void
+MediaSourceDemuxer::GetMozDebugReaderData(nsAString& aString)
+{
+  MonitorAutoLock mon(mMonitor);
+  nsAutoCString result;
+  result += nsPrintfCString("Dumping data for demuxer %p:\n", this);
+  if (mAudioTrack) {
+    result += nsPrintfCString("\tDumping Audio Track Buffer(%s): - mLastAudioTime: %f\n"
+                              "\t\tNumSamples:%u Size:%u NextGetSampleIndex:%u NextInsertionIndex:%d\n",
+                              mAudioTrack->mAudioTracks.mInfo->mMimeType.get(),
+                              mAudioTrack->mAudioTracks.mNextSampleTime.ToSeconds(),
+                              mAudioTrack->mAudioTracks.mBuffers[0].Length(),
+                              mAudioTrack->mAudioTracks.mSizeBuffer,
+                              mAudioTrack->mAudioTracks.mNextGetSampleIndex.valueOr(-1),
+                              mAudioTrack->mAudioTracks.mNextInsertionIndex.valueOr(-1));
+
+    result += nsPrintfCString("\t\tBuffered: ranges=%s\n",
+                              DumpTimeRanges(mAudioTrack->SafeBuffered(TrackInfo::kAudioTrack)).get());
+  }
+  if (mVideoTrack) {
+    result += nsPrintfCString("\tDumping Video Track Buffer(%s) - mLastVideoTime: %f\n"
+                              "\t\tNumSamples:%u Size:%u NextGetSampleIndex:%u NextInsertionIndex:%d\n",
+                              mVideoTrack->mVideoTracks.mInfo->mMimeType.get(),
+                              mVideoTrack->mVideoTracks.mNextSampleTime.ToSeconds(),
+                              mVideoTrack->mVideoTracks.mBuffers[0].Length(),
+                              mVideoTrack->mVideoTracks.mSizeBuffer,
+                              mVideoTrack->mVideoTracks.mNextGetSampleIndex.valueOr(-1),
+                              mVideoTrack->mVideoTracks.mNextInsertionIndex.valueOr(-1));
+
+    result += nsPrintfCString("\t\tBuffered: ranges=%s\n",
+                              DumpTimeRanges(mVideoTrack->SafeBuffered(TrackInfo::kVideoTrack)).get());
+  }
+  aString += NS_ConvertUTF8toUTF16(result);
+}
+
 MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
                                                  TrackInfo::TrackType aType,
                                                  TrackBuffersManager* aManager)
   : mParent(aParent)
   , mManager(aManager)
   , mType(aType)
   , mMonitor("MediaSourceTrackDemuxer")
 {
@@ -309,17 +345,20 @@ MediaSourceTrackDemuxer::GetBuffered()
   return mManager->Buffered();
 }
 
 void
 MediaSourceTrackDemuxer::BreakCycles()
 {
   nsRefPtr<MediaSourceTrackDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableFunction([self]() { self->mParent = nullptr; } );
+    NS_NewRunnableFunction([self]() {
+      self->mParent = nullptr;
+      self->mManager = nullptr;
+    } );
   mParent->GetTaskQueue()->Dispatch(task.forget());
 }
 
 nsRefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
 {
   if (aTime.ToMicroseconds() && !mBufferedRanges.Contains(aTime)) {
     // We don't have the data to seek to.
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -50,16 +50,20 @@ public:
   bool ShouldComputeStartTime() const override { return false; }
 
   /* interface for TrackBuffersManager */
   void AttachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   void DetachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   MediaTaskQueue* GetTaskQueue() { return mTaskQueue; }
   void NotifyTimeRangesChanged();
 
+  // Returns a string describing the state of the MediaSource internal
+  // buffered data. Used for debugging purposes.
+  void GetMozDebugReaderData(nsAString& aString);
+
 private:
   ~MediaSourceDemuxer();
   friend class MediaSourceTrackDemuxer;
   // Scan source buffers and update information.
   bool ScanSourceBuffersForContent();
   nsRefPtr<InitPromise> AttemptInit();
   TrackBuffersManager* GetManager(TrackInfo::TrackType aType);
   TrackInfo* GetTrackInfo(TrackInfo::TrackType);
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1708,16 +1708,25 @@ TrackBuffersManager::GetMetadata()
 
 const TimeIntervals&
 TrackBuffersManager::Buffered(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   return GetTracksData(aTrack).mBufferedRanges;
 }
 
+TimeIntervals
+TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const
+{
+  MonitorAutoLock mon(mMonitor);
+  return aTrack == TrackInfo::kVideoTrack
+    ? mVideoBufferedRanges
+    : mAudioBufferedRanges;
+}
+
 const TrackBuffersManager::TrackBuffer&
 TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   return GetTracksData(aTrack).mBuffers.LastElement();
 }
 
 TimeUnit
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -72,16 +72,17 @@ public:
   void SetGroupStartTimestamp(const TimeUnit& aGroupStartTimestamp) override;
   void RestartGroupStartTimestamp() override;
   TimeUnit GroupEndTimestamp() override;
 
   // Interface for MediaSourceDemuxer
   MediaInfo GetMetadata();
   const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack);
   const TimeIntervals& Buffered(TrackInfo::TrackType);
+  TimeIntervals SafeBuffered(TrackInfo::TrackType) const;
   bool IsEnded() const
   {
     return mEnded;
   }
   TimeUnit Seek(TrackInfo::TrackType aTrack, const TimeUnit& aTime);
   uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
                                        const TimeUnit& aTimeThreadshold,
                                        bool& aFound);
@@ -90,16 +91,18 @@ public:
                                            bool& aError);
   TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
 
 #if defined(DEBUG)
   void Dump(const char* aPath) override;
 #endif
 
 private:
+  // for MediaSourceDemuxer::GetMozDebugReaderData
+  friend class MediaSourceDemuxer;
   virtual ~TrackBuffersManager();
   // All following functions run on the taskqueue.
   nsRefPtr<AppendPromise> InitSegmentParserLoop();
   void ScheduleSegmentParserLoop();
   void SegmentParserLoop();
   void AppendIncomingBuffers();
   void InitializationSegmentReceived();
   void ShutdownDemuxers();
--- a/dom/media/test/test_bug495145.html
+++ b/dom/media/test/test_bug495145.html
@@ -10,16 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495145">Mozilla Bug 495145</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+//longer timeout for slow platforms
+if (isSlowPlatform()) {
+  SimpleTest.requestLongerTimeout(1.5);
+}
 
 var manager = new MediaTestManager;
 
 function start(e) {
   e.target.play();
 }
 
 function ended1(e) {
--- a/dom/media/test/test_bug879717.html
+++ b/dom/media/test/test_bug879717.html
@@ -4,16 +4,21 @@
   <title>Test for bug 879717, check that a video element can be drawn into a canvas at various states of playback</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+//longer timeout for slow platforms
+if (isSlowPlatform()) {
+  SimpleTest.requestLongerTimeout(1.5);
+}
+
 var manager = new MediaTestManager;
 
 var canvas = document.createElement('canvas');
 document.body.appendChild(canvas);
 
 var checkDrawImage = function(eventName, videoElement) {
   var exception = null;
   var exceptionName = "nothing";
--- a/dom/media/test/test_loop.html
+++ b/dom/media/test/test_loop.html
@@ -4,16 +4,21 @@
   <title>Test looping support</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+//longer timeout for slow platforms
+if (isSlowPlatform()) {
+  SimpleTest.requestLongerTimeout(1.5);
+}
+
 var manager = new MediaTestManager;
 
 function startTest(test, token) {
   manager.started(token);
   var v = document.createElement('video');
   v.token = token;
   v.src = test.name;
   v.name = test.name;
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -89,18 +89,18 @@ skip-if = toolkit == 'gonk' # B2G emulat
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_capturedVideo.html]
 tags=capturestream
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_webgl.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
-[test_peerConnection_certificates.html]
-skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
+# [test_peerConnection_certificates.html] # bug 1180968
+# skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_close.html]
 [test_peerConnection_closeDuringIce.html]
 [test_peerConnection_errorCallbacks.html]
 [test_peerConnection_iceFailure.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Disabling because of test failures on B2G emulator
 [test_peerConnection_forwarding_basicAudioVideoCombined.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_noTrickleAnswer.html]
--- a/dom/media/webaudio/AlignedTArray.h
+++ b/dom/media/webaudio/AlignedTArray.h
@@ -9,77 +9,55 @@
 
 #include "mozilla/Alignment.h"
 #include "nsTArray.h"
 
 /**
  * E: element type, must be a POD type.
  * N: N bytes alignment for the first element, defaults to 32
   */
-template <typename E, int N, typename Alloc>
-class AlignedTArray_Impl : public nsTArray_Impl<E, Alloc>
+template <typename E, int N = 32>
+class AlignedTArray : public nsTArray_Impl<E, nsTArrayInfallibleAllocator>
 {
   static_assert((N & (N-1)) == 0, "N must be power of 2");
-  typedef nsTArray_Impl<E, Alloc>                    base_type;
+  typedef nsTArray_Impl<E, nsTArrayInfallibleAllocator> base_type;
 public:
   typedef E                                          elem_type;
   typedef typename base_type::size_type              size_type;
   typedef typename base_type::index_type             index_type;
 
-  AlignedTArray_Impl() {}
-  explicit AlignedTArray_Impl(size_type capacity) : base_type(capacity+sExtra) {}
+  AlignedTArray() {}
+  explicit AlignedTArray(size_type capacity) : base_type(capacity + sExtra) {}
   elem_type* Elements() { return getAligned(base_type::Elements()); }
   const elem_type* Elements() const { return getAligned(base_type::Elements()); }
   elem_type& operator[](index_type i) { return Elements()[i];}
   const elem_type& operator[](index_type i) const { return Elements()[i]; }
 
-  typename Alloc::ResultType SetLength(size_type newLen) {
-    return base_type::SetLength(newLen + sExtra);
+  void SetLength(size_type newLen)
+  {
+    base_type::SetLength(newLen + sExtra);
   }
+
+  MOZ_WARN_UNUSED_RESULT
+  bool SetLength(size_type newLen, const mozilla::fallible_t&)
+  {
+    return base_type::SetLength(newLen + sExtra, mozilla::fallible);
+  }
+
   size_type Length() const {
     return base_type::Length() <= sExtra ? 0 : base_type::Length() - sExtra;
   }
 
 private:
-  AlignedTArray_Impl(const AlignedTArray_Impl& other) = delete;
-  void operator=(const AlignedTArray_Impl& other) = delete;
+  AlignedTArray(const AlignedTArray& other) = delete;
+  void operator=(const AlignedTArray& other) = delete;
 
   static const size_type sPadding = N <= MOZ_ALIGNOF(E) ? 0 : N - MOZ_ALIGNOF(E);
   static const size_type sExtra = (sPadding + sizeof(E) - 1) / sizeof(E);
 
   template <typename U>
   static U* getAligned(U* p)
   {
     return reinterpret_cast<U*>(((uintptr_t)p + N - 1) & ~(N-1));
   }
 };
 
-template <typename E, int N=32>
-class AlignedTArray : public AlignedTArray_Impl<E, N, nsTArrayInfallibleAllocator>
-{
-public:
-  typedef AlignedTArray_Impl<E, N, nsTArrayInfallibleAllocator> base_type;
-  typedef AlignedTArray<E, N>                                   self_type;
-  typedef typename base_type::size_type                         size_type;
-
-  AlignedTArray() {}
-  explicit AlignedTArray(size_type capacity) : base_type(capacity) {}
-private:
-  AlignedTArray(const AlignedTArray& other) = delete;
-  void operator=(const AlignedTArray& other) = delete;
-};
-
-template <typename E, int N=32>
-class AlignedFallibleTArray : public AlignedTArray_Impl<E, N, nsTArrayFallibleAllocator>
-{
-public:
-  typedef AlignedTArray_Impl<E, N, nsTArrayFallibleAllocator> base_type;
-  typedef AlignedFallibleTArray<E, N>                         self_type;
-  typedef typename base_type::size_type                       size_type;
-
-  AlignedFallibleTArray() {}
-  explicit AlignedFallibleTArray(size_type capacity) : base_type(capacity) {}
-private:
-  AlignedFallibleTArray(const AlignedFallibleTArray& other) = delete;
-  void operator=(const AlignedFallibleTArray& other) = delete;
-};
-
 #endif // AlignedTArray_h__
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -246,21 +246,21 @@ AnalyserNode::GetByteTimeDomainData(cons
     buffer[i] = static_cast<unsigned char>(scaled);
   }
 }
 
 bool
 AnalyserNode::FFTAnalysis()
 {
   float* inputBuffer;
-  AlignedFallibleTArray<float> tmpBuffer;
+  AlignedTArray<float> tmpBuffer;
   if (mWriteIndex == 0) {
     inputBuffer = mBuffer.Elements();
   } else {
-    if (!tmpBuffer.SetLength(FftSize())) {
+    if (!tmpBuffer.SetLength(FftSize(), fallible)) {
       return false;
     }
     inputBuffer = tmpBuffer.Elements();
     memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
     memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
   }
 
   ApplyBlackmanWindow(inputBuffer, FftSize());
@@ -296,23 +296,23 @@ AnalyserNode::ApplyBlackmanWindow(float*
   }
 }
 
 bool
 AnalyserNode::AllocateBuffer()
 {
   bool result = true;
   if (mBuffer.Length() != FftSize()) {
-    if (!mBuffer.SetLength(FftSize())) {
+    if (!mBuffer.SetLength(FftSize(), fallible)) {
       return false;
     }
     memset(mBuffer.Elements(), 0, sizeof(float) * FftSize());
     mWriteIndex = 0;
 
-    if (!mOutputBuffer.SetLength(FrequencyBinCount())) {
+    if (!mOutputBuffer.SetLength(FrequencyBinCount(), fallible)) {
       return false;
     }
     memset(mOutputBuffer.Elements(), 0, sizeof(float) * FrequencyBinCount());
   }
   return result;
 }
 
 void
--- a/dom/media/webaudio/AnalyserNode.h
+++ b/dom/media/webaudio/AnalyserNode.h
@@ -73,17 +73,17 @@ private:
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
 
 private:
   FFTBlock mAnalysisBlock;
   double mMinDecibels;
   double mMaxDecibels;
   double mSmoothingTimeConstant;
   uint32_t mWriteIndex;
-  AlignedFallibleTArray<float> mBuffer;
-  AlignedFallibleTArray<float> mOutputBuffer;
+  AlignedTArray<float> mBuffer;
+  AlignedTArray<float> mOutputBuffer;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -130,17 +130,17 @@ mozilla::plugins::SetupBridge(uint32_t a
         return true;
     }
     *rv = PPluginModule::Bridge(aContentParent, chromeParent);
     if (NS_FAILED(*rv)) {
 #if defined(MOZ_CRASHREPORTER)
         // We are going to abort due to the failure, lets note the cause
         // in the report for diagnosing.
         nsAutoCString error;
-        error.AppendPrintf("%X", *rv);
+        error.AppendPrintf("%X %d", *rv, chromeParent->GetIPCChannel()->GetChannelState__TotallyRacy());
         CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BridgePluginError"), error);
 #endif
       return false;
     }
     return true;
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
--- a/dom/speakermanager/SpeakerManagerService.cpp
+++ b/dom/speakermanager/SpeakerManagerService.cpp
@@ -174,35 +174,47 @@ SpeakerManagerService::Observe(nsISuppor
         }
         if (mOrgSpeakerStatus) {
           TurnOnSpeaker(!mOrgSpeakerStatus);
           mOrgSpeakerStatus = false;
         }
     } else {
       NS_WARNING("ipc:content-shutdown message without childID property");
     }
+  } else if (!strcmp(aTopic, "xpcom-will-shutdown")) {
+    // Note that we need to do this before xpcom-shutdown, since the
+    // AudioChannelService cannot be used past that point.
+    nsRefPtr<AudioChannelService> audioChannelService =
+      AudioChannelService::GetOrCreate();
+    audioChannelService->UnregisterSpeakerManager(this);
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->RemoveObserver(this, "ipc:content-shutdown");
+      obs->RemoveObserver(this, "xpcom-will-shutdown");
+    }
+
+    Shutdown();
   }
   return NS_OK;
 }
 
 SpeakerManagerService::SpeakerManagerService()
   : mOrgSpeakerStatus(false),
     mVisible(false)
 {
   MOZ_COUNT_CTOR(SpeakerManagerService);
   if (XRE_IsParentProcess()) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
+      obs->AddObserver(this, "xpcom-will-shutdown", false);
     }
   }
   nsRefPtr<AudioChannelService> audioChannelService =
     AudioChannelService::GetOrCreate();
   audioChannelService->RegisterSpeakerManager(this);
 }
 
 SpeakerManagerService::~SpeakerManagerService()
 {
   MOZ_COUNT_DTOR(SpeakerManagerService);
-  nsRefPtr<AudioChannelService> audioChannelService =
-    AudioChannelService::GetOrCreate();
-  audioChannelService->UnregisterSpeakerManager(this);
 }
--- a/dom/speakermanager/SpeakerManagerService.h
+++ b/dom/speakermanager/SpeakerManagerService.h
@@ -41,30 +41,31 @@ public:
   void RegisterSpeakerManager(SpeakerManager* aSpeakerManager)
   {
     mRegisteredSpeakerManagers.AppendElement(aSpeakerManager);
   }
   void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager)
   {
     mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager);
   }
-  /**
-   * Shutdown the singleton.
-   */
-  static void Shutdown();
 
 protected:
   SpeakerManagerService();
 
   virtual ~SpeakerManagerService();
   // Notify to UA if device speaker status changed
   virtual void Notify();
 
   void TurnOnSpeaker(bool aEnable);
 
+  /**
+   * Shutdown the singleton.
+   */
+  static void Shutdown();
+
   nsTArray<nsRefPtr<SpeakerManager> > mRegisteredSpeakerManagers;
   // Set for remember all the child speaker status
   nsCheapSet<nsUint64HashKey> mSpeakerStatusSet;
   // The Speaker status assign by UA
   bool mOrgSpeakerStatus;
 
   bool mVisible;
   // This is needed for IPC communication between
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1724,16 +1724,17 @@ ScriptExecutorRunnable::WorkerRun(JSCont
 
     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
 
     if (NS_FAILED(loadInfo.mLoadResult)) {
       scriptloader::ReportLoadError(aCx, loadInfo.mURL, loadInfo.mLoadResult,
                                     false);
+      aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
       return true;
     }
 
     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
 
     JS::CompileOptions options(aCx);
     options.setFileAndLine(filename.get(), 1)
            .setNoScriptRval(true);
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -159,18 +159,23 @@ ServiceWorkerContainer::GetController()
 {
   if (!mControllerWorker) {
     nsresult rv;
     nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
     if (!swm) {
       return nullptr;
     }
 
+    // TODO: What should we do here if the ServiceWorker script fails to load?
+    //       In theory the DOM ServiceWorker object can exist without the worker
+    //       thread running, but it seems our design does not expect that.
     nsCOMPtr<nsISupports> serviceWorker;
-    rv = swm->GetDocumentController(GetOwner(), getter_AddRefs(serviceWorker));
+    rv = swm->GetDocumentController(GetOwner(),
+                                    nullptr, // aLoadFailedRunnable
+                                    getter_AddRefs(serviceWorker));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     mControllerWorker =
       static_cast<workers::ServiceWorker*>(serviceWorker.get());
   }
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1057,47 +1057,63 @@ public:
     }
 
     nsAutoString cacheName;
     // We have to create a ServiceWorker here simply to ensure there are no
     // errors. Ideally we should just pass this worker on to ContinueInstall.
     MOZ_ASSERT(!data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
     data->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true);
 
+    // Call FailScopeUpdate on main thread if the SW script load fails below.
+    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs
+      <StorensRefPtrPassByPtr<ServiceWorkerManager>, nsCString>
+      (this, &ServiceWorkerRegisterJob::FailScopeUpdate, swm, scopeKey);
+
     MOZ_ASSERT(!mUpdateAndInstallInfo);
     mUpdateAndInstallInfo =
       new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec,
                             aNewCacheName);
     nsRefPtr<ServiceWorker> serviceWorker;
     rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
                                   mUpdateAndInstallInfo,
+                                  failRunnable,
                                   getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
-      return Fail(NS_ERROR_DOM_ABORT_ERR);
+      return FailScopeUpdate(swm, scopeKey);
     }
 
     nsRefPtr<ServiceWorkerJob> upcasted = this;
     nsMainThreadPtrHandle<nsISupports> handle(
         new nsMainThreadPtrHolder<nsISupports>(upcasted));
 
     nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
       new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
     nsRefPtr<CheckWorkerEvaluationAndContinueUpdateWorkerRunnable> r =
       new CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(serviceWorkerHandle, handle);
     AutoJSAPI jsapi;
     jsapi.Init();
     bool ok = r->Dispatch(jsapi.cx());
     if (NS_WARN_IF(!ok)) {
-      data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
-      return Fail(NS_ERROR_DOM_ABORT_ERR);
+      return FailScopeUpdate(swm, scopeKey);
     }
   }
 
+  void
+  FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aSwm);
+    ServiceWorkerManager::RegistrationDataPerPrincipal* data;
+    if (aSwm->mRegistrationInfos.Get(aScopeKey, &data)) {
+      data->mSetOfScopesBeingUpdated.Remove(aScopeKey);
+    }
+    Fail(NS_ERROR_DOM_ABORT_ERR);
+  }
+
   // Public so our error handling code can use it.
   // Callers MUST hold a strong ref before calling this!
   void
   Fail(const ErrorEventInit& aError)
   {
     MOZ_ASSERT(mCallback);
     nsRefPtr<ServiceWorkerUpdateFinishCallback> callback = mCallback.forget();
     // With cancellation support, we may only be running with one reference
@@ -1163,19 +1179,25 @@ public:
     nsCOMPtr<nsIRunnable> upr =
       NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
         swm,
         &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
         mRegistration);
 
     NS_DispatchToMainThread(upr);
 
+    // Call ContinueAfterInstallEvent(false, false) on main thread if the SW
+    // script fails to load.
+    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool, bool>
+      (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false, false);
+
     nsRefPtr<ServiceWorker> serviceWorker;
     rv = swm->CreateServiceWorker(mRegistration->mPrincipal,
                                   mRegistration->mInstallingWorker,
+                                  failRunnable,
                                   getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       ContinueAfterInstallEvent(false /* aSuccess */, false /* aActivateImmediately */);
       return;
     }
 
     nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
@@ -1829,28 +1851,30 @@ ServiceWorkerRegistrationInfo::Activate(
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
     NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
                                                                 &ServiceWorkerManager::FireControllerChange,
                                                                 this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
+  nsCOMPtr<nsIRunnable> failRunnable =
+    NS_NewRunnableMethodWithArg<bool>(this,
+                                      &ServiceWorkerRegistrationInfo::FinishActivate,
+                                      false /* success */);
+
   MOZ_ASSERT(mActiveWorker);
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv =
     swm->CreateServiceWorker(mPrincipal,
                              mActiveWorker,
+                             failRunnable,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    nsCOMPtr<nsIRunnable> r =
-      NS_NewRunnableMethodWithArg<bool>(this,
-                                        &ServiceWorkerRegistrationInfo::FinishActivate,
-                                        false /* success */);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(failRunnable)));
     return;
   }
 
   nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
     new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
 
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
@@ -2222,17 +2246,17 @@ ServiceWorkerManager::SendPushEvent(cons
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker =
-    CreateServiceWorkerForScope(attrs, aScope);
+    CreateServiceWorkerForScope(attrs, aScope, nullptr /* failure runnable */);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
 
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendPushEventRunnable> r =
@@ -2257,17 +2281,17 @@ ServiceWorkerManager::SendPushSubscripti
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker =
-    CreateServiceWorkerForScope(attrs, aScope);
+    CreateServiceWorkerForScope(attrs, aScope, nullptr /* fail runnable */);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendPushSubscriptionChangeEventRunnable> r =
     new SendPushSubscriptionChangeEventRunnable(
@@ -2382,17 +2406,18 @@ ServiceWorkerManager::SendNotificationCl
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(attrs, aScope);
+  nsRefPtr<ServiceWorker> serviceWorker =
+    CreateServiceWorkerForScope(attrs, aScope, nullptr);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendNotificationClickEventRunnable> r =
     new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(),
@@ -2520,17 +2545,18 @@ ServiceWorkerManager::CheckReadyPromise(
     return true;
   }
 
   return false;
 }
 
 already_AddRefed<ServiceWorker>
 ServiceWorkerManager::CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
-                                                  const nsACString& aScope)
+                                                  const nsACString& aScope,
+                                                  nsIRunnable* aLoadFailedRunnable)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_FAILED(rv)) {
     return nullptr;
   }
@@ -2542,16 +2568,17 @@ ServiceWorkerManager::CreateServiceWorke
 
   if (!registration->mActiveWorker) {
     return nullptr;
   }
 
   nsRefPtr<ServiceWorker> sw;
   rv = CreateServiceWorker(registration->mPrincipal,
                            registration->mActiveWorker,
+                           aLoadFailedRunnable,
                            getter_AddRefs(sw));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return sw.forget();
 }
@@ -2822,16 +2849,17 @@ ServiceWorkerRegistrationInfo::FinishAct
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
                                                    ServiceWorkerInfo* aInfo,
+                                                   nsIRunnable* aLoadFailedRunnable,
                                                    ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aInfo);
 
   AutoJSAPI jsapi;
   jsapi.Init(aWindow);
@@ -2846,16 +2874,17 @@ ServiceWorkerManager::CreateServiceWorke
                                            &loadInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(!aInfo->CacheName().IsEmpty());
   loadInfo.mServiceWorkerCacheName = aInfo->CacheName();
   loadInfo.mServiceWorkerID = aInfo->ID();
+  loadInfo.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
 
   RuntimeService* rs = RuntimeService::GetOrCreateService();
   if (!rs) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<SharedWorker> sharedWorker;
   rv = rs->CreateSharedWorkerForServiceWorkerFromLoadInfo(cx, &loadInfo,
@@ -3413,19 +3442,23 @@ ServiceWorkerManager::GetServiceWorkerFo
   } else {
     MOZ_CRASH("Invalid worker type");
   }
 
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
+  // TODO: How should we handle async failure of SW load for getters?  In
+  //       theory getting a handle to the DOM ServiceWorker object should not
+  //       require the worker thread to actually be running.
   nsRefPtr<ServiceWorker> serviceWorker;
   rv = CreateServiceWorkerForWindow(window,
                                     info,
+                                    nullptr, // load failed runnable
                                     getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker->SetState(info->State());
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
@@ -3685,21 +3718,27 @@ ServiceWorkerManager::DispatchFetchEvent
   nsCOMPtr<nsISupports> serviceWorker;
 
   bool isNavigation = false;
   aRv = aChannel->GetIsNavigation(&isNavigation);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
+  // if the ServiceWorker script fails to load for some reason, just resume
+  // the original channel.
+  nsCOMPtr<nsIRunnable> failRunnable =
+    NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
+
   nsAutoPtr<ServiceWorkerClientInfo> clientInfo;
 
   if (!isNavigation) {
     MOZ_ASSERT(aDoc);
-    aRv = GetDocumentController(aDoc->GetInnerWindow(), getter_AddRefs(serviceWorker));
+    aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
+                                getter_AddRefs(serviceWorker));
     clientInfo = new ServiceWorkerClientInfo(aDoc, aDoc->GetWindow());
   } else {
     nsCOMPtr<nsIChannel> internalChannel;
     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
@@ -3718,16 +3757,17 @@ ServiceWorkerManager::DispatchFetchEvent
     }
 
     // This should only happen if IsAvailable() returned true.
     MOZ_ASSERT(registration->mActiveWorker);
 
     nsRefPtr<ServiceWorker> sw;
     aRv = CreateServiceWorker(registration->mPrincipal,
                               registration->mActiveWorker,
+                              failRunnable,
                               getter_AddRefs(sw));
     serviceWorker = sw.forget();
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
@@ -3801,17 +3841,19 @@ ServiceWorkerManager::GetDocumentRegistr
   return NS_OK;
 }
 
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
 NS_IMETHODIMP
-ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports** aServiceWorker)
+ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow,
+                                            nsIRunnable* aLoadFailedRunnable,
+                                            nsISupports** aServiceWorker)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   MOZ_ASSERT(window);
   if (!window || !window->GetExtantDoc()) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
@@ -3820,16 +3862,17 @@ ServiceWorkerManager::GetDocumentControl
   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker;
   rv = CreateServiceWorkerForWindow(window,
                                     registration->mActiveWorker,
+                                    aLoadFailedRunnable,
                                     getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
@@ -3862,16 +3905,17 @@ ServiceWorkerManager::GetActive(nsIDOMWi
   return GetServiceWorkerForScope(aWindow, aScope,
                                   WhichServiceWorker::ACTIVE_WORKER,
                                   aServiceWorker);
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
                                           ServiceWorkerInfo* aInfo,
+                                          nsIRunnable* aLoadFailedRunnable,
                                           ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
 
   WorkerLoadInfo info;
   nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aInfo->ScriptSpec(),
                           nullptr, nullptr);
@@ -3916,16 +3960,18 @@ ServiceWorkerManager::CreateServiceWorke
   // NOTE: this defaults the SW load context to:
   //  - private browsing = false
   //  - content = true
   //  - use remote tabs = false
   // Alternatively we could persist the original load group values and use
   // them here.
   WorkerPrivate::OverrideLoadInfoLoadGroup(info);
 
+  info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
+
   RuntimeService* rs = RuntimeService::GetOrCreateService();
   if (!rs) {
     return NS_ERROR_FAILURE;
   }
 
   AutoJSAPI jsapi;
   jsapi.Init();
   nsRefPtr<SharedWorker> sharedWorker;
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -415,32 +415,35 @@ private:
   Update(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   GetDocumentRegistration(nsIDocument* aDoc, ServiceWorkerRegistrationInfo** aRegistrationInfo);
 
   NS_IMETHOD
   CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
                                ServiceWorkerInfo* aInfo,
+                               nsIRunnable* aLoadFailedRunnable,
                                ServiceWorker** aServiceWorker);
 
   NS_IMETHOD
   CreateServiceWorker(nsIPrincipal* aPrincipal,
                       ServiceWorkerInfo* aInfo,
+                      nsIRunnable* aLoadFailedRunnable,
                       ServiceWorker** aServiceWorker);
 
   NS_IMETHODIMP
   GetServiceWorkerForScope(nsIDOMWindow* aWindow,
                            const nsAString& aScope,
                            WhichServiceWorker aWhichWorker,
                            nsISupports** aServiceWorker);
 
   already_AddRefed<ServiceWorker>
   CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes,
-                              const nsACString& aScope);
+                              const nsACString& aScope,
+                              nsIRunnable* aLoadFailedRunnable);
 
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2379,16 +2379,19 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo
   aOther.mCSP.swap(mCSP);
 
   MOZ_ASSERT(!mChannel);
   aOther.mChannel.swap(mChannel);
 
   MOZ_ASSERT(!mLoadGroup);
   aOther.mLoadGroup.swap(mLoadGroup);
 
+  MOZ_ASSERT(!mLoadFailedAsyncRunnable);
+  aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable);
+
   MOZ_ASSERT(!mInterfaceRequestor);
   aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
 
   MOZ_ASSERT(!mPrincipalInfo);
   mPrincipalInfo = aOther.mPrincipalInfo.forget();
 
   mDomain = aOther.mDomain;
   mServiceWorkerCacheName = aOther.mServiceWorkerCacheName;
@@ -3413,28 +3416,29 @@ WorkerPrivateParent<Derived>::ForgetOver
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
                                       nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(!mMainThreadObjectsForgotten);
 
-  static const uint32_t kDoomedCount = 9;
+  static const uint32_t kDoomedCount = 10;
 
   aDoomed.SetCapacity(kDoomedCount);
 
   SwapToISupportsArray(mLoadInfo.mWindow, aDoomed);
   SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed);
   SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed);
   SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed);
   SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed);
   SwapToISupportsArray(mLoadInfo.mChannel, aDoomed);
   SwapToISupportsArray(mLoadInfo.mCSP, aDoomed);
   SwapToISupportsArray(mLoadInfo.mLoadGroup, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mLoadFailedAsyncRunnable, aDoomed);
   SwapToISupportsArray(mLoadInfo.mInterfaceRequestor, aDoomed);
   // Before adding anything here update kDoomedCount above!
 
   MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
 
   mMainThreadObjectsForgotten = true;
 }
 
@@ -5462,16 +5466,29 @@ WorkerPrivate::RunBeforeNextEvent(nsIRun
       return false;
     }
   }
 
   return true;
 }
 
 void
+WorkerPrivate::MaybeDispatchLoadFailedRunnable()
+{
+  AssertIsOnWorkerThread();
+
+  nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
+  if (!runnable) {
+    return;
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget())));
+}
+
+void
 WorkerPrivate::InitializeGCTimers()
 {
   AssertIsOnWorkerThread();
 
   // We need a timer for GC. The basic plan is to run a non-shrinking GC
   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
   // run a shrinking GC. If the worker receives more messages then the short
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -793,16 +793,22 @@ public:
   GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers);
 
   void
   CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
 
   void
   UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
 
+  already_AddRefed<nsIRunnable>
+  StealLoadFailedAsyncRunnable()
+  {
+    return mLoadInfo.mLoadFailedAsyncRunnable.forget();
+  }
+
   IMPL_EVENT_HANDLER(message)
   IMPL_EVENT_HANDLER(error)
 
 #ifdef DEBUG
   void
   AssertIsOnParentThread() const;
 
   void
@@ -951,16 +957,19 @@ class WorkerPrivate : public WorkerPriva
   nsCOMPtr<nsITimer> mGCTimer;
   nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
   nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
 
   nsRefPtr<MemoryReporter> mMemoryReporter;
 
   nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts;
 
+  // fired on the main thread if the worker script fails to load
+  nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
+
   TimeStamp mKillTime;
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
   bool mFrozen;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mCloseHandlerStarted;
@@ -1362,16 +1371,19 @@ public:
     return mWorkerScriptExecutedSuccessfully;
   }
 
   // Just like nsIAppShell::RunBeforeNextEvent. May only be called on the worker
   // thread.
   bool
   RunBeforeNextEvent(nsIRunnable* aRunnable);
 
+  void
+  MaybeDispatchLoadFailedRunnable();
+
 private:
   WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
                 WorkerLoadInfo& aLoadInfo);
 
   bool
   MayContinueRunning()
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -34,16 +34,17 @@
 class nsIContentSecurityPolicy;
 class nsIScriptContext;
 class nsIGlobalObject;
 class nsPIDOMWindow;
 class nsIPrincipal;
 class nsILoadGroup;
 class nsITabChild;
 class nsIChannel;
+class nsIRunnable;
 class nsIURI;
 
 namespace mozilla {
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 
 namespace dom {
@@ -216,16 +217,22 @@ struct WorkerLoadInfo
   nsCOMPtr<nsIURI> mResolvedScriptURI;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
+  // mLoadFailedAsyncRunnable will execute on main thread if script loading
+  // fails during script loading.  If script loading is never started due to
+  // a synchronous error, then the runnable is never executed.  The runnable
+  // is guaranteed to be released on the main thread.
+  nsCOMPtr<nsIRunnable> mLoadFailedAsyncRunnable;
+
   class InterfaceRequestor final : public nsIInterfaceRequestor
   {
     NS_DECL_ISUPPORTS
 
   public:
     InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
     void MaybeAddTabChild(nsILoadGroup* aLoadGroup);
     NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) override;
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -201,17 +201,19 @@ nsEditingSession::DisableJSAndPlugins(ns
   mDisabledJSAndPlugins = true;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditingSession::RestoreJSAndPlugins(nsIDOMWindow *aWindow)
 {
-  NS_ENSURE_TRUE(mDisabledJSAndPlugins, NS_OK);
+  if (!mDisabledJSAndPlugins) {
+    return NS_OK;
+  }
 
   mDisabledJSAndPlugins = false;
 
   nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1450,67 +1450,41 @@ nsPermissionManager::GetPermissionHashKe
       return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch);
     }
   }
 
   // No entry, really...
   return nullptr;
 }
 
-// helper struct for passing arguments into hash enumeration callback.
-struct nsGetEnumeratorData
-{
-  nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray,
-                      const nsTArray<nsCString> *aTypes,
-                      int64_t aSince = 0)
-   : array(aArray)
-   , types(aTypes)
-   , since(aSince) {}
-
-  nsCOMArray<nsIPermission> *array;
-  const nsTArray<nsCString> *types;
-  int64_t since;
-};
-
-static PLDHashOperator
-AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
-{
-  nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
-
-  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
-
-    // given how "default" permissions work and the possibility of them being
-    // overridden with UNKNOWN_ACTION, we might see this value here - but we
-    // do *not* want to return them via the enumerator.
-    if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
-      continue;
-    }
-
-    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
-                                          entry->GetKey()->mAppId,
-                                          entry->GetKey()->mIsInBrowserElement,
-                                          data->types->ElementAt(permEntry.mType),
-                                          permEntry.mPermission,
-                                          permEntry.mExpireType,
-                                          permEntry.mExpireTime);
-
-    data->array->AppendObject(perm);
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
 {
   // roll an nsCOMArray of all our permissions, then hand out an enumerator
   nsCOMArray<nsIPermission> array;
-  nsGetEnumeratorData data(&array, &mTypeArray);
+
+  for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
+    PermissionHashKey* entry = iter.Get();
+    for (const auto& permEntry : entry->GetPermissions()) {
+      // Given how "default" permissions work and the possibility of them being
+      // overridden with UNKNOWN_ACTION, we might see this value here - but we
+      // do *not* want to return them via the enumerator.
+      if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
+        continue;
+      }
 
-  mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
+      array.AppendObject(
+        new nsPermission(entry->GetKey()->mHost,
+                         entry->GetKey()->mAppId,
+                         entry->GetKey()->mIsInBrowserElement,
+                         mTypeArray.ElementAt(permEntry.mType),
+                         permEntry.mPermission,
+                         permEntry.mExpireType,
+                         permEntry.mExpireTime));
+    }
+  }
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
@@ -1524,52 +1498,39 @@ NS_IMETHODIMP nsPermissionManager::Obser
   else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
     // the profile has already changed; init the db from the new location
     InitDB(false);
   }
 
   return NS_OK;
 }
 
-static PLDHashOperator
-AddPermissionsModifiedSinceToList(
-  nsPermissionManager::PermissionHashKey* entry, void* arg)
-{
-  nsGetEnumeratorData* data = static_cast<nsGetEnumeratorData *>(arg);
-
-  for (size_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    const nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
-
-    if (data->since > permEntry.mModificationTime) {
-      continue;
-    }
-
-    nsPermission* perm = new nsPermission(entry->GetKey()->mHost,
-                                          entry->GetKey()->mAppId,
-                                          entry->GetKey()->mIsInBrowserElement,
-                                          data->types->ElementAt(permEntry.mType),
-                                          permEntry.mPermission,
-                                          permEntry.mExpireType,
-                                          permEntry.mExpireTime);
-
-    data->array->AppendObject(perm);
-  }
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
-  // roll an nsCOMArray of all our permissions, then hand out an enumerator
   nsCOMArray<nsIPermission> array;
-  nsGetEnumeratorData data(&array, &mTypeArray, aModificationTime);
+  for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
+    PermissionHashKey* entry = iter.Get();
+    for (const auto& permEntry : entry->GetPermissions()) {
+      if (aModificationTime > permEntry.mModificationTime) {
+        continue;
+      }
 
-  mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data);
+      array.AppendObject(
+        new nsPermission(entry->GetKey()->mHost,
+                         entry->GetKey()->mAppId,
+                         entry->GetKey()->mIsInBrowserElement,
+                         mTypeArray.ElementAt(permEntry.mType),
+                         permEntry.mPermission,
+                         permEntry.mExpireType,
+                         permEntry.mExpireTime));
+    }
+  }
 
   for (int32_t i = 0; i<array.Count(); ++i) {
     nsAutoCString host;
     bool isInBrowserElement = false;
     nsAutoCString type;
     uint32_t appId = 0;
 
     array[i]->GetHost(host);
@@ -1594,54 +1555,29 @@ nsPermissionManager::RemoveAllModifiedSi
       nsPermissionManager::eWriteToDB);
   }
   // now re-import any defaults as they may now be required if we just deleted
   // an override.
   ImportDefaults();
   return NS_OK;
 }
 
-PLDHashOperator
-nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg)
-{
-  GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg);
-
-  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
-
-    if (entry->GetKey()->mAppId != data->appId ||
-        (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) {
-      continue;
-    }
-
-    data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost,
-                                                    entry->GetKey()->mAppId,
-                                                    entry->GetKey()->mIsInBrowserElement,
-                                                    gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
-                                                    permEntry.mPermission,
-                                                    permEntry.mExpireType,
-                                                    permEntry.mExpireTime));
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 NS_IMETHODIMP
 nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
 {
   ENSURE_NOT_CHILD_PROCESS;
   NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID);
 
   // We begin by removing all the permissions from the DB.
   // After clearing the DB, we call AddInternal() to make sure that all
   // processes are aware of this change and the representation of the DB in
   // memory is updated.
-  // We have to get all permissions associated with an application and then
-  // remove those because doing so in EnumerateEntries() would fail because
-  // we might happen to actually delete entries from the list.
+  // We have to get all permissions associated with an application first
+  // because removing entries from the permissions table while iterating over
+  // it is dangerous.
 
   nsAutoCString sql;
   sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId=");
   sql.AppendInt(aAppId);
 
   if (aBrowserOnly) {
     sql.AppendLiteral(" AND isInBrowserElement=1");
   }
@@ -1649,27 +1585,44 @@ nsPermissionManager::RemovePermissionsFo
   nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
   nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIStoragePendingStatement> pending;
   rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  GetPermissionsForAppStruct data(aAppId, aBrowserOnly);
-  mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data);
+  nsCOMArray<nsIPermission> permissions;
+  for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
+    PermissionHashKey* entry = iter.Get();
+    if (entry->GetKey()->mAppId != aAppId ||
+        (aBrowserOnly && !entry->GetKey()->mIsInBrowserElement)) {
+      continue;
+    }
 
-  for (int32_t i=0; i<data.permissions.Count(); ++i) {
+    for (const auto& permEntry : entry->GetPermissions()) {
+      permissions.AppendObject(
+        new nsPermission(entry->GetKey()->mHost,
+                         entry->GetKey()->mAppId,
+                         entry->GetKey()->mIsInBrowserElement,
+                         mTypeArray.ElementAt(permEntry.mType),
+                         permEntry.mPermission,
+                         permEntry.mExpireType,
+                         permEntry.mExpireTime));
+    }
+  }
+
+  for (int32_t i = 0; i < permissions.Count(); ++i) {
     nsAutoCString host;
     bool isInBrowserElement;
     nsAutoCString type;
 
-    data.permissions[i]->GetHost(host);
-    data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement);
-    data.permissions[i]->GetType(type);
+    permissions[i]->GetHost(host);
+    permissions[i]->GetIsInBrowserElement(&isInBrowserElement);
+    permissions[i]->GetType(type);
 
     nsCOMPtr<nsIPrincipal> principal;
     if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement,
                                getter_AddRefs(principal)))) {
       NS_ERROR("GetPrincipal() failed!");
       continue;
     }
 
@@ -1682,73 +1635,69 @@ nsPermissionManager::RemovePermissionsFo
                 0,
                 nsPermissionManager::eNotify,
                 nsPermissionManager::eNoDBOperation);
   }
 
   return NS_OK;
 }
 
-PLDHashOperator
-nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
-  nsPermissionManager::PermissionHashKey* entry, void* arg)
-{
-  uint32_t* appId = static_cast<uint32_t*>(arg);
-
-  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    if (entry->GetKey()->mAppId != *appId) {
-      continue;
-    }
-
-    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
-    if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) {
-      continue;
-    }
-
-    if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) {
-      PermissionEntry oldPermissionEntry = entry->GetPermissions()[i];
-
-      entry->GetPermissions().RemoveElementAt(i);
-
-      gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
-                                                        entry->GetKey()->mAppId,
-                                                        entry->GetKey()->mIsInBrowserElement,
-                                                        gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType),
-                                                        oldPermissionEntry.mPermission,
-                                                        oldPermissionEntry.mExpireType,
-                                                        oldPermissionEntry.mExpireTime,
-                                                        MOZ_UTF16("deleted"));
-      --i;
-      continue;
-    }
-
-    permEntry.mPermission = permEntry.mNonSessionPermission;
-    permEntry.mExpireType = permEntry.mNonSessionExpireType;
-    permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
-
-    gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
-                                                      entry->GetKey()->mAppId,
-                                                      entry->GetKey()->mIsInBrowserElement,
-                                                      gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
-                                                      permEntry.mPermission,
-                                                      permEntry.mExpireType,
-                                                      permEntry.mExpireTime,
-                                                      MOZ_UTF16("changed"));
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
-  if (aAppId != nsIScriptSecurityManager::NO_APP_ID) {
-    mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId);
+  if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
+    return NS_OK;
+  }
+
+  for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
+    PermissionHashKey* entry = iter.Get();
+    if (entry->GetKey()->mAppId != aAppId) {
+      continue;
+    }
+
+    for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
+      PermissionEntry& permEntry = entry->GetPermissions()[i];
+      if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) {
+        continue;
+      }
+
+      if (permEntry.mNonSessionExpireType ==
+            nsIPermissionManager::EXPIRE_SESSION) {
+        PermissionEntry oldPermEntry = entry->GetPermissions()[i];
+
+        entry->GetPermissions().RemoveElementAt(i);
+
+        NotifyObserversWithPermission(entry->GetKey()->mHost,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
+                                      mTypeArray.ElementAt(oldPermEntry.mType),
+                                      oldPermEntry.mPermission,
+                                      oldPermEntry.mExpireType,
+                                      oldPermEntry.mExpireTime,
+                                      MOZ_UTF16("deleted"));
+
+        --i;
+        continue;
+      }
+
+      permEntry.mPermission = permEntry.mNonSessionPermission;
+      permEntry.mExpireType = permEntry.mNonSessionExpireType;
+      permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
+
+      NotifyObserversWithPermission(entry->GetKey()->mHost,
+                                    entry->GetKey()->mAppId,
+                                    entry->GetKey()->mIsInBrowserElement,
+                                    mTypeArray.ElementAt(permEntry.mType),
+                                    permEntry.mPermission,
+                                    permEntry.mExpireType,
+                                    permEntry.mExpireTime,
+                                    MOZ_UTF16("changed"));
+    }
   }
 
   return NS_OK;
 }
 
 //*****************************************************************************
 //*** nsPermissionManager private methods
 //*****************************************************************************
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -268,51 +268,16 @@ private:
                        int64_t aExpireTime,
                        int64_t aModificationTime,
                        uint32_t aAppId,
                        bool aIsInBrowserElement);
 
   nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId);
 
   /**
-   * This struct has to be passed as an argument to GetPermissionsForApp.
-   * |appId| and |browserOnly| have to be defined.
-   * |permissions| will be filed with permissions that are related to the app.
-   * If |browserOnly| is true, only permissions related to a browserElement will
-   * be in |permissions|.
-   */
-  struct GetPermissionsForAppStruct {
-    uint32_t                  appId;
-    bool                      browserOnly;
-    nsCOMArray<nsIPermission> permissions;
-
-    GetPermissionsForAppStruct() = delete;
-    GetPermissionsForAppStruct(uint32_t aAppId, bool aBrowserOnly)
-      : appId(aAppId)
-      , browserOnly(aBrowserOnly)
-    {}
-  };
-
-  /**
-   * This method will return the list of all permissions that are related to a
-   * specific app.
-   * @param arg has to be an instance of GetPermissionsForAppStruct.
-   */
-  static PLDHashOperator
-  GetPermissionsForApp(PermissionHashKey* entry, void* arg);
-
-  /**
-   * This method restores an app's permissions when its session ends.
-   */
-  static PLDHashOperator
-  RemoveExpiredPermissionsForAppEnumerator(PermissionHashKey* entry,
-                                           void* nonused);
-
-
-  /**
    * This method removes all permissions modified after the specified time.
    */
   nsresult
   RemoveAllModifiedSince(int64_t aModificationTime);
 
   /**
    * Retrieve permissions from chrome process.
    */
--- a/extensions/spellcheck/src/mozPersonalDictionary.cpp
+++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp
@@ -237,26 +237,16 @@ void mozPersonalDictionary::SyncLoadInte
         if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true;
       }
       mDictionaryTable.PutEntry(word.get());
     }
   } while(!done);
   mDirty = false;
 }
 
-// A little helper function to add the key to the list.
-// This is not threadsafe, and only safe if the consumer does not 
-// modify the list.
-static PLDHashOperator
-AddHostToStringArray(nsUnicharPtrHashKey *aEntry, void *aArg)
-{
-  static_cast<nsTArray<nsString>*>(aArg)->AppendElement(nsDependentString(aEntry->GetKey()));
-  return PL_DHASH_NEXT;
-}
-
 /* void Save (); */
 NS_IMETHODIMP mozPersonalDictionary::Save()
 {
   nsCOMPtr<nsIFile> theFile;
   nsresult res;
 
   WaitForLoad();
   if(!mDirty) return NS_OK;
@@ -272,17 +262,19 @@ NS_IMETHODIMP mozPersonalDictionary::Sav
   NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664);
 
   // get a buffered output stream 4096 bytes big, to optimize writes
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096);
   if (NS_FAILED(res)) return res;
 
   nsTArray<nsString> array(mDictionaryTable.Count());
-  mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array);
+  for (auto iter = mDictionaryTable.Iter(); !iter.Done(); iter.Next()) {
+    array.AppendElement(nsDependentString(iter.Get()->GetKey()));
+  }
 
   uint32_t bytesWritten;
   nsAutoCString utf8Key;
   for (uint32_t i = 0; i < array.Length(); ++i ) {
     CopyUTF16toUTF8(array[i], utf8Key);
 
     bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten);
     bufferedOutputStream->Write("\n", 1, &bytesWritten);
@@ -302,20 +294,19 @@ NS_IMETHODIMP mozPersonalDictionary::Sav
 NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords)
 {
   NS_ENSURE_ARG_POINTER(aWords);
   *aWords = nullptr;
 
   WaitForLoad();
 
   nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count());
-  if (!array)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  mDictionaryTable.EnumerateEntries(AddHostToStringArray, array);
+  for (auto iter = mDictionaryTable.Iter(); !iter.Done(); iter.Next()) {
+    array->AppendElement(nsDependentString(iter.Get()->GetKey()));
+  }
 
   array->Sort();
 
   return NS_NewAdoptingStringEnumerator(aWords, array);
 }
 
 /* boolean Check (in wstring word, in wstring language); */
 NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t *aLanguage, bool *aResult)
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -861,17 +861,17 @@ ClientTiledLayerBuffer::GetSurfaceDescri
       tileDesc = PlaceholderTileDescriptor();
     } else {
       tileDesc = tile.GetTileDescriptor();
     }
     tiles.AppendElement(tileDesc);
     // Reset the update rect
     tile.mUpdateRect = IntRect();
   }
-  return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
+  return SurfaceDescriptorTiles(mValidRegion,
                                 tiles,
                                 mTiles.mFirst.x, mTiles.mFirst.y,
                                 mTiles.mSize.width, mTiles.mSize.height,
                                 mResolution, mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
 
 void
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -104,38 +104,23 @@ TiledContentHost::UseTiledLayerBuffer(IS
   }
   return true;
 }
 
 void
 UseTileTexture(CompositableTextureHostRef& aTexture,
                CompositableTextureSourceRef& aTextureSource,
                const IntRect& aUpdateRect,
-               TextureHost* aNewTexture,
                Compositor* aCompositor)
 {
-  MOZ_ASSERT(aNewTexture);
-  if (!aNewTexture) {
+  MOZ_ASSERT(aTexture);
+  if (!aTexture) {
     return;
   }
 
-  if (aTexture) {
-    aTexture->SetCompositor(aCompositor);
-    aNewTexture->SetCompositor(aCompositor);
-
-    if (aTexture->GetFormat() != aNewTexture->GetFormat()) {
-      // Only reuse textures if their format match the new texture's.
-      aTextureSource = nullptr;
-      aTexture = nullptr;
-    }
-  }
-
-  aTexture = aNewTexture;
-
-
   if (aCompositor) {
     aTexture->SetCompositor(aCompositor);
   }
 
   if (!aUpdateRect.IsEmpty()) {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     aTexture->Updated(nullptr);
 #else
@@ -170,16 +155,93 @@ GetCopyOnWriteLock(const TileLock& ipcLo
       // The corresponding AddRef is in TiledClient::GetTileDescriptor
       sharedLock.get()->Release();
     }
   }
   aTile.mSharedLock = sharedLock;
   return true;
 }
 
+void
+TiledLayerBufferComposite::MarkTilesForUnlock()
+{
+  // Tiles without an internal buffer will have internal locks
+  // held by the gpu driver until the previous draw operation has finished.
+  // We don't know when that will be exactly, so wait until we start the
+  // next composite before unlocking.
+  for (TileHost& tile : mRetainedTiles) {
+    // Tile with an internal buffer get unlocked as soon as we've uploaded,
+    // so won't have a lock at this point.
+    if (tile.mTextureHost && tile.mSharedLock) {
+      mDelayedUnlocks.AppendElement(tile.mSharedLock);
+      tile.mSharedLock = nullptr;
+    }
+  }
+}
+
+class TextureSourceRecycler
+{
+public:
+  explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet)
+    : mTiles(Move(aTileSet))
+    , mFirstPossibility(0)
+  {}
+
+  // Attempts to recycle a texture source that is already bound to the
+  // texture host for aTile.
+  void RecycleTextureSourceForTile(TileHost& aTile) {
+    for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+      // Skip over existing tiles without a retained texture source
+      // and make sure we don't iterate them in the future.
+      if (!mTiles[i].mTextureSource) {
+        if (i == mFirstPossibility) {
+          mFirstPossibility++;
+        }
+        continue;
+      }
+
+      // If this tile matches, then copy across the retained texture source (if
+      // any).
+      if (aTile.mTextureHost == mTiles[i].mTextureHost) {
+        aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+        if (aTile.mTextureHostOnWhite) {
+          aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+        }
+        break;
+      }
+    }
+  }
+
+  // Attempts to recycle any texture source to avoid needing to allocate
+  // a new one.
+  void RecycleTextureSource(TileHost& aTile) {
+    for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+      if (!mTiles[i].mTextureSource) {
+        if (i == mFirstPossibility) {
+          mFirstPossibility++;
+        }
+        continue;
+      }
+
+      if (mTiles[i].mTextureSource &&
+          mTiles[i].mTextureHost->GetFormat() == aTile.mTextureHost->GetFormat()) {
+        aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+        if (aTile.mTextureHostOnWhite) {
+          aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+        }
+        break;
+      }
+    }
+  }
+
+protected:
+  nsTArray<TileHost> mTiles;
+  size_t mFirstPossibility;
+};
+
 bool
 TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
                                     Compositor* aCompositor,
                                     ISurfaceAllocator* aAllocator)
 {
   if (mResolution != aTiles.resolution()) {
     Clear();
   }
@@ -190,179 +252,138 @@ TiledLayerBufferComposite::UseTiles(cons
   }
 
   if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
     // There are divisions by mResolution so this protects the compositor process
     // against malicious content processes and fuzzing.
     return false;
   }
 
-  TilesPlacement oldTiles = mTiles;
   TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
                           aTiles.retainedWidth(), aTiles.retainedHeight());
 
   const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
 
-  nsTArray<TileHost> oldRetainedTiles;
-  mRetainedTiles.SwapElements(oldRetainedTiles);
+  // Step 1, unlock all the old tiles that haven't been unlocked yet. Any tiles that
+  // exist in both the old and new sets will have been locked again by content, so this
+  // doesn't result in the surface being writeable again.
+  MarkTilesForUnlock();
+
+  TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles));
   mRetainedTiles.SetLength(tileDescriptors.Length());
 
-  // Step 1, we need to unlock tiles that don't have an internal buffer after the
-  // next frame where they are replaced.
-  // Since we are about to replace the tiles' textures, we need to keep their locks
-  // somewhere (in mPreviousSharedLock) until we composite the layer.
-  for (size_t i = 0; i < oldRetainedTiles.Length(); ++i) {
-    TileHost& tile = oldRetainedTiles[i];
-    // It can happen that we still have a previous lock at this point,
-    // if we changed a tile's front buffer (causing mSharedLock to
-    // go into mPreviousSharedLock, and then did not composite that tile until
-    // the next transaction, either because the tile is offscreen or because the
-    // two transactions happened with no composition in between (over-production).
-    tile.ReadUnlockPrevious();
-
-    if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) {
-      MOZ_ASSERT(tile.mSharedLock);
-      const TileIntPoint tilePosition = oldTiles.TilePosition(i);
-      if (newTiles.HasTile(tilePosition)) {
-        // This tile still exist in the new buffer
-        tile.mPreviousSharedLock = tile.mSharedLock;
-        tile.mSharedLock = nullptr;
-      } else {
-        // This tile does not exist anymore in the new buffer because the size
-        // changed.
-        tile.ReadUnlock();
-      }
-    }
-
-    // By now we should not have anything in mSharedLock.
-    MOZ_ASSERT(!tile.mSharedLock);
-  }
-
-  // Step 2, move the tiles in mRetainedTiles at places that correspond to where
-  // they should be with the new retained with and height rather than the
-  // old one.
+  // Step 2, deserialize the incoming set of tiles into mRetainedTiles, and attempt
+  // to recycle the TextureSource for any repeated tiles.
+  //
+  // Since we don't have any retained 'tile' object, we have to search for instances
+  // of the same TextureHost in the old tile set. The cost of binding a TextureHost
+  // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really
+  // high, so we avoid this whenever possible.
   for (size_t i = 0; i < tileDescriptors.Length(); i++) {
-    const TileIntPoint tilePosition = newTiles.TilePosition(i);
-    // First, get the already existing tiles to the right place in the array,
-    // and use placeholders where there was no tiles.
-    if (!oldTiles.HasTile(tilePosition)) {
-      mRetainedTiles[i] = GetPlaceholderTile();
-    } else {
-      mRetainedTiles[i] = oldRetainedTiles[oldTiles.TileIndex(tilePosition)];
-      // If we hit this assertion it means we probably mixed something up in the
-      // logic that tries to reuse tiles on the compositor side. It is most likely
-      // benign, but we are missing some fast paths so let's try to make it not happen.
-      MOZ_ASSERT(tilePosition.x == mRetainedTiles[i].x &&
-                 tilePosition.y == mRetainedTiles[i].y);
-    }
-  }
-
-  // It is important to remove the duplicated reference to tiles before calling
-  // TextureHost::PrepareTextureSource, etc. because depending on the textures
-  // ref counts we may or may not get some of the fast paths.
-  oldRetainedTiles.Clear();
-
-  // Step 3, handle the texture updates and release the copy-on-write locks.
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     const TileDescriptor& tileDesc = tileDescriptors[i];
 
     TileHost& tile = mRetainedTiles[i];
 
-    switch (tileDesc.type()) {
-      case TileDescriptor::TTexturedTileDescriptor: {
-        const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+    if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+      NS_WARN_IF_FALSE(tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor,
+                       "Unrecognised tile descriptor type");
+      continue;
+    }
 
-        const TileLock& ipcLock = texturedDesc.sharedLock();
-        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
-          return false;
-        }
+    const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
 
-        RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost(
-          texturedDesc.textureParent()
-        );
+    const TileLock& ipcLock = texturedDesc.sharedLock();
+    if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
+      return false;
+    }
 
-        RefPtr<TextureHost> textureOnWhite = nullptr;
-        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(
-            texturedDesc.textureOnWhite().get_PTextureParent()
-          );
-        }
+    tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent());
+
+    if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+      tile.mTextureHostOnWhite =
+        TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent());
+    }
 
-        UseTileTexture(tile.mTextureHost,
-                       tile.mTextureSource,
-                       texturedDesc.updateRect(),
-                       textureHost,
-                       aCompositor);
+    tile.mTilePosition = newTiles.TilePosition(i);
+
+    // If this same tile texture existed in the old tile set then this will move the texture
+    // source into our new tile.
+    oldRetainedTiles.RecycleTextureSourceForTile(tile);
+  }
 
-        if (textureOnWhite) {
-          UseTileTexture(tile.mTextureHostOnWhite,
-                         tile.mTextureSourceOnWhite,
-                         texturedDesc.updateRect(),
-                         textureOnWhite,
-                         aCompositor);
-        } else {
-          // We could still have component alpha textures from a previous frame.
-          tile.mTextureSourceOnWhite = nullptr;
-          tile.mTextureHostOnWhite = nullptr;
-        }
+  // Step 3, attempt to recycle unused texture sources from the old tile set into new tiles.
+  //
+  // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way
+  // to ensure that any implicit locking on the old gralloc image is released.
+  for (TileHost& tile : mRetainedTiles) {
+    if (!tile.mTextureHost || tile.mTextureSource) {
+      continue;
+    }
+    oldRetainedTiles.RecycleTextureSource(tile);
+  }
 
-        if (textureHost->HasInternalBuffer()) {
-          // Now that we did the texture upload (in UseTileTexture), we can release
-          // the lock.
-          tile.ReadUnlock();
-        }
+  // Step 4, handle the texture uploads, texture source binding and release the
+  // copy-on-write locks for textures with an internal buffer.
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    TileHost& tile = mRetainedTiles[i];
+    if (!tile.mTextureHost) {
+      continue;
+    }
 
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-      case TileDescriptor::TPlaceholderTileDescriptor: {
+    const TileDescriptor& tileDesc = tileDescriptors[i];
+    const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+    UseTileTexture(tile.mTextureHost,
+                   tile.mTextureSource,
+                   texturedDesc.updateRect(),
+                   aCompositor);
 
-        if (tile.mTextureHost) {
-          tile.mTextureHost->UnbindTextureSource();
-          tile.mTextureSource = nullptr;
-        }
-        if (tile.mTextureHostOnWhite) {
-          tile.mTextureHostOnWhite->UnbindTextureSource();
-          tile.mTextureSourceOnWhite = nullptr;
-        }
-        // we may have a previous lock, and are about to loose our reference to it.
-        // It is okay to unlock it because we just destroyed the texture source.
-        tile.ReadUnlockPrevious();
-        tile = GetPlaceholderTile();
+    if (tile.mTextureHostOnWhite) {
+      UseTileTexture(tile.mTextureHostOnWhite,
+                     tile.mTextureSourceOnWhite,
+                     texturedDesc.updateRect(),
+                     aCompositor);
+    }
 
-        break;
-      }
+    if (tile.mTextureHost->HasInternalBuffer()) {
+      // Now that we did the texture upload (in UseTileTexture), we can release
+      // the lock.
+      tile.ReadUnlock();
     }
-    TileIntPoint tilePosition = newTiles.TilePosition(i);
-    tile.x = tilePosition.x;
-    tile.y = tilePosition.y;
   }
 
   mTiles = newTiles;
   mValidRegion = aTiles.validRegion();
   mResolution = aTiles.resolution();
   mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
                                              aTiles.frameYResolution());
 
   return true;
 }
 
 void
+TiledLayerBufferComposite::ProcessDelayedUnlocks()
+{
+  for (gfxSharedReadLock* lock : mDelayedUnlocks) {
+    lock->ReadUnlock();
+  }
+  mDelayedUnlocks.Clear();
+}
+
+void
 TiledLayerBufferComposite::Clear()
 {
   for (TileHost& tile : mRetainedTiles) {
     tile.ReadUnlock();
-    tile.ReadUnlockPrevious();
   }
   mRetainedTiles.Clear();
+  ProcessDelayedUnlocks();
   mTiles.mFirst = TileIntPoint();
   mTiles.mSize = TileIntSize();
   mValidRegion = nsIntRegion();
-  mPaintedRegion = nsIntRegion();
   mResolution = 1.0;
 }
 
 void
 TiledContentHost::Composite(LayerComposite* aLayer,
                             EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
@@ -411,16 +432,18 @@ TiledContentHost::Composite(LayerComposi
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
+  mLowPrecisionTiledBuffer.ProcessDelayedUnlocks();
+  mTiledBuffer.ProcessDelayedUnlocks();
 }
 
 
 void
 TiledContentHost::RenderTile(TileHost& aTile,
                              EffectChain& aEffectChain,
                              float aOpacity,
                              const gfx::Matrix4x4& aTransform,
@@ -474,17 +497,16 @@ TiledContentHost::RenderTile(TileHost& a
     mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform, aVisibleRect);
   }
   DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
   if (aTile.mTextureHostOnWhite) {
     flags |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
-  aTile.ReadUnlockPrevious();
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const gfxRGBA* aBackgroundColor,
                                     EffectChain& aEffectChain,
                                     float aOpacity,
                                     const gfx::Filter& aFilter,
@@ -553,17 +575,17 @@ TiledContentHost::RenderLayerBuffer(Tile
   for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
     TileHost& tile = aLayerBuffer.GetTile(i);
     if (tile.IsPlaceholderTile()) {
       continue;
     }
 
     TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i);
     // A sanity check that catches a lot of mistakes.
-    MOZ_ASSERT(tilePosition.x == tile.x && tilePosition.y == tile.y);
+    MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y);
 
     IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition);
     nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
     tileDrawRegion.AndWith(compositeRegion);
 
     if (tileDrawRegion.IsEmpty()) {
       continue;
     }
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -47,57 +47,49 @@ struct EffectChain;
 
 class TileHost {
 public:
   // Constructs a placeholder TileHost. See the comments above
   // TiledLayerBuffer for more information on what this is used for;
   // essentially, this is a sentinel used to represent an invalid or blank
   // tile.
   TileHost()
-  : x(-1)
-  , y(-1)
   {}
 
   // Constructs a TileHost from a gfxSharedReadLock and TextureHost.
   TileHost(gfxSharedReadLock* aSharedLock,
                TextureHost* aTextureHost,
                TextureHost* aTextureHostOnWhite,
                TextureSource* aSource,
                TextureSource* aSourceOnWhite)
     : mSharedLock(aSharedLock)
     , mTextureHost(aTextureHost)
     , mTextureHostOnWhite(aTextureHostOnWhite)
     , mTextureSource(aSource)
     , mTextureSourceOnWhite(aSourceOnWhite)
-    , x(-1)
-    , y(-1)
   {}
 
   TileHost(const TileHost& o) {
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
-    mPreviousSharedLock = o.mPreviousSharedLock;
-    x = o.x;
-    y = o.y;
+    mTilePosition = o.mTilePosition;
   }
   TileHost& operator=(const TileHost& o) {
     if (this == &o) {
       return *this;
     }
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
-    mPreviousSharedLock = o.mPreviousSharedLock;
-    x = o.x;
-    y = o.y;
+    mTilePosition = o.mTilePosition;
     return *this;
   }
 
   bool operator== (const TileHost& o) const {
     return mTextureHost == o.mTextureHost;
   }
   bool operator!= (const TileHost& o) const {
     return mTextureHost != o.mTextureHost;
@@ -107,41 +99,32 @@ public:
 
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
       mSharedLock = nullptr;
     }
   }
 
-  void ReadUnlockPrevious() {
-    if (mPreviousSharedLock) {
-      mPreviousSharedLock->ReadUnlock();
-      mPreviousSharedLock = nullptr;
-    }
-  }
-
   void Dump(std::stringstream& aStream) {
     aStream << "TileHost(...)"; // fill in as needed
   }
 
   void DumpTexture(std::stringstream& aStream) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableHost::DumpTextureHost(aStream, mTextureHost);
   }
 
   RefPtr<gfxSharedReadLock> mSharedLock;
-  RefPtr<gfxSharedReadLock> mPreviousSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
   // This is not strictly necessary but makes debugging whole lot easier.
-  int x;
-  int y;
+  TileIntPoint mTilePosition;
 };
 
 class TiledLayerBufferComposite
   : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
 {
   friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
 
 public:
@@ -149,30 +132,35 @@ public:
   ~TiledLayerBufferComposite();
 
   bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
                 Compositor* aCompositor,
                 ISurfaceAllocator* aAllocator);
 
   void Clear();
 
+  void MarkTilesForUnlock();
+  void ProcessDelayedUnlocks();
+
   TileHost GetPlaceholderTile() const { return TileHost(); }
 
   // Stores the absolute resolution of the containing frame, calculated
   // by the sum of the resolutions of all parent layers' FrameMetrics.
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
   void SetCompositor(Compositor* aCompositor);
 
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
+
   CSSToParentLayerScale2D mFrameResolution;
+  nsTArray<RefPtr<gfxSharedReadLock>> mDelayedUnlocks;
 };
 
 /**
  * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
  * flakes, we have a unique update process. All the textures that back the
  * tiles are added in the usual way, but Updated is called on the host side
  * in response to a message that describes the transaction for every tile.
  * Composition happens in the normal way.
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -327,17 +327,16 @@ struct PlaceholderTileDescriptor {
 
 union TileDescriptor {
   TexturedTileDescriptor;
   PlaceholderTileDescriptor;
 };
 
 struct SurfaceDescriptorTiles {
   nsIntRegion validRegion;
-  nsIntRegion paintedRegion;
   TileDescriptor[] tiles;
   int         firstTileX;
   int         firstTileY;
   int         retainedWidth;
   int         retainedHeight;
   float       resolution;
   float       frameXResolution;
   float       frameYResolution;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -225,16 +225,17 @@ private:
 
   DECL_GFX_PREF(Once, "gfx.direct2d.disabled",                 Direct2DDisabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled",            Direct2DForceEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.direct2d.use1_1",                   Direct2DUse1_1, bool, false);
   DECL_GFX_PREF(Live, "gfx.draw-color-bars",                   CompositorDrawColorBars, bool, false);
   // This should be set to values in the DriverInitStatus enumeration found in
   // DriverInitCrashDetection.h.
   DECL_GFX_PREF(Live, "gfx.driver-init.status",                DriverInitStatus, int32_t, 0);
+  DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.enabled", DirectWriteFontRenderingEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels",     GrallocFenceWithReadPixels, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.enabled",                LayerScopeEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.port",                   LayerScopePort, int32_t, 23456);
   // Note that        "gfx.logging.level" is defined in Logging.h
   DECL_GFX_PREF(Once, "gfx.logging.crash.length",              GfxLoggingCrashLength, uint32_t, 6);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Once, "gfx.touch.resample",                    TouchResampling, bool, false);
 
old mode 100644
new mode 100755
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -432,16 +432,105 @@ gfxWindowsPlatform::CanUseHardwareVideoD
 {
     if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) {
         return false;
     }
     return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
 }
 
 void
+gfxWindowsPlatform::InitD2DSupport()
+{
+#ifdef CAIRO_HAS_D2D_SURFACE
+  bool d2dBlocked = false;
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+  if (gfxInfo) {
+    int32_t status;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        d2dBlocked = true;
+      }
+    }
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        d2dBlocked = true;
+      }
+    }
+  }
+
+  // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then
+  // we don't attempt to use D2D.
+  if ((d2dBlocked || gfxPrefs::LayersPreferD3D9()) && !gfxPrefs::Direct2DForceEnabled()) {
+    return;
+  }
+
+  // Do not ever try to use D2D if it's explicitly disabled or if we're not
+  // using DWrite fonts.
+  if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) {
+    return;
+  }
+
+  ID3D11Device* device = GetD3D11Device();
+  if (IsVistaOrLater() &&
+      !InSafeMode() &&
+      device &&
+      mDoesD3D11TextureSharingWork)
+  {
+    VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled());
+    if (mD3D10Device && GetD3D11Device()) {
+      mRenderMode = RENDER_DIRECT2D;
+      mUseDirectWrite = true;
+    }
+  } else {
+    mD3D10Device = nullptr;
+  }
+#endif
+}
+
+void
+gfxWindowsPlatform::InitDWriteSupport()
+{
+#ifdef CAIRO_HAS_DWRITE_FONT
+  // Enable when it's preffed on -and- we're using Vista or higher. Or when
+  // we're going to use D2D.
+  if (mDWriteFactory || (!mUseDirectWrite || !IsVistaOrLater())) {
+    return;
+  }
+
+  mozilla::ScopedGfxFeatureReporter reporter("DWrite");
+  decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
+      GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory");
+
+  if (!createDWriteFactory) {
+    return;
+  }
+
+  // I need a direct pointer to be able to cast to IUnknown**, I also need to
+  // remember to release this because the nsRefPtr will AddRef it.
+  IDWriteFactory *factory;
+  HRESULT hr = createDWriteFactory(
+      DWRITE_FACTORY_TYPE_SHARED,
+      __uuidof(IDWriteFactory),
+      reinterpret_cast<IUnknown**>(&factory));
+
+  if (SUCCEEDED(hr) && factory) {
+    mDWriteFactory = factory;
+    factory->Release();
+    hr = mDWriteFactory->CreateTextAnalyzer(getter_AddRefs(mDWriteAnalyzer));
+  }
+
+  SetupClearTypeParams();
+
+  if (hr == S_OK) {
+    reporter.SetSuccessful();
+  }
+#endif
+}
+
+void
 gfxWindowsPlatform::UpdateRenderMode()
 {
 /* Pick the default render mode for
  * desktop.
  */
     bool didReset = false;
     DeviceResetReason resetReason = DeviceResetReason::OK;
     if (DidRenderingDeviceReset(&resetReason)) {
@@ -456,102 +545,20 @@ gfxWindowsPlatform::UpdateRenderMode()
       imgLoader::Singleton()->ClearCache(true);
       imgLoader::Singleton()->ClearCache(false);
       Factory::SetDirect3D11Device(nullptr);
 
       didReset = true;
     }
 
     mRenderMode = RENDER_GDI;
-
-    bool isVistaOrHigher = IsVistaOrLater();
-
-    mUseDirectWrite = Preferences::GetBool("gfx.font_rendering.directwrite.enabled", false);
-
-#ifdef CAIRO_HAS_D2D_SURFACE
-    bool d2dDisabled = false;
-    bool d2dForceEnabled = false;
-    bool d2dBlocked = false;
-
-    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-    if (gfxInfo) {
-        int32_t status;
-        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
-            if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-                d2dBlocked = true;
-            }
-        }
-        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
-            if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-                d2dBlocked = true;
-            }
-        }
-    }
-
-    // These will only be evaluated once, and any subsequent changes to
-    // the preferences will be ignored until restart.
-    d2dDisabled = gfxPrefs::Direct2DDisabled();
-    d2dForceEnabled = gfxPrefs::Direct2DForceEnabled();
-
-    bool tryD2D = d2dForceEnabled || (!d2dBlocked && !gfxPrefs::LayersPreferD3D9());
-
-    // Do not ever try if d2d is explicitly disabled,
-    // or if we're not using DWrite fonts.
-    if (d2dDisabled || mUsingGDIFonts) {
-        tryD2D = false;
-    }
-
-    ID3D11Device *device = GetD3D11Device();
-    if (isVistaOrHigher && !InSafeMode() && tryD2D && device &&
-        mDoesD3D11TextureSharingWork) {
+    mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled();
 
-        VerifyD2DDevice(d2dForceEnabled);
-        if (mD3D10Device && GetD3D11Device()) {
-            mRenderMode = RENDER_DIRECT2D;
-            mUseDirectWrite = true;
-        }
-    } else {
-        mD3D10Device = nullptr;
-    }
-#endif
-
-#ifdef CAIRO_HAS_DWRITE_FONT
-    // Enable when it's preffed on -and- we're using Vista or higher. Or when
-    // we're going to use D2D.
-    if (!mDWriteFactory && (mUseDirectWrite && isVistaOrHigher)) {
-        mozilla::ScopedGfxFeatureReporter reporter("DWrite");
-        decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
-            GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory");
-
-        if (createDWriteFactory) {
-            /**
-             * I need a direct pointer to be able to cast to IUnknown**, I also
-             * need to remember to release this because the nsRefPtr will
-             * AddRef it.
-             */
-            IDWriteFactory *factory;
-            HRESULT hr = createDWriteFactory(
-                DWRITE_FACTORY_TYPE_SHARED,
-                __uuidof(IDWriteFactory),
-                reinterpret_cast<IUnknown**>(&factory));
-
-            if (SUCCEEDED(hr) && factory) {
-                mDWriteFactory = factory;
-                factory->Release();
-                hr = mDWriteFactory->CreateTextAnalyzer(
-                    getter_AddRefs(mDWriteAnalyzer));
-            }
-
-            SetupClearTypeParams();
-
-            if (hr == S_OK)
-              reporter.SetSuccessful();
-        }
-    }
-#endif
+    InitD2DSupport();
+    InitDWriteSupport();
 
     uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
     uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
     BackendType defaultBackend = BackendType::CAIRO;
     if (mRenderMode == RENDER_DIRECT2D) {
       canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
       contentMask |= BackendTypeBit(BackendType::DIRECT2D);
       if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1() &&
@@ -1885,222 +1892,254 @@ bool DoesD3D11TextureSharingWork(ID3D11D
   return result;
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
+auto
+gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status
+{
+  if (gfxPrefs::LayersD3D11ForceWARP()) {
+    return D3D11Status::ForceWARP;
+  }
+
+  if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
+    int32_t status;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        // See if we can use WARP instead.
+        //
+        // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more.
+        if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) {
+          return D3D11Status::Blocked;
+        }
+
+        if (!IsWin8OrLater()) {
+          // We do not use WARP on Windows 7 or earlier.
+          return D3D11Status::Blocked;
+        }
+
+        return D3D11Status::TryWARP;
+      }
+    }
+  }
+
+  // Either nsIGfxInfo was bugged or we're not blacklisted.
+  if (!GetDXGIAdapter()) {
+    return D3D11Status::TryWARP;
+  }
+  return D3D11Status::Ok;
+}
+
+// We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
+// since it doesn't include d3d11.h, so we use a static here. It should only
+// be used within InitD3D11Devices.
+decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
+
+bool
+gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+{
+  RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
+  MOZ_ASSERT(adapter);
+
+  HRESULT hr = E_INVALIDARG;
+  MOZ_SEH_TRY {
+    hr =
+      sD3D11CreateDeviceFn(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
+                           // Use
+                           // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+                           // to prevent bug 1092260. IE 11 also uses this flag.
+                           D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
+  } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+    gfxCriticalError() << "Crash during D3D11 device creation";
+    return false;
+  }
+
+  if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) {
+    gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
+    return false;
+  }
+  if (!mD3D11Device) {
+    return false;
+  }
+
+  CheckIfRenderTargetViewNeedsRecreating(mD3D11Device);
+
+  // Only test this when not using WARP since it can fail and cause
+  // GetDeviceRemovedReason to return weird values.
+  mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  mIsWARP = false;
+  return true;
+}
+
+bool
+gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+{
+  if (gfxPrefs::LayersD3D11DisableWARP()) {
+    return false;
+  }
+
+  MOZ_ASSERT(!mD3D11Device);
+
+  ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
+
+  MOZ_SEH_TRY {
+    HRESULT hr =
+      sD3D11CreateDeviceFn(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
+                           // Use
+                           // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+                           // to prevent bug 1092260. IE 11 also uses this flag.
+                           D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
+
+    if (FAILED(hr)) {
+      // This should always succeed... in theory.
+      gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
+      return false;
+    }
+
+    reporterWARP.SetSuccessful();
+  } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+    gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
+    return false;
+  }
+
+  mIsWARP = true;
+  return true;
+}
+
+bool
+gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+{
+  HRESULT hr = E_INVALIDARG;
+  MOZ_SEH_TRY {
+    hr =
+      sD3D11CreateDeviceFn(mIsWARP ? nullptr : GetDXGIAdapter(),
+                           mIsWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN,
+                           nullptr,
+                           D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
+  } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+    return false;
+  }
+
+  return SUCCEEDED(hr);
+}
+
+bool
+gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+{
+  HRESULT hr = E_INVALIDARG;
+  MOZ_SEH_TRY{
+    hr =
+      sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
+                           D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr);
+  } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+    return false;
+  }
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  mD3D11ImageBridgeDevice->SetExceptionMode(0);
+
+  return DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice);
+}
+
 void
 gfxWindowsPlatform::InitD3D11Devices()
 {
   // This function attempts to initialize our D3D11 devices. If the hardware
   // is not blacklisted for D3D11 layers. This will first attempt to create a
   // hardware accelerated device. If this creation fails or the hardware is
   // blacklisted, then this function will abort if WARP is disabled, causing us
   // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use
   // a WARP device which should always be available on Windows 7 and higher.
-
   mD3D11DeviceInitialized = true;
   mDoesD3D11TextureSharingWork = false;
 
   MOZ_ASSERT(!mD3D11Device); 
 
   DriverInitCrashDetection detectCrashes;
   if (InSafeMode() || detectCrashes.DisableAcceleration()) {
     return;
   }
 
-  bool useWARP = false;
-
-  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-  if (gfxInfo) {
-    int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-
-        // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more.
-        if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) {
-          return;
-        }
-
-        if (!IsWin8OrLater()) {
-            /* On Windows 7 WARP runs very badly on the builtin vga driver */
-            nsString driver;
-            gfxInfo->GetAdapterDriver(driver);
-            // driver can start with vga or svga so only look for "framebuf..."
-            if (driver.Find("framebuf vga256 vga64k") != kNotFound) {
-                gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Disabling WARP on builtin vga driver";
-                return;
-            }
-        }
-
-        useWARP = true;
-      }
-    }
-  }
-
-  if (gfxPrefs::LayersD3D11ForceWARP()) {
-    useWARP = true;
+  D3D11Status status = CheckD3D11Support();
+  if (status == D3D11Status::Blocked) {
+    return;
   }
 
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
-  decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
-    GetProcAddress(d3d11Module, "D3D11CreateDevice");
+  sD3D11CreateDeviceFn =
+    (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice");
 
-  if (!d3d11CreateDevice) {
+  if (!sD3D11CreateDeviceFn) {
     // We should just be on Windows Vista or XP in this case.
     return;
   }
 
   nsTArray<D3D_FEATURE_LEVEL> featureLevels;
   if (IsWin8OrLater()) {
     featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
   }
   featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
   featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
   featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
   featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
-  RefPtr<IDXGIAdapter1> adapter;
-
-  if (!useWARP) {
-    adapter = GetDXGIAdapter();
-
-    if (!adapter) {
-      if (!gfxPrefs::LayersD3D11DisableWARP()) {
-        return;
-      }
-      useWARP = true;
+  if (status == D3D11Status::Ok) {
+    if (!AttemptD3D11DeviceCreation(featureLevels)) {
+      status = D3D11Status::TryWARP;
     }
   }
 
-  HRESULT hr = E_INVALIDARG;
-
-  if (!useWARP) {
-    MOZ_SEH_TRY {
-      hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
-                             // Use
-                             // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
-                             // to prevent bug 1092260. IE 11 also uses this flag.
-                             D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                             featureLevels.Elements(), featureLevels.Length(),
-                             D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
-    } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
-      gfxCriticalError() << "Crash during D3D11 device creation";
-
-      if (gfxPrefs::LayersD3D11DisableWARP()) {
-        return;
-      }
-
-      useWARP = true;
-      adapter = nullptr;
-    }
-
-    if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) {
-      gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
-      if (gfxPrefs::LayersD3D11DisableWARP()) {
-        return;
-      }
-
-      useWARP = true;
-      adapter = nullptr;
-    }
-
-    if (mD3D11Device) {
-      CheckIfRenderTargetViewNeedsRecreating(mD3D11Device);
-      // Only test this when not using WARP since it can fail and cause GetDeviceRemovedReason to return
-      // weird values.
-      mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  if (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP) {
+    if (!AttemptWARPDeviceCreation(featureLevels)) {
+      // Nothing more we can do.
+      return;
     }
   }
 
-  if (useWARP) {
-    MOZ_ASSERT(!gfxPrefs::LayersD3D11DisableWARP());
-    MOZ_ASSERT(!mD3D11Device);
-    MOZ_ASSERT(!adapter);
-
-    ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
-
-    MOZ_SEH_TRY {
-      hr = d3d11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
-                             // Use
-                             // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
-                             // to prevent bug 1092260. IE 11 also uses this flag.
-                             D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                             featureLevels.Elements(), featureLevels.Length(),
-                             D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
-
-      if (FAILED(hr)) {
-        // This should always succeed... in theory.
-        gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
-        return;
-      }
-
-      mIsWARP = true;
-      reporterWARP.SetSuccessful();
-    } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
-      gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
-      return;
-    }
+  if (!mD3D11Device) {
+    return;
   }
 
   mD3D11Device->SetExceptionMode(0);
 
   // We create our device for D2D content drawing here. Normally we don't use
   // D2D content drawing when using WARP. However when WARP is forced by
   // default we will let Direct2D use WARP as well.
-  if (Factory::SupportsD2D1() && (!useWARP || gfxPrefs::LayersD3D11ForceWARP())) {
-    MOZ_ASSERT((useWARP && !adapter) || !useWARP);
-
-    hr = E_INVALIDARG;
-    MOZ_SEH_TRY {
-      hr = d3d11CreateDevice(adapter, useWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN, nullptr,
-                             D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                             featureLevels.Elements(), featureLevels.Length(),
-                             D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
-    } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+  if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) {
+    if (!AttemptD3D11ContentDeviceCreation(featureLevels)) {
       mD3D11ContentDevice = nullptr;
-    }
-
-    if (FAILED(hr)) {
       d3d11Module.disown();
       return;
     }
 
     mD3D11ContentDevice->SetExceptionMode(0);
-
     nsRefPtr<ID3D10Multithread> multi;
     mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
     multi->SetMultithreadProtected(TRUE);
 
     Factory::SetDirect3D11Device(mD3D11ContentDevice);
   }
 
-  if (!useWARP) {
-    hr = E_INVALIDARG;
-
-    MOZ_SEH_TRY{
-      hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
-                             D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                             featureLevels.Elements(), featureLevels.Length(),
-                             D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr);
-    } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
-      mD3D11ImageBridgeDevice = nullptr;
-    }
-
-    if (FAILED(hr)) {
-      d3d11Module.disown();
-      return;
-    }
-
-    mD3D11ImageBridgeDevice->SetExceptionMode(0);
-
-    if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice)) {
+  if (!mIsWARP) {
+    if (!AttemptD3D11ImageBridgeDeviceCreation(featureLevels)) {
       mD3D11ImageBridgeDevice = nullptr;
     }
   }
 
   // We leak these everywhere and we need them our entire runtime anyway, let's
   // leak it here as well.
   d3d11Module.disown();
 }
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -270,17 +270,36 @@ public:
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
     int8_t mUseClearTypeAlways;
 
 private:
     void Init();
+
     void InitD3D11Devices();
+
+    // Used by InitD3D11Devices().
+    enum class D3D11Status {
+      Ok,
+      TryWARP,
+      ForceWARP,
+      Blocked
+    };
+    D3D11Status CheckD3D11Support();
+    bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
+    bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
+    bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
+    bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
+
+    // Used by UpdateRenderMode().
+    void InitD2DSupport();
+    void InitDWriteSupport();
+
     IDXGIAdapter1 *GetDXGIAdapter();
     bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
 
     bool mUseDirectWrite;
     bool mUsingGDIFonts;
 
 #ifdef CAIRO_HAS_DWRITE_FONT
     nsRefPtr<IDWriteFactory> mDWriteFactory;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -862,42 +862,44 @@ MessageChannel::Send(Message* aMsg, Mess
     if (mTimedOutMessageSeqno) {
         // Don't bother sending another sync message if a previous one timed out
         // and we haven't received a reply for it. Once the original timed-out
         // message receives a reply, we'll be able to send more sync messages
         // again.
         return false;
     }
 
-    if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
+    if (mCurrentTransaction &&
+        DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
         msg->priority() > IPC::Message::PRIORITY_NORMAL)
     {
         // Don't allow sending CPOWs while we're dispatching a sync message.
         // If you want to do that, use sendRpcMessage instead.
         return false;
     }
 
     if (mCurrentTransaction &&
         (msg->priority() < DispatchingSyncMessagePriority() ||
-         mAwaitingSyncReplyPriority > msg->priority() ||
-         DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT ||
-         DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT))
+         mAwaitingSyncReplyPriority > msg->priority()))
     {
         CancelCurrentTransactionInternal();
         mLink->SendMessage(new CancelMessage());
     }
 
     IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here");
-    IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(),
-               "can't send sync message of a lesser priority than what's being dispatched");
-    IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(),
-               "nested sync message sends must be of increasing priority");
 
-    IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
-               "not allowed to send messages while dispatching urgent messages");
+    if (mCurrentTransaction) {
+        IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(),
+                   "can't send sync message of a lesser priority than what's being dispatched");
+        IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(),
+                   "nested sync message sends must be of increasing priority");
+        IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
+                   "not allowed to send messages while dispatching urgent messages");
+    }
+
     IPC_ASSERT(DispatchingAsyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
                "not allowed to send messages while dispatching urgent messages");
 
     if (!Connected()) {
         ReportConnectionError("MessageChannel::SendAndWait", msg);
         return false;
     }
 
@@ -1311,21 +1313,16 @@ MessageChannel::DispatchSyncMessage(cons
 
     // We don't want to run any code that might run a nested event loop here, so
     // we avoid running event handlers. Once we've sent the response to the
     // urgent message, it's okay to run event handlers again since the parent is
     // no longer blocked.
     MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
     MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL);
 
-    IPC_ASSERT(prio >= DispatchingSyncMessagePriority(),
-               "priority inversion while dispatching sync message");
-    IPC_ASSERT(prio >= mAwaitingSyncReplyPriority,
-               "dispatching a message of lower priority while waiting for a response");
-
     MessageChannel* dummy;
     MessageChannel*& blockingVar = ShouldBlockScripts() ? gParentProcessBlocker : dummy;
 
     Result rv;
     if (mTimedOutMessageSeqno && mTimedOutMessagePriority >= prio) {
         // If the other side sends a message in response to one of our messages
         // that we've timed out, then we reply with an error.
         //
@@ -2017,26 +2014,36 @@ MessageChannel::CancelCurrentTransaction
     // canceled. Consequently, we have to update the state variables below.
     //
     // We also need to ensure that when any IPC functions on the stack return,
     // they don't reset these values using an RAII class like AutoSetValue. To
     // avoid that, these RAII classes check if the variable they set has been
     // tampered with (by us). If so, they don't reset the variable to the old
     // value.
 
-    MOZ_ASSERT(!mCurrentTransaction);
+    MOZ_ASSERT(mCurrentTransaction);
     mCurrentTransaction = 0;
+
+    mAwaitingSyncReply = false;
+    mAwaitingSyncReplyPriority = 0;
+
+    // We could also zero out mDispatchingSyncMessage here. However, that would
+    // cause a race because mDispatchingSyncMessage is a worker-thread-only
+    // field and we can be called on the I/O thread. Luckily, we can check to
+    // see if mCurrentTransaction is 0 before examining DispatchSyncMessage.
 }
 
 void
 MessageChannel::CancelCurrentTransaction()
 {
     MonitorAutoLock lock(*mMonitor);
-    CancelCurrentTransactionInternal();
-    mLink->SendMessage(new CancelMessage());
+    if (mCurrentTransaction) {
+        CancelCurrentTransactionInternal();
+        mLink->SendMessage(new CancelMessage());
+    }
 }
 
 void
 CancelCPOWs()
 {
     if (gParentProcessBlocker) {
         mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true);
         gParentProcessBlocker->CancelCurrentTransaction();
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -124,16 +124,21 @@ class MessageChannel : HasResultCodes
     // Make an Interrupt call to the other side of the channel
     bool Call(Message* aMsg, Message* aReply);
 
     // Wait until a message is received
     bool WaitForIncomingMessage();
 
     bool CanSend() const;
 
+    // Currently only for debugging purposes, doesn't aquire mMonitor.
+    ChannelState GetChannelState__TotallyRacy() const {
+        return mChannelState;
+    }
+
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
     bool IsOnCxxStack() const {
         return !mCxxStackFrames.empty();
     }
 
     void CancelCurrentTransaction();
 
@@ -549,17 +554,17 @@ class MessageChannel : HasResultCodes
     // The current transaction ID.
     int32_t mCurrentTransaction;
 
     class AutoEnterTransaction
     {
      public:
        explicit AutoEnterTransaction(MessageChannel *aChan, int32_t aMsgSeqno)
         : mChan(aChan),
-          mNewTransaction(0),
+          mNewTransaction(INT32_MAX),
           mOldTransaction(mChan->mCurrentTransaction)
        {
            mChan->mMonitor->AssertCurrentThreadOwns();
            if (mChan->mCurrentTransaction == 0) {
                mNewTransaction = aMsgSeqno;
                mChan->mCurrentTransaction = aMsgSeqno;
            }
        }
--- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -128,17 +128,17 @@ TestBridgeSubParent::Main()
 {
     if (!SendPing())
         fail("sending Ping");
 }
 
 bool
 TestBridgeSubParent::RecvBridgeEm()
 {
-    if (!PTestBridgeMainSub::Bridge(gBridgeMainChild, this))
+    if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this)))
         fail("bridging Main and Sub");
     return true;
 }
 
 void
 TestBridgeSubParent::ActorDestroy(ActorDestroyReason why)
 {
     if (NormalShutdown != why)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4130,38 +4130,58 @@ BytecodeEmitter::emitDestructuringOps(Pa
     return emitDestructuringOpsHelper(pattern, emitOption);
 }
 
 bool
 BytecodeEmitter::emitTemplateString(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
 
+    bool pushedString = false;
+
     for (ParseNode* pn2 = pn->pn_head; pn2 != NULL; pn2 = pn2->pn_next) {
-        if (pn2->getKind() != PNK_STRING && pn2->getKind() != PNK_TEMPLATE_STRING) {
+        bool isString = (pn2->getKind() == PNK_STRING || pn2->getKind() == PNK_TEMPLATE_STRING);
+
+        // Skip empty strings. These are very common: a template string like
+        // `${a}${b}` has three empty strings and without this optimization
+        // we'd emit four JSOP_ADD operations instead of just one.
+        if (isString && pn2->pn_atom->empty())
+            continue;
+
+        if (!isString) {
             // We update source notes before emitting the expression
             if (!updateSourceCoordNotes(pn2->pn_pos.begin))
                 return false;
         }
+
         if (!emitTree(pn2))
             return false;
 
-        if (pn2->getKind() != PNK_STRING && pn2->getKind() != PNK_TEMPLATE_STRING) {
+        if (!isString) {
             // We need to convert the expression to a string
             if (!emit1(JSOP_TOSTRING))
                 return false;
         }
 
-        if (pn2 != pn->pn_head) {
+        if (pushedString) {
             // We've pushed two strings onto the stack. Add them together, leaving just one.
             if (!emit1(JSOP_ADD))
                 return false;
-        }
-
-    }
+        } else {
+            pushedString = true;
+        }
+    }
+
+    if (!pushedString) {
+        // All strings were empty, this can happen for something like `${""}`.
+        // Just push an empty string.
+        if (!emitAtomOp(cx->names().empty, JSOP_STRING))
+            return false;
+    }
+
     return true;
 }
 
 bool
 BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption, bool isLetExpr)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
     MOZ_ASSERT(isLetExpr == (emitOption == PushInitialValues));
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -1254,19 +1254,21 @@ SavedNonVolatileRegisters(AllocatableGen
     LiveGeneralRegisterSet result;
 
     for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
         Register reg = *iter;
         if (!unused.has(reg))
             result.add(reg);
     }
 
-    // ARM and MIPS require an additional register to be saved, if calls can be made.
+    // Some platforms require the link register to be saved, if calls can be made.
 #if defined(JS_CODEGEN_ARM)
     result.add(Register::FromCode(Registers::lr));
+#elif defined(JS_CODEGEN_ARM64)
+    result.add(Register::FromCode(Registers::lr));
 #elif defined(JS_CODEGEN_MIPS)
     result.add(Register::FromCode(Registers::ra));
 #endif
 
     return result;
 }
 
 } // namespace jit
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -215,17 +215,17 @@ IsObjectEscaped(MInstruction* ins, JSObj
             }
 #endif
             break;
           }
 
           case MDefinition::Op_GuardShape: {
             MGuardShape* guard = def->toGuardShape();
             MOZ_ASSERT(!ins->isGuardShape());
-            if (obj->as<NativeObject>().lastProperty() != guard->shape()) {
+            if (obj->maybeShape() != guard->shape()) {
                 JitSpewDef(JitSpew_Escape, "has a non-matching guard shape\n", guard);
                 return true;
             }
             if (IsObjectEscaped(def->toInstruction(), obj)) {
                 JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def);
                 return true;
             }
             break;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1024,29 +1024,30 @@ xpc::CreateSandboxObject(JSContext* cx, 
                        ? &SandboxWriteToProtoClass
                        : &SandboxClass;
 
     RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
                                                      principal, compartmentOptions));
     if (!sandbox)
         return NS_ERROR_FAILURE;
 
-    CompartmentPrivate::Get(sandbox)->writeToGlobalPrototype =
-      options.writeToGlobalPrototype;
+    CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
+    priv->allowWaivers = options.allowWaivers;
+    priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
 
     // Set up the wantXrays flag, which indicates whether xrays are desired even
     // for same-origin access.
     //
     // This flag has historically been ignored for chrome sandboxes due to
     // quirks in the wrapping implementation that have now been removed. Indeed,
     // same-origin Xrays for chrome->chrome access seems a bit superfluous.
     // Arguably we should just flip the default for chrome and still honor the
     // flag, but such a change would break code in subtle ways for minimal
     // benefit. So we just switch it off here.
-    CompartmentPrivate::Get(sandbox)->wantXrays =
+    priv->wantXrays =
       AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
 
     {
         JSAutoCompartment ac(cx, sandbox);
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
             if (!ok)
@@ -1475,16 +1476,17 @@ SandboxOptions::ParseGlobalProperties()
 /*
  * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
  */
 bool
 SandboxOptions::Parse()
 {
     bool ok = ParseObject("sandboxPrototype", &proto) &&
               ParseBoolean("wantXrays", &wantXrays) &&
+              ParseBoolean("allowWaivers", &allowWaivers) &&
               ParseBoolean("wantComponents", &wantComponents) &&
               ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
               ParseString("sandboxName", sandboxName) &&
               ParseObject("sameZoneAs", &sameZoneAs) &&
               ParseBoolean("freshZone", &freshZone) &&
               ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
               ParseBoolean("discardSource", &discardSource) &&
               ParseJSString("addonId", &addonId) &&
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1292,17 +1292,17 @@ class MOZ_STACK_CLASS CallMethodHelper
     nsAutoTArray<nsXPTCVariant, 8> mDispatchParams;
     uint8_t mJSContextIndex; // TODO make const
     uint8_t mOptArgcIndex; // TODO make const
 
     jsval* const mArgv;
     const uint32_t mArgc;
 
     MOZ_ALWAYS_INLINE bool
-    GetArraySizeFromParam(uint8_t paramIndex, uint32_t* result) const;
+    GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result);
 
     MOZ_ALWAYS_INLINE bool
     GetInterfaceTypeFromParam(uint8_t paramIndex,
                               const nsXPTType& datum_type,
                               nsID* result) const;
 
     MOZ_ALWAYS_INLINE bool
     GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
@@ -1441,17 +1441,17 @@ CallMethodHelper::~CallMethodHelper()
                 if (!p)
                     continue;
 
                 // Clean up the array contents if necessary.
                 if (dp->DoesValNeedCleanup()) {
                     // We need some basic information to properly destroy the array.
                     uint32_t array_count = 0;
                     nsXPTType datum_type;
-                    if (!GetArraySizeFromParam(i, &array_count) ||
+                    if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) ||
                         !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
                                                                   &paramInfo,
                                                                   1, &datum_type))) {
                         // XXXbholley - I'm not convinced that the above calls will
                         // ever fail.
                         NS_ERROR("failed to get array information, we'll leak here");
                         continue;
                     }
@@ -1475,27 +1475,44 @@ CallMethodHelper::~CallMethodHelper()
         }
     }
 
     mCallContext.GetXPCContext()->SetLastResult(mInvokeResult);
 }
 
 bool
 CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
-                                        uint32_t* result) const
+                                        HandleValue maybeArray,
+                                        uint32_t* result)
 {
     nsresult rv;
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
 
     // TODO fixup the various exceptions that are thrown
 
     rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
     if (NS_FAILED(rv))
         return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
 
+    // If the array length wasn't passed, it might have been listed as optional.
+    // When converting arguments from JS to C++, we pass the array as |maybeArray|,
+    // and give ourselves the chance to infer the length. Once we have it, we stick
+    // it in the right slot so that we can find it again when cleaning up the params.
+    // from the array.
+    if (paramIndex >= mArgc && maybeArray.isObject()) {
+        MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
+        RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
+                                                                     : nullptr);
+        if (!JS_IsArrayObject(mCallContext, maybeArray) ||
+            !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32))
+        {
+            return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
+        }
+    }
+
     *result = GetDispatchParam(paramIndex)->val.u32;
 
     return true;
 }
 
 bool
 CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
                                             const nsXPTType& datum_type,
@@ -1581,17 +1598,17 @@ CallMethodHelper::GatherAndConvertResult
                                                       &datum_type))) {
                 Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
                 return false;
             }
         } else
             datum_type = type;
 
         if (isArray || isSizedString) {
-            if (!GetArraySizeFromParam(i, &array_count))
+            if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count))
                 return false;
         }
 
         nsID param_iid;
         if (datum_type.IsInterfacePointer() &&
             !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
             return false;
 
@@ -1963,17 +1980,17 @@ CallMethodHelper::ConvertDependentParam(
     nsID param_iid;
     if (datum_type.IsInterfacePointer() &&
         !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
         return false;
 
     nsresult err;
 
     if (isArray || isSizedString) {
-        if (!GetArraySizeFromParam(i, &array_count))
+        if (!GetArraySizeFromParam(i, src, &array_count))
             return false;
 
         if (isArray) {
             if (array_count &&
                 !XPCConvert::JSArray2Native((void**)&dp->val, src,
                                             array_count, datum_type, &param_iid,
                                             &err)) {
                 // XXX need exception scheme for arrays to indicate bad element
--- a/js/xpconnect/src/XPCWrapper.cpp
+++ b/js/xpconnect/src/XPCWrapper.cpp
@@ -27,26 +27,24 @@ static bool
 UnwrapNW(JSContext* cx, unsigned argc, jsval* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
   }
 
   JS::RootedValue v(cx, args[0]);
-  if (!v.isObject() || !js::IsWrapper(&v.toObject())) {
+  if (!v.isObject() || !js::IsCrossCompartmentWrapper(&v.toObject()) ||
+      !WrapperFactory::AllowWaiver(&v.toObject())) {
     args.rval().set(v);
     return true;
   }
 
-  if (AccessCheck::wrapperSubsumes(&v.toObject())) {
-    bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
-    NS_ENSURE_TRUE(ok, false);
-  }
-
+  bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
+  NS_ENSURE_TRUE(ok, false);
   args.rval().set(v);
   return true;
 }
 
 static bool
 XrayWrapperConstructor(JSContext* cx, unsigned argc, jsval* vp)
 {
   JS::CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3038,17 +3038,17 @@ public:
     NS_IMETHOD Init(const nsAString& message,
                     const nsAString& sourceName,
                     const nsAString& sourceLine,
                     uint32_t lineNumber,
                     uint32_t columnNumber,
                     uint32_t flags,
                     const char* category) override;
 
-    NS_IMETHOD GetStack(JS::MutableHandleValue);
+    NS_IMETHOD GetStack(JS::MutableHandleValue) override;
 
 private:
     virtual ~nsScriptErrorWithStack();
     // Complete stackframe where the error happened.
     // Must be SavedFrame object.
     JS::Heap<JSObject*>  mStack;
 };
 
@@ -3467,31 +3467,33 @@ protected:
 };
 
 class MOZ_STACK_CLASS SandboxOptions : public OptionsBase {
 public:
     explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(),
                             JSObject* options = nullptr)
         : OptionsBase(cx, options)
         , wantXrays(true)
+        , allowWaivers(true)
         , wantComponents(true)
         , wantExportHelpers(false)
         , proto(cx)
         , addonId(cx)
         , writeToGlobalPrototype(false)
         , sameZoneAs(cx)
         , freshZone(false)
         , invisibleToDebugger(false)
         , discardSource(false)
         , metadata(cx)
     { }
 
     virtual bool Parse();
 
     bool wantXrays;
+    bool allowWaivers;
     bool wantComponents;
     bool wantExportHelpers;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedString addonId;
     bool writeToGlobalPrototype;
     JS::RootedObject sameZoneAs;
     bool freshZone;
@@ -3678,16 +3680,17 @@ class CompartmentPrivate
 public:
     enum LocationHint {
         LocationHintRegular,
         LocationHintAddon
     };
 
     explicit CompartmentPrivate(JSCompartment* c)
         : wantXrays(false)
+        , allowWaivers(true)
         , writeToGlobalPrototype(false)
         , skipWriteToGlobalPrototype(false)
         , universalXPConnectEnabled(false)
         , forcePermissiveCOWs(false)
         , skipCOWCallableChecks(false)
         , scriptability(c)
         , scope(nullptr)
     {
@@ -3705,19 +3708,27 @@ public:
     }
 
     static CompartmentPrivate* Get(JSObject* object)
     {
         JSCompartment* compartment = js::GetObjectCompartment(object);
         return Get(compartment);
     }
 
-
+    // Controls whether this compartment gets Xrays to same-origin. This behavior
+    // is deprecated, but is still the default for sandboxes for compatibity
+    // reasons.
     bool wantXrays;
 
+    // Controls whether this compartment is allowed to waive Xrays to content
+    // that it subsumes. This should generally be true, except in cases where we
+    // want to prevent code from depending on Xray Waivers (which might make it
+    // more portable to other browser architectures).
+    bool allowWaivers;
+
     // This flag is intended for a very specific use, internal to Gecko. It may
     // go away or change behavior at any time. It should not be added to any
     // documentation and it should not be used without consulting the XPConnect
     // module owner.
     bool writeToGlobalPrototype;
 
     // When writeToGlobalPrototype is true, we use this flag to temporarily
     // disable the writeToGlobalPrototype behavior (when resolving standard
--- a/js/xpconnect/tests/components/js/xpctest_params.js
+++ b/js/xpconnect/tests/components/js/xpctest_params.js
@@ -68,12 +68,18 @@ TestParams.prototype = {
   testDoubleArray: f_is,
   testStringArray: f_is,
   testWstringArray: f_is,
   testInterfaceArray: f_is,
   testSizedString: f_is,
   testSizedWstring: f_is,
   testInterfaceIs: f_is,
   testInterfaceIsArray: f_size_and_iid,
-  testOutAString: function(o) { o.value = "out"; }
+  testOutAString: function(o) { o.value = "out"; },
+  testStringArrayOptionalSize: function(arr, size) {
+    if (arr.length != size) { throw "bad size passed to test method"; }
+    var rv = "";
+    arr.forEach((x) => rv += x);
+    return rv;
+  }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestParams]);
--- a/js/xpconnect/tests/components/native/xpctest_params.cpp
+++ b/js/xpconnect/tests/components/native/xpctest_params.cpp
@@ -341,8 +341,21 @@ NS_IMETHODIMP nsXPCTestParams::TestInter
 }
 
 /* void testOutAString (out AString o); */
 NS_IMETHODIMP nsXPCTestParams::TestOutAString(nsAString & o)
 {
     o.AssignLiteral("out");
     return NS_OK;
 }
+
+/*
+ * ACString testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength);
+ */
+NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char * *a, uint32_t length, nsACString& out)
+{
+  out.Truncate();
+  for (uint32_t i = 0; i < length; ++i) {
+    out.Append(a[i]);
+  }
+
+  return NS_OK;
+}
--- a/js/xpconnect/tests/idl/xpctest_params.idl
+++ b/js/xpconnect/tests/idl/xpctest_params.idl
@@ -10,17 +10,17 @@
  * covered by the intersection of return values and inout).
  */
 
 #include "nsISupports.idl"
 
 interface nsIXPCTestInterfaceA;
 interface nsIXPCTestInterfaceB;
 
-[scriptable, uuid(fe2b7433-ac3b-49ef-9344-b67228bfdd46)]
+[scriptable, uuid(812145c7-9fcc-425e-a878-36ad1b7730b7)]
 interface nsIXPCTestParams : nsISupports {
 
   // These types correspond to the ones in typelib.py
   boolean               testBoolean(in boolean a, inout boolean b);
   octet                 testOctet(in octet a, inout octet b);
   short                 testShort(in short a, inout short b);
   long                  testLong(in long a, inout long b);
   long long             testLongLong(in long long a, inout long long b);
@@ -79,9 +79,12 @@ interface nsIXPCTestParams : nsISupports
                                              [array, size_is(aLength), iid_is(aIID)] in nsQIResult a,
                                              inout unsigned long bLength, inout nsIIDPtr bIID,
                                              [array, size_is(bLength), iid_is(bIID)] inout nsQIResult b,
                                              out unsigned long rvLength, out nsIIDPtr rvIID,
                                              [retval, array, size_is(rvLength), iid_is(rvIID)] out nsQIResult rv);
 
   // Test for out dipper parameters
   void                 testOutAString(out AString o);
+
+  // Test for optional array size_is.
+  ACString             testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength);
 };
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_allowWaivers.js
@@ -0,0 +1,30 @@
+const Cu = Components.utils;
+function checkWaivers(from, allowed) {
+  var sb = new Cu.Sandbox('http://example.com');
+  from.test = sb.eval('var o = {prop: 2, f: function() {return 42;}}; o');
+
+  // Make sure that |from| has Xrays to sb.
+  do_check_eq(from.eval('test.prop'), 2);
+  do_check_eq(from.eval('test.f'), undefined);
+
+  // Make sure that waivability works as expected.
+  do_check_eq(from.eval('!!test.wrappedJSObject'), allowed);
+  do_check_eq(from.eval('XPCNativeWrapper.unwrap(test) !== test'), allowed);
+
+  // Make a sandbox with the same principal as |from|, but without any waiver
+  // restrictions, and make sure that the waiver does not transfer.
+  var friend = new Cu.Sandbox(Cu.getObjectPrincipal(from));
+  friend.test = from.test;
+  friend.eval('var waived = test.wrappedJSObject;');
+  do_check_true(friend.eval('waived.f()'), 42);
+  friend.from = from;
+  friend.eval('from.waived = waived');
+  do_check_eq(from.eval('!!waived.f'), allowed);
+}
+
+function run_test() {
+  checkWaivers(new Cu.Sandbox('http://example.com'), true);
+  checkWaivers(new Cu.Sandbox('http://example.com', {allowWaivers: false}), false);
+  checkWaivers(new Cu.Sandbox(['http://example.com']), true);
+  checkWaivers(new Cu.Sandbox(['http://example.com'], {allowWaivers: false}), false);
+}
--- a/js/xpconnect/tests/unit/test_params.js
+++ b/js/xpconnect/tests/unit/test_params.js
@@ -183,16 +183,19 @@ function test_component(contractid) {
   doIsTest("testInterfaceIs", makeA(), Ci['nsIXPCTestInterfaceA'],
                               makeB(), Ci['nsIXPCTestInterfaceB'],
                               interfaceComparator, dotEqualsComparator);
 
   // Test arrays of iids.
   doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'],
                                     [makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']);
 
+  // Test optional array size.
+  do_check_eq(o.testStringArrayOptionalSize(["some", "string", "array"]), "somestringarray");
+
   // Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS.
   doTypedArrayMismatchTest("testShortArray", new Int16Array([-3, 7, 4]), 4,
                                              new Int16Array([1, -32, 6]), 3);
 
   // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS.
   doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4,
                                              new Uint16Array([1, 5, 6]), 3);
 }
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -13,16 +13,17 @@ support-files =
   component-file.manifest
   component_import.js
   component_import.manifest
   importer.jsm
   recursive_importA.jsm
   recursive_importB.jsm
   syntax_error.jsm
 
+[test_allowWaivers.js]
 [test_bogus_files.js]
 [test_bug408412.js]
 [test_bug451678.js]
 [test_bug604362.js]
 [test_bug641378.js]
 [test_bug677864.js]
 [test_bug711404.js]
 [test_bug742444.js]
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -101,16 +101,30 @@ WrapperFactory::WaiveXray(JSContext* cx,
     MOZ_ASSERT(!js::IsInnerObject(obj));
 
     JSObject* waiver = GetXrayWaiver(obj);
     if (waiver)
         return waiver;
     return CreateXrayWaiver(cx, obj);
 }
 
+/* static */ bool
+WrapperFactory::AllowWaiver(JSCompartment* target, JSCompartment* origin)
+{
+    return CompartmentPrivate::Get(target)->allowWaivers &&
+           AccessCheck::subsumes(target, origin);
+}
+
+/* static */ bool
+WrapperFactory::AllowWaiver(JSObject* wrapper) {
+    MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper));
+    return AllowWaiver(js::GetObjectCompartment(wrapper),
+                       js::GetObjectCompartment(js::UncheckedUnwrap(wrapper)));
+}
+
 inline bool
 ShouldWaiveXray(JSContext* cx, JSObject* originalObj)
 {
     unsigned flags;
     (void) js::UncheckedUnwrap(originalObj, /* stopAtOuter = */ true, &flags);
 
     // If the original object did not point through an Xray waiver, we're done.
     if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG))
@@ -463,18 +477,20 @@ WrapperFactory::Rewrap(JSContext* cx, Ha
         //
         // Xrays are a bidirectional protection, since it affords clarity to the
         // caller and privacy to the callee.
         bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays ||
                                CompartmentPrivate::Get(target)->wantXrays;
         bool wantXrays = !sameOrigin || sameOriginXrays;
 
         // If Xrays are warranted, the caller may waive them for non-security
-        // wrappers.
-        bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj);
+        // wrappers (unless explicitly forbidden from doing so).
+        bool waiveXrays = wantXrays && !securityWrapper &&
+                          CompartmentPrivate::Get(target)->allowWaivers &&
+                          HasWaiveXrayFlag(obj);
 
         // We have slightly different behavior for the case when the object
         // being wrapped is in an XBL scope.
         bool originIsContentXBLScope = IsContentXBLScope(origin);
 
         wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
                                 originIsContentXBLScope, obj);
 
@@ -537,17 +553,17 @@ WrapperFactory::WaiveXrayAndWrap(JSConte
     // handing out waivers to callers that can't use them. The transitive waiving
     // machinery unconditionally calls WaiveXrayAndWrap on return values from
     // waived functions, even though the return value might be not be same-origin
     // with the function. So if we find ourselves trying to create a waiver for
     // |cx|, we should check whether the caller has any business with waivers
     // to things in |obj|'s compartment.
     JSCompartment* target = js::GetContextCompartment(cx);
     JSCompartment* origin = js::GetObjectCompartment(obj);
-    obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj;
+    obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj;
     if (!obj)
         return false;
 
     if (!JS_WrapObject(cx, &obj))
         return false;
     argObj.set(obj);
     return true;
 }
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -32,16 +32,23 @@ class WrapperFactory {
     }
 
     static bool IsCOW(JSObject* wrapper);
 
     static JSObject* GetXrayWaiver(JS::HandleObject obj);
     static JSObject* CreateXrayWaiver(JSContext* cx, JS::HandleObject obj);
     static JSObject* WaiveXray(JSContext* cx, JSObject* obj);
 
+    // Computes whether we should allow the creation of an Xray waiver from
+    // |target| to |origin|.
+    static bool AllowWaiver(JSCompartment* target, JSCompartment* origin);
+
+    // Convenience method for the above, operating on a wrapper.
+    static bool AllowWaiver(JSObject* wrapper);
+
     // Prepare a given object for wrapping in a new compartment.
     static JSObject* PrepareForWrapping(JSContext* cx,
                                         JS::HandleObject scope,
                                         JS::HandleObject obj,
                                         JS::HandleObject objectPassedToWrap);
 
     // Rewrap an object that is about to cross compartment boundaries.
     static JSObject* Rewrap(JSContext* cx,
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1310,17 +1310,17 @@ wrappedJSObject_getter(JSContext* cx, un
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.thisv().isObject()) {
         JS_ReportError(cx, "This value not an object");
         return false;
     }
     RootedObject wrapper(cx, &args.thisv().toObject());
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) ||
-        !AccessCheck::wrapperSubsumes(wrapper)) {
+        !WrapperFactory::AllowWaiver(wrapper)) {
         JS_ReportError(cx, "Unexpected object");
         return false;
     }
 
     args.rval().setObject(*wrapper);
 
     return WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
 }
@@ -1373,17 +1373,17 @@ XrayTraits::resolveOwnProperty(JSContext
         // Pretend the property lives on the wrapper.
         desc.object().set(wrapper);
         return true;
     }
 
     // Handle .wrappedJSObject for subsuming callers. This should move once we
     // sort out own-ness for the holder.
     if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
-        AccessCheck::wrapperSubsumes(wrapper))
+        WrapperFactory::AllowWaiver(wrapper))
     {
         if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
             return false;
         if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
                                              JSPROP_ENUMERATE | JSPROP_SHARED,
                                              wrappedJSObject_getter)) {
             return false;
         }
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1461,20 +1461,21 @@ nsBidiPresUtils::RepositionRubyContentFr
   }
   nscoord residualISize = aFrame->ISize(aFrameWM) - isize;
   if (residualISize <= 0) {
     return;
   }
 
   // When ruby-align is not "start", if the content does not fill this
   // frame, we need to center the children.
+  const nsSize dummyContainerSize;
   for (nsIFrame* child : childList) {
-    LogicalRect rect = child->GetLogicalRect(aFrameWM, 0);
+    LogicalRect rect = child->GetLogicalRect(aFrameWM, dummyContainerSize);
     rect.IStart(aFrameWM) += residualISize / 2;
-    child->SetRect(aFrameWM, rect, 0);
+    child->SetRect(aFrameWM, rect, dummyContainerSize);
   }
 }
 
 /* static */ nscoord
 nsBidiPresUtils::RepositionRubyFrame(
   nsIFrame* aFrame,
   const nsContinuationStates* aContinuationStates,
   const WritingMode aContainerWM,
@@ -1614,38 +1615,33 @@ nsBidiPresUtils::RepositionFrame(nsIFram
   } else if (RubyUtils::IsRubyBox(aFrame->GetType())) {
     icoord += RepositionRubyFrame(aFrame, aContinuationStates,
                                   aContainerWM, borderPadding);
   } else {
     icoord +=
       frameWM.IsOrthogonalTo(aContainerWM) ? aFrame->BSize() : frameISize;
   }
 
-  // LogicalRect doesn't correctly calculate the vertical position
-  // in vertical writing modes with right-to-left direction (Bug 1131451).
-  // This does the correct calculation ad hoc pending the fix for that.
-  nsRect rect = aFrame->GetRect();
-
-  LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM);
   // In the following variables, if aContainerReverseDir is true, i.e.
   // the container is positioning its children in reverse of its logical
   // direction, the "StartOrEnd" refers to the distance from the frame
   // to the inline end edge of the container, elsewise, it refers to the
   // distance to the inline start edge.
-  nscoord marginStartOrEnd = aContainerReverseDir ?
-    margin.IEnd(aContainerWM) : margin.IStart(aContainerWM);
+  const LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM);
+  nscoord marginStartOrEnd =
+    aContainerReverseDir ? margin.IEnd(aContainerWM)
+                         : margin.IStart(aContainerWM);
   nscoord frameStartOrEnd = aStartOrEnd + marginStartOrEnd;
-  // Whether we are placing frames from right to left.
-  // e.g. If the frames are placed reversely in LTR mode, they are
-  // actually placed from right to left.
-  bool orderingRTL = aContainerReverseDir == aContainerWM.IsBidiLTR();
-  (aContainerWM.IsVertical() ? rect.y : rect.x) = orderingRTL ?
-    lineSize - (frameStartOrEnd + icoord) : frameStartOrEnd;
-  (aContainerWM.IsVertical() ? rect.height : rect.width) = icoord;
-  aFrame->SetRect(rect);
+
+  LogicalRect rect = aFrame->GetLogicalRect(aContainerWM, aContainerSize);
+  rect.ISize(aContainerWM) = icoord;
+  rect.IStart(aContainerWM) =
+    aContainerReverseDir ? lineSize - frameStartOrEnd - icoord
+                         : frameStartOrEnd;
+  aFrame->SetRect(aContainerWM, rect, aContainerSize);
 
   return icoord + margin.IStartEnd(aContainerWM);
 }
 
 void
 nsBidiPresUtils::InitContinuationStates(nsIFrame*              aFrame,
                                         nsContinuationStates*  aContinuationStates)
 {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -819,17 +819,17 @@ nsLayoutUtils::UsesAsyncScrolling(nsIFra
   return AsyncPanZoomEnabled(aFrame);
 }
 
 bool
 nsLayoutUtils::AsyncPanZoomEnabled(nsIFrame* aFrame)
 {
   // We use this as a shortcut, since if the compositor will never use APZ,
   // no widget will either.
-  if (!gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly()) {
+  if (!gfxPlatform::AsyncPanZoomEnabled()) {
     return false;
   }
 
   nsIFrame *frame = nsLayoutUtils::GetDisplayRootFrame(aFrame);
   nsIWidget* widget = frame->GetNearestWidget();
   if (!widget) {
     return false;
   }
@@ -5614,17 +5614,17 @@ nsLayoutUtils::GetFirstLinePosition(Writ
     }
 
     if (fType == nsGkAtoms::fieldSetFrame) {
       LinePosition kidPosition;
       nsIFrame* kid = aFrame->GetFirstPrincipalChild();
       // kid might be a legend frame here, but that's ok.
       if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
         *aResult = kidPosition +
-          kid->GetLogicalNormalPosition(aWM, aFrame->GetSize().width).B(aWM);
+          kid->GetLogicalNormalPosition(aWM, aFrame->GetSize()).B(aWM);
         return true;
       }
       return false;
     }
 
     // No baseline.
     return false;
   }
@@ -5634,19 +5634,19 @@ nsLayoutUtils::GetFirstLinePosition(Writ
        line != line_end; ++line) {
     if (line->IsBlock()) {
       nsIFrame *kid = line->mFirstChild;
       LinePosition kidPosition;
       if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
         //XXX Not sure if this is the correct value to use for container
         //    width here. It will only be used in vertical-rl layout,
         //    which we don't have full support and testing for yet.
-        nscoord containerWidth = line->mContainerWidth;
+        const nsSize& containerSize = line->mContainerSize;
         *aResult = kidPosition +
-                   kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
+                   kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
         return true;
       }
     } else {
       // XXX Is this the right test?  We have some bogus empty lines
       // floating around, but IsEmpty is perhaps too weak.
       if (line->BSize() != 0 || !line->IsEmpty()) {
         nscoord bStart = line->BStart();
         aResult->mBStart = bStart;
@@ -5669,26 +5669,26 @@ nsLayoutUtils::GetLastLineBaseline(Writi
     return false;
 
   for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(),
                                              line_end = block->rend_lines();
        line != line_end; ++line) {
     if (line->IsBlock()) {
       nsIFrame *kid = line->mFirstChild;
       nscoord kidBaseline;
-      nscoord containerWidth = line->mContainerWidth;
+      const nsSize& containerSize = line->mContainerSize;
       if (GetLastLineBaseline(aWM, kid, &kidBaseline)) {
         // Ignore relative positioning for baseline calculations
         *aResult = kidBaseline +
-          kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
+          kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
         return true;
       } else if (kid->GetType() == nsGkAtoms::scrollFrame) {
         // Use the bottom of the scroll frame.
         // XXX CSS2.1 really doesn't say what to do here.
-        *aResult = kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM) +
+        *aResult = kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM) +
                    kid->BSize(aWM);
         return true;
       }
     } else {
       // XXX Is this the right test?  We have some bogus empty lines
       // floating around, but IsEmpty is perhaps too weak.
       if (line->BSize() != 0 || !line->IsEmpty()) {
         *aResult = line->BStart() + line->GetLogicalAscent();
@@ -5706,19 +5706,19 @@ CalculateBlockContentBEnd(WritingMode aW
 
   nscoord contentBEnd = 0;
 
   for (nsBlockFrame::line_iterator line = aFrame->begin_lines(),
                                    line_end = aFrame->end_lines();
        line != line_end; ++line) {
     if (line->IsBlock()) {
       nsIFrame* child = line->mFirstChild;
-      nscoord containerWidth = line->mContainerWidth;
+      const nsSize& containerSize = line->mContainerSize;
       nscoord offset =
-        child->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
+        child->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
       contentBEnd =
         std::max(contentBEnd,
                  nsLayoutUtils::CalculateContentBEnd(aWM, child) + offset);
     }
     else {
       contentBEnd = std::max(contentBEnd, line->BEnd());
     }
   }
@@ -5748,17 +5748,17 @@ nsLayoutUtils::CalculateContentBEnd(Writ
     nsIFrame::ChildListIterator lists(aFrame);
     for (; !lists.IsDone(); lists.Next()) {
       if (!skip.Contains(lists.CurrentID())) {
         nsFrameList::Enumerator childFrames(lists.CurrentList()); 
         for (; !childFrames.AtEnd(); childFrames.Next()) {
           nsIFrame* child = childFrames.get();
           nscoord offset =
             child->GetLogicalNormalPosition(aWM,
-                                            aFrame->GetSize().width).B(aWM);
+                                            aFrame->GetSize()).B(aWM);
           contentBEnd = std::max(contentBEnd,
                                  CalculateContentBEnd(aWM, child) + offset);
         }
       }
     }
   }
   return contentBEnd;
 }
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -104,17 +104,16 @@
 #endif
 
 #include "CubebUtils.h"
 #include "Latency.h"
 #include "WebAudioUtils.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsVolumeService.h"
-#include "SpeakerManagerService.h"
 using namespace mozilla::system;
 #endif
 
 #include "nsError.h"
 
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
 #include "nsFrameMessageManager.h"
@@ -400,17 +399,16 @@ nsLayoutStatics::Shutdown()
 #endif
 
   CubebUtils::ShutdownLibrary();
   AsyncLatencyLogger::ShutdownLogger();
   WebAudioUtils::Shutdown();
 
 #ifdef MOZ_WIDGET_GONK
   nsVolumeService::Shutdown();
-  SpeakerManagerService::Shutdown();
 #endif
 
 #ifdef MOZ_WEBSPEECH
   nsSynthVoiceRegistry::Shutdown();
 #endif
 
   nsCORSListenerProxy::Shutdown();
 
@@ -430,18 +428,16 @@ nsLayoutStatics::Shutdown()
 
   HTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsDOMMutationObserver::Shutdown();
 
-  AudioChannelService::Shutdown();
-
   DataStoreService::Shutdown();
 
   ContentParent::ShutDown();
 
   nsRefreshDriver::Shutdown();
 
   DisplayItemClip::Shutdown();
 
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -458,29 +458,30 @@ nsComboboxControlFrame::ReflowDropdown(n
   // dropped down
   int32_t flags = mDroppedDown ? 0
                                : NS_FRAME_NO_MOVE_FRAME |
                                  NS_FRAME_NO_VISIBILITY |
                                  NS_FRAME_NO_SIZE_VIEW;
 
   //XXX Can this be different from the dropdown's writing mode?
   // That would be odd!
-  // Note that we don't need to pass the true frame position or container width
+  // Note that we don't need to pass the true frame position or container size
   // to ReflowChild or FinishReflowChild here; it will be positioned as needed
   // by AbsolutelyPositionDropDown().
   WritingMode outerWM = GetWritingMode();
+  const nsSize dummyContainerSize;
   nsHTMLReflowMetrics desiredSize(aReflowState);
   nsReflowStatus ignoredStatus;
   ReflowChild(mDropdownFrame, aPresContext, desiredSize,
-              kidReflowState, outerWM, LogicalPoint(outerWM), 0,
-              flags, ignoredStatus);
+              kidReflowState, outerWM, LogicalPoint(outerWM),
+              dummyContainerSize, flags, ignoredStatus);
 
    // Set the child's width and height to its desired size
   FinishReflowChild(mDropdownFrame, aPresContext, desiredSize, &kidReflowState,
-                    outerWM, LogicalPoint(outerWM), 0, flags);
+                    outerWM, LogicalPoint(outerWM), dummyContainerSize, flags);
 }
 
 nsPoint
 nsComboboxControlFrame::GetCSSTransformTranslation()
 {
   nsIFrame* frame = this;
   bool is3DTransform = false;
   Matrix transform;
@@ -570,39 +571,42 @@ nsComboboxControlFrame::GetAvailableDrop
   // location can change based on whether the dropdown is placed after
   // or before the display frame.  The approach taken here is to get the
   // absolute position of the display frame and use its location to
   // determine if the dropdown will go offscreen.
 
   // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms.
   // In the special case that our transform is only a 2D translation we
   // introduce this hack so that the dropdown will show up in the right place.
-  *aTranslation = LogicalPoint(aWM, GetCSSTransformTranslation(), 0);
+  // Use null container size when converting a vector from logical to physical.
+  const nsSize nullContainerSize;
+  *aTranslation = LogicalPoint(aWM, GetCSSTransformTranslation(),
+                               nullContainerSize);
   *aBefore = 0;
   *aAfter = 0;
 
   nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext());
-  nscoord containerWidth = screen.width;
-  LogicalRect logicalScreen(aWM, screen, containerWidth);
+  nsSize containerSize = screen.Size();
+  LogicalRect logicalScreen(aWM, screen, containerSize);
   if (mLastDropDownAfterScreenBCoord == nscoord_MIN) {
     LogicalRect thisScreenRect(aWM, GetScreenRectInAppUnits(),
-                               containerWidth);
+                               containerSize);
     mLastDropDownAfterScreenBCoord = thisScreenRect.BEnd(aWM) +
                                      aTranslation->B(aWM);
     mLastDropDownBeforeScreenBCoord = thisScreenRect.BEnd(aWM) +
                                      aTranslation->B(aWM);
   }
 
   nscoord minBCoord;
   nsPresContext* pc = PresContext()->GetToplevelContentDocumentPresContext();
   nsIFrame* root = pc ? pc->PresShell()->GetRootFrame() : nullptr;
   if (root) {
     minBCoord = LogicalRect(aWM,
                             root->GetScreenRectInAppUnits(),
-                            containerWidth).BStart(aWM);
+                            containerSize).BStart(aWM);
     if (mLastDropDownAfterScreenBCoord < minBCoord) {
       // Don't allow the drop-down to be placed before the content area.
       return;
     }
   } else {
     minBCoord = logicalScreen.BStart(aWM);
   }
 
@@ -664,22 +668,22 @@ nsComboboxControlFrame::AbsolutelyPositi
   // Position the drop-down after if there is room, otherwise place it before
   // if there is room.  If there is no room for it on either side then place
   // it after (to avoid overlapping UI like the URL bar).
   bool b = dropdownSize.BSize(wm)<= after || dropdownSize.BSize(wm) > before;
   LogicalPoint dropdownPosition(wm, 0, b ? BSize(wm) : -dropdownSize.BSize(wm));
 
   // Don't position the view unless the position changed since it might cause
   // a call to NotifyGeometryChange() and an infinite loop here.
-  nscoord containerWidth = GetRect().width;
+  nsSize containerSize = GetSize();
   const LogicalPoint currentPos =
-    mDropdownFrame->GetLogicalPosition(containerWidth);
+    mDropdownFrame->GetLogicalPosition(containerSize);
   const LogicalPoint newPos = dropdownPosition + translation;
   if (currentPos != newPos) {
-    mDropdownFrame->SetPosition(wm, newPos, containerWidth);
+    mDropdownFrame->SetPosition(wm, newPos, containerSize);
     nsContainerFrame::PositionFrameView(mDropdownFrame);
   }
   return eDropDownPositionFinal;
 }
 
 void
 nsComboboxControlFrame::NotifyGeometryChange()
 {
@@ -861,32 +865,31 @@ nsComboboxControlFrame::Reflow(nsPresCon
   }
 
   mDisplayISize = aReflowState.ComputedISize() - buttonISize;
 
   nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
 
   // The button should occupy the same space as a scrollbar
   WritingMode wm = aReflowState.GetWritingMode();
-  nscoord containerWidth = aReflowState.ComputedWidth() +
-    aReflowState.ComputedPhysicalBorderPadding().LeftRight();
-  LogicalRect buttonRect = mButtonFrame->GetLogicalRect(containerWidth);
+  nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained();
+  LogicalRect buttonRect = mButtonFrame->GetLogicalRect(containerSize);
 
   buttonRect.IStart(wm) =
     aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm) +
     mDisplayISize -
     (aReflowState.ComputedLogicalBorderPadding().IEnd(wm) -
      aReflowState.ComputedLogicalPadding().IEnd(wm));
   buttonRect.ISize(wm) = buttonISize;
 
   buttonRect.BStart(wm) = this->GetLogicalUsedBorder(wm).BStart(wm);
   buttonRect.BSize(wm) = mDisplayFrame->BSize(wm) +
                          this->GetLogicalUsedPadding(wm).BStartEnd(wm);
 
-  mButtonFrame->SetRect(buttonRect, containerWidth);
+  mButtonFrame->SetRect(buttonRect, containerSize);
 
   if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
       !NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
     // This frame didn't fit inside a fragmentation container.  Splitting
     // a nsComboboxControlFrame makes no sense, so we override the status here.
     aStatus = NS_FRAME_COMPLETE;
   }
 }
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -50,23 +50,23 @@ nsFieldSetFrame::GetType() const
 
 nsRect
 nsFieldSetFrame::VisualBorderRectRelativeToSelf() const
 {
   WritingMode wm = GetWritingMode();
   css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
   nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide);
   LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
-  nscoord containerWidth = r.Width(wm);
+  nsSize containerSize = r.Size(wm).GetPhysicalSize(wm);
   if (legendBorder < mLegendRect.BSize(wm)) {
     nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2;
     r.BStart(wm) += off;
     r.BSize(wm) -= off;
   }
-  return r.GetPhysicalRect(wm, containerWidth);
+  return r.GetPhysicalRect(wm, containerSize);
 }
 
 nsIFrame*
 nsFieldSetFrame::GetInner() const
 {
   nsIFrame* last = mFrames.LastChild();
   if (last &&
       last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) {
@@ -232,57 +232,56 @@ nsFieldSetFrame::PaintBorderBackground(n
 
   if (nsIFrame* legend = GetLegend()) {
     css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
     nscoord legendBorderWidth =
       StyleBorder()->GetComputedBorderWidth(legendSide);
 
     // Use the rect of the legend frame, not mLegendRect, so we draw our
     // border under the legend's inline-start and -end margins.
-    LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.width);
+    LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size());
 
     // Compute clipRect using logical coordinates, so that the legend space
     // will be clipped out of the appropriate physical side depending on mode.
-    LogicalRect clipRect = LogicalRect(wm, rect, rect.width);
+    LogicalRect clipRect = LogicalRect(wm, rect, rect.Size());
     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
     gfxContext* gfx = aRenderingContext.ThebesContext();
     int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
 
     // draw inline-start portion of the block-start side of the border
     clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm);
     clipRect.BSize(wm) = legendBorderWidth;
 
     gfx->Save();
-    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
+    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
                                   appUnitsPerDevPixel, *drawTarget));
     nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                 aDirtyRect, rect, mStyleContext);
     gfx->Restore();
 
     // draw inline-end portion of the block-start side of the border
-    clipRect = LogicalRect(wm, rect, rect.width);
+    clipRect = LogicalRect(wm, rect, rect.Size());
     clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm);
     clipRect.IStart(wm) = legendRect.IEnd(wm);
     clipRect.BSize(wm) = legendBorderWidth;
 
     gfx->Save();
-    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
+    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
                                   appUnitsPerDevPixel, *drawTarget));
     nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                 aDirtyRect, rect, mStyleContext);
     gfx->Restore();
 
     // draw remainder of the border (omitting the block-start side)
-    clipRect = LogicalRect(wm, rect, rect.width);
+    clipRect = LogicalRect(wm, rect, rect.Size());
     clipRect.BStart(wm) += legendBorderWidth;
-    clipRect.BSize(wm) =
-      GetLogicalRect(rect.width).BSize(wm) - (off + legendBorderWidth);
+    clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth);
 
     gfx->Save();
-    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width),
+    gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
                                   appUnitsPerDevPixel, *drawTarget));
     nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                 aDirtyRect, rect, mStyleContext);
     gfx->Restore();
   } else {
     nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                 aDirtyRect,
                                 nsRect(aPt, mRect.Size()),
@@ -444,18 +443,22 @@ nsFieldSetFrame::Reflow(nsPresContext*  
   Maybe<nsHTMLReflowState> legendReflowState;
   if (legend) {
     legendReflowState.emplace(aPresContext, aReflowState, legend,
                                 legendAvailSize);
   }
   if (reflowLegend) {
     nsHTMLReflowMetrics legendDesiredSize(aReflowState);
 
+    // We'll move the legend to its proper place later, so the position
+    // and containerSize passed here are unimportant.
+    const nsSize dummyContainerSize;
     ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState,
-                wm, LogicalPoint(wm), 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
+                wm, LogicalPoint(wm), dummyContainerSize,
+                NS_FRAME_NO_MOVE_FRAME, aStatus);
 #ifdef NOISY_REFLOW
     printf("  returned (%d, %d)\n",
            legendDesiredSize.Width(), legendDesiredSize.Height());
 #endif
     // figure out the legend's rectangle
     legendMargin = legend->GetLogicalUsedMargin(wm);
     mLegendRect =
       LogicalRect(wm, 0, 0,
@@ -472,31 +475,32 @@ nsFieldSetFrame::Reflow(nsPresContext*  
     }
 
     // if the legend space changes then we need to reflow the
     // content area as well.
     if (mLegendSpace != oldSpace && inner) {
       reflowInner = true;
     }
 
-    // We'll move the legend to its proper place later.
     FinishReflowChild(legend, aPresContext, legendDesiredSize,
-                      legendReflowState.ptr(), wm, LogicalPoint(wm), 0,
-                      NS_FRAME_NO_MOVE_FRAME);
+                      legendReflowState.ptr(), wm, LogicalPoint(wm),
+                      dummyContainerSize, NS_FRAME_NO_MOVE_FRAME);
   } else if (!legend) {
     mLegendRect.SetEmpty();
     mLegendSpace = 0;
   } else {
     // mLegendSpace and mLegendRect haven't changed, but we need
     // the used margin when placing the legend.
     legendMargin = legend->GetLogicalUsedMargin(wm);
   }
 
-  nscoord containerWidth = (wm.IsVertical() ? mLegendSpace : 0) +
-                            border.LeftRight(wm);
+  // This containerSize is incomplete as yet: it does not include the size
+  // of the |inner| frame itself.
+  nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) +
+                          border.Size(wm)).GetPhysicalSize(wm);
   // reflow the content frame only if needed
   if (reflowInner) {
     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner,
                                      innerAvailSize, nullptr,
                                      nsHTMLReflowState::CALLER_WILL_INIT);
     // Override computed padding, in case it's percentage padding
     kidReflowState.Init(aPresContext, nullptr, nullptr,
                         &aReflowState.ComputedPhysicalPadding());
@@ -520,36 +524,41 @@ nsFieldSetFrame::Reflow(nsPresContext*  
 
     nsHTMLReflowMetrics kidDesiredSize(kidReflowState,
                                        aDesiredSize.mFlags);
     // Reflow the frame
     NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
                  "Margins on anonymous fieldset child not supported!");
     LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace);
 
+    // We don't know the correct containerSize until we have reflowed |inner|,
+    // so we use a dummy value for now; FinishReflowChild will fix the position
+    // if necessary.
+    const nsSize dummyContainerSize;
     ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState,
-                wm, pt, containerWidth, 0, aStatus);
+                wm, pt, dummyContainerSize, 0, aStatus);
 
-    // update the container width after reflowing the inner frame
+    // Update containerSize to account for size of the inner frame, so that
+    // FinishReflowChild can position it correctly.
+    containerSize += kidDesiredSize.PhysicalSize();
     FinishReflowChild(inner, aPresContext, kidDesiredSize,
-                      &kidReflowState, wm, pt,
-                      containerWidth + kidDesiredSize.Width(), 0);
+                      &kidReflowState, wm, pt, containerSize, 0);
     NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
-  }
-
-  if (inner) {
-    containerWidth += inner->GetSize().width;
+  } else if (inner) {
+    // |inner| didn't need to be reflowed but we do need to include its size
+    // in containerSize.
+    containerSize += inner->GetSize();
   }
 
   LogicalRect contentRect(wm);
   if (inner) {
     // We don't support margins on inner, so our content rect is just the
-    // inner's border-box. We don't care about container-width at this point,
-    // as we'll figure out the actual positioning later.
-    contentRect = inner->GetLogicalRect(wm, containerWidth);
+    // inner's border-box. (We don't really care about container size at this
+    // point, as we'll figure out the actual positioning later.)
+    contentRect = inner->GetLogicalRect(wm, containerSize);
   }
 
   // Our content rect must fill up the available width
   LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm);
   if (availSize.ISize(wm) > contentRect.ISize(wm)) {
     contentRect.ISize(wm) = innerAvailSize.ISize(wm);
   }
 
@@ -602,19 +611,19 @@ nsFieldSetFrame::Reflow(nsPresContext*  
 
     // Note that legend's writing mode may be different from the fieldset's,
     // so we need to convert offsets before applying them to it (bug 1134534).
     LogicalMargin offsets =
       legendReflowState->ComputedLogicalOffsets().
         ConvertTo(wm, legendReflowState->GetWritingMode());
     nsHTMLReflowState::ApplyRelativePositioning(legend, wm, offsets,
                                                 &actualLegendPos,
-                                                containerWidth);
+                                                containerSize);
 
-    legend->SetPosition(wm, actualLegendPos, containerWidth);
+    legend->SetPosition(wm, actualLegendPos, containerSize);
     nsContainerFrame::PositionFrameView(legend);
     nsContainerFrame::PositionChildViews(legend);
   }
 
   // Return our size and our result.
   LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm),
                         mLegendSpace + border.BStartEnd(wm) +
                         (inner ? inner->BSize(wm) : 0));
@@ -677,11 +686,11 @@ nsFieldSetFrame::AccessibleType()
   return a11y::eHTMLGroupboxType;
 }
 #endif
 
 nscoord
 nsFieldSetFrame::GetLogicalBaseline(WritingMode aWritingMode) const
 {
   nsIFrame* inner = GetInner();
-  return inner->BStart(aWritingMode, GetParent()->GetSize().width) +
+  return inner->BStart(aWritingMode, GetParent()->GetSize()) +
     inner->GetLogicalBaseline(aWritingMode);
 }
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -315,21 +315,22 @@ nsHTMLButtonControlFrame::ReflowButtonCo
                                         adjustedButtonReflowState,
                                         aFirstKid, availSize);
 
   nsReflowStatus contentsReflowStatus;
   nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState);
   childPos.B(wm) = 0; // This will be set properly later, after reflowing the
                       // child to determine its size.
 
-  // We just pass 0 for containerWidth here, as the child will be repositioned
-  // later by FinishReflowChild.
+  // We just pass a dummy containerSize here, as the child will be
+  // repositioned later by FinishReflowChild.
+  nsSize dummyContainerSize;
   ReflowChild(aFirstKid, aPresContext,
               contentsDesiredSize, contentsReflowState,
-              wm, childPos, 0, 0, contentsReflowStatus);
+              wm, childPos, dummyContainerSize, 0, contentsReflowStatus);
   MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
              "We gave button-contents frame unconstrained available height, "
              "so it should be complete");
 
   // Compute the button's content-box size:
   LogicalSize buttonContentBox(wm);
   if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) {
     // Button has a fixed block-size -- that's its content-box bSize.
@@ -368,22 +369,23 @@ nsHTMLButtonControlFrame::ReflowButtonCo
     contentsDesiredSize.BSize(wm);
 
   childPos.B(wm) = std::max(0, extraSpace / 2);
 
   // Adjust childPos.B() to be in terms of the button's frame-rect, instead of
   // its focus-padding rect:
   childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm);
 
-  nscoord containerWidth = buttonContentBox.Width(wm) + clbp.LeftRight(wm);
+  nsSize containerSize =
+    (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm);
 
   // Place the child
   FinishReflowChild(aFirstKid, aPresContext,
                     contentsDesiredSize, &contentsReflowState,
-                    wm, childPos, containerWidth, 0);
+                    wm, childPos, containerSize, 0);
 
   // Make sure we have a useful 'ascent' value for the child
   if (contentsDesiredSize.BlockStartAscent() ==
       nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
     WritingMode wm = aButtonReflowState.GetWritingMode();
     contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
   }
 
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -170,18 +170,22 @@ nsNumberControlFrame::Reflow(nsPresConte
     LogicalPoint
       wrapperOffset(myWM,
                     aReflowState.ComputedLogicalBorderPadding().IStart(myWM) +
                     wrapperMargin.IStart(myWM),
                     aReflowState.ComputedLogicalBorderPadding().BStart(myWM) +
                     wrapperMargin.BStart(myWM));
 
     nsReflowStatus childStatus;
+    // We initially reflow the child with a dummy containerSize; positioning
+    // will be fixed later.
+    const nsSize dummyContainerSize;
     ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
-                wrapperReflowState, myWM, wrapperOffset, 0, 0, childStatus);
+                wrapperReflowState, myWM, wrapperOffset, dummyContainerSize, 0,
+                childStatus);
     MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
                "We gave our child unconstrained available block-size, "
                "so it should be complete");
 
     nscoord wrappersMarginBoxBSize =
       wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM);
 
     if (contentBoxBSize == NS_INTRINSICSIZE) {
@@ -203,28 +207,31 @@ nsNumberControlFrame::Reflow(nsPresConte
         aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM);
     }
 
     // Center child in block axis
     nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize;
     wrapperOffset.B(myWM) += std::max(0, extraSpace / 2);
 
     // Needed in FinishReflowChild, for logical-to-physical conversion:
-    nscoord borderBoxWidth = myWM.IsVertical() ?
-      borderBoxBSize : borderBoxISize;
+    nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
+                           GetPhysicalSize(myWM);
 
     // Place the child
     FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
                       &wrapperReflowState, myWM, wrapperOffset,
-                      borderBoxWidth, 0);
+                      borderBoxSize, 0);
 
+    nsSize contentBoxSize =
+      LogicalSize(myWM, contentBoxISize, contentBoxBSize).
+        GetPhysicalSize(myWM);
     aDesiredSize.SetBlockStartAscent(
        wrappersDesiredSize.BlockStartAscent() +
        outerWrapperFrame->BStart(aReflowState.GetWritingMode(),
-                                 contentBoxISize));
+                                 contentBoxSize));
   }
 
   LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
   aDesiredSize.SetSize(myWM, logicalDesiredSize);
 
   aDesiredSize.SetOverflowAreasToDesiredBounds();
 
   if (outerWrapperFrame) {
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -258,21 +258,21 @@ nsDisplayTextOverflowMarker::PaintTextTo
                               mStyle->mString.Length(), pt);
   }
 }
 
 TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aBlockFrame)
   : mContentArea(aBlockFrame->GetWritingMode(),
                  aBlockFrame->GetContentRectRelativeToSelf(),
-                 aBlockFrame->GetRect().width)
+                 aBlockFrame->GetSize())
   , mBuilder(aBuilder)
   , mBlock(aBlockFrame)
   , mScrollableFrame(nsLayoutUtils::GetScrollableFrameFor(aBlockFrame))
-  , mBlockWidth(aBlockFrame->GetRect().width)
+  , mBlockSize(aBlockFrame->GetSize())
   , mBlockWM(aBlockFrame->GetWritingMode())
   , mAdjustForPixelSnapping(false)
 {
 #ifdef MOZ_XUL
   if (!mScrollableFrame) {
     nsIAtom* pseudoType = aBlockFrame->StyleContext()->GetPseudo();
     if (pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
       mScrollableFrame =
@@ -291,19 +291,22 @@ TextOverflow::TextOverflow(nsDisplayList
       mScrollableFrame->GetScrollbarStyles().mVertical :
       mScrollableFrame->GetScrollbarStyles().mHorizontal;
     mCanHaveInlineAxisScrollbar = scrollbarStyle != NS_STYLE_OVERFLOW_HIDDEN;
     if (!mAdjustForPixelSnapping) {
       // Scrolling to the end position can leave some text still overflowing due
       // to pixel snapping behaviour in our scrolling code.
       mAdjustForPixelSnapping = mCanHaveInlineAxisScrollbar;
     }
+    // Use a null containerSize to convert a vector from logical to physical.
+    const nsSize nullContainerSize;
     mContentArea.MoveBy(mBlockWM,
                         LogicalPoint(mBlockWM,
-                                     mScrollableFrame->GetScrollPosition(), 0));
+                                     mScrollableFrame->GetScrollPosition(),
+                                     nullContainerSize));
     nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
     scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
   }
   uint8_t direction = aBlockFrame->StyleVisibility()->mDirection;
   const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
   if (mBlockWM.IsBidiLTR()) {
     mIStart.Init(style->mTextOverflow.GetLeft(direction));
     mIEnd.Init(style->mTextOverflow.GetRight(direction));
@@ -389,17 +392,17 @@ TextOverflow::AnalyzeMarkerEdges(nsIFram
                                  FrameHashtable* aFramesToHide,
                                  AlignmentEdges* aAlignmentEdges,
                                  bool*           aFoundVisibleTextOrAtomic,
                                  InnerClipEdges* aClippedMarkerEdges)
 {
   LogicalRect borderRect(mBlockWM,
                          nsRect(aFrame->GetOffsetTo(mBlock),
                                 aFrame->GetSize()),
-                         mBlockWidth);
+                         mBlockSize);
   nscoord istartOverlap = std::max(
     aInsideMarkersArea.IStart(mBlockWM) - borderRect.IStart(mBlockWM), 0);
   nscoord iendOverlap = std::max(
     borderRect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM), 0);
   bool insideIStartEdge =
     aInsideMarkersArea.IStart(mBlockWM) <= borderRect.IEnd(mBlockWM);
   bool insideIEndEdge =
     borderRect.IStart(mBlockWM) <= aInsideMarkersArea.IEnd(mBlockWM);
@@ -463,36 +466,36 @@ TextOverflow::ExamineLineFrames(nsLineBo
                                 FrameHashtable* aFramesToHide,
                                 AlignmentEdges* aAlignmentEdges)
 {
   // No ellipsing for 'clip' style.
   bool suppressIStart = mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
   bool suppressIEnd = mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
   if (mCanHaveInlineAxisScrollbar) {
     LogicalPoint pos(mBlockWM, mScrollableFrame->GetScrollPosition(),
-                     mBlockWidth);
+                     mBlockSize);
     LogicalRect scrollRange(mBlockWM, mScrollableFrame->GetScrollRange(),
-                            mBlockWidth);
+                            mBlockSize);
     // No ellipsing when nothing to scroll to on that side (this includes
     // overflow:auto that doesn't trigger a horizontal scrollbar).
     if (pos.I(mBlockWM) <= scrollRange.IStart(mBlockWM)) {
       suppressIStart = true;
     }
     if (pos.I(mBlockWM) >= scrollRange.IEnd(mBlockWM)) {
       suppressIEnd = true;
     }
   }
 
   LogicalRect contentArea = mContentArea;
   const nscoord scrollAdjust = mAdjustForPixelSnapping ?
     mBlock->PresContext()->AppUnitsPerDevPixel() : 0;
   InflateIStart(mBlockWM, &contentArea, scrollAdjust);
   InflateIEnd(mBlockWM, &contentArea, scrollAdjust);
   LogicalRect lineRect(mBlockWM, aLine->GetScrollableOverflowArea(),
-                       mBlockWidth);
+                       mBlockSize);
   const bool istartOverflow =
     !suppressIStart && lineRect.IStart(mBlockWM) < contentArea.IStart(mBlockWM);
   const bool iendOverflow =
     !suppressIEnd && lineRect.IEnd(mBlockWM) > contentArea.IEnd(mBlockWM);
   if (!istartOverflow && !iendOverflow) {
     // The line does not overflow on a side we should ellipsize.
     return;
   }
@@ -755,35 +758,35 @@ TextOverflow::CreateMarkers(const nsLine
   if (aCreateIStart) {
     DisplayListClipState::AutoSaveRestore clipState(mBuilder);
 
     LogicalRect markerLogicalRect(
       mBlockWM, aInsideMarkersArea.IStart(mBlockWM) - mIStart.mIntrinsicISize,
       aLine->BStart(), mIStart.mIntrinsicISize, aLine->BSize());
     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     nsRect markerRect =
-      markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockWidth) + offset;
-    ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockWidth) + offset,
+      markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
+    ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
                markerRect, clipState);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetLogicalAscent(), mIStart.mStyle, 0);
     mMarkerList.AppendNewToTop(marker);
   }
 
   if (aCreateIEnd) {
     DisplayListClipState::AutoSaveRestore clipState(mBuilder);
 
     LogicalRect markerLogicalRect(
       mBlockWM, aInsideMarkersArea.IEnd(mBlockWM), aLine->BStart(),
       mIEnd.mIntrinsicISize, aLine->BSize());
     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     nsRect markerRect =
-      markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockWidth) + offset;
-    ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockWidth) + offset,
+      markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
+    ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
                markerRect, clipState);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetLogicalAscent(), mIEnd.mStyle, 1);
     mMarkerList.AppendNewToTop(marker);
   }
 }
 
--- a/layout/generic/TextOverflow.h
+++ b/layout/generic/TextOverflow.h
@@ -112,17 +112,17 @@ class TextOverflow {
   };
 
   LogicalRect
     GetLogicalScrollableOverflowRectRelativeToBlock(nsIFrame* aFrame) const
   {
     return LogicalRect(mBlockWM,
                        aFrame->GetScrollableOverflowRect() +
                          aFrame->GetOffsetTo(mBlock),
-                       mBlockWidth);
+                       mBlockSize);
   }
 
   /**
    * Examines frames on the line to determine whether we should draw a left
    * and/or right marker, and if so, which frames should be completely hidden
    * and the bounds of what will be displayed between the markers.
    * @param aLine the line we're processing
    * @param aFramesToHide frames that should have their display items removed
@@ -205,17 +205,17 @@ class TextOverflow {
                      bool aCreateIStart, bool aCreateIEnd,
                      const LogicalRect& aInsideMarkersArea);
 
   LogicalRect            mContentArea;
   nsDisplayListBuilder*  mBuilder;
   nsIFrame*              mBlock;
   nsIScrollableFrame*    mScrollableFrame;
   nsDisplayList          mMarkerList;
-  nscoord                mBlockWidth;
+  nsSize                 mBlockSize;
   WritingMode            mBlockWM;
   bool                   mCanHaveInlineAxisScrollbar;
   bool                   mAdjustForPixelSnapping;
 
   class Marker {
   public:
     void Init(const nsStyleTextOverflowSide& aStyle) {
       mInitialized = false;
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -636,64 +636,51 @@ public:
     :
 #ifdef DEBUG
       mWritingMode(aWritingMode),
 #endif
       mPoint(aI, aB)
   { }
 
   // Construct from a writing mode and a physical point, within a given
-  // containing rectangle's width (defining the conversion between LTR
-  // and RTL coordinates).
+  // containing rectangle's size (defining the conversion between LTR
+  // and RTL coordinates, and between TTB and BTT coordinates).
   LogicalPoint(WritingMode aWritingMode,
                const nsPoint& aPoint,
-               nscoord aContainerWidth)
+               const nsSize& aContainerSize)
 #ifdef DEBUG
     : mWritingMode(aWritingMode)
 #endif
   {
     if (aWritingMode.IsVertical()) {
-      I() = aPoint.y;
-      B() = aWritingMode.IsVerticalLR() ? aPoint.x : aContainerWidth - aPoint.x;
+      I() = aWritingMode.IsBidiLTR() ? aPoint.y
+                                     : aContainerSize.height - aPoint.y;
+      B() = aWritingMode.IsVerticalLR() ? aPoint.x
+                                        : aContainerSize.width - aPoint.x;
     } else {
-      I() = aWritingMode.IsBidiLTR() ? aPoint.x : aContainerWidth - aPoint.x;
+      I() = aWritingMode.IsBidiLTR() ? aPoint.x
+                                     : aContainerSize.width - aPoint.x;
       B() = aPoint.y;
     }
   }
 
   /**
-   * Read-only (const) access to the coordinates, in both logical
-   * and physical terms.
+   * Read-only (const) access to the logical coordinates.
    */
   nscoord I(WritingMode aWritingMode) const // inline-axis
   {
     CHECK_WRITING_MODE(aWritingMode);
     return mPoint.x;
   }
   nscoord B(WritingMode aWritingMode) const // block-axis
   {
     CHECK_WRITING_MODE(aWritingMode);
     return mPoint.y;
   }
 
-  nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
-  {
-    CHECK_WRITING_MODE(aWritingMode);
-    if (aWritingMode.IsVertical()) {
-      return aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B();
-    } else {
-      return aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I();
-    }
-  }
-  nscoord Y(WritingMode aWritingMode) const
-  {
-    CHECK_WRITING_MODE(aWritingMode);
-    return aWritingMode.IsVertical() ? I() : B();
-  }
-
   /**
    * These non-const accessors return a reference (lvalue) that can be
    * assigned to by callers.
    */
   nscoord& I(WritingMode aWritingMode) // inline-axis
   {
     CHECK_WRITING_MODE(aWritingMode);
     return mPoint.x;
@@ -704,39 +691,42 @@ public:
     return mPoint.y;
   }
 
   /**
    * Return a physical point corresponding to our logical coordinates,
    * converted according to our writing mode.
    */
   nsPoint GetPhysicalPoint(WritingMode aWritingMode,
-                           nscoord aContainerWidth) const
+                           const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
-      return nsPoint(aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B(),
-                     I());
+      return nsPoint(aWritingMode.IsVerticalLR()
+                     ? B() : aContainerSize.width - B(),
+                     aWritingMode.IsBidiLTR()
+                     ? I() : aContainerSize.height - I());
     } else {
-      return nsPoint(aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I(),
+      return nsPoint(aWritingMode.IsBidiLTR()
+                     ? I() : aContainerSize.width - I(),
                      B());
     }
   }
 
   /**
    * Return the equivalent point in a different writing mode.
    */
   LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
-                         nscoord aContainerWidth) const
+                         const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aFromMode);
     return aToMode == aFromMode ?
       *this : LogicalPoint(aToMode,
-                           GetPhysicalPoint(aFromMode, aContainerWidth),
-                           aContainerWidth);
+                           GetPhysicalPoint(aFromMode, aContainerSize),
+                           aContainerSize);
   }
 
   bool operator==(const LogicalPoint& aOther) const
   {
     CHECK_WRITING_MODE(aOther.GetWritingMode());
     return mPoint == aOther.mPoint;
   }
 
@@ -940,17 +930,17 @@ public:
     // expected writing mode
     CHECK_WRITING_MODE(aFromMode);
     return aToMode == aFromMode ?
       *this : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
 #else
     // optimization for non-DEBUG builds where LogicalSize doesn't store
     // the writing mode
     return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
-             ? *this : LogicalSize(aToMode, BSize(), ISize());
+           ? *this : LogicalSize(aToMode, BSize(), ISize());
 #endif
   }
 
   /**
    * Test if a size is (0, 0).
    */
   bool IsAllZero() const
   {
@@ -1210,26 +1200,26 @@ public:
 
   /**
    * Return an nsMargin containing our physical coordinates
    */
   nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical()
-      ? (aWritingMode.IsVerticalLR()
-        ? (aWritingMode.IsBidiLTR()
-          ? nsMargin(IStart(), BEnd(), IEnd(), BStart())
-          : nsMargin(IEnd(), BEnd(), IStart(), BStart()))
-        : (aWritingMode.IsBidiLTR()
-          ? nsMargin(IStart(), BStart(), IEnd(), BEnd())
-          : nsMargin(IEnd(), BStart(), IStart(), BEnd())))
-      : (aWritingMode.IsBidiLTR()
-        ? nsMargin(BStart(), IEnd(), BEnd(), IStart())
-        : nsMargin(BStart(), IStart(), BEnd(), IEnd()));
+           ? (aWritingMode.IsVerticalLR()
+             ? (aWritingMode.IsBidiLTR()
+               ? nsMargin(IStart(), BEnd(), IEnd(), BStart())
+               : nsMargin(IEnd(), BEnd(), IStart(), BStart()))
+             : (aWritingMode.IsBidiLTR()
+               ? nsMargin(IStart(), BStart(), IEnd(), BEnd())
+               : nsMargin(IEnd(), BStart(), IStart(), BEnd())))
+           : (aWritingMode.IsBidiLTR()
+             ? nsMargin(BStart(), IEnd(), BEnd(), IStart())
+             : nsMargin(BStart(), IStart(), BEnd(), IEnd()));
   }
 
   /**
    * Return a LogicalMargin representing this margin in a different
    * writing mode
    */
   LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
   {
@@ -1378,38 +1368,33 @@ public:
       mRect(aOrigin.mPoint, aSize.mSize)
   {
     CHECK_WRITING_MODE(aOrigin.GetWritingMode());
     CHECK_WRITING_MODE(aSize.GetWritingMode());
   }
 
   LogicalRect(WritingMode aWritingMode,
               const nsRect& aRect,
-              nscoord aContainerWidth)
+              const nsSize& aContainerSize)
 #ifdef DEBUG
     : mWritingMode(aWritingMode)
 #endif
   {
     if (aWritingMode.IsVertical()) {
-      if (aWritingMode.IsVerticalLR()) {
-        mRect.y = aRect.x;
-      } else {
-        mRect.y = aContainerWidth - aRect.XMost();
-      }
+      mRect.y = aWritingMode.IsVerticalLR()
+                ? aRect.x : aContainerSize.width - aRect.XMost();
+      mRect.x = aWritingMode.IsBidiLTR()
+                ? aRect.y : aContainerSize.height - aRect.YMost();
       mRect.height = aRect.width;
-      mRect.x = aRect.y;
       mRect.width = aRect.height;
     } else {
-      if (aWritingMode.IsBidiLTR()) {
-        mRect.x = aRect.x;
-      } else {
-        mRect.x = aContainerWidth - aRect.XMost();
-      }
+      mRect.x = aWritingMode.IsBidiLTR()
+                ? aRect.x : aContainerSize.width - aRect.XMost();
+      mRect.y = aRect.y;
       mRect.width = aRect.width;
-      mRect.y = aRect.y;
       mRect.height = aRect.height;
     }
   }
 
   /**
    * Inline- and block-dimension geometry.
    */
   nscoord IStart(WritingMode aWritingMode) const // inline-start edge
@@ -1467,35 +1452,37 @@ public:
   {
     CHECK_WRITING_MODE(aWritingMode);
     return mRect.height;
   }
 
   /**
    * Accessors for line-relative coordinates
    */
-  nscoord LineLeft(WritingMode aWritingMode, nscoord aContainerWidth) const
+  nscoord LineLeft(WritingMode aWritingMode,
+                   const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
-    if (aWritingMode.IsVertical()) {
-      return IStart(); // sideways-left will require aContainerHeight
-    } else {
-      return aWritingMode.IsBidiLTR() ? IStart()
-                                      : aContainerWidth - IEnd();
+    if (aWritingMode.IsBidiLTR()) {
+      return IStart();
     }
+    nscoord containerISize =
+      aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
+    return containerISize - IEnd();
   }
-  nscoord LineRight(WritingMode aWritingMode, nscoord aContainerWidth) const
+  nscoord LineRight(WritingMode aWritingMode,
+                    const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
-    if (aWritingMode.IsVertical()) {
-      return IEnd(); // sideways-left will require aContainerHeight
-    } else {
-      return aWritingMode.IsBidiLTR() ? IEnd()
-                                      : aContainerWidth - IStart();
+    if (aWritingMode.IsBidiLTR()) {
+      return IEnd();
     }
+    nscoord containerISize =
+      aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
+    return containerISize - IStart();
   }
 
   /**
    * Physical coordinates of the rect.
    */
   nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
   {
     CHECK_WRITING_MODE(aWritingMode);
@@ -1503,20 +1490,25 @@ public:
       return aWritingMode.IsVerticalLR() ?
              mRect.Y() : aContainerWidth - mRect.YMost();
     } else {
       return aWritingMode.IsBidiLTR() ?
              mRect.X() : aContainerWidth - mRect.XMost();
     }
   }
 
-  nscoord Y(WritingMode aWritingMode) const
+  nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
   {
     CHECK_WRITING_MODE(aWritingMode);
-    return aWritingMode.IsVertical() ? mRect.X() : mRect.Y();
+    if (aWritingMode.IsVertical()) {
+      return aWritingMode.IsBidiLTR() ? mRect.X()
+                                      : aContainerHeight - mRect.XMost();
+    } else {
+      return mRect.Y();
+    }
   }
 
   nscoord Width(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width();
   }
 
@@ -1533,20 +1525,25 @@ public:
       return aWritingMode.IsVerticalLR() ?
              mRect.YMost() : aContainerWidth - mRect.Y();
     } else {
       return aWritingMode.IsBidiLTR() ?
              mRect.XMost() : aContainerWidth - mRect.X();
     }
   }
 
-  nscoord YMost(WritingMode aWritingMode) const
+  nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
   {
     CHECK_WRITING_MODE(aWritingMode);
-    return aWritingMode.IsVertical() ? mRect.XMost() : mRect.YMost();
+    if (aWritingMode.IsVertical()) {
+      return aWritingMode.IsBidiLTR() ? mRect.XMost()
+                                      : aContainerHeight - mRect.x;
+    } else {
+      return mRect.YMost();
+    }
   }
 
   bool IsEmpty() const
   {
     return mRect.IsEmpty();
   }
 
   bool IsAllZero() const
@@ -1638,43 +1635,45 @@ public:
   {
     CHECK_WRITING_MODE(aWritingMode);
     CHECK_WRITING_MODE(aMargin.GetWritingMode());
     mRect.Deflate(aMargin.mMargin);
   }
 
   /**
    * Return an nsRect containing our physical coordinates within the given
-   * container width
+   * container size.
    */
   nsRect GetPhysicalRect(WritingMode aWritingMode,
-                         nscoord aContainerWidth) const
+                         const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
-      return nsRect(aWritingMode.IsVerticalLR() ?
-                      BStart() : aContainerWidth - BEnd(),
-                    IStart(), BSize(), ISize());
+      return nsRect(aWritingMode.IsVerticalLR()
+                    ? BStart() : aContainerSize.width - BEnd(),
+                    aWritingMode.IsBidiLTR()
+                    ? IStart() : aContainerSize.height - IEnd(),
+                    BSize(), ISize());
     } else {
-      return nsRect(aWritingMode.IsBidiLTR() ?
-                      IStart() : aContainerWidth - IEnd(),
+      return nsRect(aWritingMode.IsBidiLTR()
+                    ? IStart() : aContainerSize.width - IEnd(),
                     BStart(), ISize(), BSize());
     }
   }
 
   /**
    * Return a LogicalRect representing this rect in a different writing mode
    */
   LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
-                        nscoord aContainerWidth) const
+                        const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aFromMode);
     return aToMode == aFromMode ?
-      *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerWidth),
-                          aContainerWidth);
+      *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerSize),
+                          aContainerSize);
   }
 
   /**
    * Set *this to be the rectangle containing the intersection of aRect1
    * and aRect2, return whether the intersection is non-empty.
    */
   bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2)
   {
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -403,17 +403,17 @@ nsAbsoluteContainingBlock::ReflowAbsolut
                              aReflowState.mStyleBorder->GetComputedBorder());
   const LogicalMargin margin =
     kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm);
   bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE)
     && aConstrainBSize
        // Don't split if told not to (e.g. for fixed frames)
     && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame)
        //XXX we don't handle splitting frames for inline absolute containing blocks yet
-    && (aKidFrame->GetLogicalRect(aContainingBlock.width).BStart(wm) <=
+    && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <=
         aReflowState.AvailableBSize());
        // Don't split things below the fold. (Ideally we shouldn't *have*
        // anything totally below the fold, but we can't position frames
        // across next-in-flow breaks yet.
   if (constrainBSize) {
     kidReflowState.AvailableBSize() =
       aReflowState.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) -
       kidReflowState.ComputedLogicalMargin().BStart(wm);
@@ -463,22 +463,18 @@ nsAbsoluteContainingBlock::ReflowAbsolut
   // Position the child relative to our padding edge
   LogicalRect rect(outerWM,
                    border.IStart(outerWM) + offsets.IStart(outerWM) +
                      margin.IStart(outerWM),
                    border.BStart(outerWM) + offsets.BStart(outerWM) +
                      margin.BStart(outerWM),
                    kidSize.ISize(outerWM), kidSize.BSize(outerWM));
   nsRect r =
-    rect.GetPhysicalRect(outerWM, logicalCBSize.Width(wm) +
-                                  border.LeftRight(outerWM));
-  // XXX hack to correct for lack of bidi support in vertical mode
-  if (outerWM.IsVertical() && !outerWM.IsBidiLTR()) {
-    r.y = logicalCBSize.Height(wm) + border.TopBottom(outerWM) - r.YMost();
-  }
+    rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) +
+                         border.Size(outerWM).GetPhysicalSize(outerWM));
 
   // Offset the frame rect by the given origin of the absolute containing block.
   // If the frame is auto-positioned on both sides of an axis, it will be
   // positioned based on its containing block and we don't need to offset.
   if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
     const nsStyleSides& offsets = kidReflowState.mStylePosition->mOffset;
     if (!(offsets.GetLeftUnit() == eStyleUnit_Auto &&
           offsets.GetRightUnit() == eStyleUnit_Auto)) {
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1239,50 +1239,49 @@ nsBlockFrame::Reflow(nsPresContext*     
 
     if (havePosition && !BulletIsEmpty()) {
       // We have some lines to align the bullet with.  
 
       // Doing the alignment using the baseline will also cater for
       // bullets that are placed next to a child block (bug 92896)
     
       // Tall bullets won't look particularly nice here...
-      LogicalRect bbox = bullet->GetLogicalRect(wm, metrics.Width());
+      LogicalRect bbox = bullet->GetLogicalRect(wm, metrics.PhysicalSize());
       bbox.BStart(wm) = position.mBaseline - metrics.BlockStartAscent();
-      bullet->SetRect(wm, bbox, metrics.Width());
+      bullet->SetRect(wm, bbox, metrics.PhysicalSize());
     }
     // Otherwise just leave the bullet where it is, up against our
     // block-start padding.
   }
 
   CheckFloats(state);
 
   // Compute our final size
   nscoord blockEndEdgeOfChildren;
   ComputeFinalSize(*reflowState, state, aMetrics, &blockEndEdgeOfChildren);
 
   // If the block direction is right-to-left, we need to update the bounds of
-  // lines that were placed relative to mContainerWidth during reflow, as
-  // we typically do not know the true container width (block-dir size of the
-  // finished paragraph/block) until we've reflowed all its children. So we
-  // use a "fake" mContainerWidth during reflow (see nsBlockReflowState's
-  // constructor) and then fix up the positions of the lines here, once the
-  // final block size is known.
+  // lines that were placed relative to mContainerSize during reflow, as
+  // we typically do not know the true container size until we've reflowed all
+  // its children. So we use a dummy mContainerSize during reflow (see
+  // nsBlockReflowState's constructor) and then fix up the positions of the
+  // lines here, once the final block size is known.
   //
   // Note that writing-mode:vertical-rl is the only case where the block
   // logical direction progresses in a negative physical direction, and
   // therefore block-dir coordinate conversion depends on knowing the width
   // of the coordinate space in order to translate between the logical and
   // physical origins.
   if (wm.IsVerticalRL()) {
-    nscoord containerWidth = aMetrics.Width();
-    nscoord deltaX = containerWidth - state.ContainerWidth();
-    if (deltaX) {
+    nsSize containerSize = aMetrics.PhysicalSize();
+    nscoord deltaX = containerSize.width - state.ContainerSize().width;
+    if (deltaX != 0) {
       for (line_iterator line = begin_lines(), end = end_lines();
            line != end; line++) {
-        UpdateLineContainerWidth(line, containerWidth);
+        UpdateLineContainerSize(line, containerSize);
       }
       for (nsIFrame* f : mFloats) {
         nsPoint physicalDelta(deltaX, 0);
         f->MovePositionBy(physicalDelta);
       }
       nsFrameList* bulletList = GetOutsideBulletList();
       if (bulletList) {
         nsPoint physicalDelta(deltaX, 0);
@@ -1928,19 +1927,19 @@ nsBlockFrame::PropagateFloatDamage(nsBlo
   if (floatManager->HasFloatDamage()) {
     // Need to check mBounds *and* mCombinedArea to find intersections 
     // with aLine's floats
     nscoord lineBCoordBefore = aLine->BStart() + aDeltaBCoord;
     nscoord lineBCoordAfter = lineBCoordBefore + aLine->BSize();
     // Scrollable overflow should be sufficient for things that affect
     // layout.
     WritingMode wm = aState.mReflowState.GetWritingMode();
-    nscoord containerWidth = aState.ContainerWidth();
+    nsSize containerSize = aState.ContainerSize();
     LogicalRect overflow = aLine->GetOverflowArea(eScrollableOverflow, wm,
-                                                  containerWidth);
+                                                  containerSize);
     nscoord lineBCoordCombinedBefore = overflow.BStart(wm) + aDeltaBCoord;
     nscoord lineBCoordCombinedAfter = lineBCoordCombinedBefore +
                                       overflow.BSize(wm);
 
     bool isDirty = floatManager->IntersectsDamage(wm, lineBCoordBefore,
                                                   lineBCoordAfter) ||
                    floatManager->IntersectsDamage(wm, lineBCoordCombinedBefore,
                                                   lineBCoordCombinedAfter);
@@ -2171,21 +2170,21 @@ nsBlockFrame::ReflowDirtyLines(nsBlockRe
     }
 
     if (!line->IsDirty()) {
       // See if there's any reflow damage that requires that we mark the
       // line dirty.
       PropagateFloatDamage(aState, line, deltaBCoord);
     }
 
-    // If the container width has changed reset the container width. If the
+    // If the container size has changed, reset mContainerSize. If the
     // line's writing mode is not ltr, or if the line is not left-aligned, also
     // mark the line dirty.
-    if (aState.ContainerWidth() != line->mContainerWidth) {
-      line->mContainerWidth = aState.ContainerWidth();
+    if (aState.ContainerSize() != line->mContainerSize) {
+      line->mContainerSize = aState.ContainerSize();
 
       bool isLastLine = line == mLines.back() &&
                         !GetNextInFlow() &&
                         NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast;
       uint8_t align = isLastLine ?
         StyleText()->mTextAlign : StyleText()->mTextAlignLast;
 
       if (line->mWritingMode.IsVertical() ||
@@ -2812,36 +2811,36 @@ nsBlockFrame::PullFrameFrom(nsLineBox*  
 
 void
 nsBlockFrame::SlideLine(nsBlockReflowState& aState,
                         nsLineBox* aLine, nscoord aDeltaBCoord)
 {
   NS_PRECONDITION(aDeltaBCoord != 0, "why slide a line nowhere?");
 
   // Adjust line state
-  aLine->SlideBy(aDeltaBCoord, aState.ContainerWidth());
+  aLine->SlideBy(aDeltaBCoord, aState.ContainerSize());
 
   // Adjust the frames in the line
   MoveChildFramesOfLine(aLine, aDeltaBCoord);
 }
 
 void
-nsBlockFrame::UpdateLineContainerWidth(nsLineBox* aLine,
-                                       nscoord aNewContainerWidth)
-{
-  if (aNewContainerWidth == aLine->mContainerWidth) {
+nsBlockFrame::UpdateLineContainerSize(nsLineBox* aLine,
+                                      const nsSize& aNewContainerSize)
+{
+  if (aNewContainerSize == aLine->mContainerSize) {
     return;
   }
 
   // Adjust line state
-  nscoord widthDelta = aLine->UpdateContainerWidth(aNewContainerWidth);
+  nsSize sizeDelta = aLine->UpdateContainerSize(aNewContainerSize);
 
   // Changing container width only matters if writing mode is vertical-rl
   if (GetWritingMode().IsVerticalRL()) {
-    MoveChildFramesOfLine(aLine, widthDelta);
+    MoveChildFramesOfLine(aLine, sizeDelta.width);
   }
 }
 
 void
 nsBlockFrame::MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord)
 {
   // Adjust the frames in the line
   nsIFrame* kid = aLine->mFirstChild;
@@ -3677,17 +3676,17 @@ nsBlockFrame::DoReflowInlineFrames(nsBlo
   printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
          this, aFloatAvailableSpace.mHasFloats);
 #endif
 
   WritingMode outerWM = aState.mReflowState.GetWritingMode();
   WritingMode lineWM = GetWritingMode(aLine->mFirstChild);
   LogicalRect lineRect =
     aFloatAvailableSpace.mRect.ConvertTo(lineWM, outerWM,
-                                         aState.ContainerWidth());
+                                         aState.ContainerSize());
 
   nscoord iStart = lineRect.IStart(lineWM);
   nscoord availISize = lineRect.ISize(lineWM);
   nscoord availBSize;
   if (aState.GetFlag(BRS_UNCONSTRAINEDBSIZE)) {
     availBSize = NS_UNCONSTRAINEDSIZE;
   }
   else {
@@ -6150,64 +6149,64 @@ nsBlockFrame::ReflowPushedFloats(nsBlock
     if (prevBlock) {
       aState.mFloatBreakType = prevBlock->FindTrailingClear();
     }
   }
 }
 
 void
 nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM,
-                            nscoord aContainerWidth)
+                            const nsSize& aContainerSize)
 {
   // Recover our own floats
   nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
                            // belong to our next-in-flow
   for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
-    LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerWidth);
-    aFloatManager.AddFloat(f, region, aWM, aContainerWidth);
+    LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize);
+    aFloatManager.AddFloat(f, region, aWM, aContainerSize);
     if (!stop && f->GetNextInFlow())
       stop = f->GetNextInFlow();
   }
 
   // Recurse into our overflow container children
   for (nsIFrame* oc = GetFirstChild(kOverflowContainersList);
        oc; oc = oc->GetNextSibling()) {
-    RecoverFloatsFor(oc, aFloatManager, aWM, aContainerWidth);
+    RecoverFloatsFor(oc, aFloatManager, aWM, aContainerSize);
   }
 
   // Recurse into our normal children
   for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) {
     if (line->IsBlock()) {
-      RecoverFloatsFor(line->mFirstChild, aFloatManager, aWM, aContainerWidth);
+      RecoverFloatsFor(line->mFirstChild, aFloatManager, aWM, aContainerSize);
     }
   }
 }
 
 void
 nsBlockFrame::RecoverFloatsFor(nsIFrame*       aFrame,
                                nsFloatManager& aFloatManager,
                                WritingMode     aWM,
-                               nscoord         aContainerWidth)
+                               const nsSize&   aContainerSize)
 {
   NS_PRECONDITION(aFrame, "null frame");
   // Only blocks have floats
   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
   // Don't recover any state inside a block that has its own space manager
   // (we don't currently have any blocks like this, though, thanks to our
   // use of extra frames for 'overflow')
   if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
     // If the element is relatively positioned, then adjust x and y
     // accordingly so that we consider relatively positioned frames
     // at their original position.
 
-    LogicalRect rect(aWM, block->GetNormalRect(), aContainerWidth);
-    nscoord lineLeft = rect.LineLeft(aWM, aContainerWidth);
+    LogicalRect rect(aWM, block->GetNormalRect(), aContainerSize);
+    nscoord lineLeft = rect.LineLeft(aWM, aContainerSize);
     nscoord blockStart = rect.BStart(aWM);
     aFloatManager.Translate(lineLeft, blockStart);
-    block->RecoverFloats(aFloatManager, aWM, aContainerWidth);
+    block->RecoverFloats(aFloatManager, aWM, aContainerSize);
     aFloatManager.Translate(-lineLeft, -blockStart);
   }
 }
 
 //////////////////////////////////////////////////////////////////////
 // Painting, event handling
 
 #ifdef DEBUG
@@ -7046,17 +7045,17 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBu
 
   // Approximate the bullets position; vertical alignment will provide
   // the final vertical location. We pass our writing-mode here, because
   // it may be different from the bullet frame's mode.
   nscoord bStart = floatAvailSpace.BStart(wm);
   aBulletFrame->SetRect(wm, LogicalRect(wm, iStart, bStart,
                                         aMetrics.ISize(wm),
                                         aMetrics.BSize(wm)),
-                        aState.ContainerWidth());
+                        aState.ContainerSize());
   aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState,
                           nsDidReflowStatus::FINISHED);
 }
 
 // This is used to scan frames for any float placeholders, add their
 // floats to the list represented by aList, and remove the
 // floats from whatever list they might be in. We don't search descendants
 // that are float containing blocks.  Floats that or not children of 'this'
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -408,20 +408,20 @@ protected:
   }
 
   /** move the frames contained by aLine by aDeltaBCoord
     * if aLine is a block, its child floats are added to the state manager
     */
   void SlideLine(nsBlockReflowState& aState,
                  nsLineBox* aLine, nscoord aDeltaBCoord);
 
-  void UpdateLineContainerWidth(nsLineBox* aLine,
-                                nscoord aNewContainerWidth);
+  void UpdateLineContainerSize(nsLineBox* aLine,
+                               const nsSize& aNewContainerSize);
 
-  // helper for SlideLine and UpdateLineContainerWidth
+  // helper for SlideLine and UpdateLineContainerSize
   void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord);
 
   void ComputeFinalSize(const nsHTMLReflowState& aReflowState,
                         nsBlockReflowState&      aState,
                         nsHTMLReflowMetrics&     aMetrics,
                         nscoord*                 aBottomEdgeOfChildren);
 
   void ComputeOverflowAreas(const nsRect&         aBounds,
@@ -479,17 +479,17 @@ public:
   /** Load all of aFrame's floats into the float manager iff aFrame is not a
    *  block formatting context. Handles all necessary float manager translations;
    *  assumes float manager is in aFrame's parent's coord system.
    *  Safe to call on non-blocks (does nothing).
    */
   static void RecoverFloatsFor(nsIFrame*            aFrame,
                                nsFloatManager&      aFloatManager,
                                mozilla::WritingMode aWM,
-                               nscoord              aContainerWidth);
+                               const nsSize&        aContainerSize);
 
   /**
    * Determine if we have any pushed floats from a previous continuation.
    *
    * @returns true, if any of the floats at the beginning of our mFloats list
    *          have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise.
    */
   bool HasPushedFloatsFromPrevContinuation() const {
@@ -551,17 +551,17 @@ protected:
    */
   void DrainPushedFloats();
 
   /** Load all our floats into the float manager (without reflowing them).
    *  Assumes float manager is in our own coordinate system.
    */
   void RecoverFloats(nsFloatManager&      aFloatManager,
                      mozilla::WritingMode aWM,
-                     nscoord              aContainerWidth);
+                     const nsSize&        aContainerSize);
 
   /** Reflow pushed floats
    */
   void ReflowPushedFloats(nsBlockReflowState& aState,
                           nsOverflowAreas&    aOverflowAreas,
                           nsReflowStatus&     aStatus);
 
   /** Find any trailing BR clear from the last line of the block (or its PIFs)
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -223,17 +223,17 @@ nsBlockReflowContext::ReflowBlock(const 
                                   bool                aIsAdjacentWithBStart,
                                   nsLineBox*          aLine,
                                   nsHTMLReflowState&  aFrameRS,
                                   nsReflowStatus&     aFrameReflowStatus,
                                   nsBlockReflowState& aState)
 {
   mFrame = aFrameRS.frame;
   mWritingMode = aState.mReflowState.GetWritingMode();
-  mContainerWidth = aState.ContainerWidth();
+  mContainerSize = aState.ContainerSize();
   mSpace = aSpace;
 
   if (!aIsAdjacentWithBStart) {
     aFrameRS.mFlags.mIsTopOfPage = false;  // make sure this is cleared
   }
 
   if (aApplyBStartMargin) {
     mBStartMargin = aPrevMargin;
@@ -275,17 +275,17 @@ nsBlockReflowContext::ReflowBlock(const 
     mICoord = mSpace.IStart(mWritingMode) + usedMargin.IStart(mWritingMode);
     mBCoord = mSpace.BStart(mWritingMode) + mBStartMargin.get() + aClearance;
 
     LogicalRect space(mWritingMode, mICoord, mBCoord,
                       mSpace.ISize(mWritingMode) -
                       usedMargin.IStartEnd(mWritingMode),
                       mSpace.BSize(mWritingMode) -
                       usedMargin.BStartEnd(mWritingMode));
-    tI = space.LineLeft(mWritingMode, mContainerWidth);
+    tI = space.LineLeft(mWritingMode, mContainerSize);
     tB = mBCoord;
 
     if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
       aFrameRS.mBlockDelta =
         mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart();
   }
 
 #ifdef DEBUG
@@ -425,29 +425,30 @@ nsBlockReflowContext::PlaceBlock(const n
                         nsDidReflowStatus::FINISHED);
       return false;
     }
   }
 
   aLine->SetBounds(mWritingMode,
                    mICoord, mBCoord - backupContainingBlockAdvance,
                    mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode),
-                   mContainerWidth);
+                   mContainerSize);
 
   WritingMode frameWM = mFrame->GetWritingMode();
   LogicalPoint logPos =
     LogicalPoint(mWritingMode, mICoord, mBCoord).
-      ConvertTo(frameWM, mWritingMode, mContainerWidth - mMetrics.Width());
+      ConvertTo(frameWM, mWritingMode,
+                mContainerSize - mMetrics.PhysicalSize());
 
   // ApplyRelativePositioning in right-to-left writing modes needs to
   // know the updated frame width
   mFrame->SetSize(mWritingMode, mMetrics.Size(mWritingMode));
-  aReflowState.ApplyRelativePositioning(&logPos, mContainerWidth);
+  aReflowState.ApplyRelativePositioning(&logPos, mContainerSize);
 
   // Now place the frame and complete the reflow process
   nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics,
                                       &aReflowState, frameWM, logPos,
-                                      mContainerWidth, 0);
+                                      mContainerSize, 0);
 
   aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition();
 
   return true;
 }
--- a/layout/generic/nsBlockReflowContext.h
+++ b/layout/generic/nsBlockReflowContext.h
@@ -78,15 +78,16 @@ public:
 
 protected:
   nsPresContext* mPresContext;
   const nsHTMLReflowState& mOuterReflowState;
 
   nsIFrame* mFrame;
   mozilla::LogicalRect mSpace;
 
-  nscoord mICoord, mBCoord, mContainerWidth;
+  nscoord mICoord, mBCoord;
+  nsSize mContainerSize;
   mozilla::WritingMode mWritingMode;
   nsHTMLReflowMetrics mMetrics;
   nsCollapsingMargin mBStartMargin;
 };
 
 #endif /* nsBlockReflowContext_h___ */
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -317,17 +317,17 @@ nsBlockReflowState::GetFloatAvailableSpa
                "bad coord system");
 #endif
 
   nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX)
     ? nscoord_MAX : std::max(mContentArea.BEnd(wm) - aBCoord, 0);
   nsFlowAreaRect result =
     mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::BAND_FROM_POINT,
                                blockSize, mContentArea, aState,
-                               ContainerWidth());
+                               ContainerSize());
   // Keep the inline size >= 0 for compatibility with nsSpaceManager.
   if (result.mRect.ISize(wm) < 0) {
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
@@ -350,17 +350,17 @@ nsBlockReflowState::GetFloatAvailableSpa
   nscoord wI, wB;
   mFloatManager->GetTranslation(wI, wB);
 
   NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
                "bad coord system");
 #endif
   nsFlowAreaRect result =
     mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::WIDTH_WITHIN_HEIGHT,
-                               aBSize, mContentArea, aState, ContainerWidth());
+                               aBSize, mContentArea, aState, ContainerSize());
   // Keep the width >= 0 for compatibility with nsSpaceManager.
   if (result.mRect.ISize(wm) < 0) {
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
@@ -474,31 +474,31 @@ nsBlockReflowState::RecoverFloats(nsLine
       if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
         nscoord tI, tB;
         mFloatManager->GetTranslation(tI, tB);
         nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
         printf("RecoverFloats: tIB=%d,%d (%d,%d) ",
                tI, tB, mFloatManagerI, mFloatManagerB);
         nsFrame::ListTag(stdout, floatFrame);
         LogicalRect region = nsFloatManager::GetRegionFor(wm, floatFrame,
-                                                          ContainerWidth());
+                                                          ContainerSize());
         printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n",
                aDeltaBCoord, region.IStart(wm), region.BStart(wm),
                region.ISize(wm), region.BSize(wm));
       }
 #endif
       mFloatManager->AddFloat(floatFrame,
                               nsFloatManager::GetRegionFor(wm, floatFrame,
-                                                           ContainerWidth()),
-                              wm, ContainerWidth());
+                                                           ContainerSize()),
+                              wm, ContainerSize());
       fc = fc->Next();
     }
   } else if (aLine->IsBlock()) {
     nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager, wm,
-                                   ContainerWidth());
+                                   ContainerSize());
   }
 }
 
 /**
  * Everything done in this function is done O(N) times for each pass of
  * reflow so it is O(N*M) where M is the number of incremental reflow
  * passes.  That's bad.  Don't do stuff here.
  *
@@ -695,17 +695,17 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // FIXME: Should give AutoRestore a getter for the value to avoid this.
   const nscoord saveBCoord = mBCoord;
 
   // Grab the float's display information
   const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay();
 
   // The float's old region, so we can propagate damage.
   LogicalRect oldRegion = nsFloatManager::GetRegionFor(wm, aFloat,
-                                                       ContainerWidth());
+                                                       ContainerSize());
 
   // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
   // ``above'' another float that preceded it in the flow.
   mBCoord = std::max(mFloatManager->GetLowestFloatTop(), mBCoord);
 
   // See if the float should clear any preceding floats...
   // XXX We need to mark this float somehow so that it gets reflowed
   // when floats are inserted before it.
@@ -847,25 +847,17 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // place and size itself as required.
 
   // Assign inline and block dir coordinates to the float. We don't use
   // LineLeft() and LineRight() here, because we would only have to
   // convert the result back into this block's writing mode.
   LogicalPoint floatPos(wm);
   bool leftFloat = NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats;
 
-  if (wm.IsVertical()) {
-    // IStart and IEnd should use the ContainerHeight in vertical modes
-    // with rtl direction. Since they don't yet (bug 1131451), we'll
-    // just put left floats at the top of the line and right floats at
-    // bottom.
-    floatPos.I(wm) = leftFloat
-                      ? floatAvailableSpace.mRect.Y(wm)
-                      : floatAvailableSpace.mRect.YMost(wm) - floatMarginISize;
-  } else if (leftFloat == wm.IsBidiLTR()) {
+  if (leftFloat == wm.IsBidiLTR()) {
     floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm);
   }
   else {
     if (!keepFloatOnSameLine) {
       floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize;
     }
     else {
       // this is the IE quirk (see few lines above)
@@ -930,49 +922,49 @@ nsBlockReflowState::FlowAndPlaceFloat(ns
   // Calculate the actual origin of the float frame's border rect
   // relative to the parent block; the margin must be added in
   // to get the border rect
   LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm),
                       floatMargin.BStart(wm) + floatPos.B(wm));
 
   // If float is relatively positioned, factor that in as well
   nsHTMLReflowState::ApplyRelativePositioning(aFloat, wm, floatOffsets,
-                                              &origin, ContainerWidth());
+                                              &origin, ContainerSize());
 
   // Position the float and make sure and views are properly
   // positioned. We need to explicitly position its child views as
   // well, since we're moving the float after flowing it.
-  bool moved = aFloat->GetLogicalPosition(wm, ContainerWidth()) != origin;
+  bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin;
   if (moved) {
-    aFloat->SetPosition(wm, origin, ContainerWidth());
+    aFloat->SetPosition(wm, origin, ContainerSize());
     nsContainerFrame::PositionFrameView(aFloat);
     nsContainerFrame::PositionChildViews(aFloat);
   }
 
   // Update the float combined area state
   // XXX Floats should really just get invalidated here if necessary
   mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() +
                                 aFloat->GetPosition());
 
   // Place the float in the float manager
   // calculate region
   LogicalRect region =
     nsFloatManager::CalculateRegionFor(wm, aFloat, floatMargin,
-                                       ContainerWidth());
+                                       ContainerSize());
   // if the float split, then take up all of the vertical height
   if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) &&
       (NS_UNCONSTRAINEDSIZE != ContentBSize())) {
     region.BSize(wm) = std::max(region.BSize(wm),
                                 ContentBSize() - floatPos.B(wm));
   }
   DebugOnly<nsresult> rv = mFloatManager->AddFloat(aFloat, region, wm,
-                                                   ContainerWidth());
+                                                   ContainerSize());
   MOZ_ASSERT(NS_SUCCEEDED(rv), "bad float placement");
   // store region
-  nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerWidth());
+  nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize());
 
   // If the float's dimensions have changed, note the damage in the
   // float manager.
   if (!region.IsEqualEdges(oldRegion)) {
     // XXXwaterson conservative: we could probably get away with noting
     // less damage; e.g., if only height has changed, then only note the
     // area into which the float has grown or from which the float has
     // shrunk.
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -202,18 +202,17 @@ public:
   }
   mozilla::LogicalSize ContentSize(mozilla::WritingMode aWM) const {
     mozilla::WritingMode wm = mReflowState.GetWritingMode();
     return mContentArea.Size(wm).ConvertTo(aWM, wm);
   }
 
   // Physical size. Use only for physical <-> logical coordinate conversion.
   nsSize mContainerSize;
-  nscoord ContainerWidth() const { return mContainerSize.width; }
-  nscoord ContainerHeight() const { return mContainerSize.height; }
+  const nsSize& ContainerSize() const { return mContainerSize; }
 
   // Continuation out-of-flow float frames that need to move to our
   // next in flow are placed here during reflow.  It's a pointer to
   // a frame list stored in the block's property table.
   nsFrameList *mPushedFloats;
   // This method makes sure pushed floats are accessible to
   // StealFrame. Call it before adding any frames to mPushedFloats.
   void SetupPushedFloatList();
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -660,30 +660,30 @@ nsCanvasFrame::Reflow(nsPresContext*    
         (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
       // Tell our kid it's being block-dir resized too.  Bit of a
       // hack for framesets.
       kidReflowState.SetBResize(true);
     }
 
     WritingMode wm = aReflowState.GetWritingMode();
     WritingMode kidWM = kidReflowState.GetWritingMode();
-    nscoord containerWidth = aReflowState.ComputedWidth();
+    nsSize containerSize = aReflowState.ComputedPhysicalSize();
 
     LogicalMargin margin = kidReflowState.ComputedLogicalMargin();
     LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
 
-    kidReflowState.ApplyRelativePositioning(&kidPt, containerWidth);
+    kidReflowState.ApplyRelativePositioning(&kidPt, containerSize);
 
     // Reflow the frame
     ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState,
-                kidWM, kidPt, containerWidth, 0, aStatus);
+                kidWM, kidPt, containerSize, 0, aStatus);
 
     // Complete the reflow and position and size the child frame
     FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState,
-                      kidWM, kidPt, containerWidth, 0);
+                      kidWM, kidPt, containerSize, 0);
 
     if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
       nsIFrame* nextFrame = kidFrame->GetNextInFlow();
       NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
         "If it's incomplete and has no nif yet, it must flag a nif reflow.");
       if (!nextFrame) {
         nextFrame = aPresContext->PresShell()->FrameConstructor()->
           CreateContinuingFrame(aPresContext, kidFrame, this);
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -355,23 +355,23 @@ nsColumnSetFrame::ReflowColumns(nsHTMLRe
                               aConfig, aLastColumnUnbounded,
                               aCarriedOutBEndMargin, aColData);
   }
 
   return feasible;
 }
 
 static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin,
-                        WritingMode aWM, nscoord aContainerWidth)
+                        WritingMode aWM, const nsSize& aContainerSize)
 {
-  if (aChild->GetLogicalPosition(aWM, aContainerWidth) == aOrigin) {
+  if (aChild->GetLogicalPosition(aWM, aContainerSize) == aOrigin) {
     return;
   }
 
-  aChild->SetPosition(aWM, aOrigin, aContainerWidth);
+  aChild->SetPosition(aWM, aOrigin, aContainerSize);
   nsContainerFrame::PlaceFrameView(aChild);
 }
 
 nscoord
 nsColumnSetFrame::GetMinISize(nsRenderingContext *aRenderingContext)
 {
   nscoord iSize = 0;
   DISPLAY_MIN_WIDTH(this, iSize);
@@ -483,35 +483,37 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
   borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState));
 
   nsRect contentRect(0, 0, 0, 0);
   nsOverflowAreas overflowRects;
 
   nsIFrame* child = mFrames.FirstChild();
   LogicalPoint childOrigin(wm, borderPadding.IStart(wm),
                            borderPadding.BStart(wm));
-  // In vertical-rl mode we can't use the computed width as the
-  // container width because it may be NS_UNCONSTRAINEDSIZE, so we use 0
-  // for now and reposition the columns after reflowing them all.
-  nscoord containerWidth = wm.IsVerticalRL() ? 0 : aReflowState.ComputedWidth();
+  // In vertical-rl mode, columns will not be correctly placed if the
+  // reflowState's ComputedWidth() is UNCONSTRAINED (in which case we'll get
+  // a containerSize.width of zero here). In that case, the column positions
+  // will be adjusted later, after our correct contentSize is known.
+  nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained();
 
   // For RTL, since the columns might not fill the frame exactly, we
   // need to account for the slop. Otherwise we'll waste time moving the
   // columns by some tiny amount
 
   // XXX when all of layout is converted to logical coordinates, we
   //     probably won't need to do this hack any more. For now, we
   //     confine it to the legacy horizontal-rl case
   if (!isVertical && isRTL) {
     nscoord availISize = aReflowState.AvailableISize();
     if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) {
       availISize = aReflowState.ComputedISize();
     }
     if (availISize != NS_INTRINSICSIZE) {
-      childOrigin.I(wm) = containerWidth - borderPadding.Left(wm) - availISize;
+      childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) -
+                          availISize;
 #ifdef DEBUG_roc
       printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm));
 #endif
     }
   }
 
   int columnCount = 0;
   int contentBEnd = 0;
@@ -562,17 +564,17 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
         NS_NOTREACHED("unknown block direction");
         break;
       }
     }
 
     nscoord childContentBEnd = 0;
     if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) {
       // This child does not need to be reflowed, but we may need to move it
-      MoveChildTo(child, childOrigin, wm, containerWidth);
+      MoveChildTo(child, childOrigin, wm, containerSize);
 
       // If this is the last frame then make sure we get the right status
       nsIFrame* kidNext = child->GetNextSibling();
       if (kidNext) {
         aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
                   ? NS_FRAME_OVERFLOW_INCOMPLETE
                   : NS_FRAME_NOT_COMPLETE;
       } else {
@@ -629,32 +631,32 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
 
       // Reflow the frame
       LogicalPoint origin(wm,
                           childOrigin.I(wm) +
                           kidReflowState.ComputedLogicalMargin().IStart(wm),
                           childOrigin.B(wm) +
                           kidReflowState.ComputedLogicalMargin().BStart(wm));
       ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState,
-                  wm, origin, containerWidth, 0, aStatus);
+                  wm, origin, containerSize, 0, aStatus);
 
       reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
 
 #ifdef DEBUG_roc
       printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n",
              columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(),
              kidDesiredSize.mCarriedOutBEndMargin.get());
 #endif
 
       NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
 
       *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin;
 
       FinishReflowChild(child, PresContext(), kidDesiredSize,
-                        &kidReflowState, wm, childOrigin, containerWidth, 0);
+                        &kidReflowState, wm, childOrigin, containerSize, 0);
 
       childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
       if (childContentBEnd > aConfig.mColMaxBSize) {
         allFit = false;
       }
       if (childContentBEnd > availSize.BSize(wm)) {
         aColData.mMaxOverflowingBSize = std::max(childContentBEnd,
             aColData.mMaxOverflowingBSize);
@@ -796,27 +798,27 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
   }
 
   contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
   contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
   aDesiredSize.SetSize(wm, contentSize);
   aDesiredSize.mOverflowAreas = overflowRects;
   aDesiredSize.UnionOverflowAreasWithDesiredBounds();
 
-  // In vertical-rl mode, make a second pass to reposition the columns
-  // with the correct container width
-  if (wm.IsVerticalRL()) {
-    child = mFrames.FirstChild();
-    while (child) {
-      // Get the logical position as set before with containerWidth=0
-      // and reset with the correct container width (which is the block
-      // size in vertical modes).
-      child->SetPosition(wm, child->GetLogicalPosition(wm, 0),
-                         contentSize.BSize(wm));
-      child = child->GetNextSibling();
+  // In vertical-rl mode, make a second pass if necessary to reposition the
+  // columns with the correct container width. (In other writing modes,
+  // correct containerSize was not required for column positioning so we don't
+  // need this fixup.)
+  if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) {
+    const nsSize finalContainerSize = aDesiredSize.PhysicalSize();
+    for (nsIFrame* child : mFrames) {
+      // Get the logical position as set previously using a provisional or
+      // dummy containerSize, and reset with the correct container size.
+      child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize),
+                         finalContainerSize);
     }
   }
 
 #ifdef DEBUG_roc
   printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
          && !NS_FRAME_IS_TRUNCATED(aStatus));
 #endif
   return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -969,30 +969,30 @@ nsContainerFrame::ComputeAutoSize(nsRend
 
 void
 nsContainerFrame::ReflowChild(nsIFrame*                aKidFrame,
                               nsPresContext*           aPresContext,
                               nsHTMLReflowMetrics&     aDesiredSize,
                               const nsHTMLReflowState& aReflowState,
                               const WritingMode&       aWM,
                               const LogicalPoint&      aPos,
-                              nscoord                  aContainerWidth,
+                              const nsSize&            aContainerSize,
                               uint32_t                 aFlags,
                               nsReflowStatus&          aStatus,
                               nsOverflowContinuationTracker* aTracker)
 {
   NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
   if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
-    NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE,
+    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
                  "ReflowChild with unconstrained container width!");
   }
 
   // Position the child frame and its view if requested.
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
-    aKidFrame->SetPosition(aWM, aPos, aContainerWidth);
+    aKidFrame->SetPosition(aWM, aPos, aContainerSize);
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
   }
 
   // Reflow the child frame
   aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
@@ -1109,32 +1109,32 @@ nsContainerFrame::PositionChildViews(nsI
  */
 void
 nsContainerFrame::FinishReflowChild(nsIFrame*                  aKidFrame,
                                     nsPresContext*             aPresContext,
                                     const nsHTMLReflowMetrics& aDesiredSize,
                                     const nsHTMLReflowState*   aReflowState,
                                     const WritingMode&         aWM,
                                     const LogicalPoint&        aPos,
-                                    nscoord                    aContainerWidth,
+                                    const nsSize&              aContainerSize,
                                     uint32_t                   aFlags)
 {
   if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
-    NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE,
+    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
                  "FinishReflowChild with unconstrained container width!");
   }
 
   nsPoint curOrigin = aKidFrame->GetPosition();
   WritingMode outerWM = aDesiredSize.GetWritingMode();
   LogicalSize convertedSize = aDesiredSize.Size(outerWM).ConvertTo(aWM,
                                                                    outerWM);
 
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
     aKidFrame->SetRect(aWM, LogicalRect(aWM, aPos, convertedSize),
-                       aContainerWidth);
+                       aContainerSize);
   } else {
     aKidFrame->SetSize(aWM, convertedSize);
   }
 
   if (aKidFrame->HasView()) {
     nsView* view = aKidFrame->GetView();
     // Make sure the frame's view is properly sized and positioned and has
     // things like opacity correct
@@ -1256,35 +1256,35 @@ nsContainerFrame::ReflowOverflowContaine
     if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
       // Get prev-in-flow
       nsIFrame* prevInFlow = frame->GetPrevInFlow();
       NS_ASSERTION(prevInFlow,
                    "overflow container frame must have a prev-in-flow");
       NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
                    "overflow container frame must have overflow container bit set");
       WritingMode wm = frame->GetWritingMode();
-      nscoord containerWidth = aReflowState.AvailableSize(wm).Width(wm);
-      LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerWidth);
+      nsSize containerSize = aReflowState.AvailableSize(wm).GetPhysicalSize(wm);
+      LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize);
 
       // Initialize reflow params
       LogicalSize availSpace(wm, prevRect.ISize(wm),
                              aReflowState.AvailableSize(wm).BSize(wm));
       nsHTMLReflowMetrics desiredSize(aReflowState);
       nsHTMLReflowState frameState(aPresContext, aReflowState,
                                    frame, availSpace);
       nsReflowStatus frameStatus;
 
       // Reflow
       LogicalPoint pos(wm, prevRect.IStart(wm), 0);
       ReflowChild(frame, aPresContext, desiredSize, frameState,
-                  wm, pos, containerWidth, aFlags, frameStatus, &tracker);
+                  wm, pos, containerSize, aFlags, frameStatus, &tracker);
       //XXXfr Do we need to override any shrinkwrap effects here?
       // e.g. desiredSize.Width() = prevRect.width;
       FinishReflowChild(frame, aPresContext, desiredSize, &frameState,
-                        wm, pos, containerWidth, aFlags);
+                        wm, pos, containerSize, aFlags);
 
       // Handle continuations
       if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
         if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
           // Abspos frames can't cause their parent to be incomplete,
           // only overflow incomplete.
           NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus);
         }
@@ -1316,17 +1316,17 @@ nsContainerFrame::ReflowOverflowContaine
       // but we have some unsplittable frames that, when taller than
       // availableHeight will push zero-height content into a next-in-flow.
     }
     else {
       tracker.Skip(frame, aStatus);
       if (aReflowState.mFloatManager) {
         nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager,
                                        aReflowState.GetWritingMode(),
-                                       aReflowState.ComputedWidth());
+                                       aReflowState.ComputedPhysicalSize());
       }
     }
     ConsiderChildOverflow(aOverflowRects, frame);
   }
 }
 
 void
 nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder*   aBuilder,
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -236,61 +236,61 @@ public:
                   const mozilla::LogicalSize& aPadding,
                   bool aShrinkWrap) override;
 
   /**
    * Positions aChildFrame and its view (if requested), and then calls Reflow().
    * If the reflow status after reflowing the child is FULLY_COMPLETE then any
    * next-in-flows are deleted using DeleteNextInFlowChild().
    *
-   * @param aContainerWidth  width of the border-box of the containing frame
+   * @param aContainerSize  size of the border-box of the containing frame
    *
    * Flags:
    * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
    *    don't want to automatically sync the frame and view
    * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this
    *    case. Also implies NS_FRAME_NO_MOVE_VIEW
    */
   void ReflowChild(nsIFrame*                      aChildFrame,
                    nsPresContext*                 aPresContext,
                    nsHTMLReflowMetrics&           aDesiredSize,
                    const nsHTMLReflowState&       aReflowState,
                    const mozilla::WritingMode&    aWM,
                    const mozilla::LogicalPoint&   aPos,
-                   nscoord                        aContainerWidth,
+                   const nsSize&                  aContainerSize,
                    uint32_t                       aFlags,
                    nsReflowStatus&                aStatus,
                    nsOverflowContinuationTracker* aTracker = nullptr);
 
   /**
    * The second half of frame reflow. Does the following:
    * - sets the frame's bounds
    * - sizes and positions (if requested) the frame's view. If the frame's final
    *   position differs from the current position and the frame itself does not
    *   have a view, then any child frames with views are positioned so they stay
    *   in sync
    * - sets the view's visibility, opacity, content transparency, and clip
    * - invoked the DidReflow() function
    *
-   * @param aContainerWidth  width of the border-box of the containing frame
+   * @param aContainerSize  size of the border-box of the containing frame
    *
    * Flags:
    * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this
    *    case. Also implies NS_FRAME_NO_MOVE_VIEW
    * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
    *    don't want to automatically sync the frame and view
    * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
    */
   static void FinishReflowChild(nsIFrame*                    aKidFrame,
                                 nsPresContext*               aPresContext,
                                 const nsHTMLReflowMetrics&   aDesiredSize,
                                 const nsHTMLReflowState*     aReflowState,
                                 const mozilla::WritingMode&  aWM,
                                 const mozilla::LogicalPoint& aPos,
-                                nscoord                      aContainerWidth,
+                                const nsSize&                aContainerSize,
                                 uint32_t                     aFlags);
 
   //XXX temporary: hold on to a copy of the old physical versions of
   //    ReflowChild and FinishReflowChild so that we can convert callers
   //    incrementally.
   void ReflowChild(nsIFrame*                      aKidFrame,
                    nsPresContext*                 aPresContext,
                    nsHTMLReflowMetrics&           aDesiredSize,
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3819,19 +3819,22 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
   const LogicalSides skipSides =
     GetLogicalSkipSides(&aReflowState) | LogicalSides(eLogicalSideBitsBEnd);
   containerBP.ApplySkipSides(skipSides);
 
   const LogicalPoint containerContentBoxOrigin(flexWM,
                                                containerBP.IStart(flexWM),
                                                containerBP.BStart(flexWM));
 
-  nscoord containerWidth = aAxisTracker.IsMainAxisHorizontal() ?
-                             aContentBoxMainSize : contentBoxCrossSize;
-  containerWidth += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
+  // Determine flex container's border-box size (used in positioning children):
+  LogicalSize logSize =
+    aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize,
+                                                  contentBoxCrossSize);
+  logSize += aReflowState.ComputedLogicalBorderPadding().Size(flexWM);
+  nsSize containerSize = logSize.GetPhysicalSize(flexWM);
 
   // FINAL REFLOW: Give each child frame another chance to reflow, now that
   // we know its final size and position.
   for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
     for (const FlexItem* item = line->GetFirstItem(); item;
          item = item->getNext()) {
       LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint(
                                item->GetMainPosition(),
@@ -3864,23 +3867,23 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
           // relative bsize.
           if (!(item->Frame()->GetStateBits() &
                 NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
             // Item has the correct size (and its children don't care that
             // it's now "definite"). Let's just make sure it's at the right
             // position.
             itemNeedsReflow = false;
             MoveFlexItemToFinalPosition(aReflowState, *item, framePos,
-                                        containerWidth);
+                                        containerSize);
           }
         }
       }
       if (itemNeedsReflow) {
         ReflowFlexItem(aPresContext, aAxisTracker, aReflowState,
-                       *item, framePos, containerWidth);
+                       *item, framePos, containerSize);
       }
 
       // If this is our first child and we haven't established a baseline for
       // the container yet (i.e. if we don't have 'align-self: baseline' on any
       // children), then use this child's baseline as the container's baseline.
       if (item->Frame() == mFrames.FirstChild() &&
           flexContainerAscent == nscoord_MIN) {
         flexContainerAscent = itemNormalBPos + item->ResolvedAscent();
@@ -3955,44 +3958,44 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize)
 }
 
 void
 nsFlexContainerFrame::MoveFlexItemToFinalPosition(
   const nsHTMLReflowState& aReflowState,
   const FlexItem& aItem,
   LogicalPoint& aFramePos,
-  nscoord aContainerWidth)
+  const nsSize& aContainerSize)
 {
   WritingMode outerWM = aReflowState.GetWritingMode();
 
   // If item is relpos, look up its offsets (cached from prev reflow)
   LogicalMargin logicalOffsets(outerWM);
   if (NS_STYLE_POSITION_RELATIVE == aItem.Frame()->StyleDisplay()->mPosition) {
     FrameProperties props = aItem.Frame()->Properties();
     nsMargin* cachedOffsets =
       static_cast<nsMargin*>(props.Get(nsIFrame::ComputedOffsetProperty()));
     MOZ_ASSERT(cachedOffsets,
                "relpos previously-reflowed frame should've cached its offsets");
     logicalOffsets = LogicalMargin(outerWM, *cachedOffsets);
   }
   nsHTMLReflowState::ApplyRelativePositioning(aItem.Frame(), outerWM,
                                               logicalOffsets, &aFramePos,
-                                              aContainerWidth);
-  aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerWidth);
+                                              aContainerSize);
+  aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize);
   PositionChildViews(aItem.Frame());
 }
 
 void
 nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext,
                                      const FlexboxAxisTracker& aAxisTracker,
                                      const nsHTMLReflowState& aReflowState,
                                      const FlexItem& aItem,
                                      LogicalPoint& aFramePos,
-                                     nscoord aContainerWidth)
+                                     const nsSize& aContainerSize)
 {
   WritingMode outerWM = aReflowState.GetWritingMode();
   WritingMode wm = aItem.Frame()->GetWritingMode();
   LogicalSize availSize = aReflowState.ComputedSize(wm);
   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                      aItem.Frame(), availSize);
 
@@ -4047,36 +4050,36 @@ nsFlexContainerFrame::ReflowFlexItem(nsP
   // NOTE: Be very careful about doing anything else with childReflowState
   // after this point, because some of its methods (e.g. SetComputedWidth)
   // internally call InitResizeFlags and stomp on mVResize & mHResize.
 
   nsHTMLReflowMetrics childDesiredSize(childReflowState);
   nsReflowStatus childReflowStatus;
   ReflowChild(aItem.Frame(), aPresContext,
               childDesiredSize, childReflowState,
-              outerWM, aFramePos, aContainerWidth,
+              outerWM, aFramePos, aContainerSize,
               0, childReflowStatus);
 
   // XXXdholbert Once we do pagination / splitting, we'll need to actually
   // handle incomplete childReflowStatuses. But for now, we give our kids
   // unconstrained available height, which means they should always
   // complete.
   MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
              "We gave flex item unconstrained available height, so it "
              "should be complete");
 
   LogicalMargin offsets =
     childReflowState.ComputedLogicalOffsets().ConvertTo(outerWM, wm);
   nsHTMLReflowState::ApplyRelativePositioning(aItem.Frame(), outerWM,
                                               offsets, &aFramePos,
-                                              aContainerWidth);
+                                              aContainerSize);
 
   FinishReflowChild(aItem.Frame(), aPresContext,
                     childDesiredSize, &childReflowState,
-                    outerWM, aFramePos, aContainerWidth, 0);
+                    outerWM, aFramePos, aContainerSize, 0);
 
   // Save the first child's ascent; it may establish container's baseline.
   if (aItem.Frame() == mFrames.FirstChild()) {
     aItem.SetAscent(childDesiredSize.BlockStartAscent());
   }
 }
 
 /* virtual */ nscoord
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -178,40 +178,40 @@ protected:
    * This can be used in cases where we've already done a "measuring reflow"
    * for the flex item at the correct size, and hence can skip its final reflow
    * (but still need to move it to the right final position).
    *
    * @param aReflowState    The flex container's reflow state.
    * @param aItem           The flex item whose frame should be moved.
    * @param aFramePos       The position where the flex item's frame should
    *                        be placed. (pre-relative positioning)
-   * @param aContainerWidth The flex container's width (required by some methods
+   * @param aContainerSize  The flex container's size (required by some methods
    *                        that we call, to interpret aFramePos correctly).
    */
   void MoveFlexItemToFinalPosition(const nsHTMLReflowState& aReflowState,
                                    const FlexItem& aItem,
                                    mozilla::LogicalPoint& aFramePos,
-                                   nscoord aContainerWidth);
+                                   const nsSize& aContainerSize);
   /**
    * Helper-function to reflow a child frame, at its final position determined
    * by flex layout.
    *
    * @param aPresContext    The presentation context being used in reflow.
    * @param aAxisTracker    A FlexboxAxisTracker with the flex container's axes.
    * @param aReflowState    The flex container's reflow state.
    * @param aItem           The flex item to be reflowed.
    * @param aFramePos       The position where the flex item's frame should
    *                        be placed. (pre-relative positioning)
-   * @param aContainerWidth The flex container's width (required by some methods
+   * @param aContainerSize  The flex container's size (required by some methods
    *                        that we call, to interpret aFramePos correctly).
    */
   void ReflowFlexItem(nsPresContext* aPresContext,
                       const FlexboxAxisTracker& aAxisTracker,
                       const nsHTMLReflowState& aReflowState,
                       const FlexItem& aItem,
                       mozilla::LogicalPoint& aFramePos,
-                      nscoord aContainerWidth);
+                      const nsSize& aContainerSize);
 
   bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids
                                    // to satisfy their 'order' values?
 };
 
 #endif /* nsFlexContainerFrame_h___ */
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -117,17 +117,17 @@ void nsFloatManager::Shutdown()
 #define CHECK_BLOCK_DIR(aWM) \
   NS_ASSERTION(aWM.GetBlockDir() == mWritingMode.value.GetBlockDir(), \
   "incompatible writing modes")
 
 nsFlowAreaRect
 nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBOffset,
                             BandInfoType aInfoType, nscoord aBSize,
                             LogicalRect aContentArea, SavedState* aState,
-                            nscoord aContainerWidth) const
+                            const nsSize& aContainerSize) const
 {
   CHECK_BLOCK_DIR(aWM);
   NS_ASSERTION(aBSize >= 0, "unexpected max block size");
   NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
                "unexpected content area inline size");
 
   nscoord blockStart = aBOffset + mBlockStart;
   if (blockStart < nscoord_MIN) {
@@ -164,18 +164,18 @@ nsFloatManager::GetFlowArea(WritingMode 
     blockEnd = nscoord_MAX;
   } else {
     blockEnd = blockStart + aBSize;
     if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
       NS_WARNING("bad value");
       blockEnd = nscoord_MAX;
     }
   }
-  nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerWidth);
-  nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerWidth);
+  nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
+  nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
   if (lineRight < lineLeft) {
     NS_WARNING("bad value");
     lineRight = lineLeft;
   }
 
   // Walk backwards through the floats until we either hit the front of
   // the list or we're above |blockStart|.
   bool haveFloats = false;
@@ -237,34 +237,35 @@ nsFloatManager::GetFlowArea(WritingMode 
         }
       }
     }
   }
 
   nscoord blockSize = (blockEnd == nscoord_MAX) ?
                        nscoord_MAX : (blockEnd - blockStart);
   // convert back from LineLeft/Right to IStart
-  nscoord inlineStart = aWM.IsVertical() || aWM.IsBidiLTR()
-                         ? lineLeft - mLineLeft
-                         : mLineLeft + aContainerWidth - lineRight;
+  nscoord inlineStart = aWM.IsBidiLTR()
+                        ? lineLeft - mLineLeft
+                        : mLineLeft - lineRight +
+                          LogicalSize(aWM, aContainerSize).ISize(aWM);
 
   return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
                         lineRight - lineLeft, blockSize, haveFloats);
 }
 
 nsresult
 nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
-                         WritingMode aWM, nscoord aContainerWidth)
+                         WritingMode aWM, const nsSize& aContainerSize)
 {
   CHECK_BLOCK_DIR(aWM);
   NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
   NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
 
   FloatInfo info(aFloatFrame,
-                 aMarginRect.LineLeft(aWM, aContainerWidth) + mLineLeft,
+                 aMarginRect.LineLeft(aWM, aContainerSize) + mLineLeft,
                  aMarginRect.BStart(aWM) + mBlockStart,
                  aMarginRect.ISize(aWM),
                  aMarginRect.BSize(aWM));
 
   // Set mLeftBEnd and mRightBEnd.
   if (HasAnyFloats()) {
     FloatInfo &tail = mFloats[mFloats.Length() - 1];
     info.mLeftBEnd = tail.mLeftBEnd;
@@ -288,22 +289,22 @@ nsFloatManager::AddFloat(nsIFrame* aFloa
   return NS_OK;
 }
 
 // static
 LogicalRect
 nsFloatManager::CalculateRegionFor(WritingMode          aWM,
                                    nsIFrame*            aFloat,
                                    const LogicalMargin& aMargin,
-                                   nscoord              aContainerWidth)
+                                   const nsSize&        aContainerSize)
 {
   // We consider relatively positioned frames at their original position.
   LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(),
                                  aFloat->GetSize()),
-                     aContainerWidth);
+                     aContainerSize);
 
   // Float region includes its margin
   region.Inflate(aWM, aMargin);
 
   // Don't store rectangles with negative margin-box width or height in
   // the float manager; it can't deal with them.
   if (region.ISize(aWM) < 0) {
     // Preserve the right margin-edge for left floats and the left
@@ -319,33 +320,33 @@ nsFloatManager::CalculateRegionFor(Writi
   }
   return region;
 }
 
 NS_DECLARE_FRAME_PROPERTY(FloatRegionProperty, DeleteValue<nsMargin>)
 
 LogicalRect
 nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
-                             nscoord aContainerWidth)
+                             const nsSize& aContainerSize)
 {
-  LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerWidth);
+  LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
   void* storedRegion = aFloat->Properties().Get(FloatRegionProperty());
   if (storedRegion) {
     nsMargin margin = *static_cast<nsMargin*>(storedRegion);
     region.Inflate(aWM, LogicalMargin(aWM, margin));
   }
   return region;
 }
 
 void
 nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
                                const LogicalRect& aRegion,
-                               nscoord aContainerWidth)
+                               const nsSize& aContainerSize)
 {
-  nsRect region = aRegion.GetPhysicalRect(aWM, aContainerWidth);
+  nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
   nsRect rect = aFloat->GetRect();
   FrameProperties props = aFloat->Properties();
   if (region.IsEqualEdges(rect)) {
     props.Delete(FloatRegionProperty());
   }
   else {
     nsMargin* storedMargin = static_cast<nsMargin*>
       (props.Get(FloatRegionProperty()));
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -54,38 +54,38 @@ public:
 
   /**
    * Get float region stored on the frame. (Defaults to mRect if it's
    * not there.) The float region is the area impacted by this float;
    * the coordinates are relative to the containing block frame.
    */
   static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM,
                                            nsIFrame* aFloatFrame,
-                                           nscoord aContainerWidth);
+                                           const nsSize& aContainerSize);
   /**
    * Calculate the float region for this frame using aMargin and the
    * frame's mRect. The region includes the margins around the float,
    * but doesn't include the relative offsets.
    * Note that if the frame is or has a continuation, aMargin's top
    * and/or bottom must be zeroed by the caller.
    */
   static mozilla::LogicalRect CalculateRegionFor(
                                 mozilla::WritingMode aWM,
                                 nsIFrame* aFloatFrame,
                                 const mozilla::LogicalMargin& aMargin,
-                                nscoord aContainerWidth);
+                                const nsSize& aContainerSize);
   /**
    * Store the float region on the frame. The region is stored
    * as a delta against the mRect, so repositioning the frame will
    * also reposition the float region.
    */
   static void StoreRegionFor(mozilla::WritingMode aWM,
                              nsIFrame* aFloat,
                              const mozilla::LogicalRect& aRegion,
-                             nscoord aContainerWidth);
+                             const nsSize& aContainerSize);
 
   // Structure that stores the current state of a frame manager for
   // Save/Restore purposes.
   struct SavedState {
     explicit SavedState() {}
   private:
     uint32_t mFloatInfoCount;
     nscoord mLineLeft, mBlockStart;
@@ -157,29 +157,30 @@ public:
    *            inline size at all (because they are entirely in the margins)
    *
    * aBCoord and aAvailSpace are positioned relative to the current translation
    */
   enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT };
   nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM,
                              nscoord aBCoord, BandInfoType aInfoType,
                              nscoord aBSize, mozilla::LogicalRect aContentArea,
-                             SavedState* aState, nscoord mContainerWidth) const;
+                             SavedState* aState,
+                             const nsSize& aContainerSize) const;
 
   /**
    * Add a float that comes after all floats previously added.  Its
    * block start must be even with or below the top of all previous
    * floats.
    *
    * aMarginRect is relative to the current translation.  The caller
    * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0.
    */
   nsresult AddFloat(nsIFrame* aFloatFrame,
                     const mozilla::LogicalRect& aMarginRect,
-                    mozilla::WritingMode aWM, nscoord aContainerWidth);
+                    mozilla::WritingMode aWM, const nsSize& aContainerSize);
 
   /**
    * Notify that we tried to place a float that could not fit at all and
    * had to be pushed to the next page/column?  (If so, we can't place
    * any more floats in this page/column because of the rule that the
    * top of a float cannot be above the top of an earlier float.  It
    * also means that any clear needs to continue to the next column.)
    */
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3550,29 +3550,29 @@ static FrameTarget GetSelectionClosestFr
 {
   nsIFrame *frame = aLine->mFirstChild;
   // Account for end of lines (any iterator from the block is valid)
   if (aLine == aParent->end_lines())
     return DrillDownToSelectionFrame(aParent, true, aFlags);
   nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr;
   nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
   WritingMode wm = aLine->mWritingMode;
-  LogicalPoint pt(wm, aPoint, aLine->mContainerWidth);
+  LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
   bool canSkipBr = false;
   for (int32_t n = aLine->GetChildCount(); n;
        --n, frame = frame->GetNextSibling()) {
     // Skip brFrames. Can only skip if the line contains at least
     // one selectable and non-empty frame before
     if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
         (canSkipBr && frame->GetType() == nsGkAtoms::brFrame)) {
       continue;
     }
     canSkipBr = true;
     LogicalRect frameRect = LogicalRect(wm, frame->GetRect(),
-                                        aLine->mContainerWidth);
+                                        aLine->mContainerSize);
     if (pt.I(wm) >= frameRect.IStart(wm)) {
       if (pt.I(wm) < frameRect.IEnd(wm)) {
         return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
       }
       if (frameRect.IEnd(wm) >= closestIStart) {
         closestFromIStart = frame;
         closestIStart = frameRect.IEnd(wm);
       }
@@ -3620,17 +3620,17 @@ static FrameTarget GetSelectionClosestFr
       return FrameTarget(aFrame, false, false, true);
     }
     return FrameTarget::Null();
   }
   nsBlockFrame::line_iterator curLine = firstLine;
   nsBlockFrame::line_iterator closestLine = end;
   // Convert aPoint into a LogicalPoint in the writing-mode of this block
   WritingMode wm = curLine->mWritingMode;
-  LogicalPoint pt(wm, aPoint, curLine->mContainerWidth);
+  LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
   while (curLine != end) {
     // Check to see if our point lies within the line's block-direction bounds
     nscoord BCoord = pt.B(wm) - curLine->BStart();
     nscoord BSize = curLine->BSize();
     if (BCoord >= 0 && BCoord < BSize) {
       closestLine = curLine;
       break; // We found the line; stop looking
     }
@@ -5676,24 +5676,26 @@ nsIFrame::ListGeneric(nsACString& aTo, c
                            wm.IsBidiLTR() ? "ltr" : "rtl",
                            ISize(), BSize());
   }
 
   nsIFrame* parent = GetParent();
   if (parent) {
     WritingMode pWM = parent->GetWritingMode();
     if (pWM.IsVertical() || !pWM.IsBidiLTR()) {
-      nscoord cw = parent->mRect.width;
-      LogicalRect lr(pWM, mRect, cw);
-      aTo += nsPrintfCString(" parent wm=%s-%s,width=%d,logicalRect={%d,%d,%d,%d}",
+      nsSize containerSize = parent->mRect.Size();
+      LogicalRect lr(pWM, mRect, containerSize);
+      aTo += nsPrintfCString(" parent wm=%s-%s, cs={%d,%d}, "
+                             " logicalRect={%d,%d,%d,%d}",
                              pWM.IsVertical() ? pWM.IsVerticalLR()
                                                 ? "vlr" : "vrl"
                                               : "htb",
                              wm.IsBidiLTR() ? "ltr" : "rtl",
-                             cw, lr.IStart(pWM), lr.BStart(pWM),
+                             containerSize.width, containerSize.height,
+                             lr.IStart(pWM), lr.BStart(pWM),
                              lr.ISize(pWM), lr.BSize(pWM));
     }
   }
   nsIFrame* f = const_cast<nsIFrame*>(this);
   if (f->HasOverflowAreas()) {
     nsRect vo = f->GetVisualOverflowRect();
     if (!vo.IsEqualEdges(mRect)) {
       aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -509,33 +509,35 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S
   // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to
   // reflect our assumptions while we reflow the child.
   bool didHaveHorizontalScrollbar = mHelper.mHasHorizontalScrollbar;
   bool didHaveVerticalScrollbar = mHelper.mHasVerticalScrollbar;
   mHelper.mHasHorizontalScrollbar = aAssumeHScroll;
   mHelper.mHasVerticalScrollbar = aAssumeVScroll;
 
   nsReflowStatus status;
-  // No need to pass a container-width to ReflowChild or
+  // No need to pass a true container-size to ReflowChild or
   // FinishReflowChild, because it's only used there when positioning
   // the frame (i.e. if NS_FRAME_NO_MOVE_FRAME isn't set)
+  const nsSize dummyContainerSize;
   ReflowChild(mHelper.mScrolledFrame, presContext, *aMetrics,
-              kidReflowState, wm, LogicalPoint(wm), 0,
+              kidReflowState, wm, LogicalPoint(wm), dummyContainerSize,
               NS_FRAME_NO_MOVE_FRAME, status);
 
   mHelper.mHasHorizontalScrollbar = didHaveHorizontalScrollbar;
   mHelper.mHasVerticalScrollbar = didHaveVerticalScrollbar;
 
   // Don't resize or position the view (if any) because we're going to resize
   // it to the correct size anyway in PlaceScrollArea. Allowing it to
   // resize here would size it to the natural height of the frame,
   // which will usually be different from the scrollport height;
   // invalidating the difference will cause unnecessary repainting.
   FinishReflowChild(mHelper.mScrolledFrame, presContext,
-                    *aMetrics, &kidReflowState, wm, LogicalPoint(wm), 0,
+                    *aMetrics, &kidReflowState, wm, LogicalPoint(wm),
+                    dummyContainerSize,
                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
 
   // XXX Some frames (e.g., nsPluginFrame, nsFrameFrame, nsTextFrame) don't bother
   // setting their mOverflowArea. This is wrong because every frame should
   // always set mOverflowArea. In fact nsPluginFrame and nsFrameFrame don't
   // support the 'outline' property because of this. Rather than fix the world
   // right now, just fix up the overflow area if necessary. Note that we don't
   // check HasOverflowRect() because it could be set even though the
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1241,18 +1241,19 @@ nsGridContainerFrame::ReflowChildren(Gri
                                      const nsTArray<TrackSize>& aColSizes,
                                      const nsTArray<TrackSize>& aRowSizes,
                                      nsHTMLReflowMetrics&       aDesiredSize,
                                      const nsHTMLReflowState&   aReflowState,
                                      nsReflowStatus&            aStatus)
 {
   WritingMode wm = aReflowState.GetWritingMode();
   const LogicalPoint gridOrigin(aContentArea.Origin(wm));
-  const nscoord containerWidth = aContentArea.Width(wm) +
-    aReflowState.ComputedPhysicalBorderPadding().LeftRight();
+  const nsSize containerSize =
+    (aContentArea.Size(wm) +
+     aReflowState.ComputedLogicalBorderPadding().Size(wm)).GetPhysicalSize(wm);
   nsPresContext* pc = PresContext();
   for (; !aIter.AtEnd(); aIter.Next()) {
     nsIFrame* child = *aIter;
     const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame;
     LogicalRect cb(wm);
     if (MOZ_LIKELY(isGridItem)) {
       GridArea* area = GetGridAreaForChild(child);
       MOZ_ASSERT(area && area->IsDefinite());
@@ -1270,30 +1271,32 @@ nsGridContainerFrame::ReflowChildren(Gri
       // clamping though, and check the prop value is actually 'stretch'!
       LogicalMargin bp = childRS.ComputedLogicalBorderPadding();
       bp.ApplySkipSides(child->GetLogicalSkipSides());
       nscoord bSize = childCBSize.BSize(childWM) - bp.BStartEnd(childWM) -
                         margin.BStartEnd(childWM);
       childRS.SetComputedBSize(std::max(bSize, 0));
     }
     // We need the width of the child before we can correctly convert
-    // the writing-mode of its origin, so we reflow at (0, 0) and then
-    // pass the correct position to FinishReflowChild.
+    // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
+    // containerSize, and then pass the correct position to FinishReflowChild.
     nsHTMLReflowMetrics childSize(childRS);
     nsReflowStatus childStatus;
+    const nsSize dummyContainerSize;
     ReflowChild(child, pc, childSize, childRS, childWM, LogicalPoint(childWM),
-                0, 0, childStatus);
+                dummyContainerSize, 0, childStatus);
     LogicalPoint childPos =
-      cb.Origin(wm).ConvertTo(childWM, wm, containerWidth - childSize.Width() -
-                                           margin.LeftRight(childWM));
+      cb.Origin(wm).ConvertTo(childWM, wm,
+                              containerSize - childSize.PhysicalSize() -
+                              margin.Size(childWM).GetPhysicalSize(childWM));
     childPos.I(childWM) += margin.IStart(childWM);
     childPos.B(childWM) += margin.BStart(childWM);
-    childRS.ApplyRelativePositioning(&childPos, containerWidth);
+    childRS.ApplyRelativePositioning(&childPos, containerSize);
     FinishReflowChild(child, pc, childSize, &childRS, childWM, childPos,
-                      containerWidth, 0);
+                      containerSize, 0);
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
     // XXX deal with 'childStatus' not being COMPLETE
   }
 
   if (IsAbsoluteContainer()) {
     nsFrameList children(GetChildList(GetAbsoluteListID()));
     if (!children.IsEmpty()) {
       LogicalMargin pad(aReflowState.ComputedLogicalPadding());
@@ -1313,17 +1316,17 @@ nsGridContainerFrame::ReflowChildren(Gri
                                                     gridOrigin, gridCB));
         // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
         nsRect* cb = static_cast<nsRect*>(child->Properties().Get(
                        GridItemContainingBlockRect()));
         if (!cb) {
           cb = new nsRect;
           child->Properties().Set(GridItemContainingBlockRect(), cb);
         }
-        *cb = itemCB.GetPhysicalRect(wm, containerWidth);
+        *cb = itemCB.GetPhysicalRect(wm, containerSize);
       }
       // This rect isn't used at all for layout so we use it to optimize
       // away the virtual GetType() call in the callee in most cases.
       // @see nsAbsoluteContainingBlock::Reflow
       nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0);
       GetAbsoluteContainingBlock()->Reflow(this, pc, aReflowState, aStatus,
                                            dummyRect, true,
                                            true, true, // XXX could be optimized
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -267,16 +267,21 @@ public:
   nscoord BlockStartAscent() const
   {
     return mBlockStartAscent;
   }
 
   nscoord& Width() { return mWritingMode.IsVertical() ? mBSize : mISize; }
   nscoord& Height() { return mWritingMode.IsVertical() ? mISize : mBSize; }
 
+  nsSize PhysicalSize()
+  {
+    return Size(mWritingMode).GetPhysicalSize(mWritingMode);
+  }
+
   void SetBlockStartAscent(nscoord aAscent)
   {
     mBlockStartAscent = aAscent;
   }
 
   enum { ASK_FOR_BASELINE = nscoord_MAX };
 
   // Metrics that _exactly_ enclose the text to allow precise MathML placements.
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -1260,54 +1260,57 @@ nsHTMLReflowState::CalculateHypothetical
     }
   }
 
   // Get the placeholder x-offset and y-offset in the coordinate
   // space of its containing block
   // XXXbz the placeholder is not fully reflowed yet if our containing block is
   // relatively positioned...
   WritingMode cbwm = cbrs->GetWritingMode();
-  nscoord containerWidth = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW
-    ? cbrs->ComputedWidth() +
-      cbrs->ComputedLogicalBorderPadding().LeftRight(cbwm)
-    : containingBlock->GetSize().width;
+  nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW
+    ? cbrs->ComputedSizeAsContainerIfConstrained()
+    : containingBlock->GetSize();
   LogicalPoint placeholderOffset(wm, aPlaceholderFrame->GetOffsetTo(containingBlock),
-                                 containerWidth);
+                                 containerSize);
 
   // XXX hack to correct for lack of LogicalPoint bidi support in vertical mode
   if (wm.IsVertical() && !wm.IsBidiLTR()) {
     placeholderOffset.I(wm) = cbrs->ComputedHeight() +
       cbrs->ComputedLogicalBorderPadding().TopBottom(cbwm) -
       placeholderOffset.I(wm);
   }
 
   // First, determine the hypothetical box's mBStart.  We want to check the
   // content insertion frame of containingBlock for block-ness, but make
   // sure to compute all coordinates in the coordinate system of
   // containingBlock.
   nsBlockFrame* blockFrame =
     nsLayoutUtils::GetAsBlock(containingBlock->GetContentInsertionFrame());
   if (blockFrame) {
-    LogicalPoint blockOffset(wm, blockFrame->GetOffsetTo(containingBlock), 0);
+    // Use a null containerSize to convert a LogicalPoint functioning as a
+    // vector into a physical nsPoint vector.
+    const nsSize nullContainerSize;
+    LogicalPoint blockOffset(wm, blockFrame->GetOffsetTo(containingBlock),
+                             nullContainerSize);
     bool isValid;
     nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
     if (!isValid) {
       // Give up.  We're probably dealing with somebody using
       // position:absolute inside native-anonymous content anyway.
       aHypotheticalBox.mBStart = placeholderOffset.B(wm);
     } else {
       NS_ASSERTION(iter.GetContainer() == blockFrame,
                    "Found placeholder in wrong block!");
       nsBlockFrame::line_iterator lineBox = iter.GetLine();
 
       // How we determine the hypothetical box depends on whether the element
       // would have been inline-level or block-level
       LogicalRect lineBounds =
         lineBox->GetBounds().ConvertTo(wm, lineBox->mWritingMode,
-                                       lineBox->mContainerWidth);
+                                       lineBox->mContainerSize);
       if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
         // Use the block-start of the inline box which the placeholder lives in
         // as the hypothetical box's block-start.
         aHypotheticalBox.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm);
       } else {
         // The element would have been block-level which means it would
         // be below the line containing the placeholder frame, unless
         // all the frames before it are empty.  In that case, it would
@@ -1410,19 +1413,20 @@ nsHTMLReflowState::CalculateHypothetical
     } while (containingBlock != cbrs->frame);
   } else {
     // XXXldb We need to either ignore scrolling for the absolute
     // positioning case too (and take the incompatibility) or figure out
     // how to make these positioned elements actually *move* when we
     // scroll, and thus avoid the resulting incremental reflow bugs.
     cbOffset = containingBlock->GetOffsetTo(cbrs->frame);
   }
-  nscoord cbrsWidth = cbrs->ComputedWidth() +
-                        cbrs->ComputedLogicalBorderPadding().LeftRight(cbwm);
-  LogicalPoint logCBOffs(wm, cbOffset, cbrsWidth - containerWidth);
+  nsSize cbrsSize =
+    cbrs->ComputedPhysicalSize() +
+    cbrs->ComputedLogicalBorderPadding().Size(cbwm).GetPhysicalSize(cbwm);
+  LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize);
   aHypotheticalBox.mIStart += logCBOffs.I(wm);
   aHypotheticalBox.mIEnd += logCBOffs.I(wm);
   aHypotheticalBox.mBStart += logCBOffs.B(wm);
 
   // The specified offsets are relative to the absolute containing block's
   // padding edge and our current values are relative to the border edge, so
   // translate.
   LogicalMargin border =
@@ -1936,21 +1940,21 @@ nsHTMLReflowState::ComputeContainingBloc
       // that's very hard.
 
       LogicalMargin computedBorder =
         aContainingBlockRS->ComputedLogicalBorderPadding() -
         aContainingBlockRS->ComputedLogicalPadding();
       cbSize.ISize(wm) = aContainingBlockRS->frame->ISize(wm) -
                          computedBorder.IStartEnd(wm);
       NS_ASSERTION(cbSize.ISize(wm) >= 0,
-                   "Negative containing block width!");
+                   "Negative containing block isize!");
       cbSize.BSize(wm) = aContainingBlockRS->frame->BSize(wm) -
                          computedBorder.BStartEnd(wm);
       NS_ASSERTION(cbSize.BSize(wm) >= 0,
-                   "Negative containing block height!");
+                   "Negative containing block bsize!");
     } else {
       // If the ancestor is block-level, the containing block is formed by the
       // padding edge of the ancestor
       cbSize.ISize(wm) +=
         aContainingBlockRS->ComputedLogicalPadding().IStartEnd(wm);
       cbSize.BSize(wm) +=
         aContainingBlockRS->ComputedLogicalPadding().BStartEnd(wm);
     }
@@ -2175,21 +2179,22 @@ nsHTMLReflowState::InitConstraints(nsPre
       // Initialize offsets to 0
       ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
     }
 
     // Calculate the computed values for min and max properties.  Note that
     // this MUST come after we've computed our border and padding.
     ComputeMinMaxValues(cbSize);
 
-    // Calculate the computed width and blockSize. This varies by frame type
+    // Calculate the computed inlineSize and blockSize.
+    // This varies by frame type.
 
     if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
       // Internal table elements. The rules vary depending on the type.
-      // Calculate the computed width
+      // Calculate the computed isize
       bool rowOrRowGroup = false;
       const nsStyleCoord &inlineSize = mStylePosition->ISize(wm);
       nsStyleUnit inlineSizeUnit = inlineSize.GetUnit();
       if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
           (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
         // 'inlineSize' property doesn't apply to table rows and row groups
         inlineSizeUnit = eStyleUnit_Auto;
         rowOrRowGroup = true;
@@ -2202,21 +2207,21 @@ nsHTMLReflowState::InitConstraints(nsPre
 
         if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
           // Internal table elements don't have margins. Only tables and
           // cells have border and padding
           ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm);
           if (ComputedISize() < 0)
             ComputedISize() = 0;
         }
-        NS_ASSERTION(ComputedISize() >= 0, "Bogus computed width");
+        NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize");
 
       } else {
         NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(),
-                     "unexpected width unit change");
+                     "unexpected inline size unit change");
         ComputedISize() = ComputeISizeValue(cbSize.ISize(wm),
                                             mStylePosition->mBoxSizing,
                                             inlineSize);
       }
 
       // Calculate the computed block size
       if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
           (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
--- a/layout/generic/nsHTMLReflowState.h
+++ b/layout/generic/nsHTMLReflowState.h
@@ -431,27 +431,43 @@ struct nsHTMLReflowState : public nsCSSO
   }
 
   mozilla::LogicalSize
   ComputedSizeWithMarginBorderPadding(mozilla::WritingMode aWM) const {
     return ComputedSizeWithMarginBorderPadding().ConvertTo(aWM,
                                                            GetWritingMode());
   }
 
+  nsSize
+  ComputedPhysicalSize() const {
+    return nsSize(ComputedWidth(), ComputedHeight());
+  }
+
   // XXX this will need to change when we make mComputedOffsets logical;
   // we won't be able to return a reference for the physical offsets
   const nsMargin& ComputedPhysicalOffsets() const { return mComputedOffsets; }
   nsMargin& ComputedPhysicalOffsets() { return mComputedOffsets; }
 
   const LogicalMargin ComputedLogicalOffsets() const
     { return LogicalMargin(mWritingMode, mComputedOffsets); }
 
   void SetComputedLogicalOffsets(const LogicalMargin& aOffsets)
     { mComputedOffsets = aOffsets.GetPhysicalMargin(mWritingMode); }
 
+  // Return the state's computed size including border-padding, with
+  // unconstrained dimensions replaced by zero.
+  nsSize ComputedSizeAsContainerIfConstrained() const {
+    const nscoord wd = ComputedWidth();
+    const nscoord ht = ComputedHeight();
+    return nsSize(wd == NS_UNCONSTRAINEDSIZE
+                  ? 0 : wd + ComputedPhysicalBorderPadding().LeftRight(),
+                  ht == NS_UNCONSTRAINEDSIZE
+                  ? 0 : ht + ComputedPhysicalBorderPadding().TopBottom());
+  }
+
 private:
   // the available width in which to reflow the frame. The space
   // represents the amount of room for the frame's margin, border,
   // padding, and content area. The frame size you choose should fit
   // within the available width.
   nscoord              mAvailableWidth;
 
   // A value of NS_UNCONSTRAINEDSIZE for the available height means
@@ -834,37 +850,37 @@ public:
     ApplyRelativePositioning(frame, ComputedPhysicalOffsets(), aPosition);
   }
 
   static void
   ApplyRelativePositioning(nsIFrame* aFrame,
                            mozilla::WritingMode aWritingMode,
                            const mozilla::LogicalMargin& aComputedOffsets,
                            mozilla::LogicalPoint* aPosition,
-                           nscoord aContainerWidth) {
-    // Subtract the width of the frame from the container width that we
+                           const nsSize& aContainerSize) {
+    // Subtract the size of the frame from the container size that we
     // use for converting between the logical and physical origins of
     // the frame. This accounts for the fact that logical origins in RTL
     // coordinate systems are at the top right of the frame instead of
     // the top left.
-    nscoord frameWidth = aFrame->GetSize().width;
+    nsSize frameSize = aFrame->GetSize();
     nsPoint pos = aPosition->GetPhysicalPoint(aWritingMode,
-                                              aContainerWidth - frameWidth);
+                                              aContainerSize - frameSize);
     ApplyRelativePositioning(aFrame,
                              aComputedOffsets.GetPhysicalMargin(aWritingMode),
                              &pos);
     *aPosition = mozilla::LogicalPoint(aWritingMode, pos,
-                                       aContainerWidth - frameWidth);
+                                       aContainerSize - frameSize);
   }
 
   void ApplyRelativePositioning(mozilla::LogicalPoint* aPosition,
-                                nscoord aContainerWidth) const {
+                                const nsSize& aContainerSize) const {
     ApplyRelativePositioning(frame, mWritingMode,
                              ComputedLogicalOffsets(), aPosition,
-                             aContainerWidth);
+                             aContainerSize);
   }
 
 #ifdef DEBUG
   // Reflow trace methods.  Defined in nsFrame.cpp so they have access
   // to the display-reflow infrastructure.
   static void* DisplayInitConstraintsEnter(nsIFrame* aFrame,
                                            nsHTMLReflowState* aState,
                                            nscoord aCBISize,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -654,49 +654,49 @@ public:
   nsSize GetSize() const { return mRect.Size(); }
   nsRect GetRectRelativeToSelf() const {
     return nsRect(nsPoint(0, 0), mRect.Size());
   }
   /**
    * Dimensions and position in logical coordinates in the frame's writing mode
    *  or another writing mode
    */
-  mozilla::LogicalRect GetLogicalRect(nscoord aContainerWidth) const {
-    return GetLogicalRect(GetWritingMode(), aContainerWidth);
+  mozilla::LogicalRect GetLogicalRect(const nsSize& aContainerSize) const {
+    return GetLogicalRect(GetWritingMode(), aContainerSize);
   }
-  mozilla::LogicalPoint GetLogicalPosition(nscoord aContainerWidth) const {
-    return GetLogicalPosition(GetWritingMode(), aContainerWidth);
+  mozilla::LogicalPoint GetLogicalPosition(const nsSize& aContainerSize) const {
+    return GetLogicalPosition(GetWritingMode(), aContainerSize);
   }
   mozilla::LogicalSize GetLogicalSize() const {
     return GetLogicalSize(GetWritingMode());
   }
   mozilla::LogicalRect GetLogicalRect(mozilla::WritingMode aWritingMode,
-                                      nscoord aContainerWidth) const {
-    return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerWidth);
+                                      const nsSize& aContainerSize) const {
+    return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerSize);
   }
   mozilla::LogicalPoint GetLogicalPosition(mozilla::WritingMode aWritingMode,
-                                           nscoord aContainerWidth) const {
-    return GetLogicalRect(aWritingMode, aContainerWidth).Origin(aWritingMode);
+                                           const nsSize& aContainerSize) const {
+    return GetLogicalRect(aWritingMode, aContainerSize).Origin(aWritingMode);
   }
   mozilla::LogicalSize GetLogicalSize(mozilla::WritingMode aWritingMode) const {
     return mozilla::LogicalSize(aWritingMode, GetSize());
   }
-  nscoord IStart(nscoord aContainerWidth) const {
-    return IStart(GetWritingMode(), aContainerWidth);
+  nscoord IStart(const nsSize& aContainerSize) const {
+    return IStart(GetWritingMode(), aContainerSize);
   }
   nscoord IStart(mozilla::WritingMode aWritingMode,
-                 nscoord aContainerWidth) const {
-    return GetLogicalPosition(aWritingMode, aContainerWidth).I(aWritingMode);
+                 const nsSize& aContainerSize) const {
+    return GetLogicalPosition(aWritingMode, aContainerSize).I(aWritingMode);
   }
-  nscoord BStart(nscoord aContainerWidth) const {
-    return BStart(GetWritingMode(), aContainerWidth);
+  nscoord BStart(const nsSize& aContainerSize) const {
+    return BStart(GetWritingMode(), aContainerSize);
   }
   nscoord BStart(mozilla::WritingMode aWritingMode,
-                 nscoord aContainerWidth) const {
-    return GetLogicalPosition(aWritingMode, aContainerWidth).B(aWritingMode);
+                 const nsSize& aContainerSize) const {
+    return GetLogicalPosition(aWritingMode, aContainerSize).B(aWritingMode);
   }
   nscoord ISize() const { return ISize(GetWritingMode()); }
   nscoord ISize(mozilla::WritingMode aWritingMode) const {
     return GetLogicalSize(aWritingMode).ISize(aWritingMode);
   }
   nscoord BSize() const { return BSize(GetWritingMode()); }
   nscoord BSize(mozilla::WritingMode aWritingMode) const {
     return GetLogicalSize(aWritingMode).BSize(aWritingMode);
@@ -716,27 +716,28 @@ public:
       SetOverflowAreas(overflow);
     } else {
       mRect = aRect;
     }
   }
   /**
    * Set this frame's rect from a logical rect in its own writing direction
    */
-  void SetRect(const mozilla::LogicalRect& aRect, nscoord aContainerWidth) {
-    SetRect(GetWritingMode(), aRect, aContainerWidth);
+  void SetRect(const mozilla::LogicalRect& aRect,
+               const nsSize& aContainerSize) {
+    SetRect(GetWritingMode(), aRect, aContainerSize);
   }
   /**
    * Set this frame's rect from a logical rect in a different writing direction
    * (GetPhysicalRect will assert if the writing mode doesn't match)
    */
   void SetRect(mozilla::WritingMode aWritingMode,
                const mozilla::LogicalRect& aRect,
-               nscoord aContainerWidth) {
-    SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerWidth));
+               const nsSize& aContainerSize) {
+    SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerSize));
   }
 
   /**
    * Set this frame's size from a logical size in its own writing direction.
    * This leaves the frame's logical position unchanged, which means its
    * physical position may change (for right-to-left modes).
    */
   void SetSize(const mozilla::LogicalSize& aSize) {
@@ -766,22 +767,22 @@ public:
    */
   void SetSize(const nsSize& aSize) {
     SetRect(nsRect(mRect.TopLeft(), aSize));
   }
 
   void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); }
   void SetPosition(mozilla::WritingMode aWritingMode,
                    const mozilla::LogicalPoint& aPt,
-                   nscoord aContainerWidth) {
-    // We subtract mRect.width from the container width to account for
+                   const nsSize& aContainerSize) {
+    // We subtract mRect.Size() from the container size to account for
     // the fact that logical origins in RTL coordinate systems are at
     // the top right of the frame instead of the top left.
     mRect.MoveTo(aPt.GetPhysicalPoint(aWritingMode,
-                                      aContainerWidth - mRect.width));
+                                      aContainerSize - mRect.Size()));
   }
 
   /**
    * Move the frame, accounting for relative positioning. Use this when
    * adjusting the frame's position by a known amount, to properly update its
    * saved normal position (see GetNormalPosition below).
    *
    * This must be used only when moving a frame *after*
@@ -793,38 +794,43 @@ public:
   void MovePositionBy(const nsPoint& aTranslation);
 
   /**
    * As above, using a logical-point delta in a given writing mode.
    */
   void MovePositionBy(mozilla::WritingMode aWritingMode,
                       const mozilla::LogicalPoint& aTranslation)
   {
-    MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode, 0));
+    // The LogicalPoint represents a vector rather than a point within a
+    // rectangular coordinate space, so we use a null containerSize when
+    // converting logical to physical.
+    const nsSize nullContainerSize;
+    MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode,
+                                                 nullContainerSize));
   }
 
   /**
    * Return frame's rect without relative positioning
    */
   nsRect GetNormalRect() const;
 
   /**
    * Return frame's position without relative positioning
    */
   nsPoint GetNormalPosition() const;
   mozilla::LogicalPoint
   GetLogicalNormalPosition(mozilla::WritingMode aWritingMode,
-                           nscoord aContainerWidth) const
+                           const nsSize& aContainerSize) const
   {
-    // Subtract the width of this frame from the container width to get
+    // Subtract the size of this frame from the container size to get
     // the correct position in rtl frames where the origin is on the
     // right instead of the left
     return mozilla::LogicalPoint(aWritingMode,
                                  GetNormalPosition(),
-                                 aContainerWidth - mRect.width);
+                                 aContainerSize - mRect.Size());
   }
 
   virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
   { return aChild->GetPosition(); }
   
   nsPoint GetPositionIgnoringScrolling();
 
   static void DestroyContentArray(void* aPropertyValue);
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -27,17 +27,17 @@ int32_t nsLineBox::GetCtorCount() { retu
 // static nsLineBox constant; initialized in the header file.
 const uint32_t nsLineBox::kMinChildCountForHashtable;
 #endif
 
 using namespace mozilla;
 
 nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
   : mFirstChild(aFrame)
-  , mContainerWidth(-1)
+  , mContainerSize(-1, -1)
   , mBounds(WritingMode()) // mBounds will be initialized with the correct
                            // writing mode when it is set
 // NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
 {
   MOZ_COUNT_CTOR(nsLineBox);
 #ifdef DEBUG
   ++ctorCount;
   NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
@@ -75,17 +75,17 @@ NS_NewLineBox(nsIPresShell* aPresShell, 
 }
 
 nsLineBox*
 NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
               nsIFrame* aFrame, int32_t aCount)
 {
   nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
   newLine->NoteFramesMovedFrom(aFromLine);
-  newLine->mContainerWidth = aFromLine->mContainerWidth;
+  newLine->mContainerSize = aFromLine->mContainerSize;
   return newLine;
 }
 
 void
 nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
 {
   MOZ_ASSERT(!mFlags.mHasHashedFrames);
   MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
@@ -244,23 +244,23 @@ nsLineBox::List(FILE* out, const char* a
           StateToString(cbuf, sizeof(cbuf)));
   if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
     str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get());
   }
   nsRect bounds = GetPhysicalBounds();
   str += nsPrintfCString("{%d,%d,%d,%d} ",
           bounds.x, bounds.y, bounds.width, bounds.height);
   if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) {
-    str += nsPrintfCString("{%s-%s: %d,%d,%d,%d; cw=%d} ",
+    str += nsPrintfCString("{%s-%s: %d,%d,%d,%d; cs=%d,%d} ",
                            mWritingMode.IsVertical()
                              ? mWritingMode.IsVerticalLR() ? "vlr" : "vrl"
                              : "htb",
                            mWritingMode.IsBidiLTR() ? "ltr" : "rtl",
                            IStart(), BStart(), ISize(), BSize(),
-                           mContainerWidth);
+                           mContainerSize.width, mContainerSize.height);
   }
   if (mData &&
       (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
        !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
     str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
             mData->mOverflowAreas.VisualOverflow().x,
             mData->mOverflowAreas.VisualOverflow().y,
             mData->mOverflowAreas.VisualOverflow().width,
@@ -732,37 +732,39 @@ nsLineIterator::FindFrameAt(int32_t aLin
   if (line->ISize() == 0 && line->BSize() == 0)
     return NS_ERROR_FAILURE;
 
   nsIFrame* frame = line->mFirstChild;
   nsIFrame* closestFromStart = nullptr;
   nsIFrame* closestFromEnd = nullptr;
 
   WritingMode wm = line->mWritingMode;
-  nscoord cw = line->mContainerWidth;
+  nsSize containerSize = line->mContainerSize;
 
-  LogicalPoint pos(wm, aPos, cw);
+  LogicalPoint pos(wm, aPos, containerSize);
 
   int32_t n = line->GetChildCount();
   while (n--) {
-    LogicalRect rect = frame->GetLogicalRect(wm, cw);
+    LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
     if (rect.ISize(wm) > 0) {
       // If pos.I() is inside this frame - this is it
       if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
         closestFromStart = closestFromEnd = frame;
         break;
       }
       if (rect.IStart(wm) < pos.I(wm)) {
         if (!closestFromStart || 
-            rect.IEnd(wm) > closestFromStart->GetLogicalRect(wm, cw).IEnd(wm))
+            rect.IEnd(wm) > closestFromStart->
+                              GetLogicalRect(wm, containerSize).IEnd(wm))
           closestFromStart = frame;
       }
       else {
         if (!closestFromEnd ||
-            rect.IStart(wm) < closestFromEnd->GetLogicalRect(wm, cw).IStart(wm))
+            rect.IStart(wm) < closestFromEnd->
+                                GetLogicalRect(wm, containerSize).IStart(wm))
           closestFromEnd = frame;
       }
     }
     frame = frame->GetNextSibling();
   }
   if (!closestFromStart && !closestFromEnd) {
     // All frames were zero-width. Just take the first one.
     closestFromStart = closestFromEnd = line->mFirstChild;
@@ -774,22 +776,25 @@ nsLineIterator::FindFrameAt(int32_t aLin
   }
   else if (!closestFromStart) {
     *aFrameFound = closestFromEnd;
   }
   else if (!closestFromEnd) {
     *aFrameFound = closestFromStart;
   }
   else { // we're between two frames
-    nscoord delta = closestFromEnd->GetLogicalRect(wm, cw).IStart(wm) -
-                    closestFromStart->GetLogicalRect(wm, cw).IEnd(wm);
-    if (pos.I(wm) < closestFromStart->GetLogicalRect(wm, cw).IEnd(wm) + delta/2)
+    nscoord delta =
+      closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
+      closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
+    if (pos.I(wm) < closestFromStart->
+                      GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
       *aFrameFound = closestFromStart;
-    else
+    } else {
       *aFrameFound = closestFromEnd;
+    }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
 {
   aFrame = aFrame->GetNextSibling();
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -443,80 +443,85 @@ public:
 
   // Combined area is the area of the line that should influence the
   // overflow area of its parent block.  The combined area should be
   // used for painting-related things, but should never be used for
   // layout (except for handling of 'overflow').
   void SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
   mozilla::LogicalRect GetOverflowArea(nsOverflowType aType,
                                        mozilla::WritingMode aWM,
-                                       nscoord aContainerWidth)
+                                       const nsSize& aContainerSize)
   {
-    return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerWidth);
+    return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize);
   }
   nsRect GetOverflowArea(nsOverflowType aType) {
     return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds();
   }
   nsOverflowAreas GetOverflowAreas() {
     if (mData) {
       return mData->mOverflowAreas;
     }
     nsRect bounds = GetPhysicalBounds();
     return nsOverflowAreas(bounds, bounds);
   }
   nsRect GetVisualOverflowArea()
     { return GetOverflowArea(eVisualOverflow); }
   nsRect GetScrollableOverflowArea()
     { return GetOverflowArea(eScrollableOverflow); }
 
-  void SlideBy(nscoord aDBCoord, nscoord aContainerWidth) {
-    NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1,
-                 "container width doesn't match");
-    mContainerWidth = aContainerWidth;
+  void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) {
+    NS_ASSERTION(aContainerSize == mContainerSize ||
+                 mContainerSize == nsSize(-1, -1),
+                 "container size doesn't match");
+    mContainerSize = aContainerSize;
     mBounds.BStart(mWritingMode) += aDBCoord;
     if (mData) {
-      nsPoint physicalDelta = mozilla::LogicalPoint(mWritingMode, 0, aDBCoord).
-                                         GetPhysicalPoint(mWritingMode, 0);
+      // Use a null containerSize to convert vector from logical to physical.
+      const nsSize nullContainerSize;
+      nsPoint physicalDelta =
+        mozilla::LogicalPoint(mWritingMode, 0, aDBCoord).
+          GetPhysicalPoint(mWritingMode, nullContainerSize);
       NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
         mData->mOverflowAreas.Overflow(otype) += physicalDelta;
       }
     }
   }
 
-  // Container-width for the line is changing (and therefore if writing mode
+  // Container-size for the line is changing (and therefore if writing mode
   // was vertical-rl, the line will move physically; this is like SlideBy,
-  // but it is the container width instead of the line's own logical coord
+  // but it is the container size instead of the line's own logical coord
   // that is changing.
-  nscoord UpdateContainerWidth(nscoord aNewContainerWidth)
+  nsSize UpdateContainerSize(const nsSize aNewContainerSize)
   {
-    NS_ASSERTION(mContainerWidth != -1, "container width not set");
-    nscoord delta = mContainerWidth - aNewContainerWidth;
-    mContainerWidth = aNewContainerWidth;
+    NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set");
+    nsSize delta = mContainerSize - aNewContainerSize;
+    mContainerSize = aNewContainerSize;
     // this has a physical-coordinate effect only in vertical-rl mode
     if (mWritingMode.IsVerticalRL() && mData) {
-      nsPoint physicalDelta = mozilla::LogicalPoint(mWritingMode, 0, delta).
-                                         GetPhysicalPoint(mWritingMode, 0);
+      nsPoint physicalDelta(-delta.width, 0);
       NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
         mData->mOverflowAreas.Overflow(otype) += physicalDelta;
       }
     }
     return delta;
   }
 
-  void IndentBy(nscoord aDICoord, nscoord aContainerWidth) {
-    NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1,
-                 "container width doesn't match");
-    mContainerWidth = aContainerWidth;
+  void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) {
+    NS_ASSERTION(aContainerSize == mContainerSize ||
+                 mContainerSize == nsSize(-1, -1),
+                 "container size doesn't match");
+    mContainerSize = aContainerSize;
     mBounds.IStart(mWritingMode) += aDICoord;
   }
 
-  void ExpandBy(nscoord aDISize, nscoord aContainerWidth) {
-    NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1,
-                 "container width doesn't match");
-    mContainerWidth = aContainerWidth;
+  void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) {
+    NS_ASSERTION(aContainerSize == mContainerSize ||
+                 mContainerSize == nsSize(-1, -1),
+                 "container size doesn't match");
+    mContainerSize = aContainerSize;
     mBounds.ISize(mWritingMode) += aDISize;
   }
 
   /**
    * The logical ascent (distance from block-start to baseline) of the
    * linebox is the logical ascent of the anonymous inline box (for
    * which we don't actually create a frame) that wraps all the
    * consecutive inline children of a block.
@@ -603,49 +608,50 @@ public:
 #ifdef DEBUG
   static int32_t GetCtorCount();
 #endif
 
   nsIFrame* mFirstChild;
 
   mozilla::WritingMode mWritingMode;
 
-  // Physical width. Use only for physical <-> logical coordinate conversion.
-  nscoord mContainerWidth;
+  // Physical size. Use only for physical <-> logical coordinate conversion.
+  nsSize mContainerSize;
 
  private:
   mozilla::LogicalRect mBounds;
 
  public:
   const mozilla::LogicalRect& GetBounds() { return mBounds; }
   nsRect GetPhysicalBounds() const
   {
     if (mBounds.IsAllZero()) {
       return nsRect(0, 0, 0, 0);
     }
 
-    NS_ASSERTION(mContainerWidth != -1, "mContainerWidth not initialized");
-    return mBounds.GetPhysicalRect(mWritingMode, mContainerWidth);
+    NS_ASSERTION(mContainerSize != nsSize(-1, -1),
+                 "mContainerSize not initialized");
+    return mBounds.GetPhysicalRect(mWritingMode, mContainerSize);
   }
   void SetBounds(mozilla::WritingMode aWritingMode,
                  nscoord aIStart, nscoord aBStart,
                  nscoord aISize, nscoord aBSize,
-                 nscoord aContainerWidth)
+                 const nsSize& aContainerSize)
   {
     mWritingMode = aWritingMode;
-    mContainerWidth = aContainerWidth;
+    mContainerSize = aContainerSize;
     mBounds = mozilla::LogicalRect(aWritingMode, aIStart, aBStart,
                                    aISize, aBSize);
   }
   void SetBounds(mozilla::WritingMode aWritingMode,
-                 nsRect aRect, nscoord aContainerWidth)
+                 nsRect aRect, const nsSize& aContainerSize)
   {
     mWritingMode = aWritingMode;
-    mContainerWidth = aContainerWidth;
-    mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerWidth);
+    mContainerSize = aContainerSize;
+    mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerSize);
   }
 
   // mFlags.mHasHashedFrames says which one to use
   union {
     nsTHashtable< nsPtrHashKey<nsIFrame> >* mFrames;
     uint32_t mChildCount;
   };
 
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -311,17 +311,17 @@ void
 nsLineLayout::UpdateBand(WritingMode aWM,
                          const LogicalRect& aNewAvailSpace,
                          nsIFrame* aFloatFrame)
 {
   WritingMode lineWM = mRootSpan->mWritingMode;
   // need to convert to our writing mode, because we might have a different
   // mode from the caller due to dir: auto
   LogicalRect availSpace = aNewAvailSpace.ConvertTo(lineWM, aWM,
-                                                    ContainerWidth());
+                                                    ContainerSize());
 #ifdef REALLY_NOISY_REFLOW
   printf("nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); frame=%p\n  will set mImpacted to true\n",
          aNewAvailSpace.x, aNewAvailSpace.y,
          aNewAvailSpace.width, aNewAvailSpace.height,
          availSpace.IStart(lineWM), availSpace.BStart(lineWM),
          availSpace.ISize(lineWM), availSpace.BSize(lineWM),
          aFloatFrame);
 #endif
@@ -937,17 +937,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
   // going to end up moving it when we do the block-direction alignment.
 
   // Adjust spacemanager coordinate system for the frame.
   nsHTMLReflowMetrics metrics(lineWM);
 #ifdef DEBUG
   metrics.ISize(lineWM) = nscoord(0xdeadbeef);
   metrics.BSize(lineWM) = nscoord(0xdeadbeef);
 #endif
-  nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerWidth());
+  nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerSize());
   nscoord tB = pfd->mBounds.BStart(lineWM);
   mFloatManager->Translate(tI, tB);
 
   int32_t savedOptionalBreakOffset;
   gfxBreakPriority savedOptionalBreakPriority;
   nsIFrame* savedOptionalBreakFrame =
     GetLastOptionalBreakPosition(&savedOptionalBreakOffset,
                                  &savedOptionalBreakPriority);
@@ -1066,17 +1066,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
   // just the union of the bounds of any absolute children. That is
   // added in later by nsLineLayout::ReflowInlineFrames.
   pfd->mOverflowAreas = metrics.mOverflowAreas;
 
   pfd->mBounds.ISize(lineWM) = metrics.ISize(lineWM);
   pfd->mBounds.BSize(lineWM) = metrics.BSize(lineWM);
 
   // Size the frame, but |RelativePositionFrames| will size the view.
-  aFrame->SetRect(lineWM, pfd->mBounds, ContainerWidthForSpan(psd));
+  aFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd));
 
   // Tell the frame that we're done reflowing it
   aFrame->DidReflow(mPresContext,
                     isText ? nullptr : reflowStateHolder.ptr(),
                     nsDidReflowStatus::FINISHED);
 
   if (aMetrics) {
     *aMetrics = metrics;
@@ -1245,23 +1245,26 @@ nsLineLayout::SyncAnnotationBounds(PerFr
   MOZ_ASSERT(aRubyFrame->mSpan);
 
   PerSpanData* span = aRubyFrame->mSpan;
   WritingMode lineWM = mRootSpan->mWritingMode;
   for (PerFrameData* pfd = span->mFirstFrame; pfd; pfd = pfd->mNext) {
     for (PerFrameData* rtc = pfd->mNextAnnotation;
          rtc; rtc = rtc->mNextAnnotation) {
       // When the annotation container is reflowed, the width of the
-      // ruby container is unknown, hence zero should be used here
-      // as container width to get the correct logical rect.
-      LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(), 0);
+      // ruby container is unknown so we use a dummy container size;
+      // in the case of RTL block direction, the final position will be
+      // fixed up later.
+      const nsSize dummyContainerSize;
+      LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(),
+                            dummyContainerSize);
       rtc->mBounds = rtcBounds;
-      nscoord rtcWidth = rtcBounds.Width(lineWM);
+      nsSize rtcSize = rtcBounds.Size(lineWM).GetPhysicalSize(lineWM);
       for (PerFrameData* rt = rtc->mSpan->mFirstFrame; rt; rt = rt->mNext) {
-        LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcWidth);
+        LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcSize);
         MOZ_ASSERT(rt->mBounds.Size(lineWM) == rtBounds.Size(lineWM),
                    "Size of the annotation should not have been changed");
         rt->mBounds.SetOrigin(lineWM, rtBounds.Origin(lineWM));