merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 06 Oct 2016 11:59:54 +0200
changeset 421653 777fb63db8de8d78b64b8141d1a998397275dd03
parent 421652 c3b0354515b6bb59a5324adc21e7e891d6217cae (current diff)
parent 421511 8a4a43911b776d61be3817ec11073b34fdcb672d (diff)
child 421654 38442ad9a422c1028f6e40b5144f3d7994e7acf3
child 421659 1504ad682c7496d5c644b8bea4b8a3ccfb56c4ef
child 421679 d0eb4cc9fd0d5f19949e1068863d482343873245
child 421713 f75a58088e69fc5aa2c3873fd79200429bbaf8e7
child 421723 5ef613528deaca7fb841e0f2b58f57b99c04cf7b
child 421754 9fbca1a06e07bef794ad6ce408359d243323ad48
child 421931 fef343991976c7ae6d9bb1960f03aa8b3c634288
child 421976 5a33fc4fd47d66e0e7399753dcb7d7fac3ff120b
child 422076 309e08638ccf3c84dba6a9962159095ba02cf066
push id31552
push userbmo:bob.silverberg@gmail.com
push dateThu, 06 Oct 2016 13:39:04 +0000
reviewersmerge
milestone52.0a1
merge mozilla-inbound to mozilla-central a=merge
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/test/test_mozDashOffset.html
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/thebes/DeviceManagerDx.cpp
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/base/tests/chrome/passpointerevents_dynamically_window.html
layout/base/tests/chrome/passpointerevents_window.html
layout/base/tests/chrome/test_passpointerevents.html
layout/base/tests/chrome/test_passpointerevents_dynamic.html
layout/base/tests/test_remote_passpointerevents.html
modules/libpref/init/all.js
--- a/accessible/aom/moz.build
+++ b/accessible/aom/moz.build
@@ -30,9 +30,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
     LOCAL_INCLUDES += [
         '/accessible/mac',
     ]
 else:
     LOCAL_INCLUDES += [
         '/accessible/other',
     ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -253,17 +253,17 @@ EventTree::Process(const RefPtr<DocAcces
 
   mDependentEvents.Clear();
 }
 
 EventTree*
 EventTree::FindOrInsert(Accessible* aContainer)
 {
   if (!mFirst) {
-    mFirst.reset(new EventTree(aContainer, true));
+    mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
     return mFirst.get();
   }
 
   EventTree* prevNode = nullptr;
   EventTree* node = mFirst.get();
   do {
     MOZ_ASSERT(!node->mContainer->IsApplication(),
                "No event for application accessible is expected here");
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -420,18 +420,18 @@ NotificationController::WillRefresh(mozi
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
       if (tabChild) {
         static_cast<TabChild*>(tabChild.get())->
           SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
 #if defined(XP_WIN)
-        IAccessibleHolder holder(CreateHolderFromAccessible(childDoc));
-        ipcDoc->SendCOMProxy(holder);
+        MOZ_ASSERT(parentIPCDoc);
+        ipcDoc->SendMsaaID(AccessibleWrap::GetChildIDFor(childDoc));
 #endif
       }
     }
   }
 
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
--- a/accessible/html/moz.build
+++ b/accessible/html/moz.build
@@ -37,12 +37,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
     LOCAL_INCLUDES += [
         '/accessible/mac',
     ]
 else:
     LOCAL_INCLUDES += [
         '/accessible/other',
     ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/ipc/DocAccessibleChildBase.cpp
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -49,89 +49,49 @@ DocAccessibleChildBase::InterfacesFor(Ac
   return interfaces;
 }
 
 /* static */ void
 DocAccessibleChildBase::SerializeTree(Accessible* aRoot,
                                       nsTArray<AccessibleData>& aTree)
 {
   uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
+#if defined(XP_WIN)
+  int32_t msaaId = AccessibleWrap::GetChildIDFor(aRoot);
+#endif
   uint32_t role = aRoot->Role();
   uint32_t childCount = aRoot->ChildCount();
   uint32_t interfaces = InterfacesFor(aRoot);
 
-#if defined(XP_WIN)
-  IAccessibleHolder holder(CreateHolderFromAccessible(aRoot));
-#endif
-
   // OuterDocAccessibles are special because we don't want to serialize the
   // child doc here, we'll call PDocAccessibleConstructor in
   // NotificationController.
   MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
   if (aRoot->IsOuterDoc()) {
     childCount = 0;
   }
 
 #if defined(XP_WIN)
-  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces,
-                                     holder));
+  aTree.AppendElement(AccessibleData(id, msaaId, role, childCount, interfaces));
 #else
   aTree.AppendElement(AccessibleData(id, role, childCount, interfaces));
 #endif
 
   for (uint32_t i = 0; i < childCount; i++) {
     SerializeTree(aRoot->GetChildAt(i), aTree);
   }
 }
 
-#if defined(XP_WIN)
-/* static */ void
-DocAccessibleChildBase::SetMsaaIds(Accessible* aRoot,
-                                   uint32_t& aMsaaIdIndex,
-                                   const nsTArray<MsaaMapping>& aNewMsaaIds)
-{
-  const MsaaMapping& mapping = aNewMsaaIds[aMsaaIdIndex];
-#if defined(DEBUG)
-  uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
-  MOZ_ASSERT(mapping.ID() == id);
-#endif // defined(DEBUG)
-  static_cast<AccessibleWrap*>(aRoot)->SetID(mapping.MsaaID());
-  ++aMsaaIdIndex;
-  if (aRoot->IsOuterDoc()) {
-    // This needs to match the tree traversal in SerializeTree
-    return;
-  }
-  for (uint32_t i = 0, n = aRoot->ChildCount(); i < n; ++i) {
-    SetMsaaIds(aRoot->GetChildAt(i), aMsaaIdIndex, aNewMsaaIds);
-  }
-}
-#endif // defined(XP_WIN)
-
 void
 DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
   uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
   uint32_t idxInParent = aShowEvent->InsertionIndex();
   nsTArray<AccessibleData> shownTree;
   ShowEventData data(parentID, idxInParent, shownTree);
   SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
-#if defined(XP_WIN)
-  nsTArray<MsaaMapping> newMsaaIds;
-  SendShowEventInfo(data, &newMsaaIds);
-  // newMsaaIds could be empty if something went wrong in SendShowEvent()
-  if (!newMsaaIds.IsEmpty()) {
-    uint32_t index = 0;
-    SetMsaaIds(aShowEvent->GetAccessible(), index, newMsaaIds);
-  }
-  // NB: On Windows, SendShowEvent attaches the subtree and generates new IDs,
-  //     but does *NOT* fire the native event. We need to do that after
-  //     we've called SetMsaaIds.
-  SendEvent(reinterpret_cast<uint64_t>(aShowEvent->GetAccessible()->UniqueID()),
-            nsIAccessibleEvent::EVENT_SHOW);
-#else
   SendShowEvent(data, aShowEvent->IsFromUserInput());
-#endif // defined(XP_WIN)
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/DocAccessibleChildBase.h
+++ b/accessible/ipc/DocAccessibleChildBase.h
@@ -55,20 +55,16 @@ public:
 
     mDoc->SetIPCDoc(nullptr);
     mDoc = nullptr;
   }
 
 protected:
   static uint32_t InterfacesFor(Accessible* aAcc);
   static void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree);
-#if defined(XP_WIN)
-  static void SetMsaaIds(Accessible* aRoot, uint32_t& aMsaaIdIndex,
-                         const nsTArray<MsaaMapping>& aNewMsaaIds);
-#endif
 
   DocAccessible*  mDoc;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_DocAccessibleChildBase_h
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -11,23 +11,18 @@
 #include "xpcAccEvents.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 
 namespace mozilla {
 namespace a11y {
 
 bool
-#if defined(XP_WIN)
-DocAccessibleParent::RecvShowEventInfo(const ShowEventData& aData,
-                                       nsTArray<MsaaMapping>* aNewMsaaIds)
-#else
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
                                    const bool& aFromUser)
-#endif // defined(XP_WIN)
 {
   if (mShutdown)
     return true;
 
   MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
@@ -44,23 +39,17 @@ DocAccessibleParent::RecvShowEvent(const
   }
 
   uint32_t newChildIdx = aData.Idx();
   if (newChildIdx > parent->ChildrenCount()) {
     NS_ERROR("invalid index to add child at");
     return true;
   }
 
-#if defined(XP_WIN)
-  aNewMsaaIds->SetCapacity(aData.NewTree().Length());
-  uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx,
-                                 aNewMsaaIds);
-#else
   uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
-#endif
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 
   // XXX This shouldn't happen, but if we failed to add children then the below
   // is pointless and can crash.
   if (!consumed) {
     return true;
   }
 
@@ -68,46 +57,38 @@ DocAccessibleParent::RecvShowEvent(const
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
   MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
-  // NB: On Windows we dispatch the native event via a subsequent call to
-  // RecvEvent().
-#if !defined(XP_WIN)
   ProxyAccessible* target = parent->ChildAt(newChildIdx);
   ProxyShowHideEvent(target, parent, true, aFromUser);
 
   if (!nsCoreUtils::AccEventObserversExist()) {
     return true;
   }
 
   uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
   xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   nsIDOMNode* node = nullptr;
   RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node,
                                               aFromUser);
   nsCoreUtils::DispatchAccEvent(Move(event));
-#endif
 
   return true;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
-                                uint32_t aIdx, uint32_t aIdxInParent
-#if defined(XP_WIN)
-                                , nsTArray<MsaaMapping>* aNewMsaaIds
-#endif
-                                )
+                                uint32_t aIdx, uint32_t aIdxInParent)
 {
   if (aNewTree.Length() <= aIdx) {
     NS_ERROR("bad index in serialized tree!");
     return 0;
   }
 
   const AccessibleData& newChild = aNewTree[aIdx];
   if (newChild.Role() > roles::LAST_ROLE) {
@@ -117,53 +98,32 @@ DocAccessibleParent::AddSubtree(ProxyAcc
 
   if (mAccessibles.Contains(newChild.ID())) {
     NS_ERROR("ID already in use");
     return 0;
   }
 
   auto role = static_cast<a11y::role>(newChild.Role());
 
-#if defined(XP_WIN)
-  const IAccessibleHolder& proxyStream = newChild.COMProxy();
-  RefPtr<IAccessible> comPtr(proxyStream.Get());
-  if (!comPtr) {
-    NS_ERROR("Could not obtain remote IAccessible interface");
-    return 0;
-  }
-
-  ProxyAccessible* newProxy =
-    new ProxyAccessible(newChild.ID(), aParent, this, role,
-                        newChild.Interfaces(), comPtr);
-#else
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role,
                         newChild.Interfaces());
-#endif
 
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
   ProxyCreated(newProxy, newChild.Interfaces());
 
 #if defined(XP_WIN)
-  Accessible* idForAcc = WrapperFor(newProxy);
-  MOZ_ASSERT(idForAcc);
-  uint32_t newMsaaId = AccessibleWrap::GetChildIDFor(idForAcc);
-  MOZ_ASSERT(newMsaaId);
-  aNewMsaaIds->AppendElement(MsaaMapping(newChild.ID(), newMsaaId));
-#endif // defined(XP_WIN)
+  WrapperFor(newProxy)->SetID(newChild.MsaaID());
+#endif
 
   uint32_t accessibles = 1;
   uint32_t kids = newChild.ChildrenCount();
   for (uint32_t i = 0; i < kids; i++) {
-    uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i
-#if defined(XP_WIN)
-                                   , aNewMsaaIds
-#endif
-                                  );
+    uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
     if (!consumed)
       return 0;
 
     accessibles += consumed;
   }
 
   MOZ_ASSERT(newProxy->ChildrenCount() == kids);
 
@@ -495,36 +455,45 @@ DocAccessibleParent::GetXPCAccessible(Pr
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   MOZ_ASSERT(doc);
 
   return doc->GetXPCAccessible(aProxy);
 }
 
 #if defined(XP_WIN)
 /**
+ * @param aMsaaID The MSAA ID that was generated by content that the chrome
+ *        process should assign to this DocAccessibleParent.
  * @param aCOMProxy COM Proxy to the document in the content process.
  * @param aParentCOMProxy COM Proxy to the OuterDocAccessible that is
  *        the parent of the document. The content process will use this
  *        proxy when traversing up across the content/chrome boundary.
  */
 bool
-DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
-                                  IAccessibleHolder* aParentCOMProxy,
-                                  uint32_t* aMsaaID)
+DocAccessibleParent::RecvCOMProxy(const int32_t& aMsaaID,
+                                  const IAccessibleHolder& aCOMProxy,
+                                  IAccessibleHolder* aParentCOMProxy)
 {
+  WrapperFor(this)->SetID(aMsaaID);
+
   RefPtr<IAccessible> ptr(aCOMProxy.Get());
   SetCOMInterface(ptr);
 
   Accessible* outerDoc = OuterDocOfRemoteBrowser();
   IAccessible* rawNative = nullptr;
   if (outerDoc) {
     outerDoc->GetNativeInterface((void**) &rawNative);
   }
 
   aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative));
-  Accessible* wrapper = WrapperFor(this);
-  *aMsaaID = AccessibleWrap::GetChildIDFor(wrapper);
+  return true;
+}
+
+bool
+DocAccessibleParent::RecvMsaaID(const int32_t& aMsaaID)
+{
+  WrapperFor(this)->SetID(aMsaaID);
   return true;
 }
 #endif // defined(XP_WIN)
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -45,23 +45,18 @@ public:
 
   /*
    * Called when a message from a document in a child process notifies the main
    * process it is firing an event.
    */
   virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType)
     override;
 
-#if defined(XP_WIN)
-  virtual bool RecvShowEventInfo(const ShowEventData& aData,
-                                 nsTArray<MsaaMapping>* aNewMsaaIds) override;
-#else
   virtual bool RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
     override;
-#endif // defined(XP_WIN)
   virtual bool RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser)
     override;
   virtual bool RecvStateChangeEvent(const uint64_t& aID,
                                     const uint64_t& aState,
                                     const bool& aEnabled) override final;
 
   virtual bool RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
     override final;
@@ -73,16 +68,17 @@ public:
 
   virtual bool RecvSelectionEvent(const uint64_t& aID,
                                   const uint64_t& aWidgetID,
                                   const uint32_t& aType) override;
 
   virtual bool RecvRoleChangedEvent(const uint32_t& aRole) override final;
 
   virtual bool RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
+
   void Unbind()
   {
     mParent = nullptr;
     if (DocAccessibleParent* parent = ParentDoc()) {
       parent->mChildDocs.RemoveElement(this);
     }
 
     mParentDoc = nullptr;
@@ -143,19 +139,21 @@ public:
   const ProxyAccessible* GetAccessible(uintptr_t aID) const
     { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
 
   size_t ChildDocCount() const { return mChildDocs.Length(); }
   const DocAccessibleParent* ChildDocAt(size_t aIdx) const
     { return mChildDocs[aIdx]; }
 
 #if defined(XP_WIN)
-  virtual bool RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
-                            IAccessibleHolder* aParentCOMProxy,
-                            uint32_t* aMsaaID) override;
+  virtual bool RecvCOMProxy(const int32_t& aMsaaID,
+                            const IAccessibleHolder& aCOMProxy,
+                            IAccessibleHolder* aParentCOMProxy) override;
+
+  virtual bool RecvMsaaID(const int32_t& aMsaaID) override;
 #endif
 
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
     explicit ProxyEntry(const void*) : mProxy(nullptr) {}
@@ -175,21 +173,17 @@ private:
 
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
-                      uint32_t aIdxInParent
-#if defined(XP_WIN)
-                      , nsTArray<MsaaMapping>* aNewMsaaIds
-#endif // defined(XP_WIN)
-                      );
+                      uint32_t aIdxInParent);
   MOZ_MUST_USE bool CheckDocTree() const;
   xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
 
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -28,18 +28,17 @@ DocAccessibleChild::DocAccessibleChild(D
 DocAccessibleChild::~DocAccessibleChild()
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
 DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy)
 {
+  int32_t msaaID = AccessibleWrap::GetChildIDFor(mDoc);
   IAccessibleHolder parentProxy;
-  uint32_t msaaID = AccessibleWrap::kNoID;
-  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy, &msaaID);
+  PDocAccessibleChild::SendCOMProxy(msaaID, aProxy, &parentProxy);
   mParentProxy.reset(parentProxy.Release());
-  mDoc->SetID(msaaID);
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -10,35 +10,29 @@ include protocol PBrowser;
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/COMPtrTypes.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
+  int32_t MsaaID;
   uint32_t Role;
   uint32_t ChildrenCount;
   uint32_t Interfaces;
-  IAccessibleHolder COMProxy;
 };
 
 struct ShowEventData
 {
   uint64_t ID;
   uint32_t Idx;
   AccessibleData[] NewTree;
 };
 
-struct MsaaMapping
-{
-  uint64_t ID;
-  uint32_t MsaaID;
-};
-
 struct Attribute
 {
   nsCString Name;
   nsString Value;
 };
 
 sync protocol PDocAccessible
 {
@@ -47,34 +41,36 @@ sync protocol PDocAccessible
 parent:
   async Shutdown();
 
   /*
    * Notify the parent process the document in the child process is firing an
    * event.
    */
   async Event(uint64_t aID, uint32_t type);
-  sync ShowEventInfo(ShowEventData data) returns (MsaaMapping[] aNewMsaaIds);
+  async ShowEvent(ShowEventData data, bool aFromUser);
   async HideEvent(uint64_t aRootID, bool aFromUser);
   async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
   async CaretMoveEvent(uint64_t aID, int32_t aOffset);
   async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
                         bool aIsInsert, bool aFromUser);
   async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);
   async RoleChangedEvent(uint32_t aRole);
 
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   // For now we'll add the command to send the proxy here. This might move to
   // PDocAccessible constructor in PBrowser.
-  sync COMProxy(IAccessibleHolder aDocCOMProxy)
-    returns(IAccessibleHolder aParentCOMProxy, uint32_t aMsaaID);
+  sync COMProxy(int32_t aMsaaID, IAccessibleHolder aDocCOMProxy)
+    returns(IAccessibleHolder aParentCOMProxy);
+
+  async MsaaID(int32_t aMsaaID);
 
 child:
   async __delete__();
 };
 
 }
 }
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -14,107 +14,106 @@
 #include "mozilla/Unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
 #include <comutil.h>
 
+static const VARIANT kChildIdSelf = {VT_I4};
+
 namespace mozilla {
 namespace a11y {
 
 bool
 ProxyAccessible::GetCOMInterface(void** aOutAccessible) const
 {
   if (!aOutAccessible) {
     return false;
   }
+
+  if (!mCOMProxy) {
+    // See if we can lazily obtain a COM proxy
+    AccessibleWrap* wrap = WrapperFor(this);
+    bool isDefunct = false;
+    ProxyAccessible* thisPtr = const_cast<ProxyAccessible*>(this);
+    thisPtr->mCOMProxy = wrap->GetIAccessibleFor(kChildIdSelf, &isDefunct);
+  }
+
   RefPtr<IAccessible> addRefed = mCOMProxy;
   addRefed.forget(aOutAccessible);
   return !!mCOMProxy;
 }
 
 void
 ProxyAccessible::Name(nsString& aName) const
 {
   aName.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
   }
 
-  VARIANT id;
-  id.vt = VT_I4;
-  id.lVal = CHILDID_SELF;
   BSTR result;
-  HRESULT hr = acc->get_accName(id, &result);
+  HRESULT hr = acc->get_accName(kChildIdSelf, &result);
   _bstr_t resultWrap(result, false);
   if (FAILED(hr)) {
     return;
   }
   aName = (wchar_t*)resultWrap;
 }
 
 void
 ProxyAccessible::Value(nsString& aValue) const
 {
   aValue.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
   }
 
-  VARIANT id;
-  id.vt = VT_I4;
-  id.lVal = CHILDID_SELF;
   BSTR result;
-  HRESULT hr = acc->get_accValue(id, &result);
+  HRESULT hr = acc->get_accValue(kChildIdSelf, &result);
   _bstr_t resultWrap(result, false);
   if (FAILED(hr)) {
     return;
   }
   aValue = (wchar_t*)resultWrap;
 }
 
 void
 ProxyAccessible::Description(nsString& aDesc) const
 {
   aDesc.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
   }
 
-  VARIANT id;
-  id.vt = VT_I4;
-  id.lVal = CHILDID_SELF;
   BSTR result;
-  HRESULT hr = acc->get_accDescription(id, &result);
+  HRESULT hr = acc->get_accDescription(kChildIdSelf, &result);
   _bstr_t resultWrap(result, false);
   if (FAILED(hr)) {
     return;
   }
   aDesc = (wchar_t*)resultWrap;
 }
 
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return state;
   }
 
-  VARIANT id;
-  id.vt = VT_I4;
-  id.lVal = CHILDID_SELF;
   VARIANT varState;
-  HRESULT hr = acc->get_accState(id, &varState);
+  HRESULT hr = acc->get_accState(kChildIdSelf, &varState);
   if (FAILED(hr)) {
     return state;
   }
   return uint64_t(varState.lVal);
 }
 
 nsIntRect
 ProxyAccessible::Bounds()
@@ -125,20 +124,17 @@ ProxyAccessible::Bounds()
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return rect;
   }
 
   long left;
   long top;
   long width;
   long height;
-  VARIANT id;
-  id.vt = VT_I4;
-  id.lVal = CHILDID_SELF;
-  HRESULT hr = acc->accLocation(&left, &top, &width, &height, id);
+  HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
   if (FAILED(hr)) {
     return rect;
   }
   rect.x = left;
   rect.y = top;
   rect.width = width;
   rect.height = height;
   return rect;
--- a/accessible/ipc/win/ProxyAccessible.h
+++ b/accessible/ipc/win/ProxyAccessible.h
@@ -20,21 +20,18 @@
 
 namespace mozilla {
 namespace a11y {
 
 class ProxyAccessible : public ProxyAccessibleBase<ProxyAccessible>
 {
 public:
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
-                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces,
-                  const RefPtr<IAccessible>& aIAccessible)
+                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces)
     : ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces)
-    , mCOMProxy(aIAccessible)
-
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
 
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
   }
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -72,17 +72,18 @@
 
     function removeARIAOwns()
     {
       this.eventSeq = [
         new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
         new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
         new invokerChecker(EVENT_HIDE, getNode("t1_button")),
         new invokerChecker(EVENT_SHOW, getNode("t1_button")),
-        new invokerChecker(EVENT_REORDER, getNode("t1_container"))
+        new invokerChecker(EVENT_REORDER, getNode("t1_container")),
+        new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox"))
       ];
 
       this.invoke = function removeARIAOwns_invoke()
       {
         getNode("t1_container").removeAttribute("aria-owns");
       }
 
       this.finalCheck = function removeARIAOwns_finalCheck()
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -37,16 +37,17 @@
 #include "nsIServiceManager.h"
 #include "nsNameSpaceManager.h"
 #include "nsTextFormatter.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsEventMap.h"
 #include "nsArrayUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsIXULRuntime.h"
 
 #include "oleacc.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 const uint32_t USE_ROLE_STRING = 0;
 
@@ -55,37 +56,36 @@ const uint32_t USE_ROLE_STRING = 0;
  */
 
 //#define DEBUG_LEAKS
 
 #ifdef DEBUG_LEAKS
 static gAccessibles = 0;
 #endif
 
-#ifdef _WIN64
-IDSet AccessibleWrap::sIDGen;
-#endif
+MsaaIdGenerator AccessibleWrap::sIDGen;
+
+static const VARIANT kVarChildIdSelf = {VT_I4};
 
 static const int32_t kIEnumVariantDisconnected = -1;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccessibleWrap
 ////////////////////////////////////////////////////////////////////////////////
 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
   Accessible(aContent, aDoc)
   , mID(kNoID)
 {
 }
 
 AccessibleWrap::~AccessibleWrap()
 {
-#ifdef _WIN64
-  if (mID != kNoID && XRE_IsParentProcess())
-    sIDGen.ReleaseID(mID);
-#endif
+  if (mID != kNoID) {
+    sIDGen.ReleaseID(this);
+  }
 }
 
 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
 
 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
 
 void
 AccessibleWrap::Shutdown()
@@ -244,56 +244,116 @@ AccessibleWrap::get_accChild(
     return E_INVALIDARG;
 
   *ppdispChild = nullptr;
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   // IAccessible::accChild is used to return this accessible or child accessible
   // at the given index or to get an accessible by child ID in the case of
-  // document accessible (it's handled by overriden GetXPAccessibleFor method
-  // on the document accessible). The getting an accessible by child ID is used
-  // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event.
-  Accessible* child = GetXPAccessibleFor(varChild);
-  if (!child)
+  // document accessible.
+  // The getting an accessible by child ID is used by AccessibleObjectFromEvent()
+  // called by AT when AT handles our MSAA event.
+  bool isDefunct = false;
+  RefPtr<IAccessible> child = GetIAccessibleFor(varChild, &isDefunct);
+  if (!child) {
     return E_INVALIDARG;
+  }
 
-  if (child->IsDefunct())
+  if (isDefunct) {
     return CO_E_OBJNOTCONNECTED;
+  }
 
-  *ppdispChild = NativeAccessible(child);
+  child.forget(ppdispChild);
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
+/**
+ * This function is a helper for implementing IAccessible methods that accept
+ * a Child ID as a parameter. If the child ID is CHILDID_SELF, the function
+ * returns S_OK but a null *aOutInterface. Otherwise, *aOutInterface points
+ * to the resolved IAccessible.
+ *
+ * The CHILDID_SELF case is special because in that case we actually execute
+ * the implementation of the IAccessible method, whereas in the non-self case,
+ * we delegate the method call to that object for execution.
+ *
+ * A sample invocation of this would look like:
+ *
+ *  RefPtr<IAccessible> accessible;
+ *  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+ *  if (FAILED(hr)) {
+ *    return hr;
+ *  }
+ *
+ *  if (accessible) {
+ *    return accessible->get_accFoo(kVarChildIdSelf, pszName);
+ *  }
+ *
+ *  // Implementation for CHILDID_SELF case goes here
+ */
+HRESULT
+AccessibleWrap::ResolveChild(const VARIANT& aVarChild,
+                             IAccessible** aOutInterface)
+{
+  MOZ_ASSERT(aOutInterface);
+  *aOutInterface = nullptr;
+
+  if (aVarChild.vt != VT_I4) {
+    return E_INVALIDARG;
+  }
+
+  if (IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
+
+  if (aVarChild.lVal == CHILDID_SELF) {
+    return S_OK;
+  }
+
+  bool isDefunct = false;
+  RefPtr<IAccessible> accessible = GetIAccessibleFor(aVarChild, &isDefunct);
+  if (!accessible) {
+    return E_INVALIDARG;
+  }
+
+  if (isDefunct) {
+    return CO_E_OBJNOTCONNECTED;
+  }
+
+  accessible.forget(aOutInterface);
+  return S_OK;
+}
+
 STDMETHODIMP
 AccessibleWrap::get_accName(
       /* [optional][in] */ VARIANT varChild,
       /* [retval][out] */ BSTR __RPC_FAR *pszName)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (!pszName)
+  if (!pszName || varChild.vt != VT_I4)
     return E_INVALIDARG;
 
   *pszName = nullptr;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accName(kVarChildIdSelf, pszName);
+  }
 
   nsAutoString name;
-  xpAccessible->Name(name);
+  Name(name);
 
   // The name was not provided, e.g. no alt attribute for an image. A screen
   // reader may choose to invent its own accessible name, e.g. from an image src
   // attribute. Refer to eNoNameOnPurpose return value.
   if (name.IsVoid())
     return S_FALSE;
 
   *pszName = ::SysAllocStringLen(name.get(), name.Length());
@@ -312,28 +372,28 @@ AccessibleWrap::get_accValue(
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pszValue)
     return E_INVALIDARG;
 
   *pszValue = nullptr;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accValue(kVarChildIdSelf, pszValue);
+  }
 
   nsAutoString value;
-  xpAccessible->Value(value);
+  Value(value);
 
   // See bug 438784: need to expose URL on doc's value attribute. For this,
   // reverting part of fix for bug 425693 to make this MSAA method behave
   // IAccessible2-style.
   if (value.IsEmpty())
     return S_FALSE;
 
   *pszValue = ::SysAllocStringLen(value.get(), value.Length());
@@ -350,28 +410,28 @@ AccessibleWrap::get_accDescription(VARIA
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pszDescription)
     return E_INVALIDARG;
 
   *pszDescription = nullptr;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accDescription(kVarChildIdSelf, pszDescription);
+  }
 
   nsAutoString description;
-  xpAccessible->Description(description);
+  Description(description);
 
   *pszDescription = ::SysAllocStringLen(description.get(),
                                         description.Length());
   return *pszDescription ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -382,33 +442,33 @@ AccessibleWrap::get_accRole(
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pvarRole)
     return E_INVALIDARG;
 
   VariantInit(pvarRole);
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accRole(kVarChildIdSelf, pvarRole);
+  }
 
   a11y::role geckoRole;
 #ifdef DEBUG
-  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
+  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(this),
                "Does not support Text when it should");
 #endif
 
-  geckoRole = xpAccessible->Role();
+  geckoRole = Role();
 
   uint32_t msaaRole = 0;
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              _msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     msaaRole = _msaaRole; \
     break;
@@ -435,17 +495,17 @@ AccessibleWrap::get_accRole(
     pvarRole->vt = VT_I4;
     pvarRole->lVal = msaaRole;  // Normal enumerated role
     return S_OK;
   }
 
   // -- Try BSTR role
   // Could not map to known enumerated MSAA role like ROLE_BUTTON
   // Use BSTR role to expose role attribute or tag name + namespace
-  nsIContent *content = xpAccessible->GetContent();
+  nsIContent *content = GetContent();
   if (!content)
     return E_FAIL;
 
   if (content->IsElement()) {
     nsAutoString roleString;
     if (msaaRole != ROLE_SYSTEM_CLIENT &&
         !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) {
       nsIDocument * document = content->GetUncomposedDoc();
@@ -484,25 +544,25 @@ AccessibleWrap::get_accState(
 
   if (!pvarState)
     return E_INVALIDARG;
 
   VariantInit(pvarState);
   pvarState->vt = VT_I4;
   pvarState->lVal = 0;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accState(kVarChildIdSelf, pvarState);
+  }
 
   // MSAA only has 31 states and the lowest 31 bits of our state bit mask
   // are the same states as MSAA.
   // Note: we map the following Gecko states to different MSAA states:
   //   REQUIRED -> ALERT_LOW
   //   ALERT -> ALERT_MEDIUM
   //   INVALID -> ALERT_HIGH
   //   CHECKABLE -> MARQUEED
@@ -558,29 +618,30 @@ AccessibleWrap::get_accKeyboardShortcut(
       /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pszKeyboardShortcut)
     return E_INVALIDARG;
   *pszKeyboardShortcut = nullptr;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
-
-  Accessible* acc = GetXPAccessibleFor(varChild);
-  if (!acc)
-    return E_INVALIDARG;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  if (acc->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accKeyboardShortcut(kVarChildIdSelf,
+                                               pszKeyboardShortcut);
+  }
 
-  KeyBinding keyBinding = acc->AccessKey();
+  KeyBinding keyBinding = AccessKey();
   if (keyBinding.IsEmpty())
-    keyBinding = acc->KeyboardShortcut();
+    keyBinding = KeyboardShortcut();
 
   nsAutoString shortcut;
   keyBinding.ToString(shortcut);
 
   *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
                                              shortcut.Length());
   return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
 
@@ -791,81 +852,80 @@ AccessibleWrap::get_accDefaultAction(
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pszDefaultAction)
     return E_INVALIDARG;
 
   *pszDefaultAction = nullptr;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->get_accDefaultAction(kVarChildIdSelf, pszDefaultAction);
+  }
 
   nsAutoString defaultAction;
-  xpAccessible->ActionNameAt(0, defaultAction);
+  ActionNameAt(0, defaultAction);
 
   *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
                                           defaultAction.Length());
   return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::accSelect(
       /* [in] */ long flagsSelect,
       /* [optional][in] */ VARIANT varChild)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  // currently only handle focus and selection
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
-
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->accSelect(flagsSelect, kVarChildIdSelf);
+  }
 
   if (flagsSelect & SELFLAG_TAKEFOCUS) {
     if (XRE_IsContentProcess()) {
       // In this case we might have been invoked while the IPC MessageChannel is
       // waiting on a sync reply. We cannot dispatch additional IPC while that
       // is happening, so we dispatch TakeFocus from the main thread to
       // guarantee that we are outside any IPC.
       nsCOMPtr<nsIRunnable> runnable =
-        mozilla::NewRunnableMethod(xpAccessible, &Accessible::TakeFocus);
+        mozilla::NewRunnableMethod(this, &Accessible::TakeFocus);
       NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
       return S_OK;
     }
-    xpAccessible->TakeFocus();
+    TakeFocus();
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_TAKESELECTION) {
-    xpAccessible->TakeSelection();
+    TakeSelection();
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_ADDSELECTION) {
-    xpAccessible->SetSelected(true);
+    SetSelected(true);
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_REMOVESELECTION) {
-    xpAccessible->SetSelected(false);
+    SetSelected(false);
     return S_OK;
   }
 
   return E_FAIL;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -882,27 +942,28 @@ AccessibleWrap::accLocation(
   if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)
     return E_INVALIDARG;
 
   *pxLeft = 0;
   *pyTop = 0;
   *pcxWidth = 0;
   *pcyHeight = 0;
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
+  if (accessible) {
+    return accessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
+                                   kVarChildIdSelf);
+  }
 
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
-
-  nsIntRect rect = xpAccessible->Bounds();
+  nsIntRect rect = Bounds();
 
   *pxLeft = rect.x;
   *pyTop = rect.y;
   *pcxWidth = rect.width;
   *pcyHeight = rect.height;
   return S_OK;
 
   A11Y_TRYBLOCK_END
@@ -916,64 +977,64 @@ AccessibleWrap::accNavigate(
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!pvarEndUpAt)
     return E_INVALIDARG;
 
   VariantInit(pvarEndUpAt);
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varStart, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* accessible = GetXPAccessibleFor(varStart);
-  if (!accessible)
-    return E_INVALIDARG;
-
-  if (accessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  if (accessible) {
+    return accessible->accNavigate(navDir, kVarChildIdSelf, pvarEndUpAt);
+  }
 
   Accessible* navAccessible = nullptr;
   Maybe<RelationType> xpRelation;
 
 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
   case msaaType: \
     xpRelation.emplace(RelationType::geckoType); \
     break;
 
   switch(navDir) {
     case NAVDIR_FIRSTCHILD:
-      if (accessible->IsProxy()) {
-        if (!accessible->Proxy()->MustPruneChildren()) {
-          navAccessible = WrapperFor(accessible->Proxy()->FirstChild());
+      if (IsProxy()) {
+        if (!Proxy()->MustPruneChildren()) {
+          navAccessible = WrapperFor(Proxy()->FirstChild());
         }
       } else {
-        if (!nsAccUtils::MustPrune(accessible))
-          navAccessible = accessible->FirstChild();
+        if (!nsAccUtils::MustPrune(this))
+          navAccessible = FirstChild();
       }
       break;
     case NAVDIR_LASTCHILD:
-      if (accessible->IsProxy()) {
-        if (!accessible->Proxy()->MustPruneChildren()) {
-          navAccessible = WrapperFor(accessible->Proxy()->LastChild());
+      if (IsProxy()) {
+        if (!Proxy()->MustPruneChildren()) {
+          navAccessible = WrapperFor(Proxy()->LastChild());
         }
       } else {
-        if (!nsAccUtils::MustPrune(accessible))
-          navAccessible = accessible->LastChild();
+        if (!nsAccUtils::MustPrune(this))
+          navAccessible = LastChild();
       }
       break;
     case NAVDIR_NEXT:
-      navAccessible = accessible->IsProxy()
-        ? WrapperFor(accessible->Proxy()->NextSibling())
-        : accessible->NextSibling();
+      navAccessible = IsProxy()
+        ? WrapperFor(Proxy()->NextSibling())
+        : NextSibling();
       break;
     case NAVDIR_PREVIOUS:
-      navAccessible = accessible->IsProxy()
-        ? WrapperFor(accessible->Proxy()->PrevSibling())
-        : accessible->PrevSibling();
+      navAccessible = IsProxy()
+        ? WrapperFor(Proxy()->PrevSibling())
+        : PrevSibling();
       break;
     case NAVDIR_DOWN:
     case NAVDIR_LEFT:
     case NAVDIR_RIGHT:
     case NAVDIR_UP:
       return E_NOTIMPL;
 
     // MSAA relationship extensions to accNavigate
@@ -1041,27 +1102,27 @@ AccessibleWrap::accHitTest(
 }
 
 STDMETHODIMP
 AccessibleWrap::accDoDefaultAction(
       /* [optional][in] */ VARIANT varChild)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
+  RefPtr<IAccessible> accessible;
+  HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
+  if (FAILED(hr)) {
+    return hr;
+  }
 
-  Accessible* xpAccessible = GetXPAccessibleFor(varChild);
-  if (!xpAccessible)
-    return E_INVALIDARG;
+  if (accessible) {
+    return accessible->accDoDefaultAction(kVarChildIdSelf);
+  }
 
-  if (xpAccessible->IsDefunct())
-    return CO_E_OBJNOTCONNECTED;
-
-  return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
+  return DoAction(0) ? S_OK : E_INVALIDARG;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::put_accName(
       /* [optional][in] */ VARIANT varChild,
       /* [in] */ BSTR szName)
@@ -1143,23 +1204,18 @@ AccessibleWrap::GetNativeInterface(void*
 {
   *aOutAccessible = static_cast<IAccessible*>(this);
   NS_ADDREF_THIS();
 }
 
 void
 AccessibleWrap::SetID(uint32_t aID)
 {
-  MOZ_ASSERT(XRE_IsContentProcess());
+  MOZ_ASSERT(XRE_IsParentProcess() && IsProxy());
   mID = aID;
-  DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document());
-  DebugOnly<AccessibleWrap*> checkAcc = nullptr;
-  MOZ_ASSERT(!(checkAcc = doc->GetAccessibleByID(aID)) ||
-             checkAcc->GetExistingID() == aID);
-  doc->AddID(aID, this);
 }
 
 void
 AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
                 "MSAA event map skewed");
@@ -1252,54 +1308,38 @@ AccessibleWrap::GetChildIDFor(Accessible
   // A child ID of the window is required, when we use NotifyWinEvent,
   // so that the 3rd party application can call back and get the IAccessible
   // the event occurred on.
 
   if (!aAccessible) {
     return 0;
   }
 
-  // Content should use mID which has been generated by the chrome process.
-  if (XRE_IsContentProcess() && !aAccessible->IsApplication()) {
+  // Chrome should use mID which has been generated by the content process.
+  if (aAccessible->IsProxy()) {
     const uint32_t id = static_cast<AccessibleWrap*>(aAccessible)->mID;
     MOZ_ASSERT(id != kNoID);
     return id;
   }
 
-#ifdef _WIN64
-  if (!aAccessible->Document() && !aAccessible->IsProxy())
+  if (!aAccessible->Document())
     return 0;
 
   uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID;
   if (*id != kNoID)
     return *id;
 
   *id = sIDGen.GetID();
 
-  if (aAccessible->IsProxy()) {
-    DocProxyAccessibleWrap* doc =
-      static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper();
-    doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
-  } else {
-    DocAccessibleWrap* doc =
-      static_cast<DocAccessibleWrap*>(aAccessible->Document());
-    doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
-  }
+  MOZ_ASSERT(!aAccessible->IsProxy());
+  DocAccessibleWrap* doc =
+    static_cast<DocAccessibleWrap*>(aAccessible->Document());
+  doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible));
 
   return *id;
-#else
-  int32_t id = - reinterpret_cast<intptr_t>(aAccessible);
-  if (aAccessible->IsProxy()) {
-    DocProxyAccessibleWrap* doc =
-      static_cast<AccessibleWrap*>(aAccessible)->DocProxyWrapper();
-    doc->AddID(id, static_cast<AccessibleWrap*>(aAccessible));
-  }
-
-  return id;
-#endif
 }
 
 HWND
 AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
 {
   if (!aAccessible) {
     return nullptr;
   }
@@ -1391,169 +1431,191 @@ GetAccessibleInSubtree(DocAccessible* aD
     child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID);
     if (child)
       return child;
   }
 
     return nullptr;
   }
 
-static AccessibleWrap*
-GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, uint32_t aID)
+static already_AddRefed<IDispatch>
+GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc,
+                              const VARIANT& aVarChild)
 {
   auto wrapper = static_cast<DocProxyAccessibleWrap*>(WrapperFor(aDoc));
-  AccessibleWrap* child = wrapper->GetAccessibleByID(aID);
-  if (child) {
-    return child;
+  RefPtr<IAccessible> comProxy;
+  int32_t wrapperChildId = AccessibleWrap::GetChildIDFor(wrapper);
+  if (wrapperChildId == aVarChild.lVal) {
+    wrapper->GetNativeInterface(getter_AddRefs(comProxy));
+    return comProxy.forget();
   }
 
-  size_t childDocs = aDoc->ChildDocCount();
-  for (size_t i = 0; i < childDocs; i++) {
-    const DocAccessibleParent* childDoc = aDoc->ChildDocAt(i);
-    child = GetProxiedAccessibleInSubtree(childDoc, aID);
-    if (child) {
-      return child;
-    }
+  MOZ_ASSERT(aDoc->IsTopLevel());
+  if (!aDoc->IsTopLevel()) {
+    return nullptr;
   }
 
-  return nullptr;
+  wrapper->GetNativeInterface(getter_AddRefs(comProxy));
+
+  RefPtr<IDispatch> disp;
+  if (FAILED(comProxy->get_accChild(aVarChild, getter_AddRefs(disp)))) {
+    return nullptr;
+  }
+
+  return disp.forget();
 }
 
-Accessible*
-AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
+already_AddRefed<IAccessible>
+AccessibleWrap::GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct)
 {
   if (aVarChild.vt != VT_I4)
     return nullptr;
 
-  // if its us real easy - this seems to always be the case
-  if (aVarChild.lVal == CHILDID_SELF)
-    return this;
+  VARIANT varChild = aVarChild;
+
+  MOZ_ASSERT(aIsDefunct);
+  *aIsDefunct = false;
+
+  RefPtr<IAccessible> result;
+
+  if (varChild.lVal == CHILDID_SELF) {
+    *aIsDefunct = IsDefunct();
+    if (*aIsDefunct) {
+      return nullptr;
+    }
+    GetNativeInterface(getter_AddRefs(result));
+    if (result) {
+      return result.forget();
+    }
+    // If we're not a proxy, there's nothing more we can do to attempt to
+    // resolve the IAccessible, so we just fail.
+    if (!IsProxy()) {
+      return nullptr;
+    }
+    // Otherwise, since we're a proxy and we have a null native interface, this
+    // indicates that we need to obtain a COM proxy. To do this, we'll replace
+    // CHILDID_SELF with our real MSAA ID and continue the search from there.
+    varChild.lVal = GetExistingID();
+  }
 
   if (IsProxy() ? Proxy()->MustPruneChildren() : nsAccUtils::MustPrune(this)) {
     return nullptr;
   }
 
-  if (aVarChild.lVal > 0) {
+  // If the MSAA ID is not a chrome id then we already know that we won't
+  // find it here and should look remotely instead.
+  if (XRE_IsParentProcess() && !sIDGen.IsChromeID(varChild.lVal)) {
+    return GetRemoteIAccessibleFor(varChild);
+  }
+  MOZ_ASSERT(XRE_IsParentProcess() ||
+             sIDGen.IsIDForThisContentProcess(varChild.lVal));
+
+  if (varChild.lVal > 0) {
     // Gecko child indices are 0-based in contrast to indices used in MSAA.
-    if (IsProxy()) {
-      if (static_cast<uint32_t>(aVarChild.lVal) > Proxy()->ChildrenCount()) {
-        return nullptr;
-      }
-
-      return WrapperFor(Proxy()->ChildAt(aVarChild.lVal - 1));
-    } else {
-      return GetChildAt(aVarChild.lVal - 1);
+    MOZ_ASSERT(!IsProxy());
+    Accessible* xpAcc = GetChildAt(varChild.lVal - 1);
+    if (!xpAcc) {
+      return nullptr;
     }
+    *aIsDefunct = xpAcc->IsDefunct();
+    static_cast<AccessibleWrap*>(xpAcc)->GetNativeInterface(getter_AddRefs(result));
+    return result.forget();
   }
 
   // If lVal negative then it is treated as child ID and we should look for
   // accessible through whole accessible subtree including subdocuments.
   // Otherwise we treat lVal as index in parent.
-  // Convert child ID to unique ID.
   // First handle the case that both this accessible and the id'd one are in
   // this process.
   if (!IsProxy()) {
-    void* uniqueID = reinterpret_cast<void*>(intptr_t(-aVarChild.lVal));
-
     DocAccessible* document = Document();
     Accessible* child =
-#ifdef _WIN64
-      GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal));
-#else
-      XRE_IsContentProcess() ?
-        GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal)) :
-        document->GetAccessibleByUniqueIDInSubtree(uniqueID);
-#endif
+      GetAccessibleInSubtree(document, static_cast<uint32_t>(varChild.lVal));
 
     // If it is a document then just return an accessible.
-    if (child && IsDoc())
-      return child;
+    if (child && IsDoc()) {
+      *aIsDefunct = child->IsDefunct();
+      static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result));
+      return result.forget();
+    }
 
     // Otherwise check whether the accessible is a child (this path works for
     // ARIA documents and popups).
     Accessible* parent = child;
     while (parent && parent != document) {
-      if (parent == this)
-        return child;
+      if (parent == this) {
+        *aIsDefunct = child->IsDefunct();
+        static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result));
+        return result.forget();
+      }
 
       parent = parent->Parent();
     }
   }
 
   // Now see about the case that both this accessible and the target one are
   // proxied.
-  uint32_t id = aVarChild.lVal;
   if (IsProxy()) {
     DocAccessibleParent* proxyDoc = Proxy()->Document();
-    AccessibleWrap* wrapper = GetProxiedAccessibleInSubtree(proxyDoc, id);
-    if (!wrapper)
+    RefPtr<IDispatch> disp = GetProxiedAccessibleInSubtree(proxyDoc, varChild);
+    if (!disp) {
       return nullptr;
-
-    MOZ_ASSERT(wrapper->IsProxy());
-
-    if (proxyDoc == this->Proxy()) {
-      return wrapper;
     }
 
-    ProxyAccessible* parent = wrapper->Proxy();
-    while (parent && parent != proxyDoc) {
-      if (parent == this->Proxy()) {
-        return wrapper;
-      }
-
-      parent = parent->Parent();
-    }
-
-    return nullptr;
+    MOZ_ASSERT(mscom::IsProxy(disp));
+    DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible,
+                                                 getter_AddRefs(result));
+    MOZ_ASSERT(SUCCEEDED(hr));
+    return result.forget();
   }
 
-  // Finally we need to handle the case that this accessible is in the main
-  // process, but the target is proxied.  This is the case when the target
-  // accessible is in a child document of this one.
+  return nullptr;
+}
+
+already_AddRefed<IAccessible>
+AccessibleWrap::GetRemoteIAccessibleFor(const VARIANT& aVarChild)
+{
   DocAccessibleParent* proxyDoc = nullptr;
   DocAccessible* doc = Document();
   const nsTArray<DocAccessibleParent*>* remoteDocs =
     DocManager::TopLevelRemoteDocs();
   if (!remoteDocs) {
     return nullptr;
   }
 
+  RefPtr<IAccessible> result;
+
   size_t docCount = remoteDocs->Length();
   for (size_t i = 0; i < docCount; i++) {
-    Accessible* outerDoc = remoteDocs->ElementAt(i)->OuterDocOfRemoteBrowser();
+    DocAccessibleParent* remoteDoc = remoteDocs->ElementAt(i);
+
+    uint32_t remoteDocMsaaId = WrapperFor(remoteDoc)->GetExistingID();
+    if (!sIDGen.IsSameContentProcessFor(aVarChild.lVal, remoteDocMsaaId)) {
+      continue;
+    }
+
+    Accessible* outerDoc = remoteDoc->OuterDocOfRemoteBrowser();
     if (!outerDoc) {
       continue;
     }
 
     if (outerDoc->Document() != doc) {
       continue;
     }
 
-    if (doc == this) {
-      AccessibleWrap* proxyWrapper =
-        GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id);
-      if (proxyWrapper) {
-        return proxyWrapper;
-      }
-
+    RefPtr<IDispatch> disp =
+      GetProxiedAccessibleInSubtree(remoteDoc, aVarChild);
+    if (!disp) {
       continue;
     }
 
-    Accessible* parent = outerDoc;
-    while (parent && parent != doc) {
-      if (parent == this) {
-        AccessibleWrap* proxyWrapper =
-          GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id);
-        if (proxyWrapper) {
-          return proxyWrapper;
-        }
-      }
-
-      parent = parent->Parent();
-    }
+    DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible,
+                                                 getter_AddRefs(result));
+    MOZ_ASSERT(SUCCEEDED(hr));
+    return result.forget();
   }
 
   return nullptr;
 }
 
 void
 AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible)
 {
@@ -1598,8 +1660,22 @@ AccessibleWrap::GetTI(LCID lcid)
   hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo);
   typeLib->Release();
 
   if (FAILED(hr))
     return nullptr;
 
   return gTypeInfo;
 }
+
+/* static */
+uint32_t
+AccessibleWrap::GetContentProcessIdFor(dom::ContentParentId aIPCContentId)
+{
+  return sIDGen.GetContentProcessIDFor(aIPCContentId);
+}
+
+/* static */
+void
+AccessibleWrap::ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId)
+{
+  sIDGen.ReleaseContentProcessIDFor(aIPCContentId);
+}
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -9,18 +9,19 @@
 
 #include "nsCOMPtr.h"
 #include "Accessible.h"
 #include "Accessible2.h"
 #include "ia2Accessible.h"
 #include "ia2AccessibleComponent.h"
 #include "ia2AccessibleHyperlink.h"
 #include "ia2AccessibleValue.h"
+#include "mozilla/a11y/MsaaIdGenerator.h"
 #include "mozilla/a11y/ProxyAccessible.h"
-#include "mozilla/a11y/IDSet.h"
+#include "mozilla/Attributes.h"
 
 #ifdef __GNUC__
 // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings
 // about virtual functions being hidden by each other. This is done by
 // design, so silence the warning.
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
 
@@ -168,47 +169,57 @@ public: // construction, destruction
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */
   void UpdateSystemCaretFor(Accessible* aAccessible);
 
   /**
    * Find an accessible by the given child ID in cached documents.
    */
-  Accessible* GetXPAccessibleFor(const VARIANT& aVarChild);
+  MOZ_MUST_USE already_AddRefed<IAccessible>
+  GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct);
 
   virtual void GetNativeInterface(void **aOutAccessible) override;
 
   static IDispatch* NativeAccessible(Accessible* aAccessible);
 
   uint32_t GetExistingID() const { return mID; }
   static const uint32_t kNoID = 0;
-  // This is only valid to call in content
   void SetID(uint32_t aID);
 
+  static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId);
+  static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId);
+
 protected:
   virtual ~AccessibleWrap();
 
   uint32_t mID;
 
+  HRESULT
+  ResolveChild(const VARIANT& aVarChild, IAccessible** aOutInterface);
+
+  /**
+   * Find a remote accessible by the given child ID.
+   */
+  MOZ_MUST_USE already_AddRefed<IAccessible>
+  GetRemoteIAccessibleFor(const VARIANT& aVarChild);
+
   /**
    * Return the wrapper for the document's proxy.
    */
   DocProxyAccessibleWrap* DocProxyWrapper() const;
 
   /**
    * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it.
    */
   static ITypeInfo* GetTI(LCID lcid);
 
   static ITypeInfo* gTypeInfo;
 
-#ifdef _WIN64
-  static IDSet sIDGen;
-#endif
+  static MsaaIdGenerator sIDGen;
 
   enum navRelations {
     NAVRELATION_CONTROLLED_BY = 0x1000,
     NAVRELATION_CONTROLLER_FOR = 0x1001,
     NAVRELATION_LABEL_FOR = 0x1002,
     NAVRELATION_LABELLED_BY = 0x1003,
     NAVRELATION_MEMBER_OF = 0x1004,
     NAVRELATION_NODE_CHILD_OF = 0x1005,
--- a/accessible/windows/msaa/IDSet.h
+++ b/accessible/windows/msaa/IDSet.h
@@ -14,72 +14,81 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/SplayTree.h"
 
 namespace mozilla {
 namespace a11y {
 
 /**
- * On windows an accessible's id must be a negative 32 bit integer.  It is
+ * On windows an accessible's id must be a negative 32 bit integer. It is
  * important to support recycling arbitrary IDs because accessibles can be
  * created and destroyed at any time in the life of a page.  IDSet provides 2
- * operations: generate an ID in the range [ - 2^31, 0 ), and release an ID so
+ * operations: generate an ID in the range (0, mMaxId], and release an ID so
  * it can be allocated again.  Allocated ID are tracked by a sparse bitmap
  * implemented with a splay tree.  Nodes in the tree are keyed by the upper N
- * bits of the bitwise negation of the ID, and the node contains a bitmap
- * tracking the allocation of 2^(32 - N) IDs.
+ * bits of the ID, and the node contains a bitmap tracking the allocation of
+ * 2^(ceil(log2(mMaxId)) - N) IDs.
+ *
+ * Note that negation is handled by MsaaIdGenerator as it performs additional
+ * decoration on the ID generated by IDSet.
+ * @see mozilla::a11y::MsaaIdGenerator
  */
 class IDSet
 {
 public:
-  constexpr IDSet() : mBitSet(), mIdx(0) {}
+  constexpr explicit IDSet(const uint32_t aMaxIdBits)
+    : mBitSet()
+    , mIdx(0)
+    , mMaxId((1UL << aMaxIdBits) - 1UL)
+    , mMaxIdx(mMaxId / bitsPerElt)
+  {
+  }
 
   /**
    * Return a new unique id.
    */
   uint32_t GetID()
   {
     uint32_t idx = mIdx;
     while (true) {
       BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx));
       if (elt->mBitvec[0] != UINT64_MAX) {
         uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]);
 
         elt->mBitvec[0] |= (1ull << i);
         mIdx = idx;
-        return ~(elt->mIdx * bitsPerElt + i);
+        return (elt->mIdx * bitsPerElt + i);
       }
 
       if (elt->mBitvec[1] != UINT64_MAX) {
         uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]);
 
         elt->mBitvec[1] |= (1ull << i);
         mIdx = idx;
-        return ~(elt->mIdx * bitsPerElt + bitsPerWord + i);
+        return (elt->mIdx * bitsPerElt + bitsPerWord + i);
       }
 
       idx++;
-      if (idx > sMaxIdx) {
+      if (idx > mMaxIdx) {
         idx = 0;
       }
 
       if (idx == mIdx) {
         MOZ_CRASH("used up all the available ids");
       }
     }
   }
 
   /**
    * Free a no longer required id so it may be allocated again.
    */
   void ReleaseID(uint32_t aID)
   {
-    aID = ~aID;
-    MOZ_ASSERT(aID < static_cast<uint32_t>(INT32_MAX));
+    MOZ_ASSERT(aID < mMaxId);
 
     uint32_t idx = aID / bitsPerElt;
     mIdx = idx;
     BitSetElt* elt = mBitSet.find(BitSetElt(idx));
     MOZ_ASSERT(elt);
 
     uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord;
     elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord));
@@ -87,17 +96,16 @@ public:
       delete mBitSet.remove(*elt);
     }
   }
 
 private:
   static const unsigned int wordsPerElt = 2;
   static const unsigned int bitsPerWord = 64;
   static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord;
-  static const uint32_t sMaxIdx = INT32_MAX / bitsPerElt;
 
   struct BitSetElt : mozilla::SplayTreeNode<BitSetElt>
   {
     explicit BitSetElt(uint32_t aIdx) :
       mIdx(aIdx)
     { mBitvec[0] = mBitvec[1] = 0; }
 
     uint64_t mBitvec[wordsPerElt];
@@ -113,14 +121,16 @@ private:
         return -1;
       }
       return 1;
     }
   };
 
   SplayTree<BitSetElt, BitSetElt> mBitSet;
   uint32_t mIdx;
+  const uint32_t mMaxId;
+  const uint32_t mMaxIdx;
 };
 
 }
 }
 
 #endif
new file mode 100644
--- /dev/null
+++ b/accessible/windows/msaa/MsaaIdGenerator.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "MsaaIdGenerator.h"
+
+#include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "nsDataHashtable.h"
+#include "nsIXULRuntime.h"
+
+// These constants may be adjusted to modify the proportion of the Child ID
+// allocated to the content ID vs proportion allocated to the unique ID. They
+// must always sum to 31, ie. the width of a 32-bit integer less the sign bit.
+
+// NB: kNumContentProcessIDBits must be large enough to successfully hold the
+// maximum permitted number of e10s content processes. If the e10s maximum
+// number of content processes changes, then kNumContentProcessIDBits must also
+// be updated if necessary to accommodate that new value!
+static const uint32_t kNumContentProcessIDBits = 7UL;
+static const uint32_t kNumUniqueIDBits = (31UL - kNumContentProcessIDBits);
+
+static_assert(kNumContentProcessIDBits + kNumUniqueIDBits == 31,
+              "Allocation of Content ID bits and Unique ID bits must sum to 31");
+
+namespace mozilla {
+namespace a11y {
+namespace detail {
+
+typedef nsDataHashtable<nsUint64HashKey, uint32_t> ContentParentIdMap;
+
+#pragma pack(push, 1)
+union MsaaID
+{
+  int32_t mInt32;
+  uint32_t mUInt32;
+  struct
+  {
+    uint32_t mUniqueID:kNumUniqueIDBits;
+    uint32_t mContentProcessID:kNumContentProcessIDBits;
+    uint32_t mSignBit:1;
+  }
+  mCracked;
+};
+#pragma pack(pop)
+
+static uint32_t
+BuildMsaaID(const uint32_t aID, const uint32_t aContentProcessID)
+{
+  MsaaID id;
+  id.mCracked.mSignBit = 0;
+  id.mCracked.mUniqueID = aID;
+  id.mCracked.mContentProcessID = aContentProcessID;
+  return ~id.mUInt32;
+}
+
+class MsaaIDCracker
+{
+public:
+  explicit MsaaIDCracker(const uint32_t aMsaaID)
+  {
+    mID.mUInt32 = ~aMsaaID;
+  }
+
+  uint32_t GetContentProcessId()
+  {
+    return mID.mCracked.mContentProcessID;
+  }
+
+  uint32_t GetUniqueId()
+  {
+    return mID.mCracked.mUniqueID;
+  }
+
+private:
+  MsaaID  mID;
+};
+
+} // namespace detail
+
+constexpr MsaaIdGenerator::MsaaIdGenerator()
+  : mIDSet(kNumUniqueIDBits)
+  , mContentProcessID(0)
+{}
+
+uint32_t
+MsaaIdGenerator::GetID()
+{
+  static const uint32_t kContentProcessId = ResolveContentProcessID();
+  uint32_t id = mIDSet.GetID();
+  MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL));
+  return detail::BuildMsaaID(id, kContentProcessId);
+}
+
+void
+MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap)
+{
+  MOZ_ASSERT(aAccWrap);
+  uint32_t id = aAccWrap->GetExistingID();
+  MOZ_ASSERT(id != AccessibleWrap::kNoID);
+  detail::MsaaIDCracker cracked(id);
+  if (cracked.GetContentProcessId() != mContentProcessID) {
+    // This may happen if chrome holds a proxy whose ID was originally generated
+    // by a content process. Since ReleaseID only has meaning in the process
+    // that originally generated that ID, we ignore ReleaseID calls for any ID
+    // that did not come from the current process.
+    MOZ_ASSERT(aAccWrap->IsProxy());
+    return;
+  }
+  mIDSet.ReleaseID(cracked.GetUniqueId());
+}
+
+bool
+MsaaIdGenerator::IsChromeID(uint32_t aID)
+{
+  detail::MsaaIDCracker cracked(aID);
+  return cracked.GetContentProcessId() == 0;
+}
+
+bool
+MsaaIdGenerator::IsIDForThisContentProcess(uint32_t aID)
+{
+  MOZ_ASSERT(XRE_IsContentProcess());
+  static const uint32_t kContentProcessId = ResolveContentProcessID();
+  detail::MsaaIDCracker cracked(aID);
+  return cracked.GetContentProcessId() == kContentProcessId;
+}
+
+bool
+MsaaIdGenerator::IsIDForContentProcess(uint32_t aID,
+                                       dom::ContentParentId aIPCContentProcessId)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  detail::MsaaIDCracker cracked(aID);
+  return cracked.GetContentProcessId() ==
+           GetContentProcessIDFor(aIPCContentProcessId);
+}
+
+bool
+MsaaIdGenerator::IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID)
+{
+  detail::MsaaIDCracker firstCracked(aFirstID);
+  detail::MsaaIDCracker secondCracked(aSecondID);
+  return firstCracked.GetContentProcessId() ==
+           secondCracked.GetContentProcessId();
+}
+
+uint32_t
+MsaaIdGenerator::ResolveContentProcessID()
+{
+  if (XRE_IsParentProcess()) {
+    return 0;
+  }
+
+  dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+  Unused << contentChild->SendGetA11yContentId(&mContentProcessID);
+
+  MOZ_ASSERT(mContentProcessID);
+  return mContentProcessID;
+}
+
+/**
+ * Each dom::ContentParent has a 64-bit ID. This ID is monotonically increasing
+ * with each new content process, so those IDs are effectively single-use. OTOH,
+ * MSAA requires 32-bit IDs. Since we only allocate kNumContentProcessIDBits for
+ * the content process ID component, the MSAA content process ID value must be
+ * reusable. sContentParentIdMap holds the current associations between
+ * dom::ContentParent IDs and the MSAA content parent IDs that have been
+ * allocated to them.
+ */
+static StaticAutoPtr<detail::ContentParentIdMap> sContentParentIdMap;
+
+static const uint32_t kBitsPerByte = 8UL;
+// Set sContentProcessIdBitmap[0] to 1 to reserve the Chrome process's id
+static uint64_t sContentProcessIdBitmap[(1UL << kNumContentProcessIDBits) /
+                                        (sizeof(uint64_t) * kBitsPerByte)] = {1ULL};
+static const uint32_t kBitsPerElement = sizeof(sContentProcessIdBitmap[0]) *
+                                        kBitsPerByte;
+
+uint32_t
+MsaaIdGenerator::GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID)
+{
+  MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
+  if (!sContentParentIdMap) {
+    sContentParentIdMap = new detail::ContentParentIdMap();
+    ClearOnShutdown(&sContentParentIdMap);
+  }
+
+  uint32_t value = 0;
+  if (sContentParentIdMap->Get(aIPCContentProcessID, &value)) {
+    return value;
+  }
+
+  uint32_t index = 0;
+  for (; index < ArrayLength(sContentProcessIdBitmap); ++index) {
+    if (sContentProcessIdBitmap[index] == UINT64_MAX) {
+      continue;
+    }
+    uint32_t bitIndex = CountTrailingZeroes64(~sContentProcessIdBitmap[index]);
+    MOZ_ASSERT(!(sContentProcessIdBitmap[index] & (1ULL << bitIndex)));
+    MOZ_ASSERT(bitIndex != 0 || index != 0);
+    sContentProcessIdBitmap[index] |= (1ULL << bitIndex);
+    value = index * kBitsPerElement + bitIndex;
+    break;
+  }
+
+  // If we run out of content process IDs, we're in trouble
+  MOZ_RELEASE_ASSERT(index < ArrayLength(sContentProcessIdBitmap));
+
+  sContentParentIdMap->Put(aIPCContentProcessID, value);
+  return value;
+}
+
+void
+MsaaIdGenerator::ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID)
+{
+  MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
+  if (!sContentParentIdMap) {
+    // Since Content IDs are generated lazily, ContentParent might attempt
+    // to release an ID that was never allocated to begin with.
+    return;
+  }
+
+  Maybe<uint32_t> mapping = sContentParentIdMap->GetAndRemove(aIPCContentProcessID);
+  if (!mapping) {
+    // Since Content IDs are generated lazily, ContentParent might attempt
+    // to release an ID that was never allocated to begin with.
+    return;
+  }
+
+  uint32_t index = mapping.ref() / kBitsPerElement;
+  MOZ_ASSERT(index < ArrayLength(sContentProcessIdBitmap));
+
+  uint64_t mask = 1ULL << (mapping.ref() % kBitsPerElement);
+  MOZ_ASSERT(sContentProcessIdBitmap[index] & mask);
+
+  sContentProcessIdBitmap[index] &= ~mask;
+}
+
+} // namespace a11y
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/accessible/windows/msaa/MsaaIdGenerator.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_MsaaIdGenerator_h
+#define mozilla_a11y_MsaaIdGenerator_h
+
+#include "mozilla/a11y/IDSet.h"
+
+#include "mozilla/dom/ipc/IdType.h"
+
+namespace mozilla {
+namespace a11y {
+
+class AccessibleWrap;
+
+/**
+ * This class is responsible for generating child IDs used by our MSAA
+ * implementation. Since e10s requires us to differentiate IDs based on the
+ * originating process of the accessible, a portion of the ID's bits are
+ * allocated to storing that information. The remaining bits represent the
+ * unique ID of the accessible, within that content process.
+ *
+ * The constants kNumContentProcessIDBits and kNumUniqueIDBits in the
+ * implementation are responsible for determining the proportion of bits that
+ * are allocated for each purpose.
+ */
+class MsaaIdGenerator
+{
+public:
+  constexpr MsaaIdGenerator();
+
+  uint32_t GetID();
+  void ReleaseID(AccessibleWrap* aAccWrap);
+  bool IsChromeID(uint32_t aID);
+  bool IsIDForThisContentProcess(uint32_t aID);
+  bool IsIDForContentProcess(uint32_t aID,
+                             dom::ContentParentId aIPCContentProcessId);
+  bool IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID);
+
+  uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
+  void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
+
+private:
+  uint32_t ResolveContentProcessID();
+
+private:
+  IDSet     mIDSet;
+  uint32_t  mContentProcessID;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_MsaaIdGenerator_h
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -80,30 +80,16 @@ void
 a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
   AccessibleWrap* wrapper =
     reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
   MOZ_ASSERT(wrapper);
   if (!wrapper)
     return;
 
-  auto doc =
-    static_cast<DocProxyAccessibleWrap*>(WrapperFor(aProxy->Document()));
-  MOZ_ASSERT(doc);
-  if (doc) {
-#ifdef _WIN64
-    uint32_t id = wrapper->GetExistingID();
-    if (id != AccessibleWrap::kNoID) {
-      doc->RemoveID(id);
-    }
-#else
-    doc->RemoveID(-reinterpret_cast<int32_t>(wrapper));
-#endif
-  }
-
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType)
 {
--- a/accessible/windows/msaa/moz.build
+++ b/accessible/windows/msaa/moz.build
@@ -8,31 +8,33 @@ EXPORTS += [
     'IUnknownImpl.h',
 ]
 
 EXPORTS.mozilla.a11y += [
     'AccessibleWrap.h',
     'Compatibility.h',
     'HyperTextAccessibleWrap.h',
     'IDSet.h',
+    'MsaaIdGenerator.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleWrap.cpp',
     'ApplicationAccessibleWrap.cpp',
     'ARIAGridAccessibleWrap.cpp',
     'ChildIDThunk.cpp',
     'Compatibility.cpp',
     'DocAccessibleWrap.cpp',
     'EnumVariant.cpp',
     'HTMLTableAccessibleWrap.cpp',
     'HTMLWin32ObjectAccessible.cpp',
     'HyperTextAccessibleWrap.cpp',
     'ImageAccessibleWrap.cpp',
     'IUnknownImpl.cpp',
+    'MsaaIdGenerator.cpp',
     'nsWinUtils.cpp',
     'Platform.cpp',
     'RootAccessibleWrap.cpp',
     'TextLeafAccessibleWrap.cpp',
 ]
 
 # This file cannot be built in unified mode because it includes ISimpleDOMNode_i.c.
 SOURCES += [
--- a/accessible/windows/sdn/moz.build
+++ b/accessible/windows/sdn/moz.build
@@ -14,9 +14,11 @@ LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/windows/msaa',
     '/accessible/xpcom',
     '/accessible/xul',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
--- a/accessible/windows/uia/moz.build
+++ b/accessible/windows/uia/moz.build
@@ -12,9 +12,11 @@ LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/windows/msaa',
     '/accessible/xpcom',
     '/accessible/xul',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
--- a/accessible/xul/moz.build
+++ b/accessible/xul/moz.build
@@ -42,12 +42,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
     LOCAL_INCLUDES += [
         '/accessible/mac',
     ]
 else:
     LOCAL_INCLUDES += [
         '/accessible/other',
     ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -380,17 +380,20 @@ var FullScreen = {
   receiveMessage: function(aMessage) {
     let browser = aMessage.target;
     switch (aMessage.name) {
       case "DOMFullscreen:Request": {
         this._windowUtils.remoteFrameFullscreenChanged(browser);
         break;
       }
       case "DOMFullscreen:NewOrigin": {
-        PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
+        // Don't show the warning if we've already exit fullscreen.
+        if (document.fullscreen) {
+          PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
+        }
         break;
       }
       case "DOMFullscreen:Exit": {
         this._windowUtils.remoteFrameFullscreenReverted();
         break;
       }
       case "DOMFullscreen:Painted": {
         Services.obs.notifyObservers(window, "fullscreen-painted", "");
@@ -405,38 +408,42 @@ var FullScreen = {
     if (!document.fullscreenElement) {
       return;
     }
 
     // If we have a current pointerlock warning shown then hide it
     // before transition.
     PointerlockFsWarning.close();
 
+    // If it is a remote browser, send a message to ask the content
+    // to enter fullscreen state. We don't need to do so if it is an
+    // in-process browser, since all related document should have
+    // entered fullscreen state at this point.
+    // This should be done before the active tab check below to ensure
+    // that the content document handles the pending request. Doing so
+    // before the check is fine since we also check the activeness of
+    // the requesting document in content-side handling code.
+    if (this._isRemoteBrowser(aBrowser)) {
+      aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered");
+    }
+
     // If we've received a fullscreen notification, we have to ensure that the
     // element that's requesting fullscreen belongs to the browser that's currently
     // active. If not, we exit fullscreen since the "full-screen document" isn't
     // actually visible now.
     if (!aBrowser || gBrowser.selectedBrowser != aBrowser ||
         // The top-level window has lost focus since the request to enter
         // full-screen was made. Cancel full-screen.
         Services.focus.activeWindow != window) {
       // This function is called synchronously in fullscreen change, so
       // we have to avoid calling exitFullscreen synchronously here.
       setTimeout(() => document.exitFullscreen(), 0);
       return;
     }
 
-    // If it is a remote browser, send a message to ask the content
-    // to enter fullscreen state. We don't need to do so if it is an
-    // in-process browser, since all related document should have
-    // entered fullscreen state at this point.
-    if (this._isRemoteBrowser(aBrowser)) {
-      aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered");
-    }
-
     document.documentElement.setAttribute("inDOMFullscreen", true);
 
     if (gFindBarInitialized) {
       gFindBar.close(true);
     }
 
     // Exit DOM full-screen mode upon open, close, or change tab.
     gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -49,16 +49,18 @@ PluginContent.prototype = {
     global.addEventListener("HiddenPlugin",          this, true);
 
     global.addMessageListener("BrowserPlugins:ActivatePlugins", this);
     global.addMessageListener("BrowserPlugins:NotificationShown", this);
     global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
     global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this);
     global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this);
     global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this);
+
+    Services.obs.addObserver(this, "decoder-doctor-notification", false);
   },
 
   uninit: function() {
     let global = this.global;
 
     global.removeEventListener("PluginBindingAttached", this, true);
     global.removeEventListener("PluginCrashed",         this, true);
     global.removeEventListener("PluginOutdated",        this, true);
@@ -70,16 +72,19 @@ PluginContent.prototype = {
     global.removeEventListener("HiddenPlugin",          this, true);
 
     global.removeMessageListener("BrowserPlugins:ActivatePlugins", this);
     global.removeMessageListener("BrowserPlugins:NotificationShown", this);
     global.removeMessageListener("BrowserPlugins:ContextMenuCommand", this);
     global.removeMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this);
     global.removeMessageListener("BrowserPlugins:CrashReportSubmitted", this);
     global.removeMessageListener("BrowserPlugins:Test:ClearCrashData", this);
+
+    Services.obs.removeObserver(this, "decoder-doctor-notification");
+
     delete this.global;
     delete this.content;
   },
 
   receiveMessage: function (msg) {
     switch (msg.name) {
       case "BrowserPlugins:ActivatePlugins":
         this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
@@ -113,16 +118,36 @@ PluginContent.prototype = {
       case "BrowserPlugins:Test:ClearCrashData":
         // This message should ONLY ever be sent by automated tests.
         if (Services.prefs.getBoolPref("plugins.testmode")) {
           this.pluginCrashData.clear();
         }
     }
   },
 
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "decoder-doctor-notification":
+        let data = JSON.parse(aData);
+        if (this.haveShownNotification &&
+            aSubject.top.document == this.content.document &&
+            data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
+          let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+          let principal = this.content.document.nodePrincipal;
+          let location = this.content.document.location.href;
+          this.global.content.pluginRequiresReload = true;
+          this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification",
+                                       { plugins: [... this.pluginData.values()],
+                                         showNow: true,
+                                         location: location,
+                                       }, null, principal);
+        }
+    }
+  },
+
   onPageShow: function (event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
     // The PluginClickToPlay events are not fired when navigating using the
     // BF cache. |persisted| is true when the page is loaded from the
@@ -135,16 +160,17 @@ PluginContent.prototype = {
   onPageHide: function (event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
     this._finishRecordingFlashPluginTelemetry();
     this.clearPluginCaches();
+    this.haveShownNotification = false;
   },
 
   getPluginUI: function (plugin, anonid) {
     return plugin.ownerDocument.
            getAnonymousElementByAttribute(plugin, "anonid", anonid);
   },
 
   _getPluginInfo: function (pluginElement) {
@@ -720,19 +746,21 @@ PluginContent.prototype = {
           if (overlay) {
             overlay.removeEventListener("click", this, true);
           }
           plugin.playPlugin();
         }
       }
     }
 
-    // If there are no instances of the plugin on the page any more, what the
+    // If there are no instances of the plugin on the page any more or if we've
+    // noted that the content needs to be reloaded due to replacing HLS, what the
     // user probably needs is for us to allow and then refresh.
-    if (newState != "block" && !pluginFound) {
+    if (newState != "block" &&
+       (!pluginFound || contentWindow.pluginRequiresReload)) {
       this.reloadPage();
     }
     this.updateNotificationUI();
   },
 
   _showClickToPlayNotification: function (plugin, showNow) {
     let plugins = [];
 
@@ -784,16 +812,18 @@ PluginContent.prototype = {
       else {
         pluginInfo.pluginPermissionPrePath = principal.originNoSuffix;
         pluginInfo.pluginPermissionType = undefined;
       }
 
       this.pluginData.set(pluginInfo.permissionString, pluginInfo);
     }
 
+    this.haveShownNotification = true;
+
     this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [... this.pluginData.values()],
       showNow: showNow,
       location: location,
     }, null, principal);
   },
 
   /**
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -20,51 +20,57 @@ if test -n "$MOZ_ASAN"; then
         MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
         if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
             AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
         fi
         AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
     fi
     CFLAGS="-fsanitize=address $CFLAGS"
     CXXFLAGS="-fsanitize=address $CXXFLAGS"
-    LDFLAGS="-fsanitize=address $LDFLAGS"
+    if test -z "$CLANG_CL"; then
+        LDFLAGS="-fsanitize=address $LDFLAGS"
+    fi
     AC_DEFINE(MOZ_ASAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_ASAN)
 
 dnl ========================================================
 dnl = Use Memory Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(memory-sanitizer,
 [  --enable-memory-sanitizer       Enable Memory Sanitizer (default=no)],
     MOZ_MSAN=1,
     MOZ_MSAN= )
 if test -n "$MOZ_MSAN"; then
     MOZ_LLVM_HACKS=1
     CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS"
     CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS"
-    LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS"
+    if test -z "$CLANG_CL"; then
+        LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS"
+    fi
     AC_DEFINE(MOZ_MSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_MSAN)
 
 dnl ========================================================
 dnl = Use Thread Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(thread-sanitizer,
 [  --enable-thread-sanitizer       Enable Thread Sanitizer (default=no)],
    MOZ_TSAN=1,
    MOZ_TSAN= )
 if test -n "$MOZ_TSAN"; then
     MOZ_LLVM_HACKS=1
     CFLAGS="-fsanitize=thread $CFLAGS"
     CXXFLAGS="-fsanitize=thread $CXXFLAGS"
-    LDFLAGS="-fsanitize=thread $LDFLAGS"
+    if test -z "$CLANG_CL"; then
+        LDFLAGS="-fsanitize=thread $LDFLAGS"
+    fi
     AC_DEFINE(MOZ_TSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_TSAN)
 
 # The LLVM symbolizer is used by all sanitizers
 AC_SUBST(LLVM_SYMBOLIZER)
 
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -329,28 +329,32 @@ nsScriptSecurityManager::GetChannelResul
 }
 
 nsresult
 nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
                                                    nsIPrincipal** aPrincipal,
                                                    bool aIgnoreSandboxing)
 {
     NS_PRECONDITION(aChannel, "Must have channel!");
+    // Check whether we have an nsILoadInfo that says what we should do.
+    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+    if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
+      NS_ADDREF(*aPrincipal = loadInfo->PrincipalToInherit());
+      return NS_OK;
+    }
+
     nsCOMPtr<nsISupports> owner;
     aChannel->GetOwner(getter_AddRefs(owner));
     if (owner) {
         CallQueryInterface(owner, aPrincipal);
         if (*aPrincipal) {
             return NS_OK;
         }
     }
 
-    // Check whether we have an nsILoadInfo that says what we should do.
-    nsCOMPtr<nsILoadInfo> loadInfo;
-    aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     if (loadInfo) {
         if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
             RefPtr<nsNullPrincipal> prin;
             if (loadInfo->LoadingPrincipal()) {
               prin =
                 nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
             } else {
               NeckoOriginAttributes nAttrs;
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -103,8 +103,9 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_framedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage, and also bug 784321
 support-files = file_framedhistoryframes.html
 [test_pushState_after_document_open.html]
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug1121701.html]
 skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
+[test_forceinheritprincipal_overrule_owner.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_forceinheritprincipal_overrule_owner.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script type="text/javascript">
+
+var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel;
+var loadInfo = channel.loadInfo;
+
+// 1) perform some sanity checks
+var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
+var loadingPrincipal = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
+var principalToInherit = channel.loadInfo.principalToInherit.URI.asciiSpec;
+
+ok(triggeringPrincipal.startsWith("http://mochi.test:8888/"),
+   "initial triggeringPrincipal correct");
+ok(loadingPrincipal.startsWith("http://mochi.test:8888/"),
+   "initial loadingPrincipal correct");
+ok(principalToInherit.startsWith("http://mochi.test:8888/"),
+   "initial principalToInherit correct");
+
+// reset principals on the loadinfo
+loadInfo.resetPrincipalsToNullPrincipal();
+
+// 2) verify loadInfo contains the correct principals
+var triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
+var loadingPrincipal = channel.loadInfo.loadingPrincipal;
+var principalToInherit = channel.loadInfo.principalToInherit;
+
+ok(triggeringPrincipal.isNullPrincipal,
+   "triggeringPrincipal after resetting correct");
+ok(loadingPrincipal.isNullPrincipal,
+   "triggeringPrincipal after resetting correct");
+ok(triggeringPrincipal.isNullPrincipal,
+   "principalToInherit after resetting correct");
+
+// 3) verify that getChannelResultPrincipal returns right principal
+var resultPrincipal = SpecialPowers.Services.scriptSecurityManager
+                                   .getChannelResultPrincipal(channel);
+
+ok(resultPrincipal.isNullPrincipal,
+   "resultPrincipal after resetting correct");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1190,30 +1190,16 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
                       ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
   bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
                         otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
   if (ourHasSrcdoc || otherHasSrcdoc) {
     // Ignore this case entirely for now, since we support XUL <-> HTML swapping
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  bool ourPassPointerEvents =
-    ourContent->AttrValueIs(kNameSpaceID_None,
-                            nsGkAtoms::mozpasspointerevents,
-                            nsGkAtoms::_true,
-                            eCaseMatters);
-  bool otherPassPointerEvents =
-    otherContent->AttrValueIs(kNameSpaceID_None,
-                              nsGkAtoms::mozpasspointerevents,
-                              nsGkAtoms::_true,
-                              eCaseMatters);
-  if (ourPassPointerEvents != otherPassPointerEvents) {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
   bool ourFullscreenAllowed =
     ourContent->IsXULElement() ||
     (OwnerIsMozBrowserOrAppFrame() &&
       (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
        ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
   bool otherFullscreenAllowed =
     otherContent->IsXULElement() ||
     (aOther->OwnerIsMozBrowserOrAppFrame() &&
@@ -2657,23 +2643,16 @@ nsFrameLoader::TryRemoteBrowser()
   nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
 
   if (rootChromeWin) {
     nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
     rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
     mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
   }
 
-  if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
-                                 nsGkAtoms::mozpasspointerevents,
-                                 nsGkAtoms::_true,
-                                 eCaseMatters)) {
-    Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
-  }
-
   ReallyLoadFrameScripts();
   InitializeBrowserAPI();
 
  return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser() const
@@ -2703,30 +2682,16 @@ NS_IMETHODIMP
 nsFrameLoader::DeactivateRemoteFrame() {
   if (mRemoteBrowser) {
     mRemoteBrowser->Deactivate();
     return NS_OK;
   }
   return NS_ERROR_UNEXPECTED;
 }
 
-void
-nsFrameLoader::ActivateUpdateHitRegion() {
-  if (mRemoteBrowser) {
-    Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
-  }
-}
-
-void
-nsFrameLoader::DeactivateUpdateHitRegion() {
-  if (mRemoteBrowser) {
-    Unused << mRemoteBrowser->SendSetUpdateHitRegion(false);
-  }
-}
-
 NS_IMETHODIMP
 nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType,
                                           float aX,
                                           float aY,
                                           int32_t aButton,
                                           int32_t aClickCount,
                                           int32_t aModifiers,
                                           bool aIgnoreRootScrollFrame)
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -213,19 +213,16 @@ public:
    * Applies a new set of sandbox flags. These are merged with the sandbox
    * flags from our owning content's owning document with a logical OR, this
    * ensures that we can only add restrictions and never remove them.
    */
   void ApplySandboxFlags(uint32_t sandboxFlags);
 
   void GetURL(nsString& aURL);
 
-  void ActivateUpdateHitRegion();
-  void DeactivateUpdateHitRegion();
-
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
   virtual nsIMessageSender* GetProcessMessageManager() const override;
 
   // public because a callback needs these.
   RefPtr<nsFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -623,17 +623,16 @@ GK_ATOM(mousedown, "mousedown")
 GK_ATOM(mousemove, "mousemove")
 GK_ATOM(mouseout, "mouseout")
 GK_ATOM(mouseover, "mouseover")
 GK_ATOM(mousethrough, "mousethrough")
 GK_ATOM(mouseup, "mouseup")
 GK_ATOM(mozaudiochannel, "mozaudiochannel")
 GK_ATOM(mozfullscreenchange, "mozfullscreenchange")
 GK_ATOM(mozfullscreenerror, "mozfullscreenerror")
-GK_ATOM(mozpasspointerevents, "mozpasspointerevents")
 GK_ATOM(mozpointerlockchange, "mozpointerlockchange")
 GK_ATOM(mozpointerlockerror, "mozpointerlockerror")
 GK_ATOM(mozprivatebrowsing, "mozprivatebrowsing")
 GK_ATOM(moz_opaque, "moz-opaque")
 GK_ATOM(moz_action_hint, "mozactionhint")
 GK_ATOM(x_moz_errormessage, "x-moz-errormessage")
 GK_ATOM(msthemecompatible, "msthemecompatible")
 GK_ATOM(multicol, "multicol")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6107,22 +6107,26 @@ nsGlobalWindow::SetFullScreen(bool aFull
 nsresult
 nsGlobalWindow::SetFullScreen(bool aFullScreen)
 {
   FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
 
   return SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullScreen);
 }
 
-void
+static void
 FinishDOMFullscreenChange(nsIDocument* aDoc, bool aInDOMFullscreen)
 {
   if (aInDOMFullscreen) {
     // Ask the document to handle any pending DOM fullscreen change.
-    nsIDocument::HandlePendingFullscreenRequests(aDoc);
+    if (!nsIDocument::HandlePendingFullscreenRequests(aDoc)) {
+      // If we don't end up having anything in fullscreen,
+      // async request exiting fullscreen.
+      nsIDocument::AsyncExitFullscreen(aDoc);
+    }
   } else {
     // If the window is leaving fullscreen state, also ask the document
     // to exit from DOM Fullscreen.
     nsIDocument::ExitFullscreenInDocTree(aDoc);
   }
 }
 
 struct FullscreenTransitionDuration
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -655,17 +655,17 @@ nsImageLoadingContent::ForceReload(const
   // defaults to true
   bool notify = !aNotify.WasPassed() || aNotify.Value();
 
   // We keep this flag around along with the old URI even for failed requests
   // without a live request object
   ImageLoadType loadType = \
     (mCurrentRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
                                                  : eImageLoadType_Normal;
-  nsresult rv = LoadImage(currentURI, true, notify, loadType, nullptr,
+  nsresult rv = LoadImage(currentURI, true, notify, loadType, true, nullptr,
                           nsIRequest::VALIDATE_ALWAYS);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::ForceReload(bool aNotify /* = true */,
@@ -746,40 +746,49 @@ nsImageLoadingContent::LoadImage(const n
 
   if (aNewURI.IsEmpty()) {
     // Cancel image requests and then fire only error event per spec.
     CancelImageRequests(aNotify);
     FireEvent(NS_LITERAL_STRING("error"));
     return NS_OK;
   }
 
-  // Second, parse the URI string to get image URI
+  // Fire loadstart event
+  FireEvent(NS_LITERAL_STRING("loadstart"));
+
+  // Parse the URI string to get image URI
   nsCOMPtr<nsIURI> imageURI;
   nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
   if (NS_FAILED(rv)) {
     // Cancel image requests and then fire error and loadend events per spec
     CancelImageRequests(aNotify);
     FireEvent(NS_LITERAL_STRING("error"));
     FireEvent(NS_LITERAL_STRING("loadend"));
     return NS_OK;
   }
 
   NS_TryToSetImmutable(imageURI);
 
-  return LoadImage(imageURI, aForce, aNotify, aImageLoadType, doc);
+  return LoadImage(imageURI, aForce, aNotify, aImageLoadType, false, doc);
 }
 
 nsresult
 nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
                                  bool aForce,
                                  bool aNotify,
                                  ImageLoadType aImageLoadType,
+                                 bool aLoadStart,
                                  nsIDocument* aDocument,
                                  nsLoadFlags aLoadFlags)
 {
+  // Fire loadstart event if required
+  if (aLoadStart) {
+    FireEvent(NS_LITERAL_STRING("loadstart"));
+  }
+
   if (!mLoadingEnabled) {
     // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
     // don't want/need it.
     FireEvent(NS_LITERAL_STRING("error"));
     FireEvent(NS_LITERAL_STRING("loadend"));
     return NS_OK;
   }
 
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -121,23 +121,25 @@ protected:
    * URI object already available, they should use this method.
    *
    * @param aNewURI the URI to be loaded
    * @param aForce If true, make sure to load the URI.  If false, only
    *        load if the URI is different from the currently loaded URI.
    * @param aNotify If true, nsIDocumentObserver state change notifications
    *                will be sent as needed.
    * @param aImageLoadType The ImageLoadType for this request
+   * @param aLoadStart If true, dispatch "loadstart" event.
    * @param aDocument Optional parameter giving the document this node is in.
    *        This is purely a performance optimization.
    * @param aLoadFlags Optional parameter specifying load flags to use for
    *        the image load
    */
   nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify,
-                     ImageLoadType aImageLoadType, nsIDocument* aDocument = nullptr,
+                     ImageLoadType aImageLoadType, bool aLoadStart = true,
+                     nsIDocument* aDocument = nullptr,
                      nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
 
   /**
    * helpers to get the document for this content (from the nodeinfo
    * and such).  Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous
    * method names in subclasses
    *
    * @return the document we belong to
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -18,16 +18,17 @@
 #include "nsPluginTags.h"
 #include "nsIObserverService.h"
 #include "nsIWeakReference.h"
 #include "mozilla/Services.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsContentUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsIDocument.h"
+#include "nsIBlocklistService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsPluginArray::nsPluginArray(nsPIDOMWindowInner* aWindow)
   : mWindow(aWindow)
 {
 }
@@ -355,17 +356,20 @@ nsPluginArray::EnsurePlugins()
   // need to wrap each of these with a nsPluginElement, which is
   // scriptable.
   for (uint32_t i = 0; i < pluginTags.Length(); ++i) {
     nsCOMPtr<nsPluginTag> pluginTag = do_QueryInterface(pluginTags[i]);
     if (!pluginTag) {
       mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
     } else if (pluginTag->IsActive()) {
       uint32_t permission = nsIPermissionManager::ALLOW_ACTION;
-      if (pluginTag->IsClicktoplay()) {
+      uint32_t blocklistState;
+      if (pluginTag->IsClicktoplay() &&
+          NS_SUCCEEDED(pluginTag->GetBlocklistState(&blocklistState)) &&
+          blocklistState == nsIBlocklistService::STATE_NOT_BLOCKED) {
         nsCString name;
         pluginTag->GetName(name);
         if (PluginShouldBeHidden(name)) {
           RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
           nsCString permString;
           nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString);
           if (rv == NS_OK) {
             nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal();
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4436,49 +4436,16 @@ CanvasRenderingContext2D::GetLineJoin(ns
     aLinejoinStyle.AssignLiteral("miter");
     break;
   default:
     aError.Throw(NS_ERROR_FAILURE);
   }
 }
 
 void
-CanvasRenderingContext2D::SetMozDash(JSContext* aCx,
-                                     const JS::Value& aMozDash,
-                                     ErrorResult& aError)
-{
-  nsTArray<Float> dash;
-  aError = JSValToDashArray(aCx, aMozDash, dash);
-  if (!aError.Failed()) {
-    ContextState& state = CurrentState();
-    state.dash = Move(dash);
-    if (state.dash.IsEmpty()) {
-      state.dashOffset = 0;
-    }
-  }
-}
-
-void
-CanvasRenderingContext2D::GetMozDash(JSContext* aCx,
-                                     JS::MutableHandle<JS::Value> aRetval,
-                                     ErrorResult& aError)
-{
-  DashArrayToJSVal(CurrentState().dash, aCx, aRetval, aError);
-}
-
-void
-CanvasRenderingContext2D::SetMozDashOffset(double aMozDashOffset)
-{
-  ContextState& state = CurrentState();
-  if (!state.dash.IsEmpty()) {
-    state.dashOffset = aMozDashOffset;
-  }
-}
-
-void
 CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments,
                                       ErrorResult& aRv)
 {
   nsTArray<mozilla::gfx::Float> dash;
 
   for (uint32_t x = 0; x < aSegments.Length(); x++) {
     if (aSegments[x] < 0.0) {
       // Pattern elements must be finite "numbers" >= 0, with "finite"
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -360,34 +360,24 @@ public:
   void GetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::MutableHandle<JSObject*> aResult,
                                      mozilla::ErrorResult& aError);
   void SetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::Handle<JSObject*> aCurrentTransform,
                                      mozilla::ErrorResult& aError);
   void GetFillRule(nsAString& aFillRule);
   void SetFillRule(const nsAString& aFillRule);
-  void GetMozDash(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
-                  mozilla::ErrorResult& aError);
-  void SetMozDash(JSContext* aCx, const JS::Value& aMozDash,
-                  mozilla::ErrorResult& aError);
 
   void SetLineDash(const Sequence<double>& aSegments,
                    mozilla::ErrorResult& aRv);
   void GetLineDash(nsTArray<double>& aSegments) const;
 
   void SetLineDashOffset(double aOffset);
   double LineDashOffset() const;
 
-  double MozDashOffset()
-  {
-    return CurrentState().dashOffset;
-  }
-  void SetMozDashOffset(double aMozDashOffset);
-
   void GetMozTextStyle(nsAString& aMozTextStyle)
   {
     GetFont(aMozTextStyle);
   }
 
   void SetMozTextStyle(const nsAString& aMozTextStyle,
                        mozilla::ErrorResult& aError)
   {
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -245,17 +245,16 @@ tags = imagebitmap
 [test_imagebitmap_structuredclone_iframe.html]
 tags = imagebitmap
 [test_imagebitmap_structuredclone_window.html]
 tags = imagebitmap
 [test_imagebitmap_transfer.html]
 tags = imagebitmap
 [test_ImageData_ctor.html]
 [test_isPointInStroke.html]
-[test_mozDashOffset.html]
 [test_mozGetAsFile.html]
 [test_strokeText_throw.html]
 [test_toBlob.html]
 [test_toDataURL_alpha.html]
 [test_toDataURL_lowercase_ascii.html]
 [test_toDataURL_parameters.html]
 [test_windingRuleUndefined.html]
 [test_2d.fillText.gradient.html]
--- a/dom/canvas/test/reftest/drawFocusIfNeeded-ref.html
+++ b/dom/canvas/test/reftest/drawFocusIfNeeded-ref.html
@@ -1,17 +1,17 @@
 <!--docytpe html-->
 <html><head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <meta charset="UTF-8">
 <script>
 window.onload=function(){
 var c=document.getElementById("myCanvas").getContext("2d");
 c.beginPath();
-c.mozDash = [1,1];
+c.setLineDash([1,1]);
 c.strokeRect(10, 10, 200, 200);
 }
 </script>
 </head>
 <body>
 <canvas id="myCanvas" height="500" width="500" style="border:1px solid black">
 </canvas>
 
deleted file mode 100644
--- a/dom/canvas/test/test_mozDashOffset.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for mozDashOffset</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display">
-<canvas id="c" width="100" height="100"><p class="fallback">FAIL (fallback content)</p></canvas>
-</p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script>
-try {
-  var canvas = document.getElementById('c');
-  var ctx = canvas.getContext('2d');
-
-  ctx.mozDash = [1, 1];
-  
-  ctx.mozDashOffset = 1;
-  ctx.mozDashOffset = Infinity;
-  ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1");
-
-  ctx.mozDashOffset = 1;
-  ctx.mozDashOffset = -Infinity;
-  ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1");
-
-  ctx.mozDashOffset = 1;
-  ctx.mozDashOffset = NaN;
-  ok(ctx.mozDashOffset === 1, "ctx.mozDashOffset === 1");
-} catch(e) {
-  ok(false, "unexpected exception thrown in: test_mozDashOffset.html");
-}
-</script>
-</pre>
-</body>
-</html>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -29,16 +29,19 @@
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "DeviceStorageStatics.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+#include "mozilla/a11y/AccessibleWrap.h"
+#endif
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
@@ -1949,16 +1952,20 @@ ContentParent::ActorDestroy(ActorDestroy
   }
 
   // Unregister all the BlobURLs registered by the ContentChild.
   for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
     nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
   }
 
   mBlobURLs.Clear();
+
+#if defined(XP_WIN32) && defined(ACCESSIBILITY)
+  a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
+#endif
 }
 
 void
 ContentParent::NotifyTabDestroying(const TabId& aTabId,
                                    const ContentParentId& aCpId)
 {
   if (XRE_IsParentProcess()) {
     // There can be more than one PBrowser for a given app process
@@ -5343,16 +5350,28 @@ ContentParent::RecvUnstoreAndBroadcastBl
   nsHostObjectProtocolHandler::RemoveDataEntry(aURI,
                                                false /* Don't broadcast */);
   BroadcastBlobURLUnregistration(aURI, this);
   mBlobURLs.RemoveElement(aURI);
 
   return true;
 }
 
+bool
+ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
+{
+#if defined(XP_WIN32) && defined(ACCESSIBILITY)
+  *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
+  MOZ_ASSERT(*aContentId);
+  return true;
+#else
+  return false;
+#endif
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -560,16 +560,19 @@ public:
   virtual bool
   RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
                                            PBlobParent* aBlobParent,
                                            const Principal& aPrincipal) override;
 
   virtual bool
   RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
 
+  virtual bool
+  RecvGetA11yContentId(uint32_t* aContentId) override;
+
   virtual int32_t Pid() const override;
 
   // Use the PHangMonitor channel to ask the child to repaint a tab.
   void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch);
 
 protected:
   void OnChannelConnected(int32_t pid) override;
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -741,23 +741,16 @@ child:
     /**
      * Sent by the chrome process when it no longer wants this remote
      * <browser>.  The child side cleans up in response, then
      * finalizing its death by sending back __delete__() to the
      * parent.
      */
     async Destroy();
 
-
-    /**
-     * Tell the child side if it has to update it's touchable region
-     * to the parent.
-     */
-    async SetUpdateHitRegion(bool aEnabled);
-
     /**
      * Update the child side docShell active (resource use) state.
      *
      * @param aIsActive
      *        Whether to activate or deactivate the docshell.
      * @param aPreserveLayers
      *        Whether layer trees should be preserved for inactive docshells.
      * @param aLayerObserverEpoch
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1231,16 +1231,18 @@ parent:
      async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
 
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistogram(Accumulation[] accumulations);
     async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
 
+     sync GetA11yContentId() returns (uint32_t aContentId);
+
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -530,30 +530,28 @@ TabChild::TabChild(nsIContentChild* aMan
   , mChromeFlags(aChromeFlags)
   , mActiveSuppressDisplayport(0)
   , mLayersId(0)
   , mAppPackageFileDescriptorRecved(false)
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
-  , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mRounding(0)
   , mDefaultScale(0)
   , mIsTransparent(false)
   , mIPCOpen(false)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
-  , mAPZChild(nullptr)
   , mLayerObserverEpoch(0)
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   , mNativeWindowHandle(0)
 #endif
 {
   // In the general case having the TabParent tell us if APZ is enabled or not
   // doesn't really work because the TabParent itself may not have a reference
   // to the owning widget during initialization. Instead we assume that this
@@ -2637,38 +2635,16 @@ TabChild::RecvDestroy()
   // that were just generated to have a chance to run.
   nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable));
 
   return true;
 }
 
 bool
-TabChild::RecvSetUpdateHitRegion(const bool& aEnabled)
-{
-    mUpdateHitRegion = aEnabled;
-
-    // We need to trigger a repaint of the child frame to ensure that it
-    // recomputes and sends its region.
-    if (!mUpdateHitRegion) {
-      return true;
-    }
-
-    nsCOMPtr<nsIDocument> document(GetDocument());
-    NS_ENSURE_TRUE(document, true);
-    nsCOMPtr<nsIPresShell> presShell = document->GetShell();
-    NS_ENSURE_TRUE(presShell, true);
-    RefPtr<nsPresContext> presContext = presShell->GetPresContext();
-    NS_ENSURE_TRUE(presContext, true);
-    presContext->InvalidatePaintedLayers();
-
-    return true;
-}
-
-bool
 TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
                                   const bool& aPreserveLayers,
                                   const uint64_t& aLayerObserverEpoch)
 {
   // Since SetDocShellIsActive requests come in from both the hang monitor
   // channel and the PContent channel, we have an ordering problem. This code
   // ensures that we respect the order in which the requests were made and
   // ignore stale requests.
@@ -3003,25 +2979,16 @@ TabChild::MakeHidden()
   // round-trip from CompositorBridgeChild to CompositorBridgeParent.
   compositor->RecvClearCachedResources(mLayersId);
 
   if (mPuppetWidget) {
     mPuppetWidget->Show(false);
   }
 }
 
-void
-TabChild::UpdateHitRegion(const nsRegion& aRegion)
-{
-  mRemoteFrame->SendUpdateHitRegion(aRegion);
-  if (mAPZChild) {
-    mAPZChild->SendUpdateHitRegion(aRegion);
-  }
-}
-
 NS_IMETHODIMP
 TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult)
 {
   if (mTabChildGlobal) {
     NS_ADDREF(*aResult = mTabChildGlobal);
     return NS_OK;
   }
   *aResult = nullptr;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -513,20 +513,16 @@ public:
                                nsICachedFileDescriptorListener* aCallback);
 
   void CancelCachedFileDescriptorCallback(
                                   const nsAString& aPath,
                                   nsICachedFileDescriptorListener* aCallback);
 
   nsIContentChild* Manager() const { return mManager; }
 
-  bool GetUpdateHitRegion() const { return mUpdateHitRegion; }
-
-  void UpdateHitRegion(const nsRegion& aRegion);
-
   static inline TabChild*
   GetFrom(nsIDocShell* aDocShell)
   {
     if (!aDocShell) {
       return nullptr;
     }
 
     nsCOMPtr<nsITabChild> tc = aDocShell->GetTabChild();
@@ -646,39 +642,32 @@ public:
                             const layers::GeckoContentController::APZStateChange& aChange,
                             const int& aArg);
   void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
   void ZoomToRect(const uint32_t& aPresShellId,
                   const FrameMetrics::ViewID& aViewId,
                   const CSSRect& aRect,
                   const uint32_t& aFlags);
 
-  void SetAPZChild(layers::APZChild* aAPZChild)
-  {
-      mAPZChild = aAPZChild;
-  }
-
   // Request that the docshell be marked as active.
   void ForcePaint(uint64_t aLayerObserverEpoch);
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
 #endif
 
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual bool RecvDestroy() override;
 
-  virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
-
   virtual bool RecvSetDocShellIsActive(const bool& aIsActive,
                                        const bool& aIsHidden,
                                        const uint64_t& aLayerObserverEpoch) override;
 
   virtual bool RecvNavigateByKey(const bool& aForward,
                                  const bool& aForDocumentNavigation) override;
 
   virtual bool RecvRequestNotifyAfterRemotePaint() override;
@@ -760,17 +749,16 @@ private:
   // At present only 1 of these is really expected.
   AutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1>
       mCachedFileDescriptorInfos;
   nscolor mLastBackgroundColor;
   bool mDidFakeShow;
   bool mNotified;
   bool mTriedBrowserInit;
   ScreenOrientationInternal mOrientation;
-  bool mUpdateHitRegion;
 
   bool mIgnoreKeyPressEvent;
   RefPtr<APZEventState> mAPZEventState;
   SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
   bool mHasValidInnerSize;
   bool mDestroyed;
   // Position of client area relative to the outer window
   LayoutDeviceIntPoint mClientOffset;
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -148,22 +148,16 @@ interface CanvasRenderingContext2D {
 
   // Mozilla-specific stuff
   // FIXME Bug 768048 mozCurrentTransform/mozCurrentTransformInverse should return a WebIDL array.
   [Throws]
   attribute object mozCurrentTransform; // [ m11, m12, m21, m22, dx, dy ], i.e. row major
   [Throws]
   attribute object mozCurrentTransformInverse;
 
-  [Throws]
-  attribute any mozDash; /* default |null| */
-
-  [LenientFloat]
-  attribute double mozDashOffset; /* default 0.0 */
-
   [SetterThrows]
   attribute DOMString mozTextStyle;
 
   // image smoothing mode -- if disabled, images won't be smoothed
   // if scaled.
   [Deprecated="PrefixedImageSmoothingEnabled"]
   attribute boolean mozImageSmoothingEnabled;
 
--- a/gfx/ipc/GraphicsMessages.ipdlh
+++ b/gfx/ipc/GraphicsMessages.ipdlh
@@ -14,16 +14,17 @@ using gfxImageFormat from "mozilla/gfx/T
 
 namespace mozilla {
 namespace gfx {
 
 struct D3D11DeviceStatus
 {
   bool isWARP;
   bool textureSharingWorks;
+  bool alphaTextureSharingWorks;
   uint32_t featureLevel;
   DxgiAdapterDesc adapter;
 };
 
 struct DevicePrefs
 {
   FeatureStatus hwCompositing;
   FeatureStatus d3d11Compositing;
--- a/gfx/layers/IMFYCbCrImage.cpp
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -232,16 +232,20 @@ IMFYCbCrImage::GetTextureClient(KnowsCom
   if (!device || backend != LayersBackend::LAYERS_D3D11) {
     if (backend == LayersBackend::LAYERS_D3D9 ||
         backend == LayersBackend::LAYERS_D3D11) {
       return GetD3D9TextureClient(aForwarder);
     }
     return nullptr;
   }
 
+  if (!gfx::DeviceManagerDx::Get()->AlphaTextureSharingWorks()) {
+    return nullptr;
+  }
+
   if (mData.mYStride < 0 || mData.mCbCrStride < 0) {
     // D3D11 only supports unsigned stride values.
     return nullptr;
   }
 
   CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM,
                                 mData.mYSize.width, mData.mYSize.height, 1, 1);
 
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -102,33 +102,16 @@ public:
    */
   virtual bool IsRepaintThread() = 0;
 
   /**
    * Runs the given task on the "repaint" thread.
    */
   virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) = 0;
 
-  /**
-   * APZ uses |FrameMetrics::mCompositionBounds| for hit testing. Sometimes,
-   * widget code has knowledge of a touch-sensitive region that should
-   * additionally constrain hit testing for all frames associated with the
-   * controller. This method allows APZ to query the controller for such a
-   * region. A return value of true indicates that the controller has such a
-   * region, and it is returned in |aOutRegion|.
-   * This method needs to be called on the main thread.
-   * TODO: once bug 928833 is implemented, this should be removed, as
-   * APZ can then get the correct touch-sensitive region for each frame
-   * directly from the layer.
-   */
-  virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion)
-  {
-    return false;
-  }
-
   enum class APZStateChange {
     /**
      * APZ started modifying the view (including panning, zooming, and fling).
      */
     eTransformBegin,
     /**
      * APZ finished modifying the view.
      */
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -271,40 +271,16 @@ ComputeClipRegion(GeckoContentController
   } else {
     // if there is no clip on this layer (which should only happen for the
     // root scrollable layer in a process, or for some of the LayerMetrics
     // expansions of a multi-metrics layer), fall back to using the comp
     // bounds which should be equivalent.
     clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
   }
 
-  // Optionally, the GeckoContentController can provide a touch-sensitive
-  // region that constrains all frames associated with the controller.
-  // In this case we intersect the composition bounds with that region.
-  CSSRect touchSensitiveRegion;
-  if (aController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
-    // Here we assume 'touchSensitiveRegion' is in the CSS pixels of the
-    // parent frame. To convert it to ParentLayer pixels, we therefore need
-    // the cumulative resolution of the parent frame. We approximate this as
-    // the quotient of our cumulative resolution and our pres shell resolution;
-    // this approximation may not be accurate in the presence of a css-driven
-    // resolution.
-    LayoutDeviceToParentLayerScale2D parentCumulativeResolution =
-          aLayer.Metrics().GetCumulativeResolution()
-        / ParentLayerToLayerScale(aLayer.Metrics().GetPresShellResolution());
-    // Not sure what rounding option is the most correct here, but if we ever
-    // figure it out we can change this. For now I'm rounding in to minimize
-    // the chances of getting a complex region.
-    ParentLayerIntRegion extraClip = RoundedIn(
-        touchSensitiveRegion
-        * aLayer.Metrics().GetDevPixelsPerCSSPixel()
-        * parentCumulativeResolution);
-    clipRegion.AndWith(extraClip);
-  }
-
   return clipRegion;
 }
 
 void
 APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
                                const AsyncPanZoomController* apzc)
 {
   const FrameMetrics& metrics = aLayer.Metrics();
--- a/gfx/layers/apz/util/ContentProcessController.cpp
+++ b/gfx/layers/apz/util/ContentProcessController.cpp
@@ -94,18 +94,16 @@ ContentProcessController::ContentProcess
     : mBrowser(nullptr)
 {
 }
 ContentProcessController::~ContentProcessController()
 {
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     os->RemoveObserver(mObserver, "tab-child-created");
-  } else if (mBrowser) {
-    mBrowser->SetAPZChild(nullptr);
   }
 }
 
 void
 ContentProcessController::SetObserver(nsIObserver* aObserver)
 {
   MOZ_ASSERT(!mBrowser);
   mObserver = aObserver;
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -41,18 +41,16 @@ namespace layers {
  * must be updated to handle it.
  */
 sync protocol PAPZ
 {
   manager PCompositorBridge;
 
 parent:
 
-  async UpdateHitRegion(nsRegion aRegion);
-
   async __delete__();
 
 child:
 
   async RequestContentRepaint(FrameMetrics frame);
 
   async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent);
 
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -25,17 +25,16 @@
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 RemoteContentController::RemoteContentController()
   : mCompositorThread(MessageLoop::current())
   , mCanSend(true)
-  , mMutex("RemoteContentController")
 {
 }
 
 RemoteContentController::~RemoteContentController()
 {
 }
 
 void
@@ -45,19 +44,19 @@ RemoteContentController::RequestContentR
 
   if (mCanSend) {
     Unused << SendRequestContentRepaint(aFrameMetrics);
   }
 }
 
 void
 RemoteContentController::HandleTapOnMainThread(TapType aTapType,
-                                               const LayoutDevicePoint& aPoint,
+                                               LayoutDevicePoint aPoint,
                                                Modifiers aModifiers,
-                                               const ScrollableLayerGuid& aGuid,
+                                               ScrollableLayerGuid aGuid,
                                                uint64_t aInputBlockId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId);
   if (tab) {
     tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
   }
@@ -88,17 +87,17 @@ RemoteContentController::HandleTap(TapTy
 
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (NS_IsMainThread()) {
     HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
   } else {
     // We don't want to get the TabParent or call TabParent::SendHandleTap() from a non-main thread (this might happen
     // on Android, where this is called from the Java UI thread)
-    NS_DispatchToMainThread(NewRunnableMethod<TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t>
+    NS_DispatchToMainThread(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, ScrollableLayerGuid, uint64_t>
         (this, &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, aModifiers, aGuid, aInputBlockId));
   }
 }
 
 void
 RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
                                             const ScrollableLayerGuid& aGuid,
                                             LayoutDeviceCoord aSpanChange,
@@ -155,28 +154,16 @@ RemoteContentController::IsRepaintThread
 }
 
 void
 RemoteContentController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
 {
   mCompositorThread->PostTask(Move(aTask));
 }
 
-bool
-RemoteContentController::GetTouchSensitiveRegion(CSSRect* aOutRegion)
-{
-  MutexAutoLock lock(mMutex);
-  if (mTouchSensitiveRegion.IsEmpty()) {
-    return false;
-  }
-
-  *aOutRegion = CSSRect::FromAppUnits(mTouchSensitiveRegion.GetBounds());
-  return true;
-}
-
 void
 RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                               APZStateChange aChange,
                                               int aArg)
 {
   if (MessageLoop::current() != mCompositorThread) {
     // We have to send messages from the compositor thread
     mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid,
@@ -259,24 +246,16 @@ RemoteContentController::NotifyFlushComp
 {
   MOZ_ASSERT(IsRepaintThread());
 
   if (mCanSend) {
     Unused << SendNotifyFlushComplete();
   }
 }
 
-bool
-RemoteContentController::RecvUpdateHitRegion(const nsRegion& aRegion)
-{
-  MutexAutoLock lock(mMutex);
-  mTouchSensitiveRegion = aRegion;
-  return true;
-}
-
 void
 RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
 {
   // This controller could possibly be kept alive longer after this
   // by a RefPtr, but it is no longer valid to send messages.
   mCanSend = false;
 }
 
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -54,51 +54,43 @@ public:
                                   Modifiers aModifiers) override;
 
   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
 
   virtual bool IsRepaintThread() override;
 
   virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
 
-  virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override;
-
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
 
   virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override;
 
   virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override;
 
   virtual void SetScrollingRootContent(bool aIsRootContent) override;
 
   virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
                                          const nsString& aEvent) override;
 
   virtual void NotifyFlushComplete() override;
 
-  virtual bool RecvUpdateHitRegion(const nsRegion& aRegion) override;
-
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual void Destroy() override;
 
 private:
   MessageLoop* mCompositorThread;
   bool mCanSend;
 
   void HandleTapOnMainThread(TapType aType,
-                             const LayoutDevicePoint& aPoint,
+                             LayoutDevicePoint aPoint,
                              Modifiers aModifiers,
-                             const ScrollableLayerGuid& aGuid,
+                             ScrollableLayerGuid aGuid,
                              uint64_t aInputBlockId);
-
-  // Mutex protecting members below accessed from multiple threads.
-  mozilla::Mutex mMutex;
-  nsRegion mTouchSensitiveRegion;
 };
 
 } // namespace layers
 
 } // namespace mozilla
 
 #endif // mozilla_layers_RemoteContentController_h
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -323,16 +323,17 @@ DeviceManagerDx::CreateCompositorDevice(
     }
   } else {
     mCompositorDeviceSupportsVideo = true;
   }
 
   // Only test this when not using WARP since it can fail and cause
   // GetDeviceRemovedReason to return weird values.
   bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+  bool alphaTextureSharingWorks = D3D11Checks::DoesAlphaTextureSharingWork(device);
 
   DXGI_ADAPTER_DESC desc;
   PodZero(&desc);
   adapter->GetDesc(&desc);
 
   if (!textureSharingWorks) {
     gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
                          FeatureStatus::Broken,
@@ -350,16 +351,17 @@ DeviceManagerDx::CreateCompositorDevice(
 
   int featureLevel = device->GetFeatureLevel();
   {
     MutexAutoLock lock(mDeviceLock);
     mCompositorDevice = device;
     mDeviceStatus = Some(D3D11DeviceStatus(
       false,
       textureSharingWorks,
+      alphaTextureSharingWorks,
       featureLevel,
       DxgiAdapterDesc::From(desc)));
   }
   mCompositorDevice->SetExceptionMode(0);
 }
 
 bool
 DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
@@ -404,30 +406,33 @@ DeviceManagerDx::CreateWARPCompositorDev
     d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device",
                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
     return;
   }
 
   // Only test for texture sharing on Windows 8 since it puts the device into
   // an unusable state if used on Windows 7
   bool textureSharingWorks = false;
+  bool alphaTextureSharingWorks = false;
   if (IsWin8OrLater()) {
     textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+    alphaTextureSharingWorks = D3D11Checks::DoesAlphaTextureSharingWork(device);
   }
 
   DxgiAdapterDesc nullAdapter;
   PodZero(&nullAdapter);
 
   int featureLevel = device->GetFeatureLevel();
   {
     MutexAutoLock lock(mDeviceLock);
     mCompositorDevice = device;
     mDeviceStatus = Some(D3D11DeviceStatus(
       true,
       textureSharingWorks,
+      alphaTextureSharingWorks,
       featureLevel,
       nullAdapter));
   }
   mCompositorDevice->SetExceptionMode(0);
 
   reporterWARP.SetSuccessful();
 }
 
@@ -666,16 +671,26 @@ DeviceManagerDx::TextureSharingWorks()
   MutexAutoLock lock(mDeviceLock);
   if (!mDeviceStatus) {
     return false;
   }
   return mDeviceStatus->textureSharingWorks();
 }
 
 bool
+DeviceManagerDx::AlphaTextureSharingWorks()
+{
+  MutexAutoLock lock(mDeviceLock);
+  if (!mDeviceStatus) {
+    return false;
+  }
+  return mDeviceStatus->alphaTextureSharingWorks();
+}
+
+bool
 DeviceManagerDx::IsWARP()
 {
   MutexAutoLock lock(mDeviceLock);
   if (!mDeviceStatus) {
     return false;
   }
   return mDeviceStatus->isWARP();
 }
--- a/gfx/thebes/DeviceManagerDx.h
+++ b/gfx/thebes/DeviceManagerDx.h
@@ -53,16 +53,17 @@ public:
 
   RefPtr<ID3D11Device> GetCompositorDevice();
   RefPtr<ID3D11Device> GetContentDevice();
   RefPtr<ID3D11Device> CreateDecoderDevice();
   IDirectDraw7* GetDirectDraw();
 
   unsigned GetCompositorFeatureLevel() const;
   bool TextureSharingWorks();
+  bool AlphaTextureSharingWorks();
   bool IsWARP();
 
   bool CreateCompositorDevices();
   void CreateContentDevices();
 
   void ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus);
   void ExportDeviceInfo(D3D11DeviceStatus* aOut);
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -19,16 +19,17 @@
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/InlinableNatives.h"
+#include "js/GCAPI.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
@@ -168,35 +169,36 @@ js::ToSimdConstant(JSContext* cx, Handle
 {
     typedef typename V::Elem Elem;
     Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
     if (!typeDescr)
         return false;
     if (!IsVectorObject<V>(v))
         return ErrorWrongTypeArg(cx, 1, typeDescr);
 
-    Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem());
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem(nogc));
     *out = jit::SimdConstant::CreateSimd128(mem);
     return true;
 }
 
 template bool js::ToSimdConstant<Int8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Int16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 
 template<typename Elem>
 static Elem
-TypedObjectMemory(HandleValue v)
+TypedObjectMemory(HandleValue v, const JS::AutoAssertOnGC& nogc)
 {
     TypedObject& obj = v.toObject().as<TypedObject>();
-    return reinterpret_cast<Elem>(obj.typedMem());
+    return reinterpret_cast<Elem>(obj.typedMem(nogc));
 }
 
 static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
@@ -417,17 +419,21 @@ template <typename T>
 static bool
 FillLanes(JSContext* cx, Handle<TypedObject*> result, const CallArgs& args)
 {
     typedef typename T::Elem Elem;
     Elem tmp;
     for (unsigned i = 0; i < T::lanes; i++) {
         if (!T::Cast(cx, args.get(i), &tmp))
             return false;
-        reinterpret_cast<Elem*>(result->typedMem())[i] = tmp;
+        // Reassure typedMem() that we won't GC while holding onto the returned
+        // pointer, even though we could GC on every iteration of this loop
+        // (but it is safe because we re-fetch each time.)
+        JS::AutoCheckCannotGC nogc(cx);
+        reinterpret_cast<Elem*>(result->typedMem(nogc))[i] = tmp;
     }
     args.rval().setObject(*result);
     return true;
 }
 
 bool
 SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -634,17 +640,18 @@ js::CreateSimd(JSContext* cx, const type
     Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
     if (!typeDescr)
         return nullptr;
 
     Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
     if (!result)
         return nullptr;
 
-    Elem* resultMem = reinterpret_cast<Elem*>(result->typedMem());
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* resultMem = reinterpret_cast<Elem*>(result->typedMem(nogc));
     memcpy(resultMem, data, sizeof(Elem) * V::lanes);
     return result;
 }
 
 #define InstantiateCreateSimd_(Type) \
     template JSObject* js::CreateSimd<Type>(JSContext* cx, const Type::Elem* data);
 
 FOR_EACH_SIMD(InstantiateCreateSimd_)
@@ -830,31 +837,60 @@ StoreResult(JSContext* cx, CallArgs& arg
 {
     RootedObject obj(cx, CreateSimd<Out>(cx, result));
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
+// StoreResult can GC, and it is commonly used after pulling something out of a
+// TypedObject:
+//
+//   Elem result = op(TypedObjectMemory<Elem>(args[0]));
+//   StoreResult<Out>(..., result);
+//
+// The pointer extracted from the typed object in args[0] in the above example
+// could be an interior pointer, and therefore be invalidated by GC.
+// TypedObjectMemory() requires an assertion token to be passed in to prove
+// that we won't GC, but the scope of eg an AutoCheckCannotGC RAII object
+// extends to the end of its containing scope -- which would include the call
+// to StoreResult, resulting in a rooting hazard.
+//
+// TypedObjectElemArray fixes this by wrapping the problematic pointer in a
+// type, and the analysis is able to see that it is dead before calling
+// StoreResult. (But if another GC called is made before the pointer is dead,
+// it will correctly report a hazard.)
+//
+template <typename Elem>
+class TypedObjectElemArray {
+    Elem* elements;
+  public:
+    explicit TypedObjectElemArray(HandleValue objVal) {
+        JS::AutoCheckCannotGC nogc;
+        elements = TypedObjectMemory<Elem*>(objVal, nogc);
+    }
+    Elem& operator[](int i) { return elements[i]; }
+} JS_HAZ_GC_POINTER;
+
 // Coerces the inputs of type In to the type Coercion, apply the operator Op
 // and converts the result to the type Out.
 template<typename In, typename Coercion, template<typename C> class Op, typename Out>
 static bool
 CoercedUnaryFunc(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename Coercion::Elem CoercionElem;
     typedef typename Out::Elem RetElem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !IsVectorObject<In>(args[0]))
         return ErrorBadArgs(cx);
 
     CoercionElem result[Coercion::lanes];
-    CoercionElem* val = TypedObjectMemory<CoercionElem*>(args[0]);
+    TypedObjectElemArray<CoercionElem> val(args[0]);
     for (unsigned i = 0; i < Coercion::lanes; i++)
         result[i] = Op<CoercionElem>::apply(val[i]);
     return StoreResult<Out>(cx, args, (RetElem*) result);
 }
 
 // Coerces the inputs of type In to the type Coercion, apply the operator Op
 // and converts the result to the type Out.
 template<typename In, typename Coercion, template<typename C> class Op, typename Out>
@@ -864,18 +900,18 @@ CoercedBinaryFunc(JSContext* cx, unsigne
     typedef typename Coercion::Elem CoercionElem;
     typedef typename Out::Elem RetElem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
         return ErrorBadArgs(cx);
 
     CoercionElem result[Coercion::lanes];
-    CoercionElem* left = TypedObjectMemory<CoercionElem*>(args[0]);
-    CoercionElem* right = TypedObjectMemory<CoercionElem*>(args[1]);
+    TypedObjectElemArray<CoercionElem> left(args[0]);
+    TypedObjectElemArray<CoercionElem> right(args[1]);
     for (unsigned i = 0; i < Coercion::lanes; i++)
         result[i] = Op<CoercionElem>::apply(left[i], right[i]);
     return StoreResult<Out>(cx, args, (RetElem*) result);
 }
 
 // Same as above, with no coercion, i.e. Coercion == In.
 template<typename In, template<typename C> class Op, typename Out>
 static bool
@@ -900,33 +936,35 @@ ExtractLane(JSContext* cx, unsigned argc
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 2 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     unsigned lane;
     if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane))
         return false;
 
-    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
     Elem val = vec[lane];
     args.rval().set(V::ToValue(val));
     return true;
 }
 
 template<typename V>
 static bool
 AllTrue(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
     bool allTrue = true;
     for (unsigned i = 0; allTrue && i < V::lanes; i++)
         allTrue = vec[i];
 
     args.rval().setBoolean(allTrue);
     return true;
 }
 
@@ -935,17 +973,18 @@ static bool
 AnyTrue(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
     bool anyTrue = false;
     for (unsigned i = 0; !anyTrue && i < V::lanes; i++)
         anyTrue = vec[i];
 
     args.rval().setBoolean(anyTrue);
     return true;
 }
 
@@ -963,17 +1002,17 @@ ReplaceLane(JSContext* cx, unsigned argc
     unsigned lane;
     if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane))
         return false;
 
     Elem value;
     if (!V::Cast(cx, args.get(2), &value))
         return false;
 
-    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
+    TypedObjectElemArray<Elem> vec(args[0]);
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = i == lane ? value : vec[i];
 
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename V>
@@ -987,18 +1026,17 @@ Swizzle(JSContext* cx, unsigned argc, Va
         return ErrorBadArgs(cx);
 
     unsigned lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         if (!ArgumentToLaneIndex(cx, args[i + 1], V::lanes, &lanes[i]))
             return false;
     }
 
-    Elem* val = TypedObjectMemory<Elem*>(args[0]);
-
+    TypedObjectElemArray<Elem> val(args[0]);
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = val[lanes[i]];
 
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename V>
@@ -1012,23 +1050,26 @@ Shuffle(JSContext* cx, unsigned argc, Va
         return ErrorBadArgs(cx);
 
     unsigned lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         if (!ArgumentToLaneIndex(cx, args[i + 2], 2 * V::lanes, &lanes[i]))
             return false;
     }
 
-    Elem* lhs = TypedObjectMemory<Elem*>(args[0]);
-    Elem* rhs = TypedObjectMemory<Elem*>(args[1]);
+    Elem result[V::lanes];
+    {
+        JS::AutoCheckCannotGC nogc(cx);
+        Elem* lhs = TypedObjectMemory<Elem*>(args[0], nogc);
+        Elem* rhs = TypedObjectMemory<Elem*>(args[1], nogc);
 
-    Elem result[V::lanes];
-    for (unsigned i = 0; i < V::lanes; i++) {
-        Elem* selectedInput = lanes[i] < V::lanes ? lhs : rhs;
-        result[i] = selectedInput[lanes[i] % V::lanes];
+        for (unsigned i = 0; i < V::lanes; i++) {
+            Elem* selectedInput = lanes[i] < V::lanes ? lhs : rhs;
+            result[i] = selectedInput[lanes[i] % V::lanes];
+        }
     }
 
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename V, template<typename T> class Op>
 static bool
 BinaryScalar(JSContext* cx, unsigned argc, Value* vp)
@@ -1041,18 +1082,18 @@ BinaryScalar(JSContext* cx, unsigned arg
 
     if (!IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     int32_t bits;
     if (!ToInt32(cx, args[1], &bits))
         return false;
 
+    TypedObjectElemArray<Elem> val(args[0]);
     Elem result[V::lanes];
-    Elem* val = TypedObjectMemory<Elem*>(args[0]);
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = Op<Elem>::apply(val[i], bits);
 
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename In, template<typename C> class Op, typename Out>
 static bool
@@ -1061,18 +1102,18 @@ CompareFunc(JSContext* cx, unsigned argc
     typedef typename In::Elem InElem;
     typedef typename Out::Elem OutElem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
         return ErrorBadArgs(cx);
 
     OutElem result[Out::lanes];
-    InElem* left = TypedObjectMemory<InElem*>(args[0]);
-    InElem* right = TypedObjectMemory<InElem*>(args[1]);
+    TypedObjectElemArray<InElem> left(args[0]);
+    TypedObjectElemArray<InElem> right(args[1]);
     for (unsigned i = 0; i < Out::lanes; i++) {
         unsigned j = (i * In::lanes) / Out::lanes;
         result[i] = Op<InElem>::apply(left[j], right[j]) ? -1 : 0;
     }
 
     return StoreResult<Out>(cx, args, result);
 }
 
@@ -1156,18 +1197,17 @@ FuncConvert(JSContext* cx, unsigned argc
     static_assert(V::lanes == Vret::lanes, "Can only convert from same number of lanes");
     static_assert(!mozilla::IsIntegral<Elem>::value || !mozilla::IsIntegral<RetElem>::value,
                   "Cannot convert between integer SIMD types");
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    Elem* val = TypedObjectMemory<Elem*>(args[0]);
-
+    TypedObjectElemArray<Elem> val(args[0]);
     RetElem result[Vret::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         if (ThrowOnConvert<Elem, RetElem>::value(val[i])) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION);
             return false;
         }
         result[i] = ConvertScalar<RetElem>(val[i]);
     }
@@ -1190,17 +1230,20 @@ FuncConvertBits(JSContext* cx, unsigned 
     if (args.length() != 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     // While we could just pass the typedMem of args[0] as StoreResults' last
     // argument, a GC could move the pointer to its memory in the meanwhile.
     // For consistency with other SIMD functions, simply copy the input in a
     // temporary array.
     RetElem copy[Vret::lanes];
-    memcpy(copy, TypedObjectMemory<RetElem*>(args[0]), Vret::lanes * sizeof(RetElem));
+    {
+        JS::AutoCheckCannotGC nogc(cx);
+        memcpy(copy, TypedObjectMemory<RetElem*>(args[0], nogc), Vret::lanes * sizeof(RetElem));
+    }
     return StoreResult<Vret>(cx, args, copy);
 }
 
 template<typename Vret>
 static bool
 FuncSplat(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename Vret::Elem RetElem;
@@ -1239,19 +1282,19 @@ SelectBits(JSContext* cx, unsigned argc,
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) ||
         !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2]))
     {
         return ErrorBadArgs(cx);
     }
 
-    MaskTypeElem* val = TypedObjectMemory<MaskTypeElem*>(args[0]);
-    MaskTypeElem* tv = TypedObjectMemory<MaskTypeElem*>(args[1]);
-    MaskTypeElem* fv = TypedObjectMemory<MaskTypeElem*>(args[2]);
+    TypedObjectElemArray<MaskTypeElem> val(args[0]);
+    TypedObjectElemArray<MaskTypeElem> tv(args[1]);
+    TypedObjectElemArray<MaskTypeElem> fv(args[2]);
 
     MaskTypeElem tr[MaskType::lanes];
     for (unsigned i = 0; i < MaskType::lanes; i++)
         tr[i] = And<MaskTypeElem>::apply(val[i], tv[i]);
 
     MaskTypeElem fr[MaskType::lanes];
     for (unsigned i = 0; i < MaskType::lanes; i++)
         fr[i] = And<MaskTypeElem>::apply(Not<MaskTypeElem>::apply(val[i]), fv[i]);
@@ -1273,19 +1316,19 @@ Select(JSContext* cx, unsigned argc, Val
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) ||
         !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2]))
     {
         return ErrorBadArgs(cx);
     }
 
-    MaskTypeElem* mask = TypedObjectMemory<MaskTypeElem*>(args[0]);
-    Elem* tv = TypedObjectMemory<Elem*>(args[1]);
-    Elem* fv = TypedObjectMemory<Elem*>(args[2]);
+    TypedObjectElemArray<MaskTypeElem> mask(args[0]);
+    TypedObjectElemArray<Elem> tv(args[1]);
+    TypedObjectElemArray<Elem> fv(args[2]);
 
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = mask[i] ? tv[i] : fv[i];
 
     return StoreResult<V>(cx, args, result);
 }
 
@@ -1356,19 +1399,20 @@ Load(JSContext* cx, unsigned argc, Value
     Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
     if (!typeDescr)
         return false;
 
     Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
     if (!result)
         return false;
 
+    JS::AutoCheckCannotGC nogc(cx);
     SharedMem<Elem*> src =
         typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>();
-    Elem* dst = reinterpret_cast<Elem*>(result->typedMem());
+    Elem* dst = reinterpret_cast<Elem*>(result->typedMem(nogc));
     jit::AtomicOperations::podCopySafeWhenRacy(SharedMem<Elem*>::unshared(dst), src, NumElem);
 
     args.rval().setObject(*result);
     return true;
 }
 
 template<class V, unsigned NumElem>
 static bool
@@ -1383,17 +1427,18 @@ Store(JSContext* cx, unsigned argc, Valu
     size_t byteStart;
     RootedObject typedArray(cx);
     if (!TypedArrayFromArgs(cx, args, sizeof(Elem) * NumElem, &typedArray, &byteStart))
         return false;
 
     if (!IsVectorObject<V>(args[2]))
         return ErrorBadArgs(cx);
 
-    Elem* src = TypedObjectMemory<Elem*>(args[2]);
+    JS::AutoCheckCannotGC nogc(cx);
+    Elem* src = TypedObjectMemory<Elem*>(args[2], nogc);
     SharedMem<Elem*> dst =
         typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>();
     js::jit::AtomicOperations::podCopySafeWhenRacy(dst, SharedMem<Elem*>::unshared(src), NumElem);
 
     args.rval().setObject(args[2].toObject());
     return true;
 }
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1506,17 +1506,18 @@ OutlineTypedObject::attach(JSContext* cx
         owner = &typedObj.as<OutlineTypedObject>().owner();
         offset += typedObj.offset();
     }
 
     if (owner->is<ArrayBufferObject>()) {
         attach(cx, owner->as<ArrayBufferObject>(), offset);
     } else {
         MOZ_ASSERT(owner->is<InlineTypedObject>());
-        setOwnerAndData(owner, owner->as<InlineTypedObject>().inlineTypedMem() + offset);
+        JS::AutoCheckCannotGC nogc(cx);
+        setOwnerAndData(owner, owner->as<InlineTypedObject>().inlineTypedMem(nogc) + offset);
     }
 }
 
 // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
 // the type `type`.
 static int32_t
 TypedObjLengthFromType(TypeDescr& descr)
 {
@@ -1559,17 +1560,18 @@ TypedObject::createZeroed(JSContext* cx,
 {
     // If possible, create an object with inline data.
     if ((size_t) descr->size() <= InlineTypedObject::MaximumSize) {
         AutoSetNewObjectMetadata metadata(cx);
 
         InlineTypedObject* obj = InlineTypedObject::create(cx, descr, heap);
         if (!obj)
             return nullptr;
-        descr->initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
+        JS::AutoCheckCannotGC nogc(cx);
+        descr->initInstances(cx->runtime(), obj->inlineTypedMem(nogc), 1);
         return obj;
     }
 
     // Create unattached wrapper object.
     Rooted<OutlineTypedObject*> obj(cx, OutlineTypedObject::createUnattached(cx, descr, length, heap));
     if (!obj)
         return nullptr;
 
@@ -2450,17 +2452,17 @@ js::SetTypedObjectOffset(JSContext*, uns
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
     MOZ_ASSERT(args[1].isInt32());
 
     OutlineTypedObject& typedObj = args[0].toObject().as<OutlineTypedObject>();
     int32_t offset = args[1].toInt32();
 
     MOZ_ASSERT(typedObj.isAttached());
-    typedObj.setData(typedObj.typedMemBase() + offset);
+    typedObj.resetOffset(offset);
     args.rval().setUndefined();
     return true;
 }
 
 bool
 js::ObjectIsTypeDescr(JSContext*, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -2574,31 +2576,32 @@ js::GetSimdTypeDescr(JSContext* cx, unsi
     MOZ_ASSERT(global);
     auto* obj = GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType(simdTypeRepr));
     args.rval().setObject(*obj);
     return true;
 }
 
 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                         \
 bool                                                                            \
-js::StoreScalar##T::Func(JSContext*, unsigned argc, Value* vp)         \
+js::StoreScalar##T::Func(JSContext* cx, unsigned argc, Value* vp)               \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     MOZ_ASSERT(args.length() == 3);                                             \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());     \
     MOZ_ASSERT(args[1].isInt32());                                              \
     MOZ_ASSERT(args[2].isNumber());                                             \
                                                                                 \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                   \
                                                                                 \
-    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
+    JS::AutoCheckCannotGC nogc(cx);                                             \
+    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));          \
     double d = args[2].toNumber();                                              \
     *target = ConvertScalar<T>(d);                                              \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }
 
 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                      \
 bool                                                                            \
@@ -2615,59 +2618,62 @@ js::StoreReference##_name::Func(JSContex
                                                                                 \
     jsid id = args[2].isString()                                                \
               ? IdToTypeId(AtomToId(&args[2].toString()->asAtom()))             \
               : JSID_VOID;                                                      \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                   \
                                                                                 \
-    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
+    JS::AutoCheckCannotGC nogc(cx);                                             \
+    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));          \
     if (!store(cx, target, args[3], &typedObj, id))                             \
         return false;                                                           \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }
 
 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                                  \
 bool                                                                                    \
-js::LoadScalar##T::Func(JSContext*, unsigned argc, Value* vp)                           \
+js::LoadScalar##T::Func(JSContext* cx, unsigned argc, Value* vp)                        \
 {                                                                                       \
     CallArgs args = CallArgsFromVp(argc, vp);                                           \
     MOZ_ASSERT(args.length() == 2);                                                     \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());             \
     MOZ_ASSERT(args[1].isInt32());                                                      \
                                                                                         \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();                       \
     int32_t offset = args[1].toInt32();                                                 \
                                                                                         \
     /* Should be guaranteed by the typed objects API: */                                \
     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                           \
                                                                                         \
-    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset));                        \
+    JS::AutoCheckCannotGC nogc(cx);                                                     \
+    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));                  \
     args.rval().setNumber((double) *target);                                            \
     return true;                                                                        \
 }
 
 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                       \
 bool                                                                            \
-js::LoadReference##_name::Func(JSContext*, unsigned argc, Value* vp)            \
+js::LoadReference##_name::Func(JSContext* cx, unsigned argc, Value* vp)         \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     MOZ_ASSERT(args.length() == 2);                                             \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());     \
     MOZ_ASSERT(args[1].isInt32());                                              \
                                                                                 \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
                                                                                 \
     /* Should be guaranteed by the typed objects API: */                        \
     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                                   \
                                                                                 \
-    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset));                \
+    JS::AutoCheckCannotGC nogc(cx);                                             \
+    T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));          \
     load(target, args.rval());                                                  \
     return true;                                                                \
 }
 
 // Because the precise syntax for storing values/objects/strings
 // differs, we abstract it away using specialized variants of the
 // private methods `store()` and `load()`.
 
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -528,44 +528,47 @@ class TypedObject : public ShapedObject
                                                           MutableHandle<PropertyDescriptor> desc);
 
     static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                                 ObjectOpResult& result);
 
     static MOZ_MUST_USE bool obj_enumerate(JSContext* cx, HandleObject obj,
                                            AutoIdVector& properties, bool enumerableOnly);
 
+
+    uint8_t* typedMem() const;
+    uint8_t* typedMemBase() const;
+
   public:
     TypedProto& typedProto() const {
         // Typed objects' prototypes can't be modified.
         return staticPrototype()->as<TypedProto>();
     }
 
     TypeDescr& typeDescr() const {
         return group()->typeDescr();
     }
 
     int32_t offset() const;
     int32_t length() const;
-    uint8_t* typedMem() const;
-    uint8_t* typedMemBase() const;
+    uint8_t* typedMem(const JS::AutoAssertOnGC&) const { return typedMem(); }
     bool isAttached() const;
 
     int32_t size() const {
         return typeDescr().size();
     }
 
-    uint8_t* typedMem(size_t offset) const {
+    uint8_t* typedMem(size_t offset, const JS::AutoAssertOnGC& nogc) const {
         // It seems a bit surprising that one might request an offset
         // == size(), but it can happen when taking the "address of" a
         // 0-sized value. (In other words, we maintain the invariant
         // that `offset + size <= size()` -- this is always checked in
         // the caller's side.)
         MOZ_ASSERT(offset <= (size_t) size());
-        return typedMem() + offset;
+        return typedMem(nogc) + offset;
     }
 
     inline MOZ_MUST_USE bool opaque() const;
 
     // Creates a new typed object whose memory is freshly allocated and
     // initialized with zeroes (or, in the case of references, an appropriate
     // default value).
     static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj, int32_t length,
@@ -613,16 +616,21 @@ class OutlineTypedObject : public TypedO
     uint8_t* outOfLineTypedMem() const {
         return data_;
     }
 
     void setData(uint8_t* data) {
         data_ = data;
     }
 
+    void resetOffset(size_t offset) {
+        MOZ_ASSERT(offset <= (size_t) size());
+        setData(typedMemBase() + offset);
+    }
+
     // Helper for createUnattached()
     static OutlineTypedObject* createUnattachedWithClass(JSContext* cx,
                                                          const Class* clasp,
                                                          HandleTypeDescr type,
                                                          int32_t length,
                                                          gc::InitialHeap heap = gc::DefaultHeap);
 
     // Creates an unattached typed object or handle (depending on the
@@ -671,31 +679,42 @@ class OutlineOpaqueTypedObject : public 
 {
   public:
     static const Class class_;
 };
 
 // Class for a typed object whose data is allocated inline.
 class InlineTypedObject : public TypedObject
 {
+    friend class TypedObject;
+
     // Start of the inline data, which immediately follows the shape and type.
     uint8_t data_[1];
 
+  protected:
+    uint8_t* inlineTypedMem() const {
+        return (uint8_t*) &data_;
+    }
+
   public:
     static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
 
     static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
         size_t nbytes = descr->size();
         MOZ_ASSERT(nbytes <= MaximumSize);
 
         return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
     }
 
-    uint8_t* inlineTypedMem() const {
-        return (uint8_t*) &data_;
+    uint8_t* inlineTypedMem(const JS::AutoAssertOnGC&) const {
+        return inlineTypedMem();
+    }
+
+    uint8_t* inlineTypedMemForGC() const {
+        return inlineTypedMem();
     }
 
     static void obj_trace(JSTracer* trace, JSObject* object);
     static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
 
     static size_t offsetOfDataStart() {
         return offsetof(InlineTypedObject, data_);
     }
@@ -709,16 +728,20 @@ class InlineTypedObject : public TypedOb
 // Class for a transparent typed object with inline data, which may have a
 // lazily allocated array buffer.
 class InlineTransparentTypedObject : public InlineTypedObject
 {
   public:
     static const Class class_;
 
     ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
+
+    uint8_t* inlineTypedMem() const {
+        return InlineTypedObject::inlineTypedMem();
+    }
 };
 
 // Class for an opaque typed object with inline data and no array buffer.
 class InlineOpaqueTypedObject : public InlineTypedObject
 {
   public:
     static const Class class_;
 };
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1475,17 +1475,17 @@ CallTraceHook(Functor f, JSTracer* trc, 
         return &obj->as<NativeObject>();
 
     if (clasp->isTrace(InlineTypedObject::obj_trace)) {
         Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC();
         f(pshape, mozilla::Forward<Args>(args)...);
 
         InlineTypedObject& tobj = obj->as<InlineTypedObject>();
         if (tobj.typeDescr().hasTraceList()) {
-            VisitTraceList(f, tobj.typeDescr().traceList(), tobj.inlineTypedMem(),
+            VisitTraceList(f, tobj.typeDescr().traceList(), tobj.inlineTypedMemForGC(),
                            mozilla::Forward<Args>(args)...);
         }
 
         return nullptr;
     }
 
     if (clasp == &UnboxedPlainObject::class_) {
         JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -108,8 +108,31 @@ function createI64(val) {
         val = val.slice(2).padStart(16, '0');
         ret = {
             low: parseInt(val.slice(8, 16), 16),
             high: parseInt(val.slice(0, 8), 16)
         };
     }
     return ret;
 }
+
+// Fully test a module:
+// - ensure it validates.
+// - ensure it compiles and produces the expected result.
+// - ensure textToBinary(binaryToText(binary)) = binary
+// Preconditions:
+// - the binary module must export a function called "run".
+function wasmFullPass(text, expected, maybeImports) {
+    let binary = wasmTextToBinary(text);
+    assertEq(WebAssembly.validate(binary), true, "Must validate.");
+
+    let module = new WebAssembly.Module(binary);
+    let instance = new WebAssembly.Instance(module, maybeImports);
+    assertEq(instance.exports.run(), expected, "Initial module must return the expected result.");
+
+    let retext = wasmBinaryToText(binary);
+    let rebinary = wasmTextToBinary(retext);
+
+    assertEq(WebAssembly.validate(rebinary), true, "Recreated binary must validate.");
+    let remodule = new WebAssembly.Module(rebinary);
+    let reinstance = new WebAssembly.Instance(remodule, maybeImports);
+    assertEq(reinstance.exports.run(), expected, "Reformed module must return the expected result");
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1302432.js
@@ -0,0 +1,10 @@
+setJitCompilerOption('ion.warmup.trigger', 0);
+gczeal(7, 1);
+var dbgGlobal = newGlobal();
+var dbg = new dbgGlobal.Debugger();
+dbg.addDebuggee(this);
+function f(x, await = () => Array.isArray(revocable.proxy), ...get) {
+    dbg.getNewestFrame().older.eval("print(a)");
+}
+function a() {}
+for (var i = 0; i < 10; i++) f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/full-cycle.js
@@ -0,0 +1,8 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+wasmFullPass(`(module
+    (func $test (result i32) (param i32) (param i32) (i32.add (get_local 0) (get_local 1)))
+    (func $run (result i32) (call $test (i32.const 1) (i32.const ${Math.pow(2, 31) - 1})))
+    (export "run" $run)
+)`, -Math.pow(2, 31));
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2554,16 +2554,21 @@ jit::CanEnter(JSContext* cx, RunState& s
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
     if (JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
     }
 
+    // Skip if the script is being compiled off thread (again).
+    // MaybeCreateThisForConstructor could have started an ion compilation.
+    if (rscript->isIonCompilingOffThread())
+        return Method_Skipped;
+
     // Attempt compilation. Returns Method_Compiled if already compiled.
     bool constructing = state.isInvoke() && state.asInvoke()->constructing();
     MethodStatus status = Compile(cx, rscript, nullptr, nullptr, constructing);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, rscript);
         return status;
     }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1256,18 +1256,19 @@ MacroAssembler::initGCThing(Register obj
 
             if (ntemplate->hasPrivate() && !ntemplate->is<TypedArrayObject>()) {
                 uint32_t nfixed = ntemplate->numFixedSlots();
                 storePtr(ImmPtr(ntemplate->getPrivate()),
                          Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
             }
         }
     } else if (templateObj->is<InlineTypedObject>()) {
+        JS::AutoAssertOnGC nogc; // off-thread, so cannot GC
         size_t nbytes = templateObj->as<InlineTypedObject>().size();
-        const uint8_t* memory = templateObj->as<InlineTypedObject>().inlineTypedMem();
+        const uint8_t* memory = templateObj->as<InlineTypedObject>().inlineTypedMem(nogc);
 
         // Memcpy the contents of the template object to the new object.
         size_t offset = 0;
         while (nbytes) {
             uintptr_t value = *(uintptr_t*)(memory + offset);
             storePtr(ImmWord(value),
                      Address(obj, InlineTypedObject::offsetOfDataStart() + offset));
             nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -142,17 +142,17 @@ RematerializedFrame::trace(JSTracer* trc
     TraceRoot(trc, &script_, "remat ion frame script");
     TraceRoot(trc, &envChain_, "remat ion frame env chain");
     if (callee_)
         TraceRoot(trc, &callee_, "remat ion frame callee");
     if (argsObj_)
         TraceRoot(trc, &argsObj_, "remat ion frame argsobj");
     TraceRoot(trc, &returnValue_, "remat ion frame return value");
     TraceRoot(trc, &thisArgument_, "remat ion frame this");
-    TraceRootRange(trc, numActualArgs_ + isConstructing_ + script_->nfixed(),
+    TraceRootRange(trc, numArgSlots() + isConstructing_ + script_->nfixed(),
                    slots_, "remat ion frame stack");
 }
 
 void
 RematerializedFrame::dump()
 {
     fprintf(stderr, " Rematerialized Ion Frame%s\n", inlined() ? " (inlined)" : "");
     if (isFunctionFrame()) {
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -3396,16 +3396,18 @@ uint32_t
 Assembler::GetNopFill()
 {
     static bool isSet = false;
     if (!isSet) {
         char* fillStr = getenv("ARM_ASM_NOP_FILL");
         uint32_t fill;
         if (fillStr && sscanf(fillStr, "%u", &fill) == 1)
             NopFill = fill;
+        if (NopFill > 8)
+            MOZ_CRASH("Nop fill > 8 is not supported");
         isSet = true;
     }
     return NopFill;
 }
 
 uint32_t Assembler::AsmPoolMaxOffset = 1024;
 
 uint32_t
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -493,21 +493,21 @@ struct Imm8mData
   public:
     uint32_t encode() const {
         MOZ_ASSERT(!invalid);
         return data | rot << 8;
     };
 
     // Default constructor makes an invalid immediate.
     Imm8mData()
-      : data(0xff), rot(0xf), invalid(1)
+      : data(0xff), rot(0xf), buff(0), invalid(1)
     { }
 
     Imm8mData(uint32_t data_, uint32_t rot_)
-      : data(data_), rot(rot_), invalid(0)
+      : data(data_), rot(rot_), buff(0), invalid(0)
     {
         MOZ_ASSERT(data == data_);
         MOZ_ASSERT(rot == rot_);
     }
 };
 
 struct Imm8Data
 {
--- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp
@@ -46,39 +46,41 @@ AssemblerX86Shared::copyPreBarrierTable(
         memcpy(dest, preBarriers_.buffer(), preBarriers_.length());
 }
 
 static void
 TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader)
 {
     while (reader.more()) {
         size_t offset = reader.readUnsigned();
-        void** ptr = X86Encoding::GetPointerRef(buffer + offset);
+        void* ptr = X86Encoding::GetPointer(buffer + offset);
 
 #ifdef JS_PUNBOX64
         // All pointers on x64 will have the top bits cleared. If those bits
         // are not cleared, this must be a Value.
-        uintptr_t* word = reinterpret_cast<uintptr_t*>(ptr);
-        if (*word >> JSVAL_TAG_SHIFT) {
+        uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
+        if (word >> JSVAL_TAG_SHIFT) {
             jsval_layout layout;
-            layout.asBits = *word;
+            layout.asBits = word;
             Value v = IMPL_TO_JSVAL(layout);
-            TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value");
-            if (*word != JSVAL_TO_IMPL(v).asBits) {
+            TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value");
+            if (word != JSVAL_TO_IMPL(v).asBits) {
                 // Only update the code if the Value changed, because the code
                 // is not writable if we're not moving objects.
-                *word = JSVAL_TO_IMPL(v).asBits;
+                X86Encoding::SetPointer(buffer + offset, (void*)JSVAL_TO_IMPL(v).asBits);
             }
             continue;
         }
 #endif
 
         // No barrier needed since these are constants.
-        TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(ptr),
-                                                 "ion-masm-ptr");
+        gc::Cell* cellPtr = reinterpret_cast<gc::Cell*>(ptr);
+        TraceManuallyBarrieredGenericPointerEdge(trc, &cellPtr, "jit-masm-ptr");
+        if (cellPtr != ptr)
+            X86Encoding::SetPointer(buffer + offset, cellPtr);
     }
 }
 
 
 void
 AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
--- a/js/src/jit/x86-shared/Patching-x86-shared.h
+++ b/js/src/jit/x86-shared/Patching-x86-shared.h
@@ -10,41 +10,39 @@
 namespace js {
 namespace jit {
 
 namespace X86Encoding {
 
 inline void*
 GetPointer(const void* where)
 {
-    return reinterpret_cast<void* const*>(where)[-1];
-}
-
-inline void**
-GetPointerRef(void* where)
-{
-    return &reinterpret_cast<void**>(where)[-1];
+    void* res;
+    memcpy(&res, (const char*)where - sizeof(void*), sizeof(void*));
+    return res;
 }
 
 inline void
 SetPointer(void* where, const void* value)
 {
-    reinterpret_cast<const void**>(where)[-1] = value;
+    memcpy((char*)where - sizeof(void*), &value, sizeof(void*));
 }
 
 inline int32_t
 GetInt32(const void* where)
 {
-    return reinterpret_cast<const int32_t*>(where)[-1];
+    int32_t res;
+    memcpy(&res, (const char*)where - sizeof(int32_t), sizeof(int32_t));
+    return res;
 }
 
 inline void
 SetInt32(void* where, int32_t value)
 {
-    reinterpret_cast<int32_t*>(where)[-1] = value;
+    memcpy((char*)where - sizeof(int32_t), &value, sizeof(int32_t));
 }
 
 inline void
 SetRel32(void* from, void* to)
 {
     intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from);
     MOZ_ASSERT(offset == static_cast<int32_t>(offset),
                "offset is too great for a 32-bit relocation");
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -341,17 +341,18 @@ void
 ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
                                       uint8_t* oldDataPointer, BufferContents newContents)
 {
     MOZ_ASSERT(!view->isSharedMemory());
 
     // Watch out for NULL data pointers in views. This means that the view
     // is not fully initialized (in which case it'll be initialized later
     // with the correct pointer).
-    uint8_t* viewDataPointer = view->dataPointerUnshared();
+    JS::AutoCheckCannotGC nogc(cx);
+    uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
     if (viewDataPointer) {
         MOZ_ASSERT(newContents);
         ptrdiff_t offset = viewDataPointer - oldDataPointer;
         viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
         view->setDataPointerUnshared(viewDataPointer);
     }
 
     // Notify compiled jit code that the base pointer has moved.
@@ -1443,17 +1444,17 @@ ArrayBufferViewObject::trace(JSTracer* t
                 JSObject* view = buf.firstView();
 
                 // Mark the object to move it into the tenured space.
                 TraceManuallyBarrieredEdge(trc, &view, "typed array nursery owner");
                 MOZ_ASSERT(view->is<InlineTypedObject>());
                 MOZ_ASSERT(view != obj);
 
                 void* srcData = obj->getPrivate();
-                void* dstData = view->as<InlineTypedObject>().inlineTypedMem() + offset;
+                void* dstData = view->as<InlineTypedObject>().inlineTypedMemForGC() + offset;
                 obj->setPrivateUnbarriered(dstData);
 
                 // We can't use a direct forwarding pointer here, as there might
                 // not be enough bytes available, and other views might have data
                 // pointers whose forwarding pointers would overlap this one.
                 trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, srcData, dstData,
                                                                      /* direct = */ false);
             } else {
@@ -1492,25 +1493,25 @@ ArrayBufferViewObject::notifyBufferDetac
             return;
         as<TypedArrayObject>().notifyBufferDetached(cx, newData);
     } else {
         as<OutlineTypedObject>().notifyBufferDetached(newData);
     }
 }
 
 uint8_t*
-ArrayBufferViewObject::dataPointerUnshared()
+ArrayBufferViewObject::dataPointerUnshared(const JS::AutoAssertOnGC& nogc)
 {
     if (is<DataViewObject>())
         return static_cast<uint8_t*>(as<DataViewObject>().dataPointer());
     if (is<TypedArrayObject>()) {
         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
         return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());
     }
-    return as<TypedObject>().typedMem();
+    return as<TypedObject>().typedMem(nogc);
 }
 
 #ifdef DEBUG
 bool
 ArrayBufferViewObject::isSharedMemory()
 {
     if (is<TypedArrayObject>())
         return as<TypedArrayObject>().isSharedMemory();
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -435,17 +435,17 @@ class ArrayBufferViewObject : public JSO
     void notifyBufferDetached(JSContext* cx, void* newData);
 
 #ifdef DEBUG
     bool isSharedMemory();
 #endif
 
     // By construction we only need unshared variants here.  See
     // comments in ArrayBufferObject.cpp.
-    uint8_t* dataPointerUnshared();
+    uint8_t* dataPointerUnshared(const JS::AutoAssertOnGC&);
     void setDataPointerUnshared(uint8_t* data);
 
     static void trace(JSTracer* trc, JSObject* obj);
 };
 
 bool
 ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7206,17 +7206,17 @@ DebuggerFrame::initClass(JSContext* cx, 
 }
 
 /* static */ DebuggerFrame*
 DebuggerFrame::create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
                       const ScriptFrameIter* maybeIter, HandleNativeObject debugger)
 {
   JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerFrame::class_, proto);
   if (!obj)
-    return nullptr;
+      return nullptr;
 
   DebuggerFrame& frame = obj->as<DebuggerFrame>();
 
   // Eagerly copy ScriptFrameIter data if we've already walked the stack.
   if (maybeIter) {
       AbstractFramePtr data = maybeIter->copyDataAsAbstractFramePtr();
       if (!data)
           return nullptr;
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -147,17 +147,18 @@ TypeSet::TypeString(TypeSet::Type type)
 /* static */ const char*
 TypeSet::ObjectGroupString(ObjectGroup* group)
 {
     return TypeString(TypeSet::ObjectType(group));
 }
 
 #ifdef DEBUG
 
-static bool InferSpewActive(SpewChannel channel)
+bool
+js::InferSpewActive(SpewChannel channel)
 {
     static bool active[SPEW_COUNT];
     static bool checked = false;
     if (!checked) {
         checked = true;
         PodArrayZero(active);
         const char* env = getenv("INFERFLAGS");
         if (!env)
@@ -217,29 +218,28 @@ js::InferSpewColor(TypeSet* types)
     static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
                                            "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
                                            "\x1b[1;37m" };
     if (!InferSpewColorable())
         return "";
     return colors[DefaultHasher<TypeSet*>::hash(types) % 7];
 }
 
+#ifdef DEBUG
 void
-js::InferSpew(SpewChannel channel, const char* fmt, ...)
+js::InferSpewImpl(const char* fmt, ...)
 {
-    if (!InferSpewActive(channel))
-        return;
-
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "[infer] ");
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
     va_end(ap);
 }
+#endif
 
 MOZ_NORETURN MOZ_COLD static void
 TypeFailure(JSContext* cx, const char* fmt, ...)
 {
     char msgbuf[1024]; /* Larger error messages will be truncated */
     char errbuf[1024];
 
     va_list ap;
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1285,31 +1285,34 @@ struct TypeZone
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
 
 #ifdef DEBUG
 
+bool InferSpewActive(SpewChannel channel);
 const char * InferSpewColorReset();
 const char * InferSpewColor(TypeConstraint* constraint);
 const char * InferSpewColor(TypeSet* types);
 
-void InferSpew(SpewChannel which, const char* fmt, ...);
+#define InferSpew(channel, ...) if (InferSpewActive(channel)) { InferSpewImpl(__VA_ARGS__); } else {}
+void InferSpewImpl(const char* fmt, ...);
 
 /* Check that the type property for id in group contains value. */
 bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value);
 
 #else
 
 inline const char * InferSpewColorReset() { return nullptr; }
 inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; }
 inline const char * InferSpewColor(TypeSet* types) { return nullptr; }
-inline void InferSpew(SpewChannel which, const char* fmt, ...) {}
+
+#define InferSpew(channel, ...) do {} while (0)
 
 #endif
 
 // Prints type information for a context if spew is enabled or force is set.
 void
 PrintTypes(JSContext* cx, JSCompartment* comp, bool force);
 
 } /* namespace js */
--- a/layout/base/nsBidi.cpp
+++ b/layout/base/nsBidi.cpp
@@ -1634,25 +1634,16 @@ nsresult nsBidi::GetDirection(nsBidiDire
 }
 
 nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel)
 {
   *aParaLevel = mParaLevel;
   return NS_OK;
 }
 
-nsresult nsBidi::GetCharTypeAt(int32_t aCharIndex, nsCharType* pType)
-{
-  if(aCharIndex<0 || mLength<=aCharIndex) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  *pType = (nsCharType)mDirProps[aCharIndex];
-  return NS_OK;
-}
-
 nsresult nsBidi::GetLogicalRun(int32_t aLogicalStart, int32_t *aLogicalLimit, nsBidiLevel *aLevel)
 {
   int32_t length = mLength;
 
   if(aLogicalStart<0 || length<=aLogicalStart) {
     return NS_ERROR_INVALID_ARG;
   }
 
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -524,25 +524,16 @@ public:
    *
    * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating the paragraph level
    *
    * @see nsBidiLevel
    */
   nsresult GetParaLevel(nsBidiLevel* aParaLevel);
 
   /**
-   * Get the bidirectional type for one character.
-   *
-   * @param aCharIndex the index of a character.
-   *
-   * @param aType receives the bidirectional type of the character at aCharIndex.
-   */
-  nsresult GetCharTypeAt(int32_t aCharIndex,  nsCharType* aType);
-
-  /**
    * Get a logical run.
    * This function returns information about a run and is used
    * to retrieve runs in logical order.<p>
    * This is especially useful for line-breaking on a paragraph.
    *
    * @param aLogicalStart is the first character of the run.
    *
    * @param aLogicalLimit will receive the limit of the run.
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1904,28 +1904,34 @@ nsBidiPresUtils::CalculateCharType(nsBid
 
 {
   bool       strongTypeFound = false;
   int32_t    offset;
   nsCharType charType;
 
   aCharType = eCharType_OtherNeutral;
 
-  for (offset = aOffset; offset < aCharTypeLimit; offset++) {
+  int32_t charLen;
+  for (offset = aOffset; offset < aCharTypeLimit; offset += charLen) {
     // Make sure we give RTL chartype to all characters that would be classified
     // as Right-To-Left by a bidi platform.
     // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
-    if (IS_HEBREW_CHAR(aText[offset]) ) {
+    charLen = 1;
+    uint32_t ch = aText[offset];
+    if (IS_HEBREW_CHAR(ch) ) {
       charType = eCharType_RightToLeft;
-    }
-    else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) {
+    } else if (IS_ARABIC_ALPHABETIC(ch) ) {
       charType = eCharType_RightToLeftArabic;
-    }
-    else {
-      aBidiEngine->GetCharTypeAt(offset, &charType);
+    } else {
+      if (NS_IS_HIGH_SURROGATE(ch) && offset + 1 < aCharTypeLimit &&
+          NS_IS_LOW_SURROGATE(aText[offset + 1])) {
+        ch = SURROGATE_TO_UCS4(ch, aText[offset + 1]);
+        charLen = 2;
+      }
+      charType = GetBidiCat(ch);
     }
 
     if (!CHARTYPE_IS_WEAK(charType) ) {
 
       if (strongTypeFound
           && (charType != aPrevCharType)
           && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
         // Stop at this point to ensure uni-directionality of the text
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2021,20 +2021,16 @@ GetMouseThrough(const nsIFrame* aFrame)
     frame = nsBox::GetParentXULBox(frame);
   }
   return false;
 }
 
 static bool
 IsFrameReceivingPointerEvents(nsIFrame* aFrame)
 {
-  nsSubDocumentFrame* frame = do_QueryFrame(aFrame);
-  if (frame && frame->PassPointerEventsToChildren()) {
-    return true;
-  }
   return NS_STYLE_POINTER_EVENTS_NONE !=
     aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame);
 }
 
 // A list of frames, and their z depth. Used for sorting
 // the results of hit testing.
 struct FramesWithDepth
 {
@@ -3674,17 +3670,17 @@ nsDisplayEventReceiver::HitTest(nsDispla
 void
 nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame)
 {
   NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
                "Reference frame mismatch");
   if (aBuilder->IsInsidePointerEventsNoneDoc()) {
     // Somewhere up the parent document chain is a subdocument with pointer-
-    // events:none set on it (and without a mozpasspointerevents).
+    // events:none set on it.
     return;
   }
   if (!aFrame->GetParent()) {
     MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::viewportFrame);
     // Viewport frames are never event targets, other frames, like canvas frames,
     // are the event targets for any regions viewport frames may cover.
     return;
   }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -514,17 +514,17 @@ public:
   /**
    * Get the caret associated with the current presshell.
    */
   nsCaret* GetCaret();
   /**
    * Notify the display list builder that we're entering a presshell.
    * aReferenceFrame should be a frame in the new presshell.
    * aPointerEventsNoneDoc should be set to true if the frame generating this
-   * document is pointer-events:none without mozpasspointerevents.
+   * document is pointer-events:none.
    */
   void EnterPresShell(nsIFrame* aReferenceFrame,
                       bool aPointerEventsNoneDoc = false);
   /**
    * For print-preview documents, we sometimes need to build display items for
    * the same frames multiple times in the same presentation, with different
    * clipping. Between each such batch of items, call
    * ResetMarkedFramesForDisplayList to make sure that the results of
@@ -1171,17 +1171,17 @@ private:
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
     uint32_t      mFirstFrameMarkedForDisplay;
     bool          mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
-    // events:none frame that doesn't have mozpasspointerevents set.
+    // events:none frame.
     bool          mInsidePointerEventsNoneDoc;
   };
 
   PresShellState* CurrentPresShellState() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
                  "Someone forgot to enter a presshell");
     return &mPresShellStates[mPresShellStates.Length() - 1];
   }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8487,21 +8487,16 @@ nsLayoutUtils::CalculateRootCompositionS
 
 /* static */ nsRect
 nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame)
 {
   nsRect contentBounds;
   if (aScrollableFrame) {
     contentBounds = aScrollableFrame->GetScrollRange();
 
-    // We ifndef the below code for Fennec because it requires special behaviour
-    // on the APZC side. Because Fennec has it's own PZC implementation which doesn't
-    // provide the special behaviour, this code will cause it to break. We can remove
-    // the ifndef once Fennec switches over to APZ or if we add the special handling
-    // to Fennec
     nsPoint scrollPosition = aScrollableFrame->GetScrollPosition();
     if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
       contentBounds.y = scrollPosition.y;
       contentBounds.height = 0;
     }
     if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
       contentBounds.x = scrollPosition.x;
       contentBounds.width = 0;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6224,57 +6224,16 @@ public:
     mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags);
   }
 
 private:
   PresShell* mShell;
   uint32_t mFlags;
 };
 
-class AutoUpdateHitRegion
-{
-public:
-  AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame)
-    : mShell(aShell), mFrame(aFrame)
-  {
-  }
-  ~AutoUpdateHitRegion()
-  {
-    if (!XRE_IsContentProcess() ||
-        !mFrame || !mShell) {
-      return;
-    }
-    TabChild* tabChild = TabChild::GetFrom(mShell);
-    if (!tabChild || !tabChild->GetUpdateHitRegion()) {
-      return;
-    }
-    nsRegion region;
-    nsDisplayListBuilder builder(mFrame,
-                                 nsDisplayListBuilderMode::EVENT_DELIVERY,
-                                 /* aBuildCert= */ false);
-    nsDisplayList list;
-    AutoTArray<nsIFrame*, 100> outFrames;
-    nsDisplayItem::HitTestState hitTestState;
-    builder.EnterPresShell(mFrame);
-    nsRect bounds = mShell->GetPresContext()->GetVisibleArea();
-    mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list);
-    builder.LeavePresShell(mFrame);
-    list.HitTest(&builder, bounds, &hitTestState, &outFrames);
-    list.DeleteAll();
-    for (int32_t i = outFrames.Length() - 1; i >= 0; --i) {
-      region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor(
-                  outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame));
-    }
-    tabChild->UpdateHitRegion(region);
-  }
-private:
-  PresShell* mShell;
-  nsIFrame* mFrame;
-};
-
 void
 PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
 {
   mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
 }
 
 void
 PresShell::Paint(nsView*        aViewToPaint,
@@ -6299,17 +6258,16 @@ PresShell::Paint(nsView*        aViewToP
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   LayerManager* layerManager =
     aViewToPaint->GetWidget()->GetLayerManager();
   NS_ASSERTION(layerManager, "Must be in paint event");
   bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
 
   nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
-  AutoUpdateHitRegion updateHitRegion(this, frame);
 
   // Whether or not we should set first paint when painting is
   // suppressed is debatable. For now we'll do it because
   // B2G relies on first paint to configure the viewport and
   // we only want to do that when we have real content to paint.
   // See Bug 798245
   if (mIsFirstPaint && !mPaintingSuppressed) {
     layerManager->SetIsFirstPaint();
--- a/layout/base/tests/chrome/chrome.ini
+++ b/layout/base/tests/chrome/chrome.ini
@@ -6,18 +6,16 @@ support-files =
   bug495648.rdf
   bug551434_childframe.html
   chrome_content_integration_window.xul
   chrome_over_plugin_window.xul
   default_background_window.xul
   dialog_with_positioning_window.xul
   no_clip_iframe_subdoc.html
   no_clip_iframe_window.xul
-  passpointerevents_window.html
-  passpointerevents_dynamically_window.html
   printpreview_bug396024_helper.xul
   printpreview_bug482976_helper.xul
   printpreview_helper.xul
   file_bug1018265.xul
 
 [test_bug396367-1.html]
 [test_bug396367-2.html]
 [test_bug420499.xul]
@@ -52,20 +50,16 @@ skip-if = buildapp == 'b2g'
 tags = openwindow
 skip-if = buildapp == 'b2g'
 [test_fixed_bg_scrolling_repaints.html]
 skip-if = buildapp == 'b2g'
 [test_leaf_layers_partition_browser_window.xul]
 skip-if = (!debug) || (toolkit == "cocoa") || (os == "linux") || (buildapp == 'b2g') # Disabled on Mac and Linux because of Bug 992311
 [test_no_clip_iframe.xul]
 skip-if = buildapp == 'b2g'
-[test_passpointerevents.html]
-skip-if = buildapp == 'b2g'
-[test_passpointerevents_dynamic.html]
-skip-if = buildapp == 'b2g'
 [test_prerendered_transforms.html]
 skip-if = buildapp == 'b2g'
 [test_printpreview.xul]
 skip-if = buildapp == 'b2g' || (os == "linux" && bits == 32) # Disabled on Linux32 for bug 1278957
 [test_printpreview_bug396024.xul]
 skip-if = buildapp == 'b2g'
 [test_printpreview_bug482976.xul]
 skip-if = buildapp == 'b2g'
deleted file mode 100644
--- a/layout/base/tests/chrome/passpointerevents_dynamically_window.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test that mozpasspointerevents works after setting it dynamically</title>
-</head>
-<body onload="startTest()">
-<iframe id="f" style="border:none; width:200px; height:200px; pointer-events:none"
-        src="data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>">
-</iframe>
-
-<script type="application/javascript">
-var SimpleTest = window.opener.SimpleTest;
-var is = window.opener.is;
-
-function startTest() {
-  var f = document.getElementById("f");
-  f.setAttribute("mozpasspointerevents", true);
-  var fRect = f.getBoundingClientRect();
-  var e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10);
-  is(e1, document.body, "check point in transparent region of the iframe");
-  var e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110);
-  is(e2, f, "check point in opaque region of the iframe");
-  window.close();
-  SimpleTest.finish();
-}
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/layout/base/tests/chrome/passpointerevents_window.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test that mozpasspointerevents works</title>
-</head>
-<body onload="startTest()">
-<iframe id="f" style="border:none; width:200px; height:200px; pointer-events:none" mozpasspointerevents
-        src="data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>">
-</iframe>
-
-<script type="application/javascript">
-var SimpleTest = window.opener.SimpleTest;
-var is = window.opener.is;
-
-function startTest() {
-  var f = document.getElementById("f");
-  var fRect = f.getBoundingClientRect();
-  var e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10);
-  is(e1, document.body, "check point in transparent region of the iframe");
-  var e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110);
-  is(e2, f, "check point in opaque region of the iframe");
-  window.close();
-  SimpleTest.finish();
-}
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/layout/base/tests/chrome/test_passpointerevents.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test that mozpasspointerevents works</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<pre id="test">
-<script type="application/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var root = getRootDirectory(window.location.href);
-window.openDialog(root + "passpointerevents_window.html", "passpointerevents",
-                  "chrome,width=400,height=400");
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/layout/base/tests/chrome/test_passpointerevents_dynamic.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test that mozpasspointerevents works after setting it dynamically</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<pre id="test">
-<script type="application/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var root = getRootDirectory(window.location.href);
-window.openDialog(root + "passpointerevents_dynamically_window.html",
-                  "passpointerevents", "chrome,width=400,height=400");
-</script>
-</pre>
-</body>
-</html>
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -239,17 +239,16 @@ skip-if = buildapp == 'mulet' || buildap
 [test_bug646757.html]
 [test_bug718809.html]
 [test_bug725426.html]
 [test_bug731777.html]
 [test_bug761572.html]
 [test_bug770106.html]
 [test_remote_frame.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
-[test_remote_passpointerevents.html]
 [test_bug842853.html]
 [test_bug842853-2.html]
 [test_bug849219.html]
 [test_bug851485.html]
 [test_bug851445.html]
 support-files = bug851445_helper.html
 [test_bug970964.html]
 support-files = bug970964_inner.html
deleted file mode 100644
--- a/layout/base/tests/test_remote_passpointerevents.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-  <head>
-    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  </head>
-  <body>
-    <script type="application/javascript;version=1.7">
-      "use strict";
-
-      SimpleTest.waitForExplicitFinish();
-
-      function checkPointerEvents() {
-        let iframe = this;
-        let fRect = iframe.getBoundingClientRect();
-        let e1 = document.elementFromPoint(fRect.left + 10, fRect.top + 10);
-        let e2 = document.elementFromPoint(fRect.left + 110, fRect.top + 110);
-        if (e1 === document.body && e2 === iframe) {
-          is(e1, document.body, "check point in transparent region of the iframe");
-          is(e2, iframe, "check point in opaque region of the iframe");
-          SimpleTest.finish();
-        }
-        else {
-          SimpleTest.executeSoon(checkPointerEvents.bind(iframe));
-        }
-      }
-
-      function runTest() {
-        let iframe = document.createElement("iframe");
-        SpecialPowers.wrap(iframe).setAttribute('mozbrowser', 'true');
-        SpecialPowers.wrap(iframe).setAttribute('mozpasspointerevents', 'true');
-        SpecialPowers.wrap(iframe).setAttribute('remote', 'true');
-        iframe.style = "border:none; width:400px; height:400px; pointer-events:none";
-        iframe.src = "data:text/html,<html style='pointer-events:none'><div style='margin:100px; width:100px; height:100px; background:yellow; pointer-events:auto'>";
-
-        document.body.appendChild(iframe);
-
-        SimpleTest.executeSoon(checkPointerEvents.bind(iframe));
-      }
-      addEventListener("load", function() {
-        SpecialPowers.addPermission("browser", true, document);
-        SpecialPowers.addPermission("embed-apps", true, document);
-        SpecialPowers.pushPrefEnv({
-          "set": [
-            ["dom.ipc.browser_frames.oop_by_default", true],
-            ["dom.mozBrowserFramesEnabled", true]
-          ]
-        }, runTest);
-      });
-    </script>
-  </body>
-</html>
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -302,41 +302,16 @@ nsSubDocumentFrame::GetSubdocumentSize()
       docSizeAppUnits = destRect.Size();
     }
 
     return ScreenIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
                          presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
   }
 }
 
-bool
-nsSubDocumentFrame::PassPointerEventsToChildren()
-{
-  // Limit use of mozpasspointerevents to documents with embedded:apps/chrome
-  // permission, because this could be used by the parent document to discover
-  // which parts of the subdocument are transparent to events (if subdocument
-  // uses pointer-events:none on its root element, which is admittedly
-  // unlikely)
-  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents)) {
-      if (PresContext()->IsChrome()) {
-        return true;
-      }
-
-      nsCOMPtr<nsIPermissionManager> permMgr =
-        services::GetPermissionManager();
-      if (permMgr) {
-        uint32_t permission = nsIPermissionManager::DENY_ACTION;
-        permMgr->TestPermissionFromPrincipal(GetContent()->NodePrincipal(),
-                                             "embed-apps", &permission);
-        return permission == nsIPermissionManager::ALLOW_ACTION;
-      }
-  }
-  return false;
-}
-
 static void
 WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
                               nsIFrame* aFrame,
                               nsDisplayList* aList)
 {
   nsDisplayList tempItems;
   nsDisplayItem* item;
   while ((item = aList->RemoveBottom()) != nullptr) {
@@ -375,32 +350,24 @@ nsSubDocumentFrame::BuildDisplayList(nsD
       // own layer so we generate a ColorLayer. This is helpful for optimizing
       // compositing; we can skip compositing the ColorLayer when the
       // remote content is opaque.
       WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground());
     }
     decorations.MoveTo(aLists);
   }
 
-  // We only care about mozpasspointerevents if we're doing hit-testing
-  // related things.
-  bool passPointerEventsToChildren =
-    (aBuilder->IsForEventDelivery() || aBuilder->IsBuildingLayerEventRegions())
-    ? PassPointerEventsToChildren() : false;
-
-  // If mozpasspointerevents is set, then we should allow subdocument content
-  // to handle events even if we're pointer-events:none.
-  if (aBuilder->IsForEventDelivery() && pointerEventsNone && !passPointerEventsToChildren) {
+  if (aBuilder->IsForEventDelivery() && pointerEventsNone) {
     return;
   }
 
   // If we're passing pointer events to children then we have to descend into
   // subdocuments no matter what, to determine which parts are transparent for
   // hit-testing or event regions.
-  bool needToDescend = aBuilder->GetDescendIntoSubdocuments() || passPointerEventsToChildren;
+  bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
   if (!mInnerView || !needToDescend) {
     return;
   }
 
   if (rfp) {
     rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
     return;
   }
@@ -443,18 +410,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD
 
       ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
       if (ignoreViewportScrolling) {
         savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
         aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
       }
     }
 
-    aBuilder->EnterPresShell(subdocRootFrame,
-                             pointerEventsNone && !passPointerEventsToChildren);
+    aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone);
   } else {
     dirty = aDirtyRect;
   }
 
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   if (ShouldClipSubdocument()) {
     clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
   }
@@ -908,26 +874,16 @@ nsSubDocumentFrame::AttributeChanged(int
     // Retrieve the attributes
     CSSIntSize margins = GetMarginAttributes();
 
     // Notify the frameloader
     RefPtr<nsFrameLoader> frameloader = FrameLoader();
     if (frameloader)
       frameloader->MarginsChanged(margins.width, margins.height);
   }
-  else if (aAttribute == nsGkAtoms::mozpasspointerevents) {
-    RefPtr<nsFrameLoader> frameloader = FrameLoader();
-    if (frameloader) {
-      if (aModType == nsIDOMMutationEvent::ADDITION) {
-        frameloader->ActivateUpdateHitRegion();
-      } else if (aModType == nsIDOMMutationEvent::REMOVAL) {
-        frameloader->DeactivateUpdateHitRegion();
-      }
-    }
-  }
 
   return NS_OK;
 }
 
 nsIFrame*
 NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSubDocumentFrame(aContext);
--- a/layout/ipc/PRenderFrame.ipdl
+++ b/layout/ipc/PRenderFrame.ipdl
@@ -25,15 +25,13 @@ namespace layout {
  */
 sync protocol PRenderFrame
 {
     manager PBrowser;
 
 parent:
     async NotifyCompositorTransaction();
 
-    async UpdateHitRegion(nsRegion aRegion);
-
     async __delete__();
 };
 
 } // namespace layout
 } // namespace mozilla
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -236,23 +236,16 @@ RenderFrameParent::ActorDestroy(ActorDes
 
 bool
 RenderFrameParent::RecvNotifyCompositorTransaction()
 {
   TriggerRepaint();
   return true;
 }
 
-bool
-RenderFrameParent::RecvUpdateHitRegion(const nsRegion& aRegion)
-{
-  mTouchRegion = aRegion;
-  return true;
-}
-
 void
 RenderFrameParent::TriggerRepaint()
 {
   nsIFrame* docFrame = mFrameLoader->GetPrimaryFrameOfOwningContent();
   if (!docFrame) {
     // Bad, but nothing we can do about it (XXX/cjones: or is there?
     // maybe bug 589337?).  When the new frame is created, we'll
     // probably still be the current render frame and will get to draw
@@ -283,22 +276,16 @@ RenderFrameParent::BuildDisplayList(nsDi
   nsPoint offset = aBuilder->ToReferenceFrame(aFrame);
   nsRect bounds = aFrame->EnsureInnerView()->GetBounds() + offset;
   clipState.ClipContentDescendants(bounds);
 
   aLists.Content()->AppendToTop(
     new (aBuilder) nsDisplayRemote(aBuilder, aFrame, this));
 }
 
-bool
-RenderFrameParent::HitTest(const nsRect& aRect)
-{
-  return mTouchRegion.Contains(aRect);
-}
-
 void
 RenderFrameParent::GetTextureFactoryIdentifier(TextureFactoryIdentifier* aTextureFactoryIdentifier)
 {
   RefPtr<LayerManager> lm = mFrameLoader ? GetFrom(mFrameLoader) : nullptr;
   // Perhaps the document containing this frame currently has no presentation?
   if (lm && lm->AsClientLayerManager()) {
     *aTextureFactoryIdentifier = lm->AsClientLayerManager()->GetTextureFactoryIdentifier();
   } else {
@@ -349,17 +336,16 @@ nsDisplayRemote::nsDisplayRemote(nsDispl
                                  nsSubDocumentFrame* aFrame,
                                  RenderFrameParent* aRemoteFrame)
   : nsDisplayItem(aBuilder, aFrame)
   , mRemoteFrame(aRemoteFrame)
   , mEventRegionsOverride(EventRegionsOverride::NoOverride)
 {
   if (aBuilder->IsBuildingLayerEventRegions()) {
     bool frameIsPointerEventsNone =
-      !aFrame->PassPointerEventsToChildren() &&
       aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame) ==
         NS_STYLE_POINTER_EVENTS_NONE;
     if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) {
       mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion;
     }
     if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell())) {
       mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent;
     }
@@ -375,18 +361,8 @@ nsDisplayRemote::BuildLayer(nsDisplayLis
   nsIntRect visibleRect = GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel);
   visibleRect += aContainerParameters.mOffset;
   RefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters);
   if (layer && layer->AsContainerLayer()) {
     layer->AsContainerLayer()->SetEventRegionsOverride(mEventRegionsOverride);
   }
   return layer.forget();
 }
-
-void
-nsDisplayRemote::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
-                         HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
-{
-  if (mRemoteFrame->HitTest(aRect)) {
-    aOutFrames->AppendElement(mFrame);
-  }
-}
-
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -86,18 +86,16 @@ public:
 
   void EnsureLayersConnected();
 
 protected:
   void ActorDestroy(ActorDestroyReason why) override;
 
   virtual bool RecvNotifyCompositorTransaction() override;
 
-  virtual bool RecvUpdateHitRegion(const nsRegion& aRegion) override;
-
 private:
   void TriggerRepaint();
   void DispatchEventForPanZoomController(const InputEvent& aEvent);
 
   uint64_t GetLayerTreeId() const;
 
   // When our child frame is pushing transactions directly to the
   // compositor, this is the ID of its layer tree in the compositor's
@@ -118,18 +116,16 @@ private:
   // Prefer the extra bit of state to null'ing out mFrameLoader in
   // Destroy() so that less code needs to be special-cased for after
   // Destroy().
   // 
   // It's possible for mFrameLoader==null and
   // mFrameLoaderDestroyed==false.
   bool mFrameLoaderDestroyed;
 
-  nsRegion mTouchRegion;
-
   bool mAsyncPanZoomEnabled;
   bool mInitted;
 };
 
 } // namespace layout
 } // namespace mozilla
 
 /**
@@ -149,19 +145,16 @@ public:
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override
   { return mozilla::LAYER_ACTIVE_FORCE; }
 
   virtual already_AddRefed<Layer>
   BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
              const ContainerLayerParameters& aContainerParameters) override;
 
-  void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
-               HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
-
   NS_DISPLAY_DECL_NAME("Remote", TYPE_REMOTE)
 
 private:
   RenderFrameParent* mRemoteFrame;
   mozilla::layers::EventRegionsOverride mEventRegionsOverride;
 };
 
 
--- a/layout/reftests/canvas/dash-1.html
+++ b/layout/reftests/canvas/dash-1.html
@@ -1,30 +1,30 @@
 <html>
 <head>
   <script type="text/javascript">
 window.onload = function() {
     var ctx = document.getElementById("c1").getContext("2d");
 
     ctx.lineWidth = 5;
 
-    ctx.mozDash = [ 5, 10 ];  // 5 on, 10 off
+    ctx.setLineDash([ 5, 10 ]);  // 5 on, 10 off
     ctx.moveTo(50, 50);
     ctx.lineTo(250, 50);
     ctx.stroke();
     ctx.beginPath();
 
-    ctx.mozDashOffset = 5;
+    ctx.lineDashOffset = 5;
     ctx.moveTo(50, 100);
     ctx.lineTo(250, 100);
     ctx.stroke();
     ctx.beginPath();
 
-    ctx.mozDashOffset = 5;
-    ctx.mozDash = [ 5 ];  // 5 on, 5 off
+    ctx.lineDashOffset = 5;
+    ctx.setLineDash([ 5 ]);  // 5 on, 5 off
     ctx.moveTo(50, 150);
     ctx.lineTo(250, 150);
     ctx.stroke();
     ctx.beginPath();
 
 }
   </script>
 </head>
--- a/layout/reftests/canvas/dash-sanity.html
+++ b/layout/reftests/canvas/dash-sanity.html
@@ -1,80 +1,93 @@
 <html>
 <head>
   <script type="text/javascript">
 function assert(cond, msg) { if (!cond) { throw msg; } }
 window.onload = function() {
     try {
         var ctx = document.getElementById("c1").getContext("2d");
 
-        assert(null === ctx.mozDash,
-               "Default dash is null (no dash)");
-        assert(0 == ctx.mozDashOffset,
-               "Default dashOffset is 0 (no dash)");
+        assert(0 === ctx.getLineDash().length,
+               "Default dash is [ ] (none)");
+        assert(0 === ctx.lineDashOffset,
+               "Default dashOffset is 0 (none)");
 
-        ctx.mozDash = [ 2 ];
-        assert(1 == ctx.mozDash.length && 2 == ctx.mozDash[0],
+        ctx.setLineDash([ 2 ]);
+        assert(2 === ctx.getLineDash().length &&
+               2 === ctx.getLineDash()[0] &&
+               2 === ctx.getLineDash()[1],
                "dash = [ 2 ] works");
-        ctx.mozDash = null;
-        assert(null === ctx.mozDash,
-               "dash = null resets to null");
-        ctx.mozDash = [ 2 ];
-        ctx.mozDash = undefined;
-        assert(null === ctx.mozDash,
-               "dash = undefined resets to null");
-        ctx.mozDash = [ 2 ];
-        ctx.mozDash = [ ];
-        assert(null === ctx.mozDash,
-               "dash = [] resets to null");
+        ctx.setLineDash([ 2 ]);
+        ctx.setLineDash([ ]);
+        assert(0 === ctx.getLineDash().length,
+               "dash = [ ] works");
+        ctx.setLineDash([ 2 ]);
+        ctx.setLineDash([ 0, 0, 0 ]);
+        assert(6 === ctx.getLineDash().length,
+               0 === ctx.getLineDash()[0] &&
+               0 === ctx.getLineDash()[1] &&
+               0 === ctx.getLineDash()[2] &&
+               0 === ctx.getLineDash()[3] &&
+               0 === ctx.getLineDash()[4] &&
+               0 === ctx.getLineDash()[5],
+               "dash = [ 0, 0, 0 ] works");
 
-        ctx.mozDash = [ 2 ];
-        assert(0 == ctx.mozDashOffset, "dashOffset is 0");
-        ctx.mozDashOffset = 1;
-        assert(1 == ctx.mozDashOffset, "Setting dashOffset succeeded");
-        ctx.mozDash = null;
-        assert(0 == ctx.mozDashOffset, "Disabling dash resets dashOffset");
-        ctx.mozDash = [ 2 ];
-        assert(0 == ctx.mozDashOffset, "Previous dashOffset isn't remembered");
-        ctx.mozDash = null;
+        ctx.setLineDash([ 2 ]);
+        assert(0 === ctx.lineDashOffset, "dashOffset is 0");
+        ctx.lineDashOffset = 1;
+        assert(1 === ctx.lineDashOffset, "Setting dashOffset succeeded");
+        ctx.setLineDash([ ]);
+        assert(1 === ctx.lineDashOffset, "Changing dash does not reset dashOffset");
 
         // NB: might want to add a |.dash = number| special case,
         // don't test that it fails here.  Might also want to add a
         // |.dash = [0]| special case for resetting, so don't test
         // that either.
         var badVals = [ -1,
+                        null,
+                        undefined,
+                        "",
                         "string",
-                        /* According to the WebIDL sequence-ifying
-                         * (really they mean array-ifying here)
-                         * algorithm, objects without .length
-                         * properties convert to a 0-length arrays.
-                         * This seems ... odd, since by the book we're
-                         * forced to accept |ctx.dash = Function|,
-                         * e.g., but there it is.
-                         */
-                        // { obj: true },
+                        { obj: true },
+                        function() {}
+        ]
+        ctx.setLineDash([ 2 ]);
+        for (var i = 0; i < badVals.length; ++i) {
+            var error = false;
+            try { ctx.setLineDash(badVals[i]); }
+            catch(e) { error = true; }
+            assert(error &&
+                   2 === ctx.getLineDash().length &&
+                   2 === ctx.getLineDash()[0] &&
+                   2 === ctx.getLineDash()[1],
+                   "Expected setLineDash("+ badVals[i] +") to throw exception and not change dash");
+        }
+
+        var ignoredVals = [
                         [ "array of string" ],
                         [ -1 ],
-                        [ 0, 0, 0 ],
                         [ 2, "string" ],
         ];
-        ctx.mozDash = [ 2 ];
-        for (var i = 0; i < badVals.length; ++i) {
-            var error = false;
-            try { ctx.mozDash = badVals[i]; }
-            catch(e) { error = true; }
-            assert(error && 1 == ctx.mozDash.length && 2 == ctx.mozDash[0],
-                   "Expected |dash = "+ badVals[i] +"| to throw exception and not change .dash");
+        ctx.setLineDash([ 2 ]);
+        for (var i = 0; i < ignoredVals.length; ++i) {
+            ctx.setLineDash(ignoredVals[i]);
+            assert(2 === ctx.getLineDash().length &&
+                   2 === ctx.getLineDash()[0] &&
+                   2 === ctx.getLineDash()[1],
+                   "Expected |setLineDash(" + ignoredVals[i] + ") to not change dash");
         }
-        ctx.mozDash = null;
 
+        ctx.setLineDash([ 2 ]);
         ctx.save();
-        ctx.mozDash = [ 2 ];
+        ctx.setLineDash([ 1, 1, 1, 1 ]);
         ctx.restore();
-        assert(null === ctx.mozDash,
+        assert(2 === ctx.getLineDash().length &&
+               2 === ctx.getLineDash()[0] &&
+               2 === ctx.getLineDash()[1],
                "dash was saved then restored");
     } catch (e) {
         document.body.innerHTML = "FAIL: "+ e.toString();
         return;
     }
     document.body.innerHTML = "Pass";
 }
   </script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-clipPath-viewBox-ref.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="600" height="400">
+  <circle cx="300" cy="200" r="100" style="fill:blue;"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-clipPath-viewBox.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="600" height="400" preserveAspectRatio="none"
+     viewBox="50 100 500 200"
+     class="reftest-wait"
+     onload="setTimeAndSnapshot(1, true)">
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+  <rect x="50" y="100" width="500" height="200" style="fill:blue;"
+        clip-path="circle(100px at center) view-box"/>
+  <animate attributeName="viewBox" values="50 100 500 200; 0 0 600 400"
+           dur="0.5s" fill="freeze"/>
+</svg>
\ No newline at end of file
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -274,8 +274,10 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerat
 == anim-defs-gradient-property.svg lime.svg
 == anim-defs-gradient-attribute.svg lime.svg
 == anim-defs-fill.svg lime.svg
 == anim-defs-width.svg lime.svg
 
 # Test animation that changes 'display' attribute
 == anim-display.svg lime.svg
 == anim-display-in-g-element.svg lime.svg
+
+pref(layout.css.clip-path-shapes.enabled,true) fuzzy(63,146) == anim-clipPath-viewBox.svg anim-clipPath-viewBox-ref.svg
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -682,17 +682,17 @@ refill_callback_duplex(cubeb_stream * st
   /* This can only happen when debugging, and having breakpoints set in the
    * callback in a way that it makes the stream underrun. */
   if (output_frames == 0) {
     return true;
   }
 
   // When WASAPI has not filled the input buffer yet, send silence.
   double output_duration = double(output_frames) / stm->output_mix_params.rate;
-  double input_duration = double(stm->linear_input_buffer.length() / stm->input_mix_params.channels) / stm->input_mix_params.rate;
+  double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
   if (input_duration < output_duration) {
     size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
     LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding);
     stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
   }
 
   refill(stm,
          stm->linear_input_buffer.data(),
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -54,8 +54,10 @@ if [ -n "$rev" ]; then
   sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\}\./$version./" README_MOZILLA
   rm README_MOZILLA.bak
 else
   echo "Remember to update README_MOZILLA with the version details."
 fi
 
 echo "Applying a patch on top of $version"
 patch -p1 < ./wasapi-drift.patch
+echo "Applying another patch on top of $version"
+patch -p1 < ./wasapi-stereo-mic.patch
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/wasapi-stereo-mic.patch
@@ -0,0 +1,28 @@
+From 50d92c0eaa05a93f4dd8ff5072e983c6c84d0369 Mon Sep 17 00:00:00 2001
+From: Paul Adenot <paul@paul.cx>
+Date: Tue, 4 Oct 2016 07:18:04 -0700
+Subject: [PATCH] Divide by the number of channel of the stream to compute the
+ duratio of the buffer.
+
+We downmix right before, so this is in frames in with the channel count of the
+stream, not the mix.
+---
+ src/cubeb_wasapi.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp
+index bd2cbed..87aa305 100644
+--- a/src/cubeb_wasapi.cpp
++++ b/src/cubeb_wasapi.cpp
+@@ -687,7 +687,7 @@ refill_callback_duplex(cubeb_stream * stm)
+ 
+   // When WASAPI has not filled the input buffer yet, send silence.
+   double output_duration = double(output_frames) / stm->output_mix_params.rate;
+-  double input_duration = double(stm->linear_input_buffer.length() / stm->input_mix_params.channels) / stm->input_mix_params.rate;
++  double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
+   if (input_duration < output_duration) {
+     size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
+     LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding);
+-- 
+2.7.4
+
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm
@@ -32,16 +32,17 @@ class AppCapturerMac : public AppCapture
 
   // AppCapturer interface.
   virtual bool GetAppList(AppList* apps) override;
   virtual bool SelectApp(ProcessId processId) override;
   virtual bool BringAppToFront() override;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) override;
+  virtual void Stop() override;
   virtual void Capture(const DesktopRegion& region) override;
 
  private:
   Callback* callback_;
   ProcessId process_id_;
 
   DISALLOW_COPY_AND_ASSIGN(AppCapturerMac);
 };
@@ -73,16 +74,20 @@ bool AppCapturerMac::BringAppToFront() {
 // DesktopCapturer interface.
 void AppCapturerMac::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void AppCapturerMac::Stop() {
+  callback_ = NULL;
+}
+
 void AppCapturerMac::Capture(const DesktopRegion& region) {
   // Check that selected process exists
   NSRunningApplication *ra = [NSRunningApplication runningApplicationWithProcessIdentifier:process_id_];
   if (!ra) {
     callback_->OnCaptureCompleted(NULL);
     return;
   }
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc
@@ -25,16 +25,17 @@ public:
 
   // AppCapturer interface.
   virtual bool GetAppList(AppList* apps) override;
   virtual bool SelectApp(ProcessId id) override;
   virtual bool BringAppToFront()	override;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) override;
+  virtual void Stop() override;
   virtual void Capture(const DesktopRegion& region) override;
 
 private:
   Callback* callback_;
 
   DISALLOW_COPY_AND_ASSIGN(AppCapturerNull);
 };
 
@@ -63,16 +64,20 @@ bool AppCapturerNull::BringAppToFront() 
 // DesktopCapturer interface.
 void AppCapturerNull::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void AppCapturerNull::Stop() {
+  callback_ = NULL;
+}
+
 void AppCapturerNull::Capture(const DesktopRegion& region) {
   // Not implemented yet: See Bug 1036653
   callback_->OnCaptureCompleted(NULL);
 }
 
 }  // namespace
 
 // static
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc
@@ -75,16 +75,17 @@ public:
 
   // AppCapturer interface.
   virtual bool GetAppList(AppList* apps) override;
   virtual bool SelectApp(ProcessId processId) override;
   virtual bool BringAppToFront() override;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) override;
+  virtual void Stop() override;
   virtual void Capture(const DesktopRegion& region) override;
 
   struct WindowItem {
     HWND handle;
     RECT bounds;
     bool owned;
   };
 
@@ -160,16 +161,19 @@ bool AppCapturerWin::BringAppToFront() {
 
 // DesktopCapturer interface.
 void AppCapturerWin::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
+void AppCapturerWin::Stop() {
+  callback_ = NULL;
+}
 void AppCapturerWin::Capture(const DesktopRegion& region) {
   assert(IsGUIThread(false));
   CaptureBySample(region);
 }
 
 BOOL CALLBACK AppCapturerWin::EnumWindowsProc(HWND handle, LPARAM lParam) {
   EnumWindowsCtx *pEnumWindowsCtx = reinterpret_cast<EnumWindowsCtx *>(lParam);
   if (!pEnumWindowsCtx) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc
@@ -79,16 +79,17 @@ public:
 
   // AppCapturer interface.
   virtual bool GetAppList(AppList* apps) override;
   virtual bool SelectApp(ProcessId processId) override;
   virtual bool BringAppToFront() override;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) override;
+  virtual void Stop() override;
   virtual void Capture(const DesktopRegion& region) override;
 
 protected:
   Display* GetDisplay() { return x_display_->display(); }
   bool UpdateRegions();
 
   void CaptureWebRTC(const DesktopRegion& region);
   void CaptureSample(const DesktopRegion& region);
@@ -152,16 +153,20 @@ bool AppCapturerLinux::BringAppToFront()
 // DesktopCapturer interface.
 void AppCapturerLinux::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void AppCapturerLinux::Stop() {
+  callback_ = NULL;
+}
+
 void AppCapturerLinux::Capture(const DesktopRegion& region) {
   CaptureSample(region);
 }
 
 void AppCapturerLinux::CaptureWebRTC(const DesktopRegion& region) {
   XErrorTrap error_trap(GetDisplay());
 
   int nScreenWidth = DisplayWidth(GetDisplay(), DefaultScreen(GetDisplay()));
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.cc
@@ -26,16 +26,21 @@ CroppingWindowCapturer::CroppingWindowCa
 
 CroppingWindowCapturer::~CroppingWindowCapturer() {}
 
 void CroppingWindowCapturer::Start(DesktopCapturer::Callback* callback) {
   callback_ = callback;
   window_capturer_->Start(callback);
 }
 
+void CroppingWindowCapturer::Stop() {
+  window_capturer_->Stop();
+  callback_ = NULL;
+}
+
 void CroppingWindowCapturer::Capture(const DesktopRegion& region) {
   if (ShouldUseScreenCapturer()) {
     if (!screen_capturer_.get()) {
       screen_capturer_.reset(ScreenCapturer::Create(options_));
       if (excluded_window_) {
         screen_capturer_->SetExcludedWindow(excluded_window_);
       }
       screen_capturer_->Start(this);
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer.h
@@ -24,16 +24,17 @@ namespace webrtc {
 class CroppingWindowCapturer : public WindowCapturer,
                                public DesktopCapturer::Callback {
  public:
   static WindowCapturer* Create(const DesktopCaptureOptions& options);
   virtual ~CroppingWindowCapturer();
 
   // DesktopCapturer implementation.
   void Start(DesktopCapturer::Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
   void SetExcludedWindow(WindowId window) override;
 
   // WindowCapturer implementation.
   bool GetWindowList(WindowList* windows) override;
   bool SelectWindow(WindowId id) override;
   bool BringSelectedWindowToFront() override;
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc
@@ -127,20 +127,27 @@ DesktopAndCursorComposer::DesktopAndCurs
       mouse_monitor_(mouse_monitor) {
 }
 
 DesktopAndCursorComposer::~DesktopAndCursorComposer() {}
 
 void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
   callback_ = callback;
   if (mouse_monitor_.get())
-    mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+    mouse_monitor_->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION);
   desktop_capturer_->Start(this);
 }
 
+void DesktopAndCursorComposer::Stop() {
+  desktop_capturer_->Stop();
+  if (mouse_monitor_.get())
+    mouse_monitor_->Stop();
+  callback_ = NULL;
+}
+
 void DesktopAndCursorComposer::Capture(const DesktopRegion& region) {
   if (mouse_monitor_.get())
     mouse_monitor_->Capture();
   desktop_capturer_->Capture(region);
 }
 
 void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
   desktop_capturer_->SetExcludedWindow(window);
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h
@@ -28,16 +28,17 @@ class DesktopAndCursorComposer : public 
   // |mouse_monitor| is NULL the frames are passed unmodified. Takes ownership
   // of both arguments.
   DesktopAndCursorComposer(DesktopCapturer* desktop_capturer,
                       MouseCursorMonitor* mouse_monitor);
   virtual ~DesktopAndCursorComposer();
 
   // DesktopCapturer interface.
   void Start(DesktopCapturer::Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
   void SetExcludedWindow(WindowId window) override;
 
  private:
   // DesktopCapturer::Callback interface.
   SharedMemory* CreateSharedMemory(size_t size) override;
   void OnCaptureCompleted(DesktopFrame* frame) override;
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h
@@ -39,18 +39,19 @@ class DesktopCapturer {
 
    protected:
     virtual ~Callback() {}
   };
 
   virtual ~DesktopCapturer() {}
 
   // Called at the beginning of a capturing session. |callback| must remain
-  // valid until capturer is destroyed.
+  // valid until capturer is destroyed or until Stop() is called
   virtual void Start(Callback* callback) = 0;
+  virtual void Stop() = 0;
 
   // Captures next frame. |region| specifies region of the capture target that
   // should be fresh in the resulting frame. The frame may also include fresh
   // data for areas outside |region|. In that case capturer will include these
   // areas in updated_region() of the frame. |region| is specified relative to
   // the top left corner of the capture target. Pending capture operations are
   // canceled when DesktopCapturer is deleted.
   virtual void Capture(const DesktopRegion& region) = 0;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor.h
@@ -68,18 +68,21 @@ class MouseCursorMonitor {
   // the entire desktop.
   //
   // TODO(sergeyu): Provide a way to select a specific screen.
   static MouseCursorMonitor* CreateForScreen(
       const DesktopCaptureOptions& options,
       ScreenId screen);
 
   // Initializes the monitor with the |callback|, which must remain valid until
-  // capturer is destroyed.
-  virtual void Init(Callback* callback, Mode mode) = 0;
+  // capturer is destroyed or until Stop()
+  virtual void Start(Callback* callback, Mode mode) = 0;
+
+  // clears the callback
+  virtual void Stop() = 0;
 
   // Captures current cursor shape and position (depending on the |mode| passed
   // to Init()). Calls Callback::OnMouseCursor() if cursor shape has
   // changed since the last call (or when Capture() is called for the first
   // time) and then Callback::OnMouseCursorPosition() if mode is set to
   // SHAPE_AND_POSITION.
   virtual void Capture() = 0;
 };
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm
@@ -30,17 +30,18 @@ namespace webrtc {
 
 class MouseCursorMonitorMac : public MouseCursorMonitor {
  public:
   MouseCursorMonitorMac(const DesktopCaptureOptions& options,
                         CGWindowID window_id,
                         ScreenId screen_id);
   virtual ~MouseCursorMonitorMac();
 
-  void Init(Callback* callback, Mode mode) override;
+  void Start(Callback* callback, Mode mode) override;
+  void Stop() override;
   void Capture() override;
 
  private:
   static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
                                            CGDisplayChangeSummaryFlags flags,
                                            void *user_parameter);
   void DisplaysReconfigured(CGDirectDisplayID display,
                             CGDisplayChangeSummaryFlags flags);
@@ -73,24 +74,28 @@ MouseCursorMonitorMac::MouseCursorMonito
       rtc::GetOSVersionName() < rtc::kMacOSLion) {
     // Single screen capture is not supported on pre OS X 10.7.
     screen_id_ = kFullDesktopScreenId;
   }
 }
 
 MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
 
-void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
+void MouseCursorMonitorMac::Start(Callback* callback, Mode mode) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
   mode_ = mode;
 }
 
+void MouseCursorMonitorMac::Stop() {
+  callback_ = NULL;
+}
+
 void MouseCursorMonitorMac::Capture() {
   assert(callback_);
 
   CaptureImage();
 
   if (mode_ != SHAPE_AND_POSITION)
     return;
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc
@@ -62,17 +62,17 @@ class MouseCursorMonitorTest : public te
 #endif
 
 TEST_F(MouseCursorMonitorTest, MAYBE(FromScreen)) {
   rtc::scoped_ptr<MouseCursorMonitor> capturer(
       MouseCursorMonitor::CreateForScreen(
           DesktopCaptureOptions::CreateDefault(),
           webrtc::kFullDesktopScreenId));
   assert(capturer.get());
-  capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+  capturer->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION);
   capturer->Capture();
 
   EXPECT_TRUE(cursor_image_.get());
   EXPECT_GE(cursor_image_->hotspot().x(), 0);
   EXPECT_LE(cursor_image_->hotspot().x(),
             cursor_image_->image()->size().width());
   EXPECT_GE(cursor_image_->hotspot().y(), 0);
   EXPECT_LE(cursor_image_->hotspot().y(),
@@ -101,31 +101,31 @@ TEST_F(MouseCursorMonitorTest, MAYBE(Fro
     cursor_image_.reset();
     position_received_ = false;
 
     rtc::scoped_ptr<MouseCursorMonitor> capturer(
         MouseCursorMonitor::CreateForWindow(
             DesktopCaptureOptions::CreateDefault(), windows[i].id));
     assert(capturer.get());
 
-    capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+    capturer->Start(this, MouseCursorMonitor::SHAPE_AND_POSITION);
     capturer->Capture();
 
     EXPECT_TRUE(cursor_image_.get());
     EXPECT_TRUE(position_received_);
   }
 }
 
 // Make sure that OnMouseCursorPosition() is not called in the SHAPE_ONLY mode.
 TEST_F(MouseCursorMonitorTest, MAYBE(ShapeOnly)) {
   rtc::scoped_ptr<MouseCursorMonitor> capturer(
       MouseCursorMonitor::CreateForScreen(
           DesktopCaptureOptions::CreateDefault(),
           webrtc::kFullDesktopScreenId));
   assert(capturer.get());
-  capturer->Init(this, MouseCursorMonitor::SHAPE_ONLY);
+  capturer->Start(this, MouseCursorMonitor::SHAPE_ONLY);
   capturer->Capture();
 
   EXPECT_TRUE(cursor_image_.get());
   EXPECT_FALSE(position_received_);
 }
 
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc
@@ -21,17 +21,18 @@
 namespace webrtc {
 
 class MouseCursorMonitorWin : public MouseCursorMonitor {
  public:
   explicit MouseCursorMonitorWin(HWND window);
   explicit MouseCursorMonitorWin(ScreenId screen);
   virtual ~MouseCursorMonitorWin();
 
-  void Init(Callback* callback, Mode mode) override;
+  void Start(Callback* callback, Mode mode) override;
+  void Stop() override;
   void Capture() override;
 
  private:
   // Get the rect of the currently selected screen, relative to the primary
   // display's top-left. If the screen is disabled or disconnected, or any error
   // happens, an empty rect is returned.
   DesktopRect GetScreenRect();
 
@@ -65,27 +66,35 @@ MouseCursorMonitorWin::MouseCursorMonito
   assert(screen >= kFullDesktopScreenId);
 }
 
 MouseCursorMonitorWin::~MouseCursorMonitorWin() {
   if (desktop_dc_)
     ReleaseDC(NULL, desktop_dc_);
 }
 
-void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
+void MouseCursorMonitorWin::Start(Callback* callback, Mode mode) {
   assert(!callback_);
   assert(callback);
   assert(IsGUIThread(false));
 
   callback_ = callback;
   mode_ = mode;
 
   desktop_dc_ = GetDC(NULL);
 }
 
+void MouseCursorMonitorWin::Stop() {
+  callback_ = NULL;
+
+  if (desktop_dc_)
+    ReleaseDC(NULL, desktop_dc_);
+  desktop_dc_ = NULL;
+}
+
 void MouseCursorMonitorWin::Capture() {
   assert(IsGUIThread(false));
   assert(callback_);
 
   CURSORINFO cursor_info;
   cursor_info.cbSize = sizeof(CURSORINFO);
   if (!GetCursorInfo(&cursor_info)) {
     LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc
@@ -59,17 +59,18 @@ Window GetTopLevelWindow(Display* displa
 namespace webrtc {
 
 class MouseCursorMonitorX11 : public MouseCursorMonitor,
                               public SharedXDisplay::XEventHandler {
  public:
   MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window, Window inner_window);
   virtual ~MouseCursorMonitorX11();
 
-  void Init(Callback* callback, Mode mode) override;
+  void Start(Callback* callback, Mode mode) override;
+  void Stop() override;
   void Capture() override;
 
  private:
   // SharedXDisplay::XEventHandler interface.
   bool HandleXEvent(const XEvent& event) override;
 
   Display* display() { return x_display_->display(); }
 
@@ -98,24 +99,21 @@ MouseCursorMonitorX11::MouseCursorMonito
       mode_(SHAPE_AND_POSITION),
       window_(window),
       inner_window_(inner_window),
       have_xfixes_(false),
       xfixes_event_base_(-1),
       xfixes_error_base_(-1) {}
 
 MouseCursorMonitorX11::~MouseCursorMonitorX11() {
-  if (have_xfixes_) {
-    x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
-                                   this);
-  }
+  Stop();
 }
 
-void MouseCursorMonitorX11::Init(Callback* callback, Mode mode) {
-  // Init can be called only once per instance of MouseCursorMonitor.
+void MouseCursorMonitorX11::Start(Callback* callback, Mode mode) {
+  // Start can be called only if not started
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
   mode_ = mode;
 
   have_xfixes_ =
       XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_);
@@ -127,16 +125,24 @@ void MouseCursorMonitorX11::Init(Callbac
     x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this);
 
     CaptureCursor();
   } else {
     LOG(LS_INFO) << "X server does not support XFixes.";
   }
 }
 
+void MouseCursorMonitorX11::Stop() {
+  callback_ = NULL;
+  if (have_xfixes_) {
+    x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
+                                   this);
+  }
+}
+
 void MouseCursorMonitorX11::Capture() {
   assert(callback_);
 
   // Process X11 events in case XFixes has sent cursor notification.
   x_display_->ProcessPendingXEvents();
 
   // cursor_shape_| is set only if we were notified of a cursor shape change.
   if (cursor_shape_.get())
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm
@@ -192,16 +192,17 @@ class ScreenCapturerMac : public ScreenC
   explicit ScreenCapturerMac(
       scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor);
   virtual ~ScreenCapturerMac();
 
   bool Init();
 
   // Overridden from ScreenCapturer:
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
   void SetExcludedWindow(WindowId window) override;
   bool GetScreenList(ScreenList* screens) override;
   bool SelectScreen(ScreenId id) override;
 
  private:
   void GlBlitFast(const DesktopFrame& frame,
                   const DesktopRegion& region);
@@ -380,16 +381,29 @@ void ScreenCapturerMac::Start(Callback* 
   // This assertion ensures that the display is woken up if it  already asleep
   // (as used by Apple Remote Desktop).
   IOPMAssertionCreateWithName(CFSTR("UserIsActive"),
                               kIOPMAssertionLevelOn,
                               CFSTR("Chrome Remote Desktop connection active"),
                               &power_assertion_id_user_);
 }
 
+void ScreenCapturerMac::Stop() {
+  if (power_assertion_id_display_ != kIOPMNullAssertionID) {
+    IOPMAssertionRelease(power_assertion_id_display_);
+    power_assertion_id_display_ = kIOPMNullAssertionID;
+  }
+  if (power_assertion_id_user_ != kIOPMNullAssertionID) {
+    IOPMAssertionRelease(power_assertion_id_user_);
+    power_assertion_id_user_ = kIOPMNullAssertionID;
+  }
+
+  callback_ = NULL;
+}
+
 void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
   TickTime capture_start_time = TickTime::Now();
 
   queue_.MoveToNextFrame();
 
   desktop_config_monitor_->Lock();
   MacDesktopConfiguration new_config =
       desktop_config_monitor_->desktop_configuration();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc
@@ -46,16 +46,17 @@ class ScreenCapturerLinux : public Scree
   ScreenCapturerLinux();
   virtual ~ScreenCapturerLinux();
 
   // TODO(ajwong): Do we really want this to be synchronous?
   bool Init(const DesktopCaptureOptions& options);
 
   // DesktopCapturer interface.
   void Start(Callback* delegate) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
 
   // ScreenCapturer interface.
   bool GetScreenList(ScreenList* screens) override;
   bool SelectScreen(ScreenId id) override;
 
  private:
   Display* display() { return options_.x_display()->display(); }
@@ -234,16 +235,20 @@ void ScreenCapturerLinux::InitXDamage() 
 
 void ScreenCapturerLinux::Start(Callback* callback) {
   DCHECK(!callback_);
   DCHECK(callback);
 
   callback_ = callback;
 }
 
+void ScreenCapturerLinux::Stop() {
+  callback_ = NULL;
+}
+
 void ScreenCapturerLinux::Capture(const DesktopRegion& region) {
   TickTime capture_start_time = TickTime::Now();
 
   queue_.MoveToNextFrame();
 
   // Process XEvents for XDamage and cursor shape tracking.
   options_.x_display()->ProcessPendingXEvents();
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -54,26 +54,17 @@ ScreenCapturerWinGdi::ScreenCapturerWinG
     composition_enabled_func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>
       (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
   }
 
   disable_composition_ = options.disable_effects();
 }
 
 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
-  if (desktop_dc_)
-    ReleaseDC(NULL, desktop_dc_);
-  if (memory_dc_)
-    DeleteDC(memory_dc_);
-
-  if (disable_composition_) {
-    // Restore Aero.
-    if (composition_func_)
-      (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
-  }
+  Stop();
 
   if (dwmapi_library_)
     FreeLibrary(dwmapi_library_);
 }
 
 void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
   assert(IsGUIThread(false));
   TickTime capture_start_time = TickTime::Now();
@@ -160,16 +151,34 @@ void ScreenCapturerWinGdi::Start(Callbac
     // Vote to disable Aero composited desktop effects while capturing. Windows
     // will restore Aero automatically if the process exits. This has no effect
     // under Windows 8 or higher.  See crbug.com/124018.
     if (composition_func_)
       (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
   }
 }
 
+void ScreenCapturerWinGdi::Stop() {
+  if (desktop_dc_) {
+    ReleaseDC(NULL, desktop_dc_);
+    desktop_dc_ = NULL;
+  }
+  if (memory_dc_) {
+    DeleteDC(memory_dc_);
+    memory_dc_ = NULL;
+  }
+
+  if (disable_composition_) {
+    // Restore Aero.
+    if (composition_func_)
+      (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
+  }
+  callback_ = NULL;
+}
+
 void ScreenCapturerWinGdi::PrepareCaptureResources() {
   assert(IsGUIThread(false));
   // Switch to the desktop receiving user input if different from the current
   // one.
   rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
     // Release GDI resources otherwise SetThreadDesktop will fail.
     if (desktop_dc_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -29,16 +29,17 @@ class Differ;
 // ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer.
 class ScreenCapturerWinGdi : public ScreenCapturer {
  public:
   explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options);
   virtual ~ScreenCapturerWinGdi();
 
   // Overridden from ScreenCapturer:
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
   bool GetScreenList(ScreenList* screens) override;
   bool SelectScreen(ScreenId id) override;
 
  private:
   typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT);
   typedef HRESULT (WINAPI * DwmIsCompositionEnabledFunc)(BOOL*);
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
@@ -52,39 +52,53 @@ ScreenCapturerWinMagnifier::ScreenCaptur
       set_image_scaling_callback_func_(NULL),
       host_window_(NULL),
       magnifier_window_(NULL),
       magnifier_initialized_(false),
       magnifier_capture_succeeded_(true) {
 }
 
 ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
-  // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
-  // destroyed automatically when host_window_ is destroyed.
-  if (host_window_)
-    DestroyWindow(host_window_);
-
-  if (magnifier_initialized_)
-    mag_uninitialize_func_();
-
-  if (mag_lib_handle_)
-    FreeLibrary(mag_lib_handle_);
-
-  if (desktop_dc_)
-    ReleaseDC(NULL, desktop_dc_);
+  Stop();
 }
 
 void ScreenCapturerWinMagnifier::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
   callback_ = callback;
 
   InitializeMagnifier();
 }
 
+void ScreenCapturerWinMagnifier::Stop() {
+  callback_ = NULL;
+
+  // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
+  // destroyed automatically when host_window_ is destroyed.
+  if (host_window_) {
+    DestroyWindow(host_window_);
+    host_window_ = NULL;
+  }
+
+  if (magnifier_initialized_) {
+    mag_uninitialize_func_();
+    magnifier_initialized_ = false;
+  }
+
+  if (mag_lib_handle_) {
+    FreeLibrary(mag_lib_handle_);
+    mag_lib_handle_ = NULL;
+  }
+
+  if (desktop_dc_) {
+    ReleaseDC(NULL, desktop_dc_);
+    desktop_dc_ = NULL;
+  }
+}
+
 void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) {
   TickTime capture_start_time = TickTime::Now();
 
   queue_.MoveToNextFrame();
 
   // Request that the system not power-down the system, or the display hardware.
   if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
     if (!set_thread_execution_state_failed_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
@@ -39,16 +39,17 @@ class ScreenCapturerWinMagnifier : publi
   // screen is being captured, or the OS does not support Magnification API, or
   // the magnifier capturer fails (e.g. in Windows8 Metro mode).
   explicit ScreenCapturerWinMagnifier(
       rtc::scoped_ptr<ScreenCapturer> fallback_capturer);
   virtual ~ScreenCapturerWinMagnifier();
 
   // Overridden from ScreenCapturer:
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
   bool GetScreenList(ScreenList* screens) override;
   bool SelectScreen(ScreenId id) override;
   void SetExcludedWindow(WindowId window) override;
 
  private:
   typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd,
                                                 void* srcdata,
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm
@@ -51,16 +51,17 @@ class WindowCapturerMac : public WindowC
 
   // WindowCapturer interface.
   bool GetWindowList(WindowList* windows) override;
   bool SelectWindow(WindowId id) override;
   bool BringSelectedWindowToFront() override;
 
   // DesktopCapturer interface.
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
 
  private:
   Callback* callback_;
 
   // The window being captured.
   CGWindowID window_id_;
 
@@ -182,16 +183,20 @@ bool WindowCapturerMac::BringSelectedWin
 
 void WindowCapturerMac::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void WindowCapturerMac::Stop() {
+  callback_ = NULL;
+}
+
 void WindowCapturerMac::Capture(const DesktopRegion& region) {
   if (!IsWindowValid(window_id_)) {
     callback_->OnCaptureCompleted(NULL);
     return;
   }
 
   CGWindowID on_screen_window = window_id_;
   if (full_screen_chrome_window_detector_) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
@@ -25,16 +25,17 @@ class WindowCapturerNull : public Window
 
   // WindowCapturer interface.
   bool GetWindowList(WindowList* windows) override;
   bool SelectWindow(WindowId id) override;
   bool BringSelectedWindowToFront() override;
 
   // DesktopCapturer interface.
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
 
  private:
   Callback* callback_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull);
 };
 
@@ -62,16 +63,20 @@ bool WindowCapturerNull::BringSelectedWi
 
 void WindowCapturerNull::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void WindowCapturerNull::Stop() {
+  callback_ = NULL;
+}
+
 void WindowCapturerNull::Capture(const DesktopRegion& region) {
   // Not implemented yet.
   callback_->OnCaptureCompleted(NULL);
 }
 
 }  // namespace
 
 // static
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
@@ -89,16 +89,17 @@ class WindowCapturerWin : public WindowC
 
   // WindowCapturer interface.
   bool GetWindowList(WindowList* windows) override;
   bool SelectWindow(WindowId id) override;
   bool BringSelectedWindowToFront() override;
 
   // DesktopCapturer interface.
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
 
  private:
   bool IsAeroEnabled();
 
   Callback* callback_;
 
   // HWND and HDC for the currently selected window or NULL if window is not
@@ -174,16 +175,20 @@ bool WindowCapturerWin::BringSelectedWin
 
 void WindowCapturerWin::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void WindowCapturerWin::Stop() {
+  callback_ = NULL;
+}
+
 void WindowCapturerWin::Capture(const DesktopRegion& region) {
   assert(IsGUIThread(false));
   if (!window_) {
     LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError();
     callback_->OnCaptureCompleted(NULL);
     return;
   }
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -41,16 +41,17 @@ class WindowCapturerLinux : public Windo
 
   // WindowCapturer interface.
   bool GetWindowList(WindowList* windows) override;
   bool SelectWindow(WindowId id) override;
   bool BringSelectedWindowToFront() override;
 
   // DesktopCapturer interface.
   void Start(Callback* callback) override;
+  void Stop() override;
   void Capture(const DesktopRegion& region) override;
 
   // SharedXDisplay::XEventHandler interface.
   bool HandleXEvent(const XEvent& event) override;
 
  private:
   Display* display() { return x_display_->display(); }
 
@@ -230,16 +231,20 @@ bool WindowCapturerLinux::BringSelectedW
 
 void WindowCapturerLinux::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
+void WindowCapturerLinux::Stop() {
+  callback_ = NULL;
+}
+
 void WindowCapturerLinux::Capture(const DesktopRegion& region) {
   x_display_->ProcessPendingXEvents();
 
   if (!x_server_pixel_buffer_.IsWindowValid()) {
     LOG(LS_INFO) << "The window is no longer valid.";
     callback_->OnCaptureCompleted(NULL);
     return;
   }
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -467,20 +467,21 @@ DesktopCaptureImpl::DesktopCaptureImpl(c
     _lastProcessFrameCount(TickTime::Now()),
     _rotateFrame(kVideoRotation_0),
   last_capture_time_(TickTime::MillisecondTimestamp()),
   delta_ntp_internal_ms_(
                          Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() -
                          TickTime::MillisecondTimestamp()),
   time_event_(EventWrapper::Create()),
 #if defined(_WIN32)
-  capturer_thread_(ThreadWrapper::CreateUIThread(Run, this, "ScreenCaptureThread")) {
+  capturer_thread_(ThreadWrapper::CreateUIThread(Run, this, "ScreenCaptureThread")),
 #else
-  capturer_thread_(ThreadWrapper::CreateThread(Run, this, "ScreenCaptureThread")) {
+  capturer_thread_(ThreadWrapper::CreateThread(Run, this, "ScreenCaptureThread")),
 #endif
+  started_(false) {
   capturer_thread_->SetPriority(kHighPriority);
   _requestedCapability.width = kDefaultWidth;
   _requestedCapability.height = kDefaultHeight;
   _requestedCapability.maxFPS = 30;
   _requestedCapability.rawType = kVideoI420;
   _requestedCapability.codecType = kVideoCodecUnknown;
   memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
 }
@@ -778,26 +779,33 @@ int32_t DesktopCaptureImpl::StartCapture
   _requestedCapability = capability;
 #if defined(_WIN32)
   uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
   capturer_thread_->RequestCallbackTimer(maxFPSNeeded);
 #endif
 
   desktop_capturer_cursor_composer_->Start(this);
   capturer_thread_->Start();
+  started_ = true;
 
   return 0;
 }
 
 int32_t DesktopCaptureImpl::StopCapture() {
+  if (started_) {
+    capturer_thread_->Stop(); // thread is guaranteed stopped before this returns
+    desktop_capturer_cursor_composer_->Stop();
+    started_ = false;
+    return 0;
+  }
   return -1;
 }
 
 bool DesktopCaptureImpl::CaptureStarted() {
-  return false;
+  return started_;
 }
 
 int32_t DesktopCaptureImpl::CaptureSettings(VideoCaptureCapability& settings) {
   return -1;
 }
 
 void DesktopCaptureImpl::OnCaptureCompleted(DesktopFrame* frame) {
   if (frame == NULL) return;
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h
@@ -256,13 +256,14 @@ public:
     return true;
   };
   void process();
 
 private:
   rtc::scoped_ptr<DesktopAndCursorComposer> desktop_capturer_cursor_composer_;
   rtc::scoped_ptr<EventWrapper> time_event_;
   rtc::scoped_ptr<ThreadWrapper> capturer_thread_;
+  bool started_;
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_MAIN_SOURCE_DESKTOP_CAPTURE_IMPL_H_
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -386,17 +386,17 @@ pref("media.apple.mp4.enabled", true);
 // media.gmp.storage.version.observed, and if the versions don't match,
 // we clear storage and set media.gmp.storage.version.observed=expected.
 // This provides a mechanism to clear GMP storage when non-compatible
 // changes are made.
 pref("media.gmp.storage.version.expected", 1);
 
 // Filter what triggers user notifications.
 // See DecoderDoctorDocumentWatcher::ReportAnalysis for details.
-pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMFNoSilverlight,MediaCannotInitializePulseAudio");
+pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMFNoSilverlight,MediaCannotInitializePulseAudio,MediaCannotPlayNoDecoders,MediaNoDecoders");
 // Whether we report partial failures.
 pref("media.decoder-doctor.verbose", false);
 // Whether DD should consider WMF-disabled a WMF failure, useful for testing.
 pref("media.decoder-doctor.wmf-disabled-is-failure", false);
 
 // Whether to suspend decoding of videos in background tabs.
 #ifdef NIGHTLY_BUILD
 pref("media.suspend-bkgnd-video.enabled", true);
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -16,16 +16,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIFrameLoader.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsGlobalWindow.h"
+#include "nsNullPrincipal.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace net {
 
 static void
 InheritOriginAttributes(nsIPrincipal* aLoadingPrincipal, NeckoOriginAttributes& aAttrs)
@@ -539,16 +540,24 @@ NS_IMETHODIMP
 LoadInfo::GetForceInheritPrincipal(bool* aInheritPrincipal)
 {
   *aInheritPrincipal =
     (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetForceInheritPrincipalOverruleOwner(bool* aInheritPrincipal)
+{
+  *aInheritPrincipal =
+    (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetLoadingSandboxed(bool* aLoadingSandboxed)
 {
   *aLoadingSandboxed = (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LoadInfo::GetAboutBlankInherits(bool* aResult)
@@ -682,16 +691,44 @@ LoadInfo::GetScriptableOriginAttributes(
 {
   if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::ResetPrincipalsToNullPrincipal()
+{
+  // take the originAttributes from the LoadInfo and create
+  // a new NullPrincipal using those origin attributes.
+  PrincipalOriginAttributes pAttrs;
+  pAttrs.InheritFromNecko(mOriginAttributes);
+  nsCOMPtr<nsIPrincipal> newNullPrincipal = nsNullPrincipal::Create(pAttrs);
+
+  MOZ_ASSERT(mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT ||
+             !mLoadingPrincipal,
+             "LoadingPrincipal should be null for toplevel loads");
+
+  // the loadingPrincipal for toplevel loads is always a nullptr;
+  if (mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT) {
+    mLoadingPrincipal = newNullPrincipal;
+  }
+  mTriggeringPrincipal = newNullPrincipal;
+  mPrincipalToInherit = newNullPrincipal;
+
+  // setting SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER will overrule
+  // any non null owner set on the channel and will return the principal
+  // form the loadinfo instead.
+  mSecurityFlags |= SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::SetScriptableOriginAttributes(JSContext* aCx,
   JS::Handle<JS::Value> aOriginAttributes)
 {
   NeckoOriginAttributes attrs;
   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -176,16 +176,28 @@ interface nsILoadInfo : nsISupports
 
   /**
    * Load an error page, it should be one of following : about:neterror,
    * about:certerror, about:blocked, or about:tabcrashed.
    */
   const unsigned long SEC_LOAD_ERROR_PAGE = (1<<13);
 
   /**
+   * Force inheriting of the principalToInherit, overruling any owner
+   * that might be set on the channel. (Please note that channel.owner
+   * is deprecated and will be removed within Bug 1286838).
+   * Setting this flag will cause GetChannelResultPrincipal to return the
+   * principalToInherit set in the loadInfo.
+   *
+   * This will happen independently of the scheme of the URI that the
+   * channel is loading.
+   */
+  const unsigned long SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER = (1<<14);
+
+  /**
    * This is the principal of the network request's caller/requester where
    * the resulting resource will be used. I.e. it is the principal which
    * will get access to the result of the request. (Where "get access to"
    * might simply mean "embed" depending on the type of resource that is
    * loaded).
    *
    * For example for an image, it is the principal of the document where
    * the image is rendered. For a stylesheet it is the principal of the
@@ -348,16 +360,24 @@ interface nsILoadInfo : nsISupports
    * If forceInheritPrincipal is true, the data coming from the channel should
    * use loadingPrincipal for its principal, even when the data is loaded over
    * http:// or another protocol that would normally use a URI-based principal.
    * This attribute will never be true when loadingSandboxed is true.
    */
   [infallible] readonly attribute boolean forceInheritPrincipal;
 
   /**
+   * If forceInheritPrincipalOverruleOwner is true, the data coming from the
+   * channel should use principalToInherit for its principal, even when the
+   * data is loaded over http:// or another protocol that would normally use
+   * a URI-based principal.
+   */
+  [infallible] readonly attribute boolean forceInheritPrincipalOverruleOwner;
+
+  /**
    * If loadingSandboxed is true, the data coming from the channel is
    * being loaded sandboxed, so it should have a nonce origin and
    * hence should use a NullPrincipal.
    */
   [infallible] readonly attribute boolean loadingSandboxed;
 
   /**
    * If aboutBlankInherits is true, then about:blank should inherit
@@ -473,16 +493,32 @@ interface nsILoadInfo : nsISupports
    * the frameOuterWindowID is the outer window containing the
    * foo.html document.
    *
    * Note: For other cases, frameOuterWindowID is 0.
    */
   [infallible] readonly attribute unsigned long long frameOuterWindowID;
 
   /**
+   * For all loads of none TYPE_DOUCMENT this function resets the 
+   * LoadingPrincipal, the TriggeringPrincipal and the
+   * PrincipalToInherit to a freshly created NullPrincipal which inherits
+   * the current origin attributes from the loadinfo.
+   * For loads of TYPE_DOCUMENT this function resets only the
+   * TriggeringPrincipal as well as the PrincipalToInherit to a freshly
+   * created NullPrincipal which inherits the origin attributes from
+   * the loadInfo. (Please note that the LoadingPrincipal for TYPE_DOCUMENT
+   * loads is always null.)
+   *
+   * WARNING: Please only use that function if you know exactly what
+   * you are doing!!!
+   */
+  void resetPrincipalsToNullPrincipal();
+
+  /**
    * Customized NeckoOriginAttributes within LoadInfo to allow overwriting of the
    * default originAttributes from the loadingPrincipal.
    *
    * In chrome side, originAttributes.privateBrowsingId will always be 0 even if
    * the usePrivateBrowsing is true, because chrome docshell won't set
    * privateBrowsingId on origin attributes (See bug 1278664). This is to make
    * sure nsILoadInfo and nsILoadContext have the same origin attributes.
    */
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -99,77 +99,109 @@ PBOverrideStatusFromLoadContext(const Se
   if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
     return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0) ?
       kPBOverride_Private :
       kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
+// Bug 1289001 - If GetValidatedAppInfo returns an error string, that usually
+// leads to a content crash with very little info about the cause.
+// We prefer to crash on the parent, so we get the reason in the crash report.
+static MOZ_NORETURN MOZ_COLD
+void CrashWithReason(const char * reason)
+{
+#ifndef RELEASE_BUILD
+  MOZ_CRASH_ANNOTATE(reason);
+  MOZ_REALLY_CRASH();
+#endif
+}
+
 const char*
 NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
                                  PContentParent* aContent,
                                  DocShellOriginAttributes& aAttrs)
 {
   if (UsingNeckoIPCSecurity()) {
     if (!aSerialized.IsNotNull()) {
+      CrashWithReason("GetValidatedAppInfo | SerializedLoadContext from child is null");
       return "SerializedLoadContext from child is null";
     }
   }
 
+  nsAutoCString debugString;
   nsTArray<TabContext> contextArray =
     static_cast<ContentParent*>(aContent)->GetManagedTabContext();
   for (uint32_t i = 0; i < contextArray.Length(); i++) {
     TabContext tabContext = contextArray[i];
     uint32_t appId = tabContext.OwnOrContainingAppId();
     bool inBrowserElement = aSerialized.IsNotNull() ?
                               aSerialized.mOriginAttributes.mInIsolatedMozBrowser :
                               tabContext.IsIsolatedMozBrowserElement();
 
     if (appId == NECKO_UNKNOWN_APP_ID) {
+      debugString.Append("u,");
       continue;
     }
     // We may get appID=NO_APP if child frame is neither a browser nor an app
     if (appId == NECKO_NO_APP_ID && tabContext.HasOwnApp()) {
       // NECKO_NO_APP_ID but also is an app?  Weird, skip.
+      debugString.Append("h,");
       continue;
     }
 
     if (!aSerialized.mOriginAttributes.mSignedPkg.IsEmpty() &&
         aSerialized.mOriginAttributes.mSignedPkg != tabContext.OriginAttributesRef().mSignedPkg) {
+      debugString.Append("s,");
       continue;
     }
     if (aSerialized.mOriginAttributes.mUserContextId != tabContext.OriginAttributesRef().mUserContextId) {
+      debugString.Append("(");
+      debugString.AppendInt(aSerialized.mOriginAttributes.mUserContextId);
+      debugString.Append(",");
+      debugString.AppendInt(tabContext.OriginAttributesRef().mUserContextId);
+      debugString.Append(")");
       continue;
     }
     aAttrs = DocShellOriginAttributes();
     aAttrs.mAppId = appId;
     aAttrs.mInIsolatedMozBrowser = inBrowserElement;
     aAttrs.mSignedPkg = aSerialized.mOriginAttributes.mSignedPkg;
     aAttrs.mUserContextId = aSerialized.mOriginAttributes.mUserContextId;
     aAttrs.mPrivateBrowsingId = aSerialized.mOriginAttributes.mPrivateBrowsingId;
     aAttrs.mFirstPartyDomain = aSerialized.mOriginAttributes.mFirstPartyDomain;
 
     return nullptr;
   }
 
   if (contextArray.Length() != 0) {
+    nsAutoCString errorString;
+    errorString.Append("GetValidatedAppInfo | App does not have permission -");
+    errorString.Append(debugString);
+
+    // Leak the buffer on the heap to make sure that it lives long enough, as
+    // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
+    // the program.
+    char * error = strdup(errorString.BeginReading());
+    CrashWithReason(error);
     return "App does not have permission";
   }
 
   if (!UsingNeckoIPCSecurity()) {
     // We are running xpcshell tests
     if (aSerialized.IsNotNull()) {
       aAttrs = aSerialized.mOriginAttributes;
     } else {
       aAttrs = DocShellOriginAttributes(NECKO_NO_APP_ID, false);
     }
     return nullptr;
   }
 
+  CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers");
   return "ContentParent does not have any PBrowsers";
 }
 
 const char *
 NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                                       PContentParent* aContent,
                                       const SerializedLoadContext& aSerialized,
                                       nsCOMPtr<nsILoadContext> &aResult)
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-e7553afc7665
+6b3812492e71
--- a/security/nss/cmd/derdump/derdump.c
+++ b/security/nss/cmd/derdump/derdump.c
@@ -29,47 +29,48 @@ Usage(char *progName)
 }
 
 int
 main(int argc, char **argv)
 {
     char *progName;
     FILE *outFile;
     PRFileDesc *inFile;
-    SECItem der;
+    SECItem der = { siBuffer, NULL, 0 };
     SECStatus rv;
     PRInt16 xp_error;
     PRBool raw = PR_FALSE;
     PLOptState *optstate;
     PLOptStatus status;
+    int retval = -1;
 
     progName = strrchr(argv[0], '/');
     progName = progName ? progName + 1 : argv[0];
 
     /* Parse command line arguments */
     inFile = 0;
     outFile = 0;
     optstate = PL_CreateOptState(argc, argv, "i:o:r");
     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
         switch (optstate->option) {
             case 'i':
                 inFile = PR_Open(optstate->value, PR_RDONLY, 0);
                 if (!inFile) {
                     fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
                             progName, optstate->value);
-                    return -1;
+                    goto cleanup;
                 }
                 break;
 
             case 'o':
                 outFile = fopen(optstate->value, "w");
                 if (!outFile) {
                     fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
                             progName, optstate->value);
-                    return -1;
+                    goto cleanup;
                 }
                 break;
 
             case 'r':
                 raw = PR_TRUE;
                 break;
 
             default:
@@ -80,30 +81,48 @@ main(int argc, char **argv)
     if (status == PL_OPT_BAD)
         Usage(progName);
 
     if (!inFile)
         inFile = PR_STDIN;
     if (!outFile)
         outFile = stdout;
 
-    rv = NSS_NoDB_Init(NULL); /* XXX */
+    rv = NSS_NoDB_Init(NULL);
     if (rv != SECSuccess) {
         SECU_PrintPRandOSError(progName);
-        return -1;
+        goto cleanup;
     }
 
     rv = SECU_ReadDERFromFile(&der, inFile, PR_FALSE, PR_FALSE);
     if (rv == SECSuccess) {
         rv = DER_PrettyPrint(outFile, &der, raw);
-        if (rv == SECSuccess)
-            return 0;
+        if (rv == SECSuccess) {
+            retval = 0;
+            goto cleanup;
+        }
     }
 
     xp_error = PORT_GetError();
     if (xp_error) {
         SECU_PrintError(progName, "error %d", xp_error);
     }
     if (errno) {
         SECU_PrintSystemError(progName, "errno=%d", errno);
     }
-    return 1;
+    retval = 1;
+
+cleanup:
+    retval |= NSS_Shutdown();
+    if (inFile) {
+        PR_Close(inFile);
+    }
+    if (outFile) {
+        fflush(outFile);
+        fclose(outFile);
+    }
+    PL_DestroyOptState(optstate);
+    if (der.data) {
+        PORT_Free(der.data);
+    }
+
+    return retval;
 }
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/coreconf/sanitizers.mk
+++ b/security/nss/coreconf/sanitizers.mk
@@ -1,13 +1,19 @@
 # Address Sanitizer support; include this in OS-specific .mk files
 # *after* defining the variables that are appended to here.
 
 ifeq ($(USE_ASAN), 1)
-SANITIZER_FLAGS_COMMON = -fsanitize=address $(EXTRA_SANITIZER_FLAGS)
+SANITIZER_FLAGS_COMMON = -fsanitize=address
+
+ifeq ($(USE_UBSAN), 1)
+SANITIZER_FLAGS_COMMON += -fsanitize=undefined -fno-sanitize-recover=undefined
+endif
+
+SANITIZER_FLAGS_COMMON += $(EXTRA_SANITIZER_FLAGS)
 SANITIZER_CFLAGS = $(SANITIZER_FLAGS_COMMON)
 SANITIZER_LDFLAGS = $(SANITIZER_FLAGS_COMMON)
 OS_CFLAGS += $(SANITIZER_CFLAGS)
 LDFLAGS += $(SANITIZER_LDFLAGS)
 
 # ASan needs frame pointers to save stack traces for allocation/free sites.
 # (Warning: some platforms, like ARM Linux in Thumb mode, don't have useful
 # frame pointers even with this option.)
--- a/security/nss/external_tests/ssl_gtest/databuffer.h
+++ b/security/nss/external_tests/ssl_gtest/databuffer.h
@@ -59,29 +59,34 @@ class DataBuffer {
       data_ = nullptr;
       len_ = 0;
     }
   }
 
   // Write will do a new allocation and expand the size of the buffer if needed.
   // Returns the offset of the end of the write.
   size_t Write(size_t index, const uint8_t* val, size_t count) {
+    assert(val);
     if (index + count > len_) {
       size_t newlen = index + count;
       uint8_t* tmp = new uint8_t[newlen];  // Always > 0.
-      memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_);
+      if (data_) {
+        memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_);
+      }
       if (index > len_) {
         memset(static_cast<void*>(tmp + len_), 0, index - len_);
       }
       delete[] data_;
       data_ = tmp;
       len_ = newlen;
     }
-    memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val),
-           count);
+    if (data_) {
+      memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val),
+             count);
+    }
     return index + count;
   }
 
   size_t Write(size_t index, const DataBuffer& buf) {
     return Write(index, buf.data(), buf.len());
   }
 
   // Write an integer, also performing host-to-network order conversion.
@@ -111,16 +116,17 @@ class DataBuffer {
   // Starting at |index|, remove |remove| bytes and replace them with the
   // contents of |buf|.
   void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) {
     Splice(buf.data(), buf.len(), index, remove);
   }
 
   void Splice(const uint8_t* ins, size_t ins_len, size_t index,
               size_t remove = 0) {
+    assert(ins);
     uint8_t* old_value = data_;
     size_t old_len = len_;
 
     // The amount of stuff remaining from the tail of the old.
     size_t tail_len = old_len - std::min(old_len, index + remove);
     // The new length: the head of the old, the new, and the tail of the old.
     len_ = index + ins_len + tail_len;
     data_ = new uint8_t[len_ ? len_ : 1];
--- a/security/nss/external_tests/ssl_gtest/libssl_internals.c
+++ b/security/nss/external_tests/ssl_gtest/libssl_internals.c
@@ -142,18 +142,19 @@ PRBool SSLInt_CheckSecretsDestroyed(PRFi
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return PR_FALSE;
   }
 
   CHECK_SECRET(currentSecret);
   CHECK_SECRET(resumptionPsk);
   CHECK_SECRET(dheSecret);
-  CHECK_SECRET(earlyTrafficSecret);
-  CHECK_SECRET(hsTrafficSecret);
+  CHECK_SECRET(clientEarlyTrafficSecret);
+  CHECK_SECRET(clientHsTrafficSecret);
+  CHECK_SECRET(serverHsTrafficSecret);
 
   return PR_TRUE;
 }
 
 PRBool sslint_DamageTrafficSecret(PRFileDesc *fd, size_t offset) {
   unsigned char data[32] = {0};
   PK11SymKey **keyPtr;
   PK11SlotInfo *slot = PK11_GetInternalSlot();
@@ -175,24 +176,29 @@ PRBool sslint_DamageTrafficSecret(PRFile
   PK11_FreeSlot(slot);
   if (!*keyPtr) {
     return PR_FALSE;
   }
 
   return PR_TRUE;
 }
 
-PRBool SSLInt_DamageHsTrafficSecret(PRFileDesc *fd) {
+PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd) {
   return sslint_DamageTrafficSecret(
-      fd, offsetof(SSL3HandshakeState, hsTrafficSecret));
+      fd, offsetof(SSL3HandshakeState, clientHsTrafficSecret));
+}
+
+PRBool SSLInt_DamageServerHsTrafficSecret(PRFileDesc *fd) {
+  return sslint_DamageTrafficSecret(
+      fd, offsetof(SSL3HandshakeState, serverHsTrafficSecret));
 }
 
 PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd) {
   return sslint_DamageTrafficSecret(
-      fd, offsetof(SSL3HandshakeState, earlyTrafficSecret));
+      fd, offsetof(SSL3HandshakeState, clientEarlyTrafficSecret));
 }
 
 SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
--- a/security/nss/external_tests/ssl_gtest/libssl_internals.h
+++ b/security/nss/external_tests/ssl_gtest/libssl_internals.h
@@ -23,17 +23,18 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom
                                          size_t msg_len);
 
 PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
 void SSLInt_ClearSessionTicketKey();
 PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd);
 void SSLInt_ForceTimerExpiry(PRFileDesc *fd);
 SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu);
 PRBool SSLInt_CheckSecretsDestroyed(PRFileDesc *fd);
-PRBool SSLInt_DamageHsTrafficSecret(PRFileDesc *fd);
+PRBool SSLInt_DamageClientHsTrafficSecret(PRFileDesc *fd);
+PRBool SSLInt_DamageServerHsTrafficSecret(PRFileDesc *fd);
 PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd);
 SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len);
 PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType);
 PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type);
 SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
--- a/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -22,41 +22,61 @@ extern "C" {
 
 namespace nss_test {
 
 TEST_P(TlsConnectTls13, ZeroRtt) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true);
+  ZeroRttSendReceive(true, true);
   Handshake();
   ExpectEarlyDataAccepted(true);
   CheckConnected();
   SendReceive();
 }
 
 TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(false);
+  ZeroRttSendReceive(true, false);
+  Handshake();
+  CheckConnected();
+  SendReceive();
+}
+
+// Test that we don't try to send 0-RTT data when the server sent
+// us a ticket without the 0-RTT flags.
+TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  Connect();
+  SendReceive();  // Need to read so that we absorb the session ticket.
+  CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
+  Reset();
+  server_->StartConnect();
+  client_->StartConnect();
+  // Now turn on 0-RTT but too late for the ticket.
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  ZeroRttSendReceive(false, false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
 TEST_P(TlsConnectTls13, ZeroRttServerForgetTicket) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ClearServerCache();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ExpectResumption(RESUME_NONE);
-  ZeroRttSendReceive(false);
+  ZeroRttSendReceive(true, false);
   Handshake();
   CheckConnected();
   SendReceive();
 }
 
 TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
   ExpectResumption(RESUME_NONE);
   server_->Set0RttEnabled(true);
@@ -82,17 +102,17 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnl
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
   EnableAlpn();
   SetupForZeroRtt();
   EnableAlpn();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ExpectEarlyDataAccepted(true);
-  ZeroRttSendReceive(true, [this]() {
+  ZeroRttSendReceive(true, true, [this]() {
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     return true;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("a");
 }
@@ -104,17 +124,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   SetupForZeroRtt();
   static const uint8_t client_alpn[] = {0x01, 0x61, 0x01, 0x62};  // "a", "b"
   static const uint8_t server_alpn[] = {0x01, 0x62};              // "b"
   client_->EnableAlpn(client_alpn, sizeof(client_alpn));
   server_->EnableAlpn(server_alpn, sizeof(server_alpn));
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(false, [this]() {
+  ZeroRttSendReceive(true, false, [this]() {
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     return true;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("b");
 }
@@ -125,17 +145,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
 // should then fail the connection.
 TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnServer) {
   EnableAlpn();
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   EnableAlpn();
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true, [this]() {
+  ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b)));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
     return true;
   });
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
@@ -145,17 +165,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
 // Set up with no ALPN and then set the client so it thinks it has ALPN.
 // The server responds without the extension and the client returns an
 // error.
 TEST_P(TlsConnectTls13, TestTls13ZeroRttNoAlpnClient) {
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(true, [this]() {
+  ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
     return true;
   });
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -165,17 +185,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpnChangeBoth) {
   EnableAlpn();
   SetupForZeroRtt();
   static const uint8_t alpn[] = {0x01, 0x62};  // "b"
   EnableAlpn(alpn, sizeof(alpn));
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  ZeroRttSendReceive(false, [this]() {
+  ZeroRttSendReceive(true, false, [this]() {
     client_->CheckAlpn(SSL_NEXT_PROTO_NO_SUPPORT);
     return false;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("b");
 }
--- a/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_damage_unittest.cc
@@ -29,17 +29,17 @@ TEST_F(TlsConnectTest, DamageSecretHandl
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->StartConnect();
   client_->StartConnect();
   client_->Handshake();
   server_->Handshake();
   std::cerr << "Damaging HS secret\n";
-  SSLInt_DamageHsTrafficSecret(server_->ssl_fd());
+  SSLInt_DamageClientHsTrafficSecret(server_->ssl_fd());
   client_->Handshake();
   server_->Handshake();
   // The client thinks it has connected.
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
   client_->Handshake();
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
 }
@@ -47,15 +47,15 @@ TEST_F(TlsConnectTest, DamageSecretHandl
 TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) {
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetPacketFilter(new AfterRecordN(
       server_, client_,
       0,  // ServerHello.
-      [this]() { SSLInt_DamageHsTrafficSecret(client_->ssl_fd()); }));
+      [this]() { SSLInt_DamageServerHsTrafficSecret(client_->ssl_fd()); }));
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 }  // namespace nspr_test
--- a/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_ecdh_unittest.cc
@@ -255,16 +255,33 @@ TEST_P(TlsConnectGeneric, P256andCurve25
   client_->ConfigNamedGroups(client_groups);
   server_->ConfigNamedGroups(server_groups);
 
   Connect();
 
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign, 255);
 }
 
+TEST_P(TlsConnectGeneric, P256ClientAndCurve25519Server) {
+  EnsureTlsSetup();
+  client_->DisableAllCiphers();
+  client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
+
+  // the client sends a P256 key share while the server prefers 25519.
+  const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ec_curve25519};
+  const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1};
+
+  client_->ConfigNamedGroups(client_groups);
+  server_->ConfigNamedGroups(server_groups);
+
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+  server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
 // Replace the point in the client key exchange message with an empty one
 class ECCClientKEXFilter : public TlsHandshakeFilter {
  public:
   ECCClientKEXFilter() {}
 
  protected:
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader &header,
                                                const DataBuffer &input,
--- a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -84,28 +84,32 @@ class TlsExtensionInjector : public TlsH
       offset = parser.consumed();
     } else {
       return KEEP;
     }
 
     *output = input;
 
     // Increase the size of the extensions.
-    uint16_t* len_addr = reinterpret_cast<uint16_t*>(output->data() + offset);
-    *len_addr = htons(ntohs(*len_addr) + data_.len() + 4);
+    uint16_t ext_len;
+    memcpy(&ext_len, output->data() + offset, sizeof(ext_len));
+    ext_len = htons(ntohs(ext_len) + data_.len() + 4);
+    memcpy(output->data() + offset, &ext_len, sizeof(ext_len));
 
     // Insert the extension type and length.
     DataBuffer type_length;
     type_length.Allocate(4);
     type_length.Write(0, extension_, 2);
     type_length.Write(2, data_.len(), 2);
     output->Splice(type_length, offset + 2);
 
     // Insert the payload.
-    output->Splice(data_, offset + 6);
+    if (data_.len() > 0) {
+      output->Splice(data_, offset + 6);
+    }
 
     return CHANGE;
   }
 
  private:
   const uint16_t extension_;
   const DataBuffer data_;
 };
@@ -178,16 +182,25 @@ class TlsExtensionTest12
 
 class TlsExtensionTest13 : public TlsExtensionTestBase,
                            public ::testing::WithParamInterface<std::string> {
  public:
   TlsExtensionTest13()
       : TlsExtensionTestBase(TlsConnectTestBase::ToMode(GetParam()),
                              SSL_LIBRARY_VERSION_TLS_1_3) {}
 
+  void ConnectWithBogusVersionList(const uint8_t* buf, size_t len) {
+    DataBuffer versions_buf(buf, len);
+    client_->SetPacketFilter(new TlsExtensionReplacer(
+        ssl_tls13_supported_versions_xtn, versions_buf));
+    ConnectExpectFail();
+    client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+    server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+  }
+
   void ConnectWithReplacementVersionList(uint16_t version) {
     DataBuffer versions_buf;
 
     size_t index = versions_buf.Write(0, 2, 1);
     versions_buf.Write(index, version, 2);
     client_->SetPacketFilter(new TlsExtensionReplacer(
         ssl_tls13_supported_versions_xtn, versions_buf));
     ConnectExpectFail();
@@ -509,16 +522,55 @@ TEST_P(TlsExtensionTest13, EmptyClientKe
 TEST_F(TlsExtensionTest13Stream, DropServerKeyShare) {
   EnsureTlsSetup();
   server_->SetPacketFilter(new TlsExtensionDropper(ssl_tls13_key_share_xtn));
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
+TEST_F(TlsExtensionTest13Stream, WrongServerKeyShare) {
+  const uint16_t wrong_group = ssl_grp_ec_secp384r1;
+
+  static const uint8_t key_share[] = {
+      wrong_group >> 8,
+      wrong_group & 0xff,  // Group we didn't offer.
+      0x00,
+      0x02,  // length = 2
+      0x01,
+      0x02};
+  DataBuffer buf(key_share, sizeof(key_share));
+  EnsureTlsSetup();
+  server_->SetPacketFilter(
+      new TlsExtensionReplacer(ssl_tls13_key_share_xtn, buf));
+  ConnectExpectFail();
+  EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code());
+  EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
+}
+
+// TODO(ekr@rtfm.com): This is the wrong error code. See bug 1307269.
+TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) {
+  const uint16_t wrong_group = 0xffff;
+
+  static const uint8_t key_share[] = {
+      wrong_group >> 8,
+      wrong_group & 0xff,  // Group we didn't offer.
+      0x00,
+      0x02,  // length = 2
+      0x01,
+      0x02};
+  DataBuffer buf(key_share, sizeof(key_share));
+  EnsureTlsSetup();
+  server_->SetPacketFilter(
+      new TlsExtensionReplacer(ssl_tls13_key_share_xtn, buf));
+  ConnectExpectFail();
+  EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
+  EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
+}
+
 TEST_F(TlsExtensionTest13Stream, DropServerSignatureAlgorithms) {
   EnsureTlsSetup();
   server_->SetPacketFilter(
       new TlsExtensionDropper(ssl_signature_algorithms_xtn));
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION,
             client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
@@ -719,16 +771,26 @@ TEST_P(TlsExtensionTest13, RemoveTls13Fr
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 #else
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 #endif
 }
 
+TEST_P(TlsExtensionTest13, EmptyVersionList) {
+  static const uint8_t ext[] = {0x00, 0x00};
+  ConnectWithBogusVersionList(ext, sizeof(ext));
+}
+
+TEST_P(TlsExtensionTest13, OddVersionList) {
+  static const uint8_t ext[] = {0x00, 0x01, 0x00};
+  ConnectWithBogusVersionList(ext, sizeof(ext));
+}
+
 INSTANTIATE_TEST_CASE_P(ExtensionStream, TlsExtensionTestGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(ExtensionDatagram, TlsExtensionTestGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV11Plus));
 INSTANTIATE_TEST_CASE_P(ExtensionDatagramOnly, TlsExtensionTestDtls,
                         TlsConnectTestBase::kTlsV11Plus);
--- a/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_hrr_unittest.cc
@@ -178,65 +178,103 @@ TEST_F(TlsConnectTest, Select12AfterHell
   server_->StartConnect();
   Handshake();
   EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, server_->error_code());
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
 }
 
 class HelloRetryRequestAgentTest : public TlsAgentTestClient {
  protected:
-  void MakeHelloRetryRequestRecord(SSLNamedGroup group, DataBuffer* hrr_record,
-                                   uint32_t seq_num = 0) const {
-    const uint8_t canned_hrr[] = {
-        SSL_LIBRARY_VERSION_TLS_1_3 >> 8,
-        SSL_LIBRARY_VERSION_TLS_1_3 & 0xff,
-        0,
-        0,  // The cipher suite is ignored.
-        static_cast<uint8_t>(group >> 8),
-        static_cast<uint8_t>(group),
-        0,
-        0  // no extensions
-    };
+  void SetUp() override {
+    TlsAgentTestClient::SetUp();
+    EnsureInit();
+    agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
+                            SSL_LIBRARY_VERSION_TLS_1_3);
+    agent_->StartConnect();
+  }
+
+  void MakeCannedHrr(const uint8_t* body, size_t len, DataBuffer* hrr_record,
+                     uint32_t seq_num = 0) const {
+    DataBuffer hrr_data;
+    hrr_data.Allocate(len + 4);
+    size_t i = 0;
+    i = hrr_data.Write(i, static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_3),
+                       2);
+    i = hrr_data.Write(i, static_cast<uint32_t>(len), 2);
+    if (len) {
+      hrr_data.Write(i, body, len);
+    }
     DataBuffer hrr;
-    MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, canned_hrr,
-                         sizeof(canned_hrr), &hrr, seq_num);
+    MakeHandshakeMessage(kTlsHandshakeHelloRetryRequest, hrr_data.data(),
+                         hrr_data.len(), &hrr, seq_num);
     MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3, hrr.data(),
                hrr.len(), hrr_record, seq_num);
   }
+
+  void MakeGroupHrr(SSLNamedGroup group, DataBuffer* hrr_record,
+                    uint32_t seq_num = 0) const {
+    const uint8_t group_hrr[] = {
+        static_cast<uint8_t>(ssl_tls13_key_share_xtn >> 8),
+        static_cast<uint8_t>(ssl_tls13_key_share_xtn),
+        0,
+        2,  // length of key share extension
+        static_cast<uint8_t>(group >> 8),
+        static_cast<uint8_t>(group)};
+    MakeCannedHrr(group_hrr, sizeof(group_hrr), hrr_record, seq_num);
+  }
 };
 
 // Send two HelloRetryRequest messages in response to the ClientHello.  The are
 // constructed to appear legitimate by asking for a new share in each, so that
 // the client has to count to work out that the server is being unreasonable.
 TEST_P(HelloRetryRequestAgentTest, SendSecondHelloRetryRequest) {
-  EnsureInit();
-  agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
-                          SSL_LIBRARY_VERSION_TLS_1_3);
-  agent_->StartConnect();
-
-  DataBuffer hrr_record;
-  MakeHelloRetryRequestRecord(ssl_grp_ec_secp384r1, &hrr_record, 0);
-  ProcessMessage(hrr_record, TlsAgent::STATE_CONNECTING);
-  MakeHelloRetryRequestRecord(ssl_grp_ec_secp521r1, &hrr_record, 1);
-  ProcessMessage(hrr_record, TlsAgent::STATE_ERROR,
+  DataBuffer hrr;
+  MakeGroupHrr(ssl_grp_ec_secp384r1, &hrr, 0);
+  ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
+  MakeGroupHrr(ssl_grp_ec_secp521r1, &hrr, 1);
+  ProcessMessage(hrr, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST);
 }
 
 // Here the client receives a HelloRetryRequest with a group that they already
 // provided a share for.
 TEST_P(HelloRetryRequestAgentTest, HandleBogusHelloRetryRequest) {
-  EnsureInit();
-  agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
-                          SSL_LIBRARY_VERSION_TLS_1_3);
-  agent_->StartConnect();
+  DataBuffer hrr;
+  MakeGroupHrr(ssl_grp_ec_secp256r1, &hrr);
+  ProcessMessage(hrr, TlsAgent::STATE_ERROR,
+                 SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+}
+
+TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) {
+  DataBuffer hrr;
+  MakeCannedHrr(nullptr, 0U, &hrr);
+  ProcessMessage(hrr, TlsAgent::STATE_ERROR,
+                 SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+}
 
-  DataBuffer hrr_record;
-  MakeHelloRetryRequestRecord(ssl_grp_ec_secp256r1, &hrr_record);
-  ProcessMessage(hrr_record, TlsAgent::STATE_ERROR,
-                 SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) {
+  const uint8_t canned_cookie_hrr[] = {
+      static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8),
+      static_cast<uint8_t>(ssl_tls13_cookie_xtn),
+      0,
+      5,  // length of cookie extension
+      0,
+      3,  // cookie value length
+      0xc0,
+      0x0c,
+      0x13};
+  DataBuffer hrr;
+  MakeCannedHrr(canned_cookie_hrr, sizeof(canned_cookie_hrr), &hrr);
+  TlsExtensionCapture* capture = new TlsExtensionCapture(ssl_tls13_cookie_xtn);
+  agent_->SetPacketFilter(capture);
+  ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
+  const size_t cookie_pos = 2 + 2;  // cookie_xtn, extension len
+  DataBuffer cookie(canned_cookie_hrr + cookie_pos,
+                    sizeof(canned_cookie_hrr) - cookie_pos);
+  EXPECT_EQ(cookie, capture->extension());
 }
 
 INSTANTIATE_TEST_CASE_P(HelloRetryRequestAgentTests, HelloRetryRequestAgentTest,
                         TlsConnectTestBase::kTlsModesAll);
 #ifndef NSS_DISABLE_TLS_1_3
 INSTANTIATE_TEST_CASE_P(HelloRetryRequestKeyExchangeTests, TlsKeyExchange13,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV13));
--- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc
@@ -34,16 +34,30 @@ TEST_P(TlsConnectGeneric, Connect) {
 
 TEST_P(TlsConnectGeneric, ConnectEcdsa) {
   SetExpectedVersion(std::get<1>(GetParam()));
   Reset(TlsAgent::kServerEcdsa256);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa);
 }
 
+TEST_P(TlsConnectGenericPre13, CipherSuiteMismatch) {
+  EnsureTlsSetup();
+  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
+    server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384);
+  } else {
+    client_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
+    server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
+  }
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+  server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
 TEST_P(TlsConnectGenericPre13, ConnectFalseStart) {
   client_->EnableFalseStart();
   Connect();
   SendReceive();
 }
 
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
--- a/security/nss/external_tests/ssl_gtest/tls_connect.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.cc
@@ -481,27 +481,32 @@ void TlsConnectTestBase::SetupForResume(
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
   Reset();
 }
 
 void TlsConnectTestBase::ZeroRttSendReceive(
-    bool expect_readable, std::function<bool()> post_clienthello_check) {
+    bool expect_writable, bool expect_readable,
+    std::function<bool()> post_clienthello_check) {
   const char* k0RttData = "ABCDEF";
   const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
 
   client_->Handshake();  // Send ClientHello.
   if (post_clienthello_check) {
     if (!post_clienthello_check()) return;
   }
   PRInt32 rv =
       PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);  // 0-RTT write.
-  EXPECT_EQ(k0RttDataLen, rv);
+  if (expect_writable) {
+    EXPECT_EQ(k0RttDataLen, rv);
+  } else {
+    EXPECT_EQ(SECFailure, rv);
+  }
   server_->Handshake();  // Consume ClientHello, EE, Finished.
 
   std::vector<uint8_t> buf(k0RttDataLen);
   rv = PR_Read(server_->ssl_fd(), buf.data(), k0RttDataLen);  // 0-RTT read
   if (expect_readable) {
     std::cerr << "0-RTT read " << rv << " bytes\n";
     EXPECT_EQ(k0RttDataLen, rv);
   } else {
--- a/security/nss/external_tests/ssl_gtest/tls_connect.h
+++ b/security/nss/external_tests/ssl_gtest/tls_connect.h
@@ -94,17 +94,17 @@ class TlsConnectTestBase : public ::test
   void EnsureModelSockets();
   void CheckAlpn(const std::string& val);
   void EnableSrtp();
   void CheckSrtp() const;
   void SendReceive();
   void SetupForZeroRtt();
   void SetupForResume();
   void ZeroRttSendReceive(
-      bool expect_readable,
+      bool expect_writable, bool expect_readable,
       std::function<bool()> post_clienthello_check = nullptr);
   void Receive(size_t amount);
   void ExpectExtendedMasterSecret(bool expected);
   void ExpectEarlyDataAccepted(bool expected);
 
  protected:
   Mode mode_;
   TlsAgent* client_;
--- a/security/nss/external_tests/ssl_gtest/tls_filter.cc
+++ b/security/nss/external_tests/ssl_gtest/tls_filter.cc
@@ -400,17 +400,19 @@ PacketFilter::Action TlsExtensionFilter:
       std::cerr << "extension old: " << extension << std::endl;
       std::cerr << "extension new: " << filtered << std::endl;
       source = &filtered;
     }
 
     // Write out extension.
     offset = output->Write(offset, extension_type, 2);
     offset = output->Write(offset, source->len(), 2);
-    offset = output->Write(offset, *source);
+    if (source->len() > 0) {
+      offset = output->Write(offset, *source);
+    }
   }
   output->Truncate(offset);
 
   if (changed) {
     size_t newlen = output->len() - length_offset - 2;
     EXPECT_GT(0x10000U, newlen);
     if (newlen >= 0x10000) {
       return KEEP;  // bad: size increased too much
--- a/security/nss/external_tests/util_gtest/util_utf8_unittest.cc
+++ b/security/nss/external_tests/util_gtest/util_utf8_unittest.cc
@@ -942,19 +942,26 @@ const char *const kUtf8BadCases[] = {
     "\xED\xBF\xBF",
     "\xED\xA0\x80\xE0\xBF\xBF",
 };
 
 // Invalid UTF-16 sequences (0-terminated)
 
 const Utf16BadCase kUtf16BadCases[] = {
     // Leading surrogate not followed by trailing surrogate:
-    {{0xD800, 0, 0}},      {{0xD800, 0x41, 0}},   {{0xD800, 0xfe, 0}},
-    {{0xD800, 0x3bb, 0}},  {{0xD800, 0xD800, 0}}, {{0xD800, 0xFEFF, 0}},
+    {{0xD800, 0, 0}},
+    {{0xD800, 0x41, 0}},
+    {{0xD800, 0xfe, 0}},
+    {{0xD800, 0x3bb, 0}},
+    {{0xD800, 0xD800, 0}},
+    {{0xD800, 0xFEFF, 0}},
     {{0xD800, 0xFFFD, 0}},
+    // Trailing surrogate, not preceded by a leading one.
+    {{0xDC00, 0, 0}},
+    {{0xDE6D, 0xD834, 0}},
 };
 
 // Parameterized test instantiations:
 
 INSTANTIATE_TEST_CASE_P(Ucs4TestCases, Ucs4Test,
                         ::testing::ValuesIn(kUcs4Cases));
 
 INSTANTIATE_TEST_CASE_P(Iso88591TestCases, Ucs2Test,
--- a/security/nss/lib/softoken/legacydb/keydb.c
+++ b/security/nss/lib/softoken/legacydb/keydb.c
@@ -125,17 +125,19 @@ encode_dbkey(NSSLOWKEYDBKey *dbkey, unsi
     PORT_Assert(dbkey->salt.len < 256);
     buf[1] = dbkey->salt.len;
 
     /* set length of nickname */
     PORT_Assert(nnlen < 256);
     buf[2] = nnlen;
 
     /* copy salt */
-    PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
+    if (dbkey->salt.len > 0) {
+        PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
+    }
 
     /* copy nickname */
     PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
 
     /* copy encrypted key */
     PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
                 dbkey->derPK.len);
 
--- a/security/nss/lib/softoken/legacydb/pcertdb.c
+++ b/security/nss/lib/softoken/legacydb/pcertdb.c
@@ -2185,18 +2185,20 @@ EncodeDBSubjectEntry(certDBEntrySubject 
         tmpbuf += 2;
     }
 
     for (i = 0; i < ncerts; i++) {
         PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
         tmpbuf += certKeys[i].len;
     }
     for (i = 0; i < ncerts; i++) {
-        PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
-        tmpbuf += keyIDs[i].len;
+        if (keyIDs[i].len) {
+            PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
+            tmpbuf += keyIDs[i].len;
+        }
     }
 
     if (entry->emailAddrs) {
         tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8);
         tmpbuf[1] = (PRUint8)(entry->nemailAddrs);
         tmpbuf += 2;
         for (i = 0; i < entry->nemailAddrs; i++) {
             int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
--- a/security/nss/lib/softoken/pkcs11c.c
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -687,27 +687,32 @@ sftk_ChaCha20Poly1305_CreateContext(cons
     if (ChaCha20Poly1305_InitContext(&ctx->freeblCtx, key, keyLen,
                                      params->ulTagLen) != SECSuccess) {
         PORT_Free(ctx);
         return NULL;
     }
 
     PORT_Memcpy(ctx->nonce, params->pNonce, sizeof(ctx->nonce));
 
+    /* AAD data and length must both be null, or both non-null. */
+    PORT_Assert((params->pAAD == NULL) == (params->ulAADLen == 0));
+
     if (params->ulAADLen > sizeof(ctx->ad)) {
         /* Need to allocate an overflow buffer for the additional data. */
         ctx->adOverflow = (unsigned char *)PORT_Alloc(params->ulAADLen);
         if (!ctx->adOverflow) {
             PORT_Free(ctx);
             return NULL;
         }
         PORT_Memcpy(ctx->adOverflow, params->pAAD, params->ulAADLen);
     } else {
         ctx->adOverflow = NULL;
-        PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen);
+        if (params->pAAD) {
+            PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen);
+        }
     }
     ctx->adLen = params->ulAADLen;
 
     return ctx;
 }
 
 static void
 sftk_ChaCha20Poly1305_DestroyContext(SFTKChaCha20Poly1305Info *ctx,
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -995,17 +995,16 @@ dtls_SetMTU(sslSocket *ss, PRUint16 adve
  * Caller must hold Handshake and RecvBuf locks.
  */
 SECStatus
 dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST;
     SECStatus rv;
     PRInt32 temp;
-    SECItem cookie = { siBuffer, NULL, 0 };
     SSL3AlertDescription desc = illegal_parameter;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake",
                 SSL_GETPID(), ss->fd));
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ss->ssl3.hs.ws != wait_server_hello) {
@@ -1020,29 +1019,28 @@ dtls_HandleHelloVerifyRequest(sslSocket 
         goto loser; /* alert has been sent */
     }
 
     if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE &&
         temp != SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) {
         goto alert_loser;
     }
 
-    /* The cookie */
-    rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length);
+    /* Read the cookie.
+     * IMPORTANT: The value of ss->ssl3.hs.cookie is only valid while the
+     * HelloVerifyRequest message remains valid. */
+    rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 1, &b, &length);
     if (rv != SECSuccess) {
         goto loser; /* alert has been sent */
     }
-    if (cookie.len > DTLS_COOKIE_BYTES) {
+    if (ss->ssl3.hs.cookie.len > DTLS_COOKIE_BYTES) {
         desc = decode_error;
         goto alert_loser; /* malformed. */
     }
 
-    PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len);
-    ss->ssl3.hs.cookieLen = cookie.len;
-
     ssl_GetXmitBufLock(ss); /*******************************/
 
     /* Now re-send the client hello */
     rv = ssl3_SendClientHello(ss, client_hello_retransmit);
 
     ssl_ReleaseXmitBufLock(ss); /*******************************/
 
     if (rv == SECSuccess)
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -5152,17 +5152,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
 
     length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
              1 + (sid->version >= SSL_LIBRARY_VERSION_TLS_1_3
                       ? 0
                       : sid->u.ssl3.sessionIDLength) +
              2 + num_suites * sizeof(ssl3CipherSuite) +
              1 + numCompressionMethods + total_exten_len;
     if (IS_DTLS(ss)) {
-        length += 1 + ss->ssl3.hs.cookieLen;
+        length += 1 + ss->ssl3.hs.cookie.len;
     }
 
     /* A padding extension may be included to ensure that the record containing
      * the ClientHello doesn't have a length between 256 and 511 bytes
      * (inclusive). Initial, ClientHello records with such lengths trigger bugs
      * in F5 devices.
      *
      * This is not done for DTLS, for renegotiation, or when there are no
@@ -5232,17 +5232,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
         if (sid->u.ssl3.lock) {
             PR_RWLock_Unlock(sid->u.ssl3.lock);
         }
         return rv; /* err set by ssl3_AppendHandshake* */
     }
 
     if (IS_DTLS(ss)) {
         rv = ssl3_AppendHandshakeVariable(
-            ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
+            ss, ss->ssl3.hs.cookie.data, ss->ssl3.hs.cookie.len, 1);
         if (rv != SECSuccess) {
             if (sid->u.ssl3.lock) {
                 PR_RWLock_Unlock(sid->u.ssl3.lock);
             }
             return rv; /* err set by ssl3_AppendHandshake* */
         }
     }
 
@@ -6891,17 +6891,19 @@ ssl3_HandleServerHelloPart2(sslSocket *s
     /* get a new sid */
     ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
     if (sid == NULL) {
         goto alert_loser; /* memory error is set. */
     }
 
     sid->version = ss->version;
     sid->u.ssl3.sessionIDLength = sidBytes->len;
-    PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len);
+    if (sidBytes->len > 0) {
+        PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len);
+    }
 
     sid->u.ssl3.keys.extendedMasterSecretUsed =
         ssl3_ExtensionNegotiated(ss, ssl_extended_master_secret_xtn);
 
     /* Copy Signed Certificate Timestamps, if any. */
     if (ss->xtnData.signedCertTimestamps.data) {
         rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps,
                               &ss->xtnData.signedCertTimestamps);
@@ -8248,18 +8250,18 @@ ssl3_HandleClientHello(sslSocket *ss, SS
             goto loser; /* malformed */
         }
     }
 
     versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn);
     if (versionExtension) {
         rv = tls13_NegotiateVersion(ss, versionExtension);
         if (rv != SECSuccess) {
-            desc = protocol_version;
-            errCode = SSL_ERROR_UNSUPPORTED_VERSION;
+            errCode = PORT_GetError();
+            desc = (errCode == SSL_ERROR_UNSUPPORTED_VERSION) ? protocol_version : illegal_parameter;
             goto alert_loser;
         }
     } else {
         /* The PR_MIN here ensures that we never negotiate 1.3 if the
          * peer didn't offer "supported_versions". */
         rv = ssl3_NegotiateVersion(ss,
                                    PR_MIN(version,
                                           SSL_LIBRARY_VERSION_TLS_1_2),
@@ -12719,18 +12721,21 @@ ssl3_InitState(sslSocket *ss)
     }
 
     ss->ssl3.hs.clientHelloHash = NULL;
     PR_INIT_CLIST(&ss->ssl3.hs.remoteKeyShares);
     ss->ssl3.hs.currentSecret = NULL;
     ss->ssl3.hs.resumptionPsk = NULL;
     ss->ssl3.hs.resumptionContext = nullItem;
     ss->ssl3.hs.dheSecret = NULL;
-    ss->ssl3.hs.trafficSecret = NULL;
-    ss->ssl3.hs.hsTrafficSecret = NULL;
+    ss->ssl3.hs.clientEarlyTrafficSecret = NULL;
+    ss->ssl3.hs.clientHsTrafficSecret = NULL;
+    ss->ssl3.hs.serverHsTrafficSecret = NULL;
+    ss->ssl3.hs.clientTrafficSecret = NULL;
+    ss->ssl3.hs.serverTrafficSecret = NULL;
     ss->ssl3.hs.certificateRequest = NULL;
     PR_INIT_CLIST(&ss->ssl3.hs.cipherSpecs);
 
     PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space);
     ss->ssl3.hs.messages.buf = NULL;
     ss->ssl3.hs.messages.space = 0;
 
     ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
@@ -13055,22 +13060,26 @@ ssl3_DestroySSL3Info(sslSocket *ss)
     if (ss->ssl3.hs.currentSecret)
         PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
     if (ss->ssl3.hs.resumptionPsk)
         PK11_FreeSymKey(ss->ssl3.hs.resumptionPsk);
     if (ss->ssl3.hs.dheSecret)
         PK11_FreeSymKey(ss->ssl3.hs.dheSecret);
     if (ss->ssl3.hs.resumptionContext.data)
         SECITEM_FreeItem(&ss->ssl3.hs.resumptionContext, PR_FALSE);
-    if (ss->ssl3.hs.earlyTrafficSecret)
-        PK11_FreeSymKey(ss->ssl3.hs.earlyTrafficSecret);
-    if (ss->ssl3.hs.hsTrafficSecret)
-        PK11_FreeSymKey(ss->ssl3.hs.hsTrafficSecret);
-    if (ss->ssl3.hs.trafficSecret)
-        PK11_FreeSymKey(ss->ssl3.hs.trafficSecret);
+    if (ss->ssl3.hs.clientEarlyTrafficSecret)
+        PK11_FreeSymKey(ss->ssl3.hs.clientEarlyTrafficSecret);
+    if (ss->ssl3.hs.clientHsTrafficSecret)
+        PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret);
+    if (ss->ssl3.hs.serverHsTrafficSecret)
+        PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret);
+    if (ss->ssl3.hs.clientTrafficSecret)
+        PK11_FreeSymKey(ss->ssl3.hs.clientTrafficSecret);
+    if (ss->ssl3.hs.serverTrafficSecret)
+        PK11_FreeSymKey(ss->ssl3.hs.serverTrafficSecret);
 
     ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
     /* Destroy TLS 1.3 buffered early data. */
     tls13_DestroyEarlyData(&ss->ssl3.hs.bufferedEarlyData);
 
     ss->ssl3.initialized = PR_FALSE;
 
     SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -91,16 +91,19 @@ static PRInt32 ssl3_SendExtendedMasterSe
 static SECStatus ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss,
                                                     PRUint16 ex_type,
                                                     SECItem *data);
 static PRInt32 tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append,
                                            PRUint32 maxBytes);
 static SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss,
                                                PRUint16 ex_type,
                                                SECItem *data);
+static SECStatus tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss,
+                                                  PRUint16 ex_type,
+                                                  SECItem *data);
 static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss,
                                                PRUint16 ex_type,
                                                SECItem *data);
 static PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append,
                                                PRUint32 maxBytes);
 static SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss,
                                                    PRUint16 ex_type,
                                                    SECItem *data);
@@ -117,16 +120,21 @@ static SECStatus tls13_ClientHandleEarly
 static SECStatus tls13_ClientHandleTicketEarlyDataInfoXtn(
     sslSocket *ss, PRUint16 ex_type,
     SECItem *data);
 static SECStatus tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type,
                                               SECItem *data);
 static PRInt32 tls13_ClientSendSupportedVersionsXtn(sslSocket *ss,
                                                     PRBool append,
                                                     PRUint32 maxBytes);
+static SECStatus tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type,
+                                             SECItem *data);
+static PRInt32 tls13_ClientSendHrrCookieXtn(sslSocket *ss,
+                                            PRBool append,
+                                            PRUint32 maxBytes);
 
 /*
  * Write bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
@@ -282,16 +290,22 @@ static const ssl3ExtensionHandler server
     { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn },
     { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn },
     { ssl_signature_algorithms_xtn, &tls13_ClientHandleSigAlgsXtn },
     { -1, NULL }
 };
 
+static const ssl3ExtensionHandler helloRetryRequestHandlers[] = {
+    { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr },
+    { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie },
+    { -1, NULL }
+};
+
 static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { -1, NULL }
 };
 
 static const ssl3ExtensionHandler newSessionTicketHandlers[] = {
     { ssl_tls13_ticket_early_data_info_xtn,
       &tls13_ClientHandleTicketEarlyDataInfoXtn },
@@ -324,17 +338,18 @@ static const ssl3HelloExtensionSender cl
       { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn },
       { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
       { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn },
       /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will
        * time out or terminate the connection if the last extension in the
        * client hello is empty. They are not intolerant of TLS 1.2, so list
        * signature_algorithms at the end. See bug 1243641. */
       { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn },
-      { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
+      { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+      { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn }
       /* any extra entries will appear as { 0, NULL }    */
     };
 
 static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
     /* any extra entries will appear as { 0, NULL }    */
 };
 
@@ -2160,16 +2175,20 @@ ssl3_HandleParsedExtensions(sslSocket *s
     switch (handshakeMessage) {
         case client_hello:
             handlers = clientHelloHandlers;
             break;
         case new_session_ticket:
             PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
             handlers = newSessionTicketHandlers;
             break;
+        case hello_retry_request:
+            PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+            handlers = helloRetryRequestHandlers;
+            break;
         case encrypted_extensions:
             PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
         /* fall through */
         case server_hello:
             if (ss->version > SSL_LIBRARY_VERSION_3_0) {
                 handlers = serverHelloHandlersTLS;
             } else {
                 handlers = serverHelloHandlersSSL3;
@@ -2186,17 +2205,18 @@ ssl3_HandleParsedExtensions(sslSocket *s
          cursor = PR_NEXT_LINK(cursor)) {
         TLSExtension *extension = (TLSExtension *)cursor;
         const ssl3ExtensionHandler *handler;
 
         /* Check whether the server sent an extension which was not advertised
          * in the ClientHello */
         if (!ss->sec.isServer &&
             !ssl3_ClientExtensionAdvertised(ss, extension->type) &&
-            (handshakeMessage != new_session_ticket)) {
+            (handshakeMessage != new_session_ticket) &&
+            (extension->type != ssl_tls13_cookie_xtn)) {
             (void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension);
             PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
             return SECFailure;
         }
 
         /* Check that this is a legal extension in TLS 1.3 */
         if (isTLS13 && !tls13_ExtensionAllowed(extension->type, handshakeMessage)) {
             if (handshakeMessage == client_hello) {
@@ -3165,16 +3185,59 @@ tls13_ClientHandleKeyShareXtn(sslSocket 
     if (data->len) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         return SECFailure;
     }
 
     return SECSuccess;
 }
 
+static SECStatus
+tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+{
+    SECStatus rv;
+    PRInt32 tmp;
+    const sslNamedGroupDef *group;
+
+    PORT_Assert(!ss->sec.isServer);
+    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+    SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR",
+                SSL_GETPID(), ss->fd));
+
+    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    if (tmp < 0) {
+        return SECFailure; /* error code already set */
+    }
+    if (data->len) {
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+        return SECFailure;
+    }
+
+    group = ssl_LookupNamedGroup((SSLNamedGroup)tmp);
+    /* If the group is not enabled, or we already have a share for the
+     * requested group, abort. */
+    if (!ssl_NamedGroupEnabled(ss, group) ||
+        ssl_LookupEphemeralKeyPair(ss, group)) {
+        (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+        return SECFailure;
+    }
+
+    rv = tls13_CreateKeyShare(ss, group);
+    if (rv != SECSuccess) {
+        (void)SSL3_SendAlert(ss, alert_fatal, internal_error);
+        PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
 /* Handle an incoming KeyShare extension at the server and copy to
  * |ss->ssl3.hs.remoteKeyShares| for future use. The key
  * share is processed in tls13_HandleClientKeyShare(). */
 static SECStatus
 tls13_ServerHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
     PRInt32 length;
@@ -3755,17 +3818,17 @@ tls13_ClientSendSupportedVersionsXtn(ssl
     SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension",
                 SSL_GETPID(), ss->fd));
 
     /* Extension type, extension len fiels, vector len field,
      * length of the values. */
     extensions_len = 2 + 2 + 1 +
                      2 * (ss->vrange.max - ss->vrange.min + 1);
 
-    if (maxBytes < extensions_len) {
+    if (maxBytes < (PRUint32)extensions_len) {
         PORT_Assert(0);
         return 0;
     }
 
     if (append) {
         rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2);
         if (rv != SECSuccess)
             return -1;
@@ -3784,16 +3847,84 @@ tls13_ClientSendSupportedVersionsXtn(ssl
             if (rv != SECSuccess)
                 return -1;
         }
     }
 
     return extensions_len;
 }
 
+/*
+ *    struct {
+ *        opaque cookie<1..2^16-1>;
+ *    } Cookie;
+ */
+SECStatus
+tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+{
+    SECStatus rv;
+
+    SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
+                SSL_GETPID(), ss->fd));
+
+    PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+    /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */
+    rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 2,
+                                       &data->data, &data->len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+        return SECFailure;
+    }
+    if (!ss->ssl3.hs.cookie.len || data->len) {
+        (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+PRInt32
+tls13_ClientSendHrrCookieXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes)
+{
+    PRInt32 extension_len;
+
+    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+        !ss->ssl3.hs.cookie.len) {
+        return 0;
+    }
+
+    SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd));
+
+    /* Extension type, length, cookie length, cookie value. */
+    extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len;
+
+    if (maxBytes < (PRUint32)extension_len) {
+        PORT_Assert(0);
+        return 0;
+    }
+
+    if (append) {
+        SECStatus rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        rv = ssl3_AppendHandshakeNumber(ss, extension_len - 4, 2);
+        if (rv != SECSuccess)
+            return -1;
+
+        rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data,
+                                          ss->ssl3.hs.cookie.len, 2);
+        if (rv != SECSuccess)
+            return -1;
+    }
+    return extension_len;
+}
+
 void
 ssl3_DestroyRemoteExtensions(PRCList *list)
 {
     PRCList *cur_p;
 
     while (!PR_CLIST_IS_EMPTY(list)) {
         cur_p = PR_LIST_TAIL(list);
         PR_REMOVE_LINK(cur_p);
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -932,40 +932,40 @@ typedef struct SSL3HandshakeStateStr {
     PRUint16 maxMessageSent;       /* The largest message we sent */
     PRUint16 recvMessageSeq;       /* The receiving message sequence
                                     * number */
     sslBuffer recvdFragments;      /* The fragments we have received in
                                     * a bitmask */
     PRInt32 recvdHighWater;        /* The high water mark for fragments
                                     * received. -1 means no reassembly
                                     * in progress. */
-    unsigned char cookie[32];      /* The cookie */
-    unsigned char cookieLen;       /* The length of the cookie */
+    SECItem cookie;                /* The Hello(Retry|Verify)Request cookie. */
     PRIntervalTime rtTimerStarted; /* When the timer was started */
     DTLSTimerCb rtTimerCb;         /* The function to call on expiry */
     PRUint32 rtTimeoutMs;          /* The length of the current timeout
                                     * used for backoff (in ms) */
     PRUint32 rtRetries;            /* The retry counter */
     SECItem srvVirtName;           /* for server: name that was negotiated
                           * with a client. For client - is
                           * always set to NULL.*/
 
     /* This group of values is used for TLS 1.3 and above */
-    PK11Context *clientHelloHash;   /* The client hello hash state, used
+    PK11Context *clientHelloHash;         /* The client hello hash state, used
                                         * by the server for 0-RTT. */
-    PRCList remoteKeyShares;        /* The other side's public keys */
-    PK11SymKey *currentSecret;      /* The secret down the "left hand side"
+    PRCList remoteKeyShares;              /* The other side's public keys */
+    PK11SymKey *currentSecret;            /* The secret down the "left hand side"
                                         * of the TLS 1.3 key schedule. */
-    PK11SymKey *resumptionPsk;      /* The resumption PSK. */
-    SECItem resumptionContext;      /* The resumption context. */
-    PK11SymKey *dheSecret;          /* The (EC)DHE shared secret. */
-    PK11SymKey *earlyTrafficSecret; /* The secret we use for 0-RTT. */
-    PK11SymKey *hsTrafficSecret;    /* The handshake traffic secret. */
-    PK11SymKey *trafficSecret;      /* The source key to use to generate
-                                        * traffic keys */
+    PK11SymKey *resumptionPsk;            /* The resumption PSK. */
+    SECItem resumptionContext;            /* The resumption context. */
+    PK11SymKey *dheSecret;                /* The (EC)DHE shared secret. */
+    PK11SymKey *clientEarlyTrafficSecret; /* The secret we use for 0-RTT. */
+    PK11SymKey *clientHsTrafficSecret;    /* The source keys for handshake */
+    PK11SymKey *serverHsTrafficSecret;    /* traffic keys. */
+    PK11SymKey *clientTrafficSecret;      /* The source keys for application */
+    PK11SymKey *serverTrafficSecret;      /* traffic keys */
     /* The certificate request from the server. */
     TLS13CertificateRequest *certificateRequest;
     PRCList cipherSpecs;            /* The cipher specs in the sequence they
                                         * will be applied. */
     ssl3CipherSpec *nullSpec;       /* In case 0-RTT is rejected. */
     sslZeroRttState zeroRttState;   /* Are we doing a 0-RTT handshake? */
     sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */
     PRCList bufferedEarlyData;      /* Buffered TLS 1.3 early data
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -309,29 +309,33 @@ typedef enum {
     ssl_signed_cert_timestamp_xtn = 18,
     ssl_padding_xtn = 21,
     ssl_extended_master_secret_xtn = 23,
     ssl_session_ticket_xtn = 35,
     ssl_tls13_key_share_xtn = 40,
     ssl_tls13_pre_shared_key_xtn = 41,
     ssl_tls13_early_data_xtn = 42,
     ssl_tls13_supported_versions_xtn = 43,
+    ssl_tls13_cookie_xtn = 44,
     ssl_next_proto_nego_xtn = 13172,
     ssl_renegotiation_info_xtn = 0xff01
 } SSLExtensionType;
 
 typedef enum {
     ssl_tls13_ticket_early_data_info_xtn = 1
 } TLS13TicketExtensionType;
 
 /* This is the old name for the supported_groups extensions. */
 #define ssl_elliptic_curves_xtn ssl_supported_groups_xtn
 
-#define SSL_MAX_EXTENSIONS 16 /* doesn't include ssl_padding_xtn or \
-                               * TLS 1.3 NewSessionTicket extensions. */
+/* SSL_MAX_EXTENSIONS doesn't include ssl_padding_xtn.  It includes the maximum
+ * number of extensions that are supported for any single message type.  That
+ * is, a ClientHello; ServerHello and TLS 1.3 NewSessionTicket and
+ * HelloRetryRequest extensions are smaller. */
+#define SSL_MAX_EXTENSIONS 17
 
 /* Deprecated */
 typedef enum {
     ssl_dhe_group_none = 0,
     ssl_ff_dhe_2048_group = 1,
     ssl_ff_dhe_3072_group = 2,
     ssl_ff_dhe_4096_group = 3,
     ssl_ff_dhe_6144_group = 4,
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -64,17 +64,19 @@ static SECStatus tls13_HandleCertificate
 static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b,
                                                 PRUint32 length);
 static SECStatus
 tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey);
 static SECStatus tls13_HandleCertificateVerify(
     sslSocket *ss, SSL3Opaque *b, PRUint32 length,
     TLS13CombinedHash *hashes);
 static SECStatus
-tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, const char *label,
+tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
+                   const char *prefix,
+                   const char *suffix,
                    const TLS13CombinedHash *hashes,
                    PK11SymKey **dest);
 static void tls13_SetNullCipherSpec(sslSocket *ss, ssl3CipherSpec **specp);
 static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss);
 static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey);
 static SECStatus tls13_VerifyFinished(sslSocket *ss, PK11SymKey *secret,
                                       SSL3Opaque *b, PRUint32 length,
                                       const TLS13CombinedHash *hashes);
@@ -100,35 +102,37 @@ static SECStatus tls13_ComputeApplicatio
 static SECStatus tls13_ComputeFinalSecrets(sslSocket *ss);
 static SECStatus tls13_ComputeFinished(
     sslSocket *ss, PK11SymKey *baseKey, const TLS13CombinedHash *hashes,
     PRBool sending, PRUint8 *output, unsigned int *outputLen,
     unsigned int maxOutputLen);
 static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
 static SECStatus tls13_FinishHandshake(sslSocket *ss);
 
+const char kHkdfLabelClient[] = "client";
+const char kHkdfLabelServer[] = "server";
 const char kHkdfLabelEarlyTrafficSecret[] = "early traffic secret";
 const char kHkdfLabelHandshakeTrafficSecret[] = "handshake traffic secret";
 const char kHkdfLabelApplicationTrafficSecret[] = "application traffic secret";
-const char kHkdfLabelClientFinishedSecret[] = "client finished";
-const char kHkdfLabelServerFinishedSecret[] = "server finished";
+const char kHkdfLabelFinishedSecret[] = "finished";
 const char kHkdfLabelResumptionMasterSecret[] = "resumption master secret";
 const char kHkdfLabelResumptionPsk[] = "resumption psk";
 const char kHkdfLabelResumptionContext[] = "resumption context";
 const char kHkdfLabelExporterMasterSecret[] = "exporter master secret";
 const char kHkdfPhaseEarlyHandshakeDataKeys[] = "early handshake key expansion";
 const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data key expansion";
 const char kHkdfPhaseHandshakeKeys[] = "handshake key expansion";
 const char kHkdfPhaseApplicationDataKeys[] = "application data key expansion";
-const char kHkdfPurposeClientWriteKey[] = "client write key";
-const char kHkdfPurposeServerWriteKey[] = "server write key";
-const char kHkdfPurposeClientWriteIv[] = "client write iv";
-const char kHkdfPurposeServerWriteIv[] = "server write iv";
-const char kClientFinishedLabel[] = "client finished";
-const char kServerFinishedLabel[] = "server finished";
+const char kHkdfPurposeKey[] = "key";
+const char kHkdfPurposeIv[] = "iv";
+
+#define TRAFFIC_SECRET(ss, dir, name) ((ss->sec.isServer ^            \
+                                        (dir == CipherSpecWrite))     \
+                                           ? ss->ssl3.hs.client##name \
+                                           : ss->ssl3.hs.server##name)
 
 const SSL3ProtocolVersion kTlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_0;
 const SSL3ProtocolVersion kDtlsRecordVersion = SSL_LIBRARY_VERSION_TLS_1_1;
 
 /* Belt and suspenders in case we ever add a TLS 1.4. */
 PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
                  SSL_LIBRARY_VERSION_TLS_1_3);
 
@@ -688,55 +692,62 @@ loser:
     }
     return SECFailure;
 }
 
 /* Key Derivation Functions.
  *
  * Below is the key schedule from [draft-ietf-tls-tls13].
  *
- * The relevant functions from this file are indicated by tls13_Foo().
+ * * The relevant functions from this file are indicated by tls13_Foo().
+ *                 0
+ *                 |
+ *                 v
+ *   PSK ->  HKDF-Extract
+ *                 |
+ *                 v
+ *           Early Secret ---> Derive-Secret(., "client early traffic secret",
+ *                 |                         ClientHello)
+ *                 |                         = client_early_traffic_secret
+ *                 v
+ * (EC)DHE -> HKDF-Extract
+ *                 |
+ *                 v
+ *         Handshake Secret
+ *                 |
+ *                 +---------> Derive-Secret(., "client handshake traffic secret",
+ *                 |                         ClientHello...ServerHello)
+ *                 |                         = client_handshake_traffic_secret
+ *                 |
+ *                 +---------> Derive-Secret(., "server handshake traffic secret",
+ *                 |                         ClientHello...ServerHello)
+ *                 |                         = server_handshake_traffic_secret
+ *                 |
+ *                 v
+ *      0 -> HKDF-Extract
+ *                 |
+ *                 v
+ *            Master Secret
+ *                 |
+ *                 +---------> Derive-Secret(., "client application traffic secret",
+ *                 |                         ClientHello...Server Finished)
+ *                 |                         = client_traffic_secret_0
+ *                 |
+ *                 +---------> Derive-Secret(., "server application traffic secret",
+ *                 |                         ClientHello...Server Finished)
+ *                 |                         = server_traffic_secret_0
+ *                 |
+ *                 +---------> Derive-Secret(., "exporter master secret",
+ *                 |                         ClientHello...Client Finished)
+ *                 |                         = exporter_secret
+ *                 |
+ *                 +---------> Derive-Secret(., "resumption master secret",
+ *                                           ClientHello...Client Finished)
+ *                                           = resumption_secret
  *
- *                  0
- *                  |
- *                  v
- *    PSK ->  HKDF-Extract      tls13_ComputeEarlySecrets()
- *                  |
- *                  v
- *            Early Secret  --> Derive-Secret(., "early traffic secret",
- *                  |                         ClientHello)
- *                  |                         = early_traffic_secret
- *                  v
- * (EC)DHE -> HKDF-Extract      tls13_ComputeHandshakeSecrets()
- *                  |
- *                  v
- *               Handshake
- *                Secret -----> Derive-Secret(., "handshake traffic secret",
- *                  |                         ClientHello + ServerHello)
- *                  |                         = handshake_traffic_secret
- *                  v
- *       0 -> HKDF-Extract      tls13_ComputeApplicationSecret
- *                  |
- *                  v
- *             Master Secret
- *                  |
- *                  +---------> Derive-Secret(., "application traffic secret",
- *                  |                         ClientHello...Server Finished)
- *                  |                         = traffic_secret_0
- *                  |
- *                  |
- *                  |           tls13_ComputeFinalSecrets()
- *                  |
- *                  +---------> Derive-Secret(., "exporter master secret",
- *                  |                         ClientHello...Client Finished)
- *                  |                         = exporter_secret
- *                  |
- *                  +---------> Derive-Secret(., "resumption master secret",
- *                                            ClientHello...Client Finished)
- *                                            = resumption_secret
  */
 
 static SECStatus
 tls13_ComputeEarlySecrets(sslSocket *ss, PRBool setup0Rtt)
 {
     SECStatus rv = SECSuccess;
     PK11Context *ctx;
     PRUint8 hash[HASH_LENGTH_MAX];
@@ -794,19 +805,20 @@ tls13_ComputeEarlySecrets(sslSocket *ss,
                           tls13_GetHashSize(ss)) == NULL) {
         return SECFailure;
     }
     PORT_Memcpy(ss->ssl3.hs.resumptionContext.data, hash, len);
 
     if (setup0Rtt) {
         /* Derive the early secret. */
         rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+                                kHkdfLabelClient,
                                 kHkdfLabelEarlyTrafficSecret,
                                 NULL,
-                                &ss->ssl3.hs.earlyTrafficSecret);
+                                &ss->ssl3.hs.clientEarlyTrafficSecret);
         if (rv != SECSuccess)
             return SECFailure;
     }
 
     return SECSuccess;
 }
 
 static SECStatus
@@ -824,20 +836,29 @@ tls13_ComputeHandshakeSecrets(sslSocket 
         LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
         return rv;
     }
     PK11_FreeSymKey(ss->ssl3.hs.dheSecret);
     ss->ssl3.hs.dheSecret = NULL;
     PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
     ss->ssl3.hs.currentSecret = newSecret;
 
-    /* Now compute |hsTrafficSecret| */
+    /* Now compute |*HsTrafficSecret| */
     rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+                            kHkdfLabelClient,
                             kHkdfLabelHandshakeTrafficSecret, NULL,
-                            &ss->ssl3.hs.hsTrafficSecret);
+                            &ss->ssl3.hs.clientHsTrafficSecret);
+    if (rv != SECSuccess) {
+        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+        return rv;
+    }
+    rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+                            kHkdfLabelServer,
+                            kHkdfLabelHandshakeTrafficSecret, NULL,
+                            &ss->ssl3.hs.serverHsTrafficSecret);
     if (rv != SECSuccess) {
         LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
         return rv;
     }
 
     /* Crank HKDF forward to make master secret, which we
      * stuff in current secret. */
     rv = tls13_HkdfExtract(ss->ssl3.hs.currentSecret,
@@ -856,19 +877,28 @@ tls13_ComputeHandshakeSecrets(sslSocket 
 }
 
 static SECStatus
 tls13_ComputeApplicationSecrets(sslSocket *ss)
 {
     SECStatus rv;
 
     rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+                            kHkdfLabelClient,
                             kHkdfLabelApplicationTrafficSecret,
                             NULL,
-                            &ss->ssl3.hs.trafficSecret);
+                            &ss->ssl3.hs.clientTrafficSecret);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
+                            kHkdfLabelServer,
+                            kHkdfLabelApplicationTrafficSecret,
+                            NULL,
+                            &ss->ssl3.hs.serverTrafficSecret);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     return SECSuccess;
 }
 
 static SECStatus
@@ -876,17 +906,17 @@ tls13_ComputeFinalSecrets(sslSocket *ss)
 {
     SECStatus rv;
     PK11SymKey *resumptionMasterSecret = NULL;
 
     PORT_Assert(!ss->ssl3.crSpec->master_secret);
     PORT_Assert(!ss->ssl3.cwSpec->master_secret);
 
     rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret,
-                            kHkdfLabelResumptionMasterSecret,
+                            NULL, kHkdfLabelResumptionMasterSecret,
                             NULL, &resumptionMasterSecret);
     PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
     ss->ssl3.hs.currentSecret = NULL;
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     /* This is pretty gross. TLS 1.3 uses a number of master secrets:
@@ -1370,45 +1400,52 @@ tls13_SendHelloRetryRequest(sslSocket *s
     if (ss->ssl3.hs.helloRetry) {
         FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter);
         return SECFailure;
     }
 
     ssl_GetXmitBufLock(ss);
     rv = ssl3_AppendHandshakeHeader(ss, hello_retry_request,
                                     2 +     /* version */
-                                        2 + /* cipher suite */
-                                        2 + /* group */
-                                        2 /* (empty) extensions */);
+                                        2 + /* extension length */
+                                        2 + /* group extension id */
+                                        2 + /* group extension length */
+                                        2 /* group */);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         goto loser;
     }
 
     rv = ssl3_AppendHandshakeNumber(
         ss, tls13_EncodeDraftVersion(ss->version), 2);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         goto loser;
     }
 
-    rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.cipher_suite, 2);
+    /* Length of extensions. */
+    rv = ssl3_AppendHandshakeNumber(ss, 2 + 2 + 2, 2);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         goto loser;
     }
 
-    rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2);
+    /* Key share extension - currently the only reason we send this. */
+    rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         goto loser;
     }
-
-    /* Send an empty extensions block. */
-    rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+    /* Key share extension length. */
+    rv = ssl3_AppendHandshakeNumber(ss, 2, 2);
+    if (rv != SECSuccess) {
+        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+        goto loser;
+    }
+    rv = ssl3_AppendHandshakeNumber(ss, selectedGroup->name, 2);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         goto loser;
     }
 
     rv = ssl3_FlushHandshake(ss, 0);
     if (rv != SECSuccess) {
         goto loser; /* error code set by ssl3_FlushHandshake */
@@ -1573,17 +1610,16 @@ tls13_SendCertificateRequest(sslSocket *
 }
 
 static SECStatus
 tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     PRInt32 tmp;
     PRUint32 version;
-    const sslNamedGroupDef *group;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     /* Client */
@@ -1614,55 +1650,34 @@ tls13_HandleHelloRetryRequest(sslSocket 
     }
     version = tls13_DecodeDraftVersion((PRUint16)tmp);
     if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
                     protocol_version);
         return SECFailure;
     }
 
-    /* Ignore the cipher suite, it's going to be removed soon anyway. */
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0) {
-        return SECFailure; /* error code already set */
-    }
-
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0) {
-        return SECFailure; /* error code already set */
-    }
-    group = ssl_LookupNamedGroup((SSLNamedGroup)tmp);
-    if (!ssl_NamedGroupEnabled(ss, group)) {
-        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
-                    illegal_parameter);
-        return SECFailure;
-    }
-
     tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
     if (tmp < 0) {
         return SECFailure; /* error code already set */
     }
-    /* Ignore the extensions. */
-    if (tmp != length) {
+    /* Extensions must be non-empty and use the remainder of the message.
+     * This means that a HelloRetryRequest cannot be a no-op: we must have an
+     * extension, it must be one that we understand and recognize as being valid
+     * for HelloRetryRequest, and all the extensions we permit cause us to
+     * modify our ClientHello in some way. */
+    if (!tmp || tmp != length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
                     decode_error);
         return SECFailure;
     }
 
-    /* If there is already a share for the requested group, abort. */
-    if (ssl_LookupEphemeralKeyPair(ss, group)) {
-        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
-                    illegal_parameter);
-        return SECFailure;
-    }
-
-    rv = tls13_CreateKeyShare(ss, group);
+    rv = ssl3_HandleExtensions(ss, &b, &length, hello_retry_request);
     if (rv != SECSuccess) {
-        FATAL_ERROR(ss, SEC_ERROR_KEYGEN_FAIL, internal_error);
-        return SECFailure;
+        return SECFailure; /* Error code set below */
     }
 
     ss->ssl3.hs.helloRetry = PR_TRUE;
 
     ssl_GetXmitBufLock(ss);
     rv = ssl3_SendClientHello(ss, client_hello_retry);
     ssl_ReleaseXmitBufLock(ss);
     if (rv != SECSuccess) {
@@ -1808,17 +1823,17 @@ tls13_SendEncryptedServerSequence(sslSoc
 
         svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey;
         rv = tls13_SendCertificateVerify(ss, svrPrivKey);
         if (rv != SECSuccess) {
             return SECFailure; /* err code is set. */
         }
     }
 
-    rv = tls13_SendFinished(ss, ss->ssl3.hs.hsTrafficSecret);
+    rv = tls13_SendFinished(ss, ss->ssl3.hs.serverHsTrafficSecret);
     if (rv != SECSuccess) {
         return SECFailure; /* error code is set. */
     }
 
     return SECSuccess;
 }
 
 /* Called from:  ssl3_HandleClientHello */
@@ -2243,22 +2258,39 @@ loser:
 }
 
 /*
  *    Derive-Secret(Secret, Label, Messages) =
  *       HKDF-Expand-Label(Secret, Label,
  *                         Hash(Messages) + Hash(resumption_context), L))
  */
 static SECStatus
-tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, const char *label,
+tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
+                   const char *prefix,
+                   const char *suffix,
                    const TLS13CombinedHash *hashes,
                    PK11SymKey **dest)
 {
     SECStatus rv;
     TLS13CombinedHash hashesTmp;
+    char buf[100];
+    const char *label;
+
+    if (prefix) {
+        if ((strlen(prefix) + strlen(suffix) + 2) > sizeof(buf)) {
+            PORT_Assert(0);
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+        }
+        (void)PR_snprintf(buf, sizeof(buf), "%s %s",
+                          prefix, suffix);
+        label = buf;
+    } else {
+        label = suffix;
+    }
 
     SSL_TRC(3, ("%d: TLS13[%d]: deriving secret '%s'",
                 SSL_GETPID(), ss->fd, label));
     if (!hashes) {
         rv = tls13_ComputeHandshakeHashes(ss, &hashesTmp);
         if (rv != SECSuccess) {
             PORT_Assert(0); /* Should never fail */
             ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE);
@@ -2289,96 +2321,96 @@ tls13_DeriveTrafficKeys(sslSocket *ss, s
     size_t keySize = spec->cipher_def->key_size;
     size_t ivSize = spec->cipher_def->iv_size +
                     spec->cipher_def->explicit_nonce_size; /* This isn't always going to
                                                               * work, but it does for
                                                               * AES-GCM */
     CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipher_def->calg);
     PK11SymKey **prkp = NULL;
     PK11SymKey *prk = NULL;
+    PRBool clientKey;
+    ssl3KeyMaterial *target;
     const char *phase;
     char label[256]; /* Arbitrary buffer large enough to hold the label */
     SECStatus rv;
 
+    if (ss->sec.isServer ^ (direction == CipherSpecWrite)) {
+        clientKey = PR_TRUE;
+        target = &spec->client;
+    } else {
+        clientKey = PR_FALSE;
+        target = &spec->server;
+    }
+
 #define FORMAT_LABEL(phase_, purpose_)                                              \
     do {                                                                            \
         PRUint32 n = PR_snprintf(label, sizeof(label), "%s, %s", phase_, purpose_); \
         /* Check for getting close. */                                              \
         if ((n + 1) >= sizeof(label)) {                                             \
             LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);                               \
             PORT_Assert(0);                                                         \
             goto loser;                                                             \
         }                                                                           \
     } while (0)
-#define EXPAND_TRAFFIC_KEY(purpose_, target_)                               \
-    do {                                                                    \
-        FORMAT_LABEL(phase, purpose_);                                      \
-        rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss),                  \
-                                   NULL, 0,                                 \
-                                   label, strlen(label),                    \
-                                   bulkAlgorithm, keySize, &spec->target_); \
-        if (rv != SECSuccess) {                                             \
-            LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);                       \
-            PORT_Assert(0);                                                 \
-            goto loser;                                                     \
-        }                                                                   \
-    } while (0)
-
-#define EXPAND_TRAFFIC_IV(purpose_, target_)                  \
-    do {                                                      \
-        FORMAT_LABEL(phase, purpose_);                        \
-        rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss), \
-                                      NULL, 0,                \
-                                      label, strlen(label),   \
-                                      spec->target_, ivSize); \
-        if (rv != SECSuccess) {                               \
-            LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);         \
-            PORT_Assert(0);                                   \
-            goto loser;                                       \
-        }                                                     \
-    } while (0)
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     switch (type) {
         case TrafficKeyEarlyHandshake:
+            PORT_Assert(clientKey);
             phase = kHkdfPhaseEarlyHandshakeDataKeys;
-            prkp = &ss->ssl3.hs.earlyTrafficSecret;
+            prkp = &ss->ssl3.hs.clientEarlyTrafficSecret;
             break;
         case TrafficKeyEarlyApplicationData:
+            PORT_Assert(clientKey);
             phase = kHkdfPhaseEarlyApplicationDataKeys;
-            prkp = &ss->ssl3.hs.earlyTrafficSecret;
+            prkp = &ss->ssl3.hs.clientEarlyTrafficSecret;
             break;
         case TrafficKeyHandshake:
             phase = kHkdfPhaseHandshakeKeys;
-            prkp = &ss->ssl3.hs.hsTrafficSecret;
+            prkp = clientKey ? &ss->ssl3.hs.clientHsTrafficSecret : &ss->ssl3.hs.serverHsTrafficSecret;
             break;
         case TrafficKeyApplicationData:
             phase = kHkdfPhaseApplicationDataKeys;
-            prkp = &ss->ssl3.hs.trafficSecret;
+            prkp = clientKey ? &ss->ssl3.hs.clientTrafficSecret : &ss->ssl3.hs.serverTrafficSecret;
             break;
         default:
             LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
             PORT_Assert(0);
             return SECFailure;
     }
     PORT_Assert(prkp != NULL);
     prk = *prkp;
 
     SSL_TRC(3, ("%d: TLS13[%d]: deriving traffic keys phase='%s'",
                 SSL_GETPID(), ss->fd, phase));
     PORT_Assert(phase);
     spec->phase = phase;
 
-    if ((direction == CipherSpecWrite) ^ (ss->sec.isServer)) {
-        EXPAND_TRAFFIC_KEY(kHkdfPurposeClientWriteKey, client.write_key);
-        EXPAND_TRAFFIC_IV(kHkdfPurposeClientWriteIv, client.write_iv);
-    } else {
-        EXPAND_TRAFFIC_KEY(kHkdfPurposeServerWriteKey, server.write_key);
-        EXPAND_TRAFFIC_IV(kHkdfPurposeServerWriteIv, server.write_iv);
+    FORMAT_LABEL(phase, kHkdfPurposeKey);
+    rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss),
+                               NULL, 0,
+                               label, strlen(label),
+                               bulkAlgorithm, keySize,
+                               &target->write_key);
+    if (rv != SECSuccess) {
+        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+        PORT_Assert(0);
+        goto loser;
+    }
+
+    FORMAT_LABEL(phase, kHkdfPurposeIv);
+    rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss),
+                                  NULL, 0,
+                                  label, strlen(label),
+                                  target->write_iv, ivSize);
+    if (rv != SECSuccess) {
+        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
+        PORT_Assert(0);
+        goto loser;
     }
 
     if (deleteSecret) {
         PK11_FreeSymKey(prk);
         *prkp = NULL;
     }
     return SECSuccess;
 
@@ -3030,17 +3062,17 @@ tls13_ComputeFinished(sslSocket *ss, PK1
                       PRBool sending, PRUint8 *output, unsigned int *outputLen,
                       unsigned int maxOutputLen)
 {
     SECStatus rv;
     PK11Context *hmacCtx = NULL;
     CK_MECHANISM_TYPE macAlg = tls13_GetHmacMechanism(ss);
     SECItem param = { siBuffer, NULL, 0 };
     unsigned int outputLenUint;
-    const char *label = (ss->sec.isServer ^ sending) ? kHkdfLabelClientFinishedSecret : kHkdfLabelServerFinishedSecret;
+    const char *label = kHkdfLabelFinishedSecret;
     PK11SymKey *secret = NULL;
 
     PORT_Assert(baseKey);
     PRINT_BUF(50, (NULL, "Handshake hash", hashes->hash, hashes->len));
 
     /* Now derive the appropriate finished secret from the base secret. */
     rv = tls13_HkdfExpandLabel(baseKey,
                                tls13_GetHash(ss),
@@ -3181,17 +3213,17 @@ tls13_ClientHandleFinished(sslSocket *ss
                 SSL_GETPID(), ss->fd));
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED,
                               wait_finished);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    rv = tls13_VerifyFinished(ss, ss->ssl3.hs.hsTrafficSecret,
+    rv = tls13_VerifyFinished(ss, ss->ssl3.hs.serverHsTrafficSecret,
                               b, length, hashes);
     if (rv != SECSuccess)
         return SECFailure;
 
     return tls13_SendClientSecondRound(ss);
 }
 
 static SECStatus
@@ -3209,19 +3241,19 @@ tls13_ServerHandleFinished(sslSocket *ss
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, wait_finished,
                               wait_0rtt_finished);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     if (TLS13_IN_HS_STATE(ss, wait_finished)) {
-        secret = ss->ssl3.hs.hsTrafficSecret;
+        secret = ss->ssl3.hs.clientHsTrafficSecret;
     } else {
-        secret = ss->ssl3.hs.earlyTrafficSecret;
+        secret = ss->ssl3.hs.clientEarlyTrafficSecret;
     }
 
     rv = tls13_VerifyFinished(ss, secret, b, length, hashes);
     if (rv != SECSuccess)
         return SECFailure;
 
     if (TLS13_IN_HS_STATE(ss, wait_0rtt_finished)) {
         /* Reset the hashes. */
@@ -3282,18 +3314,20 @@ tls13_FinishHandshake(sslSocket *ss)
     rv = tls13_ComputeFinalSecrets(ss);
     if (rv != SECSuccess)
         return SECFailure;
 
     /* The first handshake is now completed. */
     ss->handshake = NULL;
 
     /* Don't need this. */
-    PK11_FreeSymKey(ss->ssl3.hs.hsTrafficSecret);
-    ss->ssl3.hs.hsTrafficSecret = NULL;
+    PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret);
+    ss->ssl3.hs.clientHsTrafficSecret = NULL;
+    PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret);
+    ss->ssl3.hs.serverHsTrafficSecret = NULL;
 
     TLS13_SET_HS_STATE(ss, idle_handshake);
 
     ssl_FinishHandshake(ss);
 
     return SECSuccess;
 }
 
@@ -3384,17 +3418,17 @@ tls13_SendClientSecondRound(sslSocket *s
         rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey);
         SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
         ss->ssl3.clientPrivateKey = NULL;
         if (rv != SECSuccess) {
             goto loser; /* err is set. */
         }
     }
 
-    rv = tls13_SendFinished(ss, ss->ssl3.hs.hsTrafficSecret);
+    rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret);
     if (rv != SECSuccess) {
         goto loser; /* err code was set. */
     }
     rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0);
     if (rv != SECSuccess) {
         goto loser;
     }
 
@@ -3664,16 +3698,18 @@ tls13_HandleNewSessionTicket(sslSocket *
 
     return SECSuccess;
 }
 
 typedef enum {
     ExtensionNotUsed,
     ExtensionClientOnly,
     ExtensionSendClear,
+    ExtensionSendClearOrHrr,
+    ExtensionSendHrr,
     ExtensionSendEncrypted,
     ExtensionNewSessionTicket
 } Tls13ExtensionStatus;
 
 static const struct {
     PRUint16 ex_value;
     Tls13ExtensionStatus status;
 } KnownExtensions[] = {
@@ -3681,33 +3717,35 @@ static const struct {
     { ssl_supported_groups_xtn, ExtensionSendEncrypted },
     { ssl_ec_point_formats_xtn, ExtensionNotUsed },
     { ssl_signature_algorithms_xtn, ExtensionSendClear },
     { ssl_use_srtp_xtn, ExtensionSendEncrypted },
     { ssl_app_layer_protocol_xtn, ExtensionSendEncrypted },
     { ssl_padding_xtn, ExtensionNotUsed },
     { ssl_extended_master_secret_xtn, ExtensionNotUsed },
     { ssl_session_ticket_xtn, ExtensionClientOnly },
-    { ssl_tls13_key_share_xtn, ExtensionSendClear },
+    { ssl_tls13_key_share_xtn, ExtensionSendClearOrHrr },
     { ssl_tls13_pre_shared_key_xtn, ExtensionSendClear },
     { ssl_tls13_early_data_xtn, ExtensionSendEncrypted },
     { ssl_next_proto_nego_xtn, ExtensionNotUsed },
     { ssl_renegotiation_info_xtn, ExtensionNotUsed },
     { ssl_signed_cert_timestamp_xtn, ExtensionSendEncrypted },
     { ssl_cert_status_xtn, ExtensionSendEncrypted },
-    { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket }
+    { ssl_tls13_ticket_early_data_info_xtn, ExtensionNewSessionTicket },
+    { ssl_tls13_cookie_xtn, ExtensionSendHrr }
 };
 
 PRBool
 tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message)
 {
     unsigned int i;
 
     PORT_Assert((message == client_hello) ||
                 (message == server_hello) ||
+                (message == hello_retry_request) ||
                 (message == encrypted_extensions) ||
                 (message == new_session_ticket));
 
     for (i = 0; i < PR_ARRAY_SIZE(KnownExtensions); i++) {
         if (KnownExtensions[i].ex_value == extension)
             break;
     }
     if (i == PR_ARRAY_SIZE(KnownExtensions)) {
@@ -3719,16 +3757,23 @@ tls13_ExtensionAllowed(PRUint16 extensio
     switch (KnownExtensions[i].status) {
         case ExtensionNotUsed:
             return PR_FALSE;
         case ExtensionClientOnly:
             return message == client_hello;
         case ExtensionSendClear:
             return message == client_hello ||
                    message == server_hello;
+        case ExtensionSendClearOrHrr:
+            return message == client_hello ||
+                   message == server_hello ||
+                   message == hello_retry_request;
+        case ExtensionSendHrr:
+            return message == client_hello ||
+                   message == hello_retry_request;
         case ExtensionSendEncrypted:
             return message == client_hello ||
                    message == encrypted_extensions;
         case ExtensionNewSessionTicket:
             return message == new_session_ticket;
     }
 
     PORT_Assert(0);
@@ -4013,17 +4058,17 @@ tls13_MaybeDo0RTTHandshake(sslSocket *ss
 
     rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyHandshake,
                              CipherSpecWrite, PR_FALSE);
     if (rv != SECSuccess) {
         FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
         return SECFailure;
     }
 
-    rv = tls13_SendFinished(ss, ss->ssl3.hs.earlyTrafficSecret);
+    rv = tls13_SendFinished(ss, ss->ssl3.hs.clientEarlyTrafficSecret);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
     /* Restore the handshake hashes to where they were before we
      * sent Finished. */
     ss->ssl3.hs.messages.len = bufferLen;
 
--- a/security/nss/lib/ssl/tls13con.h
+++ b/security/nss/lib/ssl/tls13con.h
@@ -47,16 +47,17 @@ SECStatus tls13_HandleClientHelloPart2(s
                                        const SECItem *suites,
                                        sslSessionID *sid);
 SECStatus tls13_HandleServerHelloPart2(sslSocket *ss);
 SECStatus tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, SSL3Opaque *b,
                                                 PRUint32 length,
                                                 SSL3Hashes *hashesPtr);
 void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry);
 void tls13_DestroyKeyShares(PRCList *list);
+SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef);
 void tls13_DestroyEarlyData(PRCList *list);
 void tls13_CipherSpecAddRef(ssl3CipherSpec *spec);
 void tls13_CipherSpecRelease(ssl3CipherSpec *spec);
 void tls13_DestroyCipherSpecs(PRCList *list);
 PRBool tls13_ExtensionAllowed(PRUint16 extension, SSL3HandshakeType message);
 SECStatus tls13_ProtectRecord(sslSocket *ss,
                               ssl3CipherSpec *cwSpec,
                               SSL3ContentType type,
--- a/security/nss/lib/util/secasn1e.c
+++ b/security/nss/lib/util/secasn1e.c
@@ -1415,18 +1415,20 @@ static void
 sec_asn1e_encode_item_store(void *arg, const char *buf, unsigned long len,
                             int depth, SEC_ASN1EncodingPart data_kind)
 {
     SECItem *dest;
 
     dest = (SECItem *)arg;
     PORT_Assert(dest != NULL);
 
-    PORT_Memcpy(dest->data + dest->len, buf, len);
-    dest->len += len;
+    if (len > 0) {
+        PORT_Memcpy(dest->data + dest->len, buf, len);
+        dest->len += len;
+    }
 }
 
 /*
  * Allocate an entire SECItem, or just the data part of it, to hold
  * "len" bytes of stuff.  Allocate from the given pool, if specified,
  * otherwise just do a vanilla PORT_Alloc.
  *
  * XXX This seems like a reasonable general-purpose function (for SECITEM_)?
--- a/security/nss/lib/util/utf8.c
+++ b/security/nss/lib/util/utf8.c
@@ -317,25 +317,28 @@ sec_port_ucs2_utf8_conversion_function(
             return PR_FALSE;
         }
 
         for (i = 0; i < inBufLen; i += 2) {
             if ((inBuf[i + H_0] == 0x00) && ((inBuf[i + H_1] & 0x80) == 0x00))
                 len += 1;
             else if (inBuf[i + H_0] < 0x08)
                 len += 2;
-            else if (((inBuf[i + 0 + H_0] & 0xFC) == 0xD8)) {
+            else if (((inBuf[i + H_0] & 0xFC) == 0xD8)) {
                 if (((inBufLen - i) > 2) && ((inBuf[i + 2 + H_0] & 0xFC) == 0xDC)) {
                     i += 2;
                     len += 4;
                 } else {
                     return PR_FALSE;
                 }
-            } else
+            } else if ((inBuf[i + H_0] & 0xFC) == 0xDC) {
+                return PR_FALSE;
+            } else {
                 len += 3;
+            }
         }
 
         if (len > maxOutBufLen) {
             *outBufLen = len;
             return PR_FALSE;
         }
 
         len = 0;
--- a/taskcluster/ci/docker-image/image.yml
+++ b/taskcluster/ci/docker-image/image.yml
@@ -30,16 +30,17 @@ task:
       CONTEXT_URL: '{{context_url}}'
       CONTEXT_PATH: '{{context_path}}'
       BASE_REPOSITORY: '{{base_repository}}'
       HEAD_REPOSITORY: '{{head_repository}}'
       HEAD_REV: '{{head_rev}}'
       HEAD_REF: '{{head_ref}}'
     features:
       dind: true
+      chainOfTrust: true
     image: '{{#docker_image}}image_builder{{/docker_image}}'
     command:
       - /bin/bash
       - -c
       - /home/worker/bin/build_image.sh
     maxRunTime: 3600
     artifacts:
       '{{artifact_path}}':
--- a/taskcluster/taskgraph/transforms/build.py
+++ b/taskcluster/taskgraph/transforms/build.py
@@ -16,9 +16,10 @@ transforms = TransformSequence()
 @transforms.add
 def set_defaults(config, jobs):
     """Set defaults, including those that differ per worker implementation"""
     for job in jobs:
         job['treeherder'].setdefault('kind', 'build')
         job['treeherder'].setdefault('tier', 1)
         if job['worker']['implementation'] in ('docker-worker', 'docker-engine'):
             job['worker'].setdefault('docker-image', {'in-tree': 'desktop-build'})
+            job['worker']['chainOfTrust'] = True
         yield job
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -152,16 +152,17 @@ task_description_schema = Schema({
             # a raw Docker image path (repo/image:tag)
             basestring,
             # an in-tree generated docker image (from `testing/docker/<name>`)
             {'in-tree': basestring}
         ),
 
         # worker features that should be enabled
         Required('relengapi-proxy', default=False): bool,
+        Required('chainOfTrust', default=False): bool,
         Required('taskcluster-proxy', default=False): bool,
         Required('allow-ptrace', default=False): bool,
         Required('loopback-video', default=False): bool,
         Required('loopback-audio', default=False): bool,
 
         # caches to set up for the task
         Optional('caches'): [{
             # only one type is supported by any of the workers right now
@@ -321,16 +322,19 @@ def build_docker_worker_payload(config, 
 
     if worker.get('taskcluster-proxy'):
         features['taskclusterProxy'] = True
 
     if worker.get('allow-ptrace'):
         features['allowPtrace'] = True
         task_def['scopes'].append('docker-worker:feature:allowPtrace')
 
+    if worker.get('chainOfTrust'):
+        features['chainOfTrust'] = True
+
     capabilities = {}
 
     for lo in 'audio', 'video':
         if worker.get('loopback-' + lo):
             capitalized = 'loopback' + lo.capitalize()
             devices = capabilities.setdefault('devices', {})
             devices[capitalized] = True
             task_def['scopes'].append('docker-worker:capability:device:' + capitalized)
--- a/testing/tools/fileid/linux_fileid.cpp
+++ b/testing/tools/fileid/linux_fileid.cpp
@@ -1,51 +1,41 @@
 /* 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 <stdio.h>
+#include <string>
 
 #include "common/linux/file_id.h"
+#include "common/memory.h"
 
-//TODO: move this somewhere common, this is copied from dump_symbols.cc
-// Format the Elf file identifier in IDENTIFIER as a UUID with the
-// dashes removed.
-void FormatIdentifier(unsigned char identifier[google_breakpad::kMDGUIDSize],
-                      char result_guid[40]) {
-  char identifier_str[40];
-  google_breakpad::FileID::ConvertIdentifierToString(
-      identifier,
-      identifier_str,
-      sizeof(identifier_str));
-  int bufpos = 0;
-  for (int i = 0; identifier_str[i] != '\0'; ++i)
-    if (identifier_str[i] != '-')
-      result_guid[bufpos++] = identifier_str[i];
-  // Add an extra "0" by the end.  PDB files on Windows have an 'age'
-  // number appended to the end of the file identifier; this isn't
-  // really used or necessary on other platforms, but let's preserve
-  // the pattern.
-  result_guid[bufpos++] = '0';
-  // And null terminate.
-  result_guid[bufpos] = '\0';
-}
+using std::string;
 
+using google_breakpad::auto_wasteful_vector;
+using google_breakpad::FileID;
+using google_breakpad::PageAllocator;
 
 int main(int argc, char** argv)
 {
+
   if (argc != 2) {
     fprintf(stderr, "usage: fileid <elf file>\n");
     return 1;
   }
 
-  unsigned char identifier[google_breakpad::kMDGUIDSize];
-  google_breakpad::FileID file_id(argv[1]);
+  PageAllocator allocator;
+  auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator);
+  FileID file_id(argv[1]);
   if (!file_id.ElfFileIdentifier(identifier)) {
     fprintf(stderr, "%s: unable to generate file identifier\n",
             argv[1]);
     return 1;
   }
-  char result_guid[40];
-  FormatIdentifier(identifier, result_guid);
-  printf("%s\n", result_guid);
+
+  string result_guid = FileID::ConvertIdentifierToUUIDString(identifier);
+
+  // Add an extra "0" at the end.  PDB files on Windows have an 'age'
+  // number appended to the end of the file identifier; this isn't
+  // really used or necessary on other platforms, but be consistent.
+  printf("%s0\n", result_guid.c_str());
   return 0;
 }
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -1547,17 +1547,20 @@ internal_JSHistogram_Add(JSContext *cx, 
     }
 
     if (!JS::ToUint32(cx, args[0], &value)) {
       JS_ReportErrorASCII(cx, "Failed to convert argument");
       return false;
     }
   }
 
-  internal_Accumulate(*h, value);
+  {
+    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+    internal_Accumulate(*h, value);
+  }
   return true;
 }
 
 bool
 internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
@@ -1795,17 +1798,20 @@ internal_JSKeyedHistogram_Add(JSContext 
       return false;
     }
 
     if (!JS::ToInt32(cx, args[1], &value)) {
       return false;
     }
   }
 
-  internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
+  {
+    StaticMutexAutoLock locker(gTelemetryHistogramMutex);
+    internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
+  }
   return true;
 }
 
 bool
 internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj) {
--- a/toolkit/crashreporter/google-breakpad/.gitignore
+++ b/toolkit/crashreporter/google-breakpad/.gitignore
@@ -26,30 +26,32 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # Ignore other VCSs.
 .svn/
 
 # Ignore common compiled artifacts.
 *~
+*.dwo
 *.o
 lib*.a
 /breakpad.pc
 /breakpad-client.pc
 /src/client/linux/linux_client_unittest_shlib
 /src/client/linux/linux_dumper_unittest_helper
 /src/processor/microdump_stackwalk
 /src/processor/minidump_dump
 /src/processor/minidump_stackwalk
 /src/tools/linux/core2md/core2md
 /src/tools/linux/dump_syms/dump_syms
 /src/tools/linux/md2core/minidump-2-core
 /src/tools/linux/symupload/minidump_upload
 /src/tools/linux/symupload/sym_upload
+/src/tools/mac/dump_syms/dump_syms
 
 # Ignore autotools generated artifacts.
 .deps
 .dirstamp
 autom4te.cache/
 /config.cache
 config.h
 /config.log
--- a/toolkit/crashreporter/google-breakpad/DEPS
+++ b/toolkit/crashreporter/google-breakpad/DEPS
@@ -30,33 +30,41 @@
 # SVN) based checkouts of Breakpad. As such, its use is entirely optional. If
 # using a manually managed SVN checkout as opposed to a gclient managed checkout
 # you can still use the hooks mechanism for generating project files by calling
 # 'gclient runhooks' rather than 'gclient sync'.
 
 deps = {
   # Logging code.
   "src/src/third_party/glog":
-    "http://google-glog.googlecode.com/svn/trunk@97",
+    "https://github.com/google/glog.git" +
+      "@v0.3.4",
 
   # Testing libraries and utilities.
-  "src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408",
-  "src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615",
+  "src/src/testing":
+    "https://github.com/google/googlemock.git" +
+      "@release-1.7.0",
+  "src/src/testing/gtest":
+    "https://github.com/google/googletest.git" +
+      "@release-1.7.0",
 
   # Protobuf.
   "src/src/third_party/protobuf/protobuf":
-    "http://protobuf.googlecode.com/svn/trunk@407",
+    "https://github.com/google/protobuf.git" +
+      "@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac",
 
   # GYP project generator.
-  "src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886",
+  "src/src/tools/gyp":
+    "https://chromium.googlesource.com/external/gyp/" +
+      "@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
 
   # Linux syscall support.
   "src/src/third_party/lss":
     "https://chromium.googlesource.com/linux-syscall-support/" +
-      "@9292030109847793f7a6689adac1ddafb412fe14"
+      "@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb",
 }
 
 hooks = [
   {
     # TODO(chrisha): Fix the GYP files so that they work without
     # --no-circular-check.
     "pattern": ".",
     "action": ["python",
--- a/toolkit/crashreporter/google-breakpad/GIT-INFO
+++ b/toolkit/crashreporter/google-breakpad/GIT-INFO
@@ -1,1 +1,1 @@
-c53ed143108948eb7e2d7ee77dc8c0d92050ce7c
+704f41ec901c419f8c321742114b415e6f5ceacc
--- a/toolkit/crashreporter/google-breakpad/Makefile.am
+++ b/toolkit/crashreporter/google-breakpad/Makefile.am
@@ -78,16 +78,19 @@ dist_doc_DATA = \
 	NEWS \
 	README.md
 
 ## Headers
 if LINUX_HOST
 includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler
 includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h
 
+includecldwcdir = $(includedir)/$(PACKAGE)/client/linux/dump_writer_common
+includecldwc_HEADERS = $(top_srcdir)/src/client/linux/dump_writer_common/*.h
+
 includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer
 includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h
 
 includeclcdir = $(includedir)/$(PACKAGE)/client/linux/crash_generation
 includeclc_HEADERS = $(top_srcdir)/src/client/linux/crash_generation/*.h
 
 includelssdir = $(includedir)/$(PACKAGE)/third_party/lss
 includelss_HEADERS = $(top_srcdir)/src/third_party/lss/*.h
@@ -104,21 +107,51 @@ includec_HEADERS = $(top_srcdir)/src/com
 
 includepdir = $(includedir)/$(PACKAGE)/processor
 includep_HEADERS = $(top_srcdir)/src/processor/*.h
 
 ## pkgconfig files
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA =
 
+## Common test logic
+if SYSTEM_TEST_LIBS
+TEST_CFLAGS = $(GTEST_CFLAGS) $(GMOCK_CFLAGS)
+TEST_LIBS = $(GTEST_LIBS) -lgtest_main $(GMOCK_LIBS)
+TEST_DEPS =
+else
+TEST_CFLAGS = \
+	-I$(top_srcdir)/src/testing/include \
+	-I$(top_srcdir)/src/testing/gtest/include \
+	-I$(top_srcdir)/src/testing/gtest \
+	-I$(top_srcdir)/src/testing
+TEST_LIBS = src/testing/libtesting.a
+TEST_DEPS = $(TEST_LIBS)
+endif
+
 ## Libraries
+check_LIBRARIES =
 noinst_LIBRARIES =
 lib_LIBRARIES =
 bin_PROGRAMS =
 check_PROGRAMS =
+EXTRA_PROGRAMS =
+CLEANFILES =
+
+check_LIBRARIES += src/testing/libtesting.a
+
+if !SYSTEM_TEST_LIBS
+src_testing_libtesting_a_SOURCES = \
+	src/breakpad_googletest_includes.h \
+	src/testing/gtest/src/gtest-all.cc \
+	src/testing/gtest/src/gtest_main.cc \
+	src/testing/src/gmock-all.cc
+src_testing_libtesting_a_CPPFLAGS = \
+	$(AM_CPPFLAGS) $(TEST_CFLAGS)
+endif
 
 if !DISABLE_PROCESSOR
 lib_LIBRARIES += src/libbreakpad.a
 pkgconfig_DATA += breakpad.pc
 noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a
 endif
 
 if LINUX_HOST
@@ -126,29 +159,43 @@ lib_LIBRARIES += src/client/linux/libbre
 pkgconfig_DATA += breakpad-client.pc
 
 src_client_linux_libbreakpad_client_a_SOURCES = \
 	src/client/linux/crash_generation/crash_generation_client.cc \
 	src/client/linux/crash_generation/crash_generation_server.cc \
 	src/client/linux/dump_writer_common/thread_info.cc \
 	src/client/linux/dump_writer_common/ucontext_reader.cc \
 	src/client/linux/handler/exception_handler.cc \
+	src/client/linux/handler/exception_handler.h \
 	src/client/linux/handler/minidump_descriptor.cc \
+	src/client/linux/handler/minidump_descriptor.h \
 	src/client/linux/log/log.cc \
+	src/client/linux/log/log.h \
 	src/client/linux/microdump_writer/microdump_writer.cc \
+	src/client/linux/microdump_writer/microdump_writer.h \
+	src/client/linux/minidump_writer/linux_core_dumper.cc \
 	src/client/linux/minidump_writer/linux_dumper.cc \
 	src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
 	src/client/linux/minidump_writer/minidump_writer.cc \
+	src/client/minidump_file_writer-inl.h \
 	src/client/minidump_file_writer.cc \
+	src/client/minidump_file_writer.h \
 	src/common/convert_UTF.c \
+	src/common/convert_UTF.h \
 	src/common/md5.cc \
+	src/common/md5.h \
 	src/common/string_conversion.cc \
+	src/common/string_conversion.h \
+	src/common/linux/elf_core_dump.cc \
 	src/common/linux/elfutils.cc \
+	src/common/linux/elfutils.h \
 	src/common/linux/file_id.cc \
+	src/common/linux/file_id.h \
 	src/common/linux/guid_creator.cc \
+	src/common/linux/guid_creator.h \
 	src/common/linux/linux_libc_support.cc \
 	src/common/linux/memory_mapped_file.cc \
 	src/common/linux/safe_readlink.cc
 if ANDROID_HOST
 src_client_linux_libbreakpad_client_a_SOURCES += \
 	src/common/android/breakpad_getcontext.S
 endif
 endif LINUX_HOST
@@ -265,18 +312,16 @@ src_libbreakpad_a_SOURCES = \
 	src/processor/static_map.h \
 	src/processor/static_range_map-inl.h \
 	src/processor/static_range_map.h \
 	src/processor/symbolic_constants_win.cc \
 	src/processor/symbolic_constants_win.h \
 	src/processor/tokenize.cc \
 	src/processor/tokenize.h
 
-src_libbreakpad_a_LIBADD = src/third_party/libdisasm/libdisasm.a
-
 src_third_party_libdisasm_libdisasm_a_SOURCES = \
 	src/third_party/libdisasm/ia32_implicit.c \
 	src/third_party/libdisasm/ia32_implicit.h \
 	src/third_party/libdisasm/ia32_insn.c \
 	src/third_party/libdisasm/ia32_insn.h \
 	src/third_party/libdisasm/ia32_invariant.c \
 	src/third_party/libdisasm/ia32_invariant.h \
 	src/third_party/libdisasm/ia32_modrm.c \
@@ -303,27 +348,32 @@ src_third_party_libdisasm_libdisasm_a_SO
 ## Programs
 bin_PROGRAMS += \
 	src/processor/microdump_stackwalk \
 	src/processor/minidump_dump \
 	src/processor/minidump_stackwalk
 endif !DISABLE_PROCESSOR
 
 if LINUX_HOST
-bin_PROGRAMS += \
+EXTRA_PROGRAMS += \
+	src/client/linux/linux_dumper_unittest_helper
+CLEANFILES += \
 	src/client/linux/linux_dumper_unittest_helper
 
 if !DISABLE_TOOLS
 bin_PROGRAMS += \
 	src/tools/linux/core2md/core2md \
 	src/tools/linux/dump_syms/dump_syms \
 	src/tools/linux/md2core/minidump-2-core \
 	src/tools/linux/symupload/minidump_upload \
-	src/tools/linux/symupload/sym_upload \
-	src/tools/mac/dump_syms/dump_syms
+	src/tools/linux/symupload/sym_upload
+if X86_HOST
+bin_PROGRAMS += \
+	src/tools/mac/dump_syms/dump_syms_mac
+endif
 endif
 endif LINUX_HOST
 
 
 ## Tests
 if !DISABLE_PROCESSOR
 check_PROGRAMS += \
 	src/common/test_assembler_unittest \
@@ -340,38 +390,45 @@ check_PROGRAMS += \
 	src/processor/minidump_unittest \
 	src/processor/static_address_map_unittest \
 	src/processor/static_contained_range_map_unittest \
 	src/processor/static_map_unittest \
 	src/processor/static_range_map_unittest \
 	src/processor/pathname_stripper_unittest \
 	src/processor/postfix_evaluator_unittest \
 	src/processor/proc_maps_linux_unittest \
+	src/processor/range_map_shrink_down_unittest \
 	src/processor/range_map_unittest \
 	src/processor/stackwalker_amd64_unittest \
 	src/processor/stackwalker_arm_unittest \
 	src/processor/stackwalker_arm64_unittest \
 	src/processor/stackwalker_address_list_unittest \
 	src/processor/stackwalker_mips_unittest \
+	src/processor/stackwalker_mips64_unittest \
 	src/processor/stackwalker_x86_unittest \
 	src/processor/synth_minidump_unittest
 endif
 
 if LINUX_HOST
-EXTRA_PROGRAMS = \
+EXTRA_PROGRAMS += \
+	src/client/linux/linux_client_unittest_shlib
+CLEANFILES += \
 	src/client/linux/linux_client_unittest_shlib
 
 check_PROGRAMS += \
 	src/client/linux/linux_client_unittest
 
 if !DISABLE_TOOLS
 check_PROGRAMS += \
 	src/common/dumper_unittest \
-	src/common/mac/macho_reader_unittest \
 	src/tools/linux/md2core/minidump_2_core_unittest
+if X86_HOST
+check_PROGRAMS += \
+	src/common/mac/macho_reader_unittest
+endif
 endif
 endif LINUX_HOST
 
 if !DISABLE_PROCESSOR
 if SELFTEST
 check_PROGRAMS += \
 	src/processor/stackwalker_selftest
 endif SELFTEST
@@ -408,34 +465,35 @@ if ANDROID_HOST
 # On Android PTHREAD_CFLAGS is empty, and adding src/common/android/include
 # to the include path is necessary to build this program.
 src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(AM_CXXFLAGS)
 else
 src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
 endif
 
 src_client_linux_linux_client_unittest_shlib_SOURCES = \
+	$(src_testing_libtesting_a_SOURCES) \
 	src/client/linux/handler/exception_handler_unittest.cc \
 	src/client/linux/minidump_writer/directory_reader_unittest.cc \
 	src/client/linux/minidump_writer/cpu_set_unittest.cc \
 	src/clie