Merge mozilla-inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 26 Mar 2015 14:43:39 -0400
changeset 264679 d5a537c7c5582d7edcdf83a3705d888259c62a6e
parent 264575 8a1bac74ed807e4e0e7f272761ada3a4979bff2b (current diff)
parent 264678 f51c5b9a3ba40e79a1050d21cb33af1db2fc1a45 (diff)
child 264680 59554288b4eb65a814176fa00fc3cec847cd17f9
child 264688 02755adcb1284dfcaa51135c365dc08dd123b6e8
child 264706 a6c42747e5bc189b5fb34c5de587b1e63e59b49b
child 264814 a489d18b4df2d2beee539693e6de44c87c5ae361
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to m-c. a=merge CLOSED TREE
modules/libpref/init/all.js
--- a/accessible/atk/nsMaiInterfaceHypertext.cpp
+++ b/accessible/atk/nsMaiInterfaceHypertext.cpp
@@ -5,69 +5,91 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessible.h"
 #include "nsMai.h"
 #include "nsMaiHyperlink.h"
+#include "ProxyAccessible.h"
 #include "mozilla/Likely.h"
 
+
 using namespace mozilla::a11y;
 
 extern "C" {
 
 static AtkHyperlink*
 getLinkCB(AtkHypertext *aText, gint aLinkIndex)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
+  if (accWrap) {
+    HyperTextAccessible* hyperText = accWrap->AsHyperText();
+    NS_ENSURE_TRUE(hyperText, nullptr);
+
+    Accessible* hyperLink = hyperText->LinkAt(aLinkIndex);
+    if (!hyperLink) {
+      return nullptr;
+    }
 
-  HyperTextAccessible* hyperText = accWrap->AsHyperText();
-  NS_ENSURE_TRUE(hyperText, nullptr);
+    AtkObject* hyperLinkAtkObj = AccessibleWrap::GetAtkObject(hyperLink);
+    AccessibleWrap* accChild = GetAccessibleWrap(hyperLinkAtkObj);
+    NS_ENSURE_TRUE(accChild, nullptr);
 
-  Accessible* hyperLink = hyperText->LinkAt(aLinkIndex);
-  if (!hyperLink)
+    MaiHyperlink* maiHyperlink = accChild->GetMaiHyperlink();
+    NS_ENSURE_TRUE(maiHyperlink, nullptr);
+    return maiHyperlink->GetAtkHyperlink();
+  }
+
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    ProxyAccessible* proxyLink = proxy->LinkAt(aLinkIndex);
+    if (proxyLink) {
+      NS_WARNING("IMPLEMENT ME! See bug 1146518.");
+      // We should somehow get from ProxyAccessible* to AtkHyperlink*.
+    }
     return nullptr;
+  }
 
-  AtkObject* hyperLinkAtkObj = AccessibleWrap::GetAtkObject(hyperLink);
-  AccessibleWrap* accChild = GetAccessibleWrap(hyperLinkAtkObj);
-  NS_ENSURE_TRUE(accChild, nullptr);
-
-  MaiHyperlink *maiHyperlink = accChild->GetMaiHyperlink();
-  NS_ENSURE_TRUE(maiHyperlink, nullptr);
-  return maiHyperlink->GetAtkHyperlink();
+  return nullptr;
 }
 
 static gint
 getLinkCountCB(AtkHypertext *aText)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return -1;
+  if (accWrap) {
+    HyperTextAccessible* hyperText = accWrap->AsHyperText();
+    NS_ENSURE_TRUE(hyperText, -1);
+    return hyperText->LinkCount();
+  }
 
-  HyperTextAccessible* hyperText = accWrap->AsHyperText();
-  NS_ENSURE_TRUE(hyperText, -1);
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    return proxy->LinkCount();
+  }
 
-  return hyperText->LinkCount();
+  return -1;
 }
 
 static gint
 getLinkIndexCB(AtkHypertext *aText, gint aCharIndex)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return -1;
+  if (accWrap) {
+    HyperTextAccessible* hyperText = accWrap->AsHyperText();
+    NS_ENSURE_TRUE(hyperText, -1);
 
-  HyperTextAccessible* hyperText = accWrap->AsHyperText();
-  NS_ENSURE_TRUE(hyperText, -1);
+    return hyperText->LinkIndexAtOffset(aCharIndex);
+  }
 
-  return hyperText->LinkIndexAtOffset(aCharIndex);
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    return proxy->LinkIndexAtOffset(aCharIndex);
+  }
+
+  return -1;
 }
 }
 
 void
 hypertextInterfaceInitCB(AtkHypertextIface* aIface)
 {
   NS_ASSERTION(aIface, "no interface!");
   if (MOZ_UNLIKELY(!aIface))
--- a/accessible/atk/nsMaiInterfaceSelection.cpp
+++ b/accessible/atk/nsMaiInterfaceSelection.cpp
@@ -4,100 +4,139 @@
  * 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 "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "AccessibleWrap.h"
 #include "nsMai.h"
+#include "ProxyAccessible.h"
 #include "mozilla/Likely.h"
 
 #include <atk/atk.h>
 
 using namespace mozilla::a11y;
 
 extern "C" {
 
 static gboolean
 addSelectionCB(AtkSelection *aSelection, gint i)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return FALSE;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->AddItemToSelection(i);
+  }
 
-  return accWrap->AddItemToSelection(i);
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->AddItemToSelection(i);
+  }
+
+  return FALSE;
 }
 
 static gboolean
 clearSelectionCB(AtkSelection *aSelection)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return FALSE;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->UnselectAll();
+  }
 
-  return accWrap->UnselectAll();
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->UnselectAll();
+  }
+
+  return FALSE;
 }
 
 static AtkObject*
 refSelectionCB(AtkSelection *aSelection, gint i)
 {
+  AtkObject* atkObj = nullptr;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return nullptr;
+  if (accWrap && accWrap->IsSelect()) {
+    Accessible* selectedItem = accWrap->GetSelectedItem(i);
+    if (!selectedItem) {
+      return nullptr;
+    }
 
-  Accessible* selectedItem = accWrap->GetSelectedItem(i);
-  if (!selectedItem)
-    return nullptr;
+    atkObj = AccessibleWrap::GetAtkObject(selectedItem);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    ProxyAccessible* selectedItem = proxy->GetSelectedItem(i);
+    if (selectedItem) {
+      atkObj = GetWrapperFor(selectedItem);
+    }
+  }
 
-  AtkObject* atkObj = AccessibleWrap::GetAtkObject(selectedItem);
-  if (atkObj)
+  if (atkObj) {
     g_object_ref(atkObj);
+  }
 
   return atkObj;
 }
 
 static gint
 getSelectionCountCB(AtkSelection *aSelection)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return -1;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->SelectedItemCount();
+  }
 
-  return accWrap->SelectedItemCount();
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->SelectedItemCount();
+  }
+
+  return -1;
 }
 
 static gboolean
 isChildSelectedCB(AtkSelection *aSelection, gint i)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return FALSE;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->IsItemSelected(i);
+  }
 
-  return accWrap->IsItemSelected(i);
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->IsItemSelected(i);
+  }
+
+  return FALSE;
 }
 
 static gboolean
 removeSelectionCB(AtkSelection *aSelection, gint i)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return FALSE;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->RemoveItemFromSelection(i);
+  }
 
-  return accWrap->RemoveItemFromSelection(i);
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->RemoveItemFromSelection(i);
+  }
+
+  return FALSE;
 }
 
 static gboolean
 selectAllSelectionCB(AtkSelection *aSelection)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
-  if (!accWrap || !accWrap->IsSelect())
-    return FALSE;
+  if (accWrap && accWrap->IsSelect()) {
+    return accWrap->SelectAll();
+  }
 
-  return accWrap->SelectAll();
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
+    return proxy->SelectAll();
+  }
+
+  return FALSE;
 }
 }
 
 void
 selectionInterfaceInitCB(AtkSelectionIface* aIface)
 {
   NS_ASSERTION(aIface, "Invalid aIface");
   if (MOZ_UNLIKELY(!aIface))
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -309,44 +309,38 @@ HTMLTableHeaderCellAccessible::NativeRol
     case 0:
     case 1:
       return roles::COLUMNHEADER;
     case 2:
     case 3:
       return roles::ROWHEADER;
   }
 
-  // Assume it's columnheader if there are headers in siblings, otherwise
-  // rowheader.
-  // This should iterate the flattened tree
-  nsIContent* parentContent = mContent->GetParent();
-  if (!parentContent) {
-    NS_ERROR("Deattached content on alive accessible?");
+  TableAccessible* table = Table();
+  if (!table)
     return roles::NOTHING;
-  }
+
+  // If the cell next to this one is not a header cell then assume this cell is
+  // a row header for it.
+  uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
+  Accessible* cell = table->CellAt(rowIdx, colIdx + ColExtent());
+  if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+    return roles::ROWHEADER;
 
-  for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent;
-       siblingContent = siblingContent->GetPreviousSibling()) {
-    if (siblingContent->IsElement()) {
-      return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-        roles::COLUMNHEADER : roles::ROWHEADER;
-    }
-  }
+  // If the cell below this one is not a header cell then assume this cell is
+  // a column header for it.
+  uint32_t rowExtent = RowExtent();
+  cell = table->CellAt(rowIdx + rowExtent, colIdx);
+  if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+    return roles::COLUMNHEADER;
 
-  for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent;
-       siblingContent = siblingContent->GetNextSibling()) {
-    if (siblingContent->IsElement()) {
-      return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
-       roles::COLUMNHEADER : roles::ROWHEADER;
-    }
-  }
-
-  // No elements in siblings what means the table has one column only. Therefore
-  // it should be column header.
-  return roles::COLUMNHEADER;
+  // Otherwise if this cell is surrounded by header cells only then make a guess
+  // based on its cell spanning. In other words if it is row spanned then assume
+  // it's a row header, otherwise it's a column header.
+  return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableRowAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible)
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -6,16 +6,18 @@
 
 #include "DocAccessibleChild.h"
 
 #include "Accessible-inl.h"
 #include "ProxyAccessible.h"
 #include "Relation.h"
 #include "HyperTextAccessible-inl.h"
 #include "ImageAccessible.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 namespace mozilla {
 namespace a11y {
 
 static uint32_t
 InterfacesFor(Accessible* aAcc)
@@ -47,31 +49,58 @@ SerializeTree(Accessible* aRoot, nsTArra
 }
 
 Accessible*
 DocAccessibleChild::IdToAccessible(const uint64_t& aID) const
 {
   return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
 }
 
+Accessible*
+DocAccessibleChild::IdToAccessibleLink(const uint64_t& aID) const
+{
+  Accessible* acc = IdToAccessible(aID);
+  return acc && acc->IsLink() ? acc : nullptr;
+}
+
+Accessible*
+DocAccessibleChild::IdToAccessibleSelect(const uint64_t& aID) const
+{
+  Accessible* acc = IdToAccessible(aID);
+  return acc && acc->IsSelect() ? acc : nullptr;
+}
+
 HyperTextAccessible*
 DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID) const
 {
   Accessible* acc = IdToAccessible(aID);
-  MOZ_ASSERT(!acc || acc->IsHyperText());
-  return acc ? acc->AsHyperText() : nullptr;
+  return acc && acc->IsHyperText() ? acc->AsHyperText() : nullptr;
 }
 
 ImageAccessible*
 DocAccessibleChild::IdToImageAccessible(const uint64_t& aID) const
 {
   Accessible* acc = IdToAccessible(aID);
   return (acc && acc->IsImage()) ? acc->AsImage() : nullptr;
 }
 
+TableCellAccessible*
+DocAccessibleChild::IdToTableCellAccessible(const uint64_t& aID) const
+{
+  Accessible* acc = IdToAccessible(aID);
+  return (acc && acc->IsTableCell()) ? acc->AsTableCell() : nullptr;
+}
+
+TableAccessible*
+DocAccessibleChild::IdToTableAccessible(const uint64_t& aID) const
+{
+  Accessible* acc = IdToAccessible(aID);
+  return (acc && acc->IsTable()) ? acc->AsTable() : nullptr;
+}
+
 void
 DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
   uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
   uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
   nsTArray<AccessibleData> shownTree;
   ShowEventData data(parentID, idxInParent, shownTree);
@@ -613,10 +642,793 @@ DocAccessibleChild::RecvImageSize(const 
   ImageAccessible* acc = IdToImageAccessible(aID);
   if (acc) {
     *aRetVal = acc->Size();
   }
 
   return true;
 }
 
+bool
+DocAccessibleChild::RecvStartOffset(const uint64_t& aID,
+                                    uint32_t* aRetVal,
+                                    bool* aOk)
+{
+  Accessible* acc = IdToAccessibleLink(aID);
+  if (acc) {
+    *aRetVal = acc->StartOffset();
+    *aOk = true;
+  } else {
+    *aRetVal = 0;
+    *aOk = false;
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvEndOffset(const uint64_t& aID,
+                                  uint32_t* aRetVal,
+                                  bool* aOk)
+{
+  Accessible* acc = IdToAccessibleLink(aID);
+  if (acc) {
+    *aRetVal = acc->EndOffset();
+    *aOk = true;
+  } else {
+    *aRetVal = 0;
+    *aOk = false;
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvIsLinkValid(const uint64_t& aID,
+                                    bool* aRetVal)
+{
+  Accessible* acc = IdToAccessibleLink(aID);
+  if (acc) {
+    *aRetVal = acc->IsLinkValid();
+  } else {
+    *aRetVal = false;
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvAnchorCount(const uint64_t& aID,
+                                    uint32_t* aRetVal,
+                                    bool* aOk)
+{
+  Accessible* acc = IdToAccessibleLink(aID);
+  if (acc) {
+    *aRetVal = acc->AnchorCount();
+    *aOk = true;
+  } else {
+    *aRetVal = 0;
+    *aOk = false;
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvAnchorURIAt(const uint64_t& aID,
+                                    const uint32_t& aIndex,
+                                    nsCString* aURI,
+                                    bool* aOk)
+{
+  Accessible* acc = IdToAccessibleLink(aID);
+  *aOk = false;
+  if (acc) {
+    nsCOMPtr<nsIURI> uri = acc->AnchorURIAt(aIndex);
+    if (uri) {
+      uri->GetSpec(*aURI);
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvAnchorAt(const uint64_t& aID,
+                                 const uint32_t& aIndex,
+                                 uint64_t* aIDOfAnchor,
+                                 bool* aOk)
+{
+  *aIDOfAnchor = 0;
+  *aOk = false;
+  Accessible* acc = IdToAccessibleLink(aID);
+  if (acc) {
+    Accessible* anchor = acc->AnchorAt(aIndex);
+    if (anchor) {
+      *aIDOfAnchor = reinterpret_cast<uint64_t>(anchor->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvLinkCount(const uint64_t& aID,
+                                  uint32_t* aCount)
+{
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  *aCount = acc ? acc->LinkCount() : 0;
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvLinkAt(const uint64_t& aID,
+                               const uint32_t& aIndex,
+                               uint64_t* aIDOfLink,
+                               bool* aOk)
+{
+  *aIDOfLink = 0;
+  *aOk = false;
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  if (acc) {
+    Accessible* link = acc->LinkAt(aIndex);
+    if (link) {
+      *aIDOfLink = reinterpret_cast<uint64_t>(link->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvLinkIndexOf(const uint64_t& aID,
+                                    const uint64_t& aLinkID,
+                                    int32_t* aIndex)
+{
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  Accessible* link = IdToAccessible(aLinkID);
+  *aIndex = -1;
+  if (acc && link) {
+    *aIndex = acc->LinkIndexOf(link);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvLinkIndexAtOffset(const uint64_t& aID,
+                                          const uint32_t& aOffset,
+                                          int32_t* aIndex)
+{
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  *aIndex = acc ? acc->LinkIndexAtOffset(aOffset) : -1;
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableOfACell(const uint64_t& aID,
+                                     uint64_t* aTableID,
+                                     bool* aOk)
+{
+  *aTableID = 0;
+  *aOk = false;
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    TableAccessible* table = acc->Table();
+    if (table) {
+      *aTableID = reinterpret_cast<uint64_t>(table->AsAccessible()->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvColIdx(const uint64_t& aID,
+                               uint32_t* aIndex)
+{
+  *aIndex = 0;
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    *aIndex = acc->ColIdx();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvRowIdx(const uint64_t& aID,
+                               uint32_t* aIndex)
+{
+  *aIndex = 0;
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    *aIndex = acc->RowIdx();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvColExtent(const uint64_t& aID,
+                                  uint32_t* aExtent)
+{
+  *aExtent = 0;
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    *aExtent = acc->ColExtent();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvRowExtent(const uint64_t& aID,
+                                  uint32_t* aExtent)
+{
+  *aExtent = 0;
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    *aExtent = acc->RowExtent();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvColHeaderCells(const uint64_t& aID,
+                                       nsTArray<uint64_t>* aCells)
+{
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    nsAutoTArray<Accessible*, 10> headerCells;
+    acc->ColHeaderCells(&headerCells);
+    aCells->SetCapacity(headerCells.Length());
+    for (uint32_t i = 0; i < headerCells.Length(); ++i) {
+      aCells->AppendElement(
+        reinterpret_cast<uint64_t>(headerCells[i]->UniqueID()));
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvRowHeaderCells(const uint64_t& aID,
+                                       nsTArray<uint64_t>* aCells)
+{
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  if (acc) {
+    nsAutoTArray<Accessible*, 10> headerCells;
+    acc->RowHeaderCells(&headerCells);
+    aCells->SetCapacity(headerCells.Length());
+    for (uint32_t i = 0; i < headerCells.Length(); ++i) {
+      aCells->AppendElement(
+        reinterpret_cast<uint64_t>(headerCells[i]->UniqueID()));
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvIsCellSelected(const uint64_t& aID,
+                                       bool* aSelected)
+{
+  TableCellAccessible* acc = IdToTableCellAccessible(aID);
+  *aSelected = acc && acc->Selected();
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableCaption(const uint64_t& aID,
+                                     uint64_t* aCaptionID,
+                                     bool* aOk)
+{
+  *aCaptionID = 0;
+  *aOk = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    Accessible* caption = acc->Caption();
+    if (caption) {
+      *aCaptionID = reinterpret_cast<uint64_t>(caption->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSummary(const uint64_t& aID,
+                                     nsString* aSummary)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->Summary(*aSummary);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableColumnCount(const uint64_t& aID,
+                                         uint32_t* aColCount)
+{
+  *aColCount = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aColCount = acc->ColCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowCount(const uint64_t& aID,
+                                      uint32_t* aRowCount)
+{
+  *aRowCount = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aRowCount = acc->RowCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableCellAt(const uint64_t& aID,
+                                    const uint32_t& aRow,
+                                    const uint32_t& aCol,
+                                    uint64_t* aCellID,
+                                    bool* aOk)
+{
+  *aCellID = 0;
+  *aOk = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    Accessible* cell = acc->CellAt(aRow, aCol);
+    if (cell) {
+      *aCellID = reinterpret_cast<uint64_t>(cell->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableCellIndexAt(const uint64_t& aID,
+                                         const uint32_t& aRow,
+                                         const uint32_t& aCol,
+                                         int32_t* aIndex)
+{
+  *aIndex = -1;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aIndex = acc->CellIndexAt(aRow, aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableColumnIndexAt(const uint64_t& aID,
+                                           const uint32_t& aCellIndex,
+                                           int32_t* aCol)
+{
+  *aCol = -1;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aCol = acc->ColIndexAt(aCellIndex);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowIndexAt(const uint64_t& aID,
+                                        const uint32_t& aCellIndex,
+                                        int32_t* aRow)
+{
+  *aRow = -1;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aRow = acc->RowIndexAt(aCellIndex);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowAndColumnIndicesAt(const uint64_t& aID,
+                                                  const uint32_t& aCellIndex,
+                                                  int32_t* aRow,
+                                                  int32_t* aCol)
+{
+  *aRow = -1;
+  *aCol = -1;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->RowAndColIndicesAt(aCellIndex, aRow, aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableColumnExtentAt(const uint64_t& aID,
+                                            const uint32_t& aRow,
+                                            const uint32_t& aCol,
+                                            uint32_t* aExtent)
+{
+  *aExtent = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aExtent = acc->ColExtentAt(aRow, aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowExtentAt(const uint64_t& aID,
+                                         const uint32_t& aRow,
+                                         const uint32_t& aCol,
+                                         uint32_t* aExtent)
+{
+  *aExtent = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aExtent = acc->RowExtentAt(aRow, aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableColumnDescription(const uint64_t& aID,
+                                               const uint32_t& aCol,
+                                               nsString* aDescription)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->ColDescription(aCol, *aDescription);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowDescription(const uint64_t& aID,
+                                            const uint32_t& aRow,
+                                            nsString* aDescription)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->RowDescription(aRow, *aDescription);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableColumnSelected(const uint64_t& aID,
+                                            const uint32_t& aCol,
+                                            bool* aSelected)
+{
+  *aSelected = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelected = acc->IsColSelected(aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableRowSelected(const uint64_t& aID,
+                                         const uint32_t& aRow,
+                                         bool* aSelected)
+{
+  *aSelected = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelected = acc->IsRowSelected(aRow);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableCellSelected(const uint64_t& aID,
+                                          const uint32_t& aRow,
+                                          const uint32_t& aCol,
+                                          bool* aSelected)
+{
+  *aSelected = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelected = acc->IsCellSelected(aRow, aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedCellCount(const uint64_t& aID,
+                                               uint32_t* aSelectedCells)
+{
+  *aSelectedCells = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelectedCells = acc->SelectedCellCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedColumnCount(const uint64_t& aID,
+                                                 uint32_t* aSelectedColumns)
+{
+  *aSelectedColumns = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelectedColumns = acc->SelectedColCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedRowCount(const uint64_t& aID,
+                                              uint32_t* aSelectedRows)
+{
+  *aSelectedRows = 0;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aSelectedRows = acc->SelectedRowCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedCells(const uint64_t& aID,
+                                           nsTArray<uint64_t>* aCellIDs)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    nsAutoTArray<Accessible*, 30> cells;
+    acc->SelectedCells(&cells);
+    aCellIDs->SetCapacity(cells.Length());
+    for (uint32_t i = 0; i < cells.Length(); ++i) {
+      aCellIDs->AppendElement(
+        reinterpret_cast<uint64_t>(cells[i]->UniqueID()));
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedCellIndices(const uint64_t& aID,
+                                                 nsTArray<uint32_t>* aCellIndices)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->SelectedCellIndices(aCellIndices);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedColumnIndices(const uint64_t& aID,
+                                                   nsTArray<uint32_t>* aColumnIndices)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->SelectedColIndices(aColumnIndices);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectedRowIndices(const uint64_t& aID,
+                                                nsTArray<uint32_t>* aRowIndices)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->SelectedRowIndices(aRowIndices);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectColumn(const uint64_t& aID,
+                                          const uint32_t& aCol)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->SelectCol(aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableSelectRow(const uint64_t& aID,
+                                       const uint32_t& aRow)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->SelectRow(aRow);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableUnselectColumn(const uint64_t& aID,
+                                            const uint32_t& aCol)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->UnselectCol(aCol);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableUnselectRow(const uint64_t& aID,
+                                         const uint32_t& aRow)
+{
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    acc->UnselectRow(aRow);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTableIsProbablyForLayout(const uint64_t& aID,
+                                                 bool* aForLayout)
+{
+  *aForLayout = false;
+  TableAccessible* acc = IdToTableAccessible(aID);
+  if (acc) {
+    *aForLayout = acc->IsProbablyLayoutTable();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvSelectedItems(const uint64_t& aID,
+                                      nsTArray<uint64_t>* aSelectedItemIDs)
+{
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    nsAutoTArray<Accessible*, 10> selectedItems;
+    acc->SelectedItems(&selectedItems);
+    aSelectedItemIDs->SetCapacity(selectedItems.Length());
+    for (size_t i = 0; i < selectedItems.Length(); ++i) {
+      aSelectedItemIDs->AppendElement(
+        reinterpret_cast<uint64_t>(selectedItems[i]->UniqueID()));
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvSelectedItemCount(const uint64_t& aID,
+                                          uint32_t* aCount)
+{
+  *aCount = 0;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aCount = acc->SelectedItemCount();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvGetSelectedItem(const uint64_t& aID,
+                                        const uint32_t& aIndex,
+                                        uint64_t* aSelected,
+                                        bool* aOk)
+{
+  *aSelected = 0;
+  *aOk = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    Accessible* item = acc->GetSelectedItem(aIndex);
+    if (item) {
+      *aSelected = reinterpret_cast<uint64_t>(item->UniqueID());
+      *aOk = true;
+    }
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvIsItemSelected(const uint64_t& aID,
+                                       const uint32_t& aIndex,
+                                       bool* aSelected)
+{
+  *aSelected = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aSelected = acc->IsItemSelected(aIndex);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvAddItemToSelection(const uint64_t& aID,
+                                           const uint32_t& aIndex,
+                                           bool* aSuccess)
+{
+  *aSuccess = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aSuccess = acc->AddItemToSelection(aIndex);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvRemoveItemFromSelection(const uint64_t& aID,
+                                                const uint32_t& aIndex,
+                                                bool* aSuccess)
+{
+  *aSuccess = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aSuccess = acc->RemoveItemFromSelection(aIndex);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvSelectAll(const uint64_t& aID,
+                                  bool* aSuccess)
+{
+  *aSuccess = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aSuccess = acc->SelectAll();
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvUnselectAll(const uint64_t& aID,
+                                    bool* aSuccess)
+{
+  *aSuccess = false;
+  Accessible* acc = IdToAccessibleSelect(aID);
+  if (acc) {
+    *aSuccess = acc->UnselectAll();
+  }
+
+  return true;
+}
+
 }
 }
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -11,17 +11,18 @@
 #include "mozilla/a11y/PDocAccessibleChild.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace a11y {
 class Accessible;
 class HyperTextAccessible;
 class ImageAccessible;
-
+class TableAccessible;
+class TableCellAccessible;
 class AccShowEvent;
 
   /*
    * These objects handle content side communication for an accessible document,
    * and their lifetime is the same as the document they represent.
    */
 class DocAccessibleChild : public PDocAccessibleChild
 {
@@ -184,21 +185,186 @@ public:
 
   virtual bool RecvImagePosition(const uint64_t& aID,
                                  const uint32_t& aCoordType,
                                  nsIntPoint* aRetVal) override;
 
   virtual bool RecvImageSize(const uint64_t& aID,
                              nsIntSize* aRetVal) override;
 
+  virtual bool RecvStartOffset(const uint64_t& aID,
+                               uint32_t* aRetVal,
+                               bool* aOk) override;
+  virtual bool RecvEndOffset(const uint64_t& aID,
+                             uint32_t* aRetVal,
+                             bool* aOk) override;
+  virtual bool RecvIsLinkValid(const uint64_t& aID,
+                               bool* aRetVal) override;
+  virtual bool RecvAnchorCount(const uint64_t& aID,
+                               uint32_t* aRetVal, bool* aOk) override;
+  virtual bool RecvAnchorURIAt(const uint64_t& aID,
+                               const uint32_t& aIndex,
+                               nsCString* aURI,
+                               bool* aOk) override;
+  virtual bool RecvAnchorAt(const uint64_t& aID,
+                            const uint32_t& aIndex,
+                            uint64_t* aIDOfAnchor,
+                            bool* aOk) override;
+
+  virtual bool RecvLinkCount(const uint64_t& aID,
+                             uint32_t* aCount) override;
+
+  virtual bool RecvLinkAt(const uint64_t& aID,
+                          const uint32_t& aIndex,
+                          uint64_t* aIDOfLink,
+                          bool* aOk) override;
+
+  virtual bool RecvLinkIndexOf(const uint64_t& aID,
+                               const uint64_t& aLinkID,
+                               int32_t* aIndex) override;
+
+  virtual bool RecvLinkIndexAtOffset(const uint64_t& aID,
+                                     const uint32_t& aOffset,
+                                     int32_t* aIndex) override;
+
+  virtual bool RecvTableOfACell(const uint64_t& aID,
+                                uint64_t* aTableID,
+                                bool* aOk) override;
+
+  virtual bool RecvColIdx(const uint64_t& aID, uint32_t* aIndex) override;
+
+  virtual bool RecvRowIdx(const uint64_t& aID, uint32_t* aIndex) override;
+
+  virtual bool RecvColExtent(const uint64_t& aID, uint32_t* aExtent) override;
+
+  virtual bool RecvRowExtent(const uint64_t& aID, uint32_t* aExtent) override;
+
+  virtual bool RecvColHeaderCells(const uint64_t& aID,
+                                  nsTArray<uint64_t>* aCells) override;
+
+  virtual bool RecvRowHeaderCells(const uint64_t& aID,
+                                  nsTArray<uint64_t>* aCells) override;
+
+  virtual bool RecvIsCellSelected(const uint64_t& aID,
+                                  bool* aSelected) override;
+
+  virtual bool RecvTableCaption(const uint64_t& aID,
+                                uint64_t* aCaptionID,
+                                bool* aOk) override;
+  virtual bool RecvTableSummary(const uint64_t& aID,
+                                nsString* aSummary) override;
+  virtual bool RecvTableColumnCount(const uint64_t& aID,
+                                    uint32_t* aColCount) override;
+  virtual bool RecvTableRowCount(const uint64_t& aID,
+                                 uint32_t* aRowCount) override;
+  virtual bool RecvTableCellAt(const uint64_t& aID,
+                               const uint32_t& aRow,
+                               const uint32_t& aCol,
+                               uint64_t* aCellID,
+                               bool* aOk) override;
+  virtual bool RecvTableCellIndexAt(const uint64_t& aID,
+                                    const uint32_t& aRow,
+                                    const uint32_t& aCol,
+                                    int32_t* aIndex) override;
+  virtual bool RecvTableColumnIndexAt(const uint64_t& aID,
+                                      const uint32_t& aCellIndex,
+                                      int32_t* aCol) override;
+  virtual bool RecvTableRowIndexAt(const uint64_t& aID,
+                                   const uint32_t& aCellIndex,
+                                   int32_t* aRow) override;
+  virtual bool RecvTableRowAndColumnIndicesAt(const uint64_t& aID,
+                                             const uint32_t& aCellIndex,
+                                             int32_t* aRow,
+                                             int32_t* aCol) override;
+  virtual bool RecvTableColumnExtentAt(const uint64_t& aID,
+                                       const uint32_t& aRow,
+                                       const uint32_t& aCol,
+                                       uint32_t* aExtent) override;
+  virtual bool RecvTableRowExtentAt(const uint64_t& aID,
+                                    const uint32_t& aRow,
+                                    const uint32_t& aCol,
+                                    uint32_t* aExtent) override;
+  virtual bool RecvTableColumnDescription(const uint64_t& aID,
+                                          const uint32_t& aCol,
+                                          nsString* aDescription) override;
+  virtual bool RecvTableRowDescription(const uint64_t& aID,
+                                       const uint32_t& aRow,
+                                       nsString* aDescription) override;
+  virtual bool RecvTableColumnSelected(const uint64_t& aID,
+                                       const uint32_t& aCol,
+                                       bool* aSelected) override;
+  virtual bool RecvTableRowSelected(const uint64_t& aID,
+                                    const uint32_t& aRow,
+                                    bool* aSelected) override;
+  virtual bool RecvTableCellSelected(const uint64_t& aID,
+                                     const uint32_t& aRow,
+                                     const uint32_t& aCol,
+                                     bool* aSelected) override;
+  virtual bool RecvTableSelectedCellCount(const uint64_t& aID,
+                                          uint32_t* aSelectedCells) override;
+  virtual bool RecvTableSelectedColumnCount(const uint64_t& aID,
+                                            uint32_t* aSelectedColumns) override;
+  virtual bool RecvTableSelectedRowCount(const uint64_t& aID,
+                                         uint32_t* aSelectedRows) override;
+  virtual bool RecvTableSelectedCells(const uint64_t& aID,
+                                      nsTArray<uint64_t>* aCellIDs) override;
+  virtual bool RecvTableSelectedCellIndices(const uint64_t& aID,
+                                            nsTArray<uint32_t>* aCellIndices) override;
+  virtual bool RecvTableSelectedColumnIndices(const uint64_t& aID,
+                                              nsTArray<uint32_t>* aColumnIndices) override;
+  virtual bool RecvTableSelectedRowIndices(const uint64_t& aID,
+                                           nsTArray<uint32_t>* aRowIndices) override;
+  virtual bool RecvTableSelectColumn(const uint64_t& aID,
+                                     const uint32_t& aCol) override;
+  virtual bool RecvTableSelectRow(const uint64_t& aID,
+                                  const uint32_t& aRow) override;
+  virtual bool RecvTableUnselectColumn(const uint64_t& aID,
+                                       const uint32_t& aCol) override;
+  virtual bool RecvTableUnselectRow(const uint64_t& aID,
+                                    const uint32_t& aRow) override;
+  virtual bool RecvTableIsProbablyForLayout(const uint64_t& aID,
+                                            bool* aForLayout) override;
+
+  virtual bool RecvSelectedItems(const uint64_t& aID,
+                                 nsTArray<uint64_t>* aSelectedItemIDs) override;
+
+  virtual bool RecvSelectedItemCount(const uint64_t& aID,
+                                     uint32_t* aCount) override;
+
+  virtual bool RecvGetSelectedItem(const uint64_t& aID,
+                                   const uint32_t& aIndex,
+                                   uint64_t* aSelected,
+                                   bool* aOk) override;
+
+  virtual bool RecvIsItemSelected(const uint64_t& aID,
+                                  const uint32_t& aIndex,
+                                  bool* aSelected) override;
+
+  virtual bool RecvAddItemToSelection(const uint64_t& aID,
+                                      const uint32_t& aIndex,
+                                      bool* aSuccess) override;
+
+  virtual bool RecvRemoveItemFromSelection(const uint64_t& aID,
+                                           const uint32_t& aIndex,
+                                           bool* aSuccess) override;
+
+  virtual bool RecvSelectAll(const uint64_t& aID,
+                             bool* aSuccess) override;
+
+  virtual bool RecvUnselectAll(const uint64_t& aID,
+                               bool* aSuccess) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
+  Accessible* IdToAccessibleLink(const uint64_t& aID) const;
+  Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
+  TableCellAccessible* IdToTableCellAccessible(const uint64_t& aID) const;
+  TableAccessible* IdToTableAccessible(const uint64_t& aID) const;
 
   bool PersistentPropertiesToArray(nsIPersistentProperties* aProps,
                                    nsTArray<Attribute>* aAttributes);
 
   DocAccessible* mDoc;
 };
 
 }
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -127,12 +127,72 @@ child:
   prio(high) sync InsertText(uint64_t aID, nsString aText, int32_t aPosition);
   prio(high) sync CopyText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync PasteText(uint64_t aID, int32_t aPosition);
 
   prio(high) sync ImagePosition(uint64_t aID, uint32_t aCoordType) returns(nsIntPoint aRetVal);
   prio(high) sync ImageSize(uint64_t aID) returns(nsIntSize aRetVal);
+
+  prio(high) sync StartOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+  prio(high) sync EndOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+  prio(high) sync IsLinkValid(uint64_t aID) returns(bool aRetVal);
+  prio(high) sync AnchorCount(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+  prio(high) sync AnchorURIAt(uint64_t aID, uint32_t aIndex) returns(nsCString aURI, bool aOk);
+  prio(high) sync AnchorAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfAnchor, bool aOk);
+
+  prio(high) sync LinkCount(uint64_t aID) returns(uint32_t aCount);
+  prio(high) sync LinkAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfLink, bool aOk);
+  prio(high) sync LinkIndexOf(uint64_t aID, uint64_t aLinkID) returns(int32_t aIndex);
+  prio(high) sync LinkIndexAtOffset(uint64_t aID, uint32_t aOffset) returns(int32_t aIndex);
+
+  prio(high) sync TableOfACell(uint64_t aID) returns(uint64_t aTableID, bool aOk);
+  prio(high) sync ColIdx(uint64_t aID) returns(uint32_t aIndex);
+  prio(high) sync RowIdx(uint64_t aID) returns(uint32_t aIndex);
+  prio(high) sync ColExtent(uint64_t aID) returns(uint32_t aExtent);
+  prio(high) sync RowExtent(uint64_t aID) returns(uint32_t aExtent);
+  prio(high) sync ColHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
+  prio(high) sync RowHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
+  prio(high) sync IsCellSelected(uint64_t aID) returns(bool aSelected);
+
+  prio(high) sync TableCaption(uint64_t aID) returns(uint64_t aCaptionID, bool aOk);
+  prio(high) sync TableSummary(uint64_t aID) returns(nsString aSummary);
+  prio(high) sync TableColumnCount(uint64_t aID) returns(uint32_t aColCount);
+  prio(high) sync TableRowCount(uint64_t aID) returns(uint32_t aRowCount);
+  prio(high) sync TableCellAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint64_t aCellID, bool aOk);
+  prio(high) sync TableCellIndexAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(int32_t aIndex);
+  prio(high) sync TableColumnIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aCol);
+  prio(high) sync TableRowIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow);
+  prio(high) sync TableRowAndColumnIndicesAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow, int32_t aCol);
+  prio(high) sync TableColumnExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
+  prio(high) sync TableRowExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
+  prio(high) sync TableColumnDescription(uint64_t aID, uint32_t aCol) returns(nsString aDescription);
+  prio(high) sync TableRowDescription(uint64_t aID, uint32_t aRow) returns(nsString aDescription);
+  prio(high) sync TableColumnSelected(uint64_t aID, uint32_t aCol) returns(bool aSelected);
+  prio(high) sync TableRowSelected(uint64_t aID, uint32_t aRow) returns(bool aSelected);
+  prio(high) sync TableCellSelected(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(bool aSelected);
+  prio(high) sync TableSelectedCellCount(uint64_t aID) returns(uint32_t aSelectedCells);
+  prio(high) sync TableSelectedColumnCount(uint64_t aID) returns(uint32_t aSelectedColumns);
+  prio(high) sync TableSelectedRowCount(uint64_t aID) returns(uint32_t aSelectedRows);
+  prio(high) sync TableSelectedCells(uint64_t aID) returns(uint64_t[] aCellIDs);
+  prio(high) sync TableSelectedCellIndices(uint64_t aID) returns(uint32_t[] aCellIndeces);
+  prio(high) sync TableSelectedColumnIndices(uint64_t aID) returns(uint32_t[] aColumnIndeces);
+  prio(high) sync TableSelectedRowIndices(uint64_t aID) returns(uint32_t[] aRowIndeces);
+  prio(high) sync TableSelectColumn(uint64_t aID, uint32_t aCol);
+  prio(high) sync TableSelectRow(uint64_t aID, uint32_t aRow);
+  prio(high) sync TableUnselectColumn(uint64_t aID, uint32_t aCol);
+  prio(high) sync TableUnselectRow(uint64_t aID, uint32_t aRow);
+  prio(high) sync TableIsProbablyForLayout(uint64_t aID) returns(bool aForLayout);
+
+  prio(high) sync SelectedItems(uint64_t aID) returns(uint64_t[] aSelectedItemIDs);
+  prio(high) sync SelectedItemCount(uint64_t aID) returns(uint32_t aCount);
+  prio(high) sync GetSelectedItem(uint64_t aID, uint32_t aIndex) returns(uint64_t aSelected, bool aOk);
+  prio(high) sync IsItemSelected(uint64_t aID, uint32_t aIndex) returns(bool aSelected);
+  prio(high) sync AddItemToSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
+  prio(high) sync RemoveItemFromSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
+  prio(high) sync SelectAll(uint64_t aID) returns(bool aSuccess);
+  prio(high) sync UnselectAll(uint64_t aID) returns(bool aSuccess);
+  
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -374,10 +374,430 @@ ProxyAccessible::ImagePosition(uint32_t 
 nsIntSize
 ProxyAccessible::ImageSize()
 {
   nsIntSize retVal;
   unused << mDoc->SendImageSize(mID, &retVal);
   return retVal;
 }
 
+uint32_t
+ProxyAccessible::StartOffset(bool* aOk)
+{
+  uint32_t retVal = 0;
+  unused << mDoc->SendStartOffset(mID, &retVal, aOk);
+  return retVal;
+}
+
+uint32_t
+ProxyAccessible::EndOffset(bool* aOk)
+{
+  uint32_t retVal = 0;
+  unused << mDoc->SendEndOffset(mID, &retVal, aOk);
+  return retVal;
+}
+
+bool
+ProxyAccessible::IsLinkValid()
+{
+  bool retVal = false;
+  unused << mDoc->SendIsLinkValid(mID, &retVal);
+  return retVal;
+}
+
+uint32_t
+ProxyAccessible::AnchorCount(bool* aOk)
+{
+  uint32_t retVal = 0;
+  unused << mDoc->SendAnchorCount(mID, &retVal, aOk);
+  return retVal;
+}
+
+void
+ProxyAccessible::AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk)
+{
+  unused << mDoc->SendAnchorURIAt(mID, aIndex, &aURI, aOk);
+}
+
+ProxyAccessible*
+ProxyAccessible::AnchorAt(uint32_t aIndex)
+{
+  uint64_t id = 0;
+  bool ok = false;
+  unused << mDoc->SendAnchorAt(mID, aIndex, &id, &ok);
+  return ok ? mDoc->GetAccessible(id) : nullptr;
+}
+
+uint32_t
+ProxyAccessible::LinkCount()
+{
+  uint32_t retVal = 0;
+  unused << mDoc->SendLinkCount(mID, &retVal);
+  return retVal;
+}
+
+ProxyAccessible*
+ProxyAccessible::LinkAt(const uint32_t& aIndex)
+{
+  uint64_t linkID = 0;
+  bool ok = false;
+  unused << mDoc->SendLinkAt(mID, aIndex, &linkID, &ok);
+  return ok ? mDoc->GetAccessible(linkID) : nullptr;
+}
+
+int32_t
+ProxyAccessible::LinkIndexOf(ProxyAccessible* aLink)
+{
+  int32_t retVal = -1;
+  if (aLink) {
+    unused << mDoc->SendLinkIndexOf(mID, aLink->ID(), &retVal);
+  }
+
+  return retVal;
+}
+
+int32_t
+ProxyAccessible::LinkIndexAtOffset(uint32_t aOffset)
+{
+  int32_t retVal = -1;
+  unused << mDoc->SendLinkIndexAtOffset(mID, aOffset, &retVal);
+  return retVal;
+}
+
+ProxyAccessible*
+ProxyAccessible::TableOfACell()
+{
+  uint64_t tableID = 0;
+  bool ok = false;
+  unused << mDoc->SendTableOfACell(mID, &tableID, &ok);
+  return ok ? mDoc->GetAccessible(tableID) : nullptr;
+}
+
+uint32_t
+ProxyAccessible::ColIdx()
+{
+  uint32_t index = 0;
+  unused << mDoc->SendColIdx(mID, &index);
+  return index;
+}
+
+uint32_t
+ProxyAccessible::RowIdx()
+{
+  uint32_t index = 0;
+  unused << mDoc->SendRowIdx(mID, &index);
+  return index;
+}
+
+uint32_t
+ProxyAccessible::ColExtent()
+{
+  uint32_t extent = 0;
+  unused << mDoc->SendColExtent(mID, &extent);
+  return extent;
+}
+
+uint32_t
+ProxyAccessible::RowExtent()
+{
+  uint32_t extent = 0;
+  unused << mDoc->SendRowExtent(mID, &extent);
+  return extent;
+}
+
+void
+ProxyAccessible::ColHeaderCells(nsTArray<uint64_t>* aCells)
+{
+  unused << mDoc->SendColHeaderCells(mID, aCells);
+}
+
+void
+ProxyAccessible::RowHeaderCells(nsTArray<uint64_t>* aCells)
+{
+  unused << mDoc->SendRowHeaderCells(mID, aCells);
+}
+
+bool
+ProxyAccessible::IsCellSelected()
+{
+  bool selected = false;
+  unused << mDoc->SendIsCellSelected(mID, &selected);
+  return selected;
+}
+
+ProxyAccessible*
+ProxyAccessible::TableCaption()
+{
+  uint64_t captionID = 0;
+  bool ok = false;
+  unused << mDoc->SendTableCaption(mID, &captionID, &ok);
+  return ok ? mDoc->GetAccessible(captionID) : nullptr;
+}
+
+void
+ProxyAccessible::TableSummary(nsString& aSummary)
+{
+  unused << mDoc->SendTableSummary(mID, &aSummary);
+}
+
+uint32_t
+ProxyAccessible::TableColumnCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendTableColumnCount(mID, &count);
+  return count;
+}
+
+uint32_t
+ProxyAccessible::TableRowCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendTableRowCount(mID, &count);
+  return count;
+}
+
+ProxyAccessible*
+ProxyAccessible::TableCellAt(uint32_t aRow, uint32_t aCol)
+{
+  uint64_t cellID = 0;
+  bool ok = false;
+  unused << mDoc->SendTableCellAt(mID, aRow, aCol, &cellID, &ok);
+  return ok ? mDoc->GetAccessible(cellID) : nullptr;
+}
+
+int32_t
+ProxyAccessible::TableCellIndexAt(uint32_t aRow, uint32_t aCol)
+{
+  int32_t index = 0;
+  unused << mDoc->SendTableCellIndexAt(mID, aRow, aCol, &index);
+  return index;
+}
+
+int32_t
+ProxyAccessible::TableColumnIndexAt(uint32_t aCellIndex)
+{
+  int32_t index = 0;
+  unused << mDoc->SendTableColumnIndexAt(mID, aCellIndex, &index);
+  return index;
+}
+
+int32_t
+ProxyAccessible::TableRowIndexAt(uint32_t aCellIndex)
+{
+  int32_t index = 0;
+  unused << mDoc->SendTableRowIndexAt(mID, aCellIndex, &index);
+  return index;
+}
+
+void
+ProxyAccessible::TableRowAndColumnIndicesAt(uint32_t aCellIndex,
+                                            int32_t* aRow, int32_t* aCol)
+{
+  unused << mDoc->SendTableRowAndColumnIndicesAt(mID, aCellIndex, aRow, aCol);
+}
+
+uint32_t
+ProxyAccessible::TableColumnExtentAt(uint32_t aRow, uint32_t aCol)
+{
+  uint32_t extent = 0;
+  unused << mDoc->SendTableColumnExtentAt(mID, aRow, aCol, &extent);
+  return extent;
+}
+
+uint32_t
+ProxyAccessible::TableRowExtentAt(uint32_t aRow, uint32_t aCol)
+{
+  uint32_t extent = 0;
+  unused << mDoc->SendTableRowExtentAt(mID, aRow, aCol, &extent);
+  return extent;
+}
+
+void
+ProxyAccessible::TableColumnDescription(uint32_t aCol, nsString& aDescription)
+{
+  unused << mDoc->SendTableColumnDescription(mID, aCol, &aDescription);
+}
+
+void
+ProxyAccessible::TableRowDescription(uint32_t aRow, nsString& aDescription)
+{
+  unused << mDoc->SendTableRowDescription(mID, aRow, &aDescription);
+}
+
+bool
+ProxyAccessible::TableColumnSelected(uint32_t aCol)
+{
+  bool selected = false;
+  unused << mDoc->SendTableColumnSelected(mID, aCol, &selected);
+  return selected;
+}
+
+bool
+ProxyAccessible::TableRowSelected(uint32_t aRow)
+{
+  bool selected = false;
+  unused << mDoc->SendTableRowSelected(mID, aRow, &selected);
+  return selected;
+}
+
+bool
+ProxyAccessible::TableCellSelected(uint32_t aRow, uint32_t aCol)
+{
+  bool selected = false;
+  unused << mDoc->SendTableCellSelected(mID, aRow, aCol, &selected);
+  return selected;
+}
+
+uint32_t
+ProxyAccessible::TableSelectedCellCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendTableSelectedCellCount(mID, &count);
+  return count;
+}
+
+uint32_t
+ProxyAccessible::TableSelectedColumnCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendTableSelectedColumnCount(mID, &count);
+  return count;
+}
+
+uint32_t
+ProxyAccessible::TableSelectedRowCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendTableSelectedRowCount(mID, &count);
+  return count;
+}
+
+void
+ProxyAccessible::TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs)
+{
+  nsAutoTArray<uint64_t, 30> cellIDs;
+  unused << mDoc->SendTableSelectedCells(mID, &cellIDs);
+  aCellIDs->SetCapacity(cellIDs.Length());
+  for (uint32_t i = 0; i < cellIDs.Length(); ++i) {
+    aCellIDs->AppendElement(mDoc->GetAccessible(cellIDs[i]));
+  }
+}
+
+void
+ProxyAccessible::TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices)
+{
+  unused << mDoc->SendTableSelectedCellIndices(mID, aCellIndices);
+}
+
+void
+ProxyAccessible::TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices)
+{
+  unused << mDoc->SendTableSelectedColumnIndices(mID, aColumnIndices);
+}
+
+void
+ProxyAccessible::TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices)
+{
+  unused << mDoc->SendTableSelectedRowIndices(mID, aRowIndices);
+}
+
+void
+ProxyAccessible::TableSelectColumn(uint32_t aCol)
+{
+  unused << mDoc->SendTableSelectColumn(mID, aCol);
+}
+
+void
+ProxyAccessible::TableSelectRow(uint32_t aRow)
+{
+  unused << mDoc->SendTableSelectRow(mID, aRow);
+}
+
+void
+ProxyAccessible::TableUnselectColumn(uint32_t aCol)
+{
+  unused << mDoc->SendTableUnselectColumn(mID, aCol);
+}
+
+void
+ProxyAccessible::TableUnselectRow(uint32_t aRow)
+{
+  unused << mDoc->SendTableUnselectRow(mID, aRow);
+}
+
+bool
+ProxyAccessible::TableIsProbablyForLayout()
+{
+  bool forLayout = false;
+  unused << mDoc->SendTableIsProbablyForLayout(mID, &forLayout);
+  return forLayout;
+}
+
+void
+ProxyAccessible::SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems)
+{
+  nsAutoTArray<uint64_t, 10> itemIDs;
+  unused << mDoc->SendSelectedItems(mID, &itemIDs);
+  aSelectedItems->SetCapacity(itemIDs.Length());
+  for (size_t i = 0; i < itemIDs.Length(); ++i) {
+    aSelectedItems->AppendElement(mDoc->GetAccessible(itemIDs[i]));
+  }
+}
+
+uint32_t
+ProxyAccessible::SelectedItemCount()
+{
+  uint32_t count = 0;
+  unused << mDoc->SendSelectedItemCount(mID, &count);
+  return count;
+}
+
+ProxyAccessible*
+ProxyAccessible::GetSelectedItem(uint32_t aIndex)
+{
+  uint64_t selectedItemID = 0;
+  bool ok = false;
+  unused << mDoc->SendGetSelectedItem(mID, aIndex, &selectedItemID, &ok);
+  return ok ? mDoc->GetAccessible(selectedItemID) : nullptr;
+}
+
+bool
+ProxyAccessible::IsItemSelected(uint32_t aIndex)
+{
+  bool selected = false;
+  unused << mDoc->SendIsItemSelected(mID, aIndex, &selected);
+  return selected;
+}
+ 
+bool
+ProxyAccessible::AddItemToSelection(uint32_t aIndex)
+{
+  bool success = false;
+  unused << mDoc->SendAddItemToSelection(mID, aIndex, &success);
+  return success;
+}
+
+bool
+ProxyAccessible::RemoveItemFromSelection(uint32_t aIndex)
+{
+  bool success = false;
+  unused << mDoc->SendRemoveItemFromSelection(mID, aIndex, &success);
+  return success;
+}
+
+bool
+ProxyAccessible::SelectAll()
+{
+  bool success = false;
+  unused << mDoc->SendSelectAll(mID, &success);
+  return success;
+}
+
+bool
+ProxyAccessible::UnselectAll()
+{
+  bool success = false;
+  unused << mDoc->SendUnselectAll(mID, &success);
+  return success;
+}
+
 }
 }
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -176,16 +176,91 @@ public:
   void DeleteText(int32_t aStartPos, int32_t aEndPos);
 
   void PasteText(int32_t aPosition);
 
   nsIntPoint ImagePosition(uint32_t aCoordType);
 
   nsIntSize ImageSize();
 
+  uint32_t StartOffset(bool* aOk);
+
+  uint32_t EndOffset(bool* aOk);
+
+  bool IsLinkValid();
+
+  uint32_t AnchorCount(bool* aOk);
+
+  void AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk);
+
+  ProxyAccessible* AnchorAt(uint32_t aIndex);
+
+  uint32_t LinkCount();
+
+  ProxyAccessible* LinkAt(const uint32_t& aIndex);
+
+  int32_t LinkIndexOf(ProxyAccessible* aLink);
+
+  int32_t LinkIndexAtOffset(uint32_t aOffset);
+
+  ProxyAccessible* TableOfACell();
+
+  uint32_t ColIdx();
+
+  uint32_t RowIdx();
+
+  uint32_t ColExtent();
+
+  uint32_t RowExtent();
+
+  void ColHeaderCells(nsTArray<uint64_t>* aCells);
+
+  void RowHeaderCells(nsTArray<uint64_t>* aCells);
+
+  bool IsCellSelected();
+
+  ProxyAccessible* TableCaption();
+  void TableSummary(nsString& aSummary);
+  uint32_t TableColumnCount();
+  uint32_t TableRowCount();
+  ProxyAccessible* TableCellAt(uint32_t aRow, uint32_t aCol);
+  int32_t TableCellIndexAt(uint32_t aRow, uint32_t aCol);
+  int32_t TableColumnIndexAt(uint32_t aCellIndex);
+  int32_t TableRowIndexAt(uint32_t aCellIndex);
+  void TableRowAndColumnIndicesAt(uint32_t aCellIndex,
+                                  int32_t* aRow, int32_t* aCol);
+  uint32_t TableColumnExtentAt(uint32_t aRow, uint32_t aCol);
+  uint32_t TableRowExtentAt(uint32_t aRow, uint32_t aCol);
+  void TableColumnDescription(uint32_t aCol, nsString& aDescription);
+  void TableRowDescription(uint32_t aRow, nsString& aDescription);
+  bool TableColumnSelected(uint32_t aCol);
+  bool TableRowSelected(uint32_t aRow);
+  bool TableCellSelected(uint32_t aRow, uint32_t aCol);
+  uint32_t TableSelectedCellCount();
+  uint32_t TableSelectedColumnCount();
+  uint32_t TableSelectedRowCount();
+  void TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs);
+  void TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices);
+  void TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices);
+  void TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices);
+  void TableSelectColumn(uint32_t aCol);
+  void TableSelectRow(uint32_t aRow);
+  void TableUnselectColumn(uint32_t aCol);
+  void TableUnselectRow(uint32_t aRow);
+  bool TableIsProbablyForLayout();
+
+  void SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems);
+  uint32_t SelectedItemCount();
+  ProxyAccessible* GetSelectedItem(uint32_t aIndex);
+  bool IsItemSelected(uint32_t aIndex);
+  bool AddItemToSelection(uint32_t aIndex);
+  bool RemoveItemFromSelection(uint32_t aIndex);
+  bool SelectAll();
+  bool UnselectAll();
+
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
--- a/accessible/tests/mochitest/table.js
+++ b/accessible/tests/mochitest/table.js
@@ -709,19 +709,19 @@ function testHeaderCells(aHeaderInfoMap)
        "Wrong number of row header cells for the cell " +
        prettyName(dataCellIdentifier));
 
     if (actualRowHeaderCellsCount == rowHeaderCellsCount) {
       for (var idx = 0; idx < rowHeaderCellsCount; idx++) {
         var rowHeaderCell = getAccessible(rowHeaderCells[idx]);
         var actualRowHeaderCell =
           actualRowHeaderCells.queryElementAt(idx, nsIAccessible);
-        ok(actualRowHeaderCell, rowHeaderCell,
-           "Wrong row header cell at index " + idx + " for the cell " +
-           prettyName(rowHeaderCells[idx]));
+        isObject(actualRowHeaderCell, rowHeaderCell,
+                 "Wrong row header cell at index " + idx + " for the cell " +
+                 dataCellIdentifier);
       }
     }
 
     // column header cells
     var colHeaderCells = aHeaderInfoMap[testIdx].columnHeaderCells;
     var colHeaderCellsCount = colHeaderCells.length;
     var actualColHeaderCells = dataCell.columnHeaderCells;
     var actualColHeaderCellsCount = actualColHeaderCells.length;
@@ -730,19 +730,19 @@ function testHeaderCells(aHeaderInfoMap)
        "Wrong number of column header cells for the cell " +
        prettyName(dataCellIdentifier));
 
     if (actualColHeaderCellsCount == colHeaderCellsCount) {
       for (var idx = 0; idx < colHeaderCellsCount; idx++) {
         var colHeaderCell = getAccessible(colHeaderCells[idx]);
         var actualColHeaderCell =
           actualColHeaderCells.queryElementAt(idx, nsIAccessible);
-        ok(actualColHeaderCell, colHeaderCell,
-           "Wrong column header cell at index " + idx + " for the cell " +
-           prettyName(colHeaderCells[idx]));
+        isObject(actualColHeaderCell, colHeaderCell,
+                 "Wrong column header cell at index " + idx + " for the cell " +
+                 dataCellIdentifier);
       }
     }
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // private implementation
 
--- a/accessible/tests/mochitest/table/test_headers_table.html
+++ b/accessible/tests/mochitest/table/test_headers_table.html
@@ -157,141 +157,277 @@
       testHeaderCells(headerInfoMap);
 
       //////////////////////////////////////////////////////////////////////////
       // @scope="rowgroup" and @scope="row"
 
       headerInfoMap = [
         {
           cell: "t7_r1c1",
-          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          rowHeaderCells: [ "t7_Mary", "t7_Females" ],
           columnHeaderCells: [ "t7_1km" ]
         },
         {
           cell: "t7_r1c2",
-          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          rowHeaderCells: [ "t7_Mary", "t7_Females" ],
           columnHeaderCells: [ "t7_5km" ]
         },
         {
           cell: "t7_r1c3",
-          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          rowHeaderCells: [ "t7_Mary", "t7_Females" ],
           columnHeaderCells: [ "t7_10km" ]
         },
         {
           cell: "t7_r2c1",
-          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
           columnHeaderCells: [ "t7_1km" ]
         },
         {
           cell: "t7_r2c2",
-          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
           columnHeaderCells: [ "t7_5km" ]
         },
         {
           cell: "t7_r2c3",
-          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
           columnHeaderCells: [ "t7_10km" ]
         },
         {
           cell: "t7_r3c1",
-          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          rowHeaderCells: [ "t7_Matt", "t7_Males" ],
           columnHeaderCells: [ "t7_1km" ]
         },
         {
           cell: "t7_r3c2",
-          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          rowHeaderCells: [ "t7_Matt", "t7_Males" ],
           columnHeaderCells: [ "t7_5km" ]
         },
         {
           cell: "t7_r3c3",
-          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          rowHeaderCells: [ "t7_Matt", "t7_Males" ],
           columnHeaderCells: [ "t7_10km" ]
         },
         {
           cell: "t7_r4c1",
-          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          rowHeaderCells: [ "t7_Todd", "t7_Males" ],
           columnHeaderCells: [ "t7_1km" ]
         },
         {
           cell: "t7_r4c2",
-          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          rowHeaderCells: [ "t7_Todd", "t7_Males" ],
           columnHeaderCells: [ "t7_5km" ]
         },
         {
           cell: "t7_r4c3",
-          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          rowHeaderCells: [ "t7_Todd", "t7_Males" ],
           columnHeaderCells: [ "t7_10km" ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
       //////////////////////////////////////////////////////////////////////////
       // @scope="colgroup" and @scope="col"
 
       headerInfoMap = [
         {
           cell: "t8_r1c1",
           rowHeaderCells: [ "t8_1km" ],
-          columnHeaderCells: [ "t7_Females", "t7_Mary" ]
+          columnHeaderCells: [ "t8_Mary", "t8_Females" ]
         },
         {
           cell: "t8_r1c2",
-          rowHeaderCells: [ "t8_5km" ],
-          columnHeaderCells: [ "t8_Females", "t8_Mary" ]
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
         },
         {
           cell: "t8_r1c3",
-          rowHeaderCells: [ "t8_10km" ],
-          columnHeaderCells: [ "t8_Females", "t8_Mary" ]
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t8_Matt", "t8_Males" ]
         },
         {
           cell: "t8_r1c4",
           rowHeaderCells: [ "t8_1km" ],
-          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+          columnHeaderCells: [ "t8_Todd", "t8_Males" ]
         },
         {
           cell: "t8_r2c1",
           rowHeaderCells: [ "t8_5km" ],
-          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+          columnHeaderCells: [ "t8_Mary", "t8_Females" ]
         },
         {
           cell: "t8_r2c2",
-          rowHeaderCells: [ "t8_10km" ],
-          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
         },
         {
           cell: "t8_r2c3",
-          rowHeaderCells: [ "t8_1km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Matt", "t8_Males" ]
         },
         {
           cell: "t8_r2c4",
           rowHeaderCells: [ "t8_5km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+          columnHeaderCells: [ "t8_Todd", "t8_Males" ]
         },
         {
           cell: "t8_r3c1",
           rowHeaderCells: [ "t8_10km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+          columnHeaderCells: [ "t8_Mary", "t8_Females" ]
         },
         {
           cell: "t8_r3c2",
-          rowHeaderCells: [ "t8_1km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
         },
         {
           cell: "t8_r3c3",
-          rowHeaderCells: [ "t8_5km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Matt", "t8_Males" ]
         },
         {
           cell: "t8_r3c4",
           rowHeaderCells: [ "t8_10km" ],
-          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+          columnHeaderCells: [ "t8_Todd", "t8_Males" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
+      //////////////////////////////////////////////////////////////////////////
+      // spanned table header cells (v1), @headers define header order
+
+      headerInfoMap = [
+        {
+          cell: "t9_r1c1",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r1c2",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r1c3",
+          rowHeaderCells: [ "t9_females", "t9_mary" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r2c1",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r2c2",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r2c3",
+          rowHeaderCells: [ "t9_females", "t9_betsy" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r3c1",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r3c2",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r3c3",
+          rowHeaderCells: [ "t9_males", "t9_matt" ],
+          columnHeaderCells: [ "t9_10km" ]
+        },
+        {
+          cell: "t9_r4c1",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_1km" ]
+        },
+        {
+          cell: "t9_r4c2",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_5km" ]
+        },
+        {
+          cell: "t9_r4c3",
+          rowHeaderCells: [ "t9_males", "t9_todd" ],
+          columnHeaderCells: [ "t9_10km" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
+      //////////////////////////////////////////////////////////////////////////
+      // spanned table header cells (v2), @headers define header order
+
+      headerInfoMap = [
+        {
+          cell: "t10_r1c1",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r1c2",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r1c3",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r1c4",
+          rowHeaderCells: [ "t10_1km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
+        },
+        {
+          cell: "t10_r2c1",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r2c2",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r2c3",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r2c4",
+          rowHeaderCells: [ "t10_5km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
+        },
+        {
+          cell: "t10_r3c1",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_females", "t10_mary" ]
+        },
+        {
+          cell: "t10_r3c2",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_females", "t10_betsy" ]
+        },
+        {
+          cell: "t10_r3c3",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_males", "t10_matt" ]
+        },
+        {
+          cell: "t10_r3c4",
+          rowHeaderCells: [ "t10_10km" ],
+          columnHeaderCells: [ "t10_males", "t10_todd" ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
       SimpleTest.finish();
     }
 
@@ -493,10 +629,85 @@
         <td id="t8_r3c2">55:38</td>
         <td id="t8_r3c3">57:04</td>
         <td id="t8_r3c4">50:35</td>
       </tr>
 
     </tbody>
   </table>
 
+  <table id="table9" border="1">
+    <caption>
+      Example 1 (row group headers):
+    </caption>
+    <tr>
+      <td colspan="2"><span class="offscreen">empty</span></td>
+      <th id="t9_1km" width="40">1 km</th>
+      <th id="t9_5km" width="35">5 km</th>
+      <th id="t9_10km" width="42">10 km</th>
+    </tr>
+    <tr>
+      <th id="t9_females" width="56" rowspan="2">Females</th>
+      <th id="t9_mary" width="39">Mary</th>
+      <td id="t9_r1c1" headers="t9_females t9_mary t9_1km">8:32</td>
+      <td id="t9_r1c2" headers="t9_females t9_mary t9_5km">28:04</td>
+      <td id="t9_r1c3" headers="t9_females t9_mary t9_10km">1:01:16</td>
+    </tr>
+    <tr>
+      <th id="t9_betsy">Betsy</th>
+      <td id="t9_r2c1" headers="t9_females t9_betsy t9_1km">7:43</td>
+      <td id="t9_r2c2" headers="t9_females t9_betsy t9_5km">26:47</td>
+      <td id="t9_r2c3" headers="t9_females t9_betsy t9_10km">55:38</td>
+    </tr>
+    <tr>
+      <th id="t9_males" rowspan="2">Males</th>
+      <th id="t9_matt">Matt</th>
+      <td id="t9_r3c1" headers="t9_males t9_matt t9_1km">7:55</td>
+      <td id="t9_r3c2" headers="t9_males t9_matt t9_5km">27:29</td>
+      <td id="t9_r3c3" headers="t9_males t9_matt t9_10km">57:04</td>
+    </tr>
+    <tr>
+      <th id="t9_todd">Todd</th>
+      <td id="t9_r4c1" headers="t9_males t9_todd t9_1km">7:01</td>
+      <td id="t9_r4c2" headers="t9_males t9_todd t9_5km">24:21</td>
+      <td id="t9_r4c3" headers="t9_males t9_todd t9_10km">50:35</td>
+    </tr>
+  </table>
+
+  <table id="table10" border="1">
+    <caption>
+      Example 2 (column group headers):
+    </caption>
+    <tr>
+      <td rowspan="2"><span class="offscreen">empty</span></td>
+      <th colspan="2" id="t10_females">Females</th>
+      <th colspan="2" id="t10_males">Males</th>
+    </tr>
+    <tr>
+      <th width="40" id="t10_mary">Mary</th>
+      <th width="35" id="t10_betsy">Betsy</th>
+      <th width="42" id="t10_matt">Matt</th>
+      <th width="42" id="t10_todd">Todd</th>
+    </tr>
+    <tr>
+      <th width="39" id="t10_1km">1 km</th>
+      <td headers="t10_females t10_mary t10_1km" id="t10_r1c1">8:32</td>
+      <td headers="t10_females t10_betsy t10_1km" id="t10_r1c2">7:43</td>
+      <td headers="t10_males t10_matt t10_1km" id="t10_r1c3">7:55</td>
+      <td headers="t10_males t10_todd t10_1km" id="t10_r1c4">7:01</td>
+    </tr>
+    <tr>
+      <th id="t10_5km">5 km</th>
+      <td headers="t10_females t10_mary t10_5km" id="t10_r2c1">28:04</td>
+      <td headers="t10_females t10_betsy t10_5km" id="t10_r2c2">26:47</td>
+      <td headers="t10_males t10_matt t10_5km" id="t10_r2c3">27:29</td>
+      <td headers="t10_males t10_todd t10_5km" id="t10_r2c4">24:21</td>
+    </tr>
+    <tr>
+      <th id="t10_10km">10 km</th>
+      <td headers="t10_females t10_mary t10_10km" id="t10_r3c1">1:01:16</td>
+      <td headers="t10_females t10_betsy t10_10km" id="t10_r3c2">55:38</td>
+      <td headers="t10_males t10_matt t10_10km" id="t10_r3c3">57:04</td>
+      <td headers="t10_males t10_todd t10_10km" id="t10_r3c4">50:35</td>
+    </tr>
+  </table>
 </body>
 </html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -991,16 +991,23 @@ pref("gfx.gralloc.fence-with-readpixels"
 // The url of the page used to display network error details.
 pref("b2g.neterror.url", "app://system.gaiamobile.org/net_error.html");
 
 // The origin used for the shared themes uri space.
 pref("b2g.theme.origin", "app://theme.gaiamobile.org");
 pref("dom.mozApps.themable", true);
 pref("dom.mozApps.selected_theme", "default_theme.gaiamobile.org");
 
+// Enable PAC generator for B2G.
+pref("network.proxy.pac_generator", true);
+
+// List of app origins to apply browsing traffic proxy setting, separated by
+// comma.  Specify '*' in the list to apply to all apps.
+pref("network.proxy.browsing.app_origins", "app://system.gaiamobile.org");
+
 // Enable Web Speech synthesis API
 pref("media.webspeech.synth.enabled", true);
 
 // Downloads API
 pref("dom.mozDownloads.enabled", true);
 pref("dom.downloads.max_retention_days", 7);
 
 // External Helper Application Handling
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -25,16 +25,20 @@ XPCOMUtils.defineLazyGetter(this, "libcu
   return libcutils;
 });
 #endif
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gPACGenerator",
+                                   "@mozilla.org/pac-generator;1",
+                                   "nsIPACGenerator");
+
 // Once Bug 731746 - Allow chrome JS object to implement nsIDOMEventTarget
 // is resolved this helper could be removed.
 var SettingsListener = {
   _callbacks: {},
 
   init: function sl_init() {
     if ('mozSettings' in navigator && navigator.mozSettings) {
       navigator.mozSettings.onsettingchange = this.onchange.bind(this);
@@ -477,16 +481,50 @@ SettingsListener.observe("theme.selected
   if (currentTheme != newTheme) {
     debug("New theme selected " + value);
     Services.prefs.setCharPref('dom.mozApps.selected_theme', newTheme);
     Services.prefs.savePrefFile(null);
     Services.obs.notifyObservers(null, 'app-theme-changed', newTheme);
   }
 });
 
+// =================== Proxy server ======================
+(function setupBrowsingProxySettings() {
+  function setPAC() {
+    let usePAC;
+    try {
+      usePAC = Services.prefs.getBoolPref('network.proxy.pac_generator');
+    } catch (ex) {}
+
+    if (usePAC) {
+      Services.prefs.setCharPref('network.proxy.autoconfig_url',
+                                 gPACGenerator.generate());
+      Services.prefs.setIntPref('network.proxy.type',
+                                Ci.nsIProtocolProxyService.PROXYCONFIG_PAC);
+    }
+  }
+
+  SettingsListener.observe('browser.proxy.enabled', false, function(value) {
+    Services.prefs.setBoolPref('network.proxy.browsing.enabled', value);
+    setPAC();
+  });
+
+  SettingsListener.observe('browser.proxy.host', '', function(value) {
+    Services.prefs.setCharPref('network.proxy.browsing.host', value);
+    setPAC();
+  });
+
+  SettingsListener.observe('browser.proxy.port', 0, function(value) {
+    Services.prefs.setIntPref('network.proxy.browsing.port', value);
+    setPAC();
+  });
+
+  setPAC();
+})();
+
 // =================== Various simple mapping  ======================
 let settingsToObserve = {
   'accessibility.screenreader_quicknav_modes': {
     prefName: 'accessibility.accessfu.quicknav_modes',
     resetToPref: true,
     defaultValue: ''
   },
   'accessibility.screenreader_quicknav_index': {
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -668,16 +668,19 @@
 @RESPATH@/components/EngineeringModeAPI.js
 @RESPATH@/components/EngineeringModeService.js
 
 #ifdef MOZ_DEBUG
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
 #endif
 
+@RESPATH@/components/PACGenerator.js
+@RESPATH@/components/PACGenerator.manifest
+
 ; Modules
 @RESPATH@/modules/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -404,16 +404,18 @@ support-files =
 [browser_selectTabAtIndex.js]
 [browser_ssl_error_reports.js]
 [browser_star_hsts.js]
 [browser_subframe_favicons_not_used.js]
 [browser_syncui.js]
 skip-if = e10s # Bug 1137087 - browser_tabopen_reflows.js fails if this was previously run with e10s
 [browser_tabDrop.js]
 skip-if = buildapp == 'mulet' || e10s
+[browser_tabReorder.js]
+skip-if = buildapp == 'mulet'
 [browser_tabMatchesInAwesomebar.js]
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
 skip-if = e10s || os == 'linux' # Bug 1093373, bug 1104755
 [browser_tab_detach_restore.js]
 [browser_tab_drag_drop_perwindow.js]
 skip-if = buildapp == 'mulet'
 [browser_tab_dragdrop.js]
 skip-if = buildapp == 'mulet'
--- a/browser/base/content/test/general/browser_fxa_profile_channel.html
+++ b/browser/base/content/test/general/browser_fxa_profile_channel.html
@@ -6,17 +6,17 @@
 </head>
 <body>
 <script>
   window.onload = function(){
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
       detail: {
         id: "account_updates",
         message: {
-          command: "profile:image:change",
+          command: "profile:change",
           data: {
             uid: "abc123",
           },
         },
       },
     });
 
     window.dispatchEvent(event);
--- a/browser/base/content/test/general/browser_fxa_profile_channel.js
+++ b/browser/base/content/test/general/browser_fxa_profile_channel.js
@@ -29,17 +29,17 @@ let gTests = [
 
           tabOpened = true;
         });
 
         let client = new FxAccountsProfileChannel({
           content_uri: HTTP_PATH,
         });
 
-        makeObserver(FxAccountsCommon.ONPROFILE_IMAGE_CHANGE_NOTIFICATION, function (subject, topic, data) {
+        makeObserver(FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
           Assert.ok(tabOpened);
           Assert.equal(data, "abc123");
           resolve();
           gBrowser.removeCurrentTab();
         });
 
         gBrowser.selectedTab = gBrowser.addTab(properUrl);
       });
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_tabReorder.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  let initialTabsLength = gBrowser.tabs.length;
+
+  let newTab1 = gBrowser.selectedTab = gBrowser.addTab("about:robots", {skipAnimation: true});
+  let newTab2 = gBrowser.selectedTab = gBrowser.addTab("about:about", {skipAnimation: true});
+  let newTab3 = gBrowser.selectedTab = gBrowser.addTab("about:config", {skipAnimation: true});
+  registerCleanupFunction(function () {
+    while (gBrowser.tabs.length > initialTabsLength) {
+      gBrowser.removeTab(gBrowser.tabs[initialTabsLength]);
+    }
+  });
+
+  is(gBrowser.tabs.length, initialTabsLength + 3, "new tabs are opened");
+  is(gBrowser.tabs[initialTabsLength], newTab1, "newTab1 position is correct");
+  is(gBrowser.tabs[initialTabsLength + 1], newTab2, "newTab2 position is correct");
+  is(gBrowser.tabs[initialTabsLength + 2], newTab3, "newTab3 position is correct");
+
+  let dataTransfer;
+  let trapDrag = function(event) {
+    dataTransfer = event.dataTransfer;
+  };
+  window.addEventListener("dragstart", trapDrag, true);
+  registerCleanupFunction(function () {
+    window.removeEventListener("dragstart", trapDrag, true);
+  });
+
+  let windowUtil = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                          getInterface(Components.interfaces.nsIDOMWindowUtils);
+  let ds = Components.classes["@mozilla.org/widget/dragservice;1"].
+           getService(Components.interfaces.nsIDragService);
+
+  function dragAndDrop(tab1, tab2, copy) {
+    let ctrlKey = copy;
+    let altKey = copy;
+
+    let rect = tab1.getBoundingClientRect();
+    let x = rect.width / 2;
+    let y = rect.height / 2;
+    let diffX = 10;
+
+    ds.startDragSession();
+    try {
+      EventUtils.synthesizeMouse(tab1, x, y, { type: "mousedown" }, window);
+      EventUtils.synthesizeMouse(tab1, x + diffX, y, { type: "mousemove" }, window);
+
+      dataTransfer.dropEffect = copy ? "copy" : "move";
+
+      let event = window.document.createEvent("DragEvents");
+      event.initDragEvent("dragover", true, true, window, 0,
+                          tab2.boxObject.screenX + x + diffX,
+                          tab2.boxObject.screenY + y,
+                          x + diffX, y, ctrlKey, altKey, false, false, 0, null, dataTransfer);
+      windowUtil.dispatchDOMEventViaPresShell(tab2, event, true);
+
+      event = window.document.createEvent("DragEvents");
+      event.initDragEvent("drop", true, true, window, 0,
+                          tab2.boxObject.screenX + x + diffX,
+                          tab2.boxObject.screenY + y,
+                          x + diffX, y, ctrlKey, altKey, false, false, 0, null, dataTransfer);
+      windowUtil.dispatchDOMEventViaPresShell(tab2, event, true);
+
+      EventUtils.synthesizeMouse(tab2, x + diffX, y, { type: "mouseup" }, window);
+    } finally {
+      ds.endDragSession(true);
+    }
+  }
+
+  dragAndDrop(newTab1, newTab2, false);
+  is(gBrowser.tabs.length, initialTabsLength + 3, "tabs are still there");
+  is(gBrowser.tabs[initialTabsLength], newTab2, "newTab2 and newTab1 are swapped");
+  is(gBrowser.tabs[initialTabsLength + 1], newTab1, "newTab1 and newTab2 are swapped");
+  is(gBrowser.tabs[initialTabsLength + 2], newTab3, "newTab3 stays same place");
+
+  dragAndDrop(newTab2, newTab1, true);
+  is(gBrowser.tabs.length, initialTabsLength + 4, "a tab is duplicated");
+  is(gBrowser.tabs[initialTabsLength], newTab2, "newTab2 stays same place");
+  is(gBrowser.tabs[initialTabsLength + 1], newTab1, "newTab1 stays same place");
+  is(gBrowser.tabs[initialTabsLength + 3], newTab3, "a new tab is inserted before newTab3");
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -613,16 +613,19 @@
 @RESPATH@/components/MozKeyboard.js
 @RESPATH@/components/InputMethod.manifest
 
 #ifdef MOZ_DEBUG
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
 #endif
 
+@RESPATH@/components/PACGenerator.js
+@RESPATH@/components/PACGenerator.manifest
+
 ; Modules
 @RESPATH@/browser/modules/*
 @RESPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_URL_CLASSIFIER
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3234,59 +3234,59 @@ nsDocShell::AddWeakScrollObserver(nsIScr
 NS_IMETHODIMP
 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
 {
   nsWeakPtr obs = do_GetWeakReference(aObserver);
   return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 void
-nsDocShell::NotifyAsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos)
+nsDocShell::NotifyAsyncPanZoomStarted()
 {
   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
   while (iter.HasMore()) {
     nsWeakPtr ref = iter.GetNext();
     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
     if (obs) {
-      obs->AsyncPanZoomStarted(aScrollPos);
+      obs->AsyncPanZoomStarted();
     } else {
       mScrollObservers.RemoveElement(ref);
     }
   }
 
   // Also notify child docshell
   for (uint32_t i = 0; i < mChildList.Length(); ++i) {
     nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
     if (kid) {
       nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
-      docShell->NotifyAsyncPanZoomStarted(aScrollPos);
+      docShell->NotifyAsyncPanZoomStarted();
     }
   }
 }
 
 void
-nsDocShell::NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
+nsDocShell::NotifyAsyncPanZoomStopped()
 {
   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
   while (iter.HasMore()) {
     nsWeakPtr ref = iter.GetNext();
     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
     if (obs) {
-      obs->AsyncPanZoomStopped(aScrollPos);
+      obs->AsyncPanZoomStopped();
     } else {
       mScrollObservers.RemoveElement(ref);
     }
   }
 
   // Also notify child docshell
   for (uint32_t i = 0; i < mChildList.Length(); ++i) {
     nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
     if (kid) {
       nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
-      docShell->NotifyAsyncPanZoomStopped(aScrollPos);
+      docShell->NotifyAsyncPanZoomStopped();
     }
   }
 }
 
 NS_IMETHODIMP
 nsDocShell::NotifyScrollObservers()
 {
   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -257,20 +257,20 @@ public:
     FireOnLocationChange(this, nullptr, mCurrentURI,
                          LOCATION_CHANGE_SAME_DOCUMENT);
   }
 
   nsresult HistoryTransactionRemoved(int32_t aIndex);
 
   // Notify Scroll observers when an async panning/zooming transform
   // has started being applied
-  void NotifyAsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos);
+  void NotifyAsyncPanZoomStarted();
   // Notify Scroll observers when an async panning/zooming transform
   // is no longer applied
-  void NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos);
+  void NotifyAsyncPanZoomStopped();
 
   // Add new profile timeline markers to this docShell. This will only add
   // markers if the docShell is currently recording profile timeline markers.
   // See nsIDocShell::recordProfileTimelineMarkers
   void AddProfileTimelineMarker(const char* aName,
                                 TracingMetadata aMetaData);
   void AddProfileTimelineMarker(mozilla::UniquePtr<TimelineMarker>& aMarker);
 
--- a/docshell/base/nsIScrollObserver.h
+++ b/docshell/base/nsIScrollObserver.h
@@ -6,37 +6,37 @@
 
 #ifndef nsIScrollObserver_h___
 #define nsIScrollObserver_h___
 
 #include "nsISupports.h"
 #include "Units.h"
 
 #define NS_ISCROLLOBSERVER_IID \
-  { 0x00bc10e3, 0xaa59, 0x4aa3, \
-    { 0x88, 0xe9, 0x43, 0x0a, 0x01, 0xa3, 0x88, 0x04 } }
+  { 0xaa5026eb, 0x2f88, 0x4026, \
+    { 0xa4, 0x6b, 0xf4, 0x59, 0x6b, 0x4e, 0xdf, 0x00 } }
 
 class nsIScrollObserver : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLOBSERVER_IID)
 
   /**
    * Called when the scroll position of some element has changed.
    */
   virtual void ScrollPositionChanged() = 0;
 
   /**
    * Called when an async panning/zooming transform has started being applied
    * and passed the scroll offset
    */
-  virtual void AsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos) {};
+  virtual void AsyncPanZoomStarted() {};
 
   /**
    * Called when an async panning/zooming transform is no longer applied
    * and passed the scroll offset
    */
-  virtual void AsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos) {};
+  virtual void AsyncPanZoomStopped() {};
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScrollObserver, NS_ISCROLLOBSERVER_IID)
 
 #endif /* nsIScrollObserver_h___ */
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -154,17 +154,17 @@ WindowNamedPropertiesHandler::getOwnProp
   aDesc.setAttributes(JSPROP_ENUMERATE);
   return true;
 }
 
 bool
 WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
                                              JS::Handle<JSObject*> aProxy,
                                              JS::Handle<jsid> aId,
-                                             JS::MutableHandle<JSPropertyDescriptor> aDesc,
+                                             JS::Handle<JSPropertyDescriptor> aDesc,
                                              JS::ObjectOpResult &result) const
 {
   ErrorResult rv;
   rv.ThrowTypeError(MSG_DEFINEPROPERTY_ON_GSP);
   rv.ReportErrorWithMessage(aCx);
   return false;
 }
 
--- a/dom/base/WindowNamedPropertiesHandler.h
+++ b/dom/base/WindowNamedPropertiesHandler.h
@@ -23,17 +23,17 @@ public:
   getOwnPropDescriptor(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                        JS::Handle<jsid> aId,
                        bool /* unused */,
                        JS::MutableHandle<JSPropertyDescriptor> aDesc)
                        const override;
   virtual bool
   defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                  JS::Handle<jsid> aId,
-                 JS::MutableHandle<JSPropertyDescriptor> aDesc,
+                 JS::Handle<JSPropertyDescriptor> aDesc,
                  JS::ObjectOpResult &result) const override;
   virtual bool
   ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
                JS::AutoIdVector& aProps) const override;
   virtual bool
   delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
           JS::ObjectOpResult &aResult) const override;
   virtual bool
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -616,17 +616,17 @@ public:
   virtual bool getOwnPropertyDescriptor(JSContext* cx,
                                         JS::Handle<JSObject*> proxy,
                                         JS::Handle<jsid> id,
                                         JS::MutableHandle<JSPropertyDescriptor> desc)
                                         const override;
   virtual bool defineProperty(JSContext* cx,
                               JS::Handle<JSObject*> proxy,
                               JS::Handle<jsid> id,
-                              JS::MutableHandle<JSPropertyDescriptor> desc,
+                              JS::Handle<JSPropertyDescriptor> desc,
                               JS::ObjectOpResult &result) const override;
   virtual bool ownPropertyKeys(JSContext *cx,
                                JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const override;
   virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id,
                        JS::ObjectOpResult &result) const override;
   virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
@@ -777,17 +777,17 @@ nsOuterWindowProxy::getOwnPropertyDescri
 
   return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
 nsOuterWindowProxy::defineProperty(JSContext* cx,
                                    JS::Handle<JSObject*> proxy,
                                    JS::Handle<jsid> id,
-                                   JS::MutableHandle<JSPropertyDescriptor> desc,
+                                   JS::Handle<JSPropertyDescriptor> desc,
                                    JS::ObjectOpResult &result) const
 {
   int32_t index = GetArrayIndexFromId(cx, id);
   if (IsArrayIndex(index)) {
     // Spec says to Reject whether this is a supported index or not,
     // since we have no indexed setter or indexed creator.  It is up
     // to the caller to decide whether to throw a TypeError.
     return result.failCantDefineWindowElement();
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1434,17 +1434,17 @@ XrayResolveOwnProperty(JSContext* cx, JS
   }
 
   return true;
 }
 
 bool
 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                    JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                   JS::MutableHandle<JSPropertyDescriptor> desc,
+                   JS::Handle<JSPropertyDescriptor> desc,
                    JS::ObjectOpResult &result, bool *defined)
 {
   if (!js::IsProxy(obj))
     return true;
 
   const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
   return handler->defineProperty(cx, wrapper, id, desc, result, defined);
 }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2461,17 +2461,17 @@ XrayResolveOwnProperty(JSContext* cx, JS
  * id and desc are the parameters for the property to be defined.
  * result is the out-parameter indicating success (read it only if
  *     this returns true and also sets *defined to true).
  * defined will be set to true if a property was set as a result of this call.
  */
 bool
 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                    JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                   JS::MutableHandle<JSPropertyDescriptor> desc,
+                   JS::Handle<JSPropertyDescriptor> desc,
                    JS::ObjectOpResult &result,
                    bool *defined);
 
 /**
  * Add to props the property keys of all indexed or named properties of obj and
  * operations, attributes and constants of the interfaces for obj.
  *
  * wrapper is the Xray JS object.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10241,17 +10241,17 @@ class CGDOMJSProxyHandler_getOwnPropDesc
 
 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
     def __init__(self, descriptor):
         # The usual convention is to name the ObjectOpResult out-parameter
         # `result`, but that name is a bit overloaded around here.
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'proxy'),
                 Argument('JS::Handle<jsid>', 'id'),
-                Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
+                Argument('JS::Handle<JSPropertyDescriptor>', 'desc'),
                 Argument('JS::ObjectOpResult&', 'opresult'),
                 Argument('bool*', 'defined')]
         ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
         set = ""
 
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -190,17 +190,17 @@ BaseDOMProxyHandler::getOwnPropertyDescr
                                               MutableHandle<JSPropertyDescriptor> desc) const
 {
   return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
                               desc);
 }
 
 bool
 DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                                MutableHandle<JSPropertyDescriptor> desc,
+                                Handle<JSPropertyDescriptor> desc,
                                 JS::ObjectOpResult &result, bool *defined) const
 {
   if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
     return result.failGetterOnly();
   }
 
   if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
     return result.succeed();
@@ -247,17 +247,17 @@ DOMProxyHandler::set(JSContext *cx, Hand
     if (!js::GetObjectProto(cx, proxy, &proto)) {
       return false;
     }
     if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
       return false;
     }
   }
 
-  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, &desc, result);
+  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, desc, result);
 }
 
 bool
 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id, JS::ObjectOpResult &result) const
 {
   JS::Rooted<JSObject*> expando(cx);
   if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -100,24 +100,24 @@ protected:
 class DOMProxyHandler : public BaseDOMProxyHandler
 {
 public:
   MOZ_CONSTEXPR DOMProxyHandler()
     : BaseDOMProxyHandler(&family)
   {}
 
   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                      JS::MutableHandle<JSPropertyDescriptor> desc,
+                      JS::Handle<JSPropertyDescriptor> desc,
                       JS::ObjectOpResult &result) const override
   {
     bool unused;
     return defineProperty(cx, proxy, id, desc, result, &unused);
   }
   virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                              JS::MutableHandle<JSPropertyDescriptor> desc,
+                              JS::Handle<JSPropertyDescriptor> desc,
                               JS::ObjectOpResult &result, bool *defined) const;
   bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                JS::ObjectOpResult &result) const override;
   bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::ObjectOpResult& result) const override;
   bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                     const override;
   bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -570,18 +570,16 @@ BrowserElementChild.prototype = {
 
     sendAsyncMsg('metachange', meta);
   },
 
   _ScrollViewChangeHandler: function(e) {
     e.stopPropagation();
     let detail = {
       state: e.state,
-      scrollX: e.scrollX,
-      scrollY: e.scrollY,
     };
     sendAsyncMsg('scrollviewchange', detail);
   },
 
   _selectionStateChangedHandler: function(e) {
     e.stopPropagation();
 
     if (!this._isContentWindowCreated) {
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -267,17 +267,18 @@ BrowserElementParent.prototype = {
         this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
       cancelCallback();
       return;
     }
 
     /* username and password */
     let detail = {
       host:     authDetail.host,
-      realm:    authDetail.realm
+      realm:    authDetail.realm,
+      isProxy:  authDetail.isProxy
     };
 
     evt = this._createEvent('usernameandpasswordrequired', detail,
                             /* cancelable */ true);
     Cu.exportFunction(function(username, password) {
       if (callbackCalled)
         return;
       callbackCalled = true;
--- a/dom/browser-element/BrowserElementPromptService.jsm
+++ b/dom/browser-element/BrowserElementPromptService.jsm
@@ -378,27 +378,56 @@ BrowserElementAuthPrompt.prototype = {
   },
 
   _createAuthDetail: function(channel, authInfo) {
     let [hostname, httpRealm] = this._getAuthTarget(channel, authInfo);
     return {
       host:             hostname,
       realm:            httpRealm,
       username:         authInfo.username,
+      isProxy:          !!(authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY),
       isOnlyPassword:   !!(authInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
     };
   },
 
+  // The code is taken from nsLoginManagerPrompter.js, with slight
+  // modification for parameter name consistency here.
   _getAuthTarget : function (channel, authInfo) {
-    let hostname = this._getFormattedHostname(channel.URI);
+    let hostname, realm;
+
+    // If our proxy is demanding authentication, don't use the
+    // channel's actual destination.
+    if (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
+      if (!(channel instanceof Ci.nsIProxiedChannel))
+        throw new Error("proxy auth needs nsIProxiedChannel");
+
+      let info = channel.proxyInfo;
+      if (!info)
+        throw new Error("proxy auth needs nsIProxyInfo");
+
+      // Proxies don't have a scheme, but we'll use "moz-proxy://"
+      // so that it's more obvious what the login is for.
+      var idnService = Cc["@mozilla.org/network/idn-service;1"].
+                       getService(Ci.nsIIDNService);
+      hostname = "moz-proxy://" +
+                  idnService.convertUTF8toACE(info.host) +
+                  ":" + info.port;
+      realm = authInfo.realm;
+      if (!realm)
+        realm = hostname;
+
+      return [hostname, realm];
+    }
+
+    hostname = this._getFormattedHostname(channel.URI);
 
     // If a HTTP WWW-Authenticate header specified a realm, that value
     // will be available here. If it wasn't set or wasn't HTTP, we'll use
     // the formatted hostname instead.
-    let realm = authInfo.realm;
+    realm = authInfo.realm;
     if (!realm)
       realm = hostname;
 
     return [hostname, realm];
   },
 
   _getFormattedHostname : function(uri) {
     let scheme = uri.scheme;
--- a/dom/browser-element/mochitest/browserElement_Auth.js
+++ b/dom/browser-element/mochitest/browserElement_Auth.js
@@ -61,28 +61,97 @@ function testHttpAuth(e) {
 
   // Will authenticate with correct password, prompt should not be
   // called again.
   iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail);
   iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
     iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
     iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail);
     is(e.detail, 'http auth success', 'expect authentication to succeed');
-    SimpleTest.executeSoon(testAuthJarNoInterfere);
+    SimpleTest.executeSoon(testProxyAuth);
   });
 
   is(e.detail.realm, 'http_realm', 'expected realm matches');
   is(e.detail.host, 'http://test', 'expected host matches');
+  is(e.detail.isProxy, false, 'expected isProxy is false');
   e.preventDefault();
 
   SimpleTest.executeSoon(function() {
     e.detail.authenticate("httpuser", "httppass");
   });
 }
 
+function testProxyAuth(e) {
+  // The testingSJS simulates the 407 proxy authentication required response
+  // for proxy server, which will trigger the browser element to send prompt
+  // event with proxy infomation.
+  var testingSJS = 'http://test/tests/dom/browser-element/mochitest/file_http_407_response.sjs';
+  var mozproxy;
+
+  function onUserNameAndPasswordRequired(e) {
+    iframe.removeEventListener("mozbrowserusernameandpasswordrequired",
+                               onUserNameAndPasswordRequired);
+    iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
+      iframe.removeEventListener("mozbrowsertitlechange", onTitleChange);
+      iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail);
+      is(e.detail, 'http auth success', 'expect authentication to succeed');
+      SimpleTest.executeSoon(testAuthJarNoInterfere);
+    });
+
+    is(e.detail.realm, 'http_realm', 'expected realm matches');
+    is(e.detail.host, mozproxy, 'expected host matches');
+    is(e.detail.isProxy, true, 'expected isProxy is true');
+    e.preventDefault();
+
+    SimpleTest.executeSoon(function() {
+      e.detail.authenticate("proxyuser", "proxypass");
+    });
+  }
+
+  // Resolve proxy information used by the test suite, we need it to validate
+  // whether the proxy information delivered with the prompt event is correct.
+  var resolveCallback = SpecialPowers.wrapCallbackObject({
+    QueryInterface: function (iid) {
+      const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports];
+
+      if (!interfaces.some( function(v) { return iid.equals(v) } )) {
+        throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+      }
+      return this;
+    },
+
+    onProxyAvailable: function (req, channel, pi, status) {
+      isnot(pi, null, 'expected proxy information available');
+      if (pi) {
+        mozproxy = "moz-proxy://" + pi.host + ":" + pi.port;
+      }
+      iframe.addEventListener("mozbrowserusernameandpasswordrequired",
+                              onUserNameAndPasswordRequired);
+
+      iframe.src = testingSJS;
+    }
+  });
+
+  var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                  .getService(SpecialPowers.Ci.nsIIOService);
+  var pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"]
+            .getService();
+  var systemPrincipal = SpecialPowers.Services.scriptSecurityManager
+                                     .getSystemPrincipal();
+  var channel = ioService.newChannel2(testingSJS,
+                                      null,
+                                      null,
+                                      null,
+                                      systemPrincipal,
+                                      null,
+                                      SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                      SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
+  pps.asyncResolve(channel, 0, resolveCallback);
+}
+
 function testAuthJarNoInterfere(e) {
   var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1']
     .getService(SpecialPowers.Ci.nsIHttpAuthManager);
   var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
                .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
   var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                   .getService(SpecialPowers.Ci.nsIIOService);
   var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_http_407_response.sjs
@@ -0,0 +1,16 @@
+function handleRequest(request, response)
+{
+  var auth = "";
+  try {
+    auth = request.getHeader("Proxy-Authorization");
+  } catch(e) {}
+
+  if (auth == "Basic cHJveHl1c2VyOnByb3h5cGFzcw==") {
+    response.setStatusLine("1.1", 200, "OK");
+    response.write("<html><head><title>http auth success</title></head><html>");
+  } else {
+    response.setStatusLine("1.1", 407, "Proxy Authentication Required");
+    response.setHeader("Proxy-Authenticate", "Basic realm=\"http_realm\"");
+    response.write("<html><head><title>http auth failed</title></head><html>");
+  }
+}
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -106,16 +106,17 @@ support-files =
   file_browserElement_XFrameOptionsSameOrigin.html
   file_bug709759.sjs
   file_bug741717.sjs
   file_download_bin.sjs
   file_empty.html
   file_empty_script.js
   file_focus.html
   file_http_401_response.sjs
+  file_http_407_response.sjs
   file_inputmethod.html
   file_post_request.html
   file_wyciwyg.html
   file_NestedFramesOuter_CopyPaste.html
 
 # Note: browserElementTestHelpers.js looks at the test's filename to determine
 # whether the test should be OOP.  "_oop_" signals OOP, "_inproc_" signals in
 # process.  Default is OOP.
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -77,37 +77,46 @@ public:
                     Action* aQuotaIOThreadAction)
     : mContext(aContext)
     , mThreadsafeHandle(aContext->CreateThreadsafeHandle())
     , mManager(aManager)
     , mQuotaIOThreadAction(aQuotaIOThreadAction)
     , mInitiatingThread(NS_GetCurrentThread())
     , mResult(NS_OK)
     , mState(STATE_INIT)
+    , mCanceled(false)
     , mNeedsQuotaRelease(false)
   {
     MOZ_ASSERT(mContext);
     MOZ_ASSERT(mManager);
     MOZ_ASSERT(mInitiatingThread);
   }
 
   nsresult Dispatch()
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     MOZ_ASSERT(mState == STATE_INIT);
 
     mState = STATE_CALL_WAIT_FOR_OPEN_ALLOWED;
     nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mState = STATE_COMPLETE;
       Clear();
     }
     return rv;
   }
 
+  void Cancel()
+  {
+    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
+    MOZ_ASSERT(!mCanceled);
+    mCanceled = true;
+    mQuotaIOThreadAction->CancelOnInitiatingThread();
+  }
+
 private:
   class SyncResolver final : public Action::Resolver
   {
   public:
     SyncResolver()
       : mResolved(false)
       , mResult(NS_OK)
     { }
@@ -147,32 +156,33 @@ private:
     STATE_ENSURE_ORIGIN_INITIALIZED,
     STATE_RUNNING,
     STATE_COMPLETING,
     STATE_COMPLETE
   };
 
   void Clear()
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     MOZ_ASSERT(mContext);
     mContext = nullptr;
     mManager = nullptr;
     mQuotaIOThreadAction = nullptr;
   }
 
   nsRefPtr<Context> mContext;
   nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
   nsRefPtr<Manager> mManager;
   nsRefPtr<Action> mQuotaIOThreadAction;
   nsCOMPtr<nsIThread> mInitiatingThread;
   nsresult mResult;
   QuotaInfo mQuotaInfo;
   nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;
   State mState;
+  Atomic<bool> mCanceled;
   bool mNeedsQuotaRelease;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 };
 
 NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
@@ -218,16 +228,22 @@ Context::QuotaInitRunnable::Run()
 
   nsRefPtr<SyncResolver> resolver = new SyncResolver();
 
   switch(mState) {
     // -----------------------------------
     case STATE_CALL_WAIT_FOR_OPEN_ALLOWED:
     {
       MOZ_ASSERT(NS_IsMainThread());
+
+      if (mCanceled) {
+        resolver->Resolve(NS_ERROR_ABORT);
+        break;
+      }
+
       QuotaManager* qm = QuotaManager::GetOrCreate();
       if (!qm) {
         resolver->Resolve(NS_ERROR_FAILURE);
         break;
       }
 
       nsRefPtr<ManagerId> managerId = mManager->GetManagerId();
       nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
@@ -261,16 +277,21 @@ Context::QuotaInitRunnable::Run()
     }
     // ------------------------------
     case STATE_WAIT_FOR_OPEN_ALLOWED:
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       mNeedsQuotaRelease = true;
 
+      if (mCanceled) {
+        resolver->Resolve(NS_ERROR_ABORT);
+        break;
+      }
+
       QuotaManager* qm = QuotaManager::Get();
       MOZ_ASSERT(qm);
 
       nsRefPtr<OfflineStorage> offlineStorage =
         OfflineStorage::Register(mThreadsafeHandle, mQuotaInfo);
       mOfflineStorage = new nsMainThreadPtrHolder<OfflineStorage>(offlineStorage);
 
       mState = STATE_ENSURE_ORIGIN_INITIALIZED;
@@ -284,16 +305,21 @@ Context::QuotaInitRunnable::Run()
     // ----------------------------------
     case STATE_ENSURE_ORIGIN_INITIALIZED:
     {
       // Can't assert quota IO thread because its an idle thread that can get
       // recreated.  At least assert we're not on main thread or owning thread.
       MOZ_ASSERT(!NS_IsMainThread());
       MOZ_ASSERT(_mOwningThread.GetThread() != PR_GetCurrentThread());
 
+      if (mCanceled) {
+        resolver->Resolve(NS_ERROR_ABORT);
+        break;
+      }
+
       QuotaManager* qm = QuotaManager::Get();
       MOZ_ASSERT(qm);
       nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
                                                   mQuotaInfo.mGroup,
                                                   mQuotaInfo.mOrigin,
                                                   mQuotaInfo.mIsApp,
                                                   getter_AddRefs(mQuotaInfo.mDir));
       if (NS_FAILED(rv)) {
@@ -313,17 +339,17 @@ Context::QuotaInitRunnable::Run()
       mQuotaIOThreadAction->RunOnTarget(resolver, mQuotaInfo);
       MOZ_ASSERT(resolver->Resolved());
 
       break;
     }
     // -------------------
     case STATE_COMPLETING:
     {
-      NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+      NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
       if (mQuotaIOThreadAction) {
         mQuotaIOThreadAction->CompleteOnInitiatingThread(mResult);
       }
       mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage);
       mState = STATE_COMPLETE;
 
       if (mNeedsQuotaRelease) {
         // Unlock the quota dir if we locked it previously
@@ -380,39 +406,39 @@ public:
     MOZ_ASSERT(mTarget);
     MOZ_ASSERT(mAction);
     MOZ_ASSERT(mQuotaInfo.mDir);
     MOZ_ASSERT(mInitiatingThread);
   }
 
   nsresult Dispatch()
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     MOZ_ASSERT(mState == STATE_INIT);
 
     mState = STATE_RUN_ON_TARGET;
     nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mState = STATE_COMPLETE;
       Clear();
     }
     return rv;
   }
 
   virtual bool
   MatchesCacheId(CacheId aCacheId) const override
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     return mAction->MatchesCacheId(aCacheId);
   }
 
   virtual void
   Cancel() override
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     mAction->CancelOnInitiatingThread();
   }
 
   virtual void Resolve(nsresult aRv) override
   {
     MOZ_ASSERT(mTarget == NS_GetCurrentThread());
     MOZ_ASSERT(mState == STATE_RUNNING);
 
@@ -442,17 +468,17 @@ private:
   {
     MOZ_ASSERT(mState == STATE_COMPLETE);
     MOZ_ASSERT(!mContext);
     MOZ_ASSERT(!mAction);
   }
 
   void Clear()
   {
-    NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     MOZ_ASSERT(mContext);
     MOZ_ASSERT(mAction);
     mContext->RemoveActivity(this);
     mContext = nullptr;
     mAction = nullptr;
   }
 
   enum State
@@ -557,17 +583,17 @@ Context::ActionRunnable::Run()
       // for this invariant violation.
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
         mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
       break;
     }
     // -------------------
     case STATE_COMPLETING:
     {
-      NS_ASSERT_OWNINGTHREAD(Action::Resolver);
+      NS_ASSERT_OWNINGTHREAD(ActionRunnable);
       mAction->CompleteOnInitiatingThread(mResult);
       mState = STATE_COMPLETE;
       // Explicitly cleanup here as the destructor could fire on any of
       // the threads we have bounced through.
       Clear();
       break;
     }
     // -----------------
@@ -674,19 +700,19 @@ Context::ThreadsafeHandle::ContextDestro
 }
 
 // static
 already_AddRefed<Context>
 Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
 {
   nsRefPtr<Context> context = new Context(aManager);
 
-  nsRefPtr<QuotaInitRunnable> runnable =
-    new QuotaInitRunnable(context, aManager, aQuotaIOThreadAction);
-  nsresult rv = runnable->Dispatch();
+  context->mInitRunnable = new QuotaInitRunnable(context, aManager,
+                                                 aQuotaIOThreadAction);
+  nsresult rv = context->mInitRunnable->Dispatch();
   if (NS_FAILED(rv)) {
     // Shutdown must be delayed until all Contexts are destroyed.  Shutdown
     // must also prevent any new Contexts from being constructed.  Crash
     // for this invariant violation.
     MOZ_CRASH("Failed to dispatch QuotaInitRunnable.");
   }
 
   return context.forget();
@@ -718,16 +744,22 @@ Context::Dispatch(nsIEventTarget* aTarge
   MOZ_ASSERT(STATE_CONTEXT_READY);
   DispatchAction(aTarget, aAction);
 }
 
 void
 Context::CancelAll()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
+
+  if (mInitRunnable) {
+    MOZ_ASSERT(mState == STATE_CONTEXT_INIT);
+    mInitRunnable->Cancel();
+  }
+
   mState = STATE_CONTEXT_CANCELED;
   mPendingActions.Clear();
   {
     ActivityList::ForwardIterator iter(mActivityList);
     while (iter.HasMore()) {
       iter.GetNext()->Cancel();
     }
   }
@@ -802,16 +834,19 @@ Context::DispatchAction(nsIEventTarget* 
 }
 
 void
 Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
                      nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
 
+  MOZ_ASSERT(mInitRunnable);
+  mInitRunnable = nullptr;
+
   mQuotaInfo = aQuotaInfo;
 
   // Always save the offline storage to ensure QuotaManager does not shutdown
   // before the Context has gone away.
   MOZ_ASSERT(!mOfflineStorage);
   mOfflineStorage = aOfflineStorage;
 
   if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) {
--- a/dom/cache/Context.h
+++ b/dom/cache/Context.h
@@ -172,16 +172,17 @@ private:
                    nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage);
 
   already_AddRefed<ThreadsafeHandle>
   CreateThreadsafeHandle();
 
   nsRefPtr<Manager> mManager;
   State mState;
   QuotaInfo mQuotaInfo;
+  nsRefPtr<QuotaInitRunnable> mInitRunnable;
   nsTArray<PendingAction> mPendingActions;
 
   // Weak refs since activites must remove themselves from this list before
   // being destroyed by calling RemoveActivity().
   typedef nsTObserverArray<Activity*> ActivityList;
   ActivityList mActivityList;
 
   // The ThreadsafeHandle may have a strong ref back to us.  This creates
--- a/dom/cache/test/mochitest/test_cache_put.js
+++ b/dom/cache/test/mochitest/test_cache_put.js
@@ -10,17 +10,41 @@ Promise.all([fetch(url),
   is(undefined, result, 'Successful put() should resolve undefined');
   return cache.match(url);
 }).then(function(response) {
   ok(response, 'match() should find resppnse that was previously put()');
   ok(response.url.endsWith(url), 'matched response should match original url');
   return Promise.all([fetchResponse.text(),
                       response.text()]);
 }).then(function(results) {
-  // suppress large assert spam unless its relevent
+  // suppress large assert spam unless it's relevent
   if (results[0] !== results[1]) {
     is(results[0], results[1], 'stored response body should match original');
   }
+
+  // Now, try to overwrite the request with a different response object.
+  return cache.put(url, new Response("overwritten"));
+}).then(function() {
+  return cache.matchAll(url);
+}).then(function(result) {
+  is(result.length, 1, "Only one entry should exist");
+  return result[0].text();
+}).then(function(body) {
+  is(body, "overwritten", "The cache entry should be successfully overwritten");
+
+  // Now, try to write a URL with a fragment
+  return cache.put(url + "#fragment", new Response("more overwritten"));
+}).then(function() {
+  return cache.matchAll(url + "#differentFragment");
+}).then(function(result) {
+  is(result.length, 1, "Only one entry should exist");
+  return result[0].text();
+}).then(function(body) {
+  is(body, "more overwritten", "The cache entry should be successfully overwritten");
+
+  // TODO: Verify that trying to store a response with an error raises a TypeError
+  // when bug 1147178 is fixed.
+
   return caches.delete('putter' + context);
 }).then(function(deleted) {
   ok(deleted, "The cache should be deleted successfully");
   testDone();
 });
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -117,26 +117,42 @@ public:
 private:
     void Uniform1uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
     void Uniform2uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
     void Uniform3uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
     void Uniform4uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
 
 public:
     void Uniform1uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform1uiv_base(loc,arr.Length(), arr.Elements());
+        Uniform1uiv_base(loc, arr.Length(), arr.Elements());
     }
     void Uniform2uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform2uiv_base(loc,arr.Length(), arr.Elements());
+        Uniform2uiv_base(loc, arr.Length(), arr.Elements());
     }
     void Uniform3uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform3uiv_base(loc,arr.Length(), arr.Elements());
+        Uniform3uiv_base(loc, arr.Length(), arr.Elements());
     }
     void Uniform4uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
-        Uniform4uiv_base(loc,arr.Length(), arr.Elements());
+        Uniform4uiv_base(loc, arr.Length(), arr.Elements());
+    }
+    void Uniform1uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
+        arr.ComputeLengthAndData();
+        Uniform1uiv_base(loc, arr.Length(), arr.Data());
+    }
+    void Uniform2uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
+        arr.ComputeLengthAndData();
+        Uniform2uiv_base(loc, arr.Length(), arr.Data());
+    }
+    void Uniform3uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
+        arr.ComputeLengthAndData();
+        Uniform3uiv_base(loc, arr.Length(), arr.Data());
+    }
+    void Uniform4uiv(WebGLUniformLocation* loc, const dom::Uint32Array& arr) {
+        arr.ComputeLengthAndData();
+        Uniform4uiv_base(loc, arr.Length(), arr.Data());
     }
 
 private:
     void UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
                                  size_t arrayLength, const GLfloat* data);
     void UniformMatrix3x2fv_base(WebGLUniformLocation* loc, bool transpose,
                                  size_t arrayLength, const GLfloat* data);
     void UniformMatrix2x4fv_base(WebGLUniformLocation* loc, bool transpose,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2426,17 +2426,17 @@ EventStateManager::DoScrollText(nsIScrol
   ScrollbarStyles overflowStyle = aScrollableFrame->GetScrollbarStyles();
   if (overflowStyle.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
     actualDevPixelScrollAmount.x = 0;
   }
   if (overflowStyle.mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
     actualDevPixelScrollAmount.y = 0;
   }
 
-  nsIScrollableFrame::ScrollSnapMode snapMode = nsIScrollableFrame::DISABLE_SNAP;
+  nsIScrollbarMediator::ScrollSnapMode snapMode = nsIScrollbarMediator::DISABLE_SNAP;
   nsIAtom* origin = nullptr;
   switch (aEvent->deltaMode) {
     case nsIDOMWheelEvent::DOM_DELTA_LINE:
       origin = nsGkAtoms::mouseWheel;
       snapMode = nsIScrollableFrame::ENABLE_SNAP;
       break;
     case nsIDOMWheelEvent::DOM_DELTA_PAGE:
       origin = nsGkAtoms::pages;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2387,17 +2387,18 @@ TabChild::RecvRealTouchEvent(const Widge
   // Dispatch event to content (potentially a long-running operation)
   nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
 
   if (!gfxPrefs::AsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
   }
 
-  mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId);
+  mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId,
+                                    nsEventStatus_eIgnore);
   return true;
 }
 
 bool
 TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  const uint64_t& aInputBlockId)
 {
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -1,360 +1,357 @@
 /* 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/.
- *
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
  * ManifestProcessor
  * Implementation of processing algorithms from:
  * http://www.w3.org/2008/webapps/manifest/
  *
  * Creates manifest processor that lets you process a JSON file
  * or individual parts of a manifest object. A manifest is just a
  * standard JS object that has been cleaned up.
  *
- *   .process({jsonText,manifestURL,docURL});
+ *   .process(jsonText, manifestURL, docURL);
  *
  * TODO: The constructor should accept the UA's supported orientations.
  * TODO: The constructor should accept the UA's supported display modes.
- * TODO: hook up developer tools to console. (1086997).
+ * TODO: hook up developer tools to issueDeveloperWarning (1086997).
  */
+/*globals Components*/
 /*exported EXPORTED_SYMBOLS */
-/*JSLint options in comment below: */
-/*globals Components, XPCOMUtils*/
 'use strict';
+
 this.EXPORTED_SYMBOLS = ['ManifestProcessor'];
-const imports = {};
 const {
   utils: Cu,
   classes: Cc,
   interfaces: Ci
 } = Components;
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+const imports = {};
+Cu.import('resource://gre/modules/Services.jsm', imports);
 Cu.importGlobalProperties(['URL']);
-XPCOMUtils.defineLazyModuleGetter(imports, 'Services',
-  'resource://gre/modules/Services.jsm');
-imports.netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil);
-// Helper function extracts values from manifest members
-// and reports conformance violations.
-function extractValue({
-  objectName,
-  object,
-  property,
-  expectedType,
-  trim
-}, console) {
-  const value = object[property];
-  const isArray = Array.isArray(value);
-  // We need to special-case "array", as it's not a JS primitive.
-  const type = (isArray) ? 'array' : typeof value;
-  if (type !== expectedType) {
-    if (type !== 'undefined') {
-      let msg = `Expected the ${objectName}'s ${property} `;
-      msg += `member to a be a ${expectedType}.`;
-      console.log(msg);
-    }
-    return undefined;
-  }
-  // Trim string and returned undefined if the empty string.
-  const shouldTrim = expectedType === 'string' && value && trim;
-  if (shouldTrim) {
-    return value.trim() || undefined;
-  }
-  return value;
-}
-const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
+const securityManager = imports.Services.scriptSecurityManager;
+const netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil);
+const defaultDisplayMode = 'browser';
+const displayModes = new Set([
+  'fullscreen',
+  'standalone',
+  'minimal-ui',
   'browser'
 ]);
-const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
-  'portrait-primary', 'portrait-secondary', 'landscape-primary',
+const orientationTypes = new Set([
+  'any',
+  'natural',
+  'landscape',
+  'portrait',
+  'portrait-primary',
+  'portrait-secondary',
+  'landscape-primary',
   'landscape-secondary'
 ]);
-const {
-  ConsoleAPI
-} = Cu.import('resource://gre/modules/devtools/Console.jsm');
 
-class ManifestProcessor {
+this.ManifestProcessor = function ManifestProcessor() {};
+/**
+ * process method: processes json text into a clean manifest
+ * that conforms with the W3C specification.
+ * @param jsonText - the JSON string to be processd.
+ * @param manifestURL - the URL of the manifest, to resolve URLs.
+ * @param docURL - the URL of the owner doc, for security checks
+ */
+this.ManifestProcessor.prototype.process = function({
+  jsonText: jsonText,
+  manifestURL: manifestURL,
+  docLocation: docURL
+}) {
+  /*
+   * This helper function is used to extract values from manifest members.
+   * It also reports conformance violations.
+   */
+  function extractValue(obj) {
+    let value = obj.object[obj.property];
+    //we need to special-case "array", as it's not a JS primitive
+    const type = (Array.isArray(value)) ? 'array' : typeof value;
 
-  constructor() {}
-
-  static get defaultDisplayMode() {
-    return 'browser';
+    if (type !== obj.expectedType) {
+      if (type !== 'undefined') {
+        let msg = `Expected the ${obj.objectName}'s ${obj.property}`;
+        msg += `member to a be a ${obj.expectedType}.`;
+        issueDeveloperWarning(msg);
+      }
+      value = undefined;
+    }
+    return value;
   }
 
-  static get displayModes() {
-    return displayModes;
+  function issueDeveloperWarning(msg) {
+    //https://bugzilla.mozilla.org/show_bug.cgi?id=1086997
   }
 
-  static get orientationTypes() {
-    return orientationTypes;
+  function processNameMember(manifest) {
+    const obj = {
+      objectName: 'manifest',
+      object: manifest,
+      property: 'name',
+      expectedType: 'string'
+    };
+    let value = extractValue(obj);
+    return (value) ? value.trim() : value;
+  }
+
+  function processShortNameMember(manifest) {
+    const obj = {
+      objectName: 'manifest',
+      object: manifest,
+      property: 'short_name',
+      expectedType: 'string'
+    };
+    let value = extractValue(obj);
+    return (value) ? value.trim() : value;
   }
 
-  // process method: processes json text into a clean manifest
-  // that conforms with the W3C specification. Takes an object
-  // expecting the following dictionary items:
-  //  * jsonText: the JSON string to be processd.
-  //  * manifestURL: the URL of the manifest, to resolve URLs.
-  //  * docURL: the URL of the owner doc, for security checks.
-  process({
-    jsonText, manifestURL, docURL
-  }) {
-    const console = new ConsoleAPI({
-      prefix: 'Web Manifest: '
-    });
-    let rawManifest = {};
+  function processOrientationMember(manifest) {
+    const obj = {
+      objectName: 'manifest',
+      object: manifest,
+      property: 'orientation',
+      expectedType: 'string'
+    };
+    let value = extractValue(obj);
+    value = (value) ? value.trim() : undefined;
+    //The spec special-cases orientation to return the empty string
+    return (orientationTypes.has(value)) ? value : '';
+  }
+
+  function processDisplayMember(manifest) {
+    const obj = {
+      objectName: 'manifest',
+      object: manifest,
+      property: 'display',
+      expectedType: 'string'
+    };
+
+    let value = extractValue(obj);
+    value = (value) ? value.trim() : value;
+    return (displayModes.has(value)) ? value : defaultDisplayMode;
+  }
+
+  function processScopeMember(manifest, manifestURL, docURL, startURL) {
+    const spec = {
+        objectName: 'manifest',
+        object: manifest,
+        property: 'scope',
+        expectedType: 'string',
+        dontTrim: true
+      },
+      value = extractValue(spec);
+    let scopeURL;
     try {
-      rawManifest = JSON.parse(jsonText);
-    } catch (e) {}
-    if (typeof rawManifest !== 'object' || rawManifest === null) {
-      let msg = 'Manifest needs to be an object.';
-      console.warn(msg);
-      rawManifest = {};
-    }
-    const processedManifest = {
-      start_url: processStartURLMember(rawManifest, manifestURL, docURL),
-      display: processDisplayMember(rawManifest),
-      orientation: processOrientationMember(rawManifest),
-      name: processNameMember(rawManifest),
-      icons: IconsProcessor.process(rawManifest, manifestURL, console),
-      short_name: processShortNameMember(rawManifest),
-    };
-    processedManifest.scope = processScopeMember(rawManifest, manifestURL,
-      docURL, processedManifest.start_url);
-    return processedManifest;
-
-    function processNameMember(aManifest) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'name',
-        expectedType: 'string',
-        trim: true
-      };
-      return extractValue(spec, console);
-    }
-
-    function processShortNameMember(aManifest) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'short_name',
-        expectedType: 'string',
-        trim: true
-      };
-      return extractValue(spec, console);
+      scopeURL = new URL(value, manifestURL);
+    } catch (e) {
+      let msg = 'The URL of scope is invalid.';
+      issueDeveloperWarning(msg);
+      return undefined;
     }
 
-    function processOrientationMember(aManifest) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'orientation',
-        expectedType: 'string',
-        trim: true
-      };
-      const value = extractValue(spec, console);
-      if (ManifestProcessor.orientationTypes.has(value)) {
-        return value;
-      }
-      // The spec special-cases orientation to return the empty string.
-      return '';
+    if (scopeURL.origin !== docURL.origin) {
+      let msg = 'Scope needs to be same-origin as Document.';
+      issueDeveloperWarning(msg);
+      return undefined;
     }
 
-    function processDisplayMember(aManifest) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'display',
-        expectedType: 'string',
-        trim: true
-      };
-      const value = extractValue(spec, console);
-      if (ManifestProcessor.displayModes.has(value)) {
-        return value;
-      }
-      return ManifestProcessor.defaultDisplayMode;
+    //If start URL is not within scope of scope URL:
+    if (startURL && startURL.origin !== scopeURL.origin || !startURL.pathname.startsWith(scopeURL.pathname)) {
+      let msg = 'The start URL is outside the scope, so scope is invalid.';
+      issueDeveloperWarning(msg);
+      return undefined;
+    }
+    return scopeURL;
+  }
+
+  function processStartURLMember(manifest, manifestURL, docURL) {
+    const obj = {
+      objectName: 'manifest',
+      object: manifest,
+      property: 'start_url',
+      expectedType: 'string'
+    };
+
+    let value = extractValue(obj),
+      result = new URL(docURL),
+      targetURI = makeURI(result),
+      sameOrigin = false,
+      potentialResult,
+      referrerURI;
+
+    if (value === undefined || value === '') {
+      return result;
     }
 
-    function processScopeMember(aManifest, aManifestURL, aDocURL, aStartURL) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'scope',
-        expectedType: 'string',
-        trim: false
-      };
-      const value = extractValue(spec, console);
-      let scopeURL;
-      try {
-        scopeURL = new URL(value, aManifestURL);
-      } catch (e) {
-        let msg = 'The URL of scope is invalid.';
-        console.warn(msg);
-        return undefined;
-      }
-      if (scopeURL.origin !== aDocURL.origin) {
-        let msg = 'Scope needs to be same-origin as Document.';
-        console.warn(msg);
-        return undefined;
-      }
-      // If start URL is not within scope of scope URL:
-      let isSameOrigin = aStartURL && aStartURL.origin !== scopeURL.origin;
-      if (isSameOrigin || !aStartURL.pathname.startsWith(scopeURL.pathname)) {
-        let msg =
-          'The start URL is outside the scope, so scope is invalid.';
-        console.warn(msg);
-        return undefined;
-      }
-      return scopeURL;
-    }
-
-    function processStartURLMember(aManifest, aManifestURL, aDocURL) {
-      const spec = {
-        objectName: 'manifest',
-        object: aManifest,
-        property: 'start_url',
-        expectedType: 'string',
-        trim: false
-      };
-      let result = new URL(aDocURL);
-      const value = extractValue(spec, console);
-      if (value === undefined || value === '') {
-        return result;
-      }
-      let potentialResult;
-      try {
-        potentialResult = new URL(value, aManifestURL);
-      } catch (e) {
-        console.warn('Invalid URL.');
-        return result;
-      }
-      if (potentialResult.origin !== aDocURL.origin) {
-        let msg = 'start_url must be same origin as document.';
-        console.warn(msg);
-      } else {
-        result = potentialResult;
-      }
+    try {
+      potentialResult = new URL(value, manifestURL);
+    } catch (e) {
+      issueDeveloperWarning('Invalid URL.');
       return result;
     }
-  }
-}
-this.ManifestProcessor = ManifestProcessor;
-
-class IconsProcessor {
+    referrerURI = makeURI(potentialResult);
+    try {
+      securityManager.checkSameOriginURI(referrerURI, targetURI, false);
+      sameOrigin = true;
+    } catch (e) {}
+    if (!sameOrigin) {
+      let msg = 'start_url must be same origin as document.';
+      issueDeveloperWarning(msg);
+    } else {
+      result = potentialResult;
+    }
+    return result;
 
-  constructor() {
-    throw new Error('Static use only.');
-  }
-
-  static get onlyDecimals() {
-    return /^\d+$/;
+    //Converts a URL to a Gecko URI
+    function makeURI(webURL) {
+      return imports.Services.io.newURI(webURL.toString(), null, null);
+    }
   }
 
-  static get anyRegEx() {
-    return new RegExp('any', 'i');
-  }
+  //Constants used by IconsProcessor
+  const onlyDecimals = /^\d+$/,
+    anyRegEx = new RegExp('any', 'i');
+
+  function IconsProcessor() {}
+  IconsProcessor.prototype.processIcons = function(manifest, baseURL) {
+    const obj = {
+        objectName: 'manifest',
+        object: manifest,
+        property: 'icons',
+        expectedType: 'array'
+      },
+      icons = [];
+    let value = extractValue(obj);
 
-  static process(aManifest, aBaseURL, console) {
-    const spec = {
-      objectName: 'manifest',
-      object: aManifest,
-      property: 'icons',
-      expectedType: 'array',
-      trim: false
-    };
-    const icons = [];
-    const value = extractValue(spec, console);
     if (Array.isArray(value)) {
-      // Filter out icons whose "src" is not useful.
-      value.filter(item => !!processSrcMember(item, aBaseURL))
-        .map(toIconObject)
-        .forEach(icon => icons.push(icon));
+      //filter out icons with no "src" or src is empty string
+      let processableIcons = value.filter(
+        icon => icon && Object.prototype.hasOwnProperty.call(icon, 'src') && icon.src !== ''
+      );
+      for (let potentialIcon of processableIcons) {
+        let src = processSrcMember(potentialIcon, baseURL)
+        if(src !== undefined){
+          let icon = {
+            src: src,
+            type: processTypeMember(potentialIcon),
+            sizes: processSizesMember(potentialIcon),
+            density: processDensityMember(potentialIcon)
+          };
+          icons.push(icon);
+        }
+      }
     }
     return icons;
 
-    function toIconObject(aIconData) {
-      return {
-        src: processSrcMember(aIconData, aBaseURL),
-        type: processTypeMember(aIconData),
-        sizes: processSizesMember(aIconData),
-        density: processDensityMember(aIconData)
-      };
+    function processTypeMember(icon) {
+      const charset = {},
+        hadCharset = {},
+        obj = {
+          objectName: 'icon',
+          object: icon,
+          property: 'type',
+          expectedType: 'string'
+        };
+      let value = extractValue(obj),
+        isParsable = (typeof value === 'string' && value.length > 0);
+      value = (isParsable) ? netutil.parseContentType(value.trim(), charset, hadCharset) : undefined;
+      return (value === '') ? undefined : value;
     }
 
-    function processTypeMember(aIcon) {
-      const charset = {};
-      const hadCharset = {};
-      const spec = {
-        objectName: 'icon',
-        object: aIcon,
-        property: 'type',
-        expectedType: 'string',
-        trim: true
-      };
-      let value = extractValue(spec, console);
-      if (value) {
-        value = imports.netutil.parseContentType(value, charset, hadCharset);
-      }
-      return value || undefined;
+    function processDensityMember(icon) {
+      const hasDensity = Object.prototype.hasOwnProperty.call(icon, 'density'),
+        rawValue = (hasDensity) ? icon.density : undefined,
+        value = parseFloat(rawValue),
+        result = (Number.isNaN(value) || value === +Infinity || value <= 0) ? 1.0 : value;
+      return result;
     }
 
-    function processDensityMember(aIcon) {
-      const value = parseFloat(aIcon.density);
-      const validNum = Number.isNaN(value) || value === +Infinity || value <=
-        0;
-      return (validNum) ? 1.0 : value;
-    }
-
-    function processSrcMember(aIcon, aBaseURL) {
-      const spec = {
-        objectName: 'icon',
-        object: aIcon,
-        property: 'src',
-        expectedType: 'string',
-        trim: false
-      };
-      const value = extractValue(spec, console);
+    function processSrcMember(icon, baseURL) {
+      const obj = {
+          objectName: 'icon',
+          object: icon,
+          property: 'src',
+          expectedType: 'string'
+        },
+        value = extractValue(obj);
       let url;
-      if (value && value.length) {
+      if (typeof value === 'string' && value.trim() !== '') {
         try {
-          url = new URL(value, aBaseURL);
+          url = new URL(value, baseURL);
         } catch (e) {}
       }
       return url;
     }
 
-    function processSizesMember(aIcon) {
+    function processSizesMember(icon) {
       const sizes = new Set(),
-        spec = {
+        obj = {
           objectName: 'icon',
-          object: aIcon,
+          object: icon,
           property: 'sizes',
-          expectedType: 'string',
-          trim: true
-        },
-        value = extractValue(spec, console);
+          expectedType: 'string'
+        };
+      let value = extractValue(obj);
+      value = (value) ? value.trim() : value;
       if (value) {
-        // Split on whitespace and filter out invalid values.
-        value.split(/\s+/)
-          .filter(isValidSizeValue)
-          .forEach(size => sizes.add(size));
+        //split on whitespace and filter out invalid values
+        let validSizes = value.split(/\s+/).filter(isValidSizeValue);
+        validSizes.forEach((size) => sizes.add(size));
       }
       return sizes;
-      // Implementation of HTML's link@size attribute checker.
-      function isValidSizeValue(aSize) {
-        const size = aSize.toLowerCase();
-        if (IconsProcessor.anyRegEx.test(aSize)) {
+
+      /*
+       * Implementation of HTML's link@size attribute checker
+       */
+      function isValidSizeValue(size) {
+        if (anyRegEx.test(size)) {
           return true;
         }
+        size = size.toLowerCase();
         if (!size.contains('x') || size.indexOf('x') !== size.lastIndexOf('x')) {
           return false;
         }
-        // Split left of x for width, after x for height.
-        const widthAndHeight = size.split('x');
-        const w = widthAndHeight.shift();
-        const h = widthAndHeight.join('x');
-        const validStarts = !w.startsWith('0') && !h.startsWith('0');
-        const validDecimals = IconsProcessor.onlyDecimals.test(w + h);
-        return (validStarts && validDecimals);
+
+        //split left of x for width, after x for height
+        const width = size.substring(0, size.indexOf('x'));
+        const height = size.substring(size.indexOf('x') + 1, size.length);
+        const isValid = !(height.startsWith('0') || width.startsWith('0') || !onlyDecimals.test(width + height));
+        return isValid;
       }
     }
+  };
+
+  function processIconsMember(manifest, manifestURL) {
+    const iconsProcessor = new IconsProcessor();
+    return iconsProcessor.processIcons(manifest, manifestURL);
   }
-}
+
+  //Processing starts here!
+  let manifest = {};
+
+  try {
+    manifest = JSON.parse(jsonText);
+    if (typeof manifest !== 'object' || manifest === null) {
+      let msg = 'Manifest needs to be an object.';
+      issueDeveloperWarning(msg);
+      manifest = {};
+    }
+  } catch (e) {
+    issueDeveloperWarning(e);
+  }
+
+  const processedManifest = {
+    start_url: processStartURLMember(manifest, manifestURL, docURL),
+    display: processDisplayMember(manifest),
+    orientation: processOrientationMember(manifest),
+    name: processNameMember(manifest),
+    icons: processIconsMember(manifest, manifestURL),
+    short_name: processShortNameMember(manifest)
+  };
+  processedManifest.scope = processScopeMember(manifest, manifestURL, docURL, processedManifest.start_url);
+  return processedManifest;
+};
\ No newline at end of file
--- a/dom/manifest/test/common.js
+++ b/dom/manifest/test/common.js
@@ -11,10 +11,10 @@ const bsp = SpecialPowers.Cu.import('res
   lineTerminators = '\u000D\u000A\u2028\u2029',
   whiteSpace = `${seperators}${lineTerminators}`,
   typeTests = [1, null, {},
     [], false
   ],
   data = {
     jsonText: '{}',
     manifestURL: manifestURL,
-    docURL: docLocation
+    docLocation: docLocation
   };
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -351,20 +351,19 @@ MediaDecoderReader::RequestAudioData()
   return p;
 }
 
 MediaTaskQueue*
 MediaDecoderReader::EnsureTaskQueue()
 {
   if (!mTaskQueue) {
     MOZ_ASSERT(!mTaskQueueIsBorrowed);
-    RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
-    NS_ENSURE_TRUE(decodePool, nullptr);
-
-    mTaskQueue = new MediaTaskQueue(decodePool.forget());
+    RefPtr<SharedThreadPool> pool(GetMediaThreadPool());
+    MOZ_DIAGNOSTIC_ASSERT(pool);
+    mTaskQueue = new MediaTaskQueue(pool.forget());
   }
 
   return mTaskQueue;
 }
 
 void
 MediaDecoderReader::BreakCycles()
 {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -244,19 +244,19 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Set up our task queue.
-  RefPtr<SharedThreadPool> threadPool(
-      SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
-  mTaskQueue = new MediaTaskQueue(threadPool.forget());
+  RefPtr<SharedThreadPool> pool(GetMediaThreadPool());
+  MOZ_DIAGNOSTIC_ASSERT(pool);
+  mTaskQueue = new MediaTaskQueue(pool.forget());
 
   static bool sPrefCacheInit = false;
   if (!sPrefCacheInit) {
     sPrefCacheInit = true;
     Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
                                  "media.video-queue.default-size",
                                  MAX_VIDEO_QUEUE_SIZE);
     Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
@@ -1277,17 +1277,18 @@ static const char* const gMachineStateSt
   "WAIT_FOR_RESOURCES",
   "WAIT_FOR_CDM",
   "DECODING_FIRSTFRAME",
   "DORMANT",
   "DECODING",
   "SEEKING",
   "BUFFERING",
   "COMPLETED",
-  "SHUTDOWN"
+  "SHUTDOWN",
+  "ERROR"
 };
 
 void MediaDecoderStateMachine::SetState(State aState)
 {
   AssertCurrentThreadInMonitor();
   if (mState == aState) {
     return;
   }
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -192,19 +192,19 @@ IsValidVideoRegion(const nsIntSize& aFra
     aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
     aPicture.width * aPicture.height != 0 &&
     aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
     aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
     aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
     aDisplay.width * aDisplay.height != 0;
 }
 
-TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool()
+TemporaryRef<SharedThreadPool> GetMediaThreadPool()
 {
-  return SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Decode"),
+  return SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Playback"),
                                Preferences::GetUint("media.num-decode-threads", 25));
 }
 
 bool
 ExtractH264CodecDetails(const nsAString& aCodec,
                         int16_t& aProfile,
                         int16_t& aLevel)
 {
@@ -296,27 +296,27 @@ GenerateRandomPathName(nsCString& aOutSa
 
   return NS_OK;
 }
 
 class CreateTaskQueueTask : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
-    mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
+    mTaskQueue = new MediaTaskQueue(GetMediaThreadPool());
     return NS_OK;
   }
   nsRefPtr<MediaTaskQueue> mTaskQueue;
 };
 
 class CreateFlushableTaskQueueTask : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
-    mTaskQueue = new FlushableMediaTaskQueue(GetMediaDecodeThreadPool());
+    mTaskQueue = new FlushableMediaTaskQueue(GetMediaThreadPool());
     return NS_OK;
   }
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
 };
 
 already_AddRefed<MediaTaskQueue>
 CreateMediaDecodeTaskQueue()
 {
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -214,17 +214,17 @@ private:
   T& mVar;
   const T mValue;
 };
 
 class SharedThreadPool;
 
 // Returns the thread pool that is shared amongst all decoder state machines
 // for decoding streams.
-TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool();
+TemporaryRef<SharedThreadPool> GetMediaThreadPool();
 
 enum H264_PROFILE {
   H264_PROFILE_UNKNOWN                     = 0,
   H264_PROFILE_BASE                        = 0x42,
   H264_PROFILE_MAIN                        = 0x4D,
   H264_PROFILE_EXTENDED                    = 0x58,
   H264_PROFILE_HIGH                        = 0x64,
 };
--- a/dom/media/eme/CDMCaps.cpp
+++ b/dom/media/eme/CDMCaps.cpp
@@ -189,9 +189,20 @@ CDMCaps::AutoLock::GetKeyStatusesForSess
   for (size_t i = 0; i < mData.mKeyStatuses.Length(); i++) {
     const auto& key = mData.mKeyStatuses[i];
     if (key.mSessionId.Equals(aSessionId)) {
       aOutKeyStatuses.AppendElement(key);
     }
   }
 }
 
+void
+CDMCaps::AutoLock::GetSessionIdsForKeyId(const CencKeyId& aKeyId,
+                                         nsTArray<nsCString>& aOutSessionIds)
+{
+  for (const auto& keyStatus : mData.mKeyStatuses) {
+    if (keyStatus.mId == aKeyId) {
+      aOutSessionIds.AppendElement(NS_ConvertUTF16toUTF8(keyStatus.mSessionId));
+    }
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/eme/CDMCaps.h
+++ b/dom/media/eme/CDMCaps.h
@@ -63,16 +63,19 @@ public:
 
     // Returns true if key status changed,
     // i.e. the key status changed from usable to expired.
     bool SetKeyStatus(const CencKeyId& aKeyId, const nsString& aSessionId, GMPMediaKeyStatus aStatus);
 
     void GetKeyStatusesForSession(const nsAString& aSessionId,
                                   nsTArray<KeyStatus>& aOutKeyStatuses);
 
+    void GetSessionIdsForKeyId(const CencKeyId& aKeyId,
+                               nsTArray<nsCString>& aOutSessionIds);
+
     // Sets the capabilities of the CDM. aCaps is the logical OR of the
     // GMP_EME_CAP_* flags from gmp-decryption.h.
     void SetCaps(uint64_t aCaps);
 
     bool CanDecryptAndDecodeAudio();
     bool CanDecryptAndDecodeVideo();
 
     bool CanDecryptAudio();
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -129,17 +129,22 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData> a
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   MOZ_ASSERT(!GetNodeId().IsEmpty());
-  mKeys->OnCDMCreated(aPromiseId, GetNodeId());
+  if (mCDM) {
+    mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
+  } else {
+    // No CDM? Just reject the promise.
+    mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+  }
 }
 
 void
 CDMProxy::CreateSession(uint32_t aCreateSessionToken,
                         dom::SessionType aSessionType,
                         PromiseId aPromiseId,
                         const nsAString& aInitDataType,
                         nsTArray<uint8_t>& aInitData)
@@ -391,17 +396,19 @@ CDMProxy::OnSetSessionId(uint32_t aCreat
                          const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
 
   nsRefPtr<dom::MediaKeySession> session(mKeys->GetPendingSession(aCreateSessionToken));
-  session->SetSessionId(aSessionId);
+  if (session) {
+    session->SetSessionId(aSessionId);
+  }
 }
 
 void
 CDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
@@ -580,16 +587,24 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
       mDecryptionJobs.RemoveElementAt(i);
       return;
     }
   }
   NS_WARNING("GMPDecryptorChild returned incorrect job ID");
 }
 
 void
+CDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
+                                nsTArray<nsCString>& aSessionIds)
+{
+  CDMCaps::AutoLock caps(Capabilites());
+  caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
+}
+
+void
 CDMProxy::Terminated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -161,16 +161,19 @@ public:
                      GMPErr aResult,
                      const nsTArray<uint8_t>& aDecryptedData);
 
   CDMCaps& Capabilites();
 
   // Main thread only.
   void OnKeyStatusesChange(const nsAString& aSessionId);
 
+  void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
+                             nsTArray<nsCString>& aSessionIds);
+
 #ifdef DEBUG
   bool IsOnGMPThread();
 #endif
 
 private:
 
   struct InitData {
     uint32_t mPromiseId;
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -61,17 +61,17 @@ MediaKeySession::MediaKeySession(JSConte
     return;
   }
   mClosed = mKeys->MakePromise(aRv);
 }
 
 void MediaKeySession::SetSessionId(const nsAString& aSessionId)
 {
   EME_LOG("MediaKeySession[%p,'%s'] session Id set",
-          this, NS_ConvertUTF16toUTF8(mSessionId).get());
+          this, NS_ConvertUTF16toUTF8(aSessionId).get());
 
   if (NS_WARN_IF(!mSessionId.IsEmpty())) {
     return;
   }
   mSessionId = aSessionId;
   mKeys->OnSessionIdReady(this);
 }
 
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -99,16 +99,20 @@ EnsureMinCDMVersion(mozIGeckoMediaPlugin
     return MediaKeySystemStatus::Available;
   }
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(aKeySystem));
   nsAutoCString versionStr;
   if (NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
                                                     &tags,
+                                                    versionStr)) &&
+      // XXX to be removed later in bug 1147692
+      NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                                                    &tags,
                                                     versionStr))) {
     return MediaKeySystemStatus::Error;
   }
 
   nsresult rv;
   int32_t version = versionStr.ToInteger(&rv);
   if (NS_FAILED(rv) || version < 0 || aMinCdmVersion > version) {
     return MediaKeySystemStatus::Cdm_insufficient_version;
@@ -148,17 +152,21 @@ MediaKeySystemAccess::GetKeySystemStatus
     if (!IsVistaOrLater()) {
       return MediaKeySystemStatus::Cdm_not_supported;
     }
     if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
     if (!HaveGMPFor(mps,
                     NS_ConvertUTF16toUTF8(aKeySystem),
-                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
+                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)) &&
+        // XXX to be removed later in bug 1147692
+        !HaveGMPFor(mps,
+                    NS_ConvertUTF16toUTF8(aKeySystem),
+                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT))) {
       return MediaKeySystemStatus::Cdm_not_installed;
     }
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
   }
 #endif
 
   return MediaKeySystemStatus::Cdm_not_supported;
 }
@@ -192,24 +200,36 @@ IsPlayableWithGMP(mozIGeckoMediaPluginSe
   if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
                                       codecs,
                                       hasAAC,
                                       hasH264,
                                       hasMP3) ||
       hasMP3) {
     return false;
   }
-  return (!hasAAC || !HaveGMPFor(aGMPS,
-                                 NS_ConvertUTF16toUTF8(aKeySystem),
-                                 NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                                 NS_LITERAL_CSTRING("aac"))) &&
-         (!hasH264 || !HaveGMPFor(aGMPS,
-                                  NS_ConvertUTF16toUTF8(aKeySystem),
-                                  NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                                  NS_LITERAL_CSTRING("h264")));
+  return (!hasAAC ||
+          !(HaveGMPFor(aGMPS,
+                       NS_ConvertUTF16toUTF8(aKeySystem),
+                       NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                       NS_LITERAL_CSTRING("aac")) ||
+            // XXX remove later in bug 1147692
+            HaveGMPFor(aGMPS,
+                       NS_ConvertUTF16toUTF8(aKeySystem),
+                       NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                       NS_LITERAL_CSTRING("aac")))) &&
+         (!hasH264 ||
+          !(HaveGMPFor(aGMPS,
+                       NS_ConvertUTF16toUTF8(aKeySystem),
+                       NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                       NS_LITERAL_CSTRING("h264")) ||
+            // XXX remove later in bug 1147692
+            HaveGMPFor(aGMPS,
+                       NS_ConvertUTF16toUTF8(aKeySystem),
+                       NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                       NS_LITERAL_CSTRING("h264"))));
 #else
   return false;
 #endif
 }
 
 /* static */
 bool
 MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -1,37 +1,39 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/MediaKeys.h"
+#include "GMPService.h"
+#include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/HTMLMediaElement.h"
-#include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeyError.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/PluginCrashedEvent.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/EMEUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "nsContentTypeParser.h"
 #ifdef MOZ_FMP4
 #include "MP4Decoder.h"
 #endif
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
-#include "mozIGeckoMediaPluginService.h"
 #include "mozilla/dom/MediaKeySystemAccess.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
@@ -175,17 +177,17 @@ MediaKeys::MakePromise(ErrorResult& aRv)
 
 PromiseId
 MediaKeys::StorePromise(Promise* aPromise)
 {
   static uint32_t sEMEPromiseCount = 1;
   MOZ_ASSERT(aPromise);
   uint32_t id = sEMEPromiseCount++;
 
-  EME_LOG("MediaKeys::StorePromise() id=%d", id);
+  EME_LOG("MediaKeys[%p]::StorePromise() id=%d", this, id);
 
   // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
   // promises are rejected in Shutdown().
   AddRef();
 
   mPromises.Put(id, aPromise);
   return id;
 }
@@ -201,17 +203,17 @@ MediaKeys::RetrievePromise(PromiseId aId
   mPromises.Remove(aId, getter_AddRefs(promise));
   Release();
   return promise.forget();
 }
 
 void
 MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
 {
-  EME_LOG("MediaKeys::RejectPromise(%d, 0x%x)", aId, aExceptionCode);
+  EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%x)", this, aId, aExceptionCode);
 
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // This promise could be a createSession or loadSession promise,
     // so we might have a pending session waiting to be resolved into
@@ -249,17 +251,17 @@ MediaKeys::OnSessionIdReady(MediaKeySess
     return;
   }
   mKeySessions.Put(aSession->GetSessionId(), aSession);
 }
 
 void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
-  EME_LOG("MediaKeys::ResolvePromise(%d)", aId);
+  EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
 
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // We should only resolve LoadSession calls via this path,
     // not CreateSession() promises.
@@ -336,17 +338,18 @@ MediaKeys::Init(ErrorResult& aRv)
 
   if (!window) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
   nsIDocument* doc = window->GetExtantDoc();
   const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
 
-  EME_LOG("MediaKeys::Create() (%s, %s), %s",
+  EME_LOG("MediaKeys[%p]::Create() (%s, %s), %s",
+          this,
           NS_ConvertUTF16toUTF8(origin).get(),
           NS_ConvertUTF16toUTF8(topLevelOrigin).get(),
           (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
 
   // The CDMProxy's initialization is asynchronous. The MediaKeys is
   // refcounted, and its instance is returned to JS by promise once
   // it's been initialized. No external refs exist to the MediaKeys while
   // we're waiting for the promise to be resolved, so we must hold a
@@ -360,34 +363,128 @@ MediaKeys::Init(ErrorResult& aRv)
   mProxy->Init(mCreatePromiseId,
                origin,
                topLevelOrigin,
                inPrivateBrowsing);
 
   return promise.forget();
 }
 
+class CrashHandler : public gmp::GeckoMediaPluginService::PluginCrashCallback
+{
+public:
+  CrashHandler(const nsACString& aPluginId,
+               nsPIDOMWindow* aParentWindow,
+               nsIDocument* aDocument)
+    : gmp::GeckoMediaPluginService::PluginCrashCallback(aPluginId)
+    , mParentWindowWeakPtr(do_GetWeakReference(aParentWindow))
+    , mDocumentWeakPtr(do_GetWeakReference(aDocument))
+  {
+  }
+
+  virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) override
+  {
+    PluginCrashedEventInit init;
+    init.mBubbles = true;
+    init.mCancelable = true;
+    init.mGmpPlugin = true;
+    init.mPluginDumpID = aPluginDumpId;
+    CopyUTF8toUTF16(aPluginName, init.mPluginName);
+    init.mSubmittedCrashReport = false;
+
+    // The following PluginCrashedEvent fields stay empty:
+    // init.mBrowserDumpID
+    // init.mPluginFilename
+    // TODO: Can/should we fill them?
+
+    nsCOMPtr<nsPIDOMWindow> parentWindow;
+    nsCOMPtr<nsIDocument> document;
+    if (!GetParentWindowAndDocumentIfValid(parentWindow, document)) {
+      return;
+    }
+
+    nsRefPtr<PluginCrashedEvent> event =
+      PluginCrashedEvent::Constructor(document, NS_LITERAL_STRING("PluginCrashed"), init);
+    event->SetTrusted(true);
+    event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+
+    EventDispatcher::DispatchDOMEvent(parentWindow, nullptr, event, nullptr, nullptr);
+  }
+
+  virtual bool IsStillValid()
+  {
+    nsCOMPtr<nsPIDOMWindow> parentWindow;
+    nsCOMPtr<nsIDocument> document;
+    return GetParentWindowAndDocumentIfValid(parentWindow, document);
+  }
+
+private:
+  virtual ~CrashHandler()
+  { }
+
+  bool
+  GetParentWindowAndDocumentIfValid(nsCOMPtr<nsPIDOMWindow>& parentWindow,
+                                    nsCOMPtr<nsIDocument>& document)
+  {
+    parentWindow = do_QueryReferent(mParentWindowWeakPtr);
+    if (!parentWindow) {
+      return false;
+    }
+    document = do_QueryReferent(mDocumentWeakPtr);
+    if (!document) {
+      return false;
+    }
+    nsCOMPtr<nsIDocument> parentWindowDocument = parentWindow->GetExtantDoc();
+    if (!parentWindowDocument || document.get() != parentWindowDocument.get()) {
+      return false;
+    }
+    return true;
+  }
+
+  nsWeakPtr mParentWindowWeakPtr;
+  nsWeakPtr mDocumentWeakPtr;
+};
+
 void
-MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId)
+MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const nsACString& aPluginId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   mNodeId = aNodeId;
   nsRefPtr<MediaKeys> keys(this);
-  EME_LOG("MediaKeys::OnCDMCreated() resolve promise id=%d", aId);
+  EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
   promise->MaybeResolve(keys);
   if (mCreatePromiseId == aId) {
     Release();
   }
 
   MediaKeySystemAccess::NotifyObservers(mParent,
                                         mKeySystem,
                                         MediaKeySystemStatus::Cdm_created);
+
+  if (!aPluginId.IsEmpty()) {
+    // Prepare plugin crash reporter.
+    nsRefPtr<gmp::GeckoMediaPluginService> service =
+      gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+    if (NS_WARN_IF(!service)) {
+      return;
+    }
+    if (NS_WARN_IF(!mParent)) {
+      return;
+    }
+    nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
+    if (NS_WARN_IF(!doc)) {
+      return;
+    }
+    service->AddPluginCrashCallback(new CrashHandler(aPluginId, mParent, doc));
+    EME_LOG("MediaKeys[%p]::OnCDMCreated() registered crash handler for pluginId '%s'",
+            this, aPluginId.Data());
+  }
 }
 
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          SessionType aSessionType,
                          ErrorResult& aRv)
 {
   EME_LOG("MediaKeys[%p] Creating session", this);
@@ -411,17 +508,17 @@ MediaKeys::CreateSession(JSContext* aCx,
 
 void
 MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
-  EME_LOG("MediaKeys::OnSessionLoaded() resolve promise id=%d", aId);
+  EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
 
   promise->MaybeResolve(aSuccess);
 }
 
 void
 MediaKeys::OnSessionClosed(MediaKeySession* aSession)
 {
   nsAutoString id;
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -38,17 +38,17 @@ typedef uint32_t PromiseId;
 // (ArrayBuffer or ArrayBufferView) IDL typed function argument.
 bool
 CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
                                      nsTArray<uint8_t>& aOutData);
 
 // This class is used on the main thread only.
 // Note: it's addref/release is not (and can't be) thread safe!
 class MediaKeys final : public nsISupports,
-                            public nsWrapperCache
+                        public nsWrapperCache
 {
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
 
   MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
@@ -75,17 +75,18 @@ public:
 
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Removes and returns MediaKeySession from the set of sessions awaiting
   // their sessionId to be assigned.
   already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
 
   // Called once a Init() operation succeeds.
-  void OnCDMCreated(PromiseId aId, const nsACString& aNodeId);
+  void OnCDMCreated(PromiseId aId,
+                    const nsACString& aNodeId, const nsACString& aPluginId);
 
   // Called once the CDM generates a sessionId while servicing a
   // MediaKeySession.generateRequest() or MediaKeySession.load() call,
   // once the sessionId of a MediaKeySession is known.
   void OnSessionIdReady(MediaKeySession* aSession);
 
   // Called once a LoadSession succeeds.
   void OnSessionLoaded(PromiseId aId, bool aSuccess);
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -243,20 +243,20 @@ nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
   mStream = new MP4Stream(mDecoder->GetResource());
 
   InitLayersBackendType();
 
-  mAudio.mTaskQueue = new FlushableMediaTaskQueue(GetMediaDecodeThreadPool());
+  mAudio.mTaskQueue = new FlushableMediaTaskQueue(GetMediaThreadPool());
   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
 
-  mVideo.mTaskQueue = new FlushableMediaTaskQueue(GetMediaDecodeThreadPool());
+  mVideo.mTaskQueue = new FlushableMediaTaskQueue(GetMediaThreadPool());
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
 
   static bool sSetupPrefCache = false;
   if (!sSetupPrefCache) {
     sSetupPrefCache = true;
     Preferences::AddBoolVarCache(&sIsEMEEnabled, "media.eme.enabled", false);
     Preferences::AddBoolVarCache(&sDemuxSkipToNextKeyframe, "media.fmp4.demux-skip", true);
   }
@@ -744,16 +744,28 @@ MP4Reader::Update(TrackType aTrack)
     nsAutoPtr<MediaSample> sample(PopSample(aTrack));
 
     // Collect telemetry from h264 Annex B SPS.
     if (!mFoundSPSForTelemetry && sample && AnnexB::HasSPS(sample->mMp4Sample)) {
       nsRefPtr<ByteBuffer> extradata = AnnexB::ExtractExtraData(sample->mMp4Sample);
       mFoundSPSForTelemetry = AccumulateSPSTelemetry(extradata);
     }
 
+    if (sample && sample->mMp4Sample && sample->mMp4Sample->crypto.valid) {
+      CryptoSample& crypto = sample->mMp4Sample->crypto;
+      MOZ_ASSERT(crypto.session_ids.IsEmpty());
+
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+      nsRefPtr<CDMProxy> proxy = mDecoder->GetCDMProxy();
+      MOZ_ASSERT(proxy);
+
+      proxy->GetSessionIdsForKeyId(crypto.key, crypto.session_ids);
+    }
+
     if (sample) {
       decoder.mDecoder->Input(sample->mMp4Sample.forget());
       if (aTrack == kVideo) {
         a.mParsed++;
       }
     } else {
       {
         MonitorAutoLock lock(decoder.mMonitor);
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -53,17 +53,17 @@ public:
       mManager->mActiveCallback->ReleaseMediaResources();
     }
   }
 
   SharedDecoderManager* mManager;
 };
 
 SharedDecoderManager::SharedDecoderManager()
-  : mTaskQueue(new FlushableMediaTaskQueue(GetMediaDecodeThreadPool()))
+  : mTaskQueue(new FlushableMediaTaskQueue(GetMediaThreadPool()))
   , mActiveProxy(nullptr)
   , mActiveCallback(nullptr)
   , mWaitForInternalDrain(false)
   , mMonitor("SharedDecoderProxy")
   , mDecoderReleasedResources(false)
 {
   MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread.
   mCallback = new SharedDecoderCallback(this);
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v6[fake]
+APIs: encode-video[h264], decode-video[h264], eme-decrypt-v7[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -624,16 +624,22 @@ GMPChild::RecvPGMPVideoEncoderConstructo
 bool
 GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
 {
   GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
   GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
 
   void* session = nullptr;
   GMPErr err = GetAPI(GMP_API_DECRYPTOR, host, &session);
+
+  if (err != GMPNoErr && !session) {
+    // XXX to remove in bug 1147692
+    err = GetAPI(GMP_API_DECRYPTOR_COMPAT, host, &session);
+  }
+
   if (err != GMPNoErr || !session) {
     return false;
   }
 
   child->Init(static_cast<GMPDecryptor*>(session));
 
   return true;
 }
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -16,22 +16,29 @@ GMPDecryptorParent::GMPDecryptorParent(G
   , mShuttingDown(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
 #ifdef DEBUG
   , mGMPThread(aPlugin->GMPThread())
 #endif
 {
   MOZ_ASSERT(mPlugin && mGMPThread);
+  mPluginId = aPlugin->GetPluginId();
 }
 
 GMPDecryptorParent::~GMPDecryptorParent()
 {
 }
 
+const nsACString&
+GMPDecryptorParent::GetPluginId() const
+{
+  return mPluginId;
+}
+
 nsresult
 GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback)
 {
   if (mIsOpen) {
     NS_WARNING("Trying to re-use an in-use GMP decrypter!");
     return NS_ERROR_FAILURE;
   }
   mCallback = aCallback;
@@ -135,17 +142,18 @@ GMPDecryptorParent::Decrypt(uint32_t aId
   }
 
   // Caller should ensure parameters passed in are valid.
   MOZ_ASSERT(!aBuffer.IsEmpty() && aCrypto.valid);
 
   GMPDecryptionData data(aCrypto.key,
                          aCrypto.iv,
                          aCrypto.plain_sizes,
-                         aCrypto.encrypted_sizes);
+                         aCrypto.encrypted_sizes,
+                         aCrypto.session_ids);
 
   unused << SendDecrypt(aId, aBuffer, data);
 }
 
 bool
 GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
                                      const nsCString& aSessionId)
 {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -15,24 +15,27 @@ class CryptoSample;
 }
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
 class GMPDecryptorParent final : public GMPDecryptorProxy
-                                   , public PGMPDecryptorParent
+                               , public PGMPDecryptorParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
   explicit GMPDecryptorParent(GMPParent *aPlugin);
 
   // GMPDecryptorProxy
+
+  virtual const nsACString& GetPluginId() const override;
+
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) override;
 
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) override;
 
@@ -102,16 +105,17 @@ private:
   virtual bool RecvSetCaps(const uint64_t& aCaps) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
   nsRefPtr<GMPParent> mPlugin;
+  nsCString mPluginId;
   GMPDecryptorProxyCallback* mCallback;
 #ifdef DEBUG
   nsIThread* const mGMPThread;
 #endif
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPDecryptorProxy.h
+++ b/dom/media/gmp/GMPDecryptorProxy.h
@@ -54,16 +54,18 @@ public:
                          GMPErr aResult,
                          const nsTArray<uint8_t>& aDecryptedData) = 0;
 };
 
 class GMPDecryptorProxy {
 public:
   ~GMPDecryptorProxy() {}
 
+  virtual const nsACString& GetPluginId() const = 0;
+
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) = 0;
 
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) = 0;
 
--- a/dom/media/gmp/GMPEncryptedBufferDataImpl.cpp
+++ b/dom/media/gmp/GMPEncryptedBufferDataImpl.cpp
@@ -9,39 +9,42 @@
 namespace mozilla {
 namespace gmp {
 
 GMPEncryptedBufferDataImpl::GMPEncryptedBufferDataImpl(const CryptoSample& aCrypto)
   : mKeyId(aCrypto.key)
   , mIV(aCrypto.iv)
   , mClearBytes(aCrypto.plain_sizes)
   , mCipherBytes(aCrypto.encrypted_sizes)
+  , mSessionIdList(aCrypto.session_ids)
 {
 }
 
 GMPEncryptedBufferDataImpl::GMPEncryptedBufferDataImpl(const GMPDecryptionData& aData)
+  : mKeyId(aData.mKeyId())
+  , mIV(aData.mIV())
+  , mClearBytes(aData.mClearBytes())
+  , mCipherBytes(aData.mCipherBytes())
+  , mSessionIdList(aData.mSessionIds())
 {
-  mKeyId = aData.mKeyId();
-  mIV = aData.mIV();
-  mClearBytes = aData.mClearBytes();
-  mCipherBytes = aData.mCipherBytes();
   MOZ_ASSERT(mClearBytes.Length() == mCipherBytes.Length());
 }
 
 GMPEncryptedBufferDataImpl::~GMPEncryptedBufferDataImpl()
 {
 }
 
 void
 GMPEncryptedBufferDataImpl::RelinquishData(GMPDecryptionData& aData)
 {
   aData.mKeyId() = Move(mKeyId);
   aData.mIV() = Move(mIV);
   aData.mClearBytes() = Move(mClearBytes);
   aData.mCipherBytes() = Move(mCipherBytes);
+  mSessionIdList.RelinquishData(aData.mSessionIds());
 }
 
 const uint8_t*
 GMPEncryptedBufferDataImpl::KeyId() const
 {
   return mKeyId.Elements();
 }
 
@@ -70,19 +73,59 @@ GMPEncryptedBufferDataImpl::ClearBytes()
 }
 
 const uint32_t*
 GMPEncryptedBufferDataImpl::CipherBytes() const
 {
   return mCipherBytes.Elements();
 }
 
+const GMPStringList*
+GMPEncryptedBufferDataImpl::SessionIds() const
+{
+  return &mSessionIdList;
+}
+
 uint32_t
 GMPEncryptedBufferDataImpl::NumSubsamples() const
 {
   MOZ_ASSERT(mClearBytes.Length() == mCipherBytes.Length());
   // Return the min of the two, to ensure there's not chance of array index
   // out-of-bounds shenanigans.
   return std::min<uint32_t>(mClearBytes.Length(), mCipherBytes.Length());
 }
 
+GMPStringListImpl::GMPStringListImpl(const nsTArray<nsCString>& aStrings)
+  : mStrings(aStrings)
+{
+}
+
+const uint32_t
+GMPStringListImpl::Size() const
+{
+  return mStrings.Length();
+}
+
+void
+GMPStringListImpl::StringAt(uint32_t aIndex,
+                            const char** aOutString,
+                            uint32_t *aOutLength) const
+{
+  if (NS_WARN_IF(aIndex >= Size())) {
+    return;
+  }
+
+  *aOutString = mStrings[aIndex].BeginReading();
+  *aOutLength = mStrings[aIndex].Length();
+}
+
+void
+GMPStringListImpl::RelinquishData(nsTArray<nsCString>& aStrings)
+{
+  aStrings = Move(mStrings);
+}
+
+GMPStringListImpl::~GMPStringListImpl()
+{
+}
+
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPEncryptedBufferDataImpl.h
+++ b/dom/media/gmp/GMPEncryptedBufferDataImpl.h
@@ -9,16 +9,30 @@
 #include "gmp-decryption.h"
 #include "mp4_demuxer/DecoderData.h"
 #include "nsTArray.h"
 #include "mozilla/gmp/GMPTypes.h"
 
 namespace mozilla {
 namespace gmp {
 
+class GMPStringListImpl : public GMPStringList
+{
+public:
+  explicit GMPStringListImpl(const nsTArray<nsCString>& aStrings);
+  virtual const uint32_t Size() const override;
+  virtual void StringAt(uint32_t aIndex,
+                        const char** aOutString, uint32_t *aOutLength) const override;
+  virtual ~GMPStringListImpl() override;
+  void RelinquishData(nsTArray<nsCString>& aStrings);
+
+private:
+  nsTArray<nsCString> mStrings;
+};
+
 class GMPEncryptedBufferDataImpl : public GMPEncryptedBufferMetadata {
 private:
   typedef mp4_demuxer::CryptoSample CryptoSample;
 public:
   explicit GMPEncryptedBufferDataImpl(const CryptoSample& aCrypto);
   explicit GMPEncryptedBufferDataImpl(const GMPDecryptionData& aData);
   virtual ~GMPEncryptedBufferDataImpl();
 
@@ -26,22 +40,25 @@ public:
 
   virtual const uint8_t* KeyId() const override;
   virtual uint32_t KeyIdSize() const override;
   virtual const uint8_t* IV() const override;
   virtual uint32_t IVSize() const override;
   virtual uint32_t NumSubsamples() const override;
   virtual const uint16_t* ClearBytes() const override;
   virtual const uint32_t* CipherBytes() const override;
+  virtual const GMPStringList* SessionIds() const override;
 
 private:
   nsTArray<uint8_t> mKeyId;
   nsTArray<uint8_t> mIV;
   nsTArray<uint16_t> mClearBytes;
   nsTArray<uint32_t> mCipherBytes;
+
+  GMPStringListImpl mSessionIdList;
 };
 
 class GMPBufferImpl : public GMPBuffer {
 public:
   GMPBufferImpl(uint32_t aId, const nsTArray<uint8_t>& aData)
     : mId(aId)
     , mData(aData)
   {
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -55,16 +55,19 @@ GMPParent::GMPParent()
   , mAbnormalShutdownInProgress(false)
   , mAsyncShutdownRequired(false)
   , mAsyncShutdownInProgress(false)
 #ifdef PR_LOGGING
   , mChildPid(0)
 #endif
 {
   LOGD("GMPParent ctor");
+  // Use the parent address to identify it.
+  // We could use any unique-to-the-parent value.
+  mPluginId.AppendInt(reinterpret_cast<uint64_t>(this));
 }
 
 GMPParent::~GMPParent()
 {
   // Can't Close or Destroy the process here, since destruction is MainThread only
   MOZ_ASSERT(NS_IsMainThread());
   LOGD("GMPParent dtor");
 }
@@ -629,53 +632,60 @@ GMPParent::GetCrashID(nsString& aResult)
   }
 
   AnnotationTable notes(4);
   WriteExtraDataForMinidump(notes);
   nsCOMPtr<nsIFile> dumpFile;
   TakeMinidump(getter_AddRefs(dumpFile), nullptr);
   if (!dumpFile) {
     NS_WARNING("GMP crash without crash report");
+    aResult = mName;
+    aResult += '-';
+    AppendUTF8toUTF16(mVersion, aResult);
     return;
   }
   GetIDFromMinidump(dumpFile, aResult);
   cr->GenerateCrashReportForMinidump(dumpFile, &notes);
 }
 
 static void
-GMPNotifyObservers(nsAString& aData)
+GMPNotifyObservers(const nsACString& aPluginId, const nsACString& aPluginName, const nsAString& aPluginDumpId)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
-    nsString temp(aData);
-    obs->NotifyObservers(nullptr, "gmp-plugin-crash", temp.get());
+    nsString id;
+    AppendUTF8toUTF16(aPluginId, id);
+    id.Append(NS_LITERAL_STRING(" "));
+    AppendUTF8toUTF16(aPluginName, id);
+    id.Append(NS_LITERAL_STRING(" "));
+    id.Append(aPluginDumpId);
+    obs->NotifyObservers(nullptr, "gmp-plugin-crash", id.Data());
+  }
+
+  nsRefPtr<gmp::GeckoMediaPluginService> service =
+    gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+  if (service) {
+    service->RunPluginCrashCallbacks(aPluginId, aPluginName, aPluginDumpId);
   }
 }
 #endif
 void
 GMPParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOGD("%s: (%d)", __FUNCTION__, (int)aWhy);
 #ifdef MOZ_CRASHREPORTER
   if (AbnormalShutdown == aWhy) {
     Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                           NS_LITERAL_CSTRING("gmplugin"), 1);
     nsString dumpID;
     GetCrashID(dumpID);
-    nsString id;
-    // use the parent address to identify it
-    // We could use any unique-to-the-parent value
-    id.AppendInt(reinterpret_cast<uint64_t>(this));
-    id.Append(NS_LITERAL_STRING(" "));
-    AppendUTF8toUTF16(mDisplayName, id);
-    id.Append(NS_LITERAL_STRING(" "));
-    id.Append(dumpID);
 
     // NotifyObservers is mainthread-only
-    NS_DispatchToMainThread(WrapRunnableNM(&GMPNotifyObservers, id),
+    NS_DispatchToMainThread(WrapRunnableNM(&GMPNotifyObservers,
+                                           mPluginId, mDisplayName, dumpID),
                             NS_DISPATCH_NORMAL);
   }
 #endif
   // warn us off trying to close again
   mState = GMPStateClosing;
   mAbnormalShutdownInProgress = true;
   CloseActive(false);
 
@@ -1008,16 +1018,22 @@ GMPParent::GetDisplayName() const
 }
 
 const nsCString&
 GMPParent::GetVersion() const
 {
   return mVersion;
 }
 
+const nsACString&
+GMPParent::GetPluginId() const
+{
+  return mPluginId;
+}
+
 bool
 GMPParent::RecvAsyncShutdownRequired()
 {
   LOGD("%s", __FUNCTION__);
   if (mAsyncShutdownRequired) {
     NS_WARNING("Received AsyncShutdownRequired message more than once!");
     return true;
   }
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -50,17 +50,17 @@ public:
 enum GMPState {
   GMPStateNotLoaded,
   GMPStateLoaded,
   GMPStateUnloading,
   GMPStateClosing
 };
 
 class GMPParent final : public PGMPParent,
-                            public GMPSharedMem
+                        public GMPSharedMem
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
 
   GMPParent();
 
   nsresult Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir);
   nsresult CloneFrom(const GMPParent* aOther);
@@ -112,16 +112,17 @@ public:
   // be shared across NodeIds.
 
   // Specifies that a GMP can only work with the specified NodeIds.
   void SetNodeId(const nsACString& aNodeId);
   const nsACString& GetNodeId() const { return mNodeId; }
 
   const nsCString& GetDisplayName() const;
   const nsCString& GetVersion() const;
+  const nsACString& GetPluginId() const;
 
   // Returns true if a plugin can be or is being used across multiple NodeIds.
   bool CanBeSharedCrossNodeIds() const;
 
   // A GMP can be used from a NodeId if it's already been set to work with
   // that NodeId, or if it's not been set to work with any NodeId and has
   // not yet been loaded (i.e. it's not shared across NodeIds).
   bool CanBeUsedFrom(const nsACString& aNodeId) const;
@@ -175,16 +176,17 @@ private:
   nsresult EnsureAsyncShutdownTimeoutSet();
 
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
   nsCString mDescription; // description of plugin for display to users
   nsCString mVersion;
+  nsCString mPluginId;
   nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
   GMPProcessParent* mProcess;
   bool mDeleteProcessOnlyOnUnload;
   bool mAbnormalShutdownInProgress;
 
   nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
   nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
   nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -165,16 +165,75 @@ GeckoMediaPluginService::~GeckoMediaPlug
 
 int32_t
 GeckoMediaPluginService::AsyncShutdownTimeoutMs()
 {
   MOZ_ASSERT(sHaveSetTimeoutPrefCache);
   return sMaxAsyncShutdownWaitMs;
 }
 
+void
+GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
+    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
+    if (!callback->IsStillValid()) {
+      LOGD(("%s::%s - Removing obsolete callback for pluginId %s",
+            __CLASS__, __FUNCTION__,
+            PromiseFlatCString(callback->PluginId()).get()));
+      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+    }
+  }
+}
+
+void
+GeckoMediaPluginService::AddPluginCrashCallback(
+  nsRefPtr<PluginCrashCallback> aPluginCrashCallback)
+{
+  RemoveObsoletePluginCrashCallbacks();
+  mPluginCrashCallbacks.AppendElement(aPluginCrashCallback);
+}
+
+void
+GeckoMediaPluginService::RemovePluginCrashCallbacks(const nsACString& aPluginId)
+{
+  RemoveObsoletePluginCrashCallbacks();
+  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
+    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
+    if (callback->PluginId() == aPluginId) {
+      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+    }
+  }
+}
+
+void
+GeckoMediaPluginService::RunPluginCrashCallbacks(const nsACString& aPluginId,
+                                                 const nsACString& aPluginName,
+                                                 const nsAString& aPluginDumpId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  LOGD(("%s::%s(%s)", __CLASS__, __FUNCTION__, aPluginId.Data()));
+  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
+    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
+    const nsACString& callbackPluginId = callback->PluginId();
+    if (!callback->IsStillValid()) {
+      LOGD(("%s::%s(%s) - Removing obsolete callback for pluginId %s",
+            __CLASS__, __FUNCTION__, aPluginId.Data(),
+            PromiseFlatCString(callback->PluginId()).get()));
+      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+    } else if (callbackPluginId == aPluginId) {
+      LOGD(("%s::%s(%s) - Running #%u",
+          __CLASS__, __FUNCTION__, aPluginId.Data(), i - 1));
+      callback->Run(aPluginName, aPluginDumpId);
+      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+    }
+  }
+}
+
 nsresult
 GeckoMediaPluginService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
@@ -538,16 +597,24 @@ GeckoMediaPluginService::GetGMPDecryptor
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
                                                NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
                                                *aTags);
+
+  if (!gmp) {
+    // XXX to remove in bug 1147692
+    gmp = SelectPluginForAPI(aNodeId,
+                             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                             *aTags);
+  }
+
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
   GMPDecryptorParent* ksp;
   nsresult rv = gmp->GetGMPDecryptor(&ksp);
   if (NS_FAILED(rv)) {
     return rv;
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -26,17 +26,17 @@ template <class> struct already_AddRefed
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
 #define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
 
 class GeckoMediaPluginService final : public mozIGeckoMediaPluginService
-                                        , public nsIObserver
+                                    , public nsIObserver
 {
 public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
   GeckoMediaPluginService();
   nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
@@ -44,16 +44,44 @@ public:
   NS_DECL_NSIOBSERVER
 
   void AsyncShutdownNeeded(GMPParent* aParent);
   void AsyncShutdownComplete(GMPParent* aParent);
   void AbortAsyncShutdown();
 
   int32_t AsyncShutdownTimeoutMs();
 
+  class PluginCrashCallback
+  {
+  public:
+    NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
+
+    PluginCrashCallback(const nsACString& aPluginId)
+      : mPluginId(aPluginId)
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+    }
+    const nsACString& PluginId() const { return mPluginId; }
+    virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) = 0;
+    virtual bool IsStillValid() = 0; // False if callback has become useless.
+  protected:
+    virtual ~PluginCrashCallback()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+    }
+  private:
+    const nsCString mPluginId;
+  };
+  void RemoveObsoletePluginCrashCallbacks(); // Called from add/remove/run.
+  void AddPluginCrashCallback(nsRefPtr<PluginCrashCallback> aPluginCrashCallback);
+  void RemovePluginCrashCallbacks(const nsACString& aPluginId);
+  void RunPluginCrashCallbacks(const nsACString& aPluginId,
+                               const nsACString& aPluginName,
+                               const nsAString& aPluginDumpId);
+
 private:
   ~GeckoMediaPluginService();
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
 
   void ClearStorage();
 
   GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
@@ -119,16 +147,18 @@ private:
   };
 
   Mutex mMutex; // Protects mGMPThread and mShuttingDown and mPlugins
   nsTArray<nsRefPtr<GMPParent>> mPlugins;
   nsCOMPtr<nsIThread> mGMPThread;
   bool mShuttingDown;
   bool mShuttingDownOnGMPThread;
 
+  nsTArray<nsRefPtr<PluginCrashCallback>> mPluginCrashCallbacks;
+
   // True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
   // plugins found there into mPlugins.
   Atomic<bool> mScannedPluginOnDisk;
 
   template<typename T>
   class MainThreadOnly {
   public:
     MOZ_IMPLICIT MainThreadOnly(T aValue)
--- a/dom/media/gmp/GMPTypes.ipdlh
+++ b/dom/media/gmp/GMPTypes.ipdlh
@@ -9,16 +9,17 @@ using GMPAudioCodecType from "gmp-audio-
 namespace mozilla {
 namespace gmp {
 
 struct GMPDecryptionData {
   uint8_t[] mKeyId;
   uint8_t[] mIV;
   uint16_t[] mClearBytes;
   uint32_t[] mCipherBytes;
+  nsCString[] mSessionIds;
 };
 
 struct GMPVideoEncodedFrameData
 {
   uint32_t mEncodedWidth;
   uint32_t mEncodedHeight;
   uint64_t mTimestamp; // microseconds
   uint64_t mDuration; // microseconds
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -14,16 +14,26 @@
 * limitations under the License.
 */
 
 #ifndef GMP_DECRYPTION_h_
 #define GMP_DECRYPTION_h_
 
 #include "gmp-platform.h"
 
+class GMPStringList {
+public:
+  virtual const uint32_t Size() const = 0;
+
+  virtual void StringAt(uint32_t aIndex,
+                        const char** aOutString, uint32_t* aOutLength) const = 0;
+
+  virtual ~GMPStringList() { }
+};
+
 class GMPEncryptedBufferMetadata {
 public:
   // Key ID to identify the decryption key.
   virtual const uint8_t* KeyId() const = 0;
 
   // Size (in bytes) of |KeyId()|.
   virtual uint32_t KeyIdSize() const = 0;
 
@@ -36,16 +46,20 @@ public:
   // Number of entries returned by ClearBytes() and CipherBytes().
   virtual uint32_t NumSubsamples() const = 0;
 
   virtual const uint16_t* ClearBytes() const = 0;
 
   virtual const uint32_t* CipherBytes() const = 0;
 
   virtual ~GMPEncryptedBufferMetadata() {}
+
+  // The set of MediaKeySession IDs associated with this decryption key in
+  // the current stream.
+  virtual const GMPStringList* SessionIds() const = 0;
 };
 
 class GMPBuffer {
 public:
   virtual uint32_t Id() const = 0;
   virtual uint8_t* Data() = 0;
   virtual uint32_t Size() const = 0;
   virtual void Resize(uint32_t aSize) = 0;
@@ -219,17 +233,20 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
-#define GMP_API_DECRYPTOR "eme-decrypt-v6"
+#define GMP_API_DECRYPTOR "eme-decrypt-v7"
+
+// XXX remove in bug 1147692
+#define GMP_API_DECRYPTOR_COMPAT "eme-decrypt-v6"
 
 // API exposed by plugin library to manage decryption sessions.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
 public:
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -333,21 +333,21 @@ public:
     }
     aStart = compositionRange.start;
     aEnd = compositionRange.end;
     MSE_DEBUG(MP4ContainerParser, "[%lld, %lld]",
               aStart, aEnd);
     return true;
   }
 
-  // Gaps of up to 20ms (marginally longer than a single frame at 60fps) are considered
+  // Gaps of up to 35ms (marginally longer than a single frame at 30fps) are considered
   // to be sequential frames.
   int64_t GetRoundingError()
   {
-    return 20000;
+    return 35000;
   }
 
 private:
   nsRefPtr<MP4Stream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
   Monitor mMonitor;
 };
 #endif
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -300,16 +300,28 @@ MediaSourceDecoder::GetMozDebugReaderDat
 nsresult
 MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
 {
   nsresult rv = MediaDecoder::SetCDMProxy(aProxy);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mReader->SetCDMProxy(aProxy);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  {
+    // The sub readers can't decrypt EME content until they have a CDMProxy,
+    // and the CDMProxy knows the capabilities of the CDM. The MediaSourceReader
+    // remains in "waiting for resources" state until then. We need to kick the
+    // reader out of waiting if the CDM gets added with known capabilities.
+    CDMCaps::AutoLock caps(aProxy->Capabilites());
+    if (!caps.AreCapsKnown()) {
+      nsCOMPtr<nsIRunnable> task(
+        NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
+      caps.CallOnMainThreadWhenCapsAvailable(task);
+    }
+  }
   return NS_OK;
 }
 #endif
 
 bool
 MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
 {
   return mReader->IsActiveReader(aReader);
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -92,23 +92,32 @@ MediaSourceReader::IsWaitingMediaResourc
 
 bool
 MediaSourceReader::IsWaitingOnCDMResource()
 {
 #ifdef MOZ_EME
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(!IsWaitingMediaResources());
 
-  for (auto& trackBuffer : mTrackBuffers) {
-    if (trackBuffer->IsWaitingOnCDMResource()) {
-      return true;
-    }
+  if (!mInfo.IsEncrypted()) {
+    return false;
   }
 
-  return mInfo.IsEncrypted() && !mCDMProxy;
+  // We'll need to wait on the CDMProxy being added, and it having received
+  // notification from the child GMP of its capabilities; whether it can
+  // decode, or whether we need to decode on our side.
+  if (!mCDMProxy) {
+    return true;
+  }
+
+  {
+    CDMCaps::AutoLock caps(mCDMProxy->Capabilites());
+    return !caps.AreCapsKnown();
+  }
+
 #else
   return false;
 #endif
 }
 
 size_t
 MediaSourceReader::SizeOfVideoQueueInFrames()
 {
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -47,17 +47,17 @@ TrackBuffer::TrackBuffer(MediaSourceDeco
   , mLastStartTimestamp(0)
   , mLastTimestampOffset(0)
   , mAdjustedTimestamp(0)
   , mIsWaitingOnCDM(false)
   , mShutdown(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
   mParser = ContainerParser::CreateForMIMEType(aType);
-  mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
+  mTaskQueue = new MediaTaskQueue(GetMediaThreadPool());
   aParentDecoder->AddTrackBuffer(this);
   mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
   MSE_DEBUG("TrackBuffer created for parent decoder %p", aParentDecoder);
 }
 
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -73,9 +73,9 @@ load buffer-source-ended-1.html
 HTTP load media-element-source-seek-1.html
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 load 1080986.html
 include ../../mediasource/test/crashtests/crashtests.list
 
 # This needs to run at the end to avoid leaking busted state into other tests.
-skip-if(winWidget||OSX==1010&&isDebugBuild) load 691096-1.html
+skip-if(B2G||winWidget||OSX==1010&&isDebugBuild) load 691096-1.html
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -2,16 +2,17 @@
 support-files =
   head.js
   hello.ogg
   hello.ogg^headers^
   silence.ogg
   silence.ogg^headers^
 
 [test_abort.html]
+skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287
 [test_audio_capture_error.html]
 [test_call_start_from_end_handler.html]
 [test_nested_eventloop.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog)
 [test_preference_enable.html]
 [test_recognition_service_error.html]
 skip-if = buildapp == 'b2g' # b2g(timed out)
 [test_success_without_recognition_service.html]
--- a/dom/network/tests/test_udpsocket.html
+++ b/dom/network/tests/test_udpsocket.html
@@ -269,16 +269,17 @@ function testInvalidUDPOptions() {
 function testOpenFailed() {
   info('test for falied on open');
 
   //according to RFC5737, address block 192.0.2.0/24 should not be used in both local and public contexts
   let socket = new UDPSocket({localAddress: '192.0.2.0'});
 
   return socket.opened.then(function() {
     ok(false, 'should not resolve openedPromise while fail to bind socket');
+    socket.close();
   }).catch(function(reason) {
     is(reason.name, 'NetworkError', 'expected openedPromise to be rejected while fail to bind socket');
   });
 }
 
 function testSendBeforeOpen() {
   info('test for send before open');
 
@@ -286,17 +287,19 @@ function testSendBeforeOpen() {
 
   try {
     socket.send(HELLO_WORLD, '127.0.0.1', 9);
     ok(false, 'unexpect to send successfully');
   } catch (e) {
     ok(true, 'expected send fail before openedPromise is resolved');
   }
 
-  return socket.opened;
+  return socket.opened.then(function() {
+    socket.close();
+  });
 }
 
 function testCloseBeforeOpened() {
   info('test for close socket before opened');
 
   let socket = new UDPSocket();
   socket.opened.then(function() {
     ok(false, 'should not resolve openedPromise if it has already been closed');
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -92,40 +92,43 @@ public:
        }
        rootDoc->SetHasMixedActiveContentLoaded(true);
 
       // Update the security UI in the tab with the allowed mixed active content
       nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
       if (eventSink) {
         // If mixed display content is loaded, make sure to include that in the state.
         if (rootDoc->GetHasMixedDisplayContentLoaded()) {
-          eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
-          nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
-          nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+          eventSink->OnSecurityChange(mContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
         } else {
-          eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
-          nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+          eventSink->OnSecurityChange(mContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
         }
       }
 
     } else if (mType == eMixedDisplay) {
       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
       if (rootDoc->GetHasMixedDisplayContentLoaded()) {
         return NS_OK;
       }
       rootDoc->SetHasMixedDisplayContentLoaded(true);
 
       // Update the security UI in the tab with the allowed mixed display content.
       nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
       if (eventSink) {
         // If mixed active content is loaded, make sure to include that in the state.
         if (rootDoc->GetHasMixedActiveContentLoaded()) {
-          eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
-          nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
-          nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+          eventSink->OnSecurityChange(mContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
         } else {
           eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
           nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
         }
       }
     }
 
     return NS_OK;
@@ -651,79 +654,102 @@ nsMixedContentBlocker::ShouldLoad(bool a
   }
   nsresult stateRV = securityUI->GetState(&State);
 
   // If the content is display content, and the pref says display content should be blocked, block it.
   if (sBlockMixedDisplay && classification == eMixedDisplay) {
     if (allowMixedContent) {
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
       *aDecision = nsIContentPolicy::ACCEPT;
-      rootDoc->SetHasMixedActiveContentLoaded(true);
-      if (!rootDoc->GetHasMixedDisplayContentLoaded() && NS_SUCCEEDED(stateRV)) {
-        rootDoc->SetHasMixedDisplayContentLoaded(true);
-        eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+      // See if mixed display content has already loaded on the page or if the state needs to be updated here.
+      // If mixed display hasn't loaded previously, then we need to call OnSecurityChange() to update the UI.
+      if (rootDoc->GetHasMixedDisplayContentLoaded()) {
+        return NS_OK;
+      }
+      rootDoc->SetHasMixedDisplayContentLoaded(true);
+
+      if (rootHasSecureConnection) {
+        if (rootDoc->GetHasMixedActiveContentLoaded()) {
+          // If mixed active content is loaded, make sure to include that in the state.
+          eventSink->OnSecurityChange(aRequestingContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+        } else {
+          eventSink->OnSecurityChange(aRequestingContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+        }
+      } else {
+        // User has overriden the pref and the root is not https;
+        // mixed display content was allowed on an https subframe.
+        if (NS_SUCCEEDED(stateRV)) {
+          eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+         }
       }
     } else {
       *aDecision = nsIContentPolicy::REJECT_REQUEST;
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
         rootDoc->SetHasMixedDisplayContentBlocked(true);
         eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
       }
     }
     return NS_OK;
 
   } else if (sBlockMixedScript && classification == eMixedScript) {
     // If the content is active content, and the pref says active content should be blocked, block it
     // unless the user has choosen to override the pref
     if (allowMixedContent) {
-       LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
-       *aDecision = nsIContentPolicy::ACCEPT;
-       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
-       if (rootDoc->GetHasMixedActiveContentLoaded()) {
-         return NS_OK;
-       }
-       rootDoc->SetHasMixedActiveContentLoaded(true);
+      LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
+      *aDecision = nsIContentPolicy::ACCEPT;
+      // See if the state will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
+      if (rootDoc->GetHasMixedActiveContentLoaded()) {
+        return NS_OK;
+      }
+      rootDoc->SetHasMixedActiveContentLoaded(true);
 
-       if (rootHasSecureConnection) {
-         // User has decided to override the pref and the root is https, so change the Security State.
-         if (rootDoc->GetHasMixedDisplayContentLoaded()) {
-           // If mixed display content is loaded, make sure to include that in the state.
-           eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN |
-           nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
-           nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
-         } else {
-           eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN |
-           nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
-         }
-         return NS_OK;
-       } else {
-         // User has already overriden the pref and the root is not https;
-         // mixed content was allowed on an https subframe.
-         if (NS_SUCCEEDED(stateRV)) {
-           eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
-         }
-         return NS_OK;
-       }
+      if (rootHasSecureConnection) {
+        // User has decided to override the pref and the root is https, so change the Security State.
+        if (rootDoc->GetHasMixedDisplayContentLoaded()) {
+          // If mixed display content is loaded, make sure to include that in the state.
+          eventSink->OnSecurityChange(aRequestingContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+        } else {
+          eventSink->OnSecurityChange(aRequestingContext,
+                                      (nsIWebProgressListener::STATE_IS_BROKEN |
+                                       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+        }
+        return NS_OK;
+      } else {
+        // User has already overriden the pref and the root is not https;
+        // mixed active content was allowed on an https subframe.
+        if (NS_SUCCEEDED(stateRV)) {
+          eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+        }
+        return NS_OK;
+      }
     } else {
-       //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
-       *aDecision = nsIContentPolicy::REJECT_REQUEST;
-       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
-       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
-       if (rootDoc->GetHasMixedActiveContentBlocked()) {
-         return NS_OK;
-       }
-       rootDoc->SetHasMixedActiveContentBlocked(true);
+      //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
+      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
+      // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
+      if (rootDoc->GetHasMixedActiveContentBlocked()) {
+        return NS_OK;
+      }
+      rootDoc->SetHasMixedActiveContentBlocked(true);
 
-       // The user has not overriden the pref, so make sure they still have an option by calling eventSink
-       // which will invoke the doorhanger
-       if (NS_SUCCEEDED(stateRV)) {
-          eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
-       }
-       return NS_OK;
+      // The user has not overriden the pref, so make sure they still have an option by calling eventSink
+      // which will invoke the doorhanger
+      if (NS_SUCCEEDED(stateRV)) {
+         eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
+      }
+      return NS_OK;
     }
 
   } else {
     // The content is not blocked by the mixed content prefs.
 
     // Log a message that we are loading mixed content.
     LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
 
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -18,31 +18,36 @@ const NETWORKSERVICE_CID = Components.ID
 const TOPIC_PREF_CHANGED             = "nsPref:changed";
 const TOPIC_XPCOM_SHUTDOWN           = "xpcom-shutdown";
 const PREF_NETWORK_DEBUG_ENABLED     = "network.debugging.enabled";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
                                    "@mozilla.org/network/worker;1",
                                    "nsINetworkWorker");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gPACGenerator",
+                                   "@mozilla.org/pac-generator;1",
+                                   "nsIPACGenerator");
+
 // 1xx - Requested action is proceeding
 const NETD_COMMAND_PROCEEDING   = 100;
 // 2xx - Requested action has been successfully completed
 const NETD_COMMAND_OKAY         = 200;
 // 4xx - The command is accepted but the requested action didn't
 // take place.
 const NETD_COMMAND_FAIL         = 400;
 // 5xx - The command syntax or parameters error
 const NETD_COMMAND_ERROR        = 500;
 // 6xx - Unsolicited broadcasts
 const NETD_COMMAND_UNSOLICITED  = 600;
 
 const WIFI_CTRL_INTERFACE = "wl0.1";
 
-const MANUAL_PROXY_CONFIGURATION = 1;
+const PROXY_TYPE_MANUAL = Ci.nsIProtocolProxyService.PROXYCONFIG_MANUAL;
+const PROXY_TYPE_PAC = Ci.nsIProtocolProxyService.PROXYCONFIG_PAC;
 
 let debug;
 function updateDebug() {
   let debugPref = false; // set default value here.
   try {
     debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
   } catch (e) {}
 
@@ -518,40 +523,64 @@ NetworkService.prototype = {
         // Sets direct connection to internet.
         this.clearNetworkProxy();
 
         debug("No proxy support for " + network.name + " network interface.");
         return;
       }
 
       debug("Going to set proxy settings for " + network.name + " network interface.");
-      // Sets manual proxy configuration.
-      Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION);
+
       // Do not use this proxy server for all protocols.
       Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false);
       Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost);
       Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost);
       let port = network.httpProxyPort === 0 ? 8080 : network.httpProxyPort;
       Services.prefs.setIntPref("network.proxy.http_port", port);
       Services.prefs.setIntPref("network.proxy.ssl_port", port);
+
+      let usePAC;
+      try {
+        usePAC = Services.prefs.getBoolPref("network.proxy.pac_generator");
+      } catch (ex) {}
+
+      if (usePAC) {
+        Services.prefs.setCharPref("network.proxy.autoconfig_url",
+                                   gPACGenerator.generate());
+        Services.prefs.setIntPref("network.proxy.type", PROXY_TYPE_PAC);
+      } else {
+        Services.prefs.setIntPref("network.proxy.type", PROXY_TYPE_MANUAL);
+      }
     } catch(ex) {
         debug("Exception " + ex + ". Unable to set proxy setting for " +
                          network.name + " network interface.");
     }
   },
 
   clearNetworkProxy: function() {
     debug("Going to clear all network proxy.");
 
-    Services.prefs.clearUserPref("network.proxy.type");
     Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
     Services.prefs.clearUserPref("network.proxy.http");
     Services.prefs.clearUserPref("network.proxy.http_port");
     Services.prefs.clearUserPref("network.proxy.ssl");
     Services.prefs.clearUserPref("network.proxy.ssl_port");
+
+    let usePAC;
+    try {
+      usePAC = Services.prefs.getBoolPref("network.proxy.pac_generator");
+    } catch (ex) {}
+
+    if (usePAC) {
+      Services.prefs.setCharPref("network.proxy.autoconfig_url",
+                                 gPACGenerator.generate());
+      Services.prefs.setIntPref("network.proxy.type", PROXY_TYPE_PAC);
+    } else {
+      Services.prefs.clearUserPref("network.proxy.type");
+    }
   },
 
   // Enable/Disable DHCP server.
   setDhcpServer: function(enabled, config, callback) {
     if (null === config) {
       config = {};
     }
 
--- a/dom/system/gonk/tests/marionette/test_data_connection_proxy.js
+++ b/dom/system/gonk/tests/marionette/test_data_connection_proxy.js
@@ -1,17 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = "head.js";
 
 const HTTP_PROXY = "10.0.2.200";
 const HTTP_PROXY_PORT = "8080";
-const MANUAL_PROXY_CONFIGURATION = 1;
+const PROXY_TYPE_MANUAL = Ci.nsIProtocolProxyService.PROXYCONFIG_MANUAL;
+const PROXY_TYPE_PAC = Ci.nsIProtocolProxyService.PROXYCONFIG_PAC;
 
 // Test initial State
 function verifyInitialState() {
   log("= verifyInitialState =");
 
   // Data should be off before starting any test.
   return getSettings(SETTINGS_KEY_DATA_ENABLED)
     .then(value => {
@@ -33,30 +34,34 @@ function setTestApn() {
 }
 
 function waitForHttpProxyVerified(aShouldBeSet) {
   let TIME_OUT_VALUE = 20000;
 
   return new Promise(function(aResolve, aReject) {
     try {
       waitFor(aResolve, () => {
+        let usePAC = SpecialPowers.getBoolPref("network.proxy.pac_generator");
         let proxyType = SpecialPowers.getIntPref("network.proxy.type");
         let httpProxy = SpecialPowers.getCharPref("network.proxy.http");
         let sslProxy = SpecialPowers.getCharPref("network.proxy.ssl");
         let httpProxyPort = SpecialPowers.getIntPref("network.proxy.http_port");
         let sslProxyPort = SpecialPowers.getIntPref("network.proxy.ssl_port");
 
         if ((aShouldBeSet &&
-             proxyType == MANUAL_PROXY_CONFIGURATION &&
+             (usePAC ? proxyType == PROXY_TYPE_PAC :
+                       proxyType == PROXY_TYPE_MANUAL) &&
              httpProxy == HTTP_PROXY &&
              sslProxy == HTTP_PROXY &&
              httpProxyPort == HTTP_PROXY_PORT &&
              sslProxyPort == HTTP_PROXY_PORT) ||
-            (!aShouldBeSet && proxyType != MANUAL_PROXY_CONFIGURATION &&
-             !httpProxy && !sslProxy && !httpProxyPort && !sslProxyPort)) {
+             (!aShouldBeSet &&
+              (usePAC ? proxyType == PROXY_TYPE_PAC :
+                        proxyType != PROXY_TYPE_MANUAL) &&
+              !httpProxy && !sslProxy && !httpProxyPort && !sslProxyPort)) {
           return true;
         }
 
         return false;
       }, TIME_OUT_VALUE);
     } catch(aError) {
       // Timed out.
       aReject(aError);
--- a/dom/webidl/ScrollViewChangeEvent.webidl
+++ b/dom/webidl/ScrollViewChangeEvent.webidl
@@ -3,19 +3,15 @@
  * 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/.
  */
 
 enum ScrollState {"started", "stopped"};
 
 dictionary ScrollViewChangeEventInit : EventInit {
   ScrollState state = "started";
-  float scrollX = 0;
-  float scrollY = 0;
 };
 
 [Constructor(DOMString type, optional ScrollViewChangeEventInit eventInit),
  ChromeOnly]
 interface ScrollViewChangeEvent : Event {
   readonly attribute ScrollState state;
-  readonly attribute float scrollX;
-  readonly attribute float scrollY;
 };
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -370,19 +370,23 @@ interface WebGL2RenderingContext : WebGL
     /* Programs and shaders */
     [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram? program, DOMString name);
 
     /* Uniforms and attributes */
     void uniform1ui(WebGLUniformLocation? location, GLuint v0);
     void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
     void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
     void uniform4ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
+    void uniform1uiv(WebGLUniformLocation? location, Uint32Array value);
     void uniform1uiv(WebGLUniformLocation? location, sequence<GLuint> value);
+    void uniform2uiv(WebGLUniformLocation? location, Uint32Array value);
     void uniform2uiv(WebGLUniformLocation? location, sequence<GLuint> value);
+    void uniform3uiv(WebGLUniformLocation? location, Uint32Array value);
     void uniform3uiv(WebGLUniformLocation? location, sequence<GLuint> value);
+    void uniform4uiv(WebGLUniformLocation? location, Uint32Array value);
     void uniform4uiv(WebGLUniformLocation? location, sequence<GLuint> value);
     void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value);
     void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value);
     void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value);
     void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value);
     void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value);
     void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value);
     void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value);
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -965,20 +965,20 @@ Proxy::Teardown()
           new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
                                              mSyncLoopTarget.forget(),
                                              false);
         if (!runnable->Dispatch(nullptr)) {
           NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
         }
       }
 
-      mWorkerPrivate = nullptr;
       mOutstandingSendCount = 0;
     }
 
+    mWorkerPrivate = nullptr;
     mXHRUpload = nullptr;
     mXHR = nullptr;
   }
 
   MOZ_ASSERT(!mWorkerPrivate);
   MOZ_ASSERT(!mSyncLoopTarget);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/context_test.js
@@ -0,0 +1,15 @@
+self.addEventListener("fetch", function(event) {
+  if (event.request.url.indexOf("index.html") >= 0 ||
+      event.request.url.indexOf("register.html") >= 0 ||
+      event.request.url.indexOf("unregister.html") >= 0) {
+    // Handle pass-through requests
+    event.respondWith(fetch(event.request));
+  } else if (event.request.url.indexOf("fetch.txt") >= 0) {
+    var body = event.request.context == "fetch" ?
+               "so fetch" : "so unfetch";
+    event.respondWith(new Response(body));
+  } else {
+    // Fail any request that we don't know about.
+    event.respondWith(Promise.reject());
+  }
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script>
+  function ok(v, msg) {
+    window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+  }
+
+  function is(a, b, msg) {
+    ok(a === b, msg + ", expected '" + b + "', got '" + a + "'");
+  }
+
+  function finish() {
+    window.parent.postMessage({status: "done"}, "*");
+  }
+
+  function testFetch() {
+    return fetch("fetch.txt").then(function(r) {
+      return r.text();
+    }).then(function(body) {
+      is(body, "so fetch", "A fetch() Request should have the 'fetch' context");
+    });
+  }
+
+  testFetch()
+  .then(function() {
+    finish();
+  });
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/register.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script>
+  function ok(v, msg) {
+    window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+  }
+
+  var isDone = false;
+  function done(reg) {
+    if (!isDone) {
+      ok(reg.waiting || reg.active, "Either active or waiting worker should be available.");
+      window.parent.postMessage({status: "registrationdone"}, "*");
+      isDone = true;
+    }
+  }
+
+  navigator.serviceWorker.register("context_test.js", {scope: "."})
+    .then(function(registration) {
+      if (registration.installing) {
+        registration.installing.onstatechange = function(e) {
+          done(registration);
+        };
+      } else {
+        done(registration);
+      }
+    });
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/unregister.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script>
+  navigator.serviceWorker.getRegistration(".").then(function(registration) {
+    registration.unregister().then(function(success) {
+      if (success) {
+        window.parent.postMessage({status: "unregistrationdone"}, "*");
+      }
+    });
+  });
+</script>
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -21,16 +21,20 @@ support-files =
   match_all_advanced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
   fetch/deliver-gzip.sjs
+  fetch/context/index.html
+  fetch/context/register.html
+  fetch/context/unregister.html
+  fetch/context/context_test.js
   fetch/https/index.html
   fetch/https/register.html
   fetch/https/unregister.html
   fetch/https/https_test.js
   fetch/https/clonedresponse/index.html
   fetch/https/clonedresponse/register.html
   fetch/https/clonedresponse/unregister.html
   fetch/https/clonedresponse/https_test.js
@@ -69,8 +73,9 @@ support-files =
 [test_post_message_advanced.html]
 [test_post_message_source.html]
 [test_match_all_client_properties.html]
 [test_close.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_match_all_client_id.html]
 [test_sandbox_intercept.html]
+[test_request_context.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context.html
@@ -0,0 +1,50 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  var iframe;
+  function runTest() {
+    iframe = document.querySelector("iframe");
+    iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/register.html";
+    window.onmessage = function(e) {
+      if (e.data.status == "ok") {
+        ok(e.data.result, e.data.message);
+      } else if (e.data.status == "registrationdone") {
+        iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/index.html";
+      } else if (e.data.status == "done") {
+        iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/unregister.html";
+      } else if (e.data.status == "unregistrationdone") {
+        window.onmessage = null;
+        ok(true, "Test finished successfully");
+        SimpleTest.finish();
+      }
+    };
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  onload = function() {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+      ["dom.serviceWorkers.enabled", true],
+      ["dom.serviceWorkers.testing.enabled", true],
+    ]}, runTest);
+  };
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/crashtests/1134545.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!-- saved from url=(0065)https://bug1134545.bugzilla.mozilla.org/attachment.cgi?id=8566418 -->
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+<script>
+
+function boom()
+{
+    textNode = document.createTextNode(" ");
+    x.appendChild(textNode);
+    x.setAttribute('contenteditable', "true");
+    textNode.remove();
+    window.getSelection().selectAllChildren(textNode);
+    document.execCommand("increasefontsize", false, null);
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="x" contenteditable="true"></div>
+
+
+</body></html>
\ No newline at end of file
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -54,8 +54,9 @@ load 767169.html
 load 769967.xhtml
 load 768748.html
 load 768765.html
 needs-focus load 771749.html
 load 772282.html
 load 776323.html
 needs-focus load 793866.html
 load 1057677.html
+load 1134545.html
--- a/editor/libeditor/nsHTMLEditorStyle.cpp
+++ b/editor/libeditor/nsHTMLEditorStyle.cpp
@@ -1522,20 +1522,20 @@ nsHTMLEditor::RelativeFontChange( int32_
     } else {
       atom = nsGkAtoms::small;
     }
 
     // Let's see in what kind of element the selection is
     int32_t offset;
     nsCOMPtr<nsINode> selectedNode;
     GetStartNodeAndOffset(selection, getter_AddRefs(selectedNode), &offset);
-    NS_ENSURE_TRUE(selectedNode, NS_OK);
-    if (IsTextNode(selectedNode)) {
+    if (selectedNode && IsTextNode(selectedNode)) {
       selectedNode = selectedNode->GetParentNode();
     }
+    NS_ENSURE_TRUE(selectedNode, NS_OK);
     if (!CanContainTag(*selectedNode, *atom)) {
       return NS_OK;
     }
 
     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
     mTypeInState->SetProp(atom, EmptyString(), EmptyString());
     return NS_OK;
   }
--- a/gfx/angle/src/common/blocklayout.cpp
+++ b/gfx/angle/src/common/blocklayout.cpp
@@ -28,16 +28,28 @@ BlockMemberInfo BlockLayoutEncoder::enco
 
     const BlockMemberInfo memberInfo(mCurrentOffset * BytesPerComponent, arrayStride * BytesPerComponent, matrixStride * BytesPerComponent, isRowMajorMatrix);
 
     advanceOffset(type, arraySize, isRowMajorMatrix, arrayStride, matrixStride);
 
     return memberInfo;
 }
 
+// static
+size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info)
+{
+    return (info.offset / BytesPerComponent) / ComponentsPerRegister;
+}
+
+// static
+size_t BlockLayoutEncoder::getBlockRegisterElement(const BlockMemberInfo &info)
+{
+    return (info.offset / BytesPerComponent) % ComponentsPerRegister;
+}
+
 void BlockLayoutEncoder::nextRegister()
 {
     mCurrentOffset = rx::roundUp<size_t>(mCurrentOffset, ComponentsPerRegister);
 }
 
 Std140BlockEncoder::Std140BlockEncoder()
 {
 }
--- a/gfx/angle/src/common/blocklayout.h
+++ b/gfx/angle/src/common/blocklayout.h
@@ -56,16 +56,19 @@ class BlockLayoutEncoder
     size_t getCurrentElement() const { return mCurrentOffset % ComponentsPerRegister; }
 
     virtual void enterAggregateType() = 0;
     virtual void exitAggregateType() = 0;
 
     static const size_t BytesPerComponent = 4u;
     static const unsigned int ComponentsPerRegister = 4u;
 
+    static size_t getBlockRegister(const BlockMemberInfo &info);
+    static size_t getBlockRegisterElement(const BlockMemberInfo &info);
+
   protected:
     size_t mCurrentOffset;
 
     void nextRegister();
 
     virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) = 0;
     virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride) = 0;
 };
--- a/gfx/angle/src/libGLESv2/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/ProgramD3D.cpp
@@ -1389,40 +1389,38 @@ void ProgramD3D::defineUniform(GLenum sh
         // Arrays are treated as aggregate types
         if (uniform.isArray())
         {
             encoder->enterAggregateType();
         }
 
         gl::LinkedUniform *linkedUniform = getUniformByName(fullName);
 
+        // Advance the uniform offset, to track registers allocation for structs
+        sh::BlockMemberInfo blockInfo = encoder->encodeType(uniform.type, uniform.arraySize, false);
+
         if (!linkedUniform)
         {
             linkedUniform = new gl::LinkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
                                               -1, sh::BlockMemberInfo::getDefaultBlockInfo());
             ASSERT(linkedUniform);
-            linkedUniform->registerElement = encoder->getCurrentElement();
+            linkedUniform->registerElement = sh::HLSLBlockEncoder::getBlockRegisterElement(blockInfo);
             mUniforms.push_back(linkedUniform);
         }
 
-        ASSERT(linkedUniform->registerElement == encoder->getCurrentElement());
-
         if (shader == GL_FRAGMENT_SHADER)
         {
-            linkedUniform->psRegisterIndex = encoder->getCurrentRegister();
+            linkedUniform->psRegisterIndex = sh::HLSLBlockEncoder::getBlockRegister(blockInfo);
         }
         else if (shader == GL_VERTEX_SHADER)
         {
-            linkedUniform->vsRegisterIndex = encoder->getCurrentRegister();
+            linkedUniform->vsRegisterIndex = sh::HLSLBlockEncoder::getBlockRegister(blockInfo);
         }
         else UNREACHABLE();
 
-        // Advance the uniform offset, to track registers allocation for structs
-        encoder->encodeType(uniform.type, uniform.arraySize, false);
-
         // Arrays are treated as aggregate types
         if (uniform.isArray())
         {
             encoder->exitAggregateType();
         }
     }
 }
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -676,16 +676,26 @@ private:
   // The value of GetLineScrollAmount(), for scroll frames.
   LayoutDeviceIntSize mLineScrollAmount;
 
   // The value of GetPageScrollAmount(), for scroll frames.
   LayoutDeviceIntSize mPageScrollAmount;
 
   // Whether or not the frame can be vertically scrolled with a mouse wheel.
   bool mAllowVerticalScrollWithWheel;
+
+  // WARNING!!!!
+  //
+  // When adding new fields to FrameMetrics, the following places should be
+  // updated to include them (as needed):
+  //    FrameMetrics::operator ==
+  //    AsyncPanZoomController::NotifyLayersUpdated
+  //    The ParamTraits specialization in GfxMessageUtils.h
+  //
+  // Please add new fields above this comment.
 };
 
 /**
  * This class allows us to uniquely identify a scrollable layer. The
  * mLayersId identifies the layer tree (corresponding to a child process
  * and/or tab) that the scrollable layer belongs to. The mPresShellId
  * is a temporal identifier (corresponding to the document loaded that
  * contains the scrollable layer, which may change over time). The
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -898,17 +898,16 @@ APZCTreeManager::ProcessWheelEvent(Widge
   return status;
 }
 
 bool
 APZCTreeManager::WillHandleWheelEvent(WidgetWheelEvent* aEvent)
 {
   return EventStateManager::WheelEventIsScrollAction(aEvent) &&
          aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
-         !gfxPrefs::MouseWheelHasScrollDeltaOverride() &&
          !EventStateManager::WheelEventNeedsDeltaMultipliers(aEvent);
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1437,16 +1437,28 @@ AsyncPanZoomController::GetScrollWheelDe
       aOutDeltaX *= scrollAmount.width;
       aOutDeltaY *= scrollAmount.height;
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
   }
 
+  if (gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) {
+    // Only apply delta multipliers if we're increasing the delta.
+    double hfactor = double(gfxPrefs::MouseWheelRootHScrollDeltaFactor()) / 100;
+    double vfactor = double(gfxPrefs::MouseWheelRootVScrollDeltaFactor()) / 100;
+    if (vfactor > 1.0) {
+      aOutDeltaX *= hfactor;
+    }
+    if (hfactor > 1.0) {
+      aOutDeltaY *= vfactor;
+    }
+  }
+
   LayoutDeviceIntSize pageScrollSize = mFrameMetrics.GetPageScrollAmount();
   if (Abs(aOutDeltaX) > pageScrollSize.width) {
     aOutDeltaX = (aOutDeltaX >= 0)
                  ? pageScrollSize.width
                  : -pageScrollSize.width;
   }
   if (Abs(aOutDeltaY) > pageScrollSize.height) {
     aOutDeltaY = (aOutDeltaY >= 0)
@@ -2850,16 +2862,18 @@ void AsyncPanZoomController::NotifyLayer
       mFrameMetrics.SetScrollableRect(aLayerMetrics.GetScrollableRect());
       needContentRepaint = true;
     }
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
     mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
     mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab());
+    mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount());
+    mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount());
 
     if (scrollOffsetUpdated) {
       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
         ToString(aLayerMetrics.GetScrollOffset()).c_str());
 
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
 
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -231,36 +231,40 @@ APZEventState::ProcessLongTap(const nsCO
   }
 
   mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled);
 }
 
 void
 APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
-                                 uint64_t aInputBlockId)
+                                 uint64_t aInputBlockId,
+                                 nsEventStatus aApzResponse)
 {
   if (aEvent.message == NS_TOUCH_START && aEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = TouchManager::gPreventMouseEvents ||
       aEvent.mFlags.mMultipleActionsPrevented;
+  bool sentContentResponse = false;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
     mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
           mPendingTouchPreventedBlockId, false);
+      sentContentResponse = true;
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, isTouchPrevented);
+      sentContentResponse = true;
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
       mPendingTouchPreventedBlockId = aInputBlockId;
     }
     break;
   }
 
@@ -269,23 +273,38 @@ APZEventState::ProcessTouchEvent(const W
       mTouchEndCancelled = true;
       mEndTouchIsClick = false;
     }
     // fall through
   case NS_TOUCH_CANCEL:
     mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
     // fall through
   case NS_TOUCH_MOVE: {
-    SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
+    sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
     break;
   }
 
   default:
     NS_WARNING("Unknown touch event type");
   }
+
+  if (sentContentResponse &&
+        aApzResponse == nsEventStatus_eConsumeDoDefault &&
+        gfxPrefs::PointerEventsEnabled()) {
+    WidgetTouchEvent cancelEvent(aEvent);
+    cancelEvent.message = NS_TOUCH_CANCEL;
+    cancelEvent.mFlags.mCancelable = false; // message != NS_TOUCH_CANCEL;
+    for (uint32_t i = 0; i < cancelEvent.touches.Length(); ++i) {
+      if (mozilla::dom::Touch* touch = cancelEvent.touches[i]) {
+        touch->convertToPointer = true;
+      }
+    }
+    nsEventStatus status;
+    cancelEvent.widget->DispatchEvent(&cancelEvent, status);
+  }
 }
 
 void
 APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId)
 {
   mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, aEvent.mFlags.mDefaultPrevented);
@@ -309,17 +328,17 @@ APZEventState::ProcessAPZStateChange(con
     if (scrollbarMediator) {
       scrollbarMediator->ScrollbarActivityStarted();
     }
 
     if (aDocument && mActiveAPZTransforms == 0) {
       nsCOMPtr<nsIDocShell> docshell(aDocument->GetDocShell());
       if (docshell && sf) {
         nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
-        nsdocshell->NotifyAsyncPanZoomStarted(sf->GetScrollPositionCSSPixels());
+        nsdocshell->NotifyAsyncPanZoomStarted();
       }
     }
     mActiveAPZTransforms++;
     break;
   }
   case APZStateChange::TransformEnd:
   {
     mActiveAPZTransforms--;
@@ -331,17 +350,17 @@ APZEventState::ProcessAPZStateChange(con
     if (scrollbarMediator) {
       scrollbarMediator->ScrollbarActivityStopped();
     }
 
     if (aDocument && mActiveAPZTransforms == 0) {
       nsCOMPtr<nsIDocShell> docshell(aDocument->GetDocShell());
       if (docshell && sf) {
         nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
-        nsdocshell->NotifyAsyncPanZoomStopped(sf->GetScrollPositionCSSPixels());
+        nsdocshell->NotifyAsyncPanZoomStopped();
       }
     }
     break;
   }
   case APZStateChange::StartTouch:
   {
     mActiveElementManager->HandleTouchStart(aArg);
     break;
@@ -359,26 +378,28 @@ APZEventState::ProcessAPZStateChange(con
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
 }
 
-void
+bool
 APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault,
                                                  const ScrollableLayerGuid& aGuid)
 {
   if (mPendingTouchPreventedResponse) {
     MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
     mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
         mPendingTouchPreventedBlockId, aPreventDefault);
     mPendingTouchPreventedResponse = false;
+    return true;
   }
+  return false;
 }
 
 already_AddRefed<nsIWidget>
 APZEventState::GetWidget() const
 {
   nsCOMPtr<nsIWidget> result = do_QueryReferent(mWidget);
   return result.forget();
 }
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -56,27 +56,28 @@ public:
   void ProcessLongTap(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
                       const CSSPoint& aPoint,
                       Modifiers aModifiers,
                       const ScrollableLayerGuid& aGuid,
                       uint64_t aInputBlockId,
                       float aPresShellResolution);
   void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
-                         uint64_t aInputBlockId);
+                         uint64_t aInputBlockId,
+                         nsEventStatus aApzResponse);
   void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId);
   void ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
                              ViewID aViewId,
                              APZStateChange aChange,
                              int aArg);
 private:
   ~APZEventState();
-  void SendPendingTouchPreventedResponse(bool aPreventDefault,
+  bool SendPendingTouchPreventedResponse(bool aPreventDefault,
                                          const ScrollableLayerGuid& aGuid);
   already_AddRefed<nsIWidget> GetWidget() const;
 private:
   nsWeakPtr mWidget;
   nsRefPtr<ActiveElementManager> mActiveElementManager;
   nsRefPtr<ContentReceivedInputBlockCallback> mContentReceivedInputBlockCallback;
   bool mPendingTouchPreventedResponse;
   ScrollableLayerGuid mPendingTouchPreventedGuid;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -61,36 +61,55 @@
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
+struct ReleaseKeepAlive : public nsRunnable
+{
+  NS_IMETHOD Run()
+  {
+    mKeep = nullptr;
+    return NS_OK;
+  }
+
+  UniquePtr<KeepAlive> mKeep;
+};
+
 /**
  * TextureChild is the content-side incarnation of the PTexture IPDL actor.
  *
  * TextureChild is used to synchronize a texture client and its corresponding
  * TextureHost if needed (a TextureClient that is not shared with the compositor
  * does not have a TextureChild)
  *
  * During the deallocation phase, a TextureChild may hold its recently destroyed
  * TextureClient's data until the compositor side confirmed that it is safe to
  * deallocte or recycle the it.
  */
 class TextureChild final : public PTextureChild
 {
-  ~TextureChild() {}
+  ~TextureChild()
+  {
+    if (mKeep && mMainThreadOnly && !NS_IsMainThread()) {
+      nsRefPtr<ReleaseKeepAlive> release = new ReleaseKeepAlive();
+      release->mKeep = Move(mKeep);
+      NS_DispatchToMainThread(release);
+    }
+  }
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
 
   TextureChild()
   : mForwarder(nullptr)
   , mTextureClient(nullptr)
+  , mMainThreadOnly(false)
   , mIPCOpen(false)
   {
   }
 
   bool Recv__delete__() override;
 
   bool RecvCompositorRecycle() override
   {
@@ -130,16 +149,17 @@ private:
     mIPCOpen = false;
     Release();
   }
 
   RefPtr<CompositableForwarder> mForwarder;
   RefPtr<TextureClient> mWaitForRecycle;
   TextureClient* mTextureClient;
   UniquePtr<KeepAlive> mKeep;
+  bool mMainThreadOnly;
   bool mIPCOpen;
 
   friend class TextureClient;
 };
 
 bool
 TextureChild::Recv__delete__()
 {
@@ -490,21 +510,22 @@ TextureClient::TextureClient(ISurfaceAll
 
 TextureClient::~TextureClient()
 {
   // All the destruction code that may lead to virtual method calls must
   // be in Finalize() which is called just before the destructor.
 }
 
 void
-TextureClient::KeepUntilFullDeallocation(UniquePtr<KeepAlive> aKeep)
+TextureClient::KeepUntilFullDeallocation(UniquePtr<KeepAlive> aKeep, bool aMainThreadOnly)
 {
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(!mActor->mKeep);
   mActor->mKeep = Move(aKeep);
+  mActor->mMainThreadOnly = aMainThreadOnly;
 }
 
 void TextureClient::ForceRemove(bool sync)
 {
   if (mValid && mActor) {
     if (sync || GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
       MOZ_PERFORMANCE_WARNING("gfx", "TextureClient/Host pair requires synchronous deallocation");
       if (mActor->IPCOpen()) {
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -398,17 +398,17 @@ public:
   bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; }
 
   /**
    * kee the passed object alive until the IPDL actor is destroyed. This can
    * help avoid race conditions in some cases.
    * It's a temporary hack to ensure that DXGI textures don't get destroyed
    * between serialization and deserialization.
    */
-  void KeepUntilFullDeallocation(UniquePtr<KeepAlive> aKeep);
+  void KeepUntilFullDeallocation(UniquePtr<KeepAlive> aKeep, bool aMainThreadOnly = false);
 
   /**
    * Create and init the TextureChild/Parent IPDL actor pair.
    *
    * Should be called only once per TextureClient.
    */
   bool InitIPDLActor(CompositableForwarder* aForwarder);
 
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1340,26 +1340,30 @@ CompositorD3D11::CreateShaders()
   return true;
 }
 
 bool
 CompositorD3D11::UpdateConstantBuffers()
 {
   HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE resource;
+  resource.pData = nullptr;
 
   hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (Failed(hr)) {
+  if (Failed(hr) || !resource.pData) {
+    gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hr;
     return false;
   }
   *(VertexShaderConstants*)resource.pData = mVSConstants;
   mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
+  resource.pData = nullptr;
 
   hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (Failed(hr)) {
+  if (Failed(hr) || !resource.pData) {
+    gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hr;
     return false;
   }
   *(PixelShaderConstants*)resource.pData = mPSConstants;
   mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
 
   ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
 
   mContext->VSSetConstantBuffers(0, 1, &buffer);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -179,19 +179,19 @@ TextureClientD3D11::TextureClientD3D11(I
   , mNeedsClear(false)
   , mNeedsClearWhite(false)
 {}
 
 TextureClientD3D11::~TextureClientD3D11()
 {
   if (mActor) {
     if (mTexture) {
-      KeepUntilFullDeallocation(MakeUnique<TKeepAlive<ID3D10Texture2D>>(mTexture10));
+      KeepUntilFullDeallocation(MakeUnique<TKeepAlive<ID3D11Texture2D>>(mTexture));
     } else if (mTexture10) {
-      KeepUntilFullDeallocation(MakeUnique<TKeepAlive<ID3D11Texture2D>>(mTexture));
+      KeepUntilFullDeallocation(MakeUnique<TKeepAlive<ID3D10Texture2D>>(mTexture10));
     }
   }
 #ifdef DEBUG
   // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
   // when it calls EndDraw. This EndDraw should not execute anything so it
   // shouldn't -really- need the lock but the debug layer chokes on this.
   if (mDrawTarget) {
     MOZ_ASSERT(!mIsLocked);
@@ -524,17 +524,17 @@ public:
 
 protected:
   RefPtr<IUnknown> mTextures[3];
 };
 
 DXGIYCbCrTextureClient::~DXGIYCbCrTextureClient()
 {
   if (mHoldRefs[0] && mActor) {
-    KeepUntilFullDeallocation(MakeUnique<YCbCrKeepAliveD3D11>(mHoldRefs));
+    KeepUntilFullDeallocation(MakeUnique<YCbCrKeepAliveD3D11>(mHoldRefs), true);
   }
   MOZ_COUNT_DTOR(DXGIYCbCrTextureClient);
 }
 
 bool
 DXGIYCbCrTextureClient::Lock(OpenMode)
 {
   MOZ_ASSERT(!mIsLocked);
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -208,17 +208,22 @@ PSharedBufferManagerParent* SharedBuffer
 bool SharedBufferManagerParent::RecvAllocateGrallocBuffer(const IntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, mozilla::layers::MaybeMagicGrallocBufferHandle* aHandle)
 {
 #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
 
   *aHandle = null_t();
 
   if (aFormat == 0 || aUsage == 0) {
     printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- format and usage must be non-zero");
-    return true;
+    return false;
+  }
+
+  if (aSize.width <= 0 || aSize.height <= 0) {
+    printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- requested gralloc buffer size is invalid");
+    return false;
   }
 
   // If the requested size is too big (i.e. exceeds the commonly used max GL texture size)
   // then we risk OOMing the parent process. It's better to just deny the allocation and
   // kill the child process, which is what the following code does.
   // TODO: actually use GL_MAX_TEXTURE_SIZE instead of hardcoding 4096
   if (aSize.width > 4096 || aSize.height > 4096) {
     printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- requested gralloc buffer is too big.");
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -350,25 +350,32 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
         bool emptyGlyf;
         const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf);
         if (glyf) {
             if (emptyGlyf) {
                 *aY = 0;
                 return;
             }
 
-            if (aGlyph >= uint32_t(mNumLongVMetrics)) {
-                aGlyph = mNumLongVMetrics - 1;
-            }
             const GlyphMetrics* metrics =
                 reinterpret_cast<const GlyphMetrics*>
                     (hb_blob_get_data(mVmtxTable, nullptr));
+            int16_t lsb;
+            if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) {
+                // Glyph is covered by the first (advance & sidebearing) array
+                lsb = int16_t(metrics->metrics[aGlyph].lsb);
+            } else {
+                // Glyph is covered by the second (sidebearing-only) array
+                const AutoSwap_PRInt16* sidebearings =
+                    reinterpret_cast<const AutoSwap_PRInt16*>
+                        (&metrics->metrics[mNumLongVMetrics]);
+                lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]);
+            }
             *aY = -FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
-                                (int16_t(metrics->metrics[aGlyph].lsb) +
-                                 int16_t(glyf->yMax)));
+                                (lsb + int16_t(glyf->yMax)));
             return;
         } else {
             // XXX TODO: not a truetype font; need to get glyph extents
             // via some other API?
             // For now, fall through to default code below.
         }
     }
 
@@ -377,18 +384,21 @@ gfxHarfBuzzShaper::GetGlyphVOrigin(hb_co
     gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
                                       TRUETYPE_TAG('h','h','e','a'));
     if (hheaTable) {
         uint32_t len;
         const MetricsHeader* hhea =
             reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable,
                                                                     &len));
         if (len >= sizeof(MetricsHeader)) {
-            *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
-                                int16_t(hhea->ascender));
+            // divide up the default advance we're using (1em) in proportion
+            // to ascender:descender from the hhea table
+            int16_t a = int16_t(hhea->ascender);
+            int16_t d = int16_t(hhea->descender);
+            *aY = -FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d));
             return;
         }
     }
 
     NS_NOTREACHED("we shouldn't be here!");
     *aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
 }
 
@@ -1266,21 +1276,32 @@ gfxHarfBuzzShaper::InitializeVertical()
     gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v','h','e','a'));
     if (vheaTable) {
         uint32_t len;
         const MetricsHeader* vhea =
             reinterpret_cast<const MetricsHeader*>
             (hb_blob_get_data(vheaTable, &len));
         if (len >= sizeof(MetricsHeader)) {
             mNumLongVMetrics = vhea->numOfLongMetrics;
-            if (mNumLongVMetrics > 0 &&
+            gfxFontEntry::AutoTable
+                maxpTable(entry, TRUETYPE_TAG('m','a','x','p'));
+            int numGlyphs = -1; // invalid if we fail to read 'maxp'
+            if (maxpTable &&
+                hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) {
+                const MaxpTableHeader* maxp =
+                    reinterpret_cast<const MaxpTableHeader*>
+                    (hb_blob_get_data(maxpTable, nullptr));
+                numGlyphs = uint16_t(maxp->numGlyphs);
+            }
+            if (mNumLongVMetrics > 0 && mNumLongVMetrics <= numGlyphs &&
                 int16_t(vhea->metricDataFormat) == 0) {
                 mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v','m','t','x'));
                 if (mVmtxTable && hb_blob_get_length(mVmtxTable) <
-                    mNumLongVMetrics * sizeof(LongMetric)) {
+                    mNumLongVMetrics * sizeof(LongMetric) +
+                    (numGlyphs - mNumLongVMetrics) * sizeof(int16_t)) {
                     // metrics table is not large enough for the claimed
                     // number of entries: invalid, do not use.
                     hb_blob_destroy(mVmtxTable);
                     mVmtxTable = nullptr;
                 }
             }
         }
     }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2325,43 +2325,53 @@ gfxPlatform::IsInLayoutAsapMode()
   // There are 2 modes of ASAP mode.
   // 1 is that the refresh driver and compositor are in lock step
   // the second is that the compositor goes ASAP and the refresh driver
   // goes at whatever the configurated rate is. This only checks the version
   // talos uses, which is the refresh driver and compositor are in lockstep.
   return Preferences::GetInt("layout.frame_rate", -1) == 0;
 }
 
+static nsString
+DetectBadApzWheelInputPrefs()
+{
+  static const char *sBadMultiplierPrefs[] = {
+    "mousewheel.default.delta_multiplier_x",
+    "mousewheel.with_alt.delta_multiplier_x",
+    "mousewheel.with_control.delta_multiplier_x",
+    "mousewheel.with_meta.delta_multiplier_x",
+    "mousewheel.with_shift.delta_multiplier_x",
+    "mousewheel.with_win.delta_multiplier_x",
+    "mousewheel.with_alt.delta_multiplier_y",
+    "mousewheel.with_control.delta_multiplier_y",
+    "mousewheel.with_meta.delta_multiplier_y",
+    "mousewheel.with_shift.delta_multiplier_y",
+    "mousewheel.with_win.delta_multiplier_y",
+  };
+
+  nsString badPref;
+  for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sBadMultiplierPrefs); i++) {
+    if (Preferences::GetInt(sBadMultiplierPrefs[i], 100) != 100) {
+      badPref.AssignASCII(sBadMultiplierPrefs[i]);
+      break;
+    }
+  }
+
+  return badPref;
+}
+
 void
 gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj)
 {
+  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+    return;
+  }
+
   if (SupportsApzWheelInput()) {
-    static const char *sBadPrefs[] = {
-      "mousewheel.system_scroll_override_on_root_content.enabled",
-      "mousewheel.default.delta_multiplier_x",
-      "mousewheel.with_alt.delta_multiplier_x",
-      "mousewheel.with_alt.delta_multiplier_x",
-      "mousewheel.with_control.delta_multiplier_x",
-      "mousewheel.with_meta.delta_multiplier_x",
-      "mousewheel.with_shift.delta_multiplier_x",
-      "mousewheel.with_win.delta_multiplier_x",
-      "mousewheel.with_alt.delta_multiplier_y",
-      "mousewheel.with_control.delta_multiplier_y",
-      "mousewheel.with_meta.delta_multiplier_y",
-      "mousewheel.with_shift.delta_multiplier_y",
-      "mousewheel.with_win.delta_multiplier_y",
-    };
-
-    nsString badPref;
-    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sBadPrefs); i++) {
-      if (Preferences::HasUserValue(sBadPrefs[i])) {
-        badPref.AssignASCII(sBadPrefs[i]);
-        break;
-      }
-    }
+    nsString badPref = DetectBadApzWheelInputPrefs();
 
     aObj.DefineProperty("ApzWheelInput", 1);
     if (badPref.Length()) {
       aObj.DefineProperty("ApzWheelInputWarning", badPref);
     }
   }
 
   if (SupportsApzTouchInput()) {
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -162,17 +162,18 @@ GetBackendName(mozilla::gfx::BackendType
 enum class DeviceResetReason
 {
   OK = 0,
   HUNG,
   REMOVED,
   RESET,
   DRIVER_ERROR,
   INVALID_CALL,
-  OUT_OF_MEMORY
+  OUT_OF_MEMORY,
+  UNKNOWN
 };
 
 class gfxPlatform {
     friend class SRGBOverrideObserver;
 
 public:
     typedef mozilla::gfx::Color Color;
     typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -181,16 +181,17 @@ private:
   DECL_GFX_PREF(Live, "apz.x_skate_size_multiplier",           APZXSkateSizeMultiplier, float, 1.5f);
   DECL_GFX_PREF(Live, "apz.x_stationary_size_multiplier",      APZXStationarySizeMultiplier, float, 3.0f);
   DECL_GFX_PREF(Live, "apz.y_skate_size_multiplier",           APZYSkateSizeMultiplier, float, 2.5f);
   DECL_GFX_PREF(Live, "apz.y_stationary_size_multiplier",      APZYStationarySizeMultiplier, float, 3.5f);
   DECL_GFX_PREF(Live, "apz.zoom_animation_duration_ms",        APZZoomAnimationDuration, int32_t, 250);
 
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.add-test-devices",               VRAddTestDevices, int32_t, 1);
+  DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
 
   DECL_GFX_PREF(Once, "gfx.android.rgb16.force",               AndroidRGB16Force, bool, false);
 #if defined(ANDROID)
   DECL_GFX_PREF(Once, "gfx.apitrace.enabled",                  UseApitrace, bool, false);
 #endif
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls",  CanvasAutoAccelerateMinCalls, int32_t, 4);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_seconds", CanvasAutoAccelerateMinSeconds, float, 5.0f);
@@ -330,18 +331,22 @@ private:
   DECL_GFX_PREF(Live, "layout.event-regions.enabled",          LayoutEventRegionsEnabled, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
 
   // This affects whether events will be routed through APZ or not.
-  DECL_GFX_PREF(Once, "mousewheel.system_scroll_override_on_root_content.enabled",
-                                                               MouseWheelHasScrollDeltaOverride, bool, false);
+  DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
+                                                               MouseWheelHasRootScrollDeltaOverride, bool, false);
+  DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.horizontal.factor",
+                                                               MouseWheelRootHScrollDeltaFactor, int32_t, 100);
+  DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.vertical.factor",
+                                                               MouseWheelRootVScrollDeltaFactor, int32_t, 100);
   DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
   DECL_GFX_PREF(Live, "mousewheel.transaction.timeout",        MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
 
   DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
 
   DECL_GFX_PREF(Live, "test.events.async.enabled",             TestEventsAsyncEnabled, bool, false);
   DECL_GFX_PREF(Live, "test.mousescroll",                      MouseScrollTestingEnabled, bool, false);
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -407,16 +407,17 @@ public:
   }
 };
 
 NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mD3D11DeviceInitialized(false)
   , mIsWARP(false)
+  , mCanInitMediaDevice(false)
 {
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
     mUsingGDIFonts = false;
 
     /* 
      * Initialize COM 
@@ -1137,60 +1138,68 @@ gfxWindowsPlatform::IsFontFormatSupporte
     if (aFormatFlags != 0) {
         return false;
     }
 
     // no format hint set, need to look at data
     return true;
 }
 
+static DeviceResetReason HResultToResetReason(HRESULT hr)
+{
+  switch (hr) {
+  case DXGI_ERROR_DEVICE_HUNG:
+    return DeviceResetReason::HUNG;
+  case DXGI_ERROR_DEVICE_REMOVED:
+    return DeviceResetReason::REMOVED;
+  case DXGI_ERROR_DEVICE_RESET:
+    return DeviceResetReason::RESET;
+  case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+    return DeviceResetReason::DRIVER_ERROR;
+  case DXGI_ERROR_INVALID_CALL:
+    return DeviceResetReason::INVALID_CALL;
+  case E_OUTOFMEMORY:
+    return DeviceResetReason::OUT_OF_MEMORY;
+  default:
+    MOZ_ASSERT(false);
+  }
+  return DeviceResetReason::UNKNOWN;
+}
+
 bool
 gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason)
 {
   if (aResetReason) {
     *aResetReason = DeviceResetReason::OK;
   }
 
   if (mD3D11Device) {
     HRESULT hr = mD3D11Device->GetDeviceRemovedReason();
     if (hr != S_OK) {
       if (aResetReason) {
-        switch (hr) {
-        case DXGI_ERROR_DEVICE_HUNG:
-          *aResetReason = DeviceResetReason::HUNG;
-          break;
-        case DXGI_ERROR_DEVICE_REMOVED:
-          *aResetReason = DeviceResetReason::REMOVED;
-          break;
-        case DXGI_ERROR_DEVICE_RESET:
-          *aResetReason = DeviceResetReason::RESET;
-          break;
-        case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
-          *aResetReason = DeviceResetReason::DRIVER_ERROR;
-          break;
-        case DXGI_ERROR_INVALID_CALL:
-          *aResetReason = DeviceResetReason::INVALID_CALL;
-          break;
-        case E_OUTOFMEMORY:
-          *aResetReason = DeviceResetReason::OUT_OF_MEMORY;
-          break;
-        default:
-          MOZ_ASSERT(false);
-        }
+        *aResetReason = HResultToResetReason(hr);
       }
       return true;
     }
   }
   if (mD3D11ContentDevice) {
-    if (mD3D11ContentDevice->GetDeviceRemovedReason() != S_OK) {
+    HRESULT hr = mD3D11ContentDevice->GetDeviceRemovedReason();
+    if (hr != S_OK) {
+      if (aResetReason) {
+        *aResetReason = HResultToResetReason(hr);
+      }
       return true;
     }
   }
   if (GetD3D10Device()) {
-    if (GetD3D10Device()->GetDeviceRemovedReason() != S_OK) {
+    HRESULT hr = GetD3D10Device()->GetDeviceRemovedReason();
+    if (hr != S_OK) {
+      if (aResetReason) {
+        *aResetReason = HResultToResetReason(hr);
+      }
       return true;
     }
   }
   return false;
 }
 
 void
 gfxWindowsPlatform::GetPlatformCMSOutputProfile(void* &mem, size_t &mem_size)
@@ -1584,21 +1593,61 @@ gfxWindowsPlatform::GetD3D11ContentDevic
   InitD3D11Devices();
 
   return mD3D11ContentDevice;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11MediaDevice()
 {
-  if (mD3D11DeviceInitialized) {
+  if (mD3D11MediaDevice) {
     return mD3D11MediaDevice;
   }
 
-  InitD3D11Devices();
+  if (!mCanInitMediaDevice) {
+    return nullptr;
+  }
+
+  mCanInitMediaDevice = false;
+
+  nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
+  decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
+    GetProcAddress(d3d11Module, "D3D11CreateDevice");
+  MOZ_ASSERT(d3d11CreateDevice);
+
+  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 = GetDXGIAdapter();
+  MOZ_ASSERT(adapter);
+
+  HRESULT 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(mD3D11MediaDevice), nullptr, nullptr);
+  } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+    mD3D11MediaDevice = nullptr;
+  }
+
+  d3d11Module.disown();
+
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  mD3D11MediaDevice->SetExceptionMode(0);
 
   return mD3D11MediaDevice;
 }
 
 
 ReadbackManagerD3D11*
 gfxWindowsPlatform::GetReadbackManager()
 {
@@ -1954,33 +2003,18 @@ gfxWindowsPlatform::InitD3D11Devices()
       return;
     }
 
     mD3D11ContentDevice->SetExceptionMode(0);
 
     Factory::SetDirect3D11Device(mD3D11ContentDevice);
   }
 
-  if (!useWARP || gfxPrefs::LayersD3D11ForceWARP()) {
-    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(mD3D11MediaDevice), nullptr, nullptr);
-    } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
-      mD3D11MediaDevice = nullptr;
-    }
-
-    if (FAILED(hr)) {
-      d3d11Module.disown();
-      return;
-    }
-
-    mD3D11MediaDevice->SetExceptionMode(0);
+  if (!useWARP) {
+    mCanInitMediaDevice = true;
   }
 
   // We leak these everywhere and we need them our entire runtime anyway, let's
   // leak it here as well.
   d3d11Module.disown();
 }
 
 static bool
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -291,16 +291,17 @@ private:
     mozilla::RefPtr<IDXGIAdapter1> mAdapter;
     nsRefPtr<mozilla::layers::DeviceManagerD3D9> mDeviceManager;
     mozilla::RefPtr<ID3D11Device> mD3D11Device;
     mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
     mozilla::RefPtr<ID3D11Device> mD3D11MediaDevice;
     bool mD3D11DeviceInitialized;
     mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
     bool mIsWARP;
+    bool mCanInitMediaDevice;
 
     virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
 };
 
 bool DoesD3D11TextureSharingWork(ID3D11Device *device);
 bool DoesD3D11DeviceWork(ID3D11Device *device);
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -92,17 +92,17 @@ class CPOWProxyHandler : public BaseProx
 
     virtual bool finalizeInBackground(Value priv) const override {
         return false;
     }
 
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
-                                MutableHandle<JSPropertyDescriptor> desc,
+                                Handle<JSPropertyDescriptor> desc,
                                 ObjectOpResult &result) const override;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const override;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
                          ObjectOpResult &result) const override;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const override;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy,
                                    ObjectOpResult &result) const override;
@@ -208,25 +208,25 @@ WrapperOwner::getOwnPropertyDescriptor(J
     if (!ok(cx, status))
         return false;
 
     return toDescriptor(cx, result, desc);
 }
 
 bool
 CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
-                                 MutableHandle<JSPropertyDescriptor> desc,
+                                 Handle<JSPropertyDescriptor> desc,
                                  ObjectOpResult &result) const
 {
     FORWARD(defineProperty, (cx, proxy, id, desc, result));
 }
 
 bool
 WrapperOwner::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
-                             MutableHandle<JSPropertyDescriptor> desc,
+                             Handle<JSPropertyDescriptor> desc,
                              ObjectOpResult &result)
 {
     ObjectId objId = idOf(proxy);
 
     JSIDVariant idVar;
     if (!toJSIDVariant(cx, id, &idVar))
         return false;
 
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -27,17 +27,17 @@ class WrapperOwner : public virtual Java
     explicit WrapperOwner(JSRuntime *rt);
     bool init();
 
     // Standard internal methods.
     // (The traps should be in the same order like js/Proxy.h)
     bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                   JS::MutableHandle<JSPropertyDescriptor> desc);
     bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
-                        JS::MutableHandle<JSPropertyDescriptor> desc,
+                        JS::Handle<JSPropertyDescriptor> desc,
                         JS::ObjectOpResult &result);
     bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                  JS::ObjectOpResult &result);
     bool preventExtensions(JSContext *cx, JS::HandleObject proxy, JS::ObjectOpResult &result);
     bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
     bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -328,18 +328,18 @@ typedef void
 /* js::Class operation signatures. */
 
 namespace js {
 
 typedef bool
 (* LookupPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                      JS::MutableHandleObject objp, JS::MutableHandle<Shape*> propp);
 typedef bool
-(* DefinePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
-                     JSGetterOp getter, JSSetterOp setter, unsigned attrs,
+(* DefinePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+                     JS::Handle<JSPropertyDescriptor> desc,
                      JS::ObjectOpResult &result);
 typedef bool
 (* HasPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *foundp);
 typedef bool
 (* GetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
                   JS::MutableHandleValue vp);
 typedef bool
 (* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -594,16 +594,17 @@ struct CompartmentStats
     macro(Other,   NotLiveGCThing, typeInferenceTypeScripts) \
     macro(Other,   NotLiveGCThing, typeInferenceAllocationSiteTables) \
     macro(Other,   NotLiveGCThing, typeInferenceArrayTypeTables) \
     macro(Other,   NotLiveGCThing, typeInferenceObjectTypeTables) \
     macro(Other,   NotLiveGCThing, compartmentObject) \
     macro(Other,   NotLiveGCThing, compartmentTables) \
     macro(Other,   NotLiveGCThing, innerViewsTable) \
     macro(Other,   NotLiveGCThing, lazyArrayBuffersTable) \
+    macro(Other,   NotLiveGCThing, objectMetadataTable) \
     macro(Other,   NotLiveGCThing, crossCompartmentWrappersTable) \
     macro(Other,   NotLiveGCThing, regexpCompartment) \
     macro(Other,   NotLiveGCThing, savedStacksSet)
 
     CompartmentStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         classInfo(),
         extra(),
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -13,16 +13,17 @@
 
 #include "js/CallNonGenericMethod.h"
 #include "js/Class.h"
 
 namespace js {
 
 using JS::AutoIdVector;
 using JS::CallArgs;
+using JS::Handle;
 using JS::HandleId;
 using JS::HandleObject;
 using JS::HandleValue;
 using JS::IsAcceptableThis;
 using JS::MutableHandle;
 using JS::MutableHandleObject;
 using JS::MutableHandleValue;
 using JS::NativeImpl;
@@ -247,17 +248,17 @@ class JS_FRIEND_API(BaseProxyHandler)
 
     virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
                        bool *bp) const;
 
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
-                                MutableHandle<JSPropertyDescriptor> desc,
+                                Handle<JSPropertyDescriptor> desc,
                                 ObjectOpResult &result) const = 0;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const = 0;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
                          ObjectOpResult &result) const = 0;
 
     /*
      * Because [[Enumerate]] is one of the standard traps it should be overridden.
@@ -368,17 +369,17 @@ class JS_FRIEND_API(DirectProxyHandler) 
                                               bool aHasSecurityPolicy = false)
       : BaseProxyHandler(aFamily, aHasPrototype, aHasSecurityPolicy)
     { }
 
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
-                                MutableHandle<JSPropertyDescriptor> desc,
+                                Handle<JSPropertyDescriptor> desc,
                                 ObjectOpResult &result) const override;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const override;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
                          ObjectOpResult &result) const override;
     virtual bool enumerate(JSContext *cx, HandleObject proxy,
                            MutableHandleObject objp) const override;
     virtual bool getPrototype(JSContext *cx, HandleObject proxy,
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -112,16 +112,17 @@ namespace JS {
     _(CantInlineHeavyweight)                                            \
     _(CantInlineNeedsArgsObj)                                           \
     _(CantInlineDebuggee)                                               \
     _(CantInlineUnknownProps)                                           \
     _(CantInlineExceededDepth)                                          \
     _(CantInlineExceededTotalBytecodeLength)                            \
     _(CantInlineBigCaller)                                              \
     _(CantInlineBigCallee)                                              \
+    _(CantInlineBigCalleeInlinedBytecodeLength)                         \
     _(CantInlineNotHot)                                                 \
     _(CantInlineNotInDispatch)                                          \
     _(CantInlineUnreachable)                                            \
     _(CantInlineNativeBadForm)                                          \
     _(CantInlineNativeBadType)                                          \
     _(CantInlineNativeNoTemplateObj)                                    \
     _(CantInlineBound)                                                  \
                                                                         \
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1389,91 +1389,74 @@ DisplayName(JSContext *cx, unsigned argc
     }
 
     JSFunction *fun = &args[0].toObject().as<JSFunction>();
     JSString *str = fun->displayAtom();
     args.rval().setString(str ? str : cx->runtime()->emptyString);
     return true;
 }
 
-static bool
-ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
+static JSObject *
+ShellObjectMetadataCallback(JSContext *cx)
 {
     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
-        return false;
+        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
 
     RootedObject stack(cx, NewDenseEmptyArray(cx));
     if (!stack)
-        return false;
+        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
 
     static int createdIndex = 0;
     createdIndex++;
 
     if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
                            JS_STUBGETTER, JS_STUBSETTER))
     {
-        return false;
+        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
     }
 
     if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
                            JS_STUBGETTER, JS_STUBSETTER))
     {
-        return false;
+        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
     }
 
     int stackIndex = 0;
     RootedId id(cx);
     RootedValue callee(cx);
     for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
         if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
             id = INT_TO_JSID(stackIndex);
             RootedObject callee(cx, iter.callee(cx));
             if (!JS_DefinePropertyById(cx, stack, id, callee, 0,
                                        JS_STUBGETTER, JS_STUBSETTER))
             {
-                return false;
+                CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
             }
             stackIndex++;
         }
     }
 
-    *pmetadata = obj;
-    return true;
+    return obj;
 }
 
 static bool
 SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool enabled = args.length() ? ToBoolean(args[0]) : false;
     SetObjectMetadataCallback(cx, enabled ? ShellObjectMetadataCallback : nullptr);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 2 || !args[0].isObject() || !args[1].isObject()) {
-        JS_ReportError(cx, "Both arguments must be objects");
-        return false;
-    }
-
-    args.rval().setUndefined();
-
-    RootedObject obj(cx, &args[0].toObject());
-    RootedObject metadata(cx, &args[1].toObject());
-    return SetObjectMetadata(cx, obj, metadata);
-}
-
-static bool
 GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !args[0].isObject()) {
         JS_ReportError(cx, "Argument must be an object");
         return false;
     }
 
@@ -1547,32 +1530,50 @@ js::testingFunc_inIon(JSContext *cx, uns
     args.rval().setBoolean(false);
     return true;
 }
 
 bool
 js::testingFunc_assertFloat32(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 2) {
+        JS_ReportError(cx, "Expects only 2 arguments");
+        return false;
+    }
 
     // NOP when not in IonMonkey
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 TestingFunc_assertJitStackInvariants(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     jit::AssertJitStackInvariants(cx);
     args.rval().setUndefined();
     return true;
 }
 
+bool
+js::testingFunc_assertRecoveredOnBailout(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 2) {
+        JS_ReportError(cx, "Expects only 2 arguments");
+        return false;
+    }
+
+    // NOP when not in IonMonkey
+    args.rval().setUndefined();
+    return true;
+}
+
 static bool
 SetJitCompilerOption(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
 
     if (args.length() != 2) {
         ReportUsageError(cx, callee, "Wrong number of arguments.");
@@ -2753,20 +2754,16 @@ gc::ZealModeHelpText),
     JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
 "isRelazifiableFunction(fun)",
 "  Ture if fun is a JSFunction with a relazifiable JSScript."),
 
     JS_FN_HELP("setObjectMetadataCallback", SetObjectMetadataCallback, 1, 0,
 "setObjectMetadataCallback(fn)",
 "  Specify function to supply metadata for all newly created objects."),
 
-    JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0,
-"setObjectMetadata(obj, metadataObj)",
-"  Change the metadata for an object."),
-
     JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
 "getObjectMetadata(obj)",
 "  Get the metadata for an object."),
 
     JS_FN_HELP("bailout", testingFunc_bailout, 0, 0,
 "bailout()",
 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
 
--- a/js/src/builtin/TestingFunctions.h
+++ b/js/src/builtin/TestingFunctions.h
@@ -16,16 +16,19 @@ DefineTestingFunctions(JSContext *cx, Ha
 
 bool
 testingFunc_bailout(JSContext *cx, unsigned argc, Value *vp);
 
 bool
 testingFunc_assertFloat32(JSContext *cx, unsigned argc, Value *vp);
 
 bool
+testingFunc_assertRecoveredOnBailout(JSContext *cx, unsigned argc, Value *vp);
+
+bool
 testingFunc_inJit(JSContext *cx, unsigned argc, Value *vp);
 
 bool
 testingFunc_inIon(JSContext *cx, unsigned argc, Value *vp);
 
 } /* namespace js */
 
 #endif /* builtin_TestingFunctions_h */
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -14,16 +14,17 @@
 #include "jsutil.h"
 
 #include "gc/Marking.h"
 #include "js/Vector.h"
 #include "vm/GlobalObject.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
+#include "vm/WeakMapObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::AssertedCast;
@@ -1373,22 +1374,21 @@ TypedObject::typedMemBase() const
         return owner.as<ArrayBufferObject>().dataPointer();
     return owner.as<InlineTypedObject>().inlineTypedMem();
 }
 
 bool
 TypedObject::isAttached() const
 {
     if (is<InlineTransparentTypedObject>()) {
-        LazyArrayBufferTable *table = compartment()->lazyArrayBuffers;
+        ObjectWeakMap *table = compartment()->lazyArrayBuffers;
         if (table) {
-            ArrayBufferObject *buffer =
-                table->maybeBuffer(&const_cast<TypedObject *>(this)->as<InlineTransparentTypedObject>());
+            JSObject *buffer = table->lookup(this);
             if (buffer)
-                return !buffer->isNeutered();
+                return !buffer->as<ArrayBufferObject>().isNeutered();
         }
         return true;
     }
     if (is<InlineOpaqueTypedObject>())
         return true;
     if (!as<OutlineTypedObject>().outOfLineTypedMem())
         return false;
     JSObject &owner = as<OutlineTypedObject>().owner();
@@ -1746,18 +1746,18 @@ ReportPropertyError(JSContext *cx,
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                          errorNumber, propName);
 
     JS_free(cx, propName);
     return false;
 }
 
 bool
-TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                                GetterOp getter, SetterOp setter, unsigned attrs,
+TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id,
+                                Handle<JSPropertyDescriptor> desc,
                                 ObjectOpResult &result)
 {
     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
     return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
 }
 
 bool
 TypedObject::obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool *foundp)
@@ -2188,124 +2188,70 @@ InlineTypedObject::objectMovedDuringMino
         trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, oldData, newData,
                                                              size_t(descr.size()) >= sizeof(uintptr_t));
     }
 }
 
 ArrayBufferObject *
 InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
 {
-    LazyArrayBufferTable *&table = cx->compartment()->lazyArrayBuffers;
+    ObjectWeakMap *&table = cx->compartment()->lazyArrayBuffers;
     if (!table) {
-        table = cx->new_<LazyArrayBufferTable>(cx);
+        table = cx->new_<ObjectWeakMap>(cx);
         if (!table)
             return nullptr;
     }
 
-    ArrayBufferObject *buffer = table->maybeBuffer(this);
-    if (buffer)
-        return buffer;
+    JSObject *obj = table->lookup(this);
+    if (obj)
+        return &obj->as<ArrayBufferObject>();
 
     ArrayBufferObject::BufferContents contents =
         ArrayBufferObject::BufferContents::createPlain(inlineTypedMem());
     size_t nbytes = typeDescr().size();
 
     // Prevent GC under ArrayBufferObject::create, which might move this object
     // and its contents.
     gc::AutoSuppressGC suppress(cx);
 
-    buffer = ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
+    ArrayBufferObject *buffer =
+        ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
     if (!buffer)
         return nullptr;
 
     // The owning object must always be the array buffer's first view. This
     // both prevents the memory from disappearing out from under the buffer
     // (the first view is held strongly by the buffer) and is used by the
     // buffer marking code to detect whether its data pointer needs to be
     // relocated.
     JS_ALWAYS_TRUE(buffer->addView(cx, this));
 
     buffer->setForInlineTypedObject();
     buffer->setHasTypedObjectViews();
 
-    if (!table->addBuffer(cx, this, buffer))
+    if (!table->add(cx, this, buffer))
         return nullptr;
 
+    if (IsInsideNursery(this)) {
+        // Make sure the buffer is traced by the next generational collection,
+        // so that its data pointer is updated after this typed object moves.
+        cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(buffer);
+    }
+
     return buffer;
 }
 
 ArrayBufferObject *
 OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
 {
     if (owner().is<ArrayBufferObject>())
         return &owner().as<ArrayBufferObject>();
     return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
 }
 
-LazyArrayBufferTable::LazyArrayBufferTable(JSContext *cx)
- : map(cx)
-{
-    if (!map.init())
-        CrashAtUnhandlableOOM("LazyArrayBufferTable");
-}
-
-LazyArrayBufferTable::~LazyArrayBufferTable()
-{
-    WeakMapBase::removeWeakMapFromList(&map);
-}
-
-ArrayBufferObject *
-LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject *obj)
-{
-    if (Map::Ptr p = map.lookup(obj))
-        return &p->value()->as<ArrayBufferObject>();
-    return nullptr;
-}
-
-bool
-LazyArrayBufferTable::addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer)
-{
-    MOZ_ASSERT(!map.has(obj));
-    if (!map.put(obj, buffer)) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    MOZ_ASSERT(!IsInsideNursery(buffer));
-    if (IsInsideNursery(obj)) {
-        // Strip the barriers from the type before inserting into the store
-        // buffer, as is done for DebugScopes::proxiedScopes.
-        Map::Base *baseHashMap = static_cast<Map::Base *>(&map);
-
-        typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
-        UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
-
-        typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
-        cx->runtime()->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, obj));
-
-        // Also make sure the buffer is traced, so that its data pointer is
-        // updated after the typed object moves.
-        cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(buffer);
-    }
-
-    return true;
-}
-
-void
-LazyArrayBufferTable::trace(JSTracer *trc)
-{
-    map.trace(trc);
-}
-
-size_t
-LazyArrayBufferTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
-{
-    return mallocSizeOf(this) + map.sizeOfExcludingThis(mallocSizeOf);
-}
-
 /******************************************************************************
  * Typed object classes
  */
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace)        \
     const Class Name::class_ = {                         \
         # Name,                                          \
         Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, \
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -523,18 +523,18 @@ class TypedObject : public JSObject
 
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
                                    MutableHandleShape propp);
 
     static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                                   MutableHandleObject objp, MutableHandleShape propp);
 
-    static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
-                                   GetterOp getter, SetterOp setter, unsigned attrs,
+    static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id,
+                                   Handle<JSPropertyDescriptor> desc,
                                    ObjectOpResult &result);
 
     static bool obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool *foundp);
 
     static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
                                 HandleId id, MutableHandleValue vp);
 
     static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
@@ -1016,40 +1016,16 @@ IsTypeDescrClass(const Class* clasp)
 }
 
 inline bool
 TypedObject::opaque() const
 {
     return IsOpaqueTypedObjectClass(getClass());
 }
 
-// Inline transparent typed objects do not initially have an array buffer, but
-// can have that buffer created lazily if it is accessed later. This table
-// manages references from such typed objects to their buffers.
-class LazyArrayBufferTable
-{
-  private:
-    // The map from transparent typed objects to their lazily created buffer.
-    // Keys in this map are InlineTransparentTypedObjects and values are
-    // ArrayBufferObjects, but we don't enforce this in the type system due to
-    // the extra marking code goop that requires.
-    typedef WeakMap<PreBarrieredObject, RelocatablePtrObject> Map;
-    Map map;
-
-  public:
-    explicit LazyArrayBufferTable(JSContext *cx);
-    ~LazyArrayBufferTable();
-
-    ArrayBufferObject *maybeBuffer(InlineTransparentTypedObject *obj);
-    bool addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer);
-
-    void trace(JSTracer *trc);
-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
-};
-
 JSObject *
 InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj);
 
 } // namespace js
 
 template <>
 inline bool
 JSObject::is<js::SimpleTypeDescr>() const
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -7,27 +7,33 @@ ABSDIR="$(cd $DIR; pwd)"
 SOURCE="$(cd $DIR/../../../..; pwd)"
 
 function usage() {
   echo "Usage: $0 [--dep] <variant>"
 }
 
 clean=""
 platform=""
+TIMEOUT=7200
 while [ $# -gt 1 ]; do
     case "$1" in
         --clobber)
             shift
             clean=1
             ;;
         --platform)
             shift
             platform="$1"
             shift
             ;;
+        --timeout)
+            shift
+            TIMEOUT="$1"
+            shift
+            ;;
         *)
             echo "Invalid arguments" >&2
             usage
             exit 1
             ;;
     esac
 done
 
@@ -40,17 +46,17 @@ if [[ "$VARIANT" = "generational" ]]; th
 fi
 
 if [ ! -f "$ABSDIR/variants/$VARIANT" ]; then
     echo "Could not find variant '$VARIANT'"
     usage
     exit 1
 fi
 
-(cd "$SOURCE/js/src"; autoconf-2.13 || autoconf2.13)
+(cd "$SOURCE/js/src"; autoconf-2.13 || autoconf2.13 || autoconf213)
 
 TRY_OVERRIDE=$SOURCE/js/src/config.try
 if [ -r $TRY_OVERRIDE ]; then
   CONFIGURE_ARGS="$(cat "$TRY_OVERRIDE")"
 else
   CONFIGURE_ARGS="$(cat "$ABSDIR/variants/$VARIANT")"
 fi
 
@@ -65,16 +71,19 @@ fi
 cd "$OBJDIR"
 
 echo "OBJDIR is $OBJDIR"
 
 USE_64BIT=false
 
 if [[ "$OSTYPE" == darwin* ]]; then
   USE_64BIT=true
+  if [ "$VARIANT" = "arm-sim-osx" ]; then
+    USE_64BIT=false
+  fi
 elif [ "$OSTYPE" = "linux-gnu" ]; then
   if [ -n "$AUTOMATION" ]; then
       GCCDIR="${GCCDIR:-/tools/gcc-4.7.2-0moz1}"
       CONFIGURE_ARGS="$CONFIGURE_ARGS --with-ccache"
   fi
   UNAME_M=$(uname -m)
   MAKEFLAGS=-j4
   if [ "$VARIANT" = "arm-sim" ]; then
@@ -122,16 +131,22 @@ COMMAND_PREFIX=''
 
 # On Linux, disable ASLR to make shell builds a bit more reproducible.
 if type setarch >/dev/null 2>&1; then
     COMMAND_PREFIX="setarch $(uname -m) -R "
 fi
 
 RUN_JSTESTS=true
 
+PARENT=$$
+sh -c "sleep $TIMEOUT; kill $PARENT" <&- >&- 2>&- &
+KILLER=$!
+disown %1
+trap "kill $KILLER" EXIT
+
 if [[ "$VARIANT" = "rootanalysis" ]]; then
     export JS_GC_ZEAL=7
 
 elif [[ "$VARIANT" = "compacting" ]]; then
     export JS_GC_ZEAL=14
 
     # Ignore timeouts from tests that are known to take too long with this zeal mode
     export JITTEST_EXTRA_ARGS=--ignore-timeouts=$ABSDIR/cgc-jittest-timeouts.txt
--- a/js/src/devtools/automation/variants/arm-sim
+++ b/js/src/devtools/automation/variants/arm-sim
@@ -1,8 +1,7 @@
 --enable-optimize
 --enable-debug
 --enable-stdcxx-compat
 --disable-shared-js
---enable-threadsafe
 --enable-arm-simulator
 --target=i686-pc-linux
 --host=i686-pc-linux
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/automation/variants/arm-sim-osx
@@ -0,0 +1,7 @@
+--enable-optimize
+--enable-debug
+--enable-stdcxx-compat
+--disable-shared-js
+--enable-arm-simulator
+--target=i686-apple-darwin10.0.0
+--host=i686-apple-darwin10.0.0
--- a/js/src/devtools/rootAnalysis/build/sixgill-b2g.manifest
+++ b/js/src/devtools/rootAnalysis/build/sixgill-b2g.manifest
@@ -1,17 +1,18 @@
 [
-{
-"sixgill_version": "0.9.1-177"
-},
-{
-"size": 2281048,
-"digest": "266cba549d2959a5c2e669c85b9adea4d0008e2e23658b844f94540cf41c55df5503ac5858f252418b01d140a48fb235725f2681fb4cbf172f8b283b2874b96c",
-"algorithm": "sha512",
-"filename": "sixgill.tar.xz"
-},
-{
-"size": 48,
-"digest": "9f4a123b8b037c4313c7065f872e07af38390f352f2498b41779c389fa1a0712e5f588d7b3d9a124875916f5257434da83553b560d7d146192f3f6c8da6f0412",
-"algorithm": "sha512",
-"filename": "setup.sh.sixgill"
-}
+   {
+      "hg_id" : "0b889f399d47+ cleaner/qtip/tip",
+      "sixgill_version" : "0.9.1-177"
+   },
+   {
+      "algorithm" : "sha512",
+      "digest" : "bb7fcbcaf8d160bba92fa66b2e3607f3335d68c8339996c880ea5757c6e611b74ced6c6d5c50d523fc8d688cc922ee6e1628f3fb514af8ebeb9c877ad580e5fd",
+      "filename" : "sixgill.tar.xz",
+      "size" : 2159496
+   },
+   {
+      "algorithm" : "sha512",
+      "digest" : "9f4a123b8b037c4313c7065f872e07af38390f352f2498b41779c389fa1a0712e5f588d7b3d9a124875916f5257434da83553b560d7d146192f3f6c8da6f0412",
+      "filename" : "setup.sh.sixgill",
+      "size" : 48
+   }
 ]
--- a/js/src/devtools/rootAnalysis/build/sixgill.manifest
+++ b/js/src/devtools/rootAnalysis/build/sixgill.manifest
@@ -1,17 +1,18 @@
 [
-{
-"sixgill_version": "0.9.1-177"
-},
-{
-"size": 2316808,
-"digest": "e58eece57ef7688db4c999b0069b2e4607b1514f681d636969e40a19633173d3b2636fd9a624b58166c789e99c6ba67bfafbdae1d335ac4df018c9f52503f0e4",
-"algorithm": "sha512",
-"filename": "sixgill.tar.xz"
-},
-{
-"size": 48,
-"digest": "9f4a123b8b037c4313c7065f872e07af38390f352f2498b41779c389fa1a0712e5f588d7b3d9a124875916f5257434da83553b560d7d146192f3f6c8da6f0412",
-"algorithm": "sha512",
-"filename": "setup.sh.sixgill"
-}
+   {
+      "hg_id" : "0b889f399d47+ cleaner/qtip/tip",
+      "sixgill_version" : "0.9.1-177"
+   },
+   {
+      "algorithm" : "sha512",
+      "digest" : "c5c22d19005a1389d8dc04e405f5b99e6b8755908fb52a285ff516213d9f32e1e64d5c402a3ee2e52bceffa7dee71852c9d922ec0003e1642a701a0daadb06d3",
+      "filename" : "sixgill.tar.xz",
+      "size" : 2255520
+   },
+   {
+      "algorithm" : "sha512",
+      "digest" : "9f4a123b8b037c4313c7065f872e07af38390f352f2498b41779c389fa1a0712e5f588d7b3d9a124875916f5257434da83553b560d7d146192f3f6c8da6f0412",
+      "filename" : "setup.sh.sixgill",
+      "size" : 48
+   }
 ]
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1068,19 +1068,16 @@ void
 BaseShape::markChildren(JSTracer *trc)
 {
     if (isOwned())
         gc::MarkBaseShape(trc, &unowned_, "base");
 
     JSObject* global = compartment()->unsafeUnbarrieredMaybeGlobal();
     if (global)
         MarkObjectUnbarriered(trc, &global, "global");
-
-    if (metadata)
-        gc::MarkObject(trc, &metadata, "metadata");
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
     MOZ_ASSERT(!IsInsideNursery(thing));
 
@@ -1117,19 +1114,16 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh
 {
     base->assertConsistency();
 
     base->compartment()->mark();
 
     if (GlobalObject *global = base->compartment()->unsafeUnbarrieredMaybeGlobal())
         gcmarker->traverse(global);
 
-    if (JSObject *metadata = base->getObjectMetadata())
-        MaybePushMarkStackBetweenSlices(gcmarker, metadata);
-
     /*
      * All children of the owned base shape are consistent with its
      * unowned one, thus we do not need to trace through children of the
      * unowned base shape.
      */
     if (base->isOwned()) {
         UnownedBaseShape *unowned = base->baseUnowned();
         MOZ_ASSERT(base->compartment() == unowned->compartment());
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -302,17 +302,17 @@ Mark(JSTracer *trc, JSObject **objp, con
 
 /* For use by Debugger::WeakMap's missingScopes HashKeyRef instantiation. */
 inline void
 Mark(JSTracer *trc, NativeObject **obj, const char *name)
 {
     MarkObjectUnbarriered(trc, obj, name);
 }
 
-/* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */
+/* For use by Debugger::WeakMap's liveScopes HashKeyRef instantiation. */
 inline void
 Mark(JSTracer *trc, ScopeObject **obj, const char *name)
 {
     MarkObjectUnbarriered(trc, obj, name);
 }
 
 inline bool
 IsMarked(BarrieredBase<Value> *v)
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -19,16 +19,17 @@
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "jit/MacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
 #include "vm/JSONParser.h"
+#include "vm/WeakMapObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
@@ -510,16 +511,19 @@ js::gc::GCRuntime::markRuntime(JSTracer 
         }
 
         /* Mark debug scopes, if present */
         if (c->debugScopes)
             c->debugScopes->mark(trc);
 
         if (c->lazyArrayBuffers)
             c->lazyArrayBuffers->trace(trc);
+
+        if (c->objectMetadataTable)
+            c->objectMetadataTable->trace(trc);
     }
 
     MarkInterpreterActivations(rt, trc);
 
     jit::MarkJitActivations(rt, trc);
 
     if (!isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
--- a/js/src/jit-test/tests/SIMD/recover.js
+++ b/js/src/jit-test/tests/SIMD/recover.js
@@ -1,40 +1,47 @@
 load(libdir + 'simd.js');
 
 if (!this.hasOwnProperty("SIMD"))
   quit();
 
+// This test case ensure that if we are able to optimize SIMD, then we can use
+// recover instructions to get rid of the allocations. So, there is no value
+// (and the test case would fail) if we are not able to inline SIMD
+// constructors.
+if (!isSimdAvailable())
+  quit();
+
 setJitCompilerOption("baseline.warmup.trigger", 10);
 setJitCompilerOption("ion.warmup.trigger", 20);
 
 // This function is used to cause an invalidation after having removed a branch
 // after DCE.  This is made to check if we correctly recover an array
 // allocation.
 var uceFault = function (i) {
     if (i > 98)
         uceFault = function (i) { return true; };
     return false;
 };
 
 // Check that we can correctly recover a boxed value.
 var uceFault_simdBox_i4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_i4'));
 function simdBox_i4(i) {
     var a = SIMD.int32x4(i, i, i, i);
-    if (uceFault_simdBox_i4(i) || uceFault_simdBox_i4(i)) {
+    if (uceFault_simdBox_i4(i) || uceFault_simdBox_i4(i))
         assertEqX4(a, [i, i, i, i]);
-    }
+    assertRecoveredOnBailout(a, true);
     return 0;
 }
 
 var uceFault_simdBox_f4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_f4'));
 function simdBox_f4(i) {
     var a = SIMD.float32x4(i, i + 0.1, i + 0.2, i + 0.3);
-    if (uceFault_simdBox_f4(i) || uceFault_simdBox_f4(i)) {
+    if (uceFault_simdBox_f4(i) || uceFault_simdBox_f4(i))
         assertEqX4(a, [i, i + 0.1, i + 0.2, i + 0.3].map(Math.fround));
-    }
+    assertRecoveredOnBailout(a, true);
     return 0;
 }
 
 for (var i = 0; i < 100; i++) {
     simdBox_i4(i);
     simdBox_f4(i);
 }
--- a/js/src/jit-test/tests/basic/metadata-hook.js
+++ b/js/src/jit-test/tests/basic/metadata-hook.js
@@ -1,12 +1,8 @@
-
-x = [1,2,3];
-setObjectMetadata(x, {y:0});
-assertEq(getObjectMetadata(x).y, 0);
 
 setObjectMetadataCallback(true);
 
 function Foo() {
   this.x = 0;
   this.y = 1;
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1146410.js
@@ -0,0 +1,9 @@
+// |jit-test| error: TypeError
+function foo() {
+  var ws = new WeakSet();
+  ws.add({});
+  for (var i = 0; i < 10; i++)
+    ws.add(WeakSet + "");
+}
+foo();
+delete Math
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -12,1096 +12,1157 @@ var uceFault = function (i) {
     return false;
 }
 
 var uceFault_bitnot_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitnot_number'));
 function rbitnot_number(i) {
     var x = ~i;
     if (uceFault_bitnot_number(i) || uceFault_bitnot_number(i))
         assertEq(x, -100  /* = ~99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_bitnot_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitnot_object'));
 function rbitnot_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = ~o; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_bitnot_object(i) || uceFault_bitnot_object(i))
         assertEq(x, -100  /* = ~99 */);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_bitand_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitand_number'));
 function rbitand_number(i) {
     var x = 1 & i;
     if (uceFault_bitand_number(i) || uceFault_bitand_number(i))
         assertEq(x, 1  /* = 1 & 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_bitand_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitand_object'));
 function rbitand_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o & i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_bitand_object(i) || uceFault_bitand_object(i))
         assertEq(x, 99);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_bitor_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitor_number'));
 function rbitor_number(i) {
     var x = i | -100; /* -100 == ~99 */
     if (uceFault_bitor_number(i) || uceFault_bitor_number(i))
         assertEq(x, -1) /* ~99 | 99 = -1 */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_bitor_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitor_object'));
 function rbitor_object(i) {
     var t = i;
     var o = { valueOf: function() { return t; } };
     var x = o | -100;
     t = 1000;
     if (uceFault_bitor_object(i) || uceFault_bitor_object(i))
         assertEq(x, -1);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_bitxor_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitxor_number'));
 function rbitxor_number(i) {
     var x = 1 ^ i;
     if (uceFault_bitxor_number(i) || uceFault_bitxor_number(i))
         assertEq(x, 98  /* = 1 XOR 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_bitxor_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_bitxor_object'));
 function rbitxor_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = 1 ^ o; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_bitxor_object(i) || uceFault_bitxor_object(i))
         assertEq(x, 98  /* = 1 XOR 99 */);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_lsh_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_lsh_number'));
 function rlsh_number(i) {
     var x = i << 1;
     if (uceFault_lsh_number(i) || uceFault_lsh_number(i))
         assertEq(x, 198); /* 99 << 1 == 198 */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_lsh_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_lsh_object'));
 function rlsh_object(i) {
     var t = i;
     var o = { valueOf: function() { return t; } };
     var x = o << 1;
     t = 1000;
     if (uceFault_lsh_object(i) || uceFault_lsh_object(i))
         assertEq(x, 198);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_rsh_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_rsh_number'));
 function rrsh_number(i) {
     var x = i >> 1;
     if (uceFault_rsh_number(i) || uceFault_rsh_number(i))
         assertEq(x, 49  /* = 99 >> 1 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_rsh_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_rsh_object'));
 function rrsh_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o >> 1; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_rsh_object(i) || uceFault_rsh_object(i))
         assertEq(x, 49  /* = 99 >> 1 */);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_ursh_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_ursh_number'));
 function rursh_number(i) {
     var x = i >>> 1;
     if (uceFault_ursh_number(i) || uceFault_ursh_number(i))
         assertEq(x, 49  /* = 99 >>> 1 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_ursh_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_ursh_object'));
 function rursh_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o >>> 1; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_ursh_object(i) || uceFault_ursh_object(i))
         assertEq(x, 49  /* = 99 >>> 1 */);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_add_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_number'));
 function radd_number(i) {
     var x = 1 + i;
     if (uceFault_add_number(i) || uceFault_add_number(i))
         assertEq(x, 100  /* = 1 + 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_add_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_float'));
 function radd_float(i) {
     var t = Math.fround(1/3);
     var fi = Math.fround(i);
     var x = Math.fround(Math.fround(Math.fround(Math.fround(t + fi) + t) + fi) + t);
     if (uceFault_add_float(i) || uceFault_add_float(i))
         assertEq(x, 199); /* != 199.00000002980232 (when computed with double additions) */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_add_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_object'));
 function radd_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o + i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_add_object(i) || uceFault_add_object(i))
         assertEq(x, 198);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_sub_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sub_number'));
 function rsub_number(i) {
     var x = 1 - i;
     if (uceFault_sub_number(i) || uceFault_sub_number(i))
         assertEq(x, -98  /* = 1 - 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_sub_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_sub_float'));
 function rsub_float(i) {
     var t = Math.fround(1/3);
     var fi = Math.fround(i);
     var x = Math.fround(Math.fround(Math.fround(Math.fround(t - fi) - t) - fi) - t);
     if (uceFault_sub_float(i) || uceFault_sub_float(i))
         assertEq(x, -198.3333282470703); /* != -198.33333334326744 (when computed with double subtractions) */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_sub_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_sub_object'));
 function rsub_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o - i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_sub_object(i) || uceFault_sub_object(i))
         assertEq(x, 0);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_mul_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_number'));
 function rmul_number(i) {
     var x = 2 * i;
     if (uceFault_mul_number(i) || uceFault_mul_number(i))
         assertEq(x, 198  /* = 1 * 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_mul_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_float'));
 function rmul_float(i) {
     var t = Math.fround(1/3);
     var fi = Math.fround(i);
     var x = Math.fround(Math.fround(Math.fround(Math.fround(t * fi) * t) * fi) * t);
     if (uceFault_mul_float(i) || uceFault_mul_float(i))
         assertEq(x, 363); /* != 363.0000324547301 (when computed with double multiplications) */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_mul_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_mul_object'));
 function rmul_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o * i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_mul_object(i) || uceFault_mul_object(i))
         assertEq(x, 9801);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_div_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_number'));
 function rdiv_number(i) {
     var x = 1 / i;
     if (uceFault_div_number(i) || uceFault_div_number(i))
         assertEq(x, 0.010101010101010102  /* = 1 / 99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_div_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_float'));
 function rdiv_float(i) {
     var t = Math.fround(1/3);
     var fi = Math.fround(i);
     var x = Math.fround(Math.fround(Math.fround(Math.fround(t / fi) / t) / fi) / t);
     if (uceFault_div_float(i) || uceFault_div_float(i))
         assertEq(x, 0.0003060912131331861); /* != 0.0003060912060598955 (when computed with double divisions) */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_div_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_div_object'));
 function rdiv_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o / i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_div_object(i) || uceFault_div_object(i))
         assertEq(x, 1);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_mod_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_mod_number'));
 function rmod_number(i) {
     var x = i % 98;
     if (uceFault_mod_number(i) || uceFault_mod_number(i))
         assertEq(x, 1); /* 99 % 98 = 1 */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_mod_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_mod_object'));
 function rmod_object(i) {
     var t = i;
     var o = { valueOf: function() { return t; } };
     var x = o % 98; /* computed with t == i, not 1000 */
     t = 1000;
     if(uceFault_mod_object(i) || uceFault_mod_object(i))
         assertEq(x, 1); /* 99 % 98 = 1 */
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_not_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_not_number'));
 function rnot_number(i) {
     var x = !i;
     if (uceFault_not_number(i) || uceFault_not_number(i))
         assertEq(x, false /* = !99 */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_not_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_not_object'));
 function rnot_object(i) {
     var o = objectEmulatingUndefined();
     var x = !o;
     if(uceFault_not_object(i) || uceFault_not_object(i))
         assertEq(x, true /* = !undefined = !document.all = !objectEmulatingUndefined() */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_concat_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_concat_string'));
 function rconcat_string(i) {
     var x = "s" + i.toString();
     if (uceFault_concat_string(i) || uceFault_concat_string(i))
         assertEq(x, "s99");
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_concat_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_concat_number'));
 function rconcat_number(i) {
     var x = "s" + i;
     if (uceFault_concat_number(i) || uceFault_concat_number(i))
         assertEq(x, "s99");
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_string_length = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_length'));
 function rstring_length(i) {
     var x = i.toString().length;
     if (uceFault_string_length(i) || uceFault_string_length(i))
         assertEq(x, 2);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_arguments_length_1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_arguments_length_1'));
 function rarguments_length_1(i) {
     var x = arguments.length;
     if (uceFault_arguments_length_1(i) || uceFault_arguments_length_1(i))
         assertEq(x, 1);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_arguments_length_3 = eval(uneval(uceFault).replace('uceFault', 'uceFault_arguments_length_3'));
 function rarguments_length_3(i) {
     var x = arguments.length;
     if (uceFault_arguments_length_3(i) || uceFault_arguments_length_3(i))
         assertEq(x, 3);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 function ret_argumentsLength() { return arguments.length; }
 
 var uceFault_inline_arguments_length_1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_inline_arguments_length_1'));
 function rinline_arguments_length_1(i) {
     var x = ret_argumentsLength.apply(this, arguments);
     if (uceFault_inline_arguments_length_1(i) || uceFault_inline_arguments_length_1(i))
         assertEq(x, 1);
+    // We cannot garantee that the function would be inlined
+    // assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_inline_arguments_length_3 = eval(uneval(uceFault).replace('uceFault', 'uceFault_inline_arguments_length_3'));
 function rinline_arguments_length_3(i) {
     var x = ret_argumentsLength.apply(this, arguments);
     if (uceFault_inline_arguments_length_3(i) || uceFault_inline_arguments_length_3(i))
         assertEq(x, 3);
+    // We cannot garantee that the function would be inlined
+    // assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_floor_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_floor_number'));
 function rfloor_number(i) {
     var x = Math.floor(i + 0.1111);
     if (uceFault_floor_number(i) || uceFault_floor_number(i))
         assertEq(x, i);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_floor_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_floor_object'));
 function rfloor_object(i) {
     var t = i + 0.1111;
     var o = { valueOf: function () { return t; } };
     var x = Math.floor(o);
     t = 1000.1111;
     if (uceFault_floor_object(i) || uceFault_floor_object(i))
         assertEq(x, i);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_ceil_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_ceil_number'));
-function rceil_number(i){
+function rceil_number(i) {
     var x = Math.ceil(-i - 0.12010799100);
-    if (uceFault_ceil_number(i) ||uceFault_ceil_number(i))
+    if (uceFault_ceil_number(i) || uceFault_ceil_number(i))
         assertEq(x, - i);
+    // NYI: uses MMathFunction and not MCeil.
+    // assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_round_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_round'));
 function rround_number(i) {
     var x = Math.round(i + 1.4);
     if (uceFault_round_number(i) || uceFault_round_number(i))
         assertEq(x, 100); /* = i + 1*/
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_round_double = eval(uneval(uceFault).replace('uceFault', 'uceFault_round_double'));
 function rround_double(i) {
     var x = Math.round(i + (-1 >>> 0));
     if (uceFault_round_double(i) || uceFault_round_double(i))
         assertEq(x, 99 + (-1 >>> 0)); /* = i + 2 ^ 32 - 1 */
-     return i;
+    assertRecoveredOnBailout(x, true);
+    return i;
 }
 
 var uceFault_Char_Code_At = eval(uneval(uceFault).replace('uceFault', 'uceFault_Char_Code_At'));
 function rcharCodeAt(i) {
     var s = "aaaaa";
     var x = s.charCodeAt(i % 4);
     if (uceFault_Char_Code_At(i) || uceFault_Char_Code_At(i))
         assertEq(x, 97 );
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_from_char_code = eval(uneval(uceFault).replace('uceFault', 'uceFault_from_char_code'));
 function rfrom_char_code(i) {
     var x = String.fromCharCode(i);
     if (uceFault_from_char_code(i) || uceFault_from_char_code(i))
         assertEq(x, "c");
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_from_char_code_non_ascii = eval(uneval(uceFault).replace('uceFault', 'uceFault_from_char_code_non_ascii'));
 function rfrom_char_code_non_ascii(i) {
     var x = String.fromCharCode(i * 100);
     if (uceFault_from_char_code_non_ascii(i) || uceFault_from_char_code_non_ascii(i))
         assertEq(x, "\u26AC");
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_pow_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_pow_number'));
 function rpow_number(i) {
     var x = Math.pow(i, 3.14159);
     if (uceFault_pow_number(i) || uceFault_pow_number(i))
         assertEq(x, Math.pow(99, 3.14159));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_pow_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_pow_object'));
 function rpow_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.pow(o, 3.14159); /* computed with t == i, not 1.5 */
     t = 1.5;
     if (uceFault_pow_object(i) || uceFault_pow_object(i))
         assertEq(x, Math.pow(99, 3.14159));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_powhalf_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_powhalf_number'));
 function rpowhalf_number(i) {
     var x = Math.pow(i, 0.5);
     if (uceFault_powhalf_number(i) || uceFault_powhalf_number(i))
         assertEq(x, Math.pow(99, 0.5));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_powhalf_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_powhalf_object'));
 function rpowhalf_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.pow(o, 0.5); /* computed with t == i, not 1.5 */
     t = 1.5;
     if (uceFault_powhalf_object(i) || uceFault_powhalf_object(i))
         assertEq(x, Math.pow(99, 0.5));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_min_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_number'));
 function rmin_number(i) {
     var x = Math.min(i, i-1, i-2.1);
     if (uceFault_min_number(i) || uceFault_min_number(i))
         assertEq(x, i-2.1);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_min_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_float'));
 function rmin_float(i) {
-    var x = Math.fround(Math.min(Math.fround(20), Math.fround(13.37)));
+    var x = Math.fround(Math.min(Math.fround(i), Math.fround(13.37)));
     if (uceFault_min_number(i) || uceFault_min_number(i))
         assertEq(x, Math.fround(13.37));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object'));
 function rmin_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.min(o, o-1, o-2.1)
     t = 1000;
     if (uceFault_min_object(i) || uceFault_min_object(i))
         assertEq(x, i-2.1);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_max_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_number'));
 function rmax_number(i) {
     var x = Math.max(i, i-1, i-2.1);
     if (uceFault_max_number(i) || uceFault_max_number(i))
         assertEq(x, i);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_max_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_float'));
 function rmax_float(i) {
-    var x = Math.fround(Math.max(Math.fround(2), Math.fround(13.37)));
+    var x = Math.fround(Math.max(Math.fround(-i), Math.fround(13.37)));
     if (uceFault_max_number(i) || uceFault_max_number(i))
         assertEq(x, Math.fround(13.37));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object'));
 function rmax_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.max(o, o-1, o-2.1)
     t = 1000;
     if (uceFault_max_object(i) || uceFault_max_object(i))
         assertEq(x, i);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_abs = eval(uneval(uceFault).replace('uceFault', 'uceFault_abs'));
 function rabs_number(i) {
     var x = Math.abs(i-42);
     if (uceFault_abs(i) || uceFault_abs(i))
         assertEq(x, 57);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_abs_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_abs_object'));
 function rabs_object(i) {
     var t = -i;
     var o = { valueOf: function() { return t; } };
     var x = Math.abs(o); /* computed with t == i, not 1000 */
     t = 1000;
     if(uceFault_abs_object(i) || uceFault_abs_object(i))
         assertEq(x, 99);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_sqrt_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sqrt_number'));
 function rsqrt_number(i) {
     var x = Math.sqrt(i);
     if (uceFault_sqrt_number(i) || uceFault_sqrt_number(i))
         assertEq(x, Math.sqrt(99));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_sqrt_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_sqrt_float'));
 function rsqrt_float(i) {
     var x = Math.fround(Math.sqrt(Math.fround(i)));
     if (uceFault_sqrt_float(i) || uceFault_sqrt_float(i))
         assertEq(x, Math.fround(Math.sqrt(Math.fround(99)))); /* != 9.9498743710662 (when computed with double sqrt) */
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_sqrt_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_sqrt_object'));
 function rsqrt_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.sqrt(o); /* computed with t == i, not 1.5 */
     t = 1.5;
     if (uceFault_sqrt_object(i) || uceFault_sqrt_object(i))
         assertEq(x, Math.sqrt(99));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_atan2_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_atan2_number'));
 function ratan2_number(i) {
     var x = Math.atan2(i, i+1);
     if (uceFault_atan2_number(i) || uceFault_atan2_number(i))
         assertEq(x, Math.atan2(99, 100));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_atan2_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_atan2_object'));
 function ratan2_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.atan2(o, o+1);
     t = 1000;
     if (uceFault_atan2_object(i) || uceFault_atan2_object(i))
         assertEq(x, Math.atan2(i, i+1));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_str_split = eval(uneval(uceFault).replace('uceFault', 'uceFault_str_split'))
 function rstr_split(i) {
     var x = "str01234567899876543210rts".split("" + i);
-    if (uceFault_str_split(i) || uceFault_str_split(i)) {
+    if (uceFault_str_split(i) || uceFault_str_split(i))
         assertEq(x[0], "str012345678");
-    }
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_regexp_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_exec'))
 function rregexp_exec(i) {
     var re = new RegExp("(str)\\d+" + i + "\\d+rts");
     var res = re.exec("str01234567899876543210rts");
-    if (uceFault_regexp_exec(i) || uceFault_regexp_exec(i)) {
+    if (uceFault_regexp_exec(i) || uceFault_regexp_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 var uceFault_regexp_y_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_exec'))
 function rregexp_y_exec(i) {
     var re = new RegExp("(str)\\d+" + (i % 10), "y");
     var res = re.exec("str00123456789");
-    if (uceFault_regexp_y_exec(i) || uceFault_regexp_y_exec(i)) {
+    if (uceFault_regexp_y_exec(i) || uceFault_regexp_y_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_y_literal_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_literal_exec'))
 function rregexp_y_literal_exec(i) {
     var re = /(str)\d*0/y;
     var res = re.exec("str00123456789");
-    if (uceFault_regexp_y_literal_exec(i) || uceFault_regexp_y_literal_exec(i)) {
+    if (uceFault_regexp_y_literal_exec(i) || uceFault_regexp_y_literal_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_g_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_exec'))
 function rregexp_g_exec(i) {
     var re = new RegExp("(str)\\d+" + (i % 10), "g");
     var res = re.exec("str00123456789str00123456789");
-    if (uceFault_regexp_g_exec(i) || uceFault_regexp_g_exec(i)) {
+    if (uceFault_regexp_g_exec(i) || uceFault_regexp_g_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_g_literal_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_literal_exec'))
 function rregexp_g_literal_exec(i) {
     var re = /(str)\d*0/g;
     var res = re.exec("str00123456789str00123456789");
-    if (uceFault_regexp_g_literal_exec(i) || uceFault_regexp_g_literal_exec(i)) {
+    if (uceFault_regexp_g_literal_exec(i) || uceFault_regexp_g_literal_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_i_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_exec'))
 function rregexp_i_exec(i) {
     var re = new RegExp("(str)\\d+" + (i % 10), "i");
     var res = re.exec("STR00123456789");
-    if (uceFault_regexp_i_exec(i) || uceFault_regexp_i_exec(i)) {
+    if (uceFault_regexp_i_exec(i) || uceFault_regexp_i_exec(i))
         assertEq(res[1], "STR");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_i_literal_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_literal_exec'))
 function rregexp_i_literal_exec(i) {
     var re = /(str)\d*0/i;
     var res = re.exec("STR00123456789");
-    if (uceFault_regexp_i_literal_exec(i) || uceFault_regexp_i_literal_exec(i)) {
+    if (uceFault_regexp_i_literal_exec(i) || uceFault_regexp_i_literal_exec(i))
         assertEq(res[1], "STR");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 
 var uceFault_regexp_m_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_exec'))
 function rregexp_m_exec(i) {
     var re = new RegExp("^(str)\\d+" + (i % 10), "m");
     var res = re.exec("abc\nstr00123456789");
-    if (uceFault_regexp_m_exec(i) || uceFault_regexp_m_exec(i)) {
+    if (uceFault_regexp_m_exec(i) || uceFault_regexp_m_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_m_literal_exec = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_literal_exec'))
 function rregexp_m_literal_exec(i) {
     var re = /^(str)\d*0/m;
     var res = re.exec("abc\nstr00123456789");
-    if (uceFault_regexp_m_literal_exec(i) || uceFault_regexp_m_literal_exec(i)) {
+    if (uceFault_regexp_m_literal_exec(i) || uceFault_regexp_m_literal_exec(i))
         assertEq(res[1], "str");
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_test'))
 function rregexp_test(i) {
     var re = new RegExp("str\\d+" + i + "\\d+rts");
     var res = re.test("str01234567899876543210rts");
-    if (uceFault_regexp_test(i) || uceFault_regexp_test(i)) {
+    if (uceFault_regexp_test(i) || uceFault_regexp_test(i))
         assertEq(res, true);
-    }
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_y_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_test'))
 function rregexp_y_test(i) {
     var re = new RegExp("str\\d+" + (i % 10), "y");
     var res = re.test("str00123456789");
-    if (uceFault_regexp_y_test(i) || uceFault_regexp_y_test(i)) {
+    if (uceFault_regexp_y_test(i) || uceFault_regexp_y_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_y_literal_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_literal_test'))
 function rregexp_y_literal_test(i) {
     var re = /str\d*0/y;
     var res = re.test("str00123456789");
-    if (uceFault_regexp_y_literal_test(i) || uceFault_regexp_y_literal_test(i)) {
+    if (uceFault_regexp_y_literal_test(i) || uceFault_regexp_y_literal_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_g_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_test'))
 function rregexp_g_test(i) {
     var re = new RegExp("str\\d+" + (i % 10), "g");
     var res = re.test("str00123456789str00123456789");
-    if (uceFault_regexp_g_test(i) || uceFault_regexp_g_test(i)) {
+    if (uceFault_regexp_g_test(i) || uceFault_regexp_g_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_g_literal_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_literal_test'))
 function rregexp_g_literal_test(i) {
     var re = /str\d*0/g;
     var res = re.test("str00123456789str00123456789");
-    if (uceFault_regexp_g_literal_test(i) || uceFault_regexp_g_literal_test(i)) {
+    if (uceFault_regexp_g_literal_test(i) || uceFault_regexp_g_literal_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, false);
     return i;
 }
 
 var uceFault_regexp_i_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_test'))
 function rregexp_i_test(i) {
     var re = new RegExp("str\\d+" + (i % 10), "i");
     var res = re.test("STR00123456789");
-    if (uceFault_regexp_i_test(i) || uceFault_regexp_i_test(i)) {
+    if (uceFault_regexp_i_test(i) || uceFault_regexp_i_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_i_literal_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_literal_test'))
 function rregexp_i_literal_test(i) {
     var re = /str\d*0/i;
     var res = re.test("STR00123456789");
-    if (uceFault_regexp_i_literal_test(i) || uceFault_regexp_i_literal_test(i)) {
+    if (uceFault_regexp_i_literal_test(i) || uceFault_regexp_i_literal_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_m_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_test'))
 function rregexp_m_test(i) {
     var re = new RegExp("^str\\d+" + (i % 10), "m");
     var res = re.test("abc\nstr00123456789");
-    if (uceFault_regexp_m_test(i) || uceFault_regexp_m_test(i)) {
+    if (uceFault_regexp_m_test(i) || uceFault_regexp_m_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_m_literal_test = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_literal_test'))
 function rregexp_m_literal_test(i) {
     var re = /^str\d*0/m;
     var res = re.test("abc\nstr00123456789");
-    if (uceFault_regexp_m_literal_test(i) || uceFault_regexp_m_literal_test(i)) {
+    if (uceFault_regexp_m_literal_test(i) || uceFault_regexp_m_literal_test(i))
         assertEq(res, true);
-    }
-
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_regexp_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_replace'))
 function rregexp_replace(i) {
     var re = new RegExp("str\\d+" + (i % 10));
     var res = "str00123456789".replace(re, "abc");
-    if (uceFault_regexp_replace(i) || uceFault_regexp_replace(i)) {
+    if (uceFault_regexp_replace(i) || uceFault_regexp_replace(i))
         assertEq(res, "abc");
-    }
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_y_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_replace'))
 function rregexp_y_replace(i) {
     var re = new RegExp("str\\d+" + (i % 10), "y");
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
     // replace will not zero the lastIndex field, if sticky flag is set
     assertEq(re.lastIndex == 0, false);
 
     if (uceFault_regexp_y_replace(i) || uceFault_regexp_y_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_y_literal_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_literal_replace'))
 function rregexp_y_literal_replace(i) {
     var re = /str\d+9/y;
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
     assertEq(re.lastIndex == 0, false);
 
     if (uceFault_regexp_y_literal_replace(i) || uceFault_regexp_y_literal_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_g_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_replace'))
 function rregexp_g_replace(i) {
     var re = new RegExp("str\\d+" + (i % 10), "g");
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
     // replace will always zero the lastIndex field, even if it was not zero before.
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_g_replace(i) || uceFault_regexp_g_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_g_literal_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_g_literal_replace'))
 function rregexp_g_literal_replace(i) {
     var re = /str\d+9/g;
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
     // replace will zero the lastIndex field.
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_g_literal_replace(i) || uceFault_regexp_g_literal_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_i_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_replace'))
 function rregexp_i_replace(i) {
     var re = new RegExp("str\\d+" + (i % 10), "i");
     re.test("STR00123456789");
     assertEq(re.lastIndex == 0, true);
 
     var res = "STR00123456789".replace(re, "abc");
 
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_i_replace(i) || uceFault_regexp_i_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_i_literal_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_i_literal_replace'))
 function rregexp_i_literal_replace(i) {
     var re = /str\d+9/i;
     re.test("STR00123456789");
     assertEq(re.lastIndex == 0, true);
 
     var res = "str00123456789".replace(re, "abc");
 
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_i_literal_replace(i) || uceFault_regexp_i_literal_replace(i))
         assertEq(res, "abc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_m_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_replace'))
 function rregexp_m_replace(i) {
     var re = new RegExp("^str\\d+" + (i % 10), "m");
     re.test("abc\nstr00123456789");
     assertEq(re.lastIndex == 0, true);
 
     var res = "abc\nstr00123456789".replace(re, "abc");
 
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_m_replace(i) || uceFault_regexp_m_replace(i))
         assertEq(res, "abc\nabc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_m_literal_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_m_literal_replace'))
 function rregexp_m_literal_replace(i) {
     var re = /^str\d+9/m;
     re.test("abc\nstr00123456789");
     assertEq(re.lastIndex == 0, true);
 
     var res = "abc\nstr00123456789".replace(re, "abc");
 
     assertEq(re.lastIndex == 0, true);
 
     if (uceFault_regexp_m_literal_replace(i) || uceFault_regexp_m_literal_replace(i))
         assertEq(res, "abc\nabc");
+    assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_string_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace'))
 function rstring_replace(i) {
     var re = /str\d+9/;
 
     assertEq(re.lastIndex == 0, true);
     var res = "str00123456789".replace(re, "abc");
-    if (uceFault_string_replace(i) || uceFault_string_replace(i)) {
+    if (uceFault_string_replace(i) || uceFault_string_replace(i))
         assertEq(res, "abc");
-    }
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
-
     return i;
 }
 
 var uceFault_string_replace_y = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_y'))
 function rstring_replace_y(i) {
     var re = /str\d+9/y;
 
     assertEq(re.lastIndex == 0, true);
     var res = "str00123456789".replace(re, "abc");
-    if (uceFault_string_replace_y(i) || uceFault_string_replace_y(i)) {
+    if (uceFault_string_replace_y(i) || uceFault_string_replace_y(i))
         assertEq(res, "abc");
-    }
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
-
     return i;
 }
 
 var uceFault_string_replace_g = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_g'))
 function rstring_replace_g(i) {
     var re = /str\d+9/g;
 
     assertEq(re.lastIndex == 0, true);
     var res = "str00123456789str00123456789".replace(re, "abc");
-    if (uceFault_string_replace_g(i) || uceFault_string_replace_g(i)) {
+    if (uceFault_string_replace_g(i) || uceFault_string_replace_g(i))
         assertEq(res, "abcabc");
-    }
+    assertRecoveredOnBailout(res, false);
     assertEq(re.lastIndex == 0, true);
-
     return i;
 }
 
 var uceFault_typeof = eval(uneval(uceFault).replace('uceFault', 'uceFault_typeof'))
 function rtypeof(i) {
     var inputs = [ {}, [], 1, true, undefined, function(){}, null ];
     var types = [ "object", "object", "number", "boolean", "undefined", "function", "object"];
     if (typeof Symbol === "function") {
       inputs.push(Symbol());
       types.push("symbol");
     }
     var x = typeof (inputs[i % inputs.length]);
     var y = types[i % types.length];
 
-    if (uceFault_typeof(i) || uceFault_typeof(i)) {
+    if (uceFault_typeof(i) || uceFault_typeof(i))
         assertEq(x, y);
-    }
-
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_todouble_value = eval(uneval(uceFault).replace('uceFault', 'uceFault_todouble_value'))
 function rtodouble_value(i) {
     var a = 1;
     if (i == 1000) a = "1";
 
     var x = a < 8.1;
 
-    if (uceFault_todouble_value(i) || uceFault_todouble_value(i)) {
+    if (uceFault_todouble_value(i) || uceFault_todouble_value(i))
         assertEq(x, true);
-    }
-
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_todouble_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_todouble_number'));
 function rtodouble_number(i) {
     var x = Math.fround(Math.fround(i) + Math.fround(i)) + 1;
     if (uceFault_todouble_number(i) || uceFault_todouble_number(i))
         assertEq(2 * i + 1, x);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_tofloat32_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_tofloat32_number'));
 function rtofloat32_number(i) {
     var x = Math.fround(i + 0.1111111111);
     if (uceFault_tofloat32_number(i) || uceFault_tofloat32_number(i))
         assertEq(x, Math.fround(99.1111111111));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_tofloat32_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_tofloat32_object'));
 function rtofloat32_object(i) {
     var t = i + 0.1111111111;
     var o = { valueOf: function () { return t; } };
     var x = Math.fround(o);
     t = 1000.1111111111;
     if (uceFault_tofloat32_object(i) || uceFault_tofloat32_object(i))
         assertEq(x, Math.fround(99.1111111111));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_trunc_to_int32_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_number'));
 function rtrunc_to_int32_number(i) {
     var x = (i + 0.12) | 0;
     if (uceFault_trunc_to_int32_number(i) || uceFault_trunc_to_int32_number(i))
         assertEq(x, (i + 0.12) | 0);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_trunc_to_int32_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_object'));
 function rtrunc_to_int32_object(i) {
     var t1 = i + 0.12;
     var o1 = { valueOf: function() { return t1; } };
     var x = o1 | 0;
     t1 = 777.12;
     if (uceFault_trunc_to_int32_object(i) || uceFault_trunc_to_int32_object(i))
         assertEq(x, (i + 0.12) | 0);
+    assertRecoveredOnBailout(x, false);
 }
 
 var uceFault_trunc_to_int32_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_string'));
 function rtrunc_to_int32_string(i) {
     var x = (i + "0") | 0;
     if (uceFault_trunc_to_int32_string(i) || uceFault_trunc_to_int32_string(i))
         assertEq(x, (i + "0") | 0);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_hypot_number_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_2args'));
 function rhypot_number_2args(i) {
     var x = Math.hypot(i, i + 1);
     if (uceFault_hypot_number_2args(i) || uceFault_hypot_number_2args(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_hypot_number_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_3args'));
 function rhypot_number_3args(i) {
     var x = Math.hypot(i, i + 1, i + 2);
     if (uceFault_hypot_number_3args(i) || uceFault_hypot_number_3args(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_hypot_number_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_4args'));
 function rhypot_number_4args(i) {
     var x = Math.hypot(i, i + 1, i + 2, i + 3);
     if (uceFault_hypot_number_4args(i) || uceFault_hypot_number_4args(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_hypot_object_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_2args'));
 function rhypot_object_2args(i) {
     var t0 = i;
     var t1 = i + 1;
     var o0 = { valueOf: function () { return t0; } };
     var o1 = { valueOf: function () { return t1; } };
     var x = Math.hypot(o0, o1);
     t0 = 1000;
     t1 = 2000;
     if (uceFault_hypot_object_2args(i) || uceFault_hypot_object_2args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_hypot_object_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_3args'));
 function rhypot_object_3args(i) {
     var t0 = i;
     var t1 = i + 1;
     var t2 = i + 2;
@@ -1109,16 +1170,17 @@ function rhypot_object_3args(i) {
     var o1 = { valueOf: function () { return t1; } };
     var o2 = { valueOf: function () { return t2; } };
     var x = Math.hypot(o0, o1, o2);
     t0 = 1000;
     t1 = 2000;
     t2 = 3000;
     if (uceFault_hypot_object_3args(i) || uceFault_hypot_object_3args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_hypot_object_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_4args'));
 function rhypot_object_4args(i) {
     var t0 = i;
     var t1 = i + 1;
     var t2 = i + 2;
@@ -1129,54 +1191,59 @@ function rhypot_object_4args(i) {
     var o3 = { valueOf: function () { return t3; } };
     var x = Math.hypot(o0, o1, o2, o3);
     t0 = 1000;
     t1 = 2000;
     t2 = 3000;
     t3 = 4000;
     if (uceFault_hypot_object_4args(i) || uceFault_hypot_object_4args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number'));
 function rsin_number(i) {
     var x = Math.sin(i);
     if (uceFault_sin_number(i) || uceFault_sin_number(i))
         assertEq(x, Math.sin(i));
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_sin_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_object'));
 function rsin_object(i) {
     var t = i;
     var o = { valueOf: function() { return t; } };
     var x = Math.sin(o);
     t = 777;
     if (uceFault_sin_object(i) || uceFault_sin_object(i))
         assertEq(x, Math.sin(i));
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 var uceFault_log_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_log_number'));
 function rlog_number(i) {
     var x = Math.log(i);
     if (uceFault_log_number(i) || uceFault_log_number(i))
         assertEq(x, Math.log(99) /* log(99) */);
+    assertRecoveredOnBailout(x, true);
     return i;
 }
 
 var uceFault_log_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_log_object'));
 function rlog_object(i) {
     var t = i;
     var o = { valueOf: function() { return t; } };
     var x = Math.log(o); /* Evaluated with t == i, not t == 1000 */
     t = 1000;
     if (uceFault_log_object(i) || uceFault_log_object(i))
         assertEq(x, Math.log(99) /* log(99) */);
+    assertRecoveredOnBailout(x, false);
     return i;
 }
 
 for (i = 0; i < 100; i++) {
     rbitnot_number(i);
     rbitnot_object(i);
     rbitand_number(i);
     rbitand_object(i);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -61,16 +61,17 @@ BaselineScript::BaselineScript(uint32_t 
     traceLoggerEngineEnabled_(false),
 # endif
     traceLoggerEnterToggleOffset_(traceLoggerEnterToggleOffset),
     traceLoggerExitToggleOffset_(traceLoggerExitToggleOffset),
     traceLoggerScriptEvent_(),
 #endif
     postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0),
+    inlinedBytecodeLength_(0),
     maxInliningDepth_(UINT8_MAX)
 { }
 
 static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
 static bool
 CheckFrame(InterpreterFrame *fp)
 {
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -207,16 +207,21 @@ struct BaselineScript
     // List mapping indexes of bytecode type sets to the offset of the opcode
     // they correspond to, for use by TypeScript::BytecodeTypes.
     uint32_t bytecodeTypeMapOffset_;
 
     // For generator scripts, we store the native code address for each yield
     // instruction.
     uint32_t yieldEntriesOffset_;
 
+    // The total bytecode length of all scripts we inlined when we Ion-compiled
+    // this script. 0 if Ion did not compile this script or if we didn't inline
+    // anything.
+    uint16_t inlinedBytecodeLength_;
+
     // The max inlining depth where we can still inline all functions we inlined
     // when we Ion-compiled this script. This starts as UINT8_MAX, since we have
     // no data yet, and won't affect inlining heuristics in that case. The value
     // is updated when we Ion-compile this script. See makeInliningDecision for
     // more info.
     uint8_t maxInliningDepth_;
 
   public:
@@ -437,16 +442,25 @@ struct BaselineScript
     }
     void setMaxInliningDepth(uint32_t depth) {
         MOZ_ASSERT(depth <= UINT8_MAX);
         maxInliningDepth_ = depth;
     }
     void resetMaxInliningDepth() {
         maxInliningDepth_ = UINT8_MAX;
     }
+
+    uint16_t inlinedBytecodeLength() const {
+        return inlinedBytecodeLength_;
+    }
+    void setInlinedBytecodeLength(uint32_t len) {
+        if (len > UINT16_MAX)
+            len = UINT16_MAX;
+        inlinedBytecodeLength_ = len;
+    }
 };
 static_assert(sizeof(BaselineScript) % sizeof(uintptr_t) == 0,
               "The data attached to the script must be aligned for fast JIT access.");
 
 inline bool
 IsBaselineEnabled(JSContext *cx)
 {
 #ifdef JS_CODEGEN_NONE
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3135,39 +3135,38 @@ CodeGenerator::emitPushArguments(LApplyA
     // Holds the function nargs. Initially undefined.
     Register argcreg = ToRegister(apply->getArgc());
     Register copyreg = ToRegister(apply->getTempObject());
 
     // Initialize the loop counter AND Compute the stack usage (if == 0)
     masm.movePtr(argcreg, extraStackSpace);
 
     // Align the JitFrameLayout on the JitStackAlignment.
-    const uint32_t alignment = JitStackAlignment / sizeof(Value);
-    if (alignment > 1) {
+    if (JitStackValueAlignment > 1) {
         MOZ_ASSERT(frameSize() % JitStackAlignment == 0,
             "Stack padding assumes that the frameSize is correct");
-        MOZ_ASSERT(alignment == 2);
+        MOZ_ASSERT(JitStackValueAlignment == 2);
         Label noPaddingNeeded;
         // if the number of arguments is odd, then we do not need any padding.
         masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
         masm.addPtr(Imm32(1), extraStackSpace);
         masm.bind(&noPaddingNeeded);
     }
 
     // Reserve space for copying the arguments.
     NativeObject::elementsSizeMustNotOverflow();
     masm.lshiftPtr(Imm32(ValueShift), extraStackSpace);
     masm.subPtr(extraStackSpace, StackPointer);
 
 #ifdef DEBUG
     // Put a magic value in the space reserved for padding. Note, this code
     // cannot be merged with the previous test, as not all architectures can
     // write below their stack pointers.
-    if (alignment > 1) {
-        MOZ_ASSERT(alignment == 2);
+    if (JitStackValueAlignment > 1) {
+        MOZ_ASSERT(JitStackValueAlignment == 2);
         Label noPaddingNeeded;
         // if the number of arguments is odd, then we do not need any padding.
         masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
         BaseValueIndex dstPtr(StackPointer, argcreg);
         masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr);
         masm.bind(&noPaddingNeeded);
     }
 #endif
@@ -3378,16 +3377,22 @@ CodeGenerator::visitBail(LBail *lir)
 
 void
 CodeGenerator::visitUnreachable(LUnreachable *lir)
 {
     masm.assumeUnreachable("end-of-block assumed unreachable");
 }
 
 void
+CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot *lir)
+{
+    encode(lir->snapshot());
+}
+
+void
 CodeGenerator::visitGetDynamicName(LGetDynamicName *lir)
 {
     Register scopeChain = ToRegister(lir->getScopeChain());
     Register name = ToRegister(lir->getName());
     Register temp1 = ToRegister(lir->temp1());
     Register temp2 = ToRegister(lir->temp2());
     Register temp3 = ToRegister(lir->temp3());
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -135,16 +135,17 @@ class CodeGenerator : public CodeGenerat
     void visitCallGeneric(LCallGeneric *call);
     void visitCallKnown(LCallKnown *call);
     void emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize);
     void emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace);
     void emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSize);
     void visitApplyArgsGeneric(LApplyArgsGeneric *apply);
     void visitBail(LBail *lir);
     void visitUnreachable(LUnreachable *unreachable);
+    void visitEncodeSnapshot(LEncodeSnapshot *lir);
     void visitGetDynamicName(LGetDynamicName *lir);
     void visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir);
     void visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir);
     void visitCallDirectEvalS(LCallDirectEvalS *lir);
     void visitCallDirectEvalV(LCallDirectEvalV *lir);
     void visitDoubleToInt32(LDoubleToInt32 *lir);
     void visitFloat32ToInt32(LFloat32ToInt32 *lir);
     void visitNewArrayCallVM(LNewArray *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -881,16 +881,22 @@ IonBuilder::build()
     insertRecompileCheck();
 
     if (!traverseBytecode())
         return false;
 
     // Discard unreferenced & pre-allocated resume points.
     replaceMaybeFallbackFunctionGetter(nullptr);
 
+    if (script_->hasBaselineScript() &&
+        inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength())
+    {
+        script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
+    }
+
     if (!maybeAddOsrTypeBarriers())
         return false;
 
     if (!processIterators())
         return false;
 
     if (!abortedPreliminaryGroups().empty()) {
         MOZ_ASSERT(!info().isAnalysis());
@@ -3648,40 +3654,41 @@ IonBuilder::improveTypesAtTest(MDefiniti
     // default behavior must return.  The default behavior assumes that a true
     // test means the incoming ins is not null or undefined and that a false
     // tests means it's one of null, undefined, false, 0, "", and objects
     // emulating undefined
     switch (ins->op()) {
       case MDefinition::Op_Not:
         return improveTypesAtTest(ins->toNot()->getOperand(0), !trueBranch, test);
       case MDefinition::Op_IsObject: {
-        TemporaryTypeSet *oldType = ins->getOperand(0)->resultTypeSet();
+        MDefinition *subject = ins->getOperand(0);
+        TemporaryTypeSet *oldType = subject->resultTypeSet();
 
         // Create temporary typeset equal to the type if there is no resultTypeSet.
         TemporaryTypeSet tmp;
         if (!oldType) {
-            if (ins->type() == MIRType_Value)
+            if (subject->type() == MIRType_Value)
                 return true;
             oldType = &tmp;
-            tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type())), alloc_->lifoAlloc());
+            tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
         }
 
         if (oldType->unknown())
             return true;
 
         TemporaryTypeSet *type = nullptr;
         if (trueBranch)
             type = oldType->cloneObjectsOnly(alloc_->lifoAlloc());
         else
             type = oldType->cloneWithoutObjects(alloc_->lifoAlloc());
 
         if (!type)
             return false;
 
-        return replaceTypeSet(ins->getOperand(0), type, test);
+        return replaceTypeSet(subject, type, test);
       }
       case MDefinition::Op_Phi: {
         bool branchIsAnd = true;
         if (!detectAndOrStructure(ins->toPhi(), &branchIsAnd)) {
             // Just fall through to the default behavior.
             break;
         }
 
@@ -4871,16 +4878,24 @@ IonBuilder::makeInliningDecision(JSObjec
         info().analysisMode() != Analysis_DefiniteProperties)
     {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot);
         JitSpew(JitSpew_Inlining, "Cannot inline %s:%" PRIuSIZE ": callee is insufficiently hot.",
                 targetScript->filename(), targetScript->lineno());
         return InliningDecision_WarmUpCountTooLow;
     }
 
+    // Don't inline if the callee is known to inline a lot of code, to avoid
+    // huge MIR graphs.
+    uint32_t inlinedBytecodeLength = targetScript->baselineScript()->inlinedBytecodeLength();
+    if (inlinedBytecodeLength > optimizationInfo().inlineMaxCalleeInlinedBytecodeLength()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineBigCalleeInlinedBytecodeLength);
+        return DontInline(targetScript, "Vetoed: callee inlinedBytecodeLength is too big");
+    }
+
     IonBuilder *outerBuilder = outermostBuilder();
 
     // Cap the total bytecode length we inline under a single script, to avoid
     // excessive inlining in pathological cases.
     size_t totalBytecodeLength = outerBuilder->inlinedBytecodeLength_ + targetScript->length();
     if (totalBytecodeLength > optimizationInfo().inlineMaxTotalBytecodeLength()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededTotalBytecodeLength);
         return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -859,16 +859,17 @@ class IonBuilder
                                   const Class *clasp3 = nullptr,
                                   const Class *clasp4 = nullptr);
     InliningStatus inlineIsConstructing(CallInfo &callInfo);
     InliningStatus inlineSubstringKernel(CallInfo &callInfo);
 
     // Testing functions.
     InliningStatus inlineBailout(CallInfo &callInfo);
     InliningStatus inlineAssertFloat32(CallInfo &callInfo);
+    InliningStatus inlineAssertRecoveredOnBailout(CallInfo &callInfo);
     InliningStatus inlineTrue(CallInfo &callInfo);
 
     // Bind function.
     InliningStatus inlineBoundFunction(CallInfo &callInfo, JSFunction *target);
 
     // Main inlining functions
     InliningStatus inlineNativeCall(CallInfo &callInfo, JSFunction *target);
     InliningStatus inlineNativeGetter(CallInfo &callInfo, JSFunction *target);
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -34,16 +34,17 @@ OptimizationInfo::initNormalOptimization
     rangeAnalysis_ = true;
     loopUnrolling_ = true;
     autoTruncate_ = true;
     sink_ = true;
     registerAllocator_ = RegisterAllocator_Backtracking;
 
     inlineMaxBytecodePerCallSiteMainThread_ = 500;
     inlineMaxBytecodePerCallSiteOffThread_ = 1000;
+    inlineMaxCalleeInlinedBytecodeLength_ = 3000;
     inlineMaxTotalBytecodeLength_ = 80000;
     inliningMaxCallerBytecodeLength_ = 1500;
     maxInlineDepth_ = 3;
     scalarReplacement_ = true;
     smallFunctionMaxInlineDepth_ = 10;
     compilerWarmUpThreshold_ = CompilerWarmupThreshold;
     inliningWarmUpThresholdFactor_ = 0.125;
     inliningRecompileThresholdFactor_ = 4;
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -89,16 +89,20 @@ class OptimizationInfo
     IonRegisterAllocator registerAllocator_;
 
     // The maximum total bytecode size of an inline call site. We use a lower
     // value if off-thread compilation is not available, to avoid stalling the
     // main thread.
     uint32_t inlineMaxBytecodePerCallSiteOffThread_;
     uint32_t inlineMaxBytecodePerCallSiteMainThread_;
 
+    // The maximum value we allow for baselineScript->inlinedBytecodeLength_
+    // when inlining.
+    uint16_t inlineMaxCalleeInlinedBytecodeLength_;
+
     // The maximum bytecode length we'll inline in a single compilation.
     uint32_t inlineMaxTotalBytecodeLength_;
 
     // The maximum bytecode length the caller may have,
     // before we stop inlining large functions in that caller.
     uint32_t inliningMaxCallerBytecodeLength_;
 
     // The maximum inlining depth.
@@ -216,16 +220,20 @@ class OptimizationInfo
     }
 
     uint32_t inlineMaxBytecodePerCallSite(bool offThread) const {
         return (offThread || !js_JitOptions.limitScriptSize)
                ? inlineMaxBytecodePerCallSiteOffThread_
                : inlineMaxBytecodePerCallSiteMainThread_;
     }
 
+    uint16_t inlineMaxCalleeInlinedBytecodeLength() const {
+        return inlineMaxCalleeInlinedBytecodeLength_;
+    }
+
     uint32_t inlineMaxTotalBytecodeLength() const {
         return inlineMaxTotalBytecodeLength_;
     }
 
     uint32_t inliningMaxCallerBytecodeLength() const {
         return inliningMaxCallerBytecodeLength_;
     }
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -1532,19 +1532,18 @@ class LStackArgV : public LInstructionHe
 };
 
 // Common code for LIR descended from MCall.
 template <size_t Defs, size_t Operands, size_t Temps>
 class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, Temps>
 {
   public:
     uint32_t argslot() const {
-        static const uint32_t alignment = JitStackAlignment / sizeof(Value);
-        if (alignment > 1)
-            return AlignBytes(mir()->numStackArgs(), alignment);
+        if (JitStackValueAlignment > 1)
+            return AlignBytes(mir()->numStackArgs(), JitStackValueAlignment);
         return mir()->numStackArgs();
     }
     MCall *mir() const {
         return this->mir_->toCall();
     }
 
     bool hasSingleTarget() const {
         return getSingleTarget() != nullptr;
@@ -1681,16 +1680,22 @@ class LBail : public LInstructionHelper<
 };
 
 class LUnreachable : public LControlInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Unreachable)
 };
 
+class LEncodeSnapshot : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(EncodeSnapshot)
+};
+
 template <size_t defs, size_t ops>
 class LDOMPropertyInstructionHelper : public LCallInstructionHelper<defs, 1 + ops, 3>
 {
   protected:
     LDOMPropertyInstructionHelper(const LDefinition &JSContextReg, const LAllocation &ObjectReg,
                                   const LDefinition &PrivReg, const LDefinition &ValueReg)
     {
         this->setOperand(0, ObjectReg);
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1755,21 +1755,22 @@ class LIRGraph
     }
     uint32_t localSlotCount() const {
         return localSlotCount_;
     }
     // Return the localSlotCount() value rounded up so that it satisfies the
     // platform stack alignment requirement, and so that it's a multiple of
     // the number of slots per Value.
     uint32_t paddedLocalSlotCount() const {
-        // Round to ABIStackAlignment, but also round to at least sizeof(Value)
-        // in case that's greater, because StackOffsetOfPassedArg rounds
-        // argument slots to 8-byte boundaries.
-        size_t Alignment = Max(size_t(JitStackAlignment), sizeof(Value));
-        return AlignBytes(localSlotCount(), Alignment);
+        // Round to JitStackAlignment, and implicitly to sizeof(Value) as
+        // JitStackAlignment is a multiple of sizeof(Value). These alignments
+        // are needed for spilling SIMD registers properly, and for
+        // StackOffsetOfPassedArg which rounds argument slots to 8-byte
+        // boundaries.
+        return AlignBytes(localSlotCount(), JitStackAlignment);
     }
     size_t paddedLocalSlotsSize() const {
         return paddedLocalSlotCount();
     }
     void setArgumentSlotCount(uint32_t argumentSlotCount) {
         argumentSlotCount_ = argumentSlotCount;
     }
     uint32_t argumentSlotCount() const {
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -70,16 +70,17 @@
     _(DefVar)                       \
     _(DefFun)                       \
     _(CallKnown)                    \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(ApplyArgsGeneric)             \
     _(Bail)                         \
     _(Unreachable)                  \
+    _(EncodeSnapshot)               \
     _(GetDynamicName)               \
     _(FilterArgumentsOrEvalS)       \
     _(FilterArgumentsOrEvalV)       \
     _(CallDirectEvalS)              \
     _(CallDirectEvalV)              \
     _(StackArgT)                    \
     _(StackArgV)                    \
     _(CreateThis)                   \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -388,19 +388,18 @@ LIRGenerator::visitLoadArrowThis(MLoadAr
 void
 LIRGenerator::lowerCallArguments(MCall *call)
 {
     uint32_t argc = call->numStackArgs();
 
     // Align the arguments of a call such that the callee would keep the same
     // alignment as the caller.
     uint32_t baseSlot = 0;
-    static const uint32_t alignment = JitStackAlignment / sizeof(Value);
-    if (alignment > 1)
-        baseSlot = AlignBytes(argc, alignment);
+    if (JitStackValueAlignment > 1)
+        baseSlot = AlignBytes(argc, JitStackValueAlignment);
     else
         baseSlot = argc;
 
     // Save the maximum number of argument, such that we can have one unique<