merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 06 Oct 2016 11:59:54 +0200
changeset 316736 777fb63db8de8d78b64b8141d1a998397275dd03
parent 316667 c3b0354515b6bb59a5324adc21e7e891d6217cae (current diff)
parent 316735 8a4a43911b776d61be3817ec11073b34fdcb672d (diff)
child 316737 38442ad9a422c1028f6e40b5144f3d7994e7acf3
child 316746 fef343991976c7ae6d9bb1960f03aa8b3c634288
child 316802 d0eb4cc9fd0d5f19949e1068863d482343873245
child 316845 5ef613528deaca7fb841e0f2b58f57b99c04cf7b
push id30780
push usercbook@mozilla.com
push dateThu, 06 Oct 2016 10:00:07 +0000
treeherdermozilla-central@777fb63db8de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
777fb63db8de / 52.0a1 / 20161006030208 / files
nightly linux64
777fb63db8de / 52.0a1 / 20161006030208 / files
nightly mac
777fb63db8de / 52.0a1 / 20161006030208 / files
nightly win32
777fb63db8de / 52.0a1 / 20161006030208 / files
nightly win64
777fb63db8de / 52.0a1 / 20161006030208 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central 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