Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 17 Sep 2016 13:44:34 -0700
changeset 314344 eaf5eb6f8fa0d8e7a09f3774c0da53c0dd6dadd7
parent 314291 f24abd2da7eb5adcd170bfb32c6f68feb8bb580e (current diff)
parent 314343 4606672d8efe7cc571a8a947edb52b984708cd84 (diff)
child 314345 de404845c4f77fd4daa2ec9aaeec9170adc27a65
child 314352 639635348b41780875198e5f702d5714078deeee
child 314361 7f798aafd21aed88c13d648390f77a4457f00872
push id30718
push userphilringnalda@gmail.com
push dateSat, 17 Sep 2016 20:44:56 +0000
treeherdermozilla-central@eaf5eb6f8fa0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
first release with
nightly linux32
eaf5eb6f8fa0 / 51.0a1 / 20160918030408 / files
nightly linux64
eaf5eb6f8fa0 / 51.0a1 / 20160918030408 / files
nightly mac
eaf5eb6f8fa0 / 51.0a1 / 20160918030408 / files
nightly win32
eaf5eb6f8fa0 / 51.0a1 / 20160918030408 / files
nightly win64
eaf5eb6f8fa0 / 51.0a1 / 20160918030408 / 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 m-i to m-c, a=merge
modules/libpref/init/all.js
security/nss/automation/taskcluster/decision_task.yml
security/nss/automation/taskcluster/graph/build.js
security/nss/automation/taskcluster/graph/linux/_build_base.yml
security/nss/automation/taskcluster/graph/linux/_test_base.yml
security/nss/automation/taskcluster/graph/linux/build32-debug.yml
security/nss/automation/taskcluster/graph/linux/build32-opt.yml
security/nss/automation/taskcluster/graph/linux/build64-asan.yml
security/nss/automation/taskcluster/graph/linux/build64-debug.yml
security/nss/automation/taskcluster/graph/linux/build64-opt.yml
security/nss/automation/taskcluster/graph/tests/chains.yml
security/nss/automation/taskcluster/graph/tests/crmf.yml
security/nss/automation/taskcluster/graph/tests/fips.yml
security/nss/automation/taskcluster/graph/tests/ssl.yml
security/nss/automation/taskcluster/graph/tools/_build_base.yml
security/nss/automation/taskcluster/graph/tools/clang-format.yml
security/nss/automation/taskcluster/graph/tools/scan-build.yml
security/nss/automation/taskcluster/graph/windows/_build_base.yml
security/nss/automation/taskcluster/graph/windows/_test_base.yml
security/nss/automation/taskcluster/graph/windows/build64-debug.yml
security/nss/automation/taskcluster/graph/windows/build64-opt.yml
widget/android/MemoryMonitor.h
widget/android/Telemetry.h
widget/android/ThumbnailHelper.h
--- a/accessible/ipc/DocAccessibleChildBase.cpp
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -77,23 +77,61 @@ DocAccessibleChildBase::SerializeTree(Ac
   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,16 +55,20 @@ 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,18 +11,23 @@
 #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");
@@ -39,17 +44,23 @@ 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;
   }
 
@@ -57,38 +68,46 @@ 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)
+                                uint32_t aIdx, uint32_t aIdxInParent
+#if defined(XP_WIN)
+                                , nsTArray<MsaaMapping>* aNewMsaaIds
+#endif
+                                )
 {
   if (aNewTree.Length() <= aIdx) {
     NS_ERROR("bad index in serialized tree!");
     return 0;
   }
 
   const AccessibleData& newChild = aNewTree[aIdx];
   if (newChild.Role() > roles::LAST_ROLE) {
@@ -119,20 +138,32 @@ DocAccessibleParent::AddSubtree(ProxyAcc
     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)
+
   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);
+    uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i
+#if defined(XP_WIN)
+                                   , aNewMsaaIds
+#endif
+                                  );
     if (!consumed)
       return 0;
 
     accessibles += consumed;
   }
 
   MOZ_ASSERT(newProxy->ChildrenCount() == kids);
 
@@ -471,26 +502,29 @@ DocAccessibleParent::GetXPCAccessible(Pr
 /**
  * @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)
+                                  IAccessibleHolder* aParentCOMProxy,
+                                  uint32_t* 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;
 }
 #endif // defined(XP_WIN)
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -45,18 +45,23 @@ 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;
@@ -139,17 +144,18 @@ public:
     { 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) override;
+                            IAccessibleHolder* aParentCOMProxy,
+                            uint32_t* aMsaaID) override;
 #endif
 
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
     explicit ProxyEntry(const void*) : mProxy(nullptr) {}
@@ -169,17 +175,21 @@ private:
 
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
-                      uint32_t aIdxInParent);
+                      uint32_t aIdxInParent
+#if defined(XP_WIN)
+                      , nsTArray<MsaaMapping>* aNewMsaaIds
+#endif // defined(XP_WIN)
+                      );
   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
@@ -29,15 +29,17 @@ DocAccessibleChild::~DocAccessibleChild(
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
 DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy)
 {
   IAccessibleHolder parentProxy;
-  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy);
+  uint32_t msaaID = AccessibleWrap::kNoID;
+  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy, &msaaID);
   mParentProxy.reset(parentProxy.Release());
+  mDoc->SetID(msaaID);
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -22,16 +22,22 @@ struct AccessibleData
 
 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
 {
@@ -40,17 +46,17 @@ 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);
-  async ShowEvent(ShowEventData data, bool aFromuser);
+  sync ShowEventInfo(ShowEventData data) returns (MsaaMapping[] aNewMsaaIds);
   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);
 
@@ -58,16 +64,16 @@ parent:
    * 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);
+    returns(IAccessibleHolder aParentCOMProxy, uint32_t aMsaaID);
 
 child:
   async __delete__();
 };
 
 }
 }
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -4,16 +4,18 @@
  * 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 "AccessibleWrap.h"
 #include "Accessible-inl.h"
 
 #include "Compatibility.h"
 #include "DocAccessible-inl.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/a11y/DocAccessibleChild.h"
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "EnumVariant.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleEvent.h"
 #include "nsWinUtils.h"
 #include "mozilla/a11y/ProxyAccessible.h"
 #include "ProxyWrappers.h"
@@ -64,19 +66,17 @@ IDSet AccessibleWrap::sIDGen;
 
 static const int32_t kIEnumVariantDisconnected = -1;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccessibleWrap
 ////////////////////////////////////////////////////////////////////////////////
 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
   Accessible(aContent, aDoc)
-#ifdef _WIN64
   , mID(kNoID)
-#endif
 {
 }
 
 AccessibleWrap::~AccessibleWrap()
 {
 #ifdef _WIN64
   if (mID != kNoID)
     sIDGen.ReleaseID(mID);
@@ -85,25 +85,23 @@ AccessibleWrap::~AccessibleWrap()
 
 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
 
 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
 
 void
 AccessibleWrap::Shutdown()
 {
-#ifdef _WIN64
   if (mID != kNoID) {
     auto doc = static_cast<DocAccessibleWrap*>(mDoc);
     MOZ_ASSERT(doc);
     if (doc) {
       doc->RemoveID(mID);
     }
   }
-#endif
 
   Accessible::Shutdown();
 }
 
 //-----------------------------------------------------
 // IUnknown interface methods - see iunknown.h for documentation
 //-----------------------------------------------------
 
@@ -1143,18 +1141,31 @@ AccessibleWrap::Invoke(DISPID dispIdMemb
 void
 AccessibleWrap::GetNativeInterface(void** aOutAccessible)
 {
   *aOutAccessible = static_cast<IAccessible*>(this);
   NS_ADDREF_THIS();
 }
 
 void
+AccessibleWrap::SetID(uint32_t aID)
+{
+  MOZ_ASSERT(XRE_IsContentProcess());
+  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");
 
   NS_ASSERTION(aEventType > 0 && aEventType < ArrayLength(gWinEventMap), "invalid event type");
 
   uint32_t winEvent = gWinEventMap[aEventType];
   if (!winEvent)
     return;
@@ -1241,16 +1252,23 @@ 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()) {
+    const uint32_t id = static_cast<AccessibleWrap*>(aAccessible)->mID;
+    MOZ_ASSERT(id != kNoID);
+    return id;
+  }
+
 #ifdef _WIN64
   if (!aAccessible->Document() && !aAccessible->IsProxy())
     return 0;
 
   uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID;
   if (*id != kNoID)
     return *id;
 
@@ -1281,16 +1299,32 @@ AccessibleWrap::GetChildIDFor(Accessible
 
 HWND
 AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
 {
   if (!aAccessible) {
     return nullptr;
   }
 
+  if (XRE_IsContentProcess()) {
+    DocAccessible* doc = aAccessible->Document();
+    if (!doc) {
+      return nullptr;
+    }
+
+    DocAccessibleChild* ipcDoc = doc->IPCDoc();
+    if (!ipcDoc) {
+      return nullptr;
+    }
+
+    auto tab = static_cast<dom::TabChild*>(ipcDoc->Manager());
+    MOZ_ASSERT(tab);
+    return reinterpret_cast<HWND>(tab->GetNativeWindowHandle());
+  }
+
   // Accessibles in child processes are said to have the HWND of the window
   // their tab is within.  Popups are always in the parent process, and so
   // never proxied, which means this is basically correct.
   if (aAccessible->IsProxy()) {
     ProxyAccessible* proxy = aAccessible->Proxy();
     if (!proxy) {
       return nullptr;
     }
@@ -1340,34 +1374,32 @@ AccessibleWrap::NativeAccessible(Accessi
     return nullptr;
   }
 
   IAccessible* msaaAccessible = nullptr;
   aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible));
   return static_cast<IDispatch*>(msaaAccessible);
 }
 
-#ifdef _WIN64
 static Accessible*
 GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID)
 {
   Accessible* child = static_cast<DocAccessibleWrap*>(aDoc)->GetAccessibleByID(aID);
   if (child)
     return child;
 
   uint32_t childDocCount = aDoc->ChildDocumentCount();
   for (uint32_t i = 0; i < childDocCount; i++) {
     child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID);
     if (child)
       return child;
   }
 
     return nullptr;
   }
-#endif
 
 static AccessibleWrap*
 GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, uint32_t aID)
 {
   auto wrapper = static_cast<DocProxyAccessibleWrap*>(WrapperFor(aDoc));
   AccessibleWrap* child = wrapper->GetAccessibleByID(aID);
   if (child) {
     return child;
@@ -1421,17 +1453,19 @@ AccessibleWrap::GetXPAccessibleFor(const
   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
-      document->GetAccessibleByUniqueIDInSubtree(uniqueID);
+      XRE_IsContentProcess() ?
+        GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal)) :
+        document->GetAccessibleByUniqueIDInSubtree(uniqueID);
 #endif
 
     // If it is a document then just return an accessible.
     if (child && IsDoc())
       return child;
 
     // Otherwise check whether the accessible is a child (this path works for
     // ARIA documents and popups).
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -174,27 +174,25 @@ public: // construction, destruction
    * Find an accessible by the given child ID in cached documents.
    */
   Accessible* GetXPAccessibleFor(const VARIANT& aVarChild);
 
   virtual void GetNativeInterface(void **aOutAccessible) override;
 
   static IDispatch* NativeAccessible(Accessible* aAccessible);
 
-#ifdef _WIN64
   uint32_t GetExistingID() const { return mID; }
   static const uint32_t kNoID = 0;
-#endif
+  // This is only valid to call in content
+  void SetID(uint32_t aID);
 
 protected:
   virtual ~AccessibleWrap();
 
-#ifdef _WIN64
   uint32_t mID;
-#endif
 
   /**
    * Return the wrapper for the document's proxy.
    */
   DocProxyAccessibleWrap* DocProxyWrapper() const;
 
   /**
    * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it.
--- a/accessible/windows/msaa/DocAccessibleWrap.cpp
+++ b/accessible/windows/msaa/DocAccessibleWrap.cpp
@@ -43,25 +43,20 @@ IMPL_IUNKNOWN_QUERY_HEAD(DocAccessibleWr
     return S_OK;
   }
 IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(HyperTextAccessibleWrap)
 
 STDMETHODIMP
 DocAccessibleWrap::get_accParent(
       /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
 {
-  HRESULT hr = DocAccessible::get_accParent(ppdispParent);
-  if (*ppdispParent) {
-    return hr;
-  }
-
   // We might be a top-level document in a content process.
   DocAccessibleChild* ipcDoc = IPCDoc();
   if (!ipcDoc) {
-    return S_FALSE;
+    return DocAccessible::get_accParent(ppdispParent);
   }
   IAccessible* dispParent = ipcDoc->GetParentIAccessible();
   if (!dispParent) {
     return S_FALSE;
   }
 
   dispParent->AddRef();
   *ppdispParent = static_cast<IDispatch*>(dispParent);
--- a/accessible/windows/msaa/DocAccessibleWrap.h
+++ b/accessible/windows/msaa/DocAccessibleWrap.h
@@ -35,35 +35,31 @@ public:
   virtual void Shutdown();
 
   // DocAccessible
   virtual void* GetNativeWindow() const;
 
   /**
    * Manage the mapping from id to Accessible.
    */
-#ifdef _WIN64
   void AddID(uint32_t aID, AccessibleWrap* aAcc)
     { mIDToAccessibleMap.Put(aID, aAcc); }
   void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
   AccessibleWrap* GetAccessibleByID(uint32_t aID) const
     { return mIDToAccessibleMap.Get(aID); }
-#endif
 
 protected:
   // DocAccessible
   virtual void DoInitialUpdate();
 
 protected:
   void* mHWND;
 
   /*
    * This provides a mapping from 32 bit id to accessible objects.
    */
-#ifdef _WIN64
   nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
-#endif
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3306,16 +3306,17 @@ var PrintPreviewListener = {
     this._tabBeforePrintPreview = null;
     gInPrintPreviewMode = false;
     this._toggleAffectedChrome();
     if (this._simplifyPageTab) {
       gBrowser.removeTab(this._simplifyPageTab);
       this._simplifyPageTab = null;
     }
     gBrowser.removeTab(this._printPreviewTab);
+    gBrowser.deactivatePrintPreviewBrowsers();
     this._printPreviewTab = null;
   },
   _toggleAffectedChrome: function () {
     gNavToolbox.collapsed = gInPrintPreviewMode;
 
     if (gInPrintPreviewMode)
       this._hideChrome();
     else
@@ -3361,17 +3362,21 @@ var PrintPreviewListener = {
     if (this._chromeState.globalNotificationsOpen)
       document.getElementById("global-notificationbox").notificationsHidden = false;
 
     if (this._chromeState.syncNotificationsOpen)
       document.getElementById("sync-notifications").notificationsHidden = false;
 
     if (this._chromeState.sidebarOpen)
       SidebarUI.show(this._sidebarCommand);
-  }
+  },
+
+  activateBrowser(browser) {
+    gBrowser.activateBrowserForPrintPreview(browser);
+  },
 }
 
 function getMarkupDocumentViewer()
 {
   return gBrowser.markupDocumentViewer;
 }
 
 // This function is obsolete. Newer code should use <tooltip page="true"/> instead.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1650,18 +1650,17 @@
             }
 
             aBrowser.droppedLinkHandler = droppedLinkHandler;
 
             // Switching a browser's remoteness will create a new frameLoader.
             // As frameLoaders start out with an active docShell we have to
             // deactivate it if this is not the selected tab's browser or the
             // browser window is minimized.
-            aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
-                                         window.windowState != window.STATE_MINIMIZED);
+            aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);
 
             // Create a new tab progress listener for the new browser we just injected,
             // since tab progress listeners have logic for handling the initial about:blank
             // load
             listener = this.mTabProgressListener(tab, aBrowser, false, false);
             this._tabListeners.set(tab, listener);
             filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
 
@@ -2754,18 +2753,21 @@
 
             // Unmap old outerWindowIDs.
             this._outerWindowIDBrowserMap.delete(ourBrowser.outerWindowID);
             let remoteBrowser = aOtherBrowser.ownerDocument.defaultView.gBrowser;
             if (remoteBrowser) {
               remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
             }
 
-            aOtherBrowser.docShellIsActive = (ourBrowser == this.selectedBrowser &&
-                                              window.windowState != window.STATE_MINIMIZED);
+            // If switcher is active, it will intercept swap events and
+            // react as needed.
+            if (!this._switcher) {
+              aOtherBrowser.docShellIsActive = this.shouldActivateDocShell(ourBrowser);
+            }
 
             // Swap the docshells
             ourBrowser.swapDocShells(aOtherBrowser);
 
             if (ourBrowser.isRemoteBrowser) {
               // Switch outerWindowIDs for remote browsers.
               let ourOuterWindowID = ourBrowser._outerWindowID;
               ourBrowser._outerWindowID = aOtherBrowser._outerWindowID;
@@ -3261,16 +3263,66 @@
         <body>
           <![CDATA[
             return SessionStore.duplicateTab(window, aTab, 0, aRestoreTabImmediately);
           ]]>
         </body>
       </method>
 
       <!--
+        List of browsers whose docshells must be active in order for print preview
+        to work.
+      -->
+      <field name="_printPreviewBrowsers">
+        new Set()
+      </field>
+
+      <method name="activateBrowserForPrintPreview">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
+            this._printPreviewBrowsers.add(aBrowser);
+            if (this._switcher) {
+              this._switcher.activateBrowserForPrintPreview(aBrowser);
+            }
+            aBrowser.docShellIsActive = true;
+          ]]>
+        </body>
+      </method>
+
+      <method name="deactivatePrintPreviewBrowsers">
+        <body>
+          <![CDATA[
+            let browsers = this._printPreviewBrowsers;
+            this._printPreviewBrowsers = new Set();
+            for (let browser of browsers) {
+              browser.docShellIsActive = this.shouldActivateDocShell(browser);
+            }
+          ]]>
+        </body>
+      </method>
+
+      <!--
+        Returns true if a given browser's docshell should be active.
+      -->
+      <method name="shouldActivateDocShell">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
+            if (this._switcher) {
+              return this._switcher.shouldActivateDocShell(aBrowser);
+            }
+            return (aBrowser == this.selectedBrowser &&
+                    window.windowState != window.STATE_MINIMIZED) ||
+                   this._printPreviewBrowsers.has(aBrowser);
+          ]]>
+        </body>
+      </method>
+
+      <!--
         The tab switcher is responsible for asynchronously switching
         tabs in e10s. It waits until the new tab is ready (i.e., the
         layer tree is available) before switching to it. Then it
         unloads the layer tree for the old tab.
 
         The tab switcher is a state machine. For each tab, it
         maintains state about whether the layer tree for the tab is
         available, being loaded, being unloaded, or unavailable. It
@@ -3291,45 +3343,22 @@
 
         3. We discard layer trees on a delay. This way, if the user is
         switching among the same tabs frequently, we don't continually
         load the same tabs.
 
         It's important that we always show either the spinner or a tab
         whose layers are available. Otherwise the compositor will draw
         an entirely black frame, which is very jarring. To ensure this
-        never happens, we do the following:
-
-        1. When switching away from a tab, we assume the old tab might
-        still be drawn until a MozAfterPaint event occurs. Because
-        layout and compositing happen asynchronously, we don't have
-        any other way of knowing when the switch actually takes
-        place. Therefore, we don't unload the old tab until the next
-        MozAfterPaint event.
-
-        2. Suppose that the user switches from tab A to B and then
-        back to A. Suppose that we ask for tab A's layers to be
-        unloaded via message M1 after the first switch and then
-        request them again via message M2 once the second switch
-        happens. Both loading and unloading of layers happens
-        asynchronously, and this can cause problems. It's possible
-        that the content process publishes one last layer tree before
-        M1 is received. The parent process doesn't know that this
-        layer tree was published before M1 and not after M2, so it
-        will display the tab. However, once M1 arrives, the content
-        process will destroy the layer tree for A and now we will
-        display black for it.
-
-        To counter this problem, we keep tab A in a separate
-        "unloading" state until the layer tree is actually dropped in
-        the compositor thread. While the tab is in the "unloading"
-        state, we're not allowed to request layers for it. Once the
-        layers are dropped in the compositor, an event will fire and
-        we will transition the tab to the "unloaded" state. Then we
-        are free to request the tab's layers again.
+        never happens when switching away from a tab, we assume the
+        old tab might still be drawn until a MozAfterPaint event
+        occurs. Because layout and compositing happen asynchronously,
+        we don't have any other way of knowing when the switch
+        actually takes place. Therefore, we don't unload the old tab
+        until the next MozAfterPaint event.
       -->
       <field name="_switcher">null</field>
       <method name="_getSwitcher">
         <body><![CDATA[
           if (this._switcher) {
             return this._switcher;
           }
 
@@ -3412,60 +3441,84 @@
             getTabState: function(tab) {
               let state = this.tabState.get(tab);
               if (state === undefined) {
                 return this.STATE_UNLOADED;
               }
               return state;
             },
 
-            setTabState: function(tab, state) {
+            setTabStateNoAction(tab, state) {
               if (state == this.STATE_UNLOADED) {
                 this.tabState.delete(tab);
               } else {
                 this.tabState.set(tab, state);
               }
+            },
+
+            setTabState: function(tab, state) {
+              this.setTabStateNoAction(tab, state);
 
               let browser = tab.linkedBrowser;
-              let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+              let {tabParent} = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
               if (state == this.STATE_LOADING) {
-                // Ask for a MozLayerTreeReady event.
-                fl.requestNotifyLayerTreeReady();
+                this.assert(!this.minimized);
                 browser.docShellIsActive = true;
+                if (!tabParent) {
+                  this.onLayersReady(browser);
+		}
               } else if (state == this.STATE_UNLOADING) {
-                // Ask for MozLayerTreeCleared event.
-                fl.requestNotifyLayerTreeCleared();
                 browser.docShellIsActive = false;
+                if (!tabParent) {
+                  this.onLayersCleared(browser);
+                }
               }
             },
 
+            get minimized() {
+              return window.windowState == window.STATE_MINIMIZED;
+            },
+
             init: function() {
               this.log("START");
 
+              // If we minimized the window before the switcher was activated,
+              // we might have set  the preserveLayers flag for the current
+              // browser. Let's clear it.
+              this.tabbrowser.mCurrentBrowser.preserveLayers(false);
+
               window.addEventListener("MozAfterPaint", this);
               window.addEventListener("MozLayerTreeReady", this);
               window.addEventListener("MozLayerTreeCleared", this);
               window.addEventListener("TabRemotenessChange", this);
-              this.setTabState(this.requestedTab, this.STATE_LOADED);
+              window.addEventListener("sizemodechange", this);
+              window.addEventListener("SwapDocShells", this, true);
+              window.addEventListener("EndSwapDocShells", this, true);
+              if (!this.minimized) {
+                this.setTabState(this.requestedTab, this.STATE_LOADED);
+              }
             },
 
             destroy: function() {
               if (this.unloadTimer) {
                 this.clearTimer(this.unloadTimer);
                 this.unloadTimer = null;
               }
               if (this.loadTimer) {
                 this.clearTimer(this.loadTimer);
                 this.loadTimer = null;
               }
 
               window.removeEventListener("MozAfterPaint", this);
               window.removeEventListener("MozLayerTreeReady", this);
               window.removeEventListener("MozLayerTreeCleared", this);
               window.removeEventListener("TabRemotenessChange", this);
+              window.removeEventListener("sizemodechange", this);
+              window.removeEventListener("SwapDocShells", this, true);
+              window.removeEventListener("EndSwapDocShells", this, true);
 
               this.tabbrowser._switcher = null;
 
               this.activeSuppressDisplayport.forEach(function(tabParent) {
                 tabParent.suppressDisplayport(false);
               });
               this.activeSuppressDisplayport.clear();
             },
@@ -3474,17 +3527,17 @@
               this.log("FINISH");
 
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
               this.assert(!this.spinnerTab);
               this.assert(!this.loadTimer);
               this.assert(!this.loadingTab);
               this.assert(this.lastVisibleTab === this.requestedTab);
-              this.assert(this.getTabState(this.requestedTab) == this.STATE_LOADED);
+              this.assert(this.minimized || this.getTabState(this.requestedTab) == this.STATE_LOADED);
 
               this.destroy();
 
               let toBrowser = this.requestedTab.linkedBrowser;
               toBrowser.setAttribute("type", "content-primary");
 
               this.tabbrowser._adjustFocusAfterTabSwitch(this.requestedTab);
 
@@ -3514,17 +3567,17 @@
                 // available, show it.
                 showTab = this.lastVisibleTab;
               } else {
                 // Show the requested tab. If it's not available, we'll show the spinner.
                 showTab = this.requestedTab;
               }
 
               // Show or hide the spinner as needed.
-              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED;
+              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED && !this.minimized;
               if (!needSpinner && this.spinnerTab) {
                 this.spinnerHidden();
                 this.tabbrowser.removeAttribute("pendingpaint");
                 this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
                 this.spinnerTab = null;
               } else if (needSpinner && this.spinnerTab !== showTab) {
                 if (this.spinnerTab) {
                   this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
@@ -3569,24 +3622,25 @@
                 dump("Assertion failure\n" + Error().stack);
                 throw new Error("Assertion failure");
               }
             },
 
             // We've decided to try to load requestedTab.
             loadRequestedTab: function() {
               this.assert(!this.loadTimer);
+              this.assert(!this.minimized);
 
               // loadingTab can be non-null here if we timed out loading the current tab.
               // In that case we just overwrite it with a different tab; it's had its chance.
               this.loadingTab = this.requestedTab;
               this.log("Loading tab " + this.tinfo(this.loadingTab));
 
+              this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
               this.setTabState(this.requestedTab, this.STATE_LOADING);
-              this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
             },
 
             // This function runs before every event. It fixes up the state
             // to account for closed tabs.
             preActions: function() {
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
 
@@ -3621,23 +3675,31 @@
                           this.getTabState(this.loadingTab) == this.STATE_LOADING);
 
               // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
               // the timer is set only when we're loading something.
               this.assert(!this.loadTimer || this.loadingTab);
               this.assert(!this.loadingTab || this.loadTimer);
 
               // If we're not loading anything, try loading the requested tab.
-              if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
+              let requestedState = this.getTabState(this.requestedTab);
+              if (!this.loadTimer && !this.minimized &&
+                  (requestedState == this.STATE_UNLOADED ||
+                   requestedState == this.STATE_UNLOADING)) {
                 this.loadRequestedTab();
               }
 
               // See how many tabs still have work to do.
               let numPending = 0;
               for (let [tab, state] of this.tabState) {
+                // Skip print preview browsers since they shouldn't affect tab switching.
+                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                  continue;
+                }
+
                 if (state == this.STATE_LOADED && tab !== this.requestedTab) {
                   numPending++;
                 }
                 if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
                   numPending++;
                 }
               }
 
@@ -3662,16 +3724,20 @@
               this.logState("onUnloadTimeout");
               this.unloadTimer = null;
               this.preActions();
 
               let numPending = 0;
 
               // Unload any tabs that can be unloaded.
               for (let [tab, state] of this.tabState) {
+                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                  continue;
+                }
+
                 if (state == this.STATE_LOADED &&
                     !this.maybeVisibleTabs.has(tab) &&
                     tab !== this.lastVisibleTab &&
                     tab !== this.loadingTab &&
                     tab !== this.requestedTab)
                 {
                   this.setTabState(tab, this.STATE_UNLOADING);
                 }
@@ -3695,19 +3761,21 @@
               this.preActions();
               this.loadTimer = null;
               this.loadingTab = null;
               this.postActions();
             },
 
             // Fires when the layers become available for a tab.
             onLayersReady: function(browser) {
-              this.logState("onLayersReady");
-
               let tab = this.tabbrowser.getTabForBrowser(browser);
+              this.logState(`onLayersReady(${tab._tPos})`);
+
+              this.assert(this.getTabState(tab) == this.STATE_LOADING ||
+                          this.getTabState(tab) == this.STATE_LOADED);
               this.setTabState(tab, this.STATE_LOADED);
 
               this.maybeFinishTabSwitch();
 
               if (this.loadingTab === tab) {
                 this.clearTimer(this.loadTimer);
                 this.loadTimer = null;
                 this.loadingTab = null;
@@ -3719,29 +3787,30 @@
             // around.
             onPaint: function() {
               this.maybeVisibleTabs.clear();
               this.maybeFinishTabSwitch();
             },
 
             // Called when we're done clearing the layers for a tab.
             onLayersCleared: function(browser) {
-              this.logState("onLayersCleared");
-
               let tab = this.tabbrowser.getTabForBrowser(browser);
               if (tab) {
+                this.logState(`onLayersCleared(${tab._tPos})`);
+                this.assert(this.getTabState(tab) == this.STATE_UNLOADING ||
+                            this.getTabState(tab) == this.STATE_UNLOADED);
                 this.setTabState(tab, this.STATE_UNLOADED);
               }
             },
 
             // Called when a tab switches from remote to non-remote. In this case
             // a MozLayerTreeReady notification that we requested may never fire,
             // so we need to simulate it.
             onRemotenessChange: function(tab) {
-              this.logState("onRemotenessChange");
+              this.logState(`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`);
               if (!tab.linkedBrowser.isRemoteBrowser) {
                 if (this.getTabState(tab) == this.STATE_LOADING) {
                   this.onLayersReady(tab.linkedBrowser);
                 } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
                   this.onLayersCleared(tab.linkedBrowser);
                 }
               }
             },
@@ -3754,32 +3823,103 @@
                 // going to be removed during this tick of the event loop.
                 // This will cause us to show a tab spinner instead.
                 this.preActions();
                 this.lastVisibleTab = null;
                 this.postActions();
               }
             },
 
+            onSizeModeChange() {
+              if (this.minimized) {
+                for (let [tab, state] of this.tabState) {
+                  // Skip print preview browsers since they shouldn't affect tab switching.
+                  if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                    continue;
+                  }
+
+                  if (state == this.STATE_LOADING || state == this.STATE_LOADED) {
+                    this.setTabState(tab, this.STATE_UNLOADING);
+                  }
+                }
+                if (this.loadTimer) {
+                  this.clearTimer(this.loadTimer);
+                  this.loadTimer = null;
+                }
+                this.loadingTab = null;
+              } else {
+                // Do nothing. We'll automatically start loading the requested tab in
+                // postActions.
+              }
+            },
+
+            onSwapDocShells(ourBrowser, otherBrowser) {
+              // This event fires before the swap. ourBrowser is from
+              // our window. We save the state of otherBrowser since ourBrowser
+              // needs to take on that state at the end of the swap.
+
+              let otherTabbrowser = otherBrowser.ownerDocument.defaultView.gBrowser;
+              let otherState;
+              if (otherTabbrowser && otherTabbrowser._switcher) {
+                let otherTab = otherTabbrowser.getTabForBrowser(otherBrowser);
+                otherState = otherTabbrowser._switcher.getTabState(otherTab);
+              } else {
+                otherState = (otherBrowser.docShellIsActive
+                              ? this.STATE_LOADED
+                              : this.STATE_UNLOADED);
+              }
+
+              if (!this.swapMap) {
+                this.swapMap = new WeakMap();
+              }
+              this.swapMap.set(otherBrowser, otherState);
+            },
+
+            onEndSwapDocShells(ourBrowser, otherBrowser) {
+              // The swap has happened. We reset the loadingTab in
+              // case it has been swapped. We also set ourBrowser's state
+              // to whatever otherBrowser's state was before the swap.
+
+              if (this.loadTimer) {
+                // Clearing the load timer means that we will
+                // immediately display a spinner if ourBrowser isn't
+                // ready yet. Typically it will already be ready
+                // though. If it's not, we're probably in a new window,
+                // in which case we have no other tabs to display anyway.
+                this.clearTimer(this.loadTimer);
+                this.loadTimer = null;
+              }
+              this.loadingTab = null;
+
+              let otherState = this.swapMap.get(otherBrowser);
+              this.swapMap.delete(otherBrowser);
+
+              let ourTab = this.tabbrowser.getTabForBrowser(ourBrowser);
+              if (ourTab) {
+                this.setTabStateNoAction(ourTab, otherState);
+              }
+            },
+
+            shouldActivateDocShell(browser) {
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              let state = this.getTabState(tab);
+              return state == this.STATE_LOADING || state == this.STATE_LOADED;
+            },
+
+            activateBrowserForPrintPreview(browser) {
+	      let tab = this.tabbrowser.getTabForBrowser(browser);
+	      this.setTabState(tab, this.STATE_LOADING);
+	    },
+
             // Called when the user asks to switch to a given tab.
             requestTab: function(tab) {
               if (tab === this.requestedTab) {
                 return;
               }
 
-              // Instrumentation to figure out bug 1166351 - if the binding
-              // on the browser we're switching to has gone away, try to find out
-              // why. We should remove this and the checkBrowserBindingAlive
-              // method once bug 1166351 has been closed.
-              if (this.tabbrowser.AppConstants.E10S_TESTING_ONLY &&
-                  !this.checkBrowserBindingAlive(tab)) {
-                Cu.reportError("Please report the above errors in bug 1166351.");
-                return;
-              }
-
               this.logState("requestTab " + this.tinfo(tab));
               this.startTabSwitch();
 
               this.requestedTab = tab;
 
               let browser = this.requestedTab.linkedBrowser;
               let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
               if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
@@ -3813,16 +3953,22 @@
               if (event.type == "MozLayerTreeReady") {
                 this.onLayersReady(event.originalTarget);
               } if (event.type == "MozAfterPaint") {
                 this.onPaint();
               } else if (event.type == "MozLayerTreeCleared") {
                 this.onLayersCleared(event.originalTarget);
               } else if (event.type == "TabRemotenessChange") {
                 this.onRemotenessChange(event.target);
+              } else if (event.type == "sizemodechange") {
+                this.onSizeModeChange();
+              } else if (event.type == "SwapDocShells") {
+                this.onSwapDocShells(event.originalTarget, event.detail);
+              } else if (event.type == "EndSwapDocShells") {
+                this.onEndSwapDocShells(event.originalTarget, event.detail);
               }
 
               this.postActions();
               this._processing = false;
             },
 
             /*
              * Telemetry and Profiler related helpers for recording tab switch
@@ -3900,52 +4046,16 @@
                 result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming");
               } catch (ex) {
               }
               this._shouldLog = result;
               this._logInit = true;
               return this._shouldLog;
             },
 
-            // Instrumentation for bug 1166351
-            checkBrowserBindingAlive: function(tab) {
-              let err = Cu.reportError;
-
-              if (!tab.linkedBrowser) {
-                err("Attempting to switch to tab that has no linkedBrowser.");
-                return false;
-              }
-
-              let b = tab.linkedBrowser;
-
-              if (!b._alive) {
-                // The browser binding has been removed. Dump a bunch of
-                // diagnostic information to the browser console.
-                let utils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-                let results = utils.getBindingURLs(b);
-                let urls = [];
-
-                for (let i = 0; i < results.length; ++i) {
-                  urls.push(results.queryElementAt(i, Ci.nsIURI).spec);
-                }
-                err("The browser has the following bindings:");
-                err(urls);
-                err("MozBinding is currently: " +
-                    window.getComputedStyle(b).MozBinding);
-                if (!b.parentNode) {
-                  err("Browser was removed from the DOM.");
-                } else {
-                  err("Parent is: " + b.parentNode.outerHTML);
-                }
-                return false;
-              }
-
-              return true;
-            },
-
             tinfo: function(tab) {
               if (tab) {
                 return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
               }
               return "null";
             },
 
             log: function(s) {
@@ -4302,19 +4412,19 @@
           switch (aEvent.type) {
             case "keydown":
               this._handleKeyDownEvent(aEvent);
               break;
             case "keypress":
               this._handleKeyPressEventMac(aEvent);
               break;
             case "sizemodechange":
-              if (aEvent.target == window) {
-                this.mCurrentBrowser.setDocShellIsActiveAndForeground(
-                  window.windowState != window.STATE_MINIMIZED);
+              if (aEvent.target == window && !this._switcher) {
+                this.mCurrentBrowser.preserveLayers(window.windowState == window.STATE_MINIMIZED);
+                this.mCurrentBrowser.docShellIsActive = this.shouldActivateDocShell(this.mCurrentBrowser);
               }
               break;
           }
         ]]></body>
       </method>
 
       <method name="receiveMessage">
         <parameter name="aMessage"/>
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -20,8 +20,9 @@ support-files =
   test_firstParty_postMessage.html
   window.html
   worker_blobify.js
   worker_deblobify.js
 
 [browser_firstPartyIsolation.js]
 [browser_localStorageIsolation.js]
 [browser_blobURLIsolation.js]
+[browser_imageCacheIsolation.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_imageCacheIsolation.js
@@ -0,0 +1,78 @@
+/*
+ * Bug 1264572 - A test case for image cache isolation.
+ */
+
+let Cu = Components.utils;
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+
+const NUM_ISOLATION_LOADS = 2;
+const NUM_CACHED_LOADS = 1;
+
+let gHits = 0;
+
+let server = new HttpServer();
+server.registerPathHandler('/image.png', imageHandler);
+server.registerPathHandler('/file.html', fileHandler);
+server.start(-1);
+
+registerCleanupFunction(() => {
+  server.stop(() => {
+    server = null;
+  });
+});
+
+let BASE_URI = 'http://localhost:' + server.identity.primaryPort;
+let IMAGE_URI = BASE_URI + '/image.png';
+let FILE_URI = BASE_URI + '/file.html';
+
+function imageHandler(metadata, response) {
+  info('XXX: loading image from server');
+  gHits++;
+  response.setHeader("Cache-Control", "max-age=10000", false);
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "image/png", false);
+  var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function fileHandler(metadata, response) {
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/html", false);
+  let body = `<html><body><image src=${IMAGE_URI}></body></html>`;
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function doBefore() {
+  // reset hit counter
+  info('XXX resetting gHits');
+  gHits = 0;
+  info('XXX clearing image cache');
+  let imageCache = Cc["@mozilla.org/image/tools;1"]
+                      .getService(Ci.imgITools)
+                      .getImgCacheForDocument(null);
+  imageCache.clearCache(true);
+  imageCache.clearCache(false);
+  info('XXX clearning network cache');
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+}
+
+// the test function does nothing on purpose.
+function doTest(aBrowser) {
+  return 0;
+}
+
+// the check function
+function doCheck(shouldIsolate, a, b) {
+  // if we're doing first party isolation and the image cache isolation is
+  // working, then gHits should be 2 because the image would have been loaded
+  // one per first party domain.  if first party isolation is disabled, then
+  // gHits should be 1 since there would be one image load from the server and
+  // one load from the image cache.
+  info(`XXX check: gHits == ${gHits}, shouldIsolate == ${shouldIsolate}`);
+  return shouldIsolate ? gHits == NUM_ISOLATION_LOADS
+                       : gHits == NUM_CACHED_LOADS;
+}
+
+IsolationTestTools.runTests(FILE_URI, doTest, doCheck, doBefore);
--- a/browser/components/originattributes/test/browser/head.js
+++ b/browser/components/originattributes/test/browser/head.js
@@ -265,18 +265,21 @@ this.IsolationTestTools = {
    *    Each of these functions will be provided the browser object of the tab,
    *    that allows modifying or fetchings results from the page content.
    * @param aCompareResultFunc
    *    An optional function which allows modifying the way how does framework
    *    check results. This function will be provided a boolean to indicate
    *    the isolation is no or off and two results. This function should return
    *    a boolean to tell that whether isolation is working. If this function
    *    is not given, the framework will take case checking by itself.
+   * @param aBeforeFunc
+   *    An optional function which is called before any tabs are created so
+   *    that the test case can set up/reset local state.
    */
-  runTests(aURL, aGetResultFuncs, aCompareResultFunc) {
+  runTests(aURL, aGetResultFuncs, aCompareResultFunc, aBeforeFunc) {
     let pageURL;
     let firstFrameSetting;
     let secondFrameSetting;
 
     if (typeof aURL === "string") {
       pageURL = aURL;
     } else if (typeof aURL === "object") {
       pageURL = aURL.url;
@@ -292,16 +295,21 @@ this.IsolationTestTools = {
                         { firstPartyDomain: "http://example.com", userContextId: 1},
                         { firstPartyDomain: "http://example.org", userContextId: 2}
                       ];
 
     this._add_task(function* (aMode) {
       let tabSettingA = 0;
 
       for (let tabSettingB of [0, 1]) {
+        // Give the test a chance to set up before each case is run.
+        if (aBeforeFunc) {
+          yield aBeforeFunc();
+        }
+
         // Create Tabs.
         let tabInfoA = yield IsolationTestTools._addTab(aMode,
                                                         pageURL,
                                                         tabSettings[tabSettingA],
                                                         firstFrameSetting);
         let tabInfoB = yield IsolationTestTools._addTab(aMode,
                                                         pageURL,
                                                         tabSettings[tabSettingB],
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -1061,17 +1061,17 @@
       </xul:deck>
       <xul:description anonid="search-panel-one-offs"
                        role="group"
                        class="search-panel-one-offs"
                        xbl:inherits="compact">
         <xul:button anonid="search-settings-compact"
                     oncommand="showSettings();"
                     class="searchbar-engine-one-off-item search-setting-button-compact"
-                    aria-label="&changeSearchSettings.button;"
+                    tooltiptext="&changeSearchSettings.tooltip;"
                     xbl:inherits="compact"/>
       </xul:description>
       <xul:vbox anonid="add-engines"/>
       <xul:button anonid="search-settings"
                   oncommand="showSettings();"
                   class="search-setting-button search-panel-header"
                   label="&changeSearchSettings.button;"
                   xbl:inherits="compact"/>
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -451,16 +451,17 @@ These should match what Safari and other
      The wording of this string should be as close as possible to
      searchFor.label and searchWith.label. This string will be used instead of
      them when the user has not typed any keyword. -->
 <!ENTITY searchWithHeader.label       "Search with:">
 <!-- LOCALIZATION NOTE (changeSearchSettings.button):
      This string won't wrap, so if the translated string is longer,
      consider translating it as if it said only "Search Settings". -->
 <!ENTITY changeSearchSettings.button  "Change Search Settings">
+<!ENTITY changeSearchSettings.tooltip "Change search settings">
 
 <!ENTITY searchInNewTab.label         "Search in New Tab">
 <!ENTITY searchInNewTab.accesskey     "T">
 <!ENTITY searchSetAsDefault.label     "Set As Default Search Engine">
 <!ENTITY searchSetAsDefault.accesskey "D">
 
 <!ENTITY openLinkCmdInTab.label       "Open Link in New Tab">
 <!ENTITY openLinkCmdInTab.accesskey   "T">
--- a/build/annotationProcessors/AnnotationProcessor.java
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -10,74 +10,76 @@ import org.mozilla.gecko.annotationProce
 import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
 
 public class AnnotationProcessor {
-    public static final String SOURCE_FILE = "GeneratedJNIWrappers.cpp";
-    public static final String HEADER_FILE = "GeneratedJNIWrappers.h";
-    public static final String NATIVES_FILE = "GeneratedJNINatives.h";
-
     public static final String GENERATED_COMMENT =
             "// GENERATED CODE\n" +
             "// Generated by the Java program at /build/annotationProcessors at compile time\n" +
             "// from annotations on Java methods. To update, change the annotations on the\n" +
             "// corresponding Java methods and rerun the build. Manually updating this file\n" +
             "// will cause your build to fail.\n" +
             "\n";
 
     private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
     private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
     private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
 
     public static void main(String[] args) {
         // We expect a list of jars on the commandline. If missing, whinge about it.
-        if (args.length <= 1) {
-            System.err.println("Usage: java AnnotationProcessor jarfiles ...");
+        if (args.length <= 2) {
+            System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ...");
             System.exit(1);
         }
 
+        final String OUTPUT_PREFIX = args[0];
+        final String SOURCE_FILE = OUTPUT_PREFIX + "JNIWrappers.cpp";
+        final String HEADER_FILE = OUTPUT_PREFIX + "JNIWrappers.h";
+        final String NATIVES_FILE = OUTPUT_PREFIX + "JNINatives.h";
+
         System.out.println("Processing annotations...");
 
         // We want to produce the same output as last time as often as possible. Ordering of
         // generated statements, therefore, needs to be consistent.
-        Arrays.sort(args);
+        final String[] jars = Arrays.copyOfRange(args, 1, args.length);
+        Arrays.sort(jars);
 
         // Start the clock!
         long s = System.currentTimeMillis();
 
         // Get an iterator over the classes in the jar files given...
-        Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+        Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(jars);
 
         headerFile.append(
                 "#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
                 "#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
                 "\n" +
                 "#include \"mozilla/jni/Refs.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
                 "namespace java {\n" +
                 "\n");
 
         implementationFile.append(
-                "#include \"GeneratedJNIWrappers.h\"\n" +
+                "#include \"" + HEADER_FILE + "\"\n" +
                 "#include \"mozilla/jni/Accessors.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
                 "namespace java {\n" +
                 "\n");
 
         nativesFile.append(
                 "#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
                 "#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
                 "\n" +
-                "#include \"GeneratedJNIWrappers.h\"\n" +
+                "#include \"" + HEADER_FILE + "\"\n" +
                 "#include \"mozilla/jni/Natives.h\"\n" +
                 "\n" +
                 "namespace mozilla {\n" +
                 "namespace java {\n" +
                 "\n");
 
         while (jarClassIterator.hasNext()) {
             generateClass(jarClassIterator.next());
--- a/config/system-headers
+++ b/config/system-headers
@@ -995,16 +995,17 @@ sys/bitypes.h
 sys/byteorder.h
 syscall.h
 sys/cdefs.h
 sys/cfgodm.h
 sys/elf.h
 sys/endian.h
 sys/epoll.h
 sys/errno.h
+sys/eventfd.h
 sys/fault.h
 sys/fcntl.h
 sys/file.h
 sys/filio.h
 sys/frame.h
 sys/immu.h
 sys/inotify.h
 sys/inttypes.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6155,45 +6155,33 @@ nsDocShell::SetIsOffScreenBrowser(bool a
 NS_IMETHODIMP
 nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen)
 {
   *aIsOffScreen = mIsOffScreenBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetIsActiveAndForeground(bool aIsActive)
-{
-  return SetIsActiveInternal(aIsActive, false);
-}
-
-NS_IMETHODIMP
 nsDocShell::SetIsActive(bool aIsActive)
 {
-  return SetIsActiveInternal(aIsActive, true);
-}
-
-nsresult
-nsDocShell::SetIsActiveInternal(bool aIsActive, bool aIsHidden)
-{
   // We disallow setting active on chrome docshells.
   if (mItemType == nsIDocShellTreeItem::typeChrome) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // Keep track ourselves.
   mIsActive = aIsActive;
 
   // Clear prerender flag if necessary.
   mIsPrerendered &= !aIsActive;
 
   // Tell the PresShell about it.
   nsCOMPtr<nsIPresShell> pshell = GetPresShell();
   if (pshell) {
-    pshell->SetIsActive(aIsActive, aIsHidden);
+    pshell->SetIsActive(aIsActive);
   }
 
   // Tell the window about it
   if (mScriptGlobal) {
     mScriptGlobal->SetIsBackground(!aIsActive);
     if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
       // Update orientation when the top-level browsing context becomes active.
       // We make an exception for apps because they currently rely on
@@ -6217,21 +6205,17 @@ nsDocShell::SetIsActiveInternal(bool aIs
   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
   while (iter.HasMore()) {
     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
     if (!docshell) {
       continue;
     }
 
     if (!docshell->GetIsMozBrowserOrApp()) {
-      if (aIsHidden) {
-        docshell->SetIsActive(aIsActive);
-      } else {
-        docshell->SetIsActiveAndForeground(aIsActive);
-      }
+      docshell->SetIsActive(aIsActive);
     }
   }
 
   // Restart or stop meta refresh timers if necessary
   if (mDisableMetaRefreshWhenInactive) {
     if (mIsActive) {
       ResumeRefreshURIs();
     } else {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -498,18 +498,16 @@ protected:
 
   // overridden from nsDocLoader, this provides more information than the
   // normal OnStateChange with flags STATE_REDIRECTING
   virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
                                      nsIChannel* aNewChannel,
                                      uint32_t aRedirectFlags,
                                      uint32_t aStateFlags) override;
 
-  nsresult SetIsActiveInternal(bool aIsActive, bool aIsHidden);
-
   /**
    * Helper function that determines if channel is an HTTP POST.
    *
    * @param aChannel
    *        The channel to test
    *
    * @return True iff channel is an HTTP post.
    */
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -641,22 +641,16 @@ interface nsIDocShell : nsIDocShellTreeI
   /**
    * Sets whether a docshell is active. An active docshell is one that is
    * visible, and thus is not a good candidate for certain optimizations
    * like image frame discarding. Docshells are active unless told otherwise.
    */
   attribute boolean isActive;
 
   /**
-   * Sets whether a docshell is active, as above, but ensuring it does
-   * not discard its layers
-   */
-  void setIsActiveAndForeground(in boolean aIsActive);
-
-  /**
    * Puts the docshell in prerendering mode. noscript because we want only
    * native code to be able to put a docshell in prerendering.
    */
   [noscript] void SetIsPrerendered();
 
   /**
    * Whether this docshell is in prerender mode.
    */
--- a/dom/base/AnonymousContent.cpp
+++ b/dom/base/AnonymousContent.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AnonymousContent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/AnonymousContentBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocument.h"
 #include "nsIDOMHTMLCollection.h"
+#include "nsIFrame.h"
 #include "nsStyledElement.h"
 #include "HTMLCanvasElement.h"
 
 namespace mozilla {
 namespace dom {
 
 // Ref counting and cycle collection
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnonymousContent, AddRef)
@@ -149,29 +150,56 @@ AnonymousContent::SetAnimationForElement
   if (!element) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   return element->Animate(aContext, aKeyframes, aOptions, aRv);
 }
 
+void
+AnonymousContent::SetCutoutRectsForElement(const nsAString& aElementId,
+                                           const Sequence<OwningNonNull<DOMRect>>& aRects,
+                                           ErrorResult& aRv)
+{
+  Element* element = GetElementById(aElementId);
+
+  if (!element) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return;
+  }
+
+  nsRegion cutOutRegion;
+  for (const auto& r : aRects) {
+    CSSRect rect(r->X(), r->Y(), r->Width(), r->Height());
+    cutOutRegion.OrWith(CSSRect::ToAppUnits(rect));
+  }
+
+  element->SetProperty(nsGkAtoms::cutoutregion, new nsRegion(cutOutRegion),
+                       nsINode::DeleteProperty<nsRegion>);
+
+  nsIFrame* frame = element->GetPrimaryFrame();
+  if (frame) {
+    frame->SchedulePaint();
+  }
+}
+
 Element*
 AnonymousContent::GetElementById(const nsAString& aElementId)
 {
   // This can be made faster in the future if needed.
   nsCOMPtr<nsIAtom> elementId = NS_Atomize(aElementId);
-  for (nsIContent* kid = mContentNode->GetFirstChild(); kid;
-       kid = kid->GetNextNode(mContentNode)) {
-    if (!kid->IsElement()) {
+  for (nsIContent* node = mContentNode; node;
+       node = node->GetNextNode(mContentNode)) {
+    if (!node->IsElement()) {
       continue;
     }
-    nsIAtom* id = kid->AsElement()->GetID();
+    nsIAtom* id = node->AsElement()->GetID();
     if (id && id == elementId) {
-      return kid->AsElement();
+      return node->AsElement();
     }
   }
   return nullptr;
 }
 
 bool
 AnonymousContent::WrapObject(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto,
--- a/dom/base/AnonymousContent.h
+++ b/dom/base/AnonymousContent.h
@@ -59,16 +59,20 @@ public:
                                                  ErrorResult& aRv);
 
   already_AddRefed<Animation> SetAnimationForElement(JSContext* aContext,
                                                      const nsAString& aElementId,
                                                      JS::Handle<JSObject*> aKeyframes,
                                                      const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
                                                      ErrorResult& aError);
 
+  void SetCutoutRectsForElement(const nsAString& aElementId,
+                                const Sequence<OwningNonNull<DOMRect>>& aRects,
+                                ErrorResult& aError);
+
 private:
   ~AnonymousContent();
   nsCOMPtr<Element> mContentNode;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -716,100 +716,16 @@ Location::SetProtocol(const nsAString& a
   rv = NS_NewURI(getter_AddRefs(uri), newSpec);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return SetURI(uri);
 }
 
-void
-Location::GetUsername(nsAString& aUsername, ErrorResult& aError)
-{
-  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
-
-  aUsername.Truncate();
-  nsCOMPtr<nsIURI> uri;
-  nsresult result = GetURI(getter_AddRefs(uri));
-  if (uri) {
-    nsAutoCString username;
-    result = uri->GetUsername(username);
-    if (NS_SUCCEEDED(result)) {
-      CopyUTF8toUTF16(username, aUsername);
-    }
-  }
-}
-
-void
-Location::SetUsername(const nsAString& aUsername, ErrorResult& aError)
-{
-  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = GetWritableURI(getter_AddRefs(uri));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aError.Throw(rv);
-    return;
-  }
-
-  if (!uri) {
-    return;
-  }
-
-  rv = uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aError.Throw(rv);
-    return;
-  }
-
-  rv = SetURI(uri);
-}
-
-void
-Location::GetPassword(nsAString& aPassword, ErrorResult& aError)
-{
-  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
-
-  aPassword.Truncate();
-  nsCOMPtr<nsIURI> uri;
-  nsresult result = GetURI(getter_AddRefs(uri));
-  if (uri) {
-    nsAutoCString password;
-    result = uri->GetPassword(password);
-    if (NS_SUCCEEDED(result)) {
-      CopyUTF8toUTF16(password, aPassword);
-    }
-  }
-}
-
-void
-Location::SetPassword(const nsAString& aPassword, ErrorResult& aError)
-{
-  THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = GetWritableURI(getter_AddRefs(uri));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aError.Throw(rv);
-    return;
-  }
-
-  if (!uri) {
-    return;
-  }
-
-  rv = uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aError.Throw(rv);
-    return;
-  }
-
-  rv = SetURI(uri);
-}
-
 NS_IMETHODIMP
 Location::GetSearch(nsAString& aSearch)
 {
   aSearch.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -86,20 +86,16 @@ public:
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetProtocol(aProtocol);
   }
   void SetProtocol(const nsAString& aProtocol, ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetProtocol(aProtocol);
   }
-  void GetUsername(nsAString& aUsername, ErrorResult& aError);
-  void SetUsername(const nsAString& aUsername, ErrorResult& aError);
-  void GetPassword(nsAString& aPassword, ErrorResult& aError);
-  void SetPassword(const nsAString& aPassword, ErrorResult& aError);
   void GetHost(nsAString& aHost, ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHost(aHost);
   }
   void SetHost(const nsAString& aHost, ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1016,18 +1016,16 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   }
 
   rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
   if (NS_FAILED(rv)) {
     mInSwap = aOther->mInSwap = false;
     return rv;
   }
 
-  mRemoteBrowser->SwapLayerTreeObservers(aOther->mRemoteBrowser);
-
   nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
     aOther->mRemoteBrowser->GetBrowserDOMWindow();
   nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
     mRemoteBrowser->GetBrowserDOMWindow();
 
   if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
@@ -3249,56 +3247,16 @@ nsFrameLoader::RequestNotifyAfterRemoteP
   if (mRemoteBrowser) {
     Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsFrameLoader::RequestNotifyLayerTreeReady()
-{
-  if (mRemoteBrowser) {
-    return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
-  }
-
-  if (!mOwnerContent) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  RefPtr<AsyncEventDispatcher> event =
-    new AsyncEventDispatcher(mOwnerContent,
-                             NS_LITERAL_STRING("MozLayerTreeReady"),
-                             true, false);
-  event->PostDOMEvent();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameLoader::RequestNotifyLayerTreeCleared()
-{
-  if (mRemoteBrowser) {
-    return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
-  }
-
-  if (!mOwnerContent) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  RefPtr<AsyncEventDispatcher> event =
-    new AsyncEventDispatcher(mOwnerContent,
-                             NS_LITERAL_STRING("MozLayerTreeCleared"),
-                             true, false);
-  event->PostDOMEvent();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsFrameLoader::Print(uint64_t aOuterWindowID,
                      nsIPrintSettings* aPrintSettings,
                      nsIWebProgressListener* aProgressListener)
 {
 #if defined(NS_PRINTING)
   if (mRemoteBrowser) {
     RefPtr<embedding::PrintingParent> printingParent =
       mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -259,16 +259,17 @@ GK_ATOM(controls, "controls")
 GK_ATOM(coords, "coords")
 GK_ATOM(copy, "copy")
 GK_ATOM(copyOf, "copy-of")
 GK_ATOM(count, "count")
 GK_ATOM(crop, "crop")
 GK_ATOM(crossorigin, "crossorigin")
 GK_ATOM(curpos, "curpos")
 GK_ATOM(current, "current")
+GK_ATOM(cutoutregion, "cutoutregion")
 GK_ATOM(cycler, "cycler")
 GK_ATOM(data, "data")
 GK_ATOM(datalist, "datalist")
 GK_ATOM(dataType, "data-type")
 GK_ATOM(dateTime, "date-time")
 GK_ATOM(datasources, "datasources")
 GK_ATOM(datetime, "datetime")
 GK_ATOM(dblclick, "dblclick")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11149,24 +11149,24 @@ nsGlobalWindow::ShowSlowScriptDialog()
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
 
   // Add a third button if necessary.
   if (showDebugButton)
     buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
 
   // Null out the operation callback while we're re-entering JS here.
-  JSInterruptCallback old = JS_SetInterruptCallback(cx, nullptr);
+  bool old = JS_DisableInterruptCallback(cx);
 
   // Open the dialog.
   rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
                          debugButton, neverShowDlg, &neverShowDlgChk,
                          &buttonPressed);
 
-  JS_SetInterruptCallback(cx, old);
+  JS_ResetInterruptCallback(cx, old);
 
   if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
     return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
   }
   if (buttonPressed == 2) {
     if (debugCallback) {
       rv = debugCallback->HandleSlowScriptDebug(this);
       return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -134,24 +134,16 @@ interface nsIFrameLoader : nsISupports
   /**
    * Request that the next time a remote layer transaction has been
    * received by the Compositor, a MozAfterRemoteFrame event be sent
    * to the window.
    */
   void requestNotifyAfterRemotePaint();
 
   /**
-   * Request an event when the layer tree from the remote tab becomes
-   * available or unavailable. When this happens, a mozLayerTreeReady
-   * or mozLayerTreeCleared event is fired.
-   */
-  void requestNotifyLayerTreeReady();
-  void requestNotifyLayerTreeCleared();
-
-  /**
    * Print the current document.
    *
    * @param aOuterWindowID the ID of the outer window to print
    * @param aPrintSettings optional print settings to use; printSilent can be
    *                       set to prevent prompting.
    * @param aProgressListener optional print progress listener.
    */
   void print(in unsigned long long aOuterWindowID,
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -50,17 +50,17 @@ void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
 {
   if (PreservingWrapper()) {
     // PreserveWrapper puts new DOM bindings in the JS holders hash, but they
     // can also be in the DOM expando hash, so we need to try to remove them
     // from both here.
     JSObject* obj = GetWrapperPreserveColor();
     if (IsDOMBinding() && obj && js::IsProxy(obj)) {
-      DOMProxyHandler::GetAndClearExpandoObject(obj);
+      DOMProxyHandler::ClearExternalRefsForWrapperRelease(obj);
     }
     SetPreservingWrapper(false);
     cyclecollector::DropJSObjectsImpl(aScriptObjectHolder);
   }
 }
 
 #ifdef DEBUG
 
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -66,16 +66,43 @@ struct SetDOMProxyInformation
     js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
                                JSPROXYSLOT_EXPANDO, DOMProxyShadows);
   }
 };
 
 SetDOMProxyInformation gSetDOMProxyInformation;
 
 // static
+void
+DOMProxyHandler::ClearExternalRefsForWrapperRelease(JSObject* obj)
+{
+  MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+  JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+  if (v.isUndefined()) {
+    // No expando.
+    return;
+  }
+
+  // See EnsureExpandoObject for the work we're trying to undo here.
+
+  if (v.isObject()) {
+    // Drop us from the DOM expando hashtable.  Don't worry about clearing our
+    // slot reference to the expando; we're about to die anyway.
+    xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
+    return;
+  }
+
+  // Prevent having a dangling pointer to our expando from the
+  // ExpandoAndGeneration.
+  js::ExpandoAndGeneration* expandoAndGeneration =
+    static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+  expandoAndGeneration->expando = UndefinedValue();
+}
+
+// static
 JSObject*
 DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
 {
   MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
   JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
   if (v.isUndefined()) {
     return nullptr;
   }
@@ -85,16 +112,29 @@ DOMProxyHandler::GetAndClearExpandoObjec
     xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
   } else {
     js::ExpandoAndGeneration* expandoAndGeneration =
       static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
     v = expandoAndGeneration->expando;
     if (v.isUndefined()) {
       return nullptr;
     }
+    // We have to expose v to active JS here.  The reason for that is that we
+    // might be in the middle of a GC right now.  If our proxy hasn't been
+    // traced yet, when it _does_ get traced it won't trace the expando, since
+    // we're breaking that link.  But the Rooted we're presumably being placed
+    // into is also not going to trace us, because Rooted marking is done at
+    // the very beginning of the GC.  In that situation, we need to manually
+    // mark the expando as live here.  JS::ExposeValueToActiveJS will do just
+    // that for us.
+    //
+    // We don't need to do this in the non-expandoAndGeneration case, because
+    // in that case our value is stored in a slot and slots will already mark
+    // the old thing live when the value in the slot changes.
+    JS::ExposeValueToActiveJS(v);
     expandoAndGeneration->expando = UndefinedValue();
   }
 
 
   return &v.toObject();
 }
 
 // static
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -127,20 +127,45 @@ public:
   /*
    * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
    * an indexed setter, call it and set *done to true on success. Otherwise, set
    * *done to false.
    */
   virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                          JS::Handle<JS::Value> v, bool *done) const;
 
+  /*
+   * Get the expando object for the given DOM proxy.
+   */
   static JSObject* GetExpandoObject(JSObject* obj);
 
-  /* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */
+  /*
+   * Clear the "external references" to this object.  If you are not
+   * nsWrapperCAche::ReleaseWrapper, you do NOT want to be calling this method.
+   *
+   * XXXbz if we nixed the DOM expando hash and just had a finalizer that
+   * cleared out the value in the ExpandoAndGeneration in the shadowing case,
+   * could we just get rid of this function altogether?
+   */
+  static void ClearExternalRefsForWrapperRelease(JSObject* obj);
+
+  /*
+   * Clear the expando object for the given DOM proxy and return it.  This
+   * function will ensure that the returned object is exposed to active JS if
+   * the given object is exposed.
+   *
+   * GetAndClearExpandoObject does not DROP or clear the preserving wrapper
+   * flag.
+   */
   static JSObject* GetAndClearExpandoObject(JSObject* obj);
+
+  /*
+   * Ensure that the given proxy (obj) has an expando object, and return it.
+   * Returns null on failure.
+   */
   static JSObject* EnsureExpandoObject(JSContext* cx,
                                        JS::Handle<JSObject*> obj);
 
   static const char family;
 };
 
 // Class used by shadowing handlers (the ones that have [OverrideBuiltins].
 // This handles tracing the expando in ExpandoAndGeneration.
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -16,50 +16,58 @@ namespace {
 
 // This class is used in the DTOR of GetFilesHelper to release resources in the
 // correct thread.
 class ReleaseRunnable final : public Runnable
 {
 public:
   static void
   MaybeReleaseOnMainThread(nsTArray<RefPtr<Promise>>& aPromises,
-                           nsTArray<RefPtr<GetFilesCallback>>& aCallbacks)
+                           nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
+                           Sequence<RefPtr<File>>& aFiles,
+                           already_AddRefed<nsIGlobalObject> aGlobal)
   {
     if (NS_IsMainThread()) {
       return;
     }
 
-    if (!aPromises.IsEmpty() || !aCallbacks.IsEmpty()) {
-      RefPtr<ReleaseRunnable> runnable =
-        new ReleaseRunnable(aPromises, aCallbacks);
-      NS_DispatchToMainThread(runnable);
-    }
+    RefPtr<ReleaseRunnable> runnable =
+      new ReleaseRunnable(aPromises, aCallbacks, aFiles, Move(aGlobal));
+    NS_DispatchToMainThread(runnable);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mPromises.Clear();
     mCallbacks.Clear();
+    mFiles.Clear();
+    mGlobal = nullptr;
 
     return NS_OK;
   }
 
 private:
   ReleaseRunnable(nsTArray<RefPtr<Promise>>& aPromises,
-                  nsTArray<RefPtr<GetFilesCallback>>& aCallbacks)
+                  nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
+                  Sequence<RefPtr<File>>& aFiles,
+                  already_AddRefed<nsIGlobalObject> aGlobal)
   {
     mPromises.SwapElements(aPromises);
     mCallbacks.SwapElements(aCallbacks);
+    mFiles.SwapElements(aFiles);
+    mGlobal = aGlobal;
   }
 
   nsTArray<RefPtr<Promise>> mPromises;
   nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
+  Sequence<RefPtr<File>> mFiles;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
 };
 
 } // anonymous
 
 ///////////////////////////////////////////////////////////////////////////////
 // GetFilesHelper Base class
 
 already_AddRefed<GetFilesHelper>
@@ -126,17 +134,18 @@ GetFilesHelper::GetFilesHelper(nsIGlobal
   , mErrorResult(NS_OK)
   , mMutex("GetFilesHelper::mMutex")
   , mCanceled(false)
 {
 }
 
 GetFilesHelper::~GetFilesHelper()
 {
-  ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks);
+  ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks, mFiles,
+                                            mGlobal.forget());
 }
 
 void
 GetFilesHelper::AddPromise(Promise* aPromise)
 {
   MOZ_ASSERT(aPromise);
 
   // Still working.
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -977,24 +977,17 @@ HTMLCanvasElement::InvalidateCanvasConte
     return;
 
   ActiveLayerTracker::NotifyContentChange(frame);
 
   Layer* layer = nullptr;
   if (damageRect) {
     nsIntSize size = GetWidthHeight();
     if (size.width != 0 && size.height != 0) {
-
-      gfx::Rect realRect(*damageRect);
-      realRect.RoundOut();
-
-      // then make it a nsIntRect
-      nsIntRect invalRect(realRect.X(), realRect.Y(),
-                          realRect.Width(), realRect.Height());
-
+      gfx::IntRect invalRect = gfx::IntRect::Truncate(*damageRect);
       layer = frame->InvalidateLayer(nsDisplayItem::TYPE_CANVAS, &invalRect);
     }
   } else {
     layer = frame->InvalidateLayer(nsDisplayItem::TYPE_CANVAS);
   }
   if (layer) {
     static_cast<CanvasLayer*>(layer)->Updated();
   }
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -20,23 +20,21 @@ interface nsITabParent : nsISupports
   /**
    * Whether this tabParent is in prerender mode.
    */
   [infallible] readonly attribute boolean isPrerendered;
 
   /**
     * As an optimisation, setting the docshell's active state to
     * inactive also triggers a layer invalidation to free up some
-    * potentially unhelpful memory usage. This attribute should be
-    * used where callers would like to set the docshell's state
-    * without losing any layer data.
-    *
-    * Otherwise, this does the same as setting the attribute above.
+    * potentially unhelpful memory usage. Calling preserveLayers
+    * will cause the layers to be preserved even for inactive
+    * docshells.
     */
-  void setDocShellIsActiveAndForeground(in boolean aIsActive);
+  void preserveLayers(in boolean aPreserveLayers);
 
   /**
    * During interactions where painting performance
    * is more important than scrolling, we may temporarily
    * suppress the displayport. Each enable called must be matched
    * with a disable call.
    */
   void suppressDisplayport(in bool aEnabled);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5399,8 +5399,17 @@ ContentParent::SendGetFilesResponseAndFo
                                              const GetFilesResponseResult& aResult)
 {
   GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
   if (helper) {
     mGetFilesPendingRequests.Remove(aUUID);
     Unused << SendGetFilesResponse(aUUID, aResult);
   }
 }
+
+void
+ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
+{
+  if (!mHangMonitorActor) {
+    return;
+  }
+  ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
+}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -558,16 +558,19 @@ public:
                                            PBlobParent* aBlobParent,
                                            const Principal& aPrincipal) override;
 
   virtual bool
   RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) 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;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -547,16 +547,21 @@ parent:
      * compositing.  This is sent when all pending changes have been
      * sent to the compositor and are ready to be shown on the next composite.
      * @see PCompositor
      * @see RequestNotifyAfterRemotePaint
      */
     async RemotePaintIsReady();
 
     /**
+     * Child informs the parent that the layer tree is already available.
+     */
+    async ForcePaintNoOp(uint64_t aLayerObserverEpoch);
+
+    /**
      * Sent by the child to the parent to inform it that an update to the
      * dimensions has been requested, likely through win.moveTo or resizeTo
      */
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
     prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
     prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
     prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
@@ -719,18 +724,26 @@ child:
     /**
      * 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
+     *        The layer observer epoch for this activation. This message should be
+     *        ignored if this epoch has already been observed (via ForcePaint).
      */
-    async SetDocShellIsActive(bool aIsActive, bool aIsHidden);
+    async SetDocShellIsActive(bool aIsActive, bool aPreserveLayers, uint64_t aLayerObserverEpoch);
 
     /**
      * Notify the child that it shouldn't paint the offscreen displayport.
      * This is useful to speed up interactive operations over async
      * scrolling performance like resize, tabswitch, pageload.
      *
      * Each enable call must be matched with a disable call. The child
      * will remain in the suppress mode as long as there's
@@ -819,16 +832,24 @@ child:
     /**
      * Tell the child to print the current page with the given settings.
      *
      * @param aOuterWindowID the ID of the outer window to print
      * @param aPrintData the serialized settings to print with
      */
     async Print(uint64_t aOuterWindowID, PrintData aPrintData);
 
+    /**
+     * Update the child with the tab's current top-level native window handle.
+     * This is used by a11y objects who must expose their native window.
+     *
+     * @param aNewHandle The native window handle of the tab's top-level window.
+     */
+    async UpdateNativeWindowHandle(uintptr_t aNewHandle);
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/PProcessHangMonitor.ipdl
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -35,11 +35,13 @@ parent:
   async HangEvidence(HangData data);
   async ClearHang();
 
 child:
   async TerminateScript();
 
   async BeginStartingDebugger();
   async EndStartingDebugger();
+
+  async ForcePaint(TabId tabId, uint64_t aLayerObserverEpoch);
 };
 
 } // namespace mozilla
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -2,19 +2,23 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 
+#include "jsapi.h"
+#include "js/GCAPI.h"
+
 #include "mozilla/Atomics.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 
 #include "nsIFrameLoader.h"
@@ -91,18 +95,21 @@ class HangMonitorChild
 
   void ClearHang();
   void ClearHangAsync();
 
   virtual bool RecvTerminateScript() override;
   virtual bool RecvBeginStartingDebugger() override;
   virtual bool RecvEndStartingDebugger() override;
 
+  virtual bool RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override;
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
+  void InterruptCallback();
   void Shutdown();
 
   static HangMonitorChild* Get() { return sInstance; }
 
   MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
 
  private:
   void ShutdownOnThread();
@@ -114,16 +121,20 @@ class HangMonitorChild
 
   // Main thread-only.
   bool mSentReport;
 
   // These fields must be accessed with mMonitor held.
   bool mTerminateScript;
   bool mStartDebugger;
   bool mFinishedStartingDebugger;
+  bool mForcePaint;
+  TabId mForcePaintTab;
+  uint64_t mForcePaintEpoch;
+  JSContext* mContext;
   bool mShutdownDone;
 
   // This field is only accessed on the hang thread.
   bool mIPCOpen;
 };
 
 Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
 
@@ -203,32 +214,37 @@ public:
   virtual bool RecvClearHang() override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
 
   void Shutdown();
 
+  void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch);
+
   void TerminateScript();
   void BeginStartingDebugger();
   void EndStartingDebugger();
   void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
 
   /**
    * Update the dump for the specified plugin. This method is thread-safe and
    * is used to replace a browser minidump with a full minidump. If aDumpId is
    * empty this is a no-op.
    */
   void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
 
   MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
 
 private:
   bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
+
+  void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
+
   void ShutdownOnThread();
 
   const RefPtr<ProcessHangMonitor> mHangMonitor;
 
   // This field is read-only after construction.
   bool mReportHangs;
 
   // This field is only accessed on the hang thread.
@@ -250,30 +266,59 @@ private:
 
 HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
  : mHangMonitor(aMonitor),
    mMonitor("HangMonitorChild lock"),
    mSentReport(false),
    mTerminateScript(false),
    mStartDebugger(false),
    mFinishedStartingDebugger(false),
+   mForcePaint(false),
    mShutdownDone(false),
    mIPCOpen(true)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  mContext = danger::GetJSContext();
 }
 
 HangMonitorChild::~HangMonitorChild()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance == this);
   sInstance = nullptr;
 }
 
 void
+HangMonitorChild::InterruptCallback()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  bool forcePaint;
+  TabId forcePaintTab;
+  uint64_t forcePaintEpoch;
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    forcePaint = mForcePaint;
+    forcePaintTab = mForcePaintTab;
+    forcePaintEpoch = mForcePaintEpoch;
+
+    mForcePaint = false;
+  }
+
+  if (forcePaint) {
+    RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
+    if (tabChild) {
+      JS::AutoAssertOnGC aaogc(mContext);
+      tabChild->ForcePaint(forcePaintEpoch);
+    }
+  }
+}
+
+void
 HangMonitorChild::Shutdown()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   MonitorAutoLock lock(mMonitor);
   while (!mShutdownDone) {
     mMonitor.Wait();
   }
@@ -326,16 +371,33 @@ HangMonitorChild::RecvEndStartingDebugge
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   MonitorAutoLock lock(mMonitor);
   mFinishedStartingDebugger = true;
   return true;
 }
 
+bool
+HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch)
+{
+  MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    mForcePaint = true;
+    mForcePaintTab = aTabId;
+    mForcePaintEpoch = aLayerObserverEpoch;
+  }
+
+  JS_RequestInterruptCallback(mContext);
+
+  return true;
+}
+
 void
 HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
                        MessageLoop* aIOLoop)
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   MOZ_ASSERT(!sInstance);
   sInstance = this;
@@ -520,16 +582,35 @@ HangMonitorParent::ShutdownOnThread()
   }
 
   MonitorAutoLock lock(mMonitor);
   mShutdownDone = true;
   mMonitor.Notify();
 }
 
 void
+HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  TabId id = aTab->GetTabId();
+  MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>(
+                            this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch));
+}
+
+void
+HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
+{
+  MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+  if (mIPCOpen) {
+    Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
+  }
+}
+
+void
 HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
   mIPCOpen = false;
 }
 
 void
 HangMonitorParent::Open(Transport* aTransport, ProcessId aPid,
@@ -954,16 +1035,26 @@ HangMonitoredProcess::UserCanceled()
 
   if (mActor) {
     uint32_t id = mHangData.get_PluginHangData().pluginId();
     mActor->CleanupPluginHang(id, true);
   }
   return NS_OK;
 }
 
+static bool
+InterruptCallback(JSContext* cx)
+{
+  if (HangMonitorChild* child = HangMonitorChild::Get()) {
+    child->InterruptCallback();
+  }
+
+  return true;
+}
+
 ProcessHangMonitor* ProcessHangMonitor::sInstance;
 
 ProcessHangMonitor::ProcessHangMonitor()
  : mCPOWTimeout(false)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   MOZ_COUNT_CTOR(ProcessHangMonitor);
@@ -1089,16 +1180,19 @@ mozilla::CreateHangMonitorParent(Content
 }
 
 PProcessHangMonitorChild*
 mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherPid)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
+  JSContext* cx = danger::GetJSContext();
+  JS_AddInterruptCallback(cx, InterruptCallback);
+
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   HangMonitorChild* child = new HangMonitorChild(monitor);
 
   monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
                                    <mozilla::ipc::Transport*,
                                     base::ProcessId,
                                     MessageLoop*>(child,
                                                   &HangMonitorChild::Open,
@@ -1137,8 +1231,18 @@ ProcessHangMonitor::RemoveProcess(PProce
 /* static */ void
 ProcessHangMonitor::ClearHang()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (HangMonitorChild* child = HangMonitorChild::Get()) {
     child->ClearHang();
   }
 }
+
+/* static */ void
+ProcessHangMonitor::ForcePaint(PProcessHangMonitorParent* aParent,
+                               dom::TabParent* aTabParent,
+                               uint64_t aLayerObserverEpoch)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  auto parent = static_cast<HangMonitorParent*>(aParent);
+  parent->ForcePaint(aTabParent, aLayerObserverEpoch);
+}
--- a/dom/ipc/ProcessHangMonitor.h
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -17,16 +17,17 @@ class MessageLoop;
 namespace base {
 class Thread;
 } // namespace base
 
 namespace mozilla {
 
 namespace dom {
 class ContentParent;
+class TabParent;
 } // namespace dom
 
 class PProcessHangMonitorParent;
 
 class ProcessHangMonitor final
   : public nsIObserver
 {
  private:
@@ -40,16 +41,20 @@ class ProcessHangMonitor final
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   static void AddProcess(dom::ContentParent* aContentParent);
   static void RemoveProcess(PProcessHangMonitorParent* aParent);
 
   static void ClearHang();
 
+  static void ForcePaint(PProcessHangMonitorParent* aParent,
+                         dom::TabParent* aTab,
+                         uint64_t aLayerObserverEpoch);
+
   enum SlowScriptAction {
     Continue,
     Terminate,
     StartDebugger
   };
   SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
                                     const char* aFileName,
                                     unsigned aLineNo);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -42,16 +42,17 @@
 #include "mozilla/Move.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Unused.h"
 #include "mozIApplication.h"
 #include "nsContentUtils.h"
+#include "nsCSSFrameConstructor.h"
 #include "nsDocShell.h"
 #include "nsEmbedCID.h"
 #include "nsGlobalWindow.h"
 #include <algorithm>
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 #include "nsFilePickerProxy.h"
@@ -79,16 +80,17 @@
 #include "nsIWebBrowserSetup.h"
 #include "nsIWebProgress.h"
 #include "nsIXULRuntime.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsLayoutUtils.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
+#include "nsViewManager.h"
 #include "nsWeakReference.h"
 #include "nsWindowWatcher.h"
 #include "PermissionMessageUtils.h"
 #include "PuppetWidget.h"
 #include "StructuredCloneData.h"
 #include "nsViewportInfo.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
@@ -541,16 +543,20 @@ TabChild::TabChild(nsIContentChild* aMan
   , mDPI(0)
   , mDefaultScale(0)
   , mIsTransparent(false)
   , mIPCOpen(true)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
   , mAPZChild(nullptr)
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  , mNativeWindowHandle(0)
+#endif
+  , mLayerObserverEpoch(0)
 {
   // 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
   // TabChild corresponds to a widget type that would have APZ enabled, and just
   // check the other conditions necessary for enabling APZ.
   mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
 
@@ -2527,16 +2533,27 @@ TabChild::RecvPrint(const uint64_t& aOut
     return true;
   }
 
 #endif
   return true;
 }
 
 bool
+TabChild::RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle)
+{
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  mNativeWindowHandle = aNewHandle;
+  return true;
+#else
+  return false;
+#endif
+}
+
+bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
   nsTArray<PContentPermissionRequestChild*> childArray =
       nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
 
@@ -2603,29 +2620,95 @@ TabChild::RecvSetUpdateHitRegion(const b
     RefPtr<nsPresContext> presContext = presShell->GetPresContext();
     NS_ENSURE_TRUE(presContext, true);
     presContext->InvalidatePaintedLayers();
 
     return true;
 }
 
 bool
-TabChild::RecvSetDocShellIsActive(const bool& aIsActive, const bool& aIsHidden)
+TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
+                                  const bool& aPreserveLayers,
+                                  const uint64_t& aLayerObserverEpoch)
 {
-    // docshell is consider prerendered only if not active yet
-    mIsPrerendered &= !aIsActive;
-    nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
-    if (docShell) {
-      if (aIsHidden) {
-        docShell->SetIsActive(aIsActive);
+  // 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.
+  if (mLayerObserverEpoch >= aLayerObserverEpoch) {
+    return true;
+  }
+  mLayerObserverEpoch = aLayerObserverEpoch;
+
+  MOZ_ASSERT(mPuppetWidget);
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
+             LayersBackend::LAYERS_CLIENT);
+
+  // We send the current layer observer epoch to the compositor so that
+  // TabParent knows whether a layer update notification corresponds to the
+  // latest SetDocShellIsActive request that was made.
+  ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
+  manager->SetLayerObserverEpoch(aLayerObserverEpoch);
+
+  // docshell is consider prerendered only if not active yet
+  mIsPrerendered &= !aIsActive;
+  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+  if (docShell) {
+    bool wasActive;
+    docShell->GetIsActive(&wasActive);
+    if (aIsActive && wasActive) {
+      // This request is a no-op. In this case, we still want a MozLayerTreeReady
+      // notification to fire in the parent (so that it knows that the child has
+      // updated its epoch). ForcePaintNoOp does that.
+      if (IPCOpen()) {
+        Unused << SendForcePaintNoOp(aLayerObserverEpoch);
+      }
+      return true;
+    }
+
+    docShell->SetIsActive(aIsActive);
+  }
+
+  if (aIsActive) {
+    MakeVisible();
+
+    // We don't use TabChildBase::GetPresShell() here because that would create
+    // a content viewer if one doesn't exist yet. Creating a content viewer can
+    // cause JS to run, which we want to avoid. nsIDocShell::GetPresShell
+    // returns null if no content viewer exists yet.
+    if (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) {
+      if (nsIFrame* root = presShell->FrameConstructor()->GetRootFrame()) {
+        FrameLayerBuilder::InvalidateAllLayersForFrame(
+          nsLayoutUtils::GetDisplayRootFrame(root));
+        root->SchedulePaint();
+      }
+
+      // If we need to repaint, let's do that right away. No sense waiting until
+      // we get back to the event loop again. We suppress the display port so that
+      // we only paint what's visible. This ensures that the tab we're switching
+      // to paints as quickly as possible.
+      APZCCallbackHelper::SuppressDisplayport(true, presShell);
+      if (nsContentUtils::IsSafeToRunScript()) {
+        WebWidget()->PaintNowIfNeeded();
       } else {
-        docShell->SetIsActiveAndForeground(aIsActive);
+        RefPtr<nsViewManager> vm = presShell->GetViewManager();
+        if (nsView* view = vm->GetRootView()) {
+          presShell->Paint(view, view->GetBounds(),
+                           nsIPresShell::PAINT_LAYERS |
+                           nsIPresShell::PAINT_SYNC_DECODE_IMAGES);
+        }
       }
+      APZCCallbackHelper::SuppressDisplayport(false, presShell);
     }
-    return true;
+  } else if (!aPreserveLayers) {
+    MakeHidden();
+  }
+
+  return true;
 }
 
 bool
 TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation)
 {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm) {
     nsCOMPtr<nsIDOMElement> result;
@@ -2838,24 +2921,32 @@ TabChild::NotifyPainted()
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
 void
 TabChild::MakeVisible()
 {
+  if (mPuppetWidget && mPuppetWidget->IsVisible()) {
+    return;
+  }
+
   if (mPuppetWidget) {
     mPuppetWidget->Show(true);
   }
 }
 
 void
 TabChild::MakeHidden()
 {
+  if (mPuppetWidget && !mPuppetWidget->IsVisible()) {
+    return;
+  }
+
   CompositorBridgeChild* compositor = CompositorBridgeChild::Get();
 
   // Clear cached resources directly. This avoids one extra IPC
   // round-trip from CompositorBridgeChild to CompositorBridgeParent.
   compositor->RecvClearCachedResources(mLayersId);
 
   if (mPuppetWidget) {
     mPuppetWidget->Show(false);
@@ -3204,16 +3295,23 @@ TabChild::GetInnerSize()
 ScreenIntRect
 TabChild::GetOuterRect()
 {
   LayoutDeviceIntRect outerRect =
     RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale());
   return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
 }
 
+void
+TabChild::ForcePaint(uint64_t aLayerObserverEpoch)
+{
+  nsAutoScriptBlocker scriptBlocker;
+  RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
+}
+
 TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
 : mTabChild(aTabChild)
 {
   SetIsNotDOMBinding();
 }
 
 TabChildGlobal::~TabChildGlobal()
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -585,16 +585,18 @@ public:
 
   virtual bool RecvHandledWindowedPluginKeyEvent(
                  const mozilla::NativeEventData& aKeyEventData,
                  const bool& aIsConsumed) override;
 
   virtual bool RecvPrint(const uint64_t& aOuterWindowID,
                          const PrintData& aPrintData) override;
 
+  virtual bool RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) override;
+
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   PPluginWidgetChild* AllocPPluginWidgetChild() override;
 
   bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
 
   nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
@@ -643,29 +645,37 @@ public:
                   const CSSRect& aRect,
                   const uint32_t& aFlags);
 
   void SetAPZChild(layers::APZChild* aAPZChild)
   {
       mAPZChild = aAPZChild;
   }
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
+#endif
+
+  // Request that the docshell be marked as active.
+  void ForcePaint(uint64_t aLayerObserverEpoch);
+
 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) override;
+                                       const bool& aIsHidden,
+                                       const uint64_t& aLayerObserverEpoch) override;
 
   virtual bool RecvNavigateByKey(const bool& aForward,
                                  const bool& aForDocumentNavigation) override;
 
   virtual bool RecvRequestNotifyAfterRemotePaint() override;
 
   virtual bool RecvSuppressDisplayport(const bool& aEnabled) override;
 
@@ -777,15 +787,23 @@ private:
 
   AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
 
   RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
   // APZChild clears this pointer from its destructor, so it shouldn't be a
   // dangling pointer.
   layers::APZChild* mAPZChild;
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  // The handle associated with the native window that contains this tab
+  uintptr_t mNativeWindowHandle;
+#endif // defined(XP_WIN)
+
+  // The most recently seen layer observer epoch in RecvSetDocShellIsActive.
+  uint64_t mLayerObserverEpoch;
+
   DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -287,23 +287,25 @@ TabParent::TabParent(nsIContentParent* a
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
   , mDragAreaX(0)
   , mDragAreaY(0)
   , mInitedByParent(false)
   , mTabId(aTabId)
   , mCreatingWindow(false)
-  , mNeedLayerTreeReadyNotification(false)
   , mCursor(nsCursor(-1))
   , mTabSetsCursor(false)
   , mHasContentOpener(false)
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
 #endif
+  , mLayerTreeEpoch(0)
+  , mPreserveLayers(false)
+  , mFirstActivate(true)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
 
@@ -393,16 +395,27 @@ TabParent::SetOwnerElement(Element* aEle
   }
 
   if (mFrameElement) {
     bool useGlobalHistory =
       !mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disableglobalhistory);
     Unused << SendSetUseGlobalHistory(useGlobalHistory);
   }
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  if (!mIsDestroyed) {
+    uintptr_t newWindowHandle = 0;
+    if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+      newWindowHandle =
+        reinterpret_cast<uintptr_t>(widget->GetNativeData(NS_NATIVE_WINDOW));
+    }
+    Unused << SendUpdateNativeWindowHandle(newWindowHandle);
+  }
+#endif
+
   AddWindowListeners();
   TryCacheDPIAndScale();
 }
 
 void
 TabParent::AddWindowListeners()
 {
   if (mFrameElement && mFrameElement->OwnerDoc()) {
@@ -488,23 +501,16 @@ TabParent::DestroyInternal()
   // If this fails, it's most likely due to a content-process crash,
   // and auto-cleanup will kick in.  Otherwise, the child side will
   // destroy itself and send back __delete__().
   Unused << SendDestroy();
 
   if (RenderFrameParent* frame = GetRenderFrame()) {
     RemoveTabParentFromTable(frame->GetLayersId());
     frame->Destroy();
-
-    // Notify our layer tree update observer that we're going away. It's
-    // possible that we race with a notification and there can be an
-    // LayerTreeUpdateRunnable on the main thread's event queue with a pointer
-    // to us. However, our actual destruction won't be until yet another event
-    // *after* that one is processed, so this should be safe.
-    mLayerUpdateObserver->TabParentDestroyed();
   }
 
   // Let all PluginWidgets know we are tearing down. Prevents
   // these objects from sending async events after the child side
   // is shut down.
   const ManagedContainer<PPluginWidgetParent>& kids =
     ManagedPPluginWidgetParent();
   for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) {
@@ -2296,22 +2302,20 @@ TabParent::GetTabIdFrom(nsIDocShell *doc
     return static_cast<TabChild*>(tabChild.get())->GetTabId();
   }
   return TabId(0);
 }
 
 RenderFrameParent*
 TabParent::GetRenderFrame()
 {
-  if (!mLayerUpdateObserver) {
-    mLayerUpdateObserver = new LayerTreeUpdateObserver(this);
-  }
-
   PRenderFrameParent* p = LoneManagedOrNullAsserts(ManagedPRenderFrameParent());
-  return static_cast<RenderFrameParent*>(p);
+  RenderFrameParent* frame = static_cast<RenderFrameParent*>(p);
+
+  return frame;
 }
 
 bool
 TabParent::RecvRequestIMEToCommitComposition(const bool& aCancel,
                                              bool* aIsCommitted,
                                              nsString* aCommittedString)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
@@ -2652,21 +2656,16 @@ TabParent::SetRenderFrame(PRenderFramePa
   bool success = renderFrame->Init(frameLoader);
   if (!success) {
     return false;
   }
 
   uint64_t layersId = renderFrame->GetLayersId();
   AddTabParentToTable(layersId, this);
 
-  if (mNeedLayerTreeReadyNotification) {
-    RequestNotifyLayerTreeReady();
-    mNeedLayerTreeReadyNotification = false;
-  }
-
   return true;
 }
 
 bool
 TabParent::GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier,
                               uint64_t* aLayersId)
 {
   RenderFrameParent* rfp = GetRenderFrame();
@@ -2839,20 +2838,36 @@ TabParent::GetUseAsyncPanZoom(bool* useA
   *useAsyncPanZoom = AsyncPanZoomEnabled();
   return NS_OK;
 }
 
 // defined in nsITabParent
 NS_IMETHODIMP
 TabParent::SetDocShellIsActive(bool isActive)
 {
+  // Increment the epoch so that layer tree updates from previous
+  // SetDocShellIsActive requests are ignored.
+  mLayerTreeEpoch++;
+
   // docshell is consider prerendered only if not active yet
   mIsPrerendered &= !isActive;
   mDocShellIsActive = isActive;
-  Unused << SendSetDocShellIsActive(isActive, true);
+  Unused << SendSetDocShellIsActive(isActive, mPreserveLayers, mLayerTreeEpoch);
+
+  // Ask the child to repaint using the PHangMonitor channel/thread (which may
+  // be less congested).
+  if (isActive) {
+    if (mFirstActivate) {
+      mFirstActivate = false;
+    } else {
+      ContentParent* cp = Manager()->AsContentParent();
+      cp->ForceTabPaint(this, mLayerTreeEpoch);
+    }
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::GetDocShellIsActive(bool* aIsActive)
 {
   *aIsActive = mDocShellIsActive;
   return NS_OK;
@@ -2861,22 +2876,19 @@ TabParent::GetDocShellIsActive(bool* aIs
 NS_IMETHODIMP
 TabParent::GetIsPrerendered(bool* aIsPrerendered)
 {
   *aIsPrerendered = mIsPrerendered;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TabParent::SetDocShellIsActiveAndForeground(bool isActive)
+TabParent::PreserveLayers(bool aPreserveLayers)
 {
-  // docshell is consider prerendered only if not active yet
-  mIsPrerendered &= !isActive;
-  mDocShellIsActive = isActive;
-  Unused << SendSetDocShellIsActive(isActive, false);
+  mPreserveLayers = aPreserveLayers;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::SuppressDisplayport(bool aEnabled)
 {
   if (IsDestroyed()) {
     return NS_OK;
@@ -2927,121 +2939,83 @@ TabParent::NavigateByKey(bool aForward, 
 {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
   return NS_OK;
 }
 
 class LayerTreeUpdateRunnable final
   : public mozilla::Runnable
 {
-  RefPtr<LayerTreeUpdateObserver> mUpdateObserver;
+  uint64_t mLayersId;
+  uint64_t mEpoch;
   bool mActive;
 
 public:
-  explicit LayerTreeUpdateRunnable(LayerTreeUpdateObserver* aObs, bool aActive)
-    : mUpdateObserver(aObs), mActive(aActive)
+  explicit LayerTreeUpdateRunnable(uint64_t aLayersId, uint64_t aEpoch, bool aActive)
+    : mLayersId(aLayersId), mEpoch(aEpoch), mActive(aActive)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
 
 private:
   NS_IMETHOD Run() override {
     MOZ_ASSERT(NS_IsMainThread());
-    if (RefPtr<TabParent> tabParent = mUpdateObserver->GetTabParent()) {
-      tabParent->LayerTreeUpdate(mActive);
+    if (RefPtr<TabParent> tabParent = TabParent::GetTabParentFromLayersId(mLayersId)) {
+      tabParent->LayerTreeUpdate(mEpoch, mActive);
     }
     return NS_OK;
   }
 };
 
-void
-LayerTreeUpdateObserver::ObserveUpdate(uint64_t aLayersId, bool aActive)
+/* static */ void
+TabParent::ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<LayerTreeUpdateRunnable> runnable =
-    new LayerTreeUpdateRunnable(this, aActive);
+    new LayerTreeUpdateRunnable(aLayersId, aEpoch, aActive);
   NS_DispatchToMainThread(runnable);
 }
 
-
-bool
-TabParent::RequestNotifyLayerTreeReady()
+void
+TabParent::LayerTreeUpdate(uint64_t aEpoch, bool aActive)
 {
-  RenderFrameParent* frame = GetRenderFrame();
-  if (!frame || !frame->IsInitted()) {
-    mNeedLayerTreeReadyNotification = true;
-  } else {
-    GPUProcessManager::Get()->RequestNotifyLayerTreeReady(
-      frame->GetLayersId(),
-      mLayerUpdateObserver);
+  // Ignore updates from old epochs. They might tell us that layers are
+  // available when we've already sent a message to clear them. We can't trust
+  // the update in that case since layers could disappear anytime after that.
+  if (aEpoch != mLayerTreeEpoch || mIsDestroyed) {
+    return;
   }
-  return true;
-}
-
-bool
-TabParent::RequestNotifyLayerTreeCleared()
-{
-  RenderFrameParent* frame = GetRenderFrame();
-  if (!frame) {
-    return false;
-  }
-
-  GPUProcessManager::Get()->RequestNotifyLayerTreeCleared(
-    frame->GetLayersId(),
-    mLayerUpdateObserver);
-  return true;
-}
-
-bool
-TabParent::LayerTreeUpdate(bool aActive)
-{
+
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
     NS_WARNING("Could not locate target for layer tree message.");
-    return true;
+    return;
   }
 
   RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
   if (aActive) {
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeReady"), true, false);
   } else {
     event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
   }
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   bool dummy;
   mFrameElement->DispatchEvent(event, &dummy);
-  return true;
 }
 
-void
-TabParent::SwapLayerTreeObservers(TabParent* aOther)
+bool
+TabParent::RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch)
 {
-  if (IsDestroyed() || aOther->IsDestroyed()) {
-    return;
-  }
-
-  RenderFrameParent* rfp = GetRenderFrame();
-  RenderFrameParent* otherRfp = aOther->GetRenderFrame();
-  if (!rfp || !otherRfp) {
-    return;
-  }
-
-  // The swap that happens for the observers in GPUProcessManager has to
-  // happen in a lock so that an update being processed on the compositor thread
-  // can't grab the layer update observer for the wrong tab parent.
-  GPUProcessManager::Get()->SwapLayerTreeObservers(
-    rfp->GetLayersId(),
-    otherRfp->GetLayersId());
-
-  // No need for a lock, destruction can only happen on the main thread and we
-  // only read mLayerUpdateObserver::mTabParent on the main thread.
-  Swap(mLayerUpdateObserver, aOther->mLayerUpdateObserver);
-  mLayerUpdateObserver->SwapTabParent(aOther->mLayerUpdateObserver);
+  // We sent a ForcePaint message when layers were already visible. In this
+  // case, we should act as if an update occurred even though we already have
+  // the layers.
+  LayerTreeUpdate(aLayerObserverEpoch, true);
+  return true;
 }
 
 bool
 TabParent::RecvRemotePaintIsReady()
 {
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   if (!target) {
     NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -75,49 +75,16 @@ class ClonedMessageData;
 class nsIContentParent;
 class Element;
 class DataTransfer;
 
 namespace ipc {
 class StructuredCloneData;
 } // ipc namespace
 
-// This observer runs on the compositor thread, so we dispatch a runnable to the
-// main thread to actually dispatch the event.
-class LayerTreeUpdateObserver : public layers::CompositorUpdateObserver
-{
-public:
-  explicit LayerTreeUpdateObserver(TabParent* aTabParent)
-    : mTabParent(aTabParent)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) override;
-
-  virtual void SwapTabParent(LayerTreeUpdateObserver* aOther) {
-    MOZ_ASSERT(NS_IsMainThread());
-    Swap(mTabParent, aOther->mTabParent);
-  }
-
-  void TabParentDestroyed() {
-    MOZ_ASSERT(NS_IsMainThread());
-    mTabParent = nullptr;
-  }
-
-  TabParent* GetTabParent() {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mTabParent;
-  }
-
-private:
-  // NB: Should never be touched off the main thread!
-  TabParent* mTabParent;
-};
-
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsIKeyEventInPluginCallback
                       , public nsSupportsWeakReference
                       , public TabContext
@@ -586,24 +553,18 @@ public:
 
   bool IsInitedByParent() const { return mInitedByParent; }
 
   static TabParent* GetNextTabParent();
 
   bool SendLoadRemoteScript(const nsString& aURL,
                             const bool& aRunInGlobalScope);
 
-  // See nsIFrameLoader requestNotifyLayerTreeReady.
-  bool RequestNotifyLayerTreeReady();
-
-  bool RequestNotifyLayerTreeCleared();
-
-  bool LayerTreeUpdate(bool aActive);
-
-  void SwapLayerTreeObservers(TabParent* aOther);
+  static void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive);
+  void LayerTreeUpdate(uint64_t aEpoch, bool aActive);
 
   virtual bool
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
                         const OptionalShmem& aVisualDnDData,
                         const uint32_t& aWidth, const uint32_t& aHeight,
                         const uint32_t& aStride, const uint8_t& aFormat,
                         const int32_t& aDragAreaX, const int32_t& aDragAreaY) override;
@@ -643,16 +604,18 @@ protected:
   nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 
   virtual PRenderFrameParent* AllocPRenderFrameParent() override;
 
   virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override;
 
   virtual bool RecvRemotePaintIsReady() override;
 
+  virtual bool RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch) override;
+
   virtual bool RecvSetDimensions(const uint32_t& aFlags,
                                  const int32_t& aX, const int32_t& aY,
                                  const int32_t& aCx, const int32_t& aCy) override;
 
   virtual bool RecvGetTabCount(uint32_t* aValue) override;
 
   virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
                                                     const bool& aActive) override;
@@ -760,21 +723,16 @@ private:
   // frame scripts for that tab are loaded before any scripts start to run in
   // the window. We can't load the frame scripts the normal way, using
   // separate IPC messages, since they won't be processed by the child until
   // returning to the event loop, which is too late. Instead, we queue up
   // frame scripts that we intend to load and send them as part of the
   // CreateWindow response. Then TabChild loads them immediately.
   nsTArray<FrameScriptInfo> mDelayedFrameScripts;
 
-  // If the user called RequestNotifyLayerTreeReady and the RenderFrameParent
-  // wasn't ready yet, we set this flag and call RequestNotifyLayerTreeReady
-  // again once the RenderFrameParent arrives.
-  bool mNeedLayerTreeReadyNotification;
-
   // Cached cursor setting from TabChild.  When the cursor is over the tab,
   // it should take this appearance.
   nsCursor mCursor;
   nsCOMPtr<imgIContainer> mCustomCursor;
   uint32_t mCustomCursorHotspotX, mCustomCursorHotspotY;
 
   // True if the cursor changes from the TabChild should change the widget
   // cursor.  This happens whenever the cursor is in the tab's region.
@@ -795,17 +753,27 @@ private:
   // to dispatch events.
   typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable;
   static LayerToTabParentTable* sLayerToTabParentTable;
 
   static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
 
   static void RemoveTabParentFromTable(uint64_t aLayersId);
 
-  RefPtr<LayerTreeUpdateObserver> mLayerUpdateObserver;
+  uint64_t mLayerTreeEpoch;
+
+  // If this flag is set, then the tab's layers will be preserved even when
+  // the tab's docshell is inactive.
+  bool mPreserveLayers;
+
+  // Normally we call ForceTabPaint when activating a tab. But we don't do this
+  // the first time we activate a tab. The tab is probably busy running the
+  // initial content scripts and we don't want to force painting then; they're
+  // probably quick and there's some cost to forcing painting.
+  bool mFirstActivate;
 
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -1,16 +1,16 @@
 /* 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 "AndroidDecoderModule.h"
 #include "AndroidBridge.h"
 #include "AndroidSurfaceTexture.h"
-#include "GeneratedJNINatives.h"
+#include "FennecJNINatives.h"
 #include "GLImages.h"
 
 #include "MediaData.h"
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "VPXDecoder.h"
 
 #include "nsThreadUtils.h"
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -2,17 +2,17 @@
  * 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 RemoteDataDecoder_h_
 #define RemoteDataDecoder_h_
 
 #include "AndroidDecoderModule.h"
 
-#include "GeneratedJNIWrappers.h"
+#include "FennecJNIWrappers.h"
 
 #include "SurfaceTexture.h"
 #include "TimeUnits.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Maybe.h"
 
 #include <deque>
 
--- a/dom/media/systemservices/ShmemPool.cpp
+++ b/dom/media/systemservices/ShmemPool.cpp
@@ -4,22 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Logging.h"
 #include "mozilla/ShmemPool.h"
 #include "mozilla/Move.h"
 
-#undef LOG
-#undef LOG_ENABLED
-extern mozilla::LazyLogModule gCamerasParentLog;
-#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
-#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
-
 namespace mozilla {
 
 ShmemPool::ShmemPool(size_t aPoolSize)
   : mMutex("mozilla::ShmemPool"),
     mPoolFree(aPoolSize)
 #ifdef DEBUG
     ,mMaxPoolUse(0)
 #endif
@@ -57,94 +51,30 @@ mozilla::ShmemBuffer ShmemPool::GetIfAva
   if (poolUse > mMaxPoolUse) {
     mMaxPoolUse = poolUse;
     LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse));
   }
 #endif
   return Move(res);
 }
 
-template <class T>
-mozilla::ShmemBuffer ShmemPool::Get(T* aInstance, size_t aSize)
-{
-  MutexAutoLock lock(mMutex);
-
-  // Pool is empty, don't block caller.
-  if (mPoolFree == 0) {
-    // This isn't initialized, so will be understood as an error.
-    return ShmemBuffer();
-  }
-
-  ShmemBuffer& res = mShmemPool[mPoolFree - 1];
-
-  if (!res.mInitialized) {
-    LOG(("Initializing new Shmem in pool"));
-    if (!aInstance->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &res.mShmem)) {
-      LOG(("Failure allocating new Shmem buffer"));
-      return ShmemBuffer();
-    }
-    res.mInitialized = true;
-  }
-
-  MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable?");
-
-  // Prepare buffer, increase size if needed (we never shrink as we don't
-  // maintain seperate sized pools and we don't want to keep reallocating)
-  if (res.mShmem.Size<char>() < aSize) {
-    LOG(("Size change/increase in Shmem Pool"));
-    aInstance->DeallocShmem(res.mShmem);
-    res.mInitialized = false;
-    // this may fail; always check return value
-    if (!aInstance->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &res.mShmem)) {
-      LOG(("Failure allocating resized Shmem buffer"));
-      return ShmemBuffer();
-    } else {
-      res.mInitialized = true;
-    }
-  }
-
-  MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable post resize?");
-
-  mPoolFree--;
-#ifdef DEBUG
-  size_t poolUse = mShmemPool.Length() - mPoolFree;
-  if (poolUse > mMaxPoolUse) {
-    mMaxPoolUse = poolUse;
-    LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse));
-  }
-#endif
-  return Move(res);
-}
-
 void ShmemPool::Put(ShmemBuffer&& aShmem)
 {
   MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mPoolFree < mShmemPool.Length());
   mShmemPool[mPoolFree] = Move(aShmem);
   mPoolFree++;
 #ifdef DEBUG
   size_t poolUse = mShmemPool.Length() - mPoolFree;
   if (poolUse > 0) {
     LOG(("ShmemPool usage reduced to %d buffers", poolUse));
   }
 #endif
 }
 
-template <class T>
-void ShmemPool::Cleanup(T* aInstance)
-{
-  MutexAutoLock lock(mMutex);
-  for (size_t i = 0; i < mShmemPool.Length(); i++) {
-    if (mShmemPool[i].mInitialized) {
-      aInstance->DeallocShmem(mShmemPool[i].Get());
-      mShmemPool[i].mInitialized = false;
-    }
-  }
-}
-
 ShmemPool::~ShmemPool()
 {
 #ifdef DEBUG
   for (size_t i = 0; i < mShmemPool.Length(); i++) {
     MOZ_ASSERT(!mShmemPool[i].Valid());
   }
 #endif
 }
--- a/dom/media/systemservices/ShmemPool.h
+++ b/dom/media/systemservices/ShmemPool.h
@@ -5,16 +5,22 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ShmemPool_h
 #define mozilla_ShmemPool_h
 
 #include "mozilla/ipc/Shmem.h"
 #include "mozilla/Mutex.h"
 
+#undef LOG
+#undef LOG_ENABLED
+extern mozilla::LazyLogModule gCamerasParentLog;
+#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
+
 namespace mozilla {
 
 class ShmemPool;
 
 class ShmemBuffer {
 public:
   ShmemBuffer() : mInitialized(false) {}
   explicit ShmemBuffer(mozilla::ipc::Shmem aShmem) {
@@ -56,24 +62,86 @@ private:
   bool mInitialized;
   mozilla::ipc::Shmem mShmem;
 };
 
 class ShmemPool {
 public:
   explicit ShmemPool(size_t aPoolSize);
   ~ShmemPool();
+  // Get/GetIfAvailable differ in what thread they can run on. GetIfAvailable
+  // can run anywhere but won't allocate if the right size isn't available.
+  ShmemBuffer GetIfAvailable(size_t aSize);
+  void Put(ShmemBuffer&& aShmem);
+
   // We need to use the allocation/deallocation functions
   // of a specific IPC child/parent instance.
-  template <class T> void Cleanup(T* aInstance);
-  // These 2 differ in what thread they can run on. GetIfAvailable
-  // can run anywhere but won't allocate if the right size isn't available.
-  ShmemBuffer GetIfAvailable(size_t aSize);
-  template <class T> ShmemBuffer Get(T* aInstance, size_t aSize);
-  void Put(ShmemBuffer&& aShmem);
+  template <class T>
+  void Cleanup(T* aInstance)
+  {
+    MutexAutoLock lock(mMutex);
+    for (size_t i = 0; i < mShmemPool.Length(); i++) {
+      if (mShmemPool[i].mInitialized) {
+        aInstance->DeallocShmem(mShmemPool[i].Get());
+        mShmemPool[i].mInitialized = false;
+      }
+    }
+  }
+
+  template <class T>
+  ShmemBuffer Get(T* aInstance, size_t aSize)
+  {
+    MutexAutoLock lock(mMutex);
+
+    // Pool is empty, don't block caller.
+    if (mPoolFree == 0) {
+      // This isn't initialized, so will be understood as an error.
+      return ShmemBuffer();
+    }
+
+    ShmemBuffer& res = mShmemPool[mPoolFree - 1];
+
+    if (!res.mInitialized) {
+      LOG(("Initializing new Shmem in pool"));
+      if (!aInstance->AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &res.mShmem)) {
+        LOG(("Failure allocating new Shmem buffer"));
+        return ShmemBuffer();
+      }
+      res.mInitialized = true;
+    }
+
+    MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable?");
+
+    // Prepare buffer, increase size if needed (we never shrink as we don't
+    // maintain seperate sized pools and we don't want to keep reallocating)
+    if (res.mShmem.Size<char>() < aSize) {
+      LOG(("Size change/increase in Shmem Pool"));
+      aInstance->DeallocShmem(res.mShmem);
+      res.mInitialized = false;
+      // this may fail; always check return value
+      if (!aInstance->AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &res.mShmem)) {
+        LOG(("Failure allocating resized Shmem buffer"));
+        return ShmemBuffer();
+      } else {
+        res.mInitialized = true;
+      }
+    }
+
+    MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable post resize?");
+
+    mPoolFree--;
+#ifdef DEBUG
+    size_t poolUse = mShmemPool.Length() - mPoolFree;
+    if (poolUse > mMaxPoolUse) {
+      mMaxPoolUse = poolUse;
+      LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse));
+    }
+#endif
+    return Move(res);
+  }
 
 private:
   Mutex mMutex;
   size_t mPoolFree;
 #ifdef DEBUG
   size_t mMaxPoolUse;
 #endif
   nsTArray<ShmemBuffer> mShmemPool;
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -295,31 +295,23 @@ MediaEngineWebRTCMicrophoneSource::Updat
       MOZ_ASSERT(aHandle);
       if (sChannelsOpen == 0) {
         if (!InitEngine()) {
           LOG(("Audio engine is not initalized"));
           return NS_ERROR_FAILURE;
         }
       }
       if (!AllocChannel()) {
-        if (sChannelsOpen == 0) {
-          DeInitEngine();
-        }
         LOG(("Audio device is not initalized"));
         return NS_ERROR_FAILURE;
       }
       if (mAudioInput->SetRecordingDevice(mCapIndex)) {
         FreeChannel();
-        if (sChannelsOpen == 0) {
-          DeInitEngine();
-        }
         return NS_ERROR_FAILURE;
       }
-      sChannelsOpen++;
-      mState = kAllocated;
       LOG(("Audio device %d allocated", mCapIndex));
       break;
 
     case kStarted:
       if (prefs == mLastPrefs) {
         return NS_OK;
       }
       if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
@@ -396,22 +388,17 @@ MediaEngineWebRTCMicrophoneSource::Deall
 
   if (!mRegisteredHandles.Length()) {
     // If empty, no callbacks to deliver data should be occuring
     if (mState != kStopped && mState != kAllocated) {
       return NS_ERROR_FAILURE;
     }
 
     FreeChannel();
-    mState = kReleased;
     LOG(("Audio device %d deallocated", mCapIndex));
-    MOZ_ASSERT(sChannelsOpen > 0);
-    if (--sChannelsOpen == 0) {
-      DeInitEngine();
-    }
   } else {
     LOG(("Audio device %d deallocated but still in use", mCapIndex));
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
@@ -677,81 +664,27 @@ bool
 MediaEngineWebRTCMicrophoneSource::InitEngine()
 {
   MOZ_ASSERT(!mVoEBase);
   mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
 
   mVoEBase->Init();
 
   mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
-  if (!mVoERender) {
-    return false;
-  }
-  mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine);
-  if (!mVoENetwork) {
-    return false;
-  }
-
-  mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine);
-  if (!mVoEProcessing) {
-    return false;
-  }
-  mNullTransport = new NullTransport();
-  return true;
-}
-
-bool
-MediaEngineWebRTCMicrophoneSource::AllocChannel()
-{
-  MOZ_ASSERT(mVoEBase);
-
-  mChannel = mVoEBase->CreateChannel();
-  if (mChannel < 0) {
-    return false;
-  }
-  if (mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) {
-    return false;
+  if (mVoERender) {
+    mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine);
+    if (mVoENetwork) {
+      mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine);
+      if (mVoEProcessing) {
+        mNullTransport = new NullTransport();
+        return true;
+      }
+    }
   }
-
-  mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE;
-  LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
-
-  // Check for availability.
-  if (mAudioInput->SetRecordingDevice(mCapIndex)) {
-    return false;
-  }
-
-#ifndef MOZ_B2G
-  // Because of the permission mechanism of B2G, we need to skip the status
-  // check here.
-  bool avail = false;
-  mAudioInput->GetRecordingDeviceStatus(avail);
-  if (!avail) {
-    return false;
-  }
-#endif // MOZ_B2G
-
-  // Set "codec" to PCM, 32kHz on 1 channel
-  ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
-  if (!ptrVoECodec) {
-    return false;
-  }
-
-  webrtc::CodecInst codec;
-  strcpy(codec.plname, ENCODING);
-  codec.channels = CHANNELS;
-  MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
-  codec.rate = SAMPLE_RATE(mSampleFrequency);
-  codec.plfreq = mSampleFrequency;
-  codec.pacsize = SAMPLE_LENGTH(mSampleFrequency);
-  codec.pltype = 0; // Default payload type
-
-  if (!ptrVoECodec->SetSendCodec(mChannel, codec)) {
-    return true;
-  }
+  DeInitEngine();
   return false;
 }
 
 // This shuts down the engine when no channel is open
 void
 MediaEngineWebRTCMicrophoneSource::DeInitEngine()
 {
   if (mVoEBase) {
@@ -761,28 +694,94 @@ MediaEngineWebRTCMicrophoneSource::DeIni
 
     mVoEProcessing = nullptr;
     mVoENetwork = nullptr;
     mVoERender = nullptr;
     mVoEBase = nullptr;
   }
 }
 
-// This shuts down the engine when no channel is open
+// This shuts down the engine when no channel is open.
+// mState records if a channel is allocated (slightly redundantly to mChannel)
 void
 MediaEngineWebRTCMicrophoneSource::FreeChannel()
 {
-  if (mChannel != -1) {
-    if (mVoENetwork) {
-      mVoENetwork->DeRegisterExternalTransport(mChannel);
+  if (mState != kReleased) {
+    if (mChannel != -1) {
+      MOZ_ASSERT(mVoENetwork && mVoEBase);
+      if (mVoENetwork) {
+        mVoENetwork->DeRegisterExternalTransport(mChannel);
+      }
+      if (mVoEBase) {
+        mVoEBase->DeleteChannel(mChannel);
+      }
+      mChannel = -1;
+    }
+    mState = kReleased;
+
+    MOZ_ASSERT(sChannelsOpen > 0);
+    if (--sChannelsOpen == 0) {
+      DeInitEngine();
     }
-    mVoEBase->DeleteChannel(mChannel);
-    mChannel = -1;
   }
-  mState = kReleased;
+}
+
+bool
+MediaEngineWebRTCMicrophoneSource::AllocChannel()
+{
+  MOZ_ASSERT(mVoEBase);
+
+  mChannel = mVoEBase->CreateChannel();
+  if (mChannel >= 0) {
+    if (!mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) {
+      mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE;
+      LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
+
+      // Check for availability.
+      if (!mAudioInput->SetRecordingDevice(mCapIndex)) {
+#ifndef MOZ_B2G
+        // Because of the permission mechanism of B2G, we need to skip the status
+        // check here.
+        bool avail = false;
+        mAudioInput->GetRecordingDeviceStatus(avail);
+        if (!avail) {
+          if (sChannelsOpen == 0) {
+            DeInitEngine();
+          }
+          return false;
+        }
+#endif // MOZ_B2G
+
+        // Set "codec" to PCM, 32kHz on 1 channel
+        ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
+        if (ptrVoECodec) {
+          webrtc::CodecInst codec;
+          strcpy(codec.plname, ENCODING);
+          codec.channels = CHANNELS;
+          MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
+          codec.rate = SAMPLE_RATE(mSampleFrequency);
+          codec.plfreq = mSampleFrequency;
+          codec.pacsize = SAMPLE_LENGTH(mSampleFrequency);
+          codec.pltype = 0; // Default payload type
+
+          if (!ptrVoECodec->SetSendCodec(mChannel, codec)) {
+            mState = kAllocated;
+            sChannelsOpen++;
+            return true;
+          }
+        }
+      }
+    }
+  }
+  mVoEBase->DeleteChannel(mChannel);
+  mChannel = -1;
+  if (sChannelsOpen == 0) {
+    DeInitEngine();
+  }
+  return false;
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::Shutdown()
 {
   Super::Shutdown();
   if (mListener) {
     // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us
@@ -810,18 +809,16 @@ MediaEngineWebRTCMicrophoneSource::Shutd
   }
 
   while (mRegisteredHandles.Length()) {
     MOZ_ASSERT(mState == kAllocated || mState == kStopped);
     Deallocate(nullptr); // XXX Extend concurrent constraints code to mics.
   }
 
   FreeChannel();
-  DeInitEngine();
-
   mAudioInput = nullptr;
 }
 
 typedef int16_t sample;
 
 void
 MediaEngineWebRTCMicrophoneSource::Process(int channel,
                                            webrtc::ProcessingTypes type,
--- a/dom/plugins/ipc/PluginUtilsOSX.mm
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -63,17 +63,17 @@ CGBitmapContextSetDataFunc CGBitmapConte
 }
 
 - (void)drawInContext:(CGContextRef)aCGContext
 {
   ::CGContextSaveGState(aCGContext); 
   ::CGContextTranslateCTM(aCGContext, 0, self.bounds.size.height);
   ::CGContextScaleCTM(aCGContext, (CGFloat) 1, (CGFloat) -1);
 
-  mUpdateRect = nsIntRect(0, 0, self.bounds.size.width, self.bounds.size.height);
+  mUpdateRect = nsIntRect::Truncate(0, 0, self.bounds.size.width, self.bounds.size.height);
 
   mDrawFunc(aCGContext, mPluginInstance, mUpdateRect);
 
   ::CGContextRestoreGState(aCGContext);
 
   mUpdateRect.SetEmpty();
 }
 
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -5,20 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 #include "nsIDocShell.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPresentationService.h"
 #include "nsIWebProgress.h"
 #include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
 #include "PresentationCallbacks.h"
 #include "PresentationRequest.h"
 #include "PresentationConnection.h"
-#include "nsThreadUtils.h"
+#include "PresentationTransportBuilderConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /*
  * Implementation of PresentationRequesterCallback
  */
 
@@ -196,17 +197,21 @@ PresentationResponderLoadingCallback::No
   uint64_t windowId = window->GetCurrentInnerWindow()->WindowID();
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return service->NotifyReceiverReady(mSessionId, windowId, aIsLoading);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    PresentationTransportBuilderConstructor::Create();
+  return service->NotifyReceiverReady(mSessionId,
+                                      windowId,aIsLoading,
+                                      constructor);
 }
 
 // nsIWebProgressListener
 NS_IMETHODIMP
 PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress,
                                                     nsIRequest* aRequest,
                                                     uint32_t aStateFlags,
                                                     nsresult aStatus)
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -43,17 +43,18 @@ PresentationDataChannelDescription.proto
 function PresentationTransportBuilder() {
   log("PresentationTransportBuilder construct");
   this._isControlChannelNeeded = true;
 }
 
 PresentationTransportBuilder.prototype = {
   classID: PRESENTATIONTRANSPORTBUILDER_CID,
   contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDataChannelSessionTransportBuilder,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder,
+                                         Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsITimerCallback]),
 
   buildDataChannelTransport: function(aRole, aWindow, aListener) {
     if (!aRole || !aWindow || !aListener) {
       log("buildDataChannelTransport with illegal parameters");
       throw Cr.NS_ERROR_ILLEGAL_VALUE;
     }
 
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -21,16 +21,17 @@
 #include "nsIURI.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 #include "nsSandboxFlags.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationAvailability.h"
 #include "PresentationCallbacks.h"
 #include "PresentationLog.h"
+#include "PresentationTransportBuilderConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationRequest)
@@ -202,17 +203,26 @@ PresentationRequest::StartWithDevice(con
   // Get xul:browser element in parent process or nsWindowRoot object in child
   // process. If it's in child process, the corresponding xul:browser element
   // will be obtained at PresentationRequestParent::DoRequest in its parent
   // process.
   nsCOMPtr<nsIDOMEventTarget> handler =
     do_QueryInterface(GetOwner()->GetChromeEventHandler());
   nsCOMPtr<nsIPresentationServiceCallback> callback =
     new PresentationRequesterCallback(this, id, promise);
-  rv = service->StartSession(mUrls, id, origin, aDeviceId, GetOwner()->WindowID(), handler, callback);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    PresentationTransportBuilderConstructor::Create();
+  rv = service->StartSession(mUrls,
+                             id,
+                             origin,
+                             aDeviceId,
+                             GetOwner()->WindowID(),
+                             handler,
+                             callback,
+                             constructor);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -102,61 +102,67 @@ ConvertURLArrayHelper(const nsTArray<nsS
  */
 
 class PresentationDeviceRequest final : public nsIPresentationDeviceRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONDEVICEREQUEST
 
-  PresentationDeviceRequest(const nsTArray<nsString>& aUrls,
-                            const nsAString& aId,
-                            const nsAString& aOrigin,
-                            uint64_t aWindowId,
-                            nsIDOMEventTarget* aEventTarget,
-                            nsIPresentationServiceCallback* aCallback);
+  PresentationDeviceRequest(
+              const nsTArray<nsString>& aUrls,
+              const nsAString& aId,
+              const nsAString& aOrigin,
+              uint64_t aWindowId,
+              nsIDOMEventTarget* aEventTarget,
+              nsIPresentationServiceCallback* aCallback,
+              nsIPresentationTransportBuilderConstructor* aBuilderConstructor);
 
 private:
   virtual ~PresentationDeviceRequest() = default;
   nsresult CreateSessionInfo(nsIPresentationDevice* aDevice,
                              const nsAString& aSelectedRequestUrl);
 
   nsTArray<nsString> mRequestUrls;
   nsString mId;
   nsString mOrigin;
   uint64_t mWindowId;
   nsWeakPtr mChromeEventHandler;
   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
 };
 
 LazyLogModule gPresentationLog("Presentation");
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest)
 
 PresentationDeviceRequest::PresentationDeviceRequest(
-                                      const nsTArray<nsString>& aUrls,
-                                      const nsAString& aId,
-                                      const nsAString& aOrigin,
-                                      uint64_t aWindowId,
-                                      nsIDOMEventTarget* aEventTarget,
-                                      nsIPresentationServiceCallback* aCallback)
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aId,
+               const nsAString& aOrigin,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
   : mRequestUrls(aUrls)
   , mId(aId)
   , mOrigin(aOrigin)
   , mWindowId(aWindowId)
   , mChromeEventHandler(do_GetWeakReference(aEventTarget))
   , mCallback(aCallback)
+  , mBuilderConstructor(aBuilderConstructor)
 {
   MOZ_ASSERT(!mRequestUrls.IsEmpty());
   MOZ_ASSERT(!mId.IsEmpty());
   MOZ_ASSERT(!mOrigin.IsEmpty());
   MOZ_ASSERT(mCallback);
+  MOZ_ASSERT(mBuilderConstructor);
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
@@ -236,16 +242,17 @@ PresentationDeviceRequest::CreateSession
   }
 
   // Initialize the session info with the control channel.
   rv = info->Init(ctrlChannel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
+  info->SetTransportBuilderConstructor(mBuilderConstructor);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::Cancel(nsresult aReason)
 {
   return mCallback->NotifyError(aReason);
 }
@@ -637,38 +644,41 @@ PresentationService::IsAppInstalled(nsIU
   if (NS_WARN_IF(!app)) {
     return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP
-PresentationService::StartSession(const nsTArray<nsString>& aUrls,
-                                  const nsAString& aSessionId,
-                                  const nsAString& aOrigin,
-                                  const nsAString& aDeviceId,
-                                  uint64_t aWindowId,
-                                  nsIDOMEventTarget* aEventTarget,
-                                  nsIPresentationServiceCallback* aCallback)
+PresentationService::StartSession(
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aSessionId,
+               const nsAString& aOrigin,
+               const nsAString& aDeviceId,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!aSessionId.IsEmpty());
   MOZ_ASSERT(!aUrls.IsEmpty());
 
   nsCOMPtr<nsIPresentationDeviceRequest> request =
     new PresentationDeviceRequest(aUrls,
                                   aSessionId,
                                   aOrigin,
                                   aWindowId,
                                   aEventTarget,
-                                  aCallback);
+                                  aCallback,
+                                  aBuilderConstructor);
 
   if (aDeviceId.IsVoid()) {
     // Pop up a prompt and ask user to select a device.
     nsCOMPtr<nsIPresentationDevicePrompt> prompt =
       do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
     if (NS_WARN_IF(!prompt)) {
       return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
@@ -940,38 +950,16 @@ PresentationService::UnregisterSessionLi
     // When content side decide not handling this session anymore, simply
     // close the connection. Session info is kept for reconnection.
     Unused << NS_WARN_IF(NS_FAILED(info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED)));
     return info->SetListener(nullptr);
   }
   return NS_OK;
 }
 
-nsresult
-PresentationService::RegisterTransportBuilder(const nsAString& aSessionId,
-                                              uint8_t aRole,
-                                              nsIPresentationSessionTransportBuilder* aBuilder)
-{
-  PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
-             NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
-
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aBuilder);
-  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
-             aRole == nsIPresentationService::ROLE_RECEIVER);
-
-  RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
-  if (NS_WARN_IF(!info)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  info->SetBuilder(aBuilder);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 PresentationService::RegisterRespondingListener(
   uint64_t aWindowId,
   nsIPresentationRespondingListener* aListener)
 {
   PRES_DEBUG("%s:windowId[%lld]\n", __func__, aWindowId);
 
   MOZ_ASSERT(NS_IsMainThread());
@@ -1004,19 +992,21 @@ PresentationService::UnregisterRespondin
 
   MOZ_ASSERT(NS_IsMainThread());
 
   mRespondingListeners.Remove(aWindowId);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationService::NotifyReceiverReady(const nsAString& aSessionId,
-                                         uint64_t aWindowId,
-                                         bool aIsLoading)
+PresentationService::NotifyReceiverReady(
+               const nsAString& aSessionId,
+               uint64_t aWindowId,
+               bool aIsLoading,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   PRES_DEBUG("%s:id[%s], windowId[%lld], loading[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId, aIsLoading);
 
   RefPtr<PresentationSessionInfo> info =
     GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -1034,16 +1024,17 @@ PresentationService::NotifyReceiverReady
   nsCOMPtr<nsIPresentationRespondingListener> listener;
   if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
     nsresult rv = listener->NotifySessionConnect(aWindowId, aSessionId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
+  info->SetTransportBuilderConstructor(aBuilderConstructor);
   return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderReady();
 }
 
 nsresult
 PresentationService::NotifyTransportClosed(const nsAString& aSessionId,
                                            uint8_t aRole,
                                            nsresult aReason)
 {
--- a/dom/presentation/PresentationService.h
+++ b/dom/presentation/PresentationService.h
@@ -51,20 +51,16 @@ public:
              info.forget() : nullptr;
     }
   }
 
   bool IsSessionAccessible(const nsAString& aSessionId,
                            const uint8_t aRole,
                            base::ProcessId aProcessId);
 
-  nsresult RegisterTransportBuilder(const nsAString& aSessionId,
-                                    uint8_t aRole,
-                                    nsIPresentationSessionTransportBuilder* aBuilder);
-
 private:
   friend class PresentationDeviceRequest;
 
   virtual ~PresentationService();
   void HandleShutdown();
   nsresult HandleDeviceChange();
   nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
   nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest);
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -236,17 +236,17 @@ PresentationSessionInfo::Shutdown(nsresu
   if (mTransport) {
     // |mIsTransportReady| will be unset once |NotifyTransportClosed| is called.
     Unused << NS_WARN_IF(NS_FAILED(mTransport->Close(aReason)));
   }
 
   mIsResponderReady = false;
   mIsOnTerminating = false;
 
-  SetBuilder(nullptr);
+  ResetBuilder();
 }
 
 nsresult
 PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
 {
   if (mListener && aListener) {
     Unused << NS_WARN_IF(NS_FAILED(mListener->NotifyReplaced()));
   }
@@ -374,17 +374,22 @@ PresentationSessionInfo::GetWindow()
   }
   uint64_t windowId = 0;
   if (NS_WARN_IF(NS_FAILED(service->GetWindowIdBySessionId(mSessionId,
                                                            mRole,
                                                            &windowId)))) {
     return nullptr;
   }
 
-  return nsGlobalWindow::GetInnerWindowWithId(windowId)->AsInner();
+  auto window = nsGlobalWindow::GetInnerWindowWithId(windowId);
+  if (!window) {
+    return nullptr;
+  }
+
+  return window->AsInner();
 }
 
 /* virtual */ bool
 PresentationSessionInfo::IsAccessible(base::ProcessId aProcessId)
 {
   // No restriction by default.
   return true;
 }
@@ -487,17 +492,17 @@ PresentationSessionInfo::NotifyData(cons
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
 PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
 {
   PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState);
 
-  SetBuilder(nullptr);
+  ResetBuilder();
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
     return NS_ERROR_FAILURE;
   }
 
   // The session transport is managed by content process
   if (!transport) {
     return NS_OK;
@@ -518,17 +523,17 @@ PresentationSessionInfo::OnSessionTransp
 }
 
 NS_IMETHODIMP
 PresentationSessionInfo::OnError(nsresult aReason)
 {
   PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole);
 
-  SetBuilder(nullptr);
+  ResetBuilder();
   return ReplyError(aReason);
 }
 
 NS_IMETHODIMP
 PresentationSessionInfo::SendOffer(nsIPresentationChannelDescription* aOffer)
 {
   return mControlChannel->SendOffer(aOffer);
 }
@@ -822,62 +827,62 @@ nsresult
 PresentationControllingInfo::BuildTransport()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
     return NS_OK;
   }
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
     // Build TCP session transport
     return GetAddress();
   }
-
-  nsPIDOMWindowInner* window = nullptr;
   /**
    * Generally transport is maintained by the chrome process. However, data
    * channel should be live with the DOM , which implies RTCDataChannel in an OOP
    * page should be establish in the content process.
    *
-   * In OOP data channel transport case, |mBuilder| is hooked when the content
-   * process is ready to build a data channel transport, trigger by:
-   * 1. PresentationIPCService::StartSession (sender)
-   * 2. PresentationIPCService::NotifyReceiverReady (receiver).
+   * |mBuilderConstructor| is responsible for creating a builder, which is for
+   * building a data channel transport.
    *
-   * In this case, |mBuilder| would be an object of |PresentationBuilderParent|
-   * and set previously. Therefore, |BuildDataChannelTransport| triggers an IPC
-   * call to make content process establish a RTCDataChannel transport.
+   * In the OOP case, |mBuilderConstructor| would create a builder which is
+   * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport|
+   * triggers an IPC call to make content process establish a RTCDataChannel
+   * transport.
    */
-  // in-process case
-  if (!mBuilder) {
-    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> builder =
-      do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
-    if (NS_WARN_IF(!builder)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-    SetBuilder(builder);
-    // OOP window would be set from content process
-    window = GetWindow();
+
+  mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
+  if (NS_WARN_IF(NS_FAILED(
+    mBuilderConstructor->CreateTransportBuilder(mTransportType,
+                                                getter_AddRefs(mBuilder))))) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
-  // OOP case
-  mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
 
   nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
     dataChannelBuilder(do_QueryInterface(mBuilder));
   if (NS_WARN_IF(!dataChannelBuilder)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+
+  // OOP window would be set from content process
+  nsPIDOMWindowInner* window = GetWindow();
+
   nsresult rv = dataChannelBuilder->
          BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
                                    window,
                                    this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationControllingInfo::NotifyDisconnected(nsresult aReason)
 {
   PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole);
@@ -918,19 +923,28 @@ PresentationControllingInfo::OnSocketAcc
   int32_t port;
   nsresult rv = aTransport->GetPort(&port);
   if (!NS_WARN_IF(NS_FAILED(rv))) {
     PRES_DEBUG("%s:receive from port[%d]\n",__func__, port);
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
   // Initialize session transport builder and use |this| as the callback.
-  nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder =
-    do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder;
+  if (NS_SUCCEEDED(mBuilderConstructor->CreateTransportBuilder(
+                     nsIPresentationChannelDescription::TYPE_TCP,
+                     getter_AddRefs(mBuilder)))) {
+    builder = do_QueryInterface(mBuilder);
+  }
+
   if (NS_WARN_IF(!builder)) {
     return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   mTransportType = nsIPresentationChannelDescription::TYPE_TCP;
   return builder->BuildTCPSenderTransport(aTransport, this);
 }
 
@@ -1130,16 +1144,17 @@ PresentationPresentingInfo::Shutdown(nsr
   if (mDevice) {
     mDevice->Disconnect();
   }
   mDevice = nullptr;
   mLoadingCallback = nullptr;
   mRequesterDescription = nullptr;
   mPendingCandidates.Clear();
   mPromise = nullptr;
+  mHasFlushPendingEvents = false;
 }
 
 // nsIPresentationSessionTransportBuilderListener
 NS_IMETHODIMP
 PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* transport)
 {
   nsresult rv = PresentationSessionInfo::OnSessionTransport(transport);
 
@@ -1207,84 +1222,80 @@ PresentationPresentingInfo::InitTranspor
   MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CONNECTING);
 
   uint8_t type = 0;
   nsresult rv = mRequesterDescription->GetType(&type);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  if (NS_WARN_IF(!mBuilderConstructor)) {
+    return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(
+    mBuilderConstructor->CreateTransportBuilder(type,
+                                                getter_AddRefs(mBuilder))))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   if (type == nsIPresentationChannelDescription::TYPE_TCP) {
     // Establish a data transport channel |mTransport| to the sender and use
     // |this| as the callback.
     nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder =
-      do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+      do_QueryInterface(mBuilder);
     if (NS_WARN_IF(!builder)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
-    SetBuilder(builder);
     mTransportType = nsIPresentationChannelDescription::TYPE_TCP;
     return builder->BuildTCPReceiverTransport(mRequesterDescription, this);
   }
 
   if (type == nsIPresentationChannelDescription::TYPE_DATACHANNEL) {
     if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
       return NS_ERROR_NOT_IMPLEMENTED;
     }
-    nsPIDOMWindowInner* window = nullptr;
-
     /**
      * Generally transport is maintained by the chrome process. However, data
      * channel should be live with the DOM , which implies RTCDataChannel in an OOP
      * page should be establish in the content process.
      *
-     * In OOP data channel transport case, |mBuilder| is hooked when the content
-     * process is ready to build a data channel transport, trigger by:
-     * 1. PresentationIPCService::StartSession (sender)
-     * 2. PresentationIPCService::NotifyReceiverReady (receiver).
+     * |mBuilderConstructor| is responsible for creating a builder, which is for
+     * building a data channel transport.
      *
-     * In this case, |mBuilder| would be an object of |PresentationBuilderParent|
-     * and set previously. Therefore, |BuildDataChannelTransport| triggers an IPC
-     * call to make content process establish a RTCDataChannel transport.
+     * In the OOP case, |mBuilderConstructor| would create a builder which is
+     * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport|
+     * triggers an IPC call to make content process establish a RTCDataChannel
+     * transport.
      */
-    // in-process case
-    if (!mBuilder) {
-      nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> builder =
-        do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
 
-      if (NS_WARN_IF(!builder)) {
-        return NS_ERROR_NOT_AVAILABLE;
-      }
-
-      SetBuilder(builder);
-
-      // OOP window would be set from content process
-      window = GetWindow();
-    }
     mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
 
-    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
-      dataChannelBuilder(do_QueryInterface(mBuilder));
+    nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> dataChannelBuilder =
+      do_QueryInterface(mBuilder);
     if (NS_WARN_IF(!dataChannelBuilder)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
+
+    nsPIDOMWindowInner* window = GetWindow();
+
     rv = dataChannelBuilder->
            BuildDataChannelTransport(nsIPresentationService::ROLE_RECEIVER,
                                      window,
                                      this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-
-    rv = this->FlushPendingEvents(dataChannelBuilder);
+    rv = FlushPendingEvents(dataChannelBuilder);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
+
     return NS_OK;
   }
 
   MOZ_ASSERT(false, "Unknown nsIPresentationChannelDescription type!");
   return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -74,21 +74,16 @@ public:
 
   nsresult SetListener(nsIPresentationSessionListener* aListener);
 
   void SetDevice(nsIPresentationDevice* aDevice)
   {
     mDevice = aDevice;
   }
 
-  void SetBuilder(nsIPresentationSessionTransportBuilder* aBuilder)
-  {
-    mBuilder = aBuilder;
-  }
-
   already_AddRefed<nsIPresentationDevice> GetDevice() const
   {
     nsCOMPtr<nsIPresentationDevice> device = mDevice;
     return device.forget();
   }
 
   void SetControlChannel(nsIPresentationControlChannel* aControlChannel)
   {
@@ -108,16 +103,22 @@ public:
                  uint32_t aState);
 
   nsresult OnTerminate(nsIPresentationControlChannel* aControlChannel);
 
   nsresult ReplyError(nsresult aReason);
 
   virtual bool IsAccessible(base::ProcessId aProcessId);
 
+  void SetTransportBuilderConstructor(
+    nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
+  {
+    mBuilderConstructor = aBuilderConstructor;
+  }
+
 protected:
   virtual ~PresentationSessionInfo()
   {
     Shutdown(NS_OK);
   }
 
   virtual void Shutdown(nsresult aReason);
 
@@ -144,16 +145,21 @@ protected:
       DebugOnly<nsresult> rv =
         mListener->NotifyStateChange(mSessionId, mState, aReason);
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyStateChanged");
     }
   }
 
   void ContinueTermination();
 
+  void ResetBuilder()
+  {
+    mBuilder = nullptr;
+  }
+
   // Should be nsIPresentationChannelDescription::TYPE_TCP/TYPE_DATACHANNEL
   uint8_t mTransportType = 0;
 
   nsPIDOMWindowInner* GetWindow();
 
   nsString mUrl;
   nsString mSessionId;
   // mRole should be nsIPresentationService::ROLE_CONTROLLER
@@ -164,16 +170,17 @@ protected:
   bool mIsOnTerminating = false;
   uint32_t mState; // CONNECTED, CLOSED, TERMINATED
   nsresult mReason;
   nsCOMPtr<nsIPresentationSessionListener> mListener;
   nsCOMPtr<nsIPresentationDevice> mDevice;
   nsCOMPtr<nsIPresentationSessionTransport> mTransport;
   nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
   nsCOMPtr<nsIPresentationSessionTransportBuilder> mBuilder;
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
 };
 
 // Session info with controlling browsing context (sender side) behaviors.
 class PresentationControllingInfo final : public PresentationSessionInfo
                                         , public nsIServerSocketListener
                                         , public nsIListNetworkAddressesListener
 {
 public:
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationTransportBuilderConstructor.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PresentationTransportBuilderConstructor.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsIPresentationControlChannel.h"
+#include "nsIPresentationSessionTransport.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(DummyPresentationTransportBuilderConstructor,
+                  nsIPresentationTransportBuilderConstructor)
+
+NS_IMETHODIMP
+DummyPresentationTransportBuilderConstructor::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  MOZ_ASSERT(false, "Unexpected to be invoked.");
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(PresentationTransportBuilderConstructor,
+                             DummyPresentationTransportBuilderConstructor)
+
+/* static */ already_AddRefed<nsIPresentationTransportBuilderConstructor>
+PresentationTransportBuilderConstructor::Create()
+{
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor;
+  if (XRE_IsContentProcess()) {
+    constructor = new DummyPresentationTransportBuilderConstructor();
+  } else {
+    constructor = new PresentationTransportBuilderConstructor();
+  }
+
+  return constructor.forget();
+}
+
+NS_IMETHODIMP
+PresentationTransportBuilderConstructor::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  if (NS_WARN_IF(!aRetval)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aRetval = nullptr;
+
+  if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
+                 aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (XRE_IsContentProcess()) {
+    MOZ_ASSERT(false,
+               "CreateTransportBuilder can only be invoked in parent process.");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
+  if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
+    builder =
+      do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  } else {
+    builder =
+      do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
+  }
+
+  if (NS_WARN_IF(!builder)) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  builder.forget(aRetval);
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationTransportBuilderConstructor.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PresentationTransportBuilderConstructor_h
+#define mozilla_dom_PresentationTransportBuilderConstructor_h
+
+#include "nsCOMPtr.h"
+#include "nsIPresentationSessionTransportBuilder.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class DummyPresentationTransportBuilderConstructor :
+  public nsIPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  DummyPresentationTransportBuilderConstructor() = default;
+
+protected:
+  virtual ~DummyPresentationTransportBuilderConstructor() = default;
+};
+
+class PresentationTransportBuilderConstructor final :
+  public DummyPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  static already_AddRefed<nsIPresentationTransportBuilderConstructor>
+  Create();
+
+private:
+  PresentationTransportBuilderConstructor() = default;
+  virtual ~PresentationTransportBuilderConstructor() = default;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationTransportBuilderConstructor_h
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -4,16 +4,17 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMEventTarget;
 interface nsIInputStream;
 interface nsIPresentationAvailabilityListener;
 interface nsIPresentationRespondingListener;
 interface nsIPresentationSessionListener;
+interface nsIPresentationTransportBuilderConstructor;
 
 %{C++
 #define PRESENTATION_SERVICE_CID \
   { 0x1d9bb10c, 0xc0ab, 0x4fe8, \
     { 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } }
 #define PRESENTATION_SERVICE_CONTRACTID \
   "@mozilla.org/presentation/presentationservice;1"
 
@@ -67,24 +68,26 @@ interface nsIPresentationService : nsISu
    *                  located in different process from this service)
    * @param eventTarget: The chrome event handler, in particular XUL browser
    *                     element in parent process, that the request was
    *                     originated in.
    * @param callback: Invoke the callback when the operation is completed.
    *                  NotifySuccess() is called with |id| if a session is
    *                  established successfully with the selected device.
    *                  Otherwise, NotifyError() is called with a error message.
+   * @param constructor: The constructor for creating a transport builder.
    */
   [noscript] void startSession(in URLArrayRef urls,
                                in DOMString sessionId,
                                in DOMString origin,
                                in DOMString deviceId,
                                in unsigned long long windowId,
                                in nsIDOMEventTarget eventTarget,
-                               in nsIPresentationServiceCallback callback);
+                               in nsIPresentationServiceCallback callback,
+                               in nsIPresentationTransportBuilderConstructor constructor);
 
   /*
    * Send the message to the session.
    *
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param data: the message being sent out.
    */
@@ -176,20 +179,22 @@ interface nsIPresentationService : nsISu
 
   /*
    * Notify the receiver page is ready for presentation use.
    *
    * @param sessionId An ID to identify presentation session.
    * @param windowId  The inner window ID associated with the presentation
    *                  session.
    * @param isLoading true if receiver page is loading successfully.
+   * @param constructor: The constructor for creating a transport builder.
    */
   void notifyReceiverReady(in DOMString sessionId,
                            in unsigned long long windowId,
-                           in boolean isLoading);
+                           in boolean isLoading,
+                           in nsIPresentationTransportBuilderConstructor constructor);
 
   /*
    * Notify the transport is closed
    *
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param reason: the error message. NS_OK indicates it is closed normally.
    */
--- a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
+++ b/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
@@ -24,16 +24,25 @@ interface nsIPresentationSessionTranspor
 };
 
 [scriptable, uuid(2fdbe67d-80f9-48dc-8237-5bef8fa19801)]
 interface nsIPresentationSessionTransportBuilder : nsISupports
 {
 };
 
 /**
+ * The constructor for creating a transport builder.
+ */
+[scriptable, uuid(706482b2-1b51-4bed-a21d-785a9cfcfac7)]
+interface nsIPresentationTransportBuilderConstructor : nsISupports
+{
+  nsIPresentationSessionTransportBuilder createTransportBuilder(in uint8_t type);
+};
+
+/**
  * Builder for TCP session transport
  */
 [scriptable, uuid(cde36d6e-f471-4262-a70d-f932a26b21d9)]
 interface nsIPresentationTCPSessionTransportBuilder : nsIPresentationSessionTransportBuilder
 {
   /**
    * The following creation functions will trigger |listener.onSessionTransport|
    * if the session transport is successfully built, |listener.onError| if some
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -49,23 +49,25 @@ PresentationIPCService::~PresentationIPC
 
   mAvailabilityListeners.Clear();
   mSessionListeners.Clear();
   mSessionInfos.Clear();
   sPresentationChild = nullptr;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::StartSession(const nsTArray<nsString>& aUrls,
-                                     const nsAString& aSessionId,
-                                     const nsAString& aOrigin,
-                                     const nsAString& aDeviceId,
-                                     uint64_t aWindowId,
-                                     nsIDOMEventTarget* aEventTarget,
-                                     nsIPresentationServiceCallback* aCallback)
+PresentationIPCService::StartSession(
+               const nsTArray<nsString>& aUrls,
+               const nsAString& aSessionId,
+               const nsAString& aOrigin,
+               const nsAString& aDeviceId,
+               uint64_t aWindowId,
+               nsIDOMEventTarget* aEventTarget,
+               nsIPresentationServiceCallback* aCallback,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   if (aWindowId != 0) {
     AddRespondingSessionId(aWindowId,
                            aSessionId,
                            nsIPresentationService::ROLE_CONTROLLER);
   }
 
   nsPIDOMWindowInner* window =
@@ -373,19 +375,21 @@ PresentationIPCService::NotifyAvailableC
     nsIPresentationAvailabilityListener* listener = iter.GetNext();
     Unused << NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aAvailable)));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId,
-                                            uint64_t aWindowId,
-                                            bool aIsLoading)
+PresentationIPCService::NotifyReceiverReady(
+               const nsAString& aSessionId,
+               uint64_t aWindowId,
+               bool aIsLoading,
+               nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // No actual window uses 0 as its ID.
   if (NS_WARN_IF(aWindowId == 0)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -4,23 +4,88 @@
  * 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 "DCPresentationChannelDescription.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/Unused.h"
 #include "nsIPresentationDeviceManager.h"
+#include "nsIPresentationSessionTransport.h"
+#include "nsIPresentationSessionTransportBuilder.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationBuilderParent.h"
 #include "PresentationParent.h"
 #include "PresentationService.h"
 #include "PresentationSessionInfo.h"
 
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+class PresentationTransportBuilderConstructorIPC final :
+  public nsIPresentationTransportBuilderConstructor
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
+
+  explicit PresentationTransportBuilderConstructorIPC(PresentationParent* aParent)
+    : mParent(aParent)
+  {
+  }
+
+private:
+  virtual ~PresentationTransportBuilderConstructorIPC() = default;
+
+  RefPtr<PresentationParent> mParent;
+};
+
+NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,
+                  nsIPresentationTransportBuilderConstructor)
+
+NS_IMETHODIMP
+PresentationTransportBuilderConstructorIPC::CreateTransportBuilder(
+                              uint8_t aType,
+                              nsIPresentationSessionTransportBuilder** aRetval)
+{
+  if (NS_WARN_IF(!aRetval)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aRetval = nullptr;
+
+  if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
+                 aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (XRE_IsContentProcess()) {
+    MOZ_ASSERT(false,
+               "CreateTransportBuilder can only be invoked in parent process.");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
+  if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
+    builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
+  } else {
+    builder = new PresentationBuilderParent(mParent);
+  }
+
+  if (NS_WARN_IF(!builder)) {
+    return NS_ERROR_DOM_OPERATION_ERR;
+  }
+
+  builder.forget(aRetval);
+  return NS_OK;
+}
+
+} // anonymous namespace
 
 /*
  * Implementation of PresentationParent
  */
 
 NS_IMPL_ISUPPORTS(PresentationParent,
                   nsIPresentationAvailabilityListener,
                   nsIPresentationSessionListener,
@@ -212,29 +277,16 @@ PresentationParent::RecvRegisterRespondi
 PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId)
 {
   MOZ_ASSERT(mService);
   mWindowIds.RemoveElement(aWindowId);
   Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
   return true;
 }
 
-bool
-PresentationParent::RegisterTransportBuilder(const nsString& aSessionId,
-                                             const uint8_t& aRole)
-{
-  MOZ_ASSERT(mService);
-
-  nsCOMPtr<nsIPresentationSessionTransportBuilder> builder =
-    new PresentationBuilderParent(this);
-  Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationService*>(mService.get())->
-                         RegisterTransportBuilder(aSessionId, aRole, builder)));
-  return true;
-}
-
 NS_IMETHODIMP
 PresentationParent::NotifyAvailableChange(bool aAvailable)
 {
   if (NS_WARN_IF(mActorDestroyed || !SendNotifyAvailableChange(aAvailable))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
@@ -285,20 +337,22 @@ PresentationParent::NotifySessionConnect
 
 bool
 PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId,
                                             const uint64_t& aWindowId,
                                             const bool& aIsLoading)
 {
   MOZ_ASSERT(mService);
 
-  RegisterTransportBuilder(aSessionId, nsIPresentationService::ROLE_RECEIVER);
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    new PresentationTransportBuilderConstructorIPC(this);
   Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId,
                                                                aWindowId,
-                                                               aIsLoading)));
+                                                               aIsLoading,
+                                                               constructor)));
   return true;
 }
 
 bool
 PresentationParent::RecvNotifyTransportClosed(const nsString& aSessionId,
                                               const uint8_t& aRole,
                                               const nsresult& aReason)
 {
@@ -333,30 +387,33 @@ PresentationRequestParent::ActorDestroy(
   mActorDestroyed = true;
   mService = nullptr;
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const StartSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
-  mNeedRegisterBuilder = true;
+
   mSessionId = aRequest.sessionId();
 
   nsCOMPtr<nsIDOMEventTarget> eventTarget;
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   RefPtr<TabParent> tp =
     cpm->GetTopLevelTabParentByProcessAndTabId(mChildId, aRequest.tabId());
   if (tp) {
     eventTarget = do_QueryInterface(tp->GetOwnerElement());
   }
-  
+
+  RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
+  nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
+    new PresentationTransportBuilderConstructorIPC(parent);
   return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
                                 aRequest.origin(), aRequest.deviceId(),
-                                aRequest.windowId(), eventTarget, this);
+                                aRequest.windowId(), eventTarget, this, constructor);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
@@ -425,17 +482,16 @@ PresentationRequestParent::DoRequest(con
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
     IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
 
     // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
     // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
     return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
-  mNeedRegisterBuilder = true;
   mSessionId = aRequest.sessionId();
   return mService->ReconnectSession(aRequest.urls(),
                                     aRequest.sessionId(),
                                     aRequest.role(),
                                     this);
 }
 
 nsresult
@@ -455,23 +511,16 @@ PresentationRequestParent::DoRequest(con
     return SendResponse(rv);
   }
   return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifySuccess(const nsAString& aUrl)
 {
-  if (mNeedRegisterBuilder) {
-    RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
-    Unused << NS_WARN_IF(!parent->RegisterTransportBuilder(
-                                      mSessionId,
-                                      nsIPresentationService::ROLE_CONTROLLER));
-  }
-
   Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
   return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifyError(nsresult aError)
 {
   return SendResponse(aError);
@@ -481,8 +530,11 @@ nsresult
 PresentationRequestParent::SendResponse(nsresult aResult)
 {
   if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/presentation/moz.build
+++ b/dom/presentation/moz.build
@@ -50,16 +50,17 @@ UNIFIED_SOURCES += [
     'PresentationReceiver.cpp',
     'PresentationRequest.cpp',
     'PresentationService.cpp',
     'PresentationServiceBase.cpp',
     'PresentationSessionInfo.cpp',
     'PresentationSessionRequest.cpp',
     'PresentationTCPSessionTransport.cpp',
     'PresentationTerminateRequest.cpp',
+    'PresentationTransportBuilderConstructor.cpp'
 ]
 
 EXTRA_COMPONENTS += [
     'PresentationDataChannelSessionTransport.js',
     'PresentationDataChannelSessionTransport.manifest',
     'PresentationDeviceInfoManager.js',
     'PresentationDeviceInfoManager.manifest',
 ]
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -215,16 +215,17 @@ const mockedDevicePrompt = {
   },
   simulateCancel: function(result) {
     this._request.cancel(result);
   }
 };
 
 const mockedSessionTransport = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport,
+                                         Ci.nsIPresentationSessionTransportBuilder,
                                          Ci.nsIPresentationTCPSessionTransportBuilder,
                                          Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsIPresentationControlChannelListener,
                                          Ci.nsIFactory]),
   createInstance: function(aOuter, aIID) {
     if (aOuter) {
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     }
--- a/dom/webidl/AnonymousContent.webidl
+++ b/dom/webidl/AnonymousContent.webidl
@@ -62,9 +62,19 @@ interface AnonymousContent {
   nsISupports? getCanvasContext(DOMString elementId,
                                 DOMString contextId);
 
   [Func="nsDocument::IsElementAnimateEnabled", Throws]
   Animation setAnimationForElement(DOMString elementId,
                                    object? keyframes,
                                    optional UnrestrictedDoubleOrKeyframeAnimationOptions
                                      options);
+
+  /**
+   * Accepts a list of (possibly overlapping) DOMRects which describe a shape
+   * in CSS pixels relative to the element's border box. This shape will be
+   * excluded from the element's background color rendering. The element will
+   * not render any background images once this method has been called.
+   */
+  [Throws]
+  void setCutoutRectsForElement(DOMString elementId,
+                                sequence<DOMRect> rects);
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -989,17 +989,17 @@ InitJSContextForWorker(WorkerPrivate* aW
 
   JS::SetAsyncTaskCallbacks(aWorkerCx, StartAsyncTaskCallback, FinishAsyncTaskCallback);
 
   if (!JS::InitSelfHostedCode(aWorkerCx)) {
     NS_WARNING("Could not init self-hosted code!");
     return false;
   }
 
-  JS_SetInterruptCallback(aWorkerCx, InterruptCallback);
+  JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
 
   js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
 
 #ifdef JS_GC_ZEAL
   JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
 #endif
 
   return true;
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -25,16 +25,18 @@
 #include "mozilla/RefPtr.h"
 
 #include "mozilla/DebugOnly.h"
 
 #ifdef MOZ_ENABLE_FREETYPE
 #include <string>
 #endif
 
+#include "gfxPrefs.h"
+
 struct _cairo_surface;
 typedef _cairo_surface cairo_surface_t;
 
 struct _cairo_scaled_font;
 typedef _cairo_scaled_font cairo_scaled_font_t;
 
 struct _FcPattern;
 typedef _FcPattern FcPattern;
@@ -667,17 +669,23 @@ class ScaledFont : public RefCounted<Sca
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
   virtual ~ScaledFont() {}
 
   typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void *aBaton);
   typedef void (*FontDescriptorOutput)(const uint8_t *aData, uint32_t aLength, Float aFontSize, void *aBaton);
 
   virtual FontType GetType() const = 0;
-  virtual AntialiasMode GetDefaultAAMode() { return AntialiasMode::DEFAULT; }
+  virtual AntialiasMode GetDefaultAAMode() {
+    if (gfxPrefs::DisableAllTextAA()) {
+      return AntialiasMode::NONE;
+    }
+
+    return AntialiasMode::DEFAULT;
+  }
 
   /** This allows getting a path that describes the outline of a set of glyphs.
    * A target is passed in so that the guarantee is made the returned path
    * can be used with any DrawTarget that has the same backend as the one
    * passed in.
    */
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
 
@@ -1477,17 +1485,18 @@ public:
   static uint64_t GetD2DVRAMUsageSourceSurface();
   static void D2DCleanup();
 
   static already_AddRefed<ScaledFont>
     CreateScaledFontForDWriteFont(IDWriteFont* aFont,
                                   IDWriteFontFamily* aFontFamily,
                                   IDWriteFontFace* aFontFace,
                                   Float aSize,
-                                  bool aUseEmbeddedBitmap);
+                                  bool aUseEmbeddedBitmap,
+                                  bool aForceGDIMode);
 
 private:
   static ID2D1Device *mD2D1Device;
   static ID3D11Device *mD3D11Device;
 #endif
 
   static DrawEventRecorder *mRecorder;
 };
--- a/gfx/2d/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -555,20 +555,20 @@ struct BaseRect {
   // signed integers, centered around a point at 0,0.  As BaseRect's represent
   // the dimensions as a top-left point with a width and height, the width
   // and height will be the largest positive 32-bit value.  The top-left
   // position coordinate is divided by two to center the rectangle around a
   // point at 0,0.
   static Sub MaxIntRect()
   {
     return Sub(
-      -std::numeric_limits<int32_t>::max() * 0.5,
-      -std::numeric_limits<int32_t>::max() * 0.5,
-      std::numeric_limits<int32_t>::max(),
-      std::numeric_limits<int32_t>::max()
+      static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+      static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
+      static_cast<T>(std::numeric_limits<int32_t>::max()),
+      static_cast<T>(std::numeric_limits<int32_t>::max())
     );
   };
 
   friend std::ostream& operator<<(std::ostream& stream,
       const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
     return stream << '(' << aRect.x << ',' << aRect.y << ','
                   << aRect.width << ',' << aRect.height << ')';
   }
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -505,23 +505,23 @@ DrawTargetSkia::DrawSurface(SourceSurfac
                             const Rect &aSource,
                             const DrawSurfaceOptions &aSurfOptions,
                             const DrawOptions &aOptions)
 {
   if (aSource.IsEmpty()) {
     return;
   }
 
+  MarkChanged();
+
   SkBitmap bitmap = GetBitmapForSurface(aSurface);
   if (bitmap.empty()) {
     return;
   }
 
-  MarkChanged();
-
   SkRect destRect = RectToSkRect(aDest);
   SkRect sourceRect = RectToSkRect(aSource);
   bool forceGroup = bitmap.colorType() == kAlpha_8_SkColorType &&
                     aOptions.mCompositionOp != CompositionOp::OP_OVER;
 
   AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest, forceGroup);
   if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
     paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
@@ -558,23 +558,23 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
                                       const Point &aOffset,
                                       Float aSigma,
                                       CompositionOp aOperator)
 {
   if (aSurface->GetSize().IsEmpty()) {
     return;
   }
 
+  MarkChanged();
+
   SkBitmap bitmap = GetBitmapForSurface(aSurface);
   if (bitmap.empty()) {
     return;
   }
 
-  MarkChanged();
-
   mCanvas->save();
   mCanvas->resetMatrix();
 
   SkPaint paint;
   paint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
 
   // bug 1201272
   // We can't use the SkDropShadowImageFilter here because it applies the xfer
@@ -1282,29 +1282,34 @@ DrawTargetSkia::FillGlyphs(ScaledFont *a
       // disabling the LCD font smoothing behaviour.
       // To accomplish this we have to explicitly disable hinting,
       // and disable LCDRenderText.
       paint.mPaint.setHinting(SkPaint::kNo_Hinting);
     }
     break;
   case FontType::GDI:
   {
-    if (!shouldLCDRenderText) {
+    if (!shouldLCDRenderText && aaEnabled) {
       // If we have non LCD GDI text, render the fonts as cleartype and convert them
       // to grayscale. This seems to be what Chrome and IE are doing on Windows 7.
       // This also applies if cleartype is disabled system wide.
       paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag);
     }
     break;
   }
 #ifdef XP_WIN
   case FontType::DWRITE:
   {
     ScaledFontDWrite* dwriteFont = static_cast<ScaledFontDWrite*>(aFont);
     paint.mPaint.setEmbeddedBitmapText(dwriteFont->UseEmbeddedBitmaps());
+
+    if (dwriteFont->ForceGDIMode()) {
+      paint.mPaint.setEmbeddedBitmapText(true);
+      useSubpixelText = false;
+    }
     break;
   }
 #endif
   default:
     break;
   }
 
   paint.mPaint.setSubpixelText(useSubpixelText);
@@ -1434,23 +1439,23 @@ DrawTarget::Draw3DTransformedSurface(Sou
 
 bool
 DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
 {
   if (aMatrix.IsSingular()) {
     return false;
   }
 
+  MarkChanged();
+
   SkBitmap bitmap = GetBitmapForSurface(aSurface);
   if (bitmap.empty()) {
     return true;
   }
 
-  MarkChanged();
-
   mCanvas->save();
 
   SkPaint paint;
   paint.setAntiAlias(true);
   paint.setFilterQuality(kLow_SkFilterQuality);
 
   SkMatrix xform;
   GfxMatrixToSkiaMatrix(aMatrix, xform);
@@ -1627,23 +1632,23 @@ DrawTargetSkia::CreateSourceSurfaceFromN
   return nullptr;
 }
 
 void
 DrawTargetSkia::CopySurface(SourceSurface *aSurface,
                             const IntRect& aSourceRect,
                             const IntPoint &aDestination)
 {
+  MarkChanged();
+
   SkBitmap bitmap = GetBitmapForSurface(aSurface);
   if (bitmap.empty()) {
     return;
   }
 
-  MarkChanged();
-
   mCanvas->save();
   mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x), SkIntToScalar(aDestination.y)));
   mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.width, aSourceRect.height), SkRegion::kReplace_Op);
 
   SkPaint paint;
   if (!bitmap.isOpaque()) {
     // Keep the xfermode as SOURCE_OVER for opaque bitmaps
     // http://code.google.com/p/skia/issues/detail?id=628
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -704,19 +704,21 @@ Factory::D2DCleanup()
   DrawTargetD2D1::CleanupD2D();
 }
 
 already_AddRefed<ScaledFont>
 Factory::CreateScaledFontForDWriteFont(IDWriteFont* aFont,
                                        IDWriteFontFamily* aFontFamily,
                                        IDWriteFontFace* aFontFace,
                                        float aSize,
-                                       bool aUseEmbeddedBitmap)
+                                       bool aUseEmbeddedBitmap,
+                                       bool aForceGDIMode)
 {
-  return MakeAndAddRef<ScaledFontDWrite>(aFont, aFontFamily, aFontFace, aSize, aUseEmbeddedBitmap);
+  return MakeAndAddRef<ScaledFontDWrite>(aFont, aFontFamily, aFontFace,
+                                         aSize, aUseEmbeddedBitmap, aForceGDIMode);
 }
 
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 already_AddRefed<DrawTarget>
 Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
                                            const IntSize &aSize,
--- a/gfx/2d/HelpersWinFonts.h
+++ b/gfx/2d/HelpersWinFonts.h
@@ -33,16 +33,19 @@ GetSystemTextQuality()
 
   return DEFAULT_QUALITY;
 }
 
 static AntialiasMode
 GetSystemDefaultAAMode()
 {
   AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
+  if (gfxPrefs::DisableAllTextAA()) {
+    return AntialiasMode::NONE;
+  }
 
   switch (GetSystemTextQuality()) {
   case CLEARTYPE_QUALITY:
     defaultMode = AntialiasMode::SUBPIXEL;
     break;
   case ANTIALIASED_QUALITY:
     defaultMode = AntialiasMode::GRAY;
     break;
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -16,16 +16,18 @@
 #include <cmath>
 
 namespace mozilla {
 
 template <typename> struct IsPixel;
 
 namespace gfx {
 
+template<class units, class F> struct RectTyped;
+
 template<class units>
 struct IntMarginTyped:
     public BaseMargin<int32_t, IntMarginTyped<units> >,
     public units {
     static_assert(IsPixel<units>::value,
                   "'units' must be a coordinate system tag");
 
     typedef BaseMargin<int32_t, IntMarginTyped<units> > Super;
@@ -81,22 +83,66 @@ template<class units>
 struct IntRectTyped :
     public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> >,
     public units {
     static_assert(IsPixel<units>::value,
                   "'units' must be a coordinate system tag");
 
     typedef BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>, IntSizeTyped<units>, IntMarginTyped<units> > Super;
     typedef IntRectTyped<units> Self;
+    typedef IntParam<int32_t> ToInt;
 
     IntRectTyped() : Super() {}
     IntRectTyped(const IntPointTyped<units>& aPos, const IntSizeTyped<units>& aSize) :
         Super(aPos, aSize) {}
-    IntRectTyped(int32_t _x, int32_t _y, int32_t _width, int32_t _height) :
-        Super(_x, _y, _width, _height) {}
+
+    IntRectTyped(ToInt aX, ToInt aY, ToInt aWidth, ToInt aHeight) :
+        Super(aX.value, aY.value, aWidth.value, aHeight.value) {}
+
+    static IntRectTyped<units> RoundIn(float aX, float aY, float aW, float aH) {
+      return IntRectTyped<units>::RoundIn(RectTyped<units, float>(aX, aY, aW, aH));
+    }
+
+    static IntRectTyped<units> RoundOut(float aX, float aY, float aW, float aH) {
+      return IntRectTyped<units>::RoundOut(RectTyped<units, float>(aX, aY, aW, aH));
+    }
+
+    static IntRectTyped<units> Round(float aX, float aY, float aW, float aH) {
+      return IntRectTyped<units>::Round(RectTyped<units, float>(aX, aY, aW, aH));
+    }
+
+    static IntRectTyped<units> Truncate(float aX, float aY, float aW, float aH) {
+      return IntRectTyped<units>(IntPointTyped<units>::Truncate(aX, aY),
+                                 IntSizeTyped<units>::Truncate(aW, aH));
+    }
+
+    static IntRectTyped<units> RoundIn(const RectTyped<units, float>& aRect) {
+      auto tmp(aRect);
+      tmp.RoundIn();
+      return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+                          int32_t(tmp.width), int32_t(tmp.height));
+    }
+
+    static IntRectTyped<units> RoundOut(const RectTyped<units, float>& aRect) {
+      auto tmp(aRect);
+      tmp.RoundOut();
+      return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+                          int32_t(tmp.width), int32_t(tmp.height));
+    }
+
+    static IntRectTyped<units> Round(const RectTyped<units, float>& aRect) {
+      auto tmp(aRect);
+      tmp.Round();
+      return IntRectTyped(int32_t(tmp.x), int32_t(tmp.y),
+                          int32_t(tmp.width), int32_t(tmp.height));
+    }
+
+    static IntRectTyped<units> Truncate(const RectTyped<units, float>& aRect) {
+      return IntRectTyped::Truncate(aRect.x, aRect.y, aRect.width, aRect.height);
+    }
 
     // Rounding isn't meaningful on an integer rectangle.
     void Round() {}
     void RoundIn() {}
     void RoundOut() {}
 
     // XXX When all of the code is ported, the following functions to convert
     // to and from unknown types should be removed.
@@ -243,41 +289,28 @@ IntRectTyped<units> RoundedToInt(const R
                              int32_t(copy.y),
                              int32_t(copy.width),
                              int32_t(copy.height));
 }
 
 template<class units>
 IntRectTyped<units> RoundedIn(const RectTyped<units>& aRect)
 {
-  RectTyped<units> copy(aRect);
-  copy.RoundIn();
-  return IntRectTyped<units>(int32_t(copy.x),
-                             int32_t(copy.y),
-                             int32_t(copy.width),
-                             int32_t(copy.height));
+  return IntRectTyped<units>::RoundIn(aRect);
 }
 
 template<class units>
 IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect)
 {
-  RectTyped<units> copy(aRect);
-  copy.RoundOut();
-  return IntRectTyped<units>(int32_t(copy.x),
-                             int32_t(copy.y),
-                             int32_t(copy.width),
-                             int32_t(copy.height));
+  return IntRectTyped<units>::RoundOut(aRect);
 }
 
 template<class units>
 IntRectTyped<units> TruncatedToInt(const RectTyped<units>& aRect) {
-  return IntRectTyped<units>(int32_t(aRect.x),
-                             int32_t(aRect.y),
-                             int32_t(aRect.width),
-                             int32_t(aRect.height));
+  return IntRectTyped<units>::Truncate(aRect);
 }
 
 template<class units>
 RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
 {
   return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -19,53 +19,58 @@ class ScaledFontDWrite final : public Sc
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
   ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
     : ScaledFontBase(aSize)
     , mFont(nullptr)
     , mFontFamily(nullptr)
     , mFontFace(aFont)
     , mUseEmbeddedBitmap(false)
+    , mForceGDIMode(false)
   {}
 
   ScaledFontDWrite(IDWriteFont* aFont, IDWriteFontFamily* aFontFamily,
-                   IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap)
+                   IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap,
+                   bool aForceGDIMode)
     : ScaledFontBase(aSize)
     , mFont(aFont)
     , mFontFamily(aFontFamily)
     , mFontFace(aFontFace)
     , mUseEmbeddedBitmap(aUseEmbeddedBitmap)
+    , mForceGDIMode(aForceGDIMode)
   {}
 
   virtual FontType GetType() const { return FontType::DWRITE; }
 
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode() override;
 
   bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
+  bool ForceGDIMode() { return mForceGDIMode; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
   bool GetFontDataFromSystemFonts(IDWriteFactory* aFactory);
   bool DefaultToArialFont(IDWriteFontCollection* aSystemFonts);
 #endif
 
   // The font and font family are only used with Skia
   RefPtr<IDWriteFont> mFont;
   RefPtr<IDWriteFontFamily> mFontFamily;
   RefPtr<IDWriteFontFace> mFontFace;
   bool mUseEmbeddedBitmap;
+  bool mForceGDIMode;
 
 protected:
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_font_face_t* GetCairoFontFace() override;
 #endif
 };
 
 class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -553,34 +553,16 @@ GPUProcessManager::DeallocateLayerTreeId
   if (mGPUChild) {
     mGPUChild->SendDeallocateLayerTreeId(aLayersId);
     return;
   }
   CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
 }
 
 void
-GPUProcessManager::RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
-{
-  CompositorBridgeParent::RequestNotifyLayerTreeReady(aLayersId, aObserver);
-}
-
-void
-GPUProcessManager::RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
-{
-  CompositorBridgeParent::RequestNotifyLayerTreeCleared(aLayersId, aObserver);
-}
-
-void
-GPUProcessManager::SwapLayerTreeObservers(uint64_t aLayer, uint64_t aOtherLayer)
-{
-  CompositorBridgeParent::SwapLayerTreeObservers(aLayer, aOtherLayer);
-}
-
-void
 GPUProcessManager::EnsureVsyncIOThread()
 {
   if (mVsyncIOThread) {
     return;
   }
 
   mVsyncIOThread = new VsyncIOThreadHolder();
   MOZ_RELEASE_ASSERT(mVsyncIOThread->Start());
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -106,20 +106,16 @@ public:
   // Must run on the content main thread.
   uint64_t AllocateLayerTreeId();
 
   // Release compositor-thread resources referred to by |aID|.
   //
   // Must run on the content main thread.
   void DeallocateLayerTreeId(uint64_t aLayersId);
 
-  void RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
-  void RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
-  void SwapLayerTreeObservers(uint64_t aLayer, uint64_t aOtherLayer);
-
   void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
   void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
 
   // Notify the GPUProcessManager that a top-level PGPU protocol has been
   // terminated. This may be called from any thread.
   void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
 
   // Returns access to the PGPU protocol if a GPU process is present.
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -2538,15 +2538,13 @@ SetAntialiasingFlags(Layer* aLayer, Draw
   permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
                       aTarget->GetOpaqueRect().Contains(intTransformedBounds);
   aTarget->SetPermitSubpixelAA(permitSubpixelAA);
 }
 
 IntRect
 ToOutsideIntRect(const gfxRect &aRect)
 {
-  gfxRect r = aRect;
-  r.RoundOut();
-  return IntRect(r.X(), r.Y(), r.Width(), r.Height());
+  return IntRect::RoundOut(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -51,16 +51,17 @@ skip-if = (os == 'android') || (os == 'b
 [test_frame_reconstruction.html]
 [test_interrupted_reflow.html]
 [test_group_touchevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
 skip-if = (toolkit == 'windows')
 [test_group_wheelevents.html]
 skip-if = (toolkit == 'android') # wheel events not supported on mobile
 [test_group_mouseevents.html]
+skip-if = (toolkit == 'android') # mouse events not supported on mobile
 [test_touch_listeners_impacting_wheel.html]
 skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
 [test_bug1253683.html]
 skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile
 [test_group_zoom.html]
 skip-if = (toolkit != 'android') # only android supports zoom
 [test_bug1285070.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -204,19 +204,17 @@ BasicLayerManager::PopGroupForLayer(Push
   }
 
   group.mFinalTarget->Restore();
 }
 
 static IntRect
 ToInsideIntRect(const gfxRect& aRect)
 {
-  gfxRect r = aRect;
-  r.RoundIn();
-  return IntRect(r.X(), r.Y(), r.Width(), r.Height());
+  return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
 }
 
 // A context helper for BasicLayerManager::PaintLayer() that holds all the
 // painting context together in a data structure so it can be easily passed
 // around. It also uses ensures that the Transform and Opaque rect are restored
 // to their former state on destruction.
 
 class PaintLayerContext {
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -31,20 +31,19 @@ namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 static nsIntRegion
 IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
 {
   gfxRect clip = aContext->GetClipExtents();
-  clip.RoundOut();
-  IntRect r(clip.X(), clip.Y(), clip.Width(), clip.Height());
   nsIntRegion result;
-  result.And(aRegion, r);
+  result.And(aRegion, IntRect::RoundOut(clip.X(), clip.Y(),
+                                        clip.Width(), clip.Height()));
   return result;
 }
 
 void
 BasicPaintedLayer::PaintThebes(gfxContext* aContext,
                               Layer* aMaskLayer,
                               LayerManager::DrawPaintedLayerCallback aCallback,
                               void* aCallbackData)
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -813,16 +813,22 @@ ClientLayerManager::AsyncPanZoomEnabled(
 
 void
 ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
 {
   mForwarder->SetPaintSyncId(aSyncId);
 }
 
 void
+ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+  mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+void
 ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
 {
   if (!mDidCompositeObservers.Contains(aObserver)) {
     mDidCompositeObservers.AppendElement(aObserver);
   }
 }
 
 void
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -217,16 +217,18 @@ public:
   void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
 
   float RequestProperty(const nsAString& aProperty) override;
 
   bool AsyncPanZoomEnabled() const override;
 
   void SetNextPaintSyncId(int32_t aSyncId);
 
+  void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
   class DidCompositeObserver {
   public:
     virtual void DidComposite() = 0;
   };
 
   void AddDidCompositeObserver(DidCompositeObserver* aObserver);
   void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1108,19 +1108,17 @@ ClientMultiTiledLayerBuffer::ValidateTil
     const IntRect& dirtyRect = iter.Get();
     gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
                        dirtyRect.y - aTileOrigin.y,
                        dirtyRect.width,
                        dirtyRect.height);
     drawRect.Scale(mResolution);
 
     // Mark the newly updated area as invalid in the front buffer
-    aTile.mInvalidFront.Or(aTile.mInvalidFront,
-      IntRect(NS_lroundf(drawRect.x), NS_lroundf(drawRect.y),
-                drawRect.width, drawRect.height));
+    aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
       dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
       dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
     } else if (content == gfxContentType::COLOR_ALPHA) {
       dt->ClearRect(drawRect);
     }
   }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1190,29 +1190,29 @@ LayerManagerComposite::RenderToPresentat
   Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
 
   mRoot->ComputeEffectiveTransforms(matrix);
   nsIntRegion opaque;
   LayerIntRegion visible;
   PostProcessLayers(mRoot, opaque, visible, Nothing());
 
   nsIntRegion invalid;
-  IntRect bounds(0, 0, scale * pageWidth, actualHeight);
+  IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight);
   IntRect rect, actualBounds;
   MOZ_ASSERT(mRoot->GetOpacity() == 1);
   mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds);
 
   // The Java side of Fennec sets a scissor rect that accounts for
   // chrome such as the URL bar. Override that so that the entire frame buffer
   // is cleared.
   ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
-  const IntRect clipRect = IntRect(0, 0, actualWidth, actualHeight);
+  const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
 
   RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
   RootLayer()->RenderLayer(clipRect);
 
   mCompositor->EndFrame();
 #ifdef MOZ_WIDGET_GONK
   mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame()
 
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -12,22 +12,16 @@
 #include "mozilla/GfxMessageUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "gfxWindowsPlatform.h"
 #include "d3d9.h"
 #include <vector>
 #include "DeviceManagerD3D9.h"
 
 namespace mozilla {
-namespace gfxs {
-class DrawTarget;
-}
-}
-
-namespace mozilla {
 namespace layers {
 
 class CompositorD3D9;
 
 class TextureSourceD3D9
 {
   friend class DeviceManagerD3D9;
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -20,16 +20,17 @@
 #include "TreeTraversal.h"              // for ForEachNode
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"             // for gfxPlatform
 #endif
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/AutoRestore.h"        // for AutoRestore
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/DebugOnly.h"          // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/gfx/2D.h"          // for DrawTarget
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"          // for IntSize
 #include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZCTreeManagerParent.h"  // for APZCTreeManagerParent
@@ -1936,29 +1937,16 @@ CompositorBridgeParent::DeallocateLayerT
   // Checking the elements of sIndirectLayerTrees exist or not before using.
   if (!CompositorLoop()) {
     gfxCriticalError() << "Attempting to post to a invalid Compositor Loop";
     return;
   }
   CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId));
 }
 
-/* static */ void
-CompositorBridgeParent::SwapLayerTreeObservers(uint64_t aLayerId, uint64_t aOtherLayerId)
-{
-  EnsureLayerTreeMapReady();
-  MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  NS_ASSERTION(sIndirectLayerTrees.find(aLayerId) != sIndirectLayerTrees.end(),
-    "SwapLayerTrees missing layer 1");
-  NS_ASSERTION(sIndirectLayerTrees.find(aOtherLayerId) != sIndirectLayerTrees.end(),
-    "SwapLayerTrees missing layer 2");
-  std::swap(sIndirectLayerTrees[aLayerId].mLayerTreeReadyObserver,
-    sIndirectLayerTrees[aOtherLayerId].mLayerTreeReadyObserver);
-}
-
 static void
 UpdateControllerForLayersId(uint64_t aLayersId,
                             GeckoContentController* aController)
 {
   // Adopt ref given to us by SetControllerForLayerTree()
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   sIndirectLayerTrees[aLayersId].mController =
     already_AddRefed<GeckoContentController>(aController);
@@ -2025,32 +2013,16 @@ CompositorBridgeParent::PostInsertVsyncP
 {
   // Called in the vsync thread
   if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
     CompositorLoop()->PostTask(
       NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
   }
 }
 
-/* static */ void
-CompositorBridgeParent::RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
-{
-  EnsureLayerTreeMapReady();
-  MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  sIndirectLayerTrees[aLayersId].mLayerTreeReadyObserver = aObserver;
-}
-
-/* static */ void
-CompositorBridgeParent::RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
-{
-  EnsureLayerTreeMapReady();
-  MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  sIndirectLayerTrees[aLayersId].mLayerTreeClearedObserver = aObserver;
-}
-
 widget::PCompositorWidgetParent*
 CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData)
 {
 #if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
   if (mWidget) {
     // Should not create two widgets on the same compositor.
     return nullptr;
   }
@@ -2693,20 +2665,18 @@ CrossProcessCompositorBridgeParent::Shad
       aPaintSequenceNumber, aIsRepeatTransaction, aHitTestUpdate);
 
   // Send the 'remote paint ready' message to the content thread if it has already asked.
   if(mNotifyAfterRemotePaint)  {
     Unused << SendRemotePaintIsReady();
     mNotifyAfterRemotePaint = false;
   }
 
-  if (state->mLayerTreeReadyObserver) {
-    RefPtr<CompositorUpdateObserver> observer = state->mLayerTreeReadyObserver;
-    state->mLayerTreeReadyObserver = nullptr;
-    observer->ObserveUpdate(id, true);
+  if (aLayerTree->ShouldParentObserveEpoch()) {
+    dom::TabParent::ObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), true);
   }
 
   aLayerTree->SetPendingTransactionId(aTransactionId);
 }
 
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 //#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__);
 //                         printf_stderr(__VA_ARGS__);
@@ -2925,25 +2895,17 @@ CrossProcessCompositorBridgeParent::Forc
 }
 
 void
 CrossProcessCompositorBridgeParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
 
-  RefPtr<CompositorUpdateObserver> observer;
-  { // scope lock
-    MonitorAutoLock lock(*sIndirectLayerTreesLock);
-    observer = sIndirectLayerTrees[id].mLayerTreeClearedObserver;
-    sIndirectLayerTrees[id].mLayerTreeClearedObserver = nullptr;
-  }
-  if (observer) {
-    observer->ObserveUpdate(id, false);
-  }
+  dom::TabParent::ObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), false);
 }
 
 bool
 CrossProcessCompositorBridgeParent::SetTestSampleTime(
   LayerTransactionParent* aLayerTree, const TimeStamp& aTime)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -186,27 +186,16 @@ private:
 #if ANDROID_VERSION >= 19
   bool mDisplayEnabled;
   mozilla::Monitor mSetDisplayMonitor;
   RefPtr<CancelableRunnable> mSetDisplayTask;
 #endif
 #endif
 };
 
-class CompositorUpdateObserver
-{
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorUpdateObserver);
-
-  virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) = 0;
-
-protected:
-  virtual ~CompositorUpdateObserver() {}
-};
-
 class CompositorBridgeParentBase : public PCompositorBridgeParent,
                                    public HostIPCAllocator,
                                    public ShmemAllocator
 {
 public:
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& aTargetConfig,
@@ -459,18 +448,16 @@ public:
     // their FrameMetrics with the corresponding child process that holds
     // the PCompositorBridgeChild
     CrossProcessCompositorBridgeParent* mCrossProcessParent;
     TargetConfig mTargetConfig;
     APZTestData mApzTestData;
     LayerTransactionParent* mLayerTree;
     nsTArray<PluginWindowData> mPluginData;
     bool mUpdatedPluginDataAvailable;
-    RefPtr<CompositorUpdateObserver> mLayerTreeReadyObserver;
-    RefPtr<CompositorUpdateObserver> mLayerTreeClearedObserver;
 
     // Number of times the compositor has been reset without having been
     // acknowledged by the child.
     uint32_t mPendingCompositorUpdates;
 
     PCompositorBridgeParent* CrossProcessPCompositorBridge() const;
   };
 
@@ -544,20 +531,16 @@ private:
 
   /**
    * Release compositor-thread resources referred to by |aID|.
    *
    * Must run on the content main thread.
    */
   static void DeallocateLayerTreeId(uint64_t aId);
 
-  static void RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
-  static void RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
-  static void SwapLayerTreeObservers(uint64_t aLayer, uint64_t aOtherLayer);
-
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeParent();
 
   void DeferredDestroy();
 
   virtual PLayerTransactionParent*
     AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -64,16 +64,17 @@ namespace {
 class ImageBridgeThread : public Thread {
 public:
 
   ImageBridgeThread() : Thread("ImageBridgeChild") {
   }
 
 protected:
 
+  MOZ_IS_CLASS_INIT
   void Init() {
 #ifdef MOZ_ENABLE_PROFILER_SPS
     mPseudoStackHack = mozilla_get_pseudo_stack();
 #endif
   }
 
   void CleanUp() {
 #ifdef MOZ_ENABLE_PROFILER_SPS
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -144,16 +144,18 @@ ShadowChild(const OpRaiseToTopChild& op)
 //--------------------------------------------------
 // LayerTransactionParent
 LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
                                                CompositorBridgeParentBase* aBridge,
                                                uint64_t aId)
   : mLayerManager(aManager)
   , mCompositorBridge(aBridge)
   , mId(aId)
+  , mChildEpoch(0)
+  , mParentEpoch(0)
   , mPendingTransaction(0)
   , mPendingCompositorUpdates(0)
   , mDestroyed(false)
   , mIPCOpen(false)
 {
 }
 
 LayerTransactionParent::~LayerTransactionParent()
@@ -709,16 +711,34 @@ LayerTransactionParent::RecvUpdate(Infal
     }
   }
 
   profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_END);
   return true;
 }
 
 bool
+LayerTransactionParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
+{
+  mChildEpoch = aLayerObserverEpoch;
+  return true;
+}
+
+bool
+LayerTransactionParent::ShouldParentObserveEpoch()
+{
+  if (mParentEpoch == mChildEpoch) {
+    return false;
+  }
+
+  mParentEpoch = mChildEpoch;
+  return true;
+}
+
+bool
 LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime)
 {
   return mCompositorBridge->SetTestSampleTime(this, aTime);
 }
 
 bool
 LayerTransactionParent::RecvLeaveTestMode()
 {
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -55,16 +55,19 @@ protected:
 public:
   void Destroy();
 
   LayerManagerComposite* layer_manager() const { return mLayerManager; }
 
   uint64_t GetId() const { return mId; }
   Layer* GetRoot() const { return mRoot; }
 
+  uint64_t GetChildEpoch() const { return mChildEpoch; }
+  bool ShouldParentObserveEpoch();
+
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
   virtual bool AllocShmem(size_t aSize,
                           ipc::SharedMemory::SharedMemoryType aType,
                           ipc::Shmem* aShmem) override;
 
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 ipc::SharedMemory::SharedMemoryType aType,
@@ -134,16 +137,18 @@ protected:
                                 PluginsArray&& aPlugins,
                                 const bool& isFirstPaint,
                                 const bool& scheduleComposite,
                                 const uint32_t& paintSequenceNumber,
                                 const bool& isRepeatTransaction,
                                 const mozilla::TimeStamp& aTransactionStart,
                                 const int32_t& aPaintSyncId) override;
 
+  virtual bool RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
+
   virtual bool RecvClearCachedResources() override;
   virtual bool RecvForceComposite() override;
   virtual bool RecvSetTestSampleTime(const TimeStamp& aTime) override;
   virtual bool RecvLeaveTestMode() override;
   virtual bool RecvGetAnimationOpacity(PLayerParent* aParent,
                                        float* aOpacity,
                                        bool* aHasAnimationOpacity) override;
   virtual bool RecvGetAnimationTransform(PLayerParent* aParent,
@@ -192,16 +197,23 @@ private:
   // containers in the "real" layer tree
   RefPtr<Layer> mRoot;
   // When this is nonzero, it refers to a layer tree owned by the
   // compositor thread.  It is always true that
   //   mId != 0 => mRoot == null
   // because the "real tree" is owned by the compositor.
   uint64_t mId;
 
+  // These fields keep track of the latest epoch values in the child and the
+  // parent. mChildEpoch is the latest epoch value received from the child.
+  // mParentEpoch is the latest epoch value that we have told TabParent about
+  // (via ObserveLayerUpdate).
+  uint64_t mChildEpoch;
+  uint64_t mParentEpoch;
+
   uint64_t mPendingTransaction;
 
   // Number of compositor updates we're waiting for the child to
   // acknowledge.
   uint32_t mPendingCompositorUpdates;
 
   // When the widget/frame/browser stuff in this process begins its
   // destruction process, we need to Disconnect() all the currently
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -65,16 +65,18 @@ parent:
   async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy,
                      uint64_t fwdTransactionId,
                      uint64_t id, TargetConfig targetConfig,
                      PluginWindowData[] plugins, bool isFirstPaint,
                      bool scheduleComposite, uint32_t paintSequenceNumber,
                      bool isRepeatTransaction, TimeStamp transactionStart,
                      int32_t paintSyncId);
 
+  async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
+
   // Testing APIs
 
   // Enter test mode, set the sample time to sampleTime, and resample
   // animations. sampleTime must not be null.
   sync SetTestSampleTime(TimeStamp sampleTime);
   // Leave test mode and resume normal compositing
   sync LeaveTestMode();
 
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -767,16 +767,25 @@ ShadowLayerForwarder::EndTransaction(Inf
 
   *aSent = true;
   mIsFirstPaint = false;
   mPaintSyncId = 0;
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
   return true;
 }
 
+void
+ShadowLayerForwarder::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+  if (!HasShadowManager() || !mShadowManager->IPCOpen()) {
+    return;
+  }
+  Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
 bool
 ShadowLayerForwarder::AllocUnsafeShmem(size_t aSize,
                                        ipc::SharedMemory::SharedMemoryType aShmType,
                                        ipc::Shmem* aShmem)
 {
   MOZ_ASSERT(HasShadowManager(), "no shadow manager");
   if (!IPCOpen()) {
     return false;
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -371,16 +371,18 @@ public:
 
   /**
    * Flag the next paint as the first for a document.
    */
   void SetIsFirstPaint() { mIsFirstPaint = true; }
 
   void SetPaintSyncId(int32_t aSyncId) { mPaintSyncId = aSyncId; }
 
+  void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
   static void PlatformSyncBeforeUpdate();
 
   virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
                                       gfxContentType aContent,
                                       SurfaceDescriptor* aBuffer) override;
 
   virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
                                               gfxContentType aContent,
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -486,21 +486,22 @@ CreateBlurMask(const IntSize& aMinSize,
 
   MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
   MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
 
   return result.forget();
 }
 
 static already_AddRefed<SourceSurface>
-CreateBoxShadow(DrawTarget& aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
+CreateBoxShadow(SourceSurface* aBlurMask, const Color& aShadowColor)
 {
   IntSize blurredSize = aBlurMask->GetSize();
+  gfxPlatform* platform = gfxPlatform::GetPlatform();
   RefPtr<DrawTarget> boxShadowDT =
-    aDestDT.CreateSimilarDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
+    platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
 
   if (!boxShadowDT) {
     return nullptr;
   }
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
@@ -547,17 +548,17 @@ GetBlur(gfxContext* aDestinationCtx,
   RefPtr<SourceSurface> blurMask =
     CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice,
                    destDT);
 
   if (!blurMask) {
     return nullptr;
   }
 
-  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDT, blurMask, aShadowColor);
+  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(blurMask, aShadowColor);
   if (!boxShadow) {
     return nullptr;
   }
 
   if (useDestRect) {
     // Since we're just going to paint the actual rect to the destination
     aSlice.SizeTo(0, 0, 0, 0);
   } else {
@@ -907,17 +908,17 @@ gfxAlphaBoxBlur::GetInsetBlur(const mozi
   // Create the A8 mask
   IntPoint topLeft;
   RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
   if (!minMask) {
     return nullptr;
   }
 
   // Fill in with the color we actually wanted
-  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(*aDestDrawTarget, minMask, aShadowColor);
+  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, aShadowColor);
   if (!minInsetBlur) {
     return nullptr;
   }
 
   if (!aIsDestRect) {
     CacheInsetBlur(outerSize, whitespaceSize,
                    aBlurRadius, &aInnerClipRadii,
                    aShadowColor, aHasBorderRadius,
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -808,17 +808,17 @@ GetRoundOutDeviceClipExtents(gfxContext*
 }
 
 void
 gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
 {
   IntRect clipExtents;
   if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
     gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
-    clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+    clipExtents = IntRect::Truncate(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
   }
   bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
                                  mDT->GetOpaqueRect().Contains(clipExtents)) &&
                                 !mDT->GetUserData(&sDontUseAsSourceKey);
 
   if (gfxPrefs::UseNativePushLayer()) {
     Save();
 
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -704,17 +704,18 @@ gfxDWriteFont::GetScaledFont(mozilla::gf
   } else if (aTarget->GetBackendType() == BackendType::SKIA) {
     gfxDWriteFontEntry *fe =
         static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
     bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
 
     mAzureScaledFont =
             Factory::CreateScaledFontForDWriteFont(mFont, mFontFamily,
                                                    mFontFace, GetAdjustedSize(),
-                                                   useEmbeddedBitmap);
+                                                   useEmbeddedBitmap,
+                                                   GetForceGDIClassic());
   } else {
     mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
                                                             GetAdjustedSize());
   }
 
   mAzureScaledFontIsCairo = wantCairo;
 
   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -40,22 +40,21 @@ gfxSurfaceDrawable::DrawWithSamplingRect
                                          gfxFloat aOpacity)
 {
   if (!mSourceSurface) {
     return true;
   }
 
   // When drawing with CLAMP we can expand the sampling rect to the nearest pixel
   // without changing the result.
-  gfxRect samplingRect = aSamplingRect;
-  samplingRect.RoundOut();
-  IntRect intRect(samplingRect.x, samplingRect.y, samplingRect.width, samplingRect.height);
+  IntRect intRect = IntRect::RoundOut(aSamplingRect.x, aSamplingRect.y,
+                                      aSamplingRect.width, aSamplingRect.height);
 
   IntSize size = mSourceSurface->GetSize();
-  if (!IntRect(0, 0, size.width, size.height).Contains(intRect)) {
+  if (!IntRect(IntPoint(), size).Contains(intRect)) {
     return false;
   }
 
   DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
                ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
   return true;
 }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -379,16 +379,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.logging.peak-texture-usage.enabled",GfxLoggingPeakTextureUsageEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.max-alloc-size",                    MaxAllocSize, int32_t, (int32_t)500000000);
   DECL_GFX_PREF(Once, "gfx.max-texture-size",                  MaxTextureSize, int32_t, (int32_t)32767);
   DECL_GFX_PREF(Live, "gfx.partialpresent.force",              PartialPresent, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
+  DECL_GFX_PREF(Once, "gfx.text.disable-aa",                   DisableAllTextAA, bool, false);
   DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion",         YCbCrAccurateConversion, bool, false);
 
   DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer",      UseNativePushLayer, bool, false);
   DECL_GFX_PREF(Live, "gfx.content.always-paint",              AlwaysPaint, bool, false);
 
   // Disable surface sharing due to issues with compatible FBConfigs on
   // NVIDIA drivers as described in bug 1193015.
   DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap",       UseGLXTextureFromPixmap, bool, false);
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -340,16 +340,15 @@ OrientedImage::GetImageSpaceInvalidation
     // Fall back to identity if the width and height aren't available.
     return rect;
   }
 
   // Transform the invalidation rect into the correct orientation.
   gfxMatrix matrix(OrientationMatrix(innerSize));
   gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
                                                      rect.width, rect.height)));
-  invalidRect.RoundOut();
 
-  return nsIntRect(invalidRect.x, invalidRect.y,
-                   invalidRect.width, invalidRect.height);
+  return IntRect::RoundOut(invalidRect.x, invalidRect.y,
+                           invalidRect.width, invalidRect.height);
 }
 
 } // namespace image
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
@@ -22,17 +22,17 @@ TestEndpointBridgeMainParent::Main()
     fail("sending Start");
   }
 }
 
 bool
 TestEndpointBridgeMainParent::RecvBridged(Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint)
 {
   TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent();
-  if (!endpoint.Bind(a, nullptr)) {
+  if (!endpoint.Bind(a)) {
     fail("Bind failed");
   }
   return true;
 }
 
 void
 TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why)
 {
@@ -185,17 +185,17 @@ TestEndpointBridgeSubChild::RecvPing()
   return true;
 }
 
 bool
 TestEndpointBridgeSubChild::RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint)
 {
   TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild();
 
-  if (!endpoint.Bind(a, nullptr)) {
+  if (!endpoint.Bind(a)) {
     fail("failed to Bind");
   }
 
   if (!a->SendHello()) {
     fail("sending Hello");
   }
 
   return true;
--- a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
@@ -49,17 +49,17 @@ static void
 OpenParent(TestEndpointOpensOpenedParent* aParent,
            Endpoint<PTestEndpointOpensOpenedParent>&& aEndpoint)
 {
   AssertNotMainThread();
 
   // Open the actor on the off-main thread to park it there.
   // Messages will be delivered to this thread's message loop
   // instead of the main thread's.
-  if (!aEndpoint.Bind(aParent, nullptr)) {
+  if (!aEndpoint.Bind(aParent)) {
     fail("binding Parent");
   }
 }
 
 bool
 TestEndpointOpensParent::RecvStartSubprotocol(
   mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint)
 {
@@ -151,17 +151,17 @@ static void
 OpenChild(TestEndpointOpensOpenedChild* aChild,
           Endpoint<PTestEndpointOpensOpenedChild>&& endpoint)
 {
   AssertNotMainThread();
 
   // Open the actor on the off-main thread to park it there.
   // Messages will be delivered to this thread's message loop
   // instead of the main thread's.
-  if (!endpoint.Bind(aChild, nullptr)) {
+  if (!endpoint.Bind(aChild)) {
     fail("binding child endpoint");
   }
 
   // Kick off the unit tests
   if (!aChild->SendHello()) {
     fail("sending Hello");
   }
 }
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -1983,17 +1983,17 @@ class MOZ_STACK_CLASS ModuleValidator
         AsmJSGlobal g(AsmJSGlobal::Constant, Move(fieldChars));
         g.pod.u.constant.value_ = constant;
         g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
                                    PropertyName* field)
     {
-        if (!JitOptions.wasmTestMode)
+        if (!JitOptions.asmJSAtomicsEnable)
             return failCurrentOffset("asm.js Atomics only enabled in wasm test mode");
 
         atomicsPresent_ = true;
 
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
--- a/js/src/jit-test/tests/asm.js/gating.js
+++ b/js/src/jit-test/tests/asm.js/gating.js
@@ -13,17 +13,17 @@
 // values that are not the expected built-in values and the link will
 // fail as desired.
 
 load(libdir + "asm.js");
 
 if (!isAsmJSCompilationAvailable())
     quit(0);
 
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 if (!this.Atomics) {
     this.Atomics = { load: function (x, y) { return 0 },
 		     store: function (x, y, z) { return 0 },
 		     exchange: function (x, y, z) { return 0 },
 		     add: function (x, y, z) { return 0 },
 		     sub: function (x, y, z) { return 0 },
 		     and: function (x, y, z) { return 0 },
--- a/js/src/jit-test/tests/asm.js/sta-transition.js
+++ b/js/src/jit-test/tests/asm.js/sta-transition.js
@@ -2,17 +2,17 @@
 //
 // These should not be run with --no-asmjs, the guard below checks this.
 
 load(libdir + "asm.js");
 
 if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
     quit(0);
 
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 //////////////////////////////////////////////////////////////////////
 //
 // Int8Array can be used on SharedArrayBuffer, if atomics are present
 
 var m1 = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
 
--- a/js/src/jit-test/tests/asm.js/testAtomic-effect.js
+++ b/js/src/jit-test/tests/asm.js/testAtomic-effect.js
@@ -1,14 +1,14 @@
 // |jit-test| test-also-noasmjs
 if (!this.Atomics)
     quit();
 
 load(libdir + "asm.js");
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 var code = `
     "use asm";
 
     var HEAP32 = new stdlib.Int32Array(heap);
     var add = stdlib.Atomics.add;
     var load = stdlib.Atomics.load;
     var _emscripten_asm_const_int=ffi._emscripten_asm_const_int;
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -4,17 +4,17 @@ if (!this.SharedArrayBuffer || !this.Ato
     quit();
 
 // The code duplication below is very far from elegant but provides
 // flexibility that comes in handy several places.
 
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 var loadModule_int32_code =
     USE_ASM + `
     var atomic_load = stdlib.Atomics.load;
     var atomic_store = stdlib.Atomics.store;
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
--- a/js/src/jit-test/tests/asm.js/testBug1155176.js
+++ b/js/src/jit-test/tests/asm.js/testBug1155176.js
@@ -1,13 +1,13 @@
 if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
     quit(0);
 
 load(libdir + "asm.js");
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 // The way this is constructed, either the first module does not
 // verify as asm.js (if the >>>0 is left off, which was legal prior to
 // bug 1155176), or the results of the two modules have to be equal.
 
 var m = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
 
--- a/js/src/jit-test/tests/asm.js/testBug1164391.js
+++ b/js/src/jit-test/tests/asm.js/testBug1164391.js
@@ -1,14 +1,14 @@
 if (!this.SharedArrayBuffer)
     quit(0);
 
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 
 var m = asmCompile("stdlib", "ffi", "heap", `
     "use asm";
     var HEAP32 = new stdlib.Int32Array(heap);
     var add = stdlib.Atomics.add;
     var load = stdlib.Atomics.load;
     function add_sharedEv(i1) {
         i1 = i1 | 0;
--- a/js/src/jit-test/tests/asm.js/testBug1302407.js
+++ b/js/src/jit-test/tests/asm.js/testBug1302407.js
@@ -1,6 +1,6 @@
 if (!this['SharedArrayBuffer'])
     quit();
 
-setJitCompilerOption('wasm.test-mode', 1);
+setJitCompilerOption('asmjs.atomics.enable', 1);
 new SharedArrayBuffer(65536);
-setJitCompilerOption('wasm.test-mode', 0)
+setJitCompilerOption('asmjs.atomics.enable', 0)
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -216,16 +216,19 @@ DefaultJitOptions::DefaultJitOptions()
         forcedRegisterAllocator = LookupRegisterAllocator(env);
         if (!forcedRegisterAllocator.isSome())
             Warn(forcedRegisterAllocatorEnv, env);
     }
 
     // Toggles whether unboxed plain objects can be created by the VM.
     SET_DEFAULT(disableUnboxedObjects, false);
 
+    // Test whether Atomics are allowed in asm.js code.
+    SET_DEFAULT(asmJSAtomicsEnable, false);
+
     // Test whether wasm int64 / double NaN bits testing is enabled.
     SET_DEFAULT(wasmTestMode, false);
 
     // Toggles the optimization whereby offsets are folded into loads and not
     // included in the bounds check.
     SET_DEFAULT(wasmFoldOffsets, true);
 
     // Determines whether we suppress using signal handlers
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -64,16 +64,17 @@ struct DefaultJitOptions
     bool disableCacheIR;
     bool disableSharedStubs;
     bool disableSincos;
     bool disableSink;
     bool eagerCompilation;
     bool forceInlineCaches;
     bool limitScriptSize;
     bool osr;
+    bool asmJSAtomicsEnable;
     bool wasmTestMode;
     bool wasmFoldOffsets;
     bool ionInterruptWithoutSignals;
     uint32_t baselineWarmUpThreshold;
     uint32_t exceptionBailoutThreshold;
     uint32_t frequentBailoutThreshold;
     uint32_t maxStackArgs;
     uint32_t osrPcMismatchesBeforeRecompile;
--- a/js/src/jsapi-tests/testSlowScript.cpp
+++ b/js/src/jsapi-tests/testSlowScript.cpp
@@ -19,17 +19,17 @@ RequestInterruptCallback(JSContext* cx, 
     if (!sRemain--)
         JS_RequestInterruptCallback(cx);
     args.rval().setUndefined();
     return true;
 }
 
 BEGIN_TEST(testSlowScript)
 {
-    JS_SetInterruptCallback(cx, InterruptCallback);
+    JS_AddInterruptCallback(cx, InterruptCallback);
     JS_DefineFunction(cx, global, "requestInterruptCallback", RequestInterruptCallback, 0, 0);
 
     test("while (true)"
          "  for (i in [0,0,0,0])"
          "    requestInterruptCallback();");
 
     test("while (true)"
          "  for (i in [0,0,0,0])"
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4612,28 +4612,34 @@ JS_New(JSContext* cx, HandleObject ctor,
 }
 
 JS_PUBLIC_API(bool)
 JS_CheckForInterrupt(JSContext* cx)
 {
     return js::CheckForInterrupt(cx);
 }
 
-JS_PUBLIC_API(JSInterruptCallback)
-JS_SetInterruptCallback(JSContext* cx, JSInterruptCallback callback)
-{
-    JSInterruptCallback old = cx->interruptCallback;
-    cx->interruptCallback = callback;
-    return old;
-}
-
-JS_PUBLIC_API(JSInterruptCallback)
-JS_GetInterruptCallback(JSContext* cx)
-{
-    return cx->interruptCallback;
+JS_PUBLIC_API(bool)
+JS_AddInterruptCallback(JSContext* cx, JSInterruptCallback callback)
+{
+    return cx->interruptCallbacks.append(callback);
+}
+
+JS_PUBLIC_API(bool)
+JS_DisableInterruptCallback(JSContext* cx)
+{
+    bool result = cx->interruptCallbackDisabled;
+    cx->interruptCallbackDisabled = true;
+    return result;
+}
+
+JS_PUBLIC_API(void)
+JS_ResetInterruptCallback(JSContext* cx, bool enable)
+{
+    cx->interruptCallbackDisabled = enable;
 }
 
 /************************************************************************/
 
 /*
  * Promises.
  */
 JS_PUBLIC_API(void)
@@ -6205,16 +6211,19 @@ JS_SetGlobalJitCompilerOption(JSContext*
         break;
       case JSJITCOMPILER_JUMP_THRESHOLD:
         if (value == uint32_t(-1)) {
             jit::DefaultJitOptions defaultValues;
             value = defaultValues.jumpThreshold;
         }
         jit::JitOptions.jumpThreshold = value;
         break;
+      case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE:
+        jit::JitOptions.asmJSAtomicsEnable = !!value;
+        break;
       case JSJITCOMPILER_WASM_TEST_MODE:
         jit::JitOptions.wasmTestMode = !!value;
         break;
       case JSJITCOMPILER_WASM_FOLD_OFFSETS:
         jit::JitOptions.wasmFoldOffsets = !!value;
         break;
       case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL:
         jit::JitOptions.ionInterruptWithoutSignals = !!value;
@@ -6239,16 +6248,19 @@ JS_GetGlobalJitCompilerOption(JSContext*
       case JSJITCOMPILER_ION_FORCE_IC:
         return jit::JitOptions.forceInlineCaches;
       case JSJITCOMPILER_ION_ENABLE:
         return JS::ContextOptionsRef(cx).ion();
       case JSJITCOMPILER_BASELINE_ENABLE:
         return JS::ContextOptionsRef(cx).baseline();
       case JSJITCOMPILER_OFFTHREAD_COMPILATION_ENABLE:
         return rt->canUseOffthreadIonCompilation();
+      case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE:
+        return jit::JitOptions.asmJSAtomicsEnable ? 1 : 0;
+        break;
       case JSJITCOMPILER_WASM_TEST_MODE:
         return jit::JitOptions.wasmTestMode ? 1 : 0;
       case JSJITCOMPILER_WASM_FOLD_OFFSETS:
         return jit::JitOptions.wasmFoldOffsets ? 1 : 0;
       case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL:
         return jit::JitOptions.ionInterruptWithoutSignals ? 1 : 0;
       default:
         break;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4300,21 +4300,24 @@ JS_CheckForInterrupt(JSContext* cx);
  * To schedule the GC and for other activities the engine internally triggers
  * interrupt callbacks. The embedding should thus not rely on callbacks being
  * triggered through the external API only.
  *
  * Important note: Additional callbacks can occur inside the callback handler
  * if it re-enters the JS engine. The embedding must ensure that the callback
  * is disconnected before attempting such re-entry.
  */
-extern JS_PUBLIC_API(JSInterruptCallback)
-JS_SetInterruptCallback(JSContext* cx, JSInterruptCallback callback);
-
-extern JS_PUBLIC_API(JSInterruptCallback)
-JS_GetInterruptCallback(JSContext* cx);
+extern JS_PUBLIC_API(bool)
+JS_AddInterruptCallback(JSContext* cx, JSInterruptCallback callback);
+
+extern JS_PUBLIC_API(bool)
+JS_DisableInterruptCallback(JSContext* cx);
+
+extern JS_PUBLIC_API(void)
+JS_ResetInterruptCallback(JSContext* cx, bool enable);
 
 extern JS_PUBLIC_API(void)
 JS_RequestInterruptCallback(JSContext* cx);
 
 namespace JS {
 
 /**
  * Sets the callback that's invoked whenever an incumbent global is required.
@@ -5631,16 +5634,17 @@ JS_SetOffthreadIonCompilationEnabled(JSC
     Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger")                     \
     Register(ION_GVN_ENABLE, "ion.gvn.enable")                             \
     Register(ION_FORCE_IC, "ion.forceinlineCaches")                        \
     Register(ION_ENABLE, "ion.enable")                                     \
     Register(ION_INTERRUPT_WITHOUT_SIGNAL, "ion.interrupt-without-signals") \
     Register(BASELINE_ENABLE, "baseline.enable")                           \
     Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
     Register(JUMP_THRESHOLD, "jump-threshold")                             \
+    Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable")                 \
     Register(WASM_TEST_MODE, "wasm.test-mode")                             \
     Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets")
 
 typedef enum JSJitCompilerOption {
 #define JIT_COMPILER_DECLARE(key, str) \
     JSJITCOMPILER_ ## key,
 
     JIT_COMPILER_OPTIONS(JIT_COMPILER_DECLARE)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6350,16 +6350,20 @@ GCRuntime::gc(JSGCInvocationKind gckind,
     invocationKind = gckind;
     collect(true, SliceBudget::unlimited(), reason);
 }
 
 void
 GCRuntime::startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
 {
     MOZ_ASSERT(!isIncrementalGCInProgress());
+    if (!JS::IsIncrementalGCEnabled(rt->contextFromMainThread())) {
+        gc(gckind, reason);
+        return;
+    }
     invocationKind = gckind;
     collect(false, defaultBudget(reason, millis), reason);
 }
 
 void
 GCRuntime::gcSlice(JS::gcreason::Reason reason, int64_t millis)
 {
     MOZ_ASSERT(isIncrementalGCInProgress());
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -7618,17 +7618,17 @@ main(int argc, char** argv, char** envp)
     size_t availMem = op.getIntOption("available-memory");
     if (availMem > 0)
         JS_SetGCParametersBasedOnAvailableMemory(cx, availMem);
 
     JS_SetTrustedPrincipals(cx, &ShellPrincipals::fullyTrusted);
     JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks);
     JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
 
-    JS_SetInterruptCallback(cx, ShellInterruptCallback);
+    JS_AddInterruptCallback(cx, ShellInterruptCallback);
     JS::SetBuildIdOp(cx, ShellBuildId);
     JS::SetAsmJSCacheOps(cx, &asmJSCacheOps);
 
     JS_SetNativeStackQuota(cx, gMaxStackSize);
 
     JS::dbg::SetDebuggerMallocSizeOf(cx, moz_malloc_size_of);
 
     if (!JS::InitSelfHostedCode(cx))
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -357,16 +357,19 @@ ExecuteState::pushInterpreterFrame(JSCon
 #ifdef _MSC_VER
 # pragma optimize("g", off)
 #endif
 bool
 js::RunScript(JSContext* cx, RunState& state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
+    // Since any script can conceivably GC, make sure it's safe to do so.
+    JS::AutoAssertOnGC::VerifyIsSafeToGC(cx->runtime());
+
     if (!Debugger::checkNoExecute(cx, state.script()))
         return false;
 
 #if defined(MOZ_HAVE_RDTSC)
     js::AutoStopwatch stopwatch(cx);
 #endif // defined(MOZ_HAVE_RDTSC)
 
     SPSEntryMarker marker(cx->runtime(), state.script());
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -144,17 +144,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     parentRuntime(parentRuntime),
 #ifdef DEBUG
     updateChildRuntimeCount(parentRuntime),
 #endif
     interrupt_(false),
     telemetryCallback(nullptr),
     handlingSegFault(false),
     handlingJitInterrupt_(false),
-    interruptCallback(nullptr),
+    interruptCallbackDisabled(false),
     getIncumbentGlobalCallback(nullptr),
     enqueuePromiseJobCallback(nullptr),
     enqueuePromiseJobCallbackData(nullptr),
     promiseRejectionTrackerCallback(nullptr),
     promiseRejectionTrackerCallbackData(nullptr),
     startAsyncTaskCallback(nullptr),
     finishAsyncTaskCallback(nullptr),
 #ifdef DEBUG
@@ -525,21 +525,26 @@ InvokeInterruptCallback(JSContext* cx)
 
     // A worker thread may have requested an interrupt after finishing an Ion
     // compilation.
     jit::AttachFinishedCompilations(cx);
 
     // Important: Additional callbacks can occur inside the callback handler
     // if it re-enters the JS engine. The embedding must ensure that the
     // callback is disconnected before attempting such re-entry.
-    JSInterruptCallback cb = cx->runtime()->interruptCallback;
-    if (!cb)
+    if (cx->runtime()->interruptCallbackDisabled)
         return true;
 
-    if (cb(cx)) {
+    bool stop = false;
+    for (JSInterruptCallback cb : cx->runtime()->interruptCallbacks) {
+        if (!cb(cx))
+            stop = true;
+    }
+
+    if (!stop) {
         // Debugger treats invoking the interrupt callback as a "step", so
         // invoke the onStep handler.
         if (cx->compartment()->isDebuggee()) {
             ScriptFrameIter iter(cx);
             if (!iter.done() &&
                 cx->compartment() == iter.compartment() &&
                 iter.script()->stepModeEnabled())
             {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -633,17 +633,19 @@ struct JSRuntime : public JS::shadow::Ru
     void finishHandlingJitInterrupt() {
         MOZ_ASSERT(handlingJitInterrupt_);
         handlingJitInterrupt_ = false;
     }
     bool handlingJitInterrupt() const {
         return handlingJitInterrupt_;
     }
 
-    JSInterruptCallback interruptCallback;
+    using InterruptCallbackVector = js::Vector<JSInterruptCallback, 2, js::SystemAllocPolicy>;
+    InterruptCallbackVector interruptCallbacks;
+    bool interruptCallbackDisabled;
 
     JSGetIncumbentGlobalCallback getIncumbentGlobalCallback;
     JSEnqueuePromiseJobCallback enqueuePromiseJobCallback;
     void* enqueuePromiseJobCallbackData;
 
     JSPromiseRejectionTrackerCallback promiseRejectionTrackerCallback;
     void* promiseRejectionTrackerCallbackData;
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -111,17 +111,17 @@ SharedArrayRawBuffer::New(JSContext* cx,
     // so guard against it on principle.
     MOZ_ASSERT(length != (uint32_t)-1);
 
     // Add a page for the header and round to a page boundary.
     uint32_t allocSize = SharedArrayAllocSize(length);
     if (allocSize <= length)
         return nullptr;
 
-    bool preparedForAsmJS = jit::JitOptions.wasmTestMode && IsValidAsmJSHeapLength(length);
+    bool preparedForAsmJS = jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length);
 
     void* p = nullptr;
     if (preparedForAsmJS) {
         // Test >= to guard against the case where multiple extant runtimes
         // race to allocate.
         if (++numLive >= maxLive) {
             JSRuntime* rt = cx->runtime();
             if (rt->largeAllocationFailureCallback)
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -3471,17 +3471,17 @@ XPCJSContext::Initialize()
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
 #ifdef MOZ_ENABLE_PROFILER_SPS
     if (PseudoStack* stack = mozilla_get_pseudo_stack())
         stack->sampleContext(cx);
 #endif
     JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
     js::SetActivityCallback(cx, ActivityCallback, this);
-    JS_SetInterruptCallback(cx, InterruptCallback);
+    JS_AddInterruptCallback(cx, InterruptCallback);
     js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
 
     // The JS engine needs to keep the source code around in order to implement
     // Function.prototype.toSource(). It'd be nice to not have to do this for
     // chrome code and simply stub out requests for source on it. Life is not so
     // easy, unfortunately. Nobody relies on chrome toSource() working in core
     // browser code, but chrome tests use it. The worst offenders are addons,
     // which like to monkeypatch chrome functions by calling toSource() on them
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1416,17 +1416,17 @@ XRE_XPCShellMain(int argc, char** argv, 
         cx = jsapi.cx();
 
         // Override the default XPConnect interrupt callback. We could store the
         // old one and restore it before shutting down, but there's not really a
         // reason to bother.
         sScriptedInterruptCallback = new PersistentRootedValue;
         sScriptedInterruptCallback->init(cx, UndefinedValue());
 
-        JS_SetInterruptCallback(cx, XPCShellInterruptCallback);
+        JS_AddInterruptCallback(cx, XPCShellInterruptCallback);
 
         argc--;
         argv++;
         ProcessArgsForCompartment(cx, argv, argc);
 
         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
         if (!xpc) {
             printf("failed to get nsXPConnect service!\n");
--- a/layout/base/nsDisplayItemTypesList.h
+++ b/layout/base/nsDisplayItemTypesList.h
@@ -45,16 +45,17 @@ DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_VIDEO)
 DECLARE_DISPLAY_ITEM_TYPE(PRINT_PLUGIN)
 DECLARE_DISPLAY_ITEM_TYPE(RANGE_FOCUS_RING)
 DECLARE_DISPLAY_ITEM_TYPE(REMOTE)
 DECLARE_DISPLAY_ITEM_TYPE(REMOTE_SHADOW)
 DECLARE_DISPLAY_ITEM_TYPE(RESOLUTION)
 DECLARE_DISPLAY_ITEM_TYPE(SCROLL_INFO_LAYER)
 DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY)
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR)
+DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR_REGION)
 DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT)
 DECLARE_DISPLAY_ITEM_TYPE(MASK)
 DECLARE_DISPLAY_ITEM_TYPE(FILTER)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_GLYPHS)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_OUTER_SVG)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_PATH_GEOMETRY)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_TEXT)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_BACKGROUND)
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2383,16 +2383,47 @@ nsDisplaySolidColor::WriteDebugInfo(std:
 {
   aStream << " (rgba "
           << (int)NS_GET_R(mColor) << ","
           << (int)NS_GET_G(mColor) << ","
           << (int)NS_GET_B(mColor) << ","
           << (int)NS_GET_A(mColor) << ")";
 }
 
+nsRect
+nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+{
+  *aSnap = true;
+  return mRegion.GetBounds();
+}
+
+void
+nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder,
+                                 nsRenderingContext* aCtx)
+{
+  int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  DrawTarget* drawTarget = aCtx->GetDrawTarget();
+  ColorPattern color(mColor);
+  for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
+    Rect rect =
+      NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget);
+    drawTarget->FillRect(rect, color);
+  }
+}
+
+void
+nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream)
+{
+  aStream << " (rgba "
+          << int(mColor.r * 255) << ","
+          << int(mColor.g * 255) << ","
+          << int(mColor.b * 255) << ","
+          << mColor.a << ")";
+}
+
 static void
 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                       nsITheme::ThemeGeometryType aType)
 {
   if (aBuilder->IsInRootChromeDocumentOrPopup() && !aBuilder->IsInTransform()) {
     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
     nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize());
     aBuilder->RegisterThemeGeometry(aType,
@@ -2509,16 +2540,50 @@ SetBackgroundClipRegion(DisplayListClipS
   if (clip.mHasAdditionalBGClipArea) {
     aClipState.ClipContentDescendants(clip.mAdditionalBGClipArea, clip.mBGClipArea,
                                       clip.mHasRoundedCorners ? clip.mRadii : nullptr);
   } else {
     aClipState.ClipContentDescendants(clip.mBGClipArea, clip.mHasRoundedCorners ? clip.mRadii : nullptr);
   }
 }
 
+/**
+ * This is used for the find bar highlighter overlay. It's only accessible
+ * through the AnonymousContent API, so it's not exposed to general web pages.
+ */
+static bool
+SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder,
+                        nsIFrame* aFrame,
+                        const nsRect& aBackgroundRect,
+                        nsDisplayList* aList,
+                        nscolor aColor)
+{
+  nsIContent* content = aFrame->GetContent();
+  if (!content) {
+    return false;
+  }
+
+  void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion);
+  if (!cutoutRegion) {
+    return false;
+  }
+
+  if (NS_GET_A(aColor) == 0) {
+    return true;
+  }
+
+  nsRegion region;
+  region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion));
+  region.MoveBy(aBuilder->ToReferenceFrame(aFrame));
+  aList->AppendNewToTop(
+    new (aBuilder) nsDisplaySolidColorRegion(aBuilder, aFrame, region, aColor));
+
+  return true;
+}
+
 
 /*static*/ bool
 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
                                                      nsIFrame* aFrame,
                                                      const nsRect& aBackgroundRect,
                                                      nsDisplayList* aList,
                                                      bool aAllowWillPaintBorderOptimization)
 {
@@ -2540,16 +2605,20 @@ nsDisplayBackgroundImage::AppendBackgrou
   nscolor color = NS_RGBA(0,0,0,0);
   if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
     bool drawBackgroundImage;
     color =
       nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame,
                                                drawBackgroundImage, drawBackgroundColor);
   }
 
+  if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList, color)) {
+    return false;
+  }
+
   const nsStyleBorder* borderStyle = aFrame->StyleBorder();
   const nsStyleEffects* effectsStyle = aFrame->StyleEffects();
   bool hasInsetShadow = effectsStyle->mBoxShadow &&
                         effectsStyle->mBoxShadow->HasShadowWithInset(true);
   bool willPaintBorder = aAllowWillPaintBorderOptimization &&
                          !isThemed && !hasInsetShadow &&
                          borderStyle->HasBorder();
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2650,16 +2650,69 @@ public:
 
   NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
 
 private:
   nsRect  mBounds;
 };
 
 /**
+ * A display item that renders a solid color over a region. This is not
+ * exposed through CSS, its only purpose is efficient invalidation of
+ * the find bar highlighter dimmer.
+ */
+class nsDisplaySolidColorRegion : public nsDisplayItem {
+  typedef mozilla::gfx::Color Color;
+
+public:
+  nsDisplaySolidColorRegion(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                            const nsRegion& aRegion, nscolor aColor)
+    : nsDisplayItem(aBuilder, aFrame), mRegion(aRegion), mColor(Color::FromABGR(aColor))
+  {
+    NS_ASSERTION(NS_GET_A(aColor) > 0, "Don't create invisible nsDisplaySolidColorRegions!");
+    MOZ_COUNT_CTOR(nsDisplaySolidColorRegion);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplaySolidColorRegion() {
+    MOZ_COUNT_DTOR(nsDisplaySolidColorRegion);
+  }
+#endif
+
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+  {
+    return new nsDisplaySolidColorRegionGeometry(this, aBuilder, mRegion, mColor);
+  }
+
+  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+                                         const nsDisplayItemGeometry* aGeometry,
+                                         nsRegion* aInvalidRegion) override
+  {
+    const nsDisplaySolidColorRegionGeometry* geometry =
+      static_cast<const nsDisplaySolidColorRegionGeometry*>(aGeometry);
+    if (mColor == geometry->mColor) {
+      aInvalidRegion->Xor(geometry->mRegion, mRegion);
+    } else {
+      aInvalidRegion->Or(geometry->mRegion.GetBounds(), mRegion.GetBounds());
+    }
+  }
+
+protected:
+
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
+  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
+  virtual void WriteDebugInfo(std::stringstream& aStream) override;
+
+  NS_DISPLAY_DECL_NAME("SolidColorRegion", TYPE_SOLID_COLOR_REGION)
+
+private:
+  nsRegion mRegion;
+  Color mColor;
+};
+
+/**
  * A display item to paint one background-image for a frame. Each background
  * image layer gets its own nsDisplayBackgroundImage.
  */
 class nsDisplayBackgroundImage : public nsDisplayImageContainer {
 public:
   /**
    * aLayer signifies which background layer this item represents.
    * aIsThemed should be the value of aFrame->IsThemed.
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -101,16 +101,23 @@ nsDisplayBoxShadowInnerGeometry::MoveBy(
 }
 
 nsDisplayBoxShadowOuterGeometry::nsDisplayBoxShadowOuterGeometry(nsDisplayItem* aItem,
     nsDisplayListBuilder* aBuilder, float aOpacity)
   : nsDisplayItemGenericGeometry(aItem, aBuilder)
   , mOpacity(aOpacity)
 {}
 
+void
+nsDisplaySolidColorRegionGeometry::MoveBy(const nsPoint& aOffset)
+{
+  nsDisplayItemGeometry::MoveBy(aOffset);
+  mRegion.MoveBy(aOffset);
+}
+
 nsDisplaySVGEffectsGeometry::nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
   , nsImageGeometryMixin(aItem, aBuilder)
   , mBBox(aItem->BBoxInUserSpace())
   , mUserSpaceOffset(aItem->UserSpaceOffset())
   , mFrameOffsetToReferenceFrame(aItem->ToReferenceFrame())
 {}
 
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -16,16 +16,22 @@
 class nsDisplayBackgroundImage;
 class nsCharClipDisplayItem;
 class nsDisplayItem;
 class nsDisplayListBuilder;
 class nsDisplaySVGEffects;
 class nsDisplayTableItem;
 class nsDisplayThemedBackground;
 
+namespace mozilla {
+namespace gfx {
+struct Color;
+}
+}
+
 /**
  * This stores the geometry of an nsDisplayItem, and the area
  * that will be affected when painting the item.
  *
  * It is used to retain information about display items so they
  * can be compared against new display items in the next paint.
  */
 class nsDisplayItemGeometry
@@ -236,16 +242,34 @@ public:
                               nscolor aColor)
     : nsDisplayItemBoundsGeometry(aItem, aBuilder)
     , mColor(aColor)
   { }
 
   nscolor mColor;
 };
 
+class nsDisplaySolidColorRegionGeometry : public nsDisplayItemBoundsGeometry
+{
+public:
+  nsDisplaySolidColorRegionGeometry(nsDisplayItem* aItem,
+                                    nsDisplayListBuilder* aBuilder,
+                                    const nsRegion& aRegion,
+                                    mozilla::gfx::Color aColor)
+    : nsDisplayItemBoundsGeometry(aItem, aBuilder)
+    , mRegion(aRegion)
+    , mColor(aColor)
+  { }
+
+  virtual void MoveBy(const nsPoint& aOffset) override;
+
+  nsRegion mRegion;
+  mozilla::gfx::Color mColor;
+};
+
 class nsDisplaySVGEffectsGeometry : public nsDisplayItemGeometry
   , public nsImageGeometryMixin<nsDisplaySVGEffectsGeometry>
 {
 public:
   nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) override;
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1245,17 +1245,17 @@ public:
   {
     mObservesMutationsForPrint = aObserve;
   }
   bool ObservesNativeAnonMutationsForPrint()
   {
     return mObservesMutationsForPrint;
   }
 
-  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) = 0;
+  virtual nsresult SetIsActive(bool aIsActive) = 0;
 
   bool IsActive()
   {
     return mIsActive;
   }
 
   // mouse capturing
   static CapturingContentInfo gCaptureInfo;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -756,17 +756,16 @@ PresShell::PresShell()
   mReflowCountMgr->SetPresContext(mPresContext);
   mReflowCountMgr->SetPresShell(this);
 #endif
   mLoadBegin = TimeStamp::Now();
 
   mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
   mIsThemeSupportDisabled = false;
   mIsActive = true;
-  mIsHidden = false;
   // FIXME/bug 735029: find a better solution to this problem
   mIsFirstPaint = true;
   mPresShellId = sNextPresShellId++;
   mFrozen = false;
   mRenderFlags = 0;
 
   mScrollPositionClampingScrollPortSizeSet = false;
 
@@ -10887,26 +10886,22 @@ SetPluginIsActive(nsISupports* aSupports
   nsIFrame *frame = content->GetPrimaryFrame();
   nsIObjectFrame *objectFrame = do_QueryFrame(frame);
   if (objectFrame) {
     objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
   }
 }
 
 nsresult
-PresShell::SetIsActive(bool aIsActive, bool aIsHidden)
+PresShell::SetIsActive(bool aIsActive)
 {
   NS_PRECONDITION(mDocument, "should only be called with a document");
 
   mIsActive = aIsActive;
 
-  // Keep track of whether we've called TabChild::MakeHidden() or not.
-  // This can still be true even if aIsHidden is false.
-  mIsHidden |= aIsHidden;
-
   nsPresContext* presContext = GetPresContext();
   if (presContext &&
       presContext->RefreshDriver()->PresContext() == presContext) {
     presContext->RefreshDriver()->SetThrottled(!mIsActive);
   }
 
   // Propagate state-change to my resource documents' PresShells
   mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
@@ -10917,55 +10912,16 @@ PresShell::SetIsActive(bool aIsActive, b
 #ifdef ACCESSIBILITY
   if (aIsActive) {
     nsAccessibilityService* accService = AccService();
     if (accService) {
       accService->PresShellActivated(this);
     }
   }
 #endif
-
-  // We have this odd special case here because remote content behaves
-  // differently from same-process content when "hidden".  In
-  // desktop-type "browser UIs", hidden "tabs" have documents that are
-  // part of the chrome tree.  When the tabs are hidden, their content
-  // is no longer part of the visible document tree, and the layers
-  // for the content are naturally released.
-  //
-  // Remote content is its own top-level tree in its subprocess.  When
-  // it's "hidden", there's no transaction in which the document
-  // thinks it's not visible, so layers can be retained forever.  This
-  // is problematic when those layers uselessly hold on to precious
-  // resources like directly texturable memory.
-  //
-  // PresShell::SetIsActive() is the first C++ entry point at which we
-  // (i) know that our parent process wants our content to be hidden;
-  // and (ii) has easy access to the TabChild.  So we use this
-  // notification to signal the TabChild to drop its layer tree and
-  // stop trying to repaint.
-  if (mIsHidden) {
-    if (TabChild* tab = TabChild::GetFrom(this)) {
-      if (aIsActive) {
-        tab->MakeVisible();
-        // The only time we should set this to false is when
-        // TabChild::MakeVisible() is called.
-        mIsHidden = false;
-
-        if (!mIsZombie) {
-          if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
-            FrameLayerBuilder::InvalidateAllLayersForFrame(
-              nsLayoutUtils::GetDisplayRootFrame(root));
-            root->SchedulePaint();
-          }
-        }
-      } else {
-        tab->MakeHidden();
-      }
-    }
-  }
   return rv;
 }
 
 /*
  * Determines the current image locking state. Called when one of the
  * dependent factors changes.
  */
 nsresult
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -364,17 +364,17 @@ public:
 
   virtual void AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
                                              nsDisplayList& aList,
                                              nsIFrame* aFrame,
                                              const nsRect& aBounds) override;
 
   virtual nscolor ComputeBackstopColor(nsView* aDisplayRoot) override;
 
-  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) override;
+  virtual nsresult SetIsActive(bool aIsActive) override;
 
   virtual bool GetIsViewportOverridden() override {
     return (mMobileViewportManager != nullptr);
   }
 
   virtual bool IsLayoutFlushObserver() override
   {
     return GetPresContext()->RefreshDriver()->
@@ -527,17 +527,16 @@ protected:
 
 
   void SetRenderingState(const RenderingState& aState);
 
   friend class nsPresShellEventCB;
 
   bool mCaretEnabled;
 
-  bool mIsHidden;
 #ifdef DEBUG
   nsStyleSet* CloneStyleSet(nsStyleSet* aSet);
   bool VerifyIncrementalReflow();
   bool mInVerifyReflow;
   void ShowEventTargetDebug();
 #endif
 
   void RecordStyleSheetChange(mozilla::StyleSheetHandle aStyleSheet);
--- a/layout/xul/PopupBoxObject.cpp
+++ b/layout/xul/PopupBoxObject.cpp
@@ -360,17 +360,17 @@ PopupBoxObject::AlignmentOffset()
 }
 
 void
 PopupBoxObject::SetConstraintRect(dom::DOMRectReadOnly& aRect)
 {
   nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false));
   if (menuPopupFrame) {
     menuPopupFrame->SetOverrideConstraintRect(
-      LayoutDeviceIntRect(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
+      LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
   }
 }
 
 } // namespace dom
 } // namespace mozilla
 
 // Creation Routine ///////////////////////////////////////////////////////////////////////
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -35,16 +35,19 @@ GARBAGE += \
   gecko.ap_  \
   res/values/strings.xml \
   res/raw/browsersearch.json \
   res/raw/suggestedsites.json \
   .aapt.deps \
   GeneratedJNINatives.h \
   GeneratedJNIWrappers.cpp \
   GeneratedJNIWrappers.h \
+  FennecJNINatives.h \
+  FennecJNIWrappers.cpp \
+  FennecJNIWrappers.h \
   $(NULL)
 
 GARBAGE_DIRS += classes db jars res sync services generated
 
 # The bootclasspath is functionally identical to the classpath, but allows the
 # classes given to redefine classes in core packages, such as java.lang.
 # android.jar is here as it provides Android's definition of the Java Standard
 # Library. The compatability lib here tweaks a few of the core classes to paint
@@ -137,46 +140,57 @@ ifdef MOZ_INSTALL_TRACKING
 endif
 
 # uniq purloined from http://stackoverflow.com/a/16151140.
 uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
 
 java_bundled_libs := $(call uniq,$(java_bundled_libs))
 java_bundled_libs := $(subst $(NULL) ,:,$(strip $(java_bundled_libs)))
 
-# All the jars we're compiling from source. (not to be confused with
-# java_bundled_libs, which holds the jars which we're including as binaries).
-ALL_JARS = \
+GECKOVIEW_JARS = \
   constants.jar \
   gecko-R.jar \
+  gecko-mozglue.jar \
+  gecko-util.jar \
+  gecko-view.jar \
+  $(NULL)
+
+geckoview_jars_classpath := $(subst $(NULL) ,:,$(strip $(GECKOVIEW_JARS)))
+
+FENNEC_JARS = \
   gecko-browser.jar \
-  gecko-mozglue.jar \
   gecko-thirdparty.jar \
-  gecko-util.jar \
   services.jar \
   sync-thirdparty.jar \
   ../javaaddons/javaaddons-1.0.jar \
   $(NULL)
 
 ifdef MOZ_WEBRTC
-ALL_JARS += webrtc.jar
+FENNEC_JARS += webrtc.jar
 endif
 
 ifdef MOZ_ANDROID_SEARCH_ACTIVITY
-ALL_JARS += search-activity.jar
+FENNEC_JARS += search-activity.jar
 endif
 
 ifdef MOZ_ANDROID_MLS_STUMBLER
-ALL_JARS += ../stumbler/stumbler.jar
+FENNEC_JARS += ../stumbler/stumbler.jar
 endif
 
 ifdef MOZ_INSTALL_TRACKING
-ALL_JARS += gecko-thirdparty-adjust_sdk.jar
+FENNEC_JARS += gecko-thirdparty-adjust_sdk.jar
 endif
 
+# All the jars we're compiling from source. (not to be confused with
+# java_bundled_libs, which holds the jars which we're including as binaries).
+ALL_JARS = \
+  $(GECKOVIEW_JARS) \
+  $(FENNEC_JARS) \
+  $(NULL)
+
 # The list of jars in Java classpath notation (colon-separated).
 all_jars_classpath := $(subst $(NULL) ,:,$(strip $(ALL_JARS)))
 
 include $(topsrcdir)/config/config.mk
 
 library_jars := \
     $(ANDROID_SDK)/android.jar \
     $(NULL)
@@ -290,29 +304,27 @@ classycle_jar := $(topsrcdir)/mobile/and
 		-injars $(subst ::,:,$(all_jars_classpath)):bundled-jars-nodebug \
 		-outjars jars-proguarded \
 		-libraryjars $(library_jars)
 
 ANNOTATION_PROCESSOR_JAR_FILES := $(DEPTH)/build/annotationProcessors/annotationProcessors.jar
 
 # This annotation processing step also generates
 # GeneratedJNIWrappers.h and GeneratedJNINatives.h
-GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES)
-GeneratedJNIWrappers.cpp: $(ALL_JARS)
-	# (bug 1278008) I added all_jars_classpath here but I'm not certain
-	# this is optimal. The annotation processor throws NoClassDefFoundError
-	# because it is missing the adjust SDK on its classpath so ideally,
-	# we'd just add that. However, another NoClassDefFoundError is thrown
-	# for HttpClient and I could not fix that.
-	#
-	# I don't want to spend too much time on this & it seems harmless to
-	# add the sources we're actively compiling to classpath of the
-	# annotation processor, which includes adjust & whatever lib we're
-	# missing for HttpClient, so I opted for this solution.
-	$(JAVA) -classpath $(all_jars_classpath):constants.jar:$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) org.mozilla.gecko.annotationProcessors.AnnotationProcessor $(ALL_JARS)
+GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) $(GECKOVIEW_JARS)
+	$(JAVA) -classpath $(geckoview_jars_classpath):$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) \
+		org.mozilla.gecko.annotationProcessors.AnnotationProcessor \
+		Generated $(GECKOVIEW_JARS)
+
+# This annotation processing step also generates
+# FennecJNIWrappers.h and FennecJNINatives.h
+FennecJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) $(FENNEC_JARS)
+	$(JAVA) -classpath $(all_jars_classpath):$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) \
+		org.mozilla.gecko.annotationProcessors.AnnotationProcessor \
+		Fennec $(FENNEC_JARS)
 
 # Certain source files need to be preprocessed.  This special rule
 # generates these files into generated/org/mozilla/gecko for
 # consumption by the build system and IDEs.
 
 # The list in moz.build looks like
 # 'preprocessed/org/mozilla/gecko/AppConstants.java'.  The list in
 # constants_PP_JAVAFILES looks like
@@ -473,19 +485,27 @@ else
 # .aapt.nodeps: $(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE
 $(eval $(call aapt_command,.aapt.nodeps,$(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
 endif
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
 update-generated-wrappers:
-	@cp $(CURDIR)/GeneratedJNIWrappers.cpp $(CURDIR)/GeneratedJNIWrappers.h $(CURDIR)/GeneratedJNINatives.h $(topsrcdir)/widget/android
+	@cp $(CURDIR)/GeneratedJNIWrappers.cpp \
+	    $(CURDIR)/GeneratedJNIWrappers.h \
+	    $(CURDIR)/GeneratedJNINatives.h $(topsrcdir)/widget/android
 	@echo Updated generated JNI code
 
+update-fennec-wrappers:
+	@cp $(CURDIR)/FennecJNIWrappers.cpp \
+	    $(CURDIR)/FennecJNIWrappers.h \
+	    $(CURDIR)/FennecJNINatives.h $(topsrcdir)/widget/android/fennec
+	@echo Updated Fennec JNI code
+
 .PHONY: update-generated-wrappers
 
 # This target is only used by IDE integrations. It rebuilds resources
 # that end up in omni.ja using the equivalent of |mach build faster|,
 # does most of the packaging step, and then updates omni.ja in
 # place. If you're not using an IDE, you should be using |mach build
 # mobile/android && mach package|.
 $(ABS_DIST)/fennec/$(OMNIJAR_NAME): FORCE
@@ -508,31 +528,47 @@ gradle-omnijar: $(abspath $(DIST)/fennec
 else
 # In automation, omni.ja is built only during packaging.
 gradle-omnijar:
 endif
 
 .PHONY: gradle-targets gradle-omnijar
 
 # GeneratedJNIWrappers.cpp target also generates
-# GeneratedJNIWrappers.h and GeneratedJNINatives.h
+#   GeneratedJNIWrappers.h and GeneratedJNINatives.h
+# FennecJNIWrappers.cpp target also generates
+#   FennecJNIWrappers.h and FennecJNINatives.h
 ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 libs:: GeneratedJNIWrappers.cpp
 	@(diff GeneratedJNIWrappers.cpp $(topsrcdir)/widget/android/GeneratedJNIWrappers.cpp >/dev/null && \
 	  diff GeneratedJNIWrappers.h $(topsrcdir)/widget/android/GeneratedJNIWrappers.h >/dev/null && \
 	  diff GeneratedJNINatives.h $(topsrcdir)/widget/android/GeneratedJNINatives.h >/dev/null) || \
 	 (echo '*****************************************************' && \
 	  echo '***   Error: The generated JNI code has changed   ***' && \
 	  echo '* To update generated code in the tree, please run  *' && \
 	  echo && \
 	  echo '  make -C $(CURDIR) update-generated-wrappers' && \
 	  echo && \
 	  echo '* Repeat the build, and check in any changes.       *' && \
 	  echo '*****************************************************' && \
 	  exit 1)
+
+libs:: FennecJNIWrappers.cpp
+	@(diff FennecJNIWrappers.cpp $(topsrcdir)/widget/android/fennec/FennecJNIWrappers.cpp >/dev/null && \
+	  diff FennecJNIWrappers.h $(topsrcdir)/widget/android/fennec/FennecJNIWrappers.h >/dev/null && \
+	  diff FennecJNINatives.h $(topsrcdir)/widget/android/fennec/FennecJNINatives.h >/dev/null) || \
+	 (echo '*****************************************************' && \
+	  echo '***     Error: The Fennec JNI code has changed    ***' && \
+	  echo '* To update generated code in the tree, please run  *' && \
+	  echo && \
+	  echo '  make -C $(CURDIR) update-fennec-wrappers' && \
+	  echo && \
+	  echo '* Repeat the build, and check in any changes.       *' && \
+	  echo '*****************************************************' && \
+	  exit 1)
 endif
 
 libs:: classes.dex
 	$(INSTALL) classes.dex $(FINAL_TARGET)
 
 # Generate Java binder interfaces from AIDL files.
 aidl_src_path := $(srcdir)/aidl
 aidl_target_path := generated
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -3,26 +3,64 @@
  * 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/. */
 
 package org.mozilla.gecko.customtabs;
 
 import android.os.Bundle;
 
 import org.mozilla.gecko.GeckoApp;
+import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.util.GeckoRequest;
+import org.mozilla.gecko.util.NativeJSObject;
+import org.mozilla.gecko.util.ThreadUtils;
 
 public class CustomTabsActivity extends GeckoApp {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
     }
 
     @Override
     public int getLayout() {
         return R.layout.customtabs_activity;
     }
 
     @Override
     public void onBackPressed() {
-        finish();
+        final Tabs tabs = Tabs.getInstance();
+        final Tab tab = tabs.getSelectedTab();
+
+        // Give Gecko a chance to handle the back press first, then fallback to the Java UI.
+        GeckoAppShell.sendRequestToGecko(new GeckoRequest("Browser:OnBackPressed", null) {
+            @Override
+            public void onResponse(NativeJSObject nativeJSObject) {
+                if (!nativeJSObject.getBoolean("handled")) {
+                    // Default behavior is Gecko didn't prevent.
+                    onDefault();
+                }
+            }
+
+            @Override
+            public void onError(NativeJSObject error) {
+                // Default behavior is Gecko didn't prevent, via failure.
+                onDefault();
+            }
+
+            // Return from Gecko thread, then back-press through the Java UI.
+            private void onDefault() {
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (tab.doBack()) {
+                            return;
+                        }
+
+                        finish();
+                    }
+                });
+            }
+        });
     }
 }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -193,22 +193,26 @@ if CONFIG['MOZ_WEBRTC']:
         audio_root + 'WebRtcAudioRecord.java',
         audio_root + 'WebRtcAudioTrack.java',
         audio_root + 'WebRtcAudioUtils.java',
     ]
     wrjar.extra_jars = [
         'constants.jar',
         'gecko-R.jar',
         'gecko-browser.jar',
+        'gecko-mozglue.jar',
         'gecko-util.jar',
-        'gecko-mozglue.jar',
+        'gecko-view.jar',
     ]
     wrjar.javac_flags += ['-Xlint:all,-deprecation,-cast']
 
-geckoview_java_files = [
+gvjar = add_java_jar('gecko-view')
+
+gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
+                  for x in [
     'AlarmReceiver.java',
     'AndroidGamepadManager.java',
     'ANRReporter.java',
     'BaseGeckoInterface.java',
     'ContextGetter.java',
     'CrashHandler.java',
     'EventDispatcher.java',
     'GeckoAccessibility.java',
@@ -278,21 +282,42 @@ geckoview_java_files = [
     'PrefsHelper.java',
     'SmsManager.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
     'TouchEventInterceptor.java',
     'widget/SwipeDismissListViewTouchListener.java',
+]]
+
+gvjar.sources += [thirdparty_source_dir + f for f in [
+    'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
+    'com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
+    'com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
+]]
+
+gvjar.extra_jars += [
+    CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
+    CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
+    'constants.jar',
+    'gecko-R.jar',
+    'gecko-mozglue.jar',
+    'gecko-util.jar',
 ]
 
+gvjar.javac_flags += [
+    '-Xlint:all,-deprecation,-fallthrough',
+    '-J-Xmx512m',
+    '-J-Xms128m'
+]
+
+
 gbjar = add_java_jar('gecko-browser')
-gbjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
-                  for x in geckoview_java_files]
 
 gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
     'AboutPages.java',
     'AccountsHelper.java',
     'ActionBarTextSelection.java',
     'ActionModeCompat.java',
     'ActionModeCompatView.java',
     'ActivityHandlerHelper.java',
@@ -737,21 +762,16 @@ gbjar.sources += ['java/org/mozilla/geck
     'widget/themed/ThemedImageButton.java',
     'widget/themed/ThemedImageView.java',
     'widget/themed/ThemedLinearLayout.java',
     'widget/themed/ThemedRelativeLayout.java',
     'widget/themed/ThemedTextSwitcher.java',
     'widget/themed/ThemedTextView.java',
     'widget/themed/ThemedView.java',
 ]]
-gbjar.sources += [ thirdparty_source_dir + f for f in [
-    'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
-    'com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
-    'com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
-] ]
 android_package_dir = CONFIG['ANDROID_PACKAGE_NAME'].replace('.', '/')
 gbjar.generated_sources = [] # Keep it this way.
 gbjar.extra_jars += [
     CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
     CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
     CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
     'constants.jar'
 ]
@@ -789,16 +809,17 @@ if max_sdk_version >= 11:
     ]]
 
 gbjar.extra_jars += [
     OBJDIR + '/../javaaddons/javaaddons-1.0.jar',
     'gecko-R.jar',
     'gecko-mozglue.jar',
     'gecko-thirdparty.jar',
     'gecko-util.jar',
+    'gecko-view.jar',
     'sync-thirdparty.jar',
     'services.jar',
 ]
 
 moz_native_devices_jars = [
     CONFIG['ANDROID_MEDIAROUTER_V7_AAR_LIB'],
     CONFIG['ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB'],
     CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_LIB'],
@@ -1064,17 +1085,18 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']
         CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
         CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
         CONFIG['ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB'],
         'constants.jar',
         'gecko-R.jar',
         'gecko-browser.jar',
         'gecko-mozglue.jar',
         'gecko-thirdparty.jar',
-        'gecko-util.jar'
+        'gecko-util.jar',
+        'gecko-view.jar',
     ]
 
 FINAL_TARGET_PP_FILES += ['package-name.txt.in']
 
 DEFINES['OBJDIR'] = OBJDIR
 DEFINES['TOPOBJDIR'] = TOPOBJDIR
 
 OBJDIR_PP_FILES.mobile.android.base += [
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.gfx;
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 
 import org.mozilla.gecko.AndroidGamepadManager;
-import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAccessibility;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoThread;
@@ -681,21 +680,21 @@ public class LayerView extends FrameLayo
         return mFullScreenState;
     }
 
     public void setMaxTranslation(float aMaxTranslation) {
         mToolbarAnimator.setMaxTranslation(aMaxTranslation);
     }
 
     public void setSurfaceTranslation(float translation) {
-        ViewHelper.setTranslationY(this, translation);
+        setTranslationY(translation);
     }
 
     public float getSurfaceTranslation() {
-        return ViewHelper.getTranslationY(this);
+        return getTranslationY();
     }
 
     // Public hooks for dynamic toolbar translation
 
     public interface DynamicToolbarListener {
         public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation);
         public void onPanZoomStopped();
         public void onMetricsChanged(ImmutableViewportMetrics viewport);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -160,21 +160,27 @@ class NativePanZoomController extends JN
             mPointerScrollFactor = outValue.getDimension(view.getContext().getResources().getDisplayMetrics());
         } else {
             mPointerScrollFactor = MAX_SCROLL;
         }
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
-            return handleMouseEvent(event);
-        } else {
-            return handleMotionEvent(event);
-        }
+// NOTE: This commented out block of code allows Fennec to generate
+//       mouse event instead of converting them to touch events.
+//       This gives Fennec similar behaviour to desktop when using
+//       a mouse.
+//
+//        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+//            return handleMouseEvent(event);
+//        } else {
+//            return handleMotionEvent(event);
+//        }
+        return handleMotionEvent(event);
     }
 
     @Override
     public boolean onMotionEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_SCROLL) {
             if (event.getDownTime() >= mLastDownTime) {
                 mLastDownTime = event.getDownTime();
--- a/mobile/android/tests/background/junit3/moz.build
+++ b/mobile/android/tests/background/junit3/moz.build
@@ -17,16 +17,17 @@ jar.extra_jars += [
     CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
     CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB'],
     TOPOBJDIR + '/mobile/android/base/constants.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-R.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-browser.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-mozglue.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-thirdparty.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-util.jar',
+    TOPOBJDIR + '/mobile/android/base/gecko-view.jar',
     TOPOBJDIR + '/mobile/android/base/services.jar',
     TOPOBJDIR + '/mobile/android/base/sync-thirdparty.jar',
 ]
 
 if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
     jar.extra_jars += [
         TOPOBJDIR + '/mobile/android/stumbler/stumbler.jar',
     ]
--- a/mobile/android/tests/browser/junit3/moz.build
+++ b/mobile/android/tests/browser/junit3/moz.build
@@ -30,16 +30,17 @@ jar.extra_jars += [
     CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
     CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB'],
     TOPOBJDIR + '/mobile/android/base/constants.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-R.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-browser.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-mozglue.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-thirdparty.jar',
     TOPOBJDIR + '/mobile/android/base/gecko-util.jar',
+    TOPOBJDIR + '/mobile/android/base/gecko-view.jar',
     TOPOBJDIR + '/mobile/android/base/services.jar',
     TOPOBJDIR + '/mobile/android/base/sync-thirdparty.jar',
 ]
 
 if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
     jar.extra_jars += [
         TOPOBJDIR + '/mobile/android/stumbler/stumbler.jar',
     ]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -775,16 +775,18 @@ pref("gfx.canvas.azure.backends", "skia"
 // Accelerated cg canvas where available (10.7+)
 pref("gfx.canvas.azure.accelerated", true);
 #else
 pref("gfx.canvas.azure.backends", "skia");
 pref("gfx.content.azure.backends", "skia");
 #endif
 #endif
 
+pref("gfx.text.disable-aa", false);
+
 pref("gfx.work-around-driver-bugs", true);
 pref("gfx.prefer-mesa-llvmpipe", false);
 
 pref("gfx.draw-color-bars", false);
 
 pref("gfx.logging.painted-pixel-count.enabled", false);
 pref("gfx.logging.texture-usage.enabled", false);
 pref("gfx.logging.peak-texture-usage.enabled", false);
--- a/python/gdbpp/gdbpp/thashtable.py
+++ b/python/gdbpp/gdbpp/thashtable.py
@@ -91,36 +91,52 @@ class thashtable_printer(object):
             if f.name == 'mKeyHash' or f.name == 'mData':
                 continue
             # ...and assume the first one we find is the key.
             self.key_field_name = f.name
             break
 
     def children(self):
         table = self.value['mTable']
-        # Number of entries
+
+        # mEntryCount is the number of occupied slots/entries in the table.
+        # We can use this to avoid doing wasted memory reads.
         entryCount = table['mEntryCount']
+        if entryCount == 0:
+            return
+
+        # The table capacity is tracked "cleverly" in terms of how many bits
+        # the hash needs to be shifted.  CapacityFromHashShift calculates the
+        # actual entry capacity via ((uint32_t)1 << (kHashBits - mHashShift));
+        capacity = 1 << (table['kHashBits'] - table['mHashShift'])
+
         # Pierce generation-tracking EntryStore class to get at buffer.  The
         # class instance always exists, but this char* may be null.
         store = table['mEntryStore']['mEntryStore']
 
         key_field_name = self.key_field_name
 
+        seenCount = 0
         pEntry = store.cast(self.entry_type.pointer())
-        for i in range(0, int(entryCount)):
+        for i in range(0, int(capacity)):
             entry = (pEntry + i).dereference()
             # An mKeyHash of 0 means empty, 1 means deleted sentinel, so skip
             # if that's the case.
             if entry['mKeyHash'] <= 1:
                 continue
 
             yield ('%d' % i, entry[key_field_name])
             if self.is_table:
                 yield ('%d' % i, entry['mData'])
 
+            # Stop iterating if we know there are no more occupied slots.
+            seenCount += 1
+            if seenCount >= entryCount:
+                break
+
     def to_string(self):
         # The most specific template type is the most interesting.
         return str(self.outermost_type)
 
     def display_hint(self):
         if self.is_table:
             return 'map'
         else:
new file mode 100644
--- /dev/null
+++ b/release/docker/firefox-snap/Dockerfile
@@ -0,0 +1,3 @@
+FROM ubuntu:16.04
+
+RUN apt-get update && apt-get install -qy snapcraft bzip2 curl && apt-get clean
new file mode 100644
--- /dev/null
+++ b/release/docker/firefox-snap/Makefile
@@ -0,0 +1,12 @@
+DOCKERIO_USERNAME =$(error DOCKERIO_USERNAME should be set)
+IMAGE_NAME = firefox-snapcraft
+FULL_IMAGE_NAME = $(DOCKERIO_USERNAME)/$(IMAGE_NAME)
+
+build:
+	docker build -t $(FULL_IMAGE_NAME) --no-cache --rm .
+
+push:
+	docker push $(FULL_IMAGE_NAME):latest
+
+pull:
+	docker pull $(FULL_IMAGE_NAME):latest
new file mode 100644
--- /dev/null
+++ b/release/docker/firefox-snap/distribution.ini
@@ -0,0 +1,9 @@
+[Global]
+id=mozilla-snap
+version=1.0
+about=Mozilla Firefox Snap
+
+[Preferences]
+app.update.enabled=false
+intl.locale.matchOS=true
+browser.shell.checkDefaultBrowser=false
new file mode 100755
--- /dev/null
+++ b/release/docker/firefox-snap/runme.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+set -xe
+
+# Required env variables
+test $VERSION
+test $BUILD_NUMBER
+test $CANDIDATES_DIR
+
+# Optional env variables
+: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
+: ARTIFACTS_DIR                 ${ARTIFACTS_DIR:=/home/worker/artifacts}
+
+
+TARGET="firefox-${VERSION}.snap"
+
+mkdir -p "$ARTIFACTS_DIR"
+rm -rf "${WORKSPACE}/source" && mkdir -p "${WORKSPACE}/source/opt" "${WORKSPACE}/source/usr/bin"
+
+CURL="curl --location --retry 10 --retry-delay 10"
+
+# Download and extract en-US linux64 binary
+$CURL -o "${WORKSPACE}/firefox.tar.bz2" \
+    "${CANDIDATES_DIR}/${VERSION}-candidates/build${BUILD_NUMBER}/linux-x86_64/en-US/firefox-${VERSION}.tar.bz2"
+
+tar -C "${WORKSPACE}/source/opt" -xf "${WORKSPACE}/firefox.tar.bz2"
+mkdir -p "${WORKSPACE}/source/opt/firefox/distribution/extensions"
+cp -v distribution.ini "${WORKSPACE}/source/opt/firefox/distribution/"
+
+# Use release-specific list of locales to fetch L10N XPIs
+$CURL -o "${WORKSPACE}/l10n_changesets.txt" "${CANDIDATES_DIR}/${VERSION}-candidates/build${BUILD_NUMBER}/l10n_changesets.txt"
+cat "${WORKSPACE}/l10n_changesets.txt"
+
+for locale in $(grep -v ja-JP-mac "${WORKSPACE}/l10n_changesets.txt" | awk '{print $1}'); do
+    $CURL -o "${WORKSPACE}/source/opt/firefox/distribution/extensions/langpack-${locale}@firefox.mozilla.org.xpi" \
+        "$CANDIDATES_DIR/${VERSION}-candidates/build${BUILD_NUMBER}/linux-x86_64/xpi/${locale}.xpi"
+done
+
+# Symlink firefox binary to /usr/bin to make it available in PATH
+ln -s ../../opt/firefox/firefox "${WORKSPACE}/source/usr/bin"
+
+# Generate snapcraft manifest
+sed -e "s/@VERSION@/${VERSION}/g" -e "s/@BUILD_NUMBER@/${BUILD_NUMBER}/g" snapcraft.yaml.in > ${WORKSPACE}/snapcraft.yaml
+cd ${WORKSPACE}
+snapcraft
+
+mv *.snap "$ARTIFACTS_DIR/$TARGET"
+
+cd $ARTIFACTS_DIR
+
+# Generate checksums file
+size=$(stat --printf="%s" $ARTIFACTS_DIR/$TARGET)
+sha=$(sha512sum $ARTIFACTS_DIR/$TARGET | awk '{print $1}')
+echo "$sha sha512 $size $TARGET" > $TARGET.checksums
+
+echo "Generating signing manifest"
+hash=$(sha512sum $TARGET.checksums | awk '{print $1}')
+
+cat << EOF > signing_manifest.json
+[{"file_to_sign": "$TARGET.checksums", "hash": "$hash"}]
+EOF
+
+# For posterity
+find . -ls
+cat $TARGET.checksums
+cat signing_manifest.json
new file mode 100644
--- /dev/null
+++ b/release/docker/firefox-snap/snapcraft.yaml.in
@@ -0,0 +1,37 @@
+name: firefox
+version: @VERSION@-@BUILD_NUMBER@
+summary: Mozilla Firefox web browser
+description:  Firefox is a powerful, extensible web browser with support for modern web application technologies.
+confinement: strict
+
+apps:
+  firefox:
+    command: desktop-launch firefox
+    plugs:
+      - unity7
+      - network
+      - home
+      - x11
+      - opengl
+      - pulseaudio
+      - gsettings
+      - camera
+      - browser-sandbox
+
+plugs:
+  browser-sandbox:
+    interface: browser-support
+    allow-sandbox: true
+
+parts:
+  firefox:
+    plugin: dump
+    source: source
+    stage-packages:
+      - libxt6
+      - libdbus-glib-1-2
+      - libasound2
+      - libpulse0
+      - libgl1-mesa-dri
+      - libgl1-mesa-glx
+    after: [desktop-gtk3]
--- a/testing/mochitest/tests/browser/browser_browserLoaded_content_loaded.js
+++ b/testing/mochitest/tests/browser/browser_browserLoaded_content_loaded.js
@@ -1,24 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 'use strict';
 
-// Check if the browser has loaded by checking ready state of the DOM.
-function isDOMLoaded(aBrowser) {
-  return aBrowser.contentWindowAsCPOW.document.readyState === 'complete';
+function isDOMLoaded(browser) {
+  return ContentTask.spawn(browser, null, function*() {
+    Assert.equal(content.document.readyState, "complete",
+      "Browser should be loaded.");
+  });
 }
 
 // It checks if calling BrowserTestUtils.browserLoaded() yields
 // browser object.
 add_task(function*() {
   let tab = gBrowser.addTab('http://example.com');
   let browser = tab.linkedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
-  Assert.ok(isDOMLoaded(browser), 'browser', 'Expect browser to have loaded.');
+  yield isDOMLoaded(browser);
   gBrowser.removeTab(tab);
 });
 
 // It checks that BrowserTestUtils.browserLoaded() works well with
 // promise.all().
 add_task(function*() {
   let tabURLs = [
     `http://example.org`,
@@ -29,14 +31,16 @@ add_task(function*() {
   let browsers = [
     for (u of tabURLs) gBrowser.addTab(u).linkedBrowser
   ];
   //wait for promises to settle
   yield Promise.all((
     for (b of browsers) BrowserTestUtils.browserLoaded(b)
   ));
   let expected = 'Expected all promised browsers to have loaded.';
-  Assert.ok(browsers.every(isDOMLoaded), expected);
+  for (const browser of browsers) {
+    yield isDOMLoaded(browser);
+  }
   //cleanup
   browsers
     .map(browser => gBrowser.getTabForBrowser(browser))
     .forEach(tab => gBrowser.removeTab(tab));
 });
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/beetmover/snap.yml.tmpl
@@ -0,0 +1,11 @@
+---
+metadata:
+    name: "Beet Mover Manifest"
+    description: "Maps artifact locations to s3 key names for snap iamge"
+    owner: "release@mozilla.com"
+
+mapping:
+  all:
+    snap:
+      artifact: {{ artifact_base_url }}/firefox-{{ version }}.snap
+      s3_key: {{ s3_prefix }}snap/firefox-{{ version }}.snap
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/beetmover/snap_checksums.yml.tmpl
@@ -0,0 +1,14 @@
+---
+metadata:
+    name: "Beet Mover Manifest"
+    description: "Maps artifact locations to s3 key names for snap checksums"
+    owner: "release@mozilla.com"
+
+mapping:
+  all:
+    snap_checksum:
+      artifact: {{ artifact_base_url }}/firefox-{{ version }}.snap.checksums
+      s3_key: {{ s3_prefix }}snap/firefox-{{ version }}.snap.checksums
+    snap_checksum_asc:
+      artifact: {{ artifact_base_url }}/firefox-{{ version }}.snap.checksums.asc
+      s3_key: {{ s3_prefix }}snap/firefox-{{ version }}.snap.checksums.asc
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -11,23 +11,25 @@ import math
 import mozdebug
 import mozinfo
 import os
 import os.path
 import random
 import re
 import shutil
 import signal
+import subprocess
 import sys
 import tempfile
 import time
 import traceback
 
 from collections import deque, namedtuple
 from distutils import dir_util
+from distutils.version import LooseVersion
 from multiprocessing import cpu_count
 from argparse import ArgumentParser
 from subprocess import Popen, PIPE, STDOUT
 from tempfile import mkdtemp, gettempdir
 from threading import (
     Timer,
     Thread,
     Event,
@@ -988,17 +990,27 @@ class XPCShellTests(object):
         """
         nodeMozInfo = {'hasNode': False} # Assume the worst
         nodeBin = None
 
         # We try to find the node executable in the path given to us by the user in
         # the MOZ_NODE_PATH environment variable
         localPath = os.getenv('MOZ_NODE_PATH', None)
         if localPath and os.path.exists(localPath) and os.path.isfile(localPath):
-            nodeBin = localPath
+            try:
+                version_str = subprocess.check_output([localPath, "--version"],
+                                                      stderr=subprocess.STDOUT)
+                # nodejs prefixes its version strings with "v"
+                version = LooseVersion(version_str.lstrip('v'))
+                # Use node only if node version is >=5.0.0 because
+                # node did not support ALPN until this version.
+                if version >= LooseVersion("5.0.0"):
+                    nodeBin = localPath
+            except (subprocess.CalledProcessError, OSError), e:
+                self.log.error('Could not retrieve node version: %s' % str(e))
 
         if nodeBin:
             self.log.info('Found node at %s' % (nodeBin,))
 
             def startServer(name, serverJs):
                 if os.path.exists(serverJs):
                     # OK, we found our server, let's try to get it running
                     self.log.info('Found %s at %s' % (name, serverJs))
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -57,17 +57,17 @@
 #endif
 #endif
 
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
-#include "GeneratedJNIWrappers.h"
+#include "FennecJNIWrappers.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
 #endif
 
 using namespace mozilla;
 using mozilla::downloads::GenerateGUID;
--- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
+++ b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
@@ -23,17 +23,17 @@
 #endif
 
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #include "../../../../xpcom/io/CocoaFileUtils.h"
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
-#include "GeneratedJNIWrappers.h"
+#include "FennecJNIWrappers.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
 #endif
 
 using namespace mozilla;
 
@@ -105,17 +105,17 @@ nsresult DownloadPlatform::DownloadDone(
   if (aTarget && NS_SUCCEEDED(aTarget->GetPath(path))) {
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
     // On Windows and Gtk, add the download to the system's "recent documents"
     // list, with a pref to disable.
     {
       bool addToRecentDocs = Preferences::GetBool(PREF_BDM_ADDTORECENTDOCS);
 #ifdef MOZ_WIDGET_ANDROID
       if (addToRecentDocs) {
-        java::DownloadsIntegration::ScanMedia(path, NS_ConvertUTF8toUTF16(aContentType));
+        java::DownloadsIntegration::ScanMedia(path, aContentType);
       }
 #else
       if (addToRecentDocs && !aIsPrivate) {
 #ifdef XP_WIN
         ::SHAddToRecentDocs(SHARD_PATHW, path.get());
 #elif defined(MOZ_WIDGET_GTK)
         GtkRecentManager* manager = gtk_recent_manager_get_default();
 
--- a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
 /* 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 "nsParentalControlsService.h"
 #include "nsString.h"
 #include "nsIFile.h"
-#include "GeneratedJNIWrappers.h"
+#include "FennecJNIWrappers.h"
 
 namespace java = mozilla::java;
 
 NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService)
 
 nsParentalControlsService::nsParentalControlsService() :
   mEnabled(false)
 {
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -567,17 +567,21 @@ var PrintUtils = {
         printPreviewTB.updateToolbar();
         ppBrowser.collapsed = false;
         ppBrowser.focus();
         return;
       }
 
       // Set the original window as an active window so any mozPrintCallbacks can
       // run without delayed setTimeouts.
-      this._sourceBrowser.docShellIsActive = true;
+      if (this._listener.activateBrowser) {
+        this._listener.activateBrowser(this._sourceBrowser);
+      } else {
+        this._sourceBrowser.docShellIsActive = true;
+      }
 
       // show the toolbar after we go into print preview mode so
       // that we can initialize the toolbar with total num pages
       const XUL_NS =
         "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
       printPreviewTB = document.createElementNS(XUL_NS, "toolbar");
       printPreviewTB.setAttribute("printpreview", true);
       printPreviewTB.setAttribute("fullscreentoolbar", true);
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -752,16 +752,20 @@ var PrintPreviewListener = {
     gBrowser.collapsed = true;
   },
 
   onExit() {
     this._ppBrowser.remove();
     gBrowser.collapsed = false;
     document.getElementById("viewSource-toolbox").hidden = false;
   },
+
+  activateBrowser(browser) {
+    browser.docShellIsActive = true;
+  },
 };
 
 // viewZoomOverlay.js uses this
 function getBrowser() {
   return gBrowser;
 }
 
 this.__defineGetter__("gPageLoader", function () {
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -291,22 +291,20 @@
           <![CDATA[
             if (this.docShell)
               return this.docShell.isActive = val;
             return false;
           ]]>
         </setter>
       </property>
 
-      <method name="setDocShellIsActiveAndForeground">
-        <parameter name="isActive"/>
+      <method name="preserveLayers">
+        <parameter name="preserve"/>
         <body>
-          <![CDATA[
-            this.docShell && this.docShell.setIsActiveAndForeground(isActive);
-          ]]>
+          // Only useful for remote browsers.
         </body>
       </method>
 
       <method name="makePrerenderedBrowserActive">
         <body>
         <![CDATA[
           let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
           if (frameLoader) {
@@ -1283,16 +1281,21 @@
               aOtherBrowser._remoteWebProgressManager.swapBrowser(aOtherBrowser);
             }
 
             if (this._remoteFinder)
               this._remoteFinder.swapBrowser(this);
             if (aOtherBrowser._remoteFinder)
               aOtherBrowser._remoteFinder.swapBrowser(aOtherBrowser);
           }
+
+          event = new CustomEvent("EndSwapDocShells", {"detail": aOtherBrowser});
+          this.dispatchEvent(event);
+          event = new CustomEvent("EndSwapDocShells", {"detail": this});
+          aOtherBrowser.dispatchEvent(event);
         ]]>
         </body>
       </method>
 
       <method name="getInPermitUnload">
         <parameter name="aCallback"/>
         <body>
         <![CDATA[
@@ -1328,19 +1331,16 @@
                                          Components.results.NS_ERROR_FAILURE);
             }
 
             owner.frameLoader.print(aOuterWindowID, aPrintSettings,
                                     aPrintProgressListener);
           ]]>
         </body>
       </method>
-
-      <!-- This will go away if the binding has been removed for some reason. -->
-      <field name="_alive">true</field>
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_F7" group="system">
         <![CDATA[
           if (event.defaultPrevented || !event.isTrusted)
             return;
 
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -254,23 +254,23 @@
           <![CDATA[
             let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
             frameLoader.tabParent.docShellIsActive = val;
             return val;
           ]]>
         </setter>
       </property>
 
-      <method name="setDocShellIsActiveAndForeground">
-        <parameter name="isActive"/>
+      <method name="preserveLayers">
+        <parameter name="preserve"/>
         <body><![CDATA[
-          // See the explanation for what this does in nsITabParent.ipdl
-
           let {frameLoader} = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
-          frameLoader.tabParent.setDocShellIsActiveAndForeground(isActive);
+          if (frameLoader.tabParent) {
+            frameLoader.tabParent.preserveLayers(preserve);
+          }
         ]]></body>
       </method>
 
       <field name="_manifestURI"/>
       <property name="manifestURI"
                 onget="return this._manifestURI"
                 readonly="true"/>
 
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -882,21 +882,18 @@ FinderHighlighter.prototype = {
       }
       dict.modalHighlightOutline = null;
     }
 
     // Abort when there's no text to highlight.
     if (!textContent.length)
       return;
 
-    let container, outlineBox;
+    let outlineBox;
     if (rebuildOutline) {
-      // The outline needs to be sitting inside a container, otherwise the anonymous
-      // content API won't find it by its ID later...
-      container = document.createElementNS(kNSHTML, "div");
       // Create the main (yellow) highlight outline box.
       outlineBox = document.createElementNS(kNSHTML, "div");
       outlineBox.setAttribute("id", kModalOutlineId);
     }
 
     const kModalOutlineTextId = kModalOutlineId + "-text";
     let i = 0;
     for (let rect of rects) {
@@ -931,21 +928,20 @@ FinderHighlighter.prototype = {
         // activate the transitions.
         outlineAnonNode.setAttributeForElement(kModalOutlineId, "style", outlineStyle);
         outlineAnonNode.setAttributeForElement(kModalOutlineTextId, "style", textStyle);
         outlineAnonNode.setTextContentForElement(kModalOutlineTextId, text);
       }
     }
 
     if (rebuildOutline) {
-      container.appendChild(outlineBox);
       dict.modalHighlightOutline = kDebug ?
         mockAnonymousContentNode((document.body ||
-          document.documentElement).appendChild(container.firstChild)) :
-        document.insertAnonymousContent(container);
+          document.documentElement).appendChild(outlineBox)) :
+        document.insertAnonymousContent(outlineBox);
     }
   },
 
   /**
    * Add a range to the list of ranges to highlight on, or cut out of, the dimmed
    * background.
    *
    * @param {nsIDOMRange}  range  Range object that should be inspected
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/gtest/TestProfileLock.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "gtest/gtest.h"
+
+#include <sys/eventfd.h>
+#include <sched.h>
+
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsProfileLock.h"
+#include "nsString.h"
+
+static void CleanParentLock(const char *tmpdir)
+{
+  // nsProfileLock doesn't clean up all its files
+  char permanent_lockfile[] = "/.parentlock";
+
+  char * parentlock_name;
+  size_t parentlock_name_size = strlen(tmpdir) + strlen(permanent_lockfile) + 1;
+  parentlock_name = (char*)malloc(parentlock_name_size);
+
+  strcpy(parentlock_name, tmpdir);
+  strcat(parentlock_name, permanent_lockfile);
+
+  EXPECT_EQ(0, unlink(parentlock_name));
+  EXPECT_EQ(0, rmdir(tmpdir));
+  free(parentlock_name);
+}
+
+TEST(ProfileLock, BasicLock)
+{
+  char templ[] = "/tmp/profilelocktest.XXXXXX";
+  char * tmpdir = mkdtemp(templ);
+  ASSERT_NE(tmpdir, nullptr);
+
+  // This scope exits so the nsProfileLock destructor
+  // can clean up the files it leaves in tmpdir.
+  {
+    nsProfileLock myLock;
+    nsresult rv;
+    nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = dir->InitWithNativePath(nsCString(tmpdir));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = myLock.Lock(dir, nullptr);
+    EXPECT_EQ(NS_SUCCEEDED(rv), true);
+  }
+
+  CleanParentLock(tmpdir);
+}
+
+TEST(ProfileLock, RetryLock)
+{
+  char templ[] = "/tmp/profilelocktest.XXXXXX";
+  char * tmpdir = mkdtemp(templ);
+  ASSERT_NE(tmpdir, nullptr);
+
+  {
+    nsProfileLock myLock;
+    nsProfileLock myLock2;
+    nsresult rv;
+    nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = dir->InitWithNativePath(nsCString(tmpdir));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    int eventfd_fd = eventfd(0, 0);
+    ASSERT_GE(eventfd_fd, 0);
+
+    // fcntl advisory locks are per process, so it's hard
+    // to avoid using fork here.
+    pid_t childpid = fork();
+
+    if (childpid) {
+      // parent
+
+      // blocking read causing us to lose the race
+      eventfd_t value;
+      EXPECT_EQ(0, eventfd_read(eventfd_fd, &value));
+
+      rv = myLock2.Lock(dir, nullptr);
+      EXPECT_EQ(NS_ERROR_FILE_ACCESS_DENIED, rv);
+
+      // kill the child
+      EXPECT_EQ(0, kill(childpid, SIGTERM));
+
+      // reap zombie (required to collect the lock)
+      int status;
+      EXPECT_EQ(childpid, waitpid(childpid, &status, 0));
+
+      rv = myLock2.Lock(dir, nullptr);
+      EXPECT_EQ(NS_SUCCEEDED(rv), true);
+    } else {
+      // child
+      rv = myLock.Lock(dir, nullptr);
+      ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+      // unblock parent
+      EXPECT_EQ(0, eventfd_write(eventfd_fd, 1));
+
+      // parent will kill us
+      for (;;)
+        sleep(1);
+    }
+
+    close(eventfd_fd);
+  }
+
+  CleanParentLock(tmpdir);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/gtest/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+LOCAL_INCLUDES += [
+    '..',
+]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+    UNIFIED_SOURCES += [
+        'TestProfileLock.cpp',
+    ]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/toolkit/profile/moz.build
+++ b/toolkit/profile/moz.build
@@ -1,16 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['gtest']
+
 XPIDL_SOURCES += [
     'nsIProfileMigrator.idl',
     'nsIProfileUnlocker.idl',
     'nsIToolkitProfile.idl',
     'nsIToolkitProfileService.idl',
 ]
 
 XPIDL_MODULE = 'toolkitprofile'
--- a/toolkit/profile/nsProfileLock.cpp
+++ b/toolkit/profile/nsProfileLock.cpp
@@ -259,18 +259,16 @@ nsresult nsProfileLock::LockWithFcntl(ns
 #ifdef DEBUG
             printf("fcntl(F_SETLK) failed. errno = %d\n", errno);
 #endif
             if (errno == EAGAIN || errno == EACCES)
                 rv = NS_ERROR_FILE_ACCESS_DENIED;
             else
                 rv = NS_ERROR_FAILURE;
         }
-        else
-            mHaveLock = true;
     }
     else
     {
         NS_ERROR("Failed to open lock file.");
         rv = NS_ERROR_FAILURE;
     }
     return rv;
 }
@@ -380,17 +378,16 @@ nsresult nsProfileLock::LockWithSymlink(
     PR_smprintf_free(signature);
     signature = nullptr;
 
     if (symlink_rv == 0)
     {
         // We exclusively created the symlink: record its name for eventual
         // unlock-via-unlink.
         rv = NS_OK;
-        mHaveLock = true;
         mPidLockFileName = strdup(fileName);
         if (mPidLockFileName)
         {
             PR_APPEND_LINK(this, &mPidLockList);
             if (!setupPidLockCleanup++)
             {
                 // Clean up on normal termination.
                 // This instanciates a dummy class, and will trigger the class
@@ -610,17 +607,18 @@ nsresult nsProfileLock::Lock(nsIFile* aP
                                                       do_QueryObject(unlocker));
             unlockerInterface.forget(aUnlocker);
           }
         }
         return NS_ERROR_FILE_ACCESS_DENIED;
     }
 #endif
 
-    mHaveLock = true;
+    if (NS_SUCCEEDED(rv))
+        mHaveLock = true;
 
     return rv;
 }
 
 
 nsresult nsProfileLock::Unlock(bool aFatalSignal)
 {
     nsresult rv = NS_OK;
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -95,17 +95,17 @@
 #include "nsIDocShellTreeItem.h"
 #include "ExternalHelperAppChild.h"
 
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
-#include "GeneratedJNIWrappers.h"
+#include "FennecJNIWrappers.h"
 #endif
 
 #include "mozilla/Preferences.h"
 #include "mozilla/ipc/URIUtils.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsDeviceStorage.h"
 #endif
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1100,16 +1100,24 @@ NS_IMETHODIMP
 PuppetWidget::PaintTask::Run()
 {
   if (mWidget) {
     mWidget->Paint();
   }
   return NS_OK;
 }
 
+void
+PuppetWidget::PaintNowIfNeeded()
+{
+  if (IsVisible() && mPaintTask.IsPending()) {
+    Paint();
+  }
+}
+
 NS_IMPL_ISUPPORTS(PuppetWidget::MemoryPressureObserver, nsIObserver)
 
 NS_IMETHODIMP
 PuppetWidget::MemoryPressureObserver::Observe(nsISupports* aSubject,
                                               const char* aTopic,
                                               const char16_t* aData)
 {
   if (!mWidget) {
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -189,16 +189,19 @@ public:
   // Contacts the parent process which gets the DPI from the
   // proper widget there. TODO: Handle DPI changes that happen
   // later on.
   virtual float GetDPI() override;
   virtual double GetDefaultScaleInternal() override;
 
   virtual bool NeedsPaint() override;
 
+  // Paint the widget immediately if any paints are queued up.
+  void PaintNowIfNeeded();
+
   virtual TabChild* GetOwningTabChild() override { return mTabChild; }
 
   void UpdateBackingScaleCache(float aDpi, double aScale)
   {
     mDPI = aDpi;
     mDefaultScale = aScale;
   }
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -49,16 +49,18 @@
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/ContentChild.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "MediaPrefs.h"
 
+#include "FennecJNIWrappers.h"
+
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::jni;
 using namespace mozilla::java;
 
 AndroidBridge* AndroidBridge::sBridge = nullptr;
 static jobject sGlobalContext = nullptr;
 nsDataHashtable<nsStringHashKey, nsString> AndroidBridge::sStoragePaths;
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -368,31 +368,16 @@ const JNINativeMethod GeckoView::Window:
             ::template Wrap<&Impl::Open>),
 
     mozilla::jni::MakeNativeMethod<GeckoView::Window::Reattach_t>(
             mozilla::jni::NativeStub<GeckoView::Window::Reattach_t, Impl>
             ::template Wrap<&Impl::Reattach>)
 };
 
 template<class Impl>
-class MemoryMonitor::Natives : public mozilla::jni::NativeImpl<MemoryMonitor, Impl>
-{
-public:
-    static const JNINativeMethod methods[1];
-};
-
-template<class Impl>
-const JNINativeMethod MemoryMonitor::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<MemoryMonitor::DispatchMemoryPressure_t>(
-            mozilla::jni::NativeStub<MemoryMonitor::DispatchMemoryPressure_t, Impl>
-            ::template Wrap<&Impl::DispatchMemoryPressure>)
-};
-
-template<class Impl>
 class PrefsHelper::Natives : public mozilla::jni::NativeImpl<PrefsHelper, Impl>
 {
 public:
     static const JNINativeMethod methods[4];
 };
 
 template<class Impl>
 const JNINativeMethod PrefsHelper::Natives<Impl>::methods[] = {
@@ -410,115 +395,31 @@ const JNINativeMethod PrefsHelper::Nativ
             ::template Wrap<&Impl::RemoveObserver>),
 
     mozilla::jni::MakeNativeMethod<PrefsHelper::SetPref_t>(
             mozilla::jni::NativeStub<PrefsHelper::SetPref_t, Impl>
             ::template Wrap<&Impl::SetPref>)
 };
 
 template<class Impl>
-class PresentationMediaPlayerManager::Natives : public mozilla::jni::NativeImpl<PresentationMediaPlayerManager, Impl>
-{
-public:
-    static const JNINativeMethod methods[3];
-};
-
-template<class Impl>
-const JNINativeMethod PresentationMediaPlayerManager::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<PresentationMediaPlayerManager::AddPresentationSurface_t>(
-            mozilla::jni::NativeStub<PresentationMediaPlayerManager::AddPresentationSurface_t, Impl>
-            ::template Wrap<&Impl::AddPresentationSurface>),
-
-    mozilla::jni::MakeNativeMethod<PresentationMediaPlayerManager::InvalidateAndScheduleComposite_t>(
-            mozilla::jni::NativeStub<PresentationMediaPlayerManager::InvalidateAndScheduleComposite_t, Impl>
-            ::template Wrap<&Impl::InvalidateAndScheduleComposite>),
-
-    mozilla::jni::MakeNativeMethod<PresentationMediaPlayerManager::RemovePresentationSurface_t>(
-            mozilla::jni::NativeStub<PresentationMediaPlayerManager::RemovePresentationSurface_t, Impl>
-            ::template Wrap<&Impl::RemovePresentationSurface>)
-};
-
-template<class Impl>
 class SurfaceTextureListener::Natives : public mozilla::jni::NativeImpl<SurfaceTextureListener, Impl>
 {
 public:
     static const JNINativeMethod methods[1];
 };
 
 template<class Impl>
 const JNINativeMethod SurfaceTextureListener::Natives<Impl>::methods[] = {
 
     mozilla::jni::MakeNativeMethod<SurfaceTextureListener::OnFrameAvailable_t>(
             mozilla::jni::NativeStub<SurfaceTextureListener::OnFrameAvailable_t, Impl>
             ::template Wrap<&Impl::OnFrameAvailable>)
 };
 
 template<class Impl>
-class Telemetry::Natives : public mozilla::jni::NativeImpl<Telemetry, Impl>
-{
-public:
-    static const JNINativeMethod methods[5];
-};
-
-template<class Impl>
-const JNINativeMethod Telemetry::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<Telemetry::AddHistogram_t>(
-            mozilla::jni::NativeStub<Telemetry::AddHistogram_t, Impl>
-            ::template Wrap<&Impl::AddHistogram>),
-
-    mozilla::jni::MakeNativeMethod<Telemetry::AddKeyedHistogram_t>(
-            mozilla::jni::NativeStub<Telemetry::AddKeyedHistogram_t, Impl>
-            ::template Wrap<&Impl::AddKeyedHistogram>),
-
-    mozilla::jni::MakeNativeMethod<Telemetry::AddUIEvent_t>(
-            mozilla::jni::NativeStub<Telemetry::AddUIEvent_t, Impl>
-            ::template Wrap<&Impl::AddUIEvent>),
-
-    mozilla::jni::MakeNativeMethod<Telemetry::StartUISession_t>(
-            mozilla::jni::NativeStub<Telemetry::StartUISession_t, Impl>
-            ::template Wrap<&Impl::StartUISession>),
-
-    mozilla::jni::MakeNativeMethod<Telemetry::StopUISession_t>(
-            mozilla::jni::NativeStub<Telemetry::StopUISession_t, Impl>
-            ::template Wrap<&Impl::StopUISession>)
-};
-
-template<class Impl>
-class ThumbnailHelper::Natives : public mozilla::jni::NativeImpl<ThumbnailHelper, Impl>
-{
-public:
-    static const JNINativeMethod methods[1];
-};
-
-template<class Impl>
-const JNINativeMethod ThumbnailHelper::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<ThumbnailHelper::RequestThumbnail_t>(
-            mozilla::jni::NativeStub<ThumbnailHelper::RequestThumbnail_t, Impl>
-            ::template Wrap<&Impl::RequestThumbnail>)
-};
-
-template<class Impl>
-class ZoomedView::Natives : public mozilla::jni::NativeImpl<ZoomedView, Impl>
-{
-public:
-    static const JNINativeMethod methods[1];
-};
-
-template<class Impl>
-const JNINativeMethod ZoomedView::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<ZoomedView::RequestZoomedViewData_t>(
-            mozilla::jni::NativeStub<ZoomedView::RequestZoomedViewData_t, Impl>
-            ::template Wrap<&Impl::RequestZoomedViewData>)
-};
-
-template<class Impl>
 class LayerView::Compositor::Natives : public mozilla::jni::NativeImpl<Compositor, Impl>
 {
 public:
     static const JNINativeMethod methods[7];
 };
 
 template<class Impl>
 const JNINativeMethod LayerView::Compositor::Natives<Impl>::methods[] = {
@@ -587,47 +488,16 @@ const JNINativeMethod NativePanZoomContr
             ::template Wrap<&Impl::HandleScrollEvent>),
 
     mozilla::jni::MakeNativeMethod<NativePanZoomController::SetIsLongpressEnabled_t>(
             mozilla::jni::NativeStub<NativePanZoomController::SetIsLongpressEnabled_t, Impl>
             ::template Wrap<&Impl::SetIsLongpressEnabled>)
 };
 
 template<class Impl>
-class CodecProxy::NativeCallbacks::Natives : public mozilla::jni::NativeImpl<NativeCallbacks, Impl>
-{
-public:
-    static const JNINativeMethod methods[5];
-};
-
-template<class Impl>
-const JNINativeMethod CodecProxy::NativeCallbacks::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::DisposeNative_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::DisposeNative_t, Impl>
-            ::template Wrap<&Impl::DisposeNative>),
-
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnError_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnError_t, Impl>
-            ::template Wrap<&Impl::OnError>),
-
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnInputExhausted_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnInputExhausted_t, Impl>
-            ::template Wrap<&Impl::OnInputExhausted>),
-
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnOutput_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnOutput_t, Impl>
-            ::template Wrap<&Impl::OnOutput>),
-
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t, Impl>
-            ::template Wrap<&Impl::OnOutputFormatChanged>)
-};
-
-template<class Impl>
 class NativeJSContainer::Natives : public mozilla::jni::NativeImpl<NativeJSContainer, Impl>
 {
 public:
     static const JNINativeMethod methods[2];
 };
 
 template<class Impl>
 const JNINativeMethod NativeJSContainer::Natives<Impl>::methods[] = {
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -59,35 +59,16 @@ auto AndroidGamepadManager::Start() -> v
 constexpr char AndroidGamepadManager::Stop_t::name[];
 constexpr char AndroidGamepadManager::Stop_t::signature[];
 
 auto AndroidGamepadManager::Stop() -> void
 {
     return mozilla::jni::Method<Stop_t>::Call(AndroidGamepadManager::Context(), nullptr);
 }
 
-const char DownloadsIntegration::name[] =
-        "org/mozilla/gecko/DownloadsIntegration";
-
-constexpr char DownloadsIntegration::GetTemporaryDownloadDirectory_t::name[];
-constexpr char DownloadsIntegration::GetTemporaryDownloadDirectory_t::signature[];
-
-auto DownloadsIntegration::GetTemporaryDownloadDirectory() -> mozilla::jni::String::LocalRef
-{
-    return mozilla::jni::Method<GetTemporaryDownloadDirectory_t>::Call(DownloadsIntegration::Context(), nullptr);
-}
-
-constexpr char DownloadsIntegration::ScanMedia_t::name[];
-constexpr char DownloadsIntegration::ScanMedia_t::signature[];
-
-auto DownloadsIntegration::ScanMedia(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> void
-{
-    return mozilla::jni::Method<ScanMedia_t>::Call(DownloadsIntegration::Context(), nullptr, a0, a1);
-}
-
 const char GeckoAppShell::name[] =
         "org/mozilla/gecko/GeckoAppShell";
 
 constexpr char GeckoAppShell::AddFullScreenPluginView_t::name[];
 constexpr char GeckoAppShell::AddFullScreenPluginView_t::signature[];
 
 auto GeckoAppShell::AddFullScreenPluginView(mozilla::jni::Object::Param a0) -> void
 {
@@ -1135,22 +1116,16 @@ constexpr char GeckoView::Window::LoadUr
 constexpr char GeckoView::Window::LoadUri_t::signature[];
 
 constexpr char GeckoView::Window::Open_t::name[];
 constexpr char GeckoView::Window::Open_t::signature[];
 
 constexpr char GeckoView::Window::Reattach_t::name[];
 constexpr char GeckoView::Window::Reattach_t::signature[];
 
-const char MemoryMonitor::name[] =
-        "org/mozilla/gecko/MemoryMonitor";
-
-constexpr char MemoryMonitor::DispatchMemoryPressure_t::name[];
-constexpr char MemoryMonitor::DispatchMemoryPressure_t::signature[];
-
 const char PrefsHelper::name[] =
         "org/mozilla/gecko/PrefsHelper";
 
 constexpr char PrefsHelper::CallPrefHandler_t::name[];
 constexpr char PrefsHelper::CallPrefHandler_t::signature[];
 
 auto PrefsHelper::CallPrefHandler(mozilla::jni::Object::Param a0, int32_t a1, mozilla::jni::String::Param a2, bool a3, int32_t a4, mozilla::jni::String::Param a5) -> void
 {
@@ -1172,91 +1147,30 @@ constexpr char PrefsHelper::SetPref_t::s
 constexpr char PrefsHelper::OnPrefChange_t::name[];
 constexpr char PrefsHelper::OnPrefChange_t::signature[];
 
 auto PrefsHelper::OnPrefChange(mozilla::jni::String::Param a0, int32_t a1, bool a2, int32_t a3, mozilla::jni::String::Param a4) -> void
 {
     return mozilla::jni::Method<OnPrefChange_t>::Call(PrefsHelper::Context(), nullptr, a0, a1, a2, a3, a4);
 }
 
-const char PresentationMediaPlayerManager::name[] =
-        "org/mozilla/gecko/PresentationMediaPlayerManager";
-
-constexpr char PresentationMediaPlayerManager::AddPresentationSurface_t::name[];
-constexpr char PresentationMediaPlayerManager::AddPresentationSurface_t::signature[];
-
-constexpr char PresentationMediaPlayerManager::InvalidateAndScheduleComposite_t::name[];
-constexpr char PresentationMediaPlayerManager::InvalidateAndScheduleComposite_t::signature[];
-
-constexpr char PresentationMediaPlayerManager::RemovePresentationSurface_t::name[];
-constexpr char PresentationMediaPlayerManager::RemovePresentationSurface_t::signature[];
-
 const char SurfaceTextureListener::name[] =
         "org/mozilla/gecko/SurfaceTextureListener";
 
 constexpr char SurfaceTextureListener::New_t::name[];
 constexpr char SurfaceTextureListener::New_t::signature[];
 
 auto SurfaceTextureListener::New() -> SurfaceTextureListener::LocalRef
 {
     return mozilla::jni::Constructor<New_t>::Call(SurfaceTextureListener::Context(), nullptr);
 }
 
 constexpr char SurfaceTextureListener::OnFrameAvailable_t::name[];
 constexpr char SurfaceTextureListener::OnFrameAvailable_t::signature[];
 
-const char Telemetry::name[] =
-        "org/mozilla/gecko/Telemetry";
-
-constexpr char Telemetry::AddHistogram_t::name[];
-constexpr char Telemetry::AddHistogram_t::signature[];
-
-constexpr char Telemetry::AddKeyedHistogram_t::name[];
-constexpr char Telemetry::AddKeyedHistogram_t::signature[];
-
-constexpr char Telemetry::AddUIEvent_t::name[];
-constexpr char Telemetry::AddUIEvent_t::signature[];
-
-constexpr char Telemetry::StartUISession_t::name[];
-constexpr char Telemetry::StartUISession_t::signature[];
-
-constexpr char Telemetry::StopUISession_t::name[];
-constexpr char Telemetry::StopUISession_t::signature[];
-
-const char ThumbnailHelper::name[] =
-        "org/mozilla/gecko/ThumbnailHelper";
-
-constexpr char ThumbnailHelper::NotifyThumbnail_t::name[];
-constexpr char ThumbnailHelper::NotifyThumbnail_t::signature[];
-
-auto ThumbnailHelper::NotifyThumbnail(mozilla::jni::ByteBuffer::Param a0, mozilla::jni::Object::Param a1, bool a2, bool a3) -> void
-{
-    return mozilla::jni::Method<NotifyThumbnail_t>::Call(ThumbnailHelper::Context(), nullptr, a0, a1, a2, a3);
-}
-
-constexpr char ThumbnailHelper::RequestThumbnail_t::name[];
-constexpr char ThumbnailHelper::RequestThumbnail_t::signature[];
-
-const char ZoomedView::name[] =
-        "org/mozilla/gecko/ZoomedView";
-
-constexpr char ZoomedView::RequestZoomedViewData_t::name[];
-constexpr char ZoomedView::RequestZoomedViewData_t::signature[];
-
-const char Distribution::name[] =
-        "org/mozilla/gecko/distribution/Distribution";
-
-constexpr char Distribution::GetDistributionDirectories_t::name[];
-constexpr char Distribution::GetDistributionDirectories_t::signature[];
-
-auto Distribution::GetDistributionDirectories() -> mozilla::jni::ObjectArray::LocalRef
-{
-    return mozilla::jni::Method<GetDistributionDirectories_t>::Call(Distribution::Context(), nullptr);
-}
-
 const char DisplayPortMetrics::name[] =
         "org/mozilla/gecko/gfx/DisplayPortMetrics";
 
 constexpr char DisplayPortMetrics::New_t::name[];
 constexpr char DisplayPortMetrics::New_t::signature[];
 
 auto DisplayPortMetrics::New(float a0, float a1, float a2, float a3, float a4) -> DisplayPortMetrics::LocalRef
 {
@@ -2009,115 +1923,16 @@ auto ViewTransform::Y() const -> float
     return mozilla::jni::Field<Y_t>::Get(ViewTransform::mCtx, nullptr);
 }
 
 auto ViewTransform::Y(float a0) const -> void
 {
     return mozilla::jni::Field<Y_t>::Set(ViewTransform::mCtx, nullptr, a0);
 }
 
-const char AudioFocusAgent::name[] =
-        "org/mozilla/gecko/media/AudioFocusAgent";
-
-constexpr char AudioFocusAgent::NotifyStartedPlaying_t::name[];
-constexpr char AudioFocusAgent::NotifyStartedPlaying_t::signature[];
-
-auto AudioFocusAgent::NotifyStartedPlaying() -> void
-{
-    return mozilla::jni::Method<NotifyStartedPlaying_t>::Call(AudioFocusAgent::Context(), nullptr);
-}
-
-constexpr char AudioFocusAgent::NotifyStoppedPlaying_t::name[];
-constexpr char AudioFocusAgent::NotifyStoppedPlaying_t::signature[];
-
-auto AudioFocusAgent::NotifyStoppedPlaying() -> void
-{
-    return mozilla::jni::Method<NotifyStoppedPlaying_t>::Call(AudioFocusAgent::Context(), nullptr);
-}
-
-const char CodecProxy::name[] =
-        "org/mozilla/gecko/media/CodecProxy";
-
-constexpr char CodecProxy::Create_t::name[];
-constexpr char CodecProxy::Create_t::signature[];
-
-auto CodecProxy::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2) -> CodecProxy::LocalRef
-{
-    return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2);
-}
-
-constexpr char CodecProxy::Flush_t::name[];
-constexpr char CodecProxy::Flush_t::signature[];
-
-auto CodecProxy::Flush() const -> bool
-{
-    return mozilla::jni::Method<Flush_t>::Call(CodecProxy::mCtx, nullptr);
-}
-
-constexpr char CodecProxy::Input_t::name[];
-constexpr char CodecProxy::Input_t::signature[];
-
-auto CodecProxy::Input(mozilla::jni::ByteArray::Param a0, mozilla::jni::Object::Param a1) const -> bool
-{
-    return mozilla::jni::Method<Input_t>::Call(CodecProxy::mCtx, nullptr, a0, a1);
-}
-
-constexpr char CodecProxy::Release_t::name[];
-constexpr char CodecProxy::Release_t::signature[];
-
-auto CodecProxy::Release() const -> bool
-{
-    return mozilla::jni::Method<Release_t>::Call(CodecProxy::mCtx, nullptr);
-}
-
-const char CodecProxy::NativeCallbacks::name[] =
-        "org/mozilla/gecko/media/CodecProxy$NativeCallbacks";
-
-constexpr char CodecProxy::NativeCallbacks::New_t::name[];
-constexpr char CodecProxy::NativeCallbacks::New_t::signature[];
-
-auto CodecProxy::NativeCallbacks::New() -> NativeCallbacks::LocalRef
-{
-    return mozilla::jni::Constructor<New_t>::Call(NativeCallbacks::Context(), nullptr);
-}
-
-constexpr char CodecProxy::NativeCallbacks::DisposeNative_t::name[];
-constexpr char CodecProxy::NativeCallbacks::DisposeNative_t::signature[];
-
-constexpr char CodecProxy::NativeCallbacks::OnError_t::name[];
-constexpr char CodecProxy::NativeCallbacks::OnError_t::signature[];
-
-constexpr char CodecProxy::NativeCallbacks::OnInputExhausted_t::name[];
-constexpr char CodecProxy::NativeCallbacks::OnInputExhausted_t::signature[];
-
-constexpr char CodecProxy::NativeCallbacks::OnOutput_t::name[];
-constexpr char CodecProxy::NativeCallbacks::OnOutput_t::signature[];
-
-constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::name[];
-constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::signature[];
-
-const char Restrictions::name[] =
-        "org/mozilla/gecko/restrictions/Restrictions";
-
-constexpr char Restrictions::IsAllowed_t::name[];
-constexpr char Restrictions::IsAllowed_t::signature[];
-
-auto Restrictions::IsAllowed(int32_t a0, mozilla::jni::String::Param a1) -> bool
-{
-    return mozilla::jni::Method<IsAllowed_t>::Call(Restrictions::Context(), nullptr, a0, a1);
-}
-
-constexpr char Restrictions::IsUserRestricted_t::name[];
-constexpr char Restrictions::IsUserRestricted_t::signature[];
-
-auto Restrictions::IsUserRestricted() -> bool
-{
-    return mozilla::jni::Method<IsUserRestricted_t>::Call(Restrictions::Context(), nullptr);
-}
-
 const char Clipboard::name[] =
         "org/mozilla/gecko/util/Clipboard";
 
 constexpr char Clipboard::ClearText_t::name[];
 constexpr char Clipboard::ClearText_t::signature[];
 
 auto Clipboard::ClearText() -> void
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -234,68 +234,16 @@ public:
     static auto Stop() -> void;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
-class DownloadsIntegration : public mozilla::jni::ObjectBase<DownloadsIntegration>
-{
-public:
-    static const char name[];
-
-    explicit DownloadsIntegration(const Context& ctx) : ObjectBase<DownloadsIntegration>(ctx) {}
-
-    struct GetTemporaryDownloadDirectory_t {
-        typedef DownloadsIntegration Owner;
-        typedef mozilla::jni::String::LocalRef ReturnType;
-        typedef mozilla::jni::String::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "getTemporaryDownloadDirectory";
-        static constexpr char signature[] =
-                "()Ljava/lang/String;";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto GetTemporaryDownloadDirectory() -> mozilla::jni::String::LocalRef;
-
-    struct ScanMedia_t {
-        typedef DownloadsIntegration Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                mozilla::jni::String::Param> Args;
-        static constexpr char name[] = "scanMedia";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;Ljava/lang/String;)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto ScanMedia(mozilla::jni::String::Param, mozilla::jni::String::Param) -> void;
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::GECKO;
-
-};
-
 class GeckoAppShell : public mozilla::jni::ObjectBase<GeckoAppShell>
 {
 public:
     static const char name[];
 
     explicit GeckoAppShell(const Context& ctx) : ObjectBase<GeckoAppShell>(ctx) {}
 
     class CameraCallback;
@@ -3596,46 +3544,16 @@ public:
     };
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
-class MemoryMonitor : public mozilla::jni::ObjectBase<MemoryMonitor>
-{
-public:
-    static const char name[];
-
-    explicit MemoryMonitor(const Context& ctx) : ObjectBase<MemoryMonitor>(ctx) {}
-
-    struct DispatchMemoryPressure_t {
-        typedef MemoryMonitor Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "dispatchMemoryPressure";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::UI;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::UI;
-
-    template<class Impl> class Natives;
-};
-
 class PrefsHelper : public mozilla::jni::ObjectBase<PrefsHelper>
 {
 public:
     static const char name[];
 
     explicit PrefsHelper(const Context& ctx) : ObjectBase<PrefsHelper>(ctx) {}
 
     struct CallPrefHandler_t {
@@ -3778,83 +3696,16 @@ public:
     static const int32_t PREF_STRING = 3;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
-class PresentationMediaPlayerManager : public mozilla::jni::ObjectBase<PresentationMediaPlayerManager>
-{
-public:
-    static const char name[];
-
-    explicit PresentationMediaPlayerManager(const Context& ctx) : ObjectBase<PresentationMediaPlayerManager>(ctx) {}
-
-    struct AddPresentationSurface_t {
-        typedef PresentationMediaPlayerManager Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param,
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "addPresentationSurface";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView;Landroid/view/Surface;)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::UI;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    struct InvalidateAndScheduleComposite_t {
-        typedef PresentationMediaPlayerManager Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "invalidateAndScheduleComposite";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView;)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::UI;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    struct RemovePresentationSurface_t {
-        typedef PresentationMediaPlayerManager Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "removePresentationSurface";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::UI;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::UI;
-
-    template<class Impl> class Natives;
-};
-
 class SurfaceTextureListener : public mozilla::jni::ObjectBase<SurfaceTextureListener>
 {
 public:
     static const char name[];
 
     explicit SurfaceTextureListener(const Context& ctx) : ObjectBase<SurfaceTextureListener>(ctx) {}
 
     struct New_t {
@@ -3894,254 +3745,16 @@ public:
     };
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
-class Telemetry : public mozilla::jni::ObjectBase<Telemetry>
-{
-public:
-    static const char name[];
-
-    explicit Telemetry(const Context& ctx) : ObjectBase<Telemetry>(ctx) {}
-
-    struct AddHistogram_t {
-        typedef Telemetry Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                int32_t> Args;
-        static constexpr char name[] = "nativeAddHistogram";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;I)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    struct AddKeyedHistogram_t {
-        typedef Telemetry Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                mozilla::jni::String::Param,
-                int32_t> Args;
-        static constexpr char name[] = "nativeAddKeyedHistogram";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;Ljava/lang/String;I)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    struct AddUIEvent_t {
-        typedef Telemetry Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                mozilla::jni::String::Param,
-                int64_t,
-                mozilla::jni::String::Param> Args;
-        static constexpr char name[] = "nativeAddUiEvent";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    struct StartUISession_t {
-        typedef Telemetry Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                int64_t> Args;
-        static constexpr char name[] = "nativeStartUiSession";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;J)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    struct StopUISession_t {
-        typedef Telemetry Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::String::Param,
-                mozilla::jni::String::Param,
-                int64_t> Args;
-        static constexpr char name[] = "nativeStopUiSession";
-        static constexpr char signature[] =
-                "(Ljava/lang/String;Ljava/lang/String;J)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-    template<class Impl> class Natives;
-};
-
-class ThumbnailHelper : public mozilla::jni::ObjectBase<ThumbnailHelper>
-{
-public:
-    static const char name[];
-
-    explicit ThumbnailHelper(const Context& ctx) : ObjectBase<ThumbnailHelper>(ctx) {}
-
-    struct NotifyThumbnail_t {
-        typedef ThumbnailHelper Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::ByteBuffer::Param,
-                mozilla::jni::Object::Param,
-                bool,
-                bool> Args;
-        static constexpr char name[] = "notifyThumbnail";
-        static constexpr char signature[] =
-                "(Ljava/nio/ByteBuffer;Lorg/mozilla/gecko/Tab;ZZ)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto NotifyThumbnail(mozilla::jni::ByteBuffer::Param, mozilla::jni::Object::Param, bool, bool) -> void;
-
-    struct RequestThumbnail_t {
-        typedef ThumbnailHelper Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::ByteBuffer::Param,
-                mozilla::jni::Object::Param,
-                int32_t,
-                int32_t,
-                int32_t> Args;
-        static constexpr char name[] = "requestThumbnailLocked";
-        static constexpr char signature[] =
-                "(Ljava/nio/ByteBuffer;Lorg/mozilla/gecko/Tab;III)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
-    };
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-    template<class Impl> class Natives;
-};
-
-class ZoomedView : public mozilla::jni::ObjectBase<ZoomedView>
-{
-public:
-    static const char name[];
-
-    explicit ZoomedView(const Context& ctx) : ObjectBase<ZoomedView>(ctx) {}
-
-    struct RequestZoomedViewData_t {
-        typedef ZoomedView Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::ByteBuffer::Param,
-                int32_t,
-                int32_t,
-                int32_t,
-                int32_t,
-                int32_t,
-                float> Args;
-        static constexpr char name[] = "requestZoomedViewData";
-        static constexpr char signature[] =
-                "(Ljava/nio/ByteBuffer;IIIIIF)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::GECKO;
-    };
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-    template<class Impl> class Natives;
-};
-
-class Distribution : public mozilla::jni::ObjectBase<Distribution>
-{
-public:
-    static const char name[];
-
-    explicit Distribution(const Context& ctx) : ObjectBase<Distribution>(ctx) {}
-
-    struct GetDistributionDirectories_t {
-        typedef Distribution Owner;
-        typedef mozilla::jni::ObjectArray::LocalRef ReturnType;
-        typedef mozilla::jni::ObjectArray::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "getDistributionDirectories";
-        static constexpr char signature[] =
-                "()[Ljava/lang/String;";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto GetDistributionDirectories() -> mozilla::jni::ObjectArray::LocalRef;
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::GECKO;
-
-};
-
 class DisplayPortMetrics : public mozilla::jni::ObjectBase<DisplayPortMetrics>
 {
 public:
     static const char name[];
 
     explicit DisplayPortMetrics(const Context& ctx) : ObjectBase<DisplayPortMetrics>(ctx) {}
 
     struct New_t {
@@ -5941,334 +5554,16 @@ public:
 
     auto Y(float) const -> void;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
-class AudioFocusAgent : public mozilla::jni::ObjectBase<AudioFocusAgent>
-{
-public:
-    static const char name[];
-
-    explicit AudioFocusAgent(const Context& ctx) : ObjectBase<AudioFocusAgent>(ctx) {}
-
-    struct NotifyStartedPlaying_t {
-        typedef AudioFocusAgent Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "notifyStartedPlaying";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto NotifyStartedPlaying() -> void;
-
-    struct NotifyStoppedPlaying_t {
-        typedef AudioFocusAgent Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "notifyStoppedPlaying";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = true;