merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 24 May 2017 11:25:03 +0200
changeset 360344 291a11111bdd05c5cd55dd552da4b1285ceba9b2
parent 360320 ffaa07672466b06cd748b07a34cf95377afdde41 (current diff)
parent 360343 735c20e6a9b7e923c92cc0286643d46268928663 (diff)
child 360345 d93182f36b3c134a3b1c6718f09eb87c2913e364
child 360377 744e9a4706a08b8e5b6484355e9531706539e351
child 360434 7fc3bfbb3e59ab0e065cc6160b4af9def63b40e6
child 361229 e4db9580e41b486ee1dbd603bd2e011003f5fb1f
push id31874
push userarchaeopteryx@coole-files.de
push dateWed, 24 May 2017 09:25:22 +0000
treeherdermozilla-central@291a11111bdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.0a1
first release with
nightly linux32
291a11111bdd / 55.0a1 / 20170524100215 / files
nightly linux64
291a11111bdd / 55.0a1 / 20170524100215 / files
nightly mac
291a11111bdd / 55.0a1 / 20170524030204 / files
nightly win32
291a11111bdd / 55.0a1 / 20170524030204 / files
nightly win64
291a11111bdd / 55.0a1 / 20170524030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: HGZE1dfSDNK
modules/libpref/init/all.js
--- a/AUTHORS
+++ b/AUTHORS
@@ -285,16 +285,17 @@ David Rajchenbach-Teller <dteller@mozill
 David Savage
 David S. Miller <davem@redhat.com>
 David Woodhouse <dwmw2@infradead.org>
 David Zbarsky <dzbarsky@gmail.com>
 Dean Tessman <dean_tessman@hotmail.com>
 <deneen@alum.bucknell.edu>
 Denis Antrushin <adu@sparc.spb.su>
 Denis Issoupov <denis@macadamian.com>
+Dennis Ek <contact@dennisek.se>
 Dennis Handly
 Derrick Rice <derrick.rice@gmail.com>
 <desale@netscape.com>
 diablohn
 Diane Trout <diane@ghic.org>
 Dietrich Ayala <dietrich@mozilla.com>
 Digital Creations 2, Inc
 Disruptive Innovations
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -69,17 +69,25 @@ void ProxyCreated(ProxyAccessible* aProx
 void ProxyDestroyed(ProxyAccessible*);
 
 /**
  * Callied when an event is fired on a proxied accessible.
  */
 void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType);
 void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState,
                            bool aEnabled);
+
+#if defined(XP_WIN)
+void ProxyFocusEvent(ProxyAccessible* aTarget,
+                     const LayoutDeviceIntRect& aCaretRect);
+void ProxyCaretMoveEvent(ProxyAccessible* aTarget,
+                         const LayoutDeviceIntRect& aCaretRect);
+#else
 void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset);
+#endif
 void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
                           int32_t aStart, uint32_t aLen, bool aIsInsert,
                           bool aFromUser);
 void ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible* aParent,
                         bool aInsert, bool aFromUser);
 void ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible* aWidget,
                          uint32_t aType);
 } // namespace a11y
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -886,16 +886,22 @@ Accessible::HandleAccEvent(AccEvent* aEv
         case nsIAccessibleEvent::EVENT_SELECTION_ADD:
         case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
           AccSelChangeEvent* selEvent = downcast_accEvent(aEvent);
           uint64_t widgetID = selEvent->Widget()->IsDoc() ? 0 :
             reinterpret_cast<uintptr_t>(selEvent->Widget());
           ipcDoc->SendSelectionEvent(id, widgetID, aEvent->GetEventType());
           break;
         }
+#if defined(XP_WIN)
+        case nsIAccessibleEvent::EVENT_FOCUS: {
+          ipcDoc->SendFocusEvent(id);
+          break;
+        }
+#endif
         default:
           ipcDoc->SendEvent(id, aEvent->GetEventType());
       }
     }
   }
 
   if (nsCoreUtils::AccEventObserversExist()) {
     nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -266,29 +266,37 @@ DocAccessibleParent::RecvStateChangeEven
     new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra,
                                aEnabled);
   nsCoreUtils::DispatchAccEvent(Move(event));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
+DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID,
+#if defined(XP_WIN)
+                                        const LayoutDeviceIntRect& aCaretRect,
+#endif // defined (XP_WIN)
+                                        const int32_t& aOffset)
 {
   if (mShutdown) {
     return IPC_OK();
   }
 
   ProxyAccessible* proxy = GetAccessible(aID);
   if (!proxy) {
     NS_ERROR("unknown caret move event target!");
     return IPC_OK();
   }
 
+#if defined(XP_WIN)
+  ProxyCaretMoveEvent(proxy, aCaretRect);
+#else
   ProxyCaretMoveEvent(proxy, aOffset);
+#endif
 
   if (!nsCoreUtils::AccEventObserversExist()) {
     return IPC_OK();
   }
 
   xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   nsIDOMNode* node = nullptr;
@@ -703,12 +711,43 @@ DocAccessibleParent::RecvGetWindowedPlug
   aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin));
 
   return IPC_OK();
 #else
   return IPC_FAIL(this, "Message unsupported in this build configuration");
 #endif
 }
 
+mozilla::ipc::IPCResult
+DocAccessibleParent::RecvFocusEvent(const uint64_t& aID,
+                                    const LayoutDeviceIntRect& aCaretRect)
+{
+  if (mShutdown) {
+    return IPC_OK();
+  }
+
+  ProxyAccessible* proxy = GetAccessible(aID);
+  if (!proxy) {
+    NS_ERROR("no proxy for event!");
+    return IPC_OK();
+  }
+
+  ProxyFocusEvent(proxy, aCaretRect);
+
+  if (!nsCoreUtils::AccEventObserversExist()) {
+    return IPC_OK();
+  }
+
+  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
+  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+  nsIDOMNode* node = nullptr;
+  bool fromUser = true; // XXX fix me
+  RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
+                                              xpcAcc, doc, node, fromUser);
+  nsCoreUtils::DispatchAccEvent(Move(event));
+
+  return IPC_OK();
+}
+
 #endif // defined(XP_WIN)
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -75,29 +75,35 @@ public:
   virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
     override;
   virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser)
     override;
   virtual mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID,
                                                        const uint64_t& aState,
                                                        const bool& aEnabled) override final;
 
-  virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
-    override final;
+  virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID,
+#if defined(XP_WIN)
+                                                     const LayoutDeviceIntRect& aCaretRect,
+#endif
+                                                     const int32_t& aOffset) override final;
 
   virtual mozilla::ipc::IPCResult RecvTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                                                       const int32_t& aStart, const uint32_t& aLen,
                                                       const bool& aIsInsert,
                                                       const bool& aFromUser) override;
 
 #if defined(XP_WIN)
   virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                                                           const int32_t& aStart, const uint32_t& aLen,
                                                           const bool& aIsInsert,
                                                           const bool& aFromUser) override;
+
+  virtual mozilla::ipc::IPCResult RecvFocusEvent(const uint64_t& aID,
+                                                 const LayoutDeviceIntRect& aCaretRect) override;
 #endif // defined(XP_WIN)
 
   virtual mozilla::ipc::IPCResult RecvSelectionEvent(const uint64_t& aID,
                                                      const uint64_t& aWidgetID,
                                                      const uint32_t& aType) override;
 
   virtual mozilla::ipc::IPCResult RecvRoleChangedEvent(const uint32_t& aRole) override final;
 
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -154,25 +154,74 @@ DocAccessibleChild::SendStateChangeEvent
     return PDocAccessibleChild::SendStateChangeEvent(aID, aState, aEnabled);
   }
 
   PushDeferredEvent(MakeUnique<SerializedStateChange>(this, aID, aState,
                                                       aEnabled));
   return true;
 }
 
+LayoutDeviceIntRect
+DocAccessibleChild::GetCaretRectFor(const uint64_t& aID)
+{
+  Accessible* target;
+
+  if (aID) {
+    target = reinterpret_cast<Accessible*>(aID);
+  } else {
+    target = mDoc;
+  }
+
+  MOZ_ASSERT(target);
+
+  HyperTextAccessible* text = target->AsHyperText();
+  if (!text) {
+    return LayoutDeviceIntRect();
+  }
+
+  nsIWidget* widget = nullptr;
+  return text->GetCaretRect(&widget);
+}
+
+bool
+DocAccessibleChild::SendFocusEvent(const uint64_t& aID)
+{
+  return SendFocusEvent(aID, GetCaretRectFor(aID));
+}
+
+bool
+DocAccessibleChild::SendFocusEvent(const uint64_t& aID,
+                                   const LayoutDeviceIntRect& aCaretRect)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendFocusEvent(aID, aCaretRect);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedFocus>(this, aID, aCaretRect));
+  return true;
+}
+
 bool
 DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
                                        const int32_t& aOffset)
 {
+  return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset);
+}
+
+bool
+DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
+                                       const LayoutDeviceIntRect& aCaretRect,
+                                       const int32_t& aOffset)
+{
   if (IsConstructedInParentProcess()) {
-    return PDocAccessibleChild::SendCaretMoveEvent(aID, aOffset);
+    return PDocAccessibleChild::SendCaretMoveEvent(aID, aCaretRect, aOffset);
   }
 
-  PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aOffset));
+  PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aCaretRect,
+                                                    aOffset));
   return true;
 }
 
 bool
 DocAccessibleChild::SendTextChangeEvent(const uint64_t& aID,
                                         const nsString& aStr,
                                         const int32_t& aStart,
                                         const uint32_t& aLen,
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -38,16 +38,22 @@ public:
 
   IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
 
   bool SendEvent(const uint64_t& aID, const uint32_t& type);
   bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser);
   bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState,
                             const bool& aEnabled);
   bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset);
+  bool SendCaretMoveEvent(const uint64_t& aID,
+                          const LayoutDeviceIntRect& aCaretRect,
+                          const int32_t& aOffset);
+  bool SendFocusEvent(const uint64_t& aID);
+  bool SendFocusEvent(const uint64_t& aID,
+                      const LayoutDeviceIntRect& aCaretRect);
   bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                            const int32_t& aStart, const uint32_t& aLen,
                            const bool& aIsInsert, const bool& aFromUser);
   bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID,
                           const uint32_t& aType);
   bool SendRoleChangedEvent(const uint32_t& aRole);
 
   bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc,
@@ -60,16 +66,18 @@ protected:
   virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser) override;
 
 private:
   void RemoveDeferredConstructor();
 
   bool IsConstructedInParentProcess() const { return mIsRemoteConstructed; }
   void SetConstructedInParentProcess() { mIsRemoteConstructed = true; }
 
+  LayoutDeviceIntRect GetCaretRectFor(const uint64_t& aID);
+
   /**
    * DocAccessibleChild should not fire events until it has asynchronously
    * received the COM proxy for its parent. OTOH, content a11y may still be
    * attempting to fire events during this window of time. If this object does
    * not yet have its parent proxy, instead of immediately sending the events to
    * our parent, we enqueue them to mDeferredEvents. As soon as
    * RecvParentCOMProxy is called, we play back mDeferredEvents.
    */
@@ -154,29 +162,49 @@ private:
     uint64_t  mID;
     uint64_t  mState;
     bool      mEnabled;
   };
 
   struct SerializedCaretMove final : public DeferredEvent
   {
     SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID,
-                        int32_t aOffset)
+                        const LayoutDeviceIntRect& aCaretRect, int32_t aOffset)
       : DeferredEvent(aTarget)
       , mID(aID)
+      , mCaretRect(aCaretRect)
       , mOffset(aOffset)
     {}
 
     void Dispatch(DocAccessibleChild* aIPCDoc) override
     {
-      Unused << aIPCDoc->SendCaretMoveEvent(mID, mOffset);
+      Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset);
     }
 
-    uint64_t  mID;
-    int32_t   mOffset;
+    uint64_t            mID;
+    LayoutDeviceIntRect mCaretRect;
+    int32_t             mOffset;
+  };
+
+  struct SerializedFocus final : public DeferredEvent
+  {
+    SerializedFocus(DocAccessibleChild* aTarget, uint64_t aID,
+                    const LayoutDeviceIntRect& aCaretRect)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mCaretRect(aCaretRect)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendFocusEvent(mID, mCaretRect);
+    }
+
+    uint64_t            mID;
+    LayoutDeviceIntRect mCaretRect;
   };
 
   struct SerializedTextChange final : public DeferredEvent
   {
     SerializedTextChange(DocAccessibleChild* aTarget, uint64_t aID,
                          const nsString& aStr, int32_t aStart, uint32_t aLen,
                          bool aIsInsert, bool aFromUser)
       : DeferredEvent(aTarget)
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -4,16 +4,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/. */
 
 include protocol PFileDescriptorSet;
 include protocol PBrowser;
 
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   int32_t MsaaID;
@@ -45,23 +46,25 @@ parent:
   /*
    * 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);
   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 CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect,
+                       int32_t aOffset);
   async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
                         bool aIsInsert, bool aFromUser);
   sync SyncTextChangeEvent(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);
+  async FocusEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect);
 
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   sync GetWindowedPluginIAccessible(WindowsHandle aHwnd)
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -77,17 +77,17 @@ AccessibleWrap::AccessibleWrap(nsIConten
   Accessible(aContent, aDoc)
   , mID(kNoID)
 {
 }
 
 AccessibleWrap::~AccessibleWrap()
 {
   if (mID != kNoID) {
-    sIDGen.ReleaseID(this);
+    sIDGen.ReleaseID(WrapNotNull(this));
   }
 }
 
 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
 
 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
 
 void
@@ -129,17 +129,17 @@ AccessibleWrap::QueryInterface(REFIID ii
 
     *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
   } else if (IID_IServiceProvider == iid)
     *ppv = new ServiceProvider(this);
   else if (IID_ISimpleDOMNode == iid && !IsProxy()) {
     if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
       return E_NOINTERFACE;
 
-    *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode()));
+    *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(WrapNotNull(this)));
   }
 
   if (nullptr == *ppv) {
     HRESULT hr = ia2Accessible::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
@@ -1554,29 +1554,53 @@ AccessibleWrap::UpdateSystemCaretFor(Acc
   ::DestroyCaret();
 
   HyperTextAccessible* text = aAccessible->AsHyperText();
   if (!text)
     return;
 
   nsIWidget* widget = nullptr;
   LayoutDeviceIntRect caretRect = text->GetCaretRect(&widget);
-  HWND caretWnd;
-  if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
+
+  if (!widget) {
+    return;
+  }
+
+  HWND caretWnd = reinterpret_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
+  UpdateSystemCaretFor(caretWnd, caretRect);
+}
+
+/* static */ void
+AccessibleWrap::UpdateSystemCaretFor(ProxyAccessible* aProxy,
+                                     const LayoutDeviceIntRect& aCaretRect)
+{
+  ::DestroyCaret();
+
+  // The HWND should be the real widget HWND, not an emulated HWND.
+  // We get the HWND from the proxy's outer doc to bypass window emulation.
+  Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
+  UpdateSystemCaretFor(GetHWNDFor(outerDoc), aCaretRect);
+}
+
+/* static */ void
+AccessibleWrap::UpdateSystemCaretFor(HWND aCaretWnd,
+                                     const LayoutDeviceIntRect& aCaretRect)
+{
+  if (!aCaretWnd || aCaretRect.IsEmpty()) {
     return;
   }
 
   // Create invisible bitmap for caret, otherwise its appearance interferes
   // with Gecko caret
-  nsAutoBitmap caretBitMap(CreateBitmap(1, caretRect.height, 1, 1, nullptr));
-  if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) {  // Also destroys the last caret
-    ::ShowCaret(caretWnd);
+  nsAutoBitmap caretBitMap(CreateBitmap(1, aCaretRect.height, 1, 1, nullptr));
+  if (::CreateCaret(aCaretWnd, caretBitMap, 1, aCaretRect.height)) {  // Also destroys the last caret
+    ::ShowCaret(aCaretWnd);
     RECT windowRect;
-    ::GetWindowRect(caretWnd, &windowRect);
-    ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
+    ::GetWindowRect(aCaretWnd, &windowRect);
+    ::SetCaretPos(aCaretRect.x - windowRect.left, aCaretRect.y - windowRect.top);
   }
 }
 
 ITypeInfo*
 AccessibleWrap::GetTI(LCID lcid)
 {
   if (gTypeInfo)
     return gTypeInfo;
@@ -1680,8 +1704,19 @@ AccessibleWrap::DispatchTextChangeToHand
   HRESULT hr = ASYNC_INVOKE(invoker, OnTextChange, PtrToLong(hwnd), msaaId,
                             isInsert, &textSegment);
 
   ::SysFreeString(textSegment.text);
 
   return SUCCEEDED(hr);
 }
 
+/* static */ void
+AccessibleWrap::AssignChildIDTo(NotNull<sdnAccessible*> aSdnAcc)
+{
+  aSdnAcc->SetUniqueID(sIDGen.GetID());
+}
+
+/* static */ void
+AccessibleWrap::ReleaseChildID(NotNull<sdnAccessible*> aSdnAcc)
+{
+  sIDGen.ReleaseID(aSdnAcc);
+}
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -167,17 +167,24 @@ public: // construction, destruction
   /**
    * System caret support: update the Windows caret position. 
    * The system caret works more universally than the MSAA caret
    * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */
   void UpdateSystemCaretFor(Accessible* aAccessible);
+  static void UpdateSystemCaretFor(ProxyAccessible* aProxy,
+                                   const LayoutDeviceIntRect& aCaretRect);
 
+private:
+  static void UpdateSystemCaretFor(HWND aCaretWnd,
+                                   const LayoutDeviceIntRect& aCaretRect);
+
+public:
   /**
    * Find an accessible by the given child ID in cached documents.
    */
   MOZ_MUST_USE already_AddRefed<IAccessible>
   GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct);
 
   virtual void GetNativeInterface(void **aOutAccessible) override;
 
@@ -189,16 +196,20 @@ public: // construction, destruction
 
   static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId);
   static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId);
 
   static void SetHandlerControl(DWORD aPid, RefPtr<IHandlerControl> aCtrl);
 
   bool DispatchTextChangeToHandler(bool aIsInsert, const nsString& aText,
                                    int32_t aStart, uint32_t aLen);
+
+  static void AssignChildIDTo(NotNull<sdnAccessible*> aSdnAcc);
+  static void ReleaseChildID(NotNull<sdnAccessible*> aSdnAcc);
+
 protected:
   virtual ~AccessibleWrap();
 
   uint32_t mID;
 
   HRESULT
   ResolveChild(const VARIANT& aVarChild, IAccessible** aOutInterface);
 
--- a/accessible/windows/msaa/MsaaIdGenerator.cpp
+++ b/accessible/windows/msaa/MsaaIdGenerator.cpp
@@ -2,22 +2,25 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MsaaIdGenerator.h"
 
 #include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Unused.h"
 #include "nsDataHashtable.h"
 #include "nsIXULRuntime.h"
+#include "sdnAccessible.h"
 
 // These constants may be adjusted to modify the proportion of the Child ID
 // allocated to the content ID vs proportion allocated to the unique ID. They
 // must always sum to 31, ie. the width of a 32-bit integer less the sign bit.
 
 // NB: kNumContentProcessIDBits must be large enough to successfully hold the
 // maximum permitted number of e10s content processes. If the e10s maximum
 // number of content processes changes, then kNumContentProcessIDBits must also
@@ -90,32 +93,48 @@ constexpr MsaaIdGenerator::MsaaIdGenerat
 uint32_t
 MsaaIdGenerator::GetID()
 {
   uint32_t id = mIDSet.GetID();
   MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL));
   return detail::BuildMsaaID(id, ResolveContentProcessID());
 }
 
-void
-MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap)
+bool
+MsaaIdGenerator::ReleaseID(uint32_t aID)
 {
-  MOZ_ASSERT(aAccWrap);
-  uint32_t id = aAccWrap->GetExistingID();
-  MOZ_ASSERT(id != AccessibleWrap::kNoID);
-  detail::MsaaIDCracker cracked(id);
+  MOZ_ASSERT(aID != AccessibleWrap::kNoID);
+  detail::MsaaIDCracker cracked(aID);
   if (cracked.GetContentProcessId() != ResolveContentProcessID()) {
+    return false;
+  }
+  mIDSet.ReleaseID(cracked.GetUniqueId());
+  return true;
+}
+
+void
+MsaaIdGenerator::ReleaseID(NotNull<AccessibleWrap*> aAccWrap)
+{
+  if (!ReleaseID(aAccWrap->GetExistingID())) {
     // This may happen if chrome holds a proxy whose ID was originally generated
     // by a content process. Since ReleaseID only has meaning in the process
     // that originally generated that ID, we ignore ReleaseID calls for any ID
     // that did not come from the current process.
     MOZ_ASSERT(aAccWrap->IsProxy());
-    return;
   }
-  mIDSet.ReleaseID(cracked.GetUniqueId());
+}
+
+void
+MsaaIdGenerator::ReleaseID(NotNull<sdnAccessible*> aSdnAcc)
+{
+  Maybe<uint32_t> id = aSdnAcc->ReleaseUniqueID();
+  if (id.isSome()) {
+    DebugOnly<bool> released = ReleaseID(id.value());
+    MOZ_ASSERT(released);
+  }
 }
 
 bool
 MsaaIdGenerator::IsChromeID(uint32_t aID)
 {
   detail::MsaaIDCracker cracked(aID);
   return cracked.GetContentProcessId() == 0;
 }
--- a/accessible/windows/msaa/MsaaIdGenerator.h
+++ b/accessible/windows/msaa/MsaaIdGenerator.h
@@ -5,21 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_MsaaIdGenerator_h
 #define mozilla_a11y_MsaaIdGenerator_h
 
 #include "mozilla/a11y/IDSet.h"
 
 #include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/NotNull.h"
 
 namespace mozilla {
 namespace a11y {
 
 class AccessibleWrap;
+class sdnAccessible;
 
 /**
  * This class is responsible for generating child IDs used by our MSAA
  * implementation. Since e10s requires us to differentiate IDs based on the
  * originating process of the accessible, a portion of the ID's bits are
  * allocated to storing that information. The remaining bits represent the
  * unique ID of the accessible, within that content process.
  *
@@ -28,27 +30,29 @@ class AccessibleWrap;
  * are allocated for each purpose.
  */
 class MsaaIdGenerator
 {
 public:
   constexpr MsaaIdGenerator();
 
   uint32_t GetID();
-  void ReleaseID(AccessibleWrap* aAccWrap);
+  void ReleaseID(NotNull<AccessibleWrap*> aAccWrap);
+  void ReleaseID(NotNull<sdnAccessible*> aSdnAcc);
   bool IsChromeID(uint32_t aID);
   bool IsIDForThisContentProcess(uint32_t aID);
   bool IsIDForContentProcess(uint32_t aID,
                              dom::ContentParentId aIPCContentProcessId);
   bool IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID);
 
   uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
   void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
 
 private:
+  bool ReleaseID(uint32_t aID);
   uint32_t ResolveContentProcessID();
 
 private:
   IDSet     mIDSet;
 };
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -111,18 +111,29 @@ a11y::ProxyEvent(ProxyAccessible* aTarge
 void
 a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t, bool)
 {
   AccessibleWrap::FireWinEvent(WrapperFor(aTarget),
                                nsIAccessibleEvent::EVENT_STATE_CHANGE);
 }
 
 void
-a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
+a11y::ProxyFocusEvent(ProxyAccessible* aTarget,
+                      const LayoutDeviceIntRect& aCaretRect)
 {
+  AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect);
+  AccessibleWrap::FireWinEvent(WrapperFor(aTarget),
+                               nsIAccessibleEvent::EVENT_FOCUS);
+}
+
+void
+a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget,
+                          const LayoutDeviceIntRect& aCaretRect)
+{
+  AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect);
   AccessibleWrap::FireWinEvent(WrapperFor(aTarget),
                                nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED);
 }
 
 void
 a11y::ProxyTextChangeEvent(ProxyAccessible* aText, const nsString& aStr,
                            int32_t aStart, uint32_t aLen, bool aInsert, bool)
 {
--- a/accessible/windows/sdn/sdnAccessible-inl.h
+++ b/accessible/windows/sdn/sdnAccessible-inl.h
@@ -16,19 +16,31 @@ namespace mozilla {
 namespace a11y {
 
 inline DocAccessible*
 sdnAccessible::GetDocument() const
 {
   return GetExistingDocAccessible(mNode->OwnerDoc());
 }
 
-inline Accessible*
-sdnAccessible::GetAccessible() const
+inline AccessibleWrap*
+sdnAccessible::GetAccessible()
 {
+  if (mWrap) {
+    return mWrap;
+  }
+
   DocAccessible* document = GetDocument();
-  return document ? document->GetAccessibleEvenIfNotInMap(mNode) : nullptr;
+  if (!document) {
+    return nullptr;
+  }
+
+  // Once we have an accessible, we should hold a reference to it so that we
+  // may preserve object identity.
+  mWrap =
+    static_cast<AccessibleWrap*>(document->GetAccessibleEvenIfNotInMap(mNode));
+  return mWrap;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_sdnAccessible_inl_h_
--- a/accessible/windows/sdn/sdnAccessible.cpp
+++ b/accessible/windows/sdn/sdnAccessible.cpp
@@ -20,30 +20,37 @@
 #include "nsRange.h"
 
 #include "mozilla/dom/BorrowedAttrInfo.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
+sdnAccessible::~sdnAccessible()
+{
+  if (mUniqueId.isSome()) {
+    AccessibleWrap::ReleaseChildID(WrapNotNull(this));
+  }
+}
+
 STDMETHODIMP
 sdnAccessible::QueryInterface(REFIID aREFIID, void** aInstancePtr)
 {
   if (!aInstancePtr)
     return E_FAIL;
   *aInstancePtr = nullptr;
 
   if (aREFIID == IID_ISimpleDOMNode) {
     *aInstancePtr = static_cast<ISimpleDOMNode*>(this);
     AddRef();
     return S_OK;
   }
 
-  AccessibleWrap* accessible = static_cast<AccessibleWrap*>(GetAccessible());
+  AccessibleWrap* accessible = GetAccessible();
   if (accessible)
     return accessible->QueryInterface(aREFIID, aInstancePtr);
 
   // IUnknown* is the canonical one if and only if this accessible doesn't have
   // an accessible.
   if (aREFIID == IID_IUnknown) {
     *aInstancePtr = static_cast<ISimpleDOMNode*>(this);
     AddRef();
@@ -93,21 +100,25 @@ sdnAccessible::get_nodeInfo(BSTR __RPC_F
 
   *aNameSpaceID = mNode->IsNodeOfType(nsINode::eCONTENT) ?
     static_cast<short>(mNode->AsContent()->GetNameSpaceID()) : 0;
 
   // This is a unique ID for every content node. The 3rd party accessibility
   // application can compare this to the childID we return for events such as
   // focus events, to correlate back to data nodes in their internal object
   // model.
-  Accessible* accessible = GetAccessible();
+  AccessibleWrap* accessible = GetAccessible();
   if (accessible) {
     *aUniqueID = AccessibleWrap::GetChildIDFor(accessible);
   } else {
-    *aUniqueID = - NS_PTR_TO_INT32(static_cast<void*>(this));
+    if (mUniqueId.isNothing()) {
+      AccessibleWrap::AssignChildIDTo(WrapNotNull(this));
+    }
+    MOZ_ASSERT(mUniqueId.isSome());
+    *aUniqueID = mUniqueId.value();
   }
 
   *aNumChildren = mNode->GetChildCount();
 
   return S_OK;
 }
 
 STDMETHODIMP
--- a/accessible/windows/sdn/sdnAccessible.h
+++ b/accessible/windows/sdn/sdnAccessible.h
@@ -7,45 +7,66 @@
 #ifndef mozilla_a11y_sdnAccessible_h_
 #define mozilla_a11y_sdnAccessible_h_
 
 #include "ISimpleDOM.h"
 #include "AccessibleWrap.h"
 #include "IUnknownImpl.h"
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/NotNull.h"
 
 namespace mozilla {
 namespace a11y {
 
 class sdnAccessible final : public ISimpleDOMNode
 {
 public:
   explicit sdnAccessible(nsINode* aNode) :
     mNode(aNode)
   {
     if (!mNode)
       MOZ_CRASH();
   }
-  ~sdnAccessible() { }
+
+  explicit sdnAccessible(NotNull<AccessibleWrap*> aAccWrap)
+    : mNode(aAccWrap->GetNode())
+    , mWrap(aAccWrap)
+  {
+  }
+
+  ~sdnAccessible();
 
   /**
-   * Retrun if the object is defunct.
+   * Return if the object is defunct.
    */
   bool IsDefunct() const { return !GetDocument(); }
 
   /**
    * Return a document accessible it belongs to if any.
    */
   DocAccessible* GetDocument() const;
 
   /*
    * Return associated accessible if any.
    */
-  Accessible* GetAccessible() const;
+  AccessibleWrap* GetAccessible();
+
+  void SetUniqueID(uint32_t aNewUniqueId)
+  {
+    mUniqueId = Some(aNewUniqueId);
+  }
+
+  Maybe<uint32_t> ReleaseUniqueID()
+  {
+    Maybe<uint32_t> result = mUniqueId;
+    mUniqueId = Nothing();
+    return result;
+  }
 
   //IUnknown
   DECL_IUNKNOWN
 
   virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nodeInfo(
     /* [out] */ BSTR __RPC_FAR* aNodeName,
     /* [out] */ short __RPC_FAR* aNameSpaceID,
     /* [out] */ BSTR __RPC_FAR* aNodeValue,
@@ -106,14 +127,16 @@ public:
   virtual /* [local][propget] */ HRESULT STDMETHODCALLTYPE get_localInterface(
     /* [retval][out] */ void __RPC_FAR *__RPC_FAR* aLocalInterface);
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_language(
     /* [out][retval] */ BSTR __RPC_FAR* aLanguage);
 
 private:
   nsCOMPtr<nsINode> mNode;
+  RefPtr<AccessibleWrap> mWrap;
+  Maybe<uint32_t> mUniqueId;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_sdnAccessible_h_
--- a/dom/events/AsyncEventDispatcher.cpp
+++ b/dom/events/AsyncEventDispatcher.cpp
@@ -34,16 +34,23 @@ AsyncEventDispatcher::AsyncEventDispatch
 }
 
 NS_IMETHODIMP
 AsyncEventDispatcher::Run()
 {
   if (mCanceled) {
     return NS_OK;
   }
+  if (mCheckStillInDoc) {
+    nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
+    MOZ_ASSERT(node);
+    if (!node->IsInComposedDoc()) {
+      return NS_OK;
+    }
+  }
   mTarget->AsyncEventRunning(this);
   RefPtr<Event> event = mEvent ? mEvent->InternalDOMEvent() : nullptr;
   if (!event) {
     event = NS_NewDOMEvent(mTarget, nullptr, nullptr);
     event->InitEvent(mEventType, mBubbles, false);
     event->SetTrusted(true);
   }
   if (mOnlyChromeDispatch) {
@@ -83,16 +90,27 @@ AsyncEventDispatcher::PostDOMEvent()
 
 void
 AsyncEventDispatcher::RunDOMEventWhenSafe()
 {
   RefPtr<AsyncEventDispatcher> ensureDeletionWhenFailing = this;
   nsContentUtils::AddScriptRunner(this);
 }
 
+void
+AsyncEventDispatcher::RequireNodeInDocument()
+{
+#ifdef DEBUG
+  nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
+  MOZ_ASSERT(node);
+#endif
+
+  mCheckStillInDoc = true;
+}
+
 /******************************************************************************
  * mozilla::LoadBlockingAsyncEventDispatcher
  ******************************************************************************/
 
 LoadBlockingAsyncEventDispatcher::~LoadBlockingAsyncEventDispatcher()
 {
   if (mBlockedDoc) {
     mBlockedDoc->UnblockOnload(true);
--- a/dom/events/AsyncEventDispatcher.h
+++ b/dom/events/AsyncEventDispatcher.h
@@ -59,22 +59,28 @@ public:
 
   AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent);
 
   NS_IMETHOD Run() override;
   nsresult Cancel() override;
   nsresult PostDOMEvent();
   void RunDOMEventWhenSafe();
 
+  // Calling this causes the Run() method to check that
+  // mTarget->IsInComposedDoc(). mTarget must be an nsINode or else we'll
+  // assert.
+  void RequireNodeInDocument();
+
   nsCOMPtr<dom::EventTarget> mTarget;
   nsCOMPtr<nsIDOMEvent> mEvent;
   nsString              mEventType;
   bool                  mBubbles = false;
   bool                  mOnlyChromeDispatch = false;
   bool                  mCanceled = false;
+  bool                  mCheckStillInDoc = false;
 };
 
 class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher
 {
 public:
   LoadBlockingAsyncEventDispatcher(nsINode* aEventNode,
                                    const nsAString& aEventType,
                                    bool aBubbles, bool aDispatchChromeOnly)
--- a/dom/flyweb/FlyWebService.cpp
+++ b/dom/flyweb/FlyWebService.cpp
@@ -785,24 +785,28 @@ FlyWebMDNSService::PairWithService(const
   DiscoveredInfo* discInfo = mServiceMap.Get(aServiceId);
 
   nsAutoString url;
   if (discInfo->mService.mCert.IsEmpty()) {
     url.AssignLiteral("http://");
   } else {
     url.AssignLiteral("https://");
   }
-  url.Append(aInfo->mService.mHostname + NS_LITERAL_STRING("/"));
+  url.Append(aInfo->mService.mHostname);
+  if (!discInfo->mService.mPath.IsEmpty()) {
+    if (discInfo->mService.mPath.Find("/") != 0) {
+      url.Append(NS_LITERAL_STRING("/"));
+    }
+    url.Append(discInfo->mService.mPath);
+  } else {
+    url.Append(NS_LITERAL_STRING("/"));
+  }
   nsCOMPtr<nsIURI> uiURL;
   NS_NewURI(getter_AddRefs(uiURL), url);
   MOZ_ASSERT(uiURL);
-  if (!discInfo->mService.mPath.IsEmpty()) {
-    nsCOMPtr<nsIURI> tmp = uiURL.forget();
-    NS_NewURI(getter_AddRefs(uiURL), discInfo->mService.mPath, nullptr, tmp);
-  }
   if (uiURL) {
     nsAutoCString spec;
     uiURL->GetSpec(spec);
     CopyUTF8toUTF16(spec, aInfo->mService.mUiUrl);
   }
 
   aInfo->mService.mDiscoveredService = discInfo->mService;
   aInfo->mDNSServiceInfo = discInfo->mDNSServiceInfo;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -820,33 +820,34 @@ ContentChild::ProvideWindowCommon(TabChi
   nsString name(aName);
   nsTArray<FrameScriptInfo> frameScripts;
   nsCString urlToLoad;
 
   PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
   TextureFactoryIdentifier textureFactoryIdentifier;
   uint64_t layersId = 0;
   CompositorOptions compositorOptions;
+  uint32_t maxTouchPoints = 0;
 
   if (aIframeMoz) {
     MOZ_ASSERT(aTabOpener);
     nsAutoCString url;
     if (aURI) {
       aURI->GetSpec(url);
     } else {
       // We can't actually send a nullptr up as the URI, since IPDL doesn't let us
       // send nullptr's for primitives. We indicate that the nsString for the URI
       // should be converted to a nullptr by voiding the string.
       url.SetIsVoid(true);
     }
 
     newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
                                          name, NS_ConvertUTF8toUTF16(features),
                                          aWindowIsNew, &textureFactoryIdentifier,
-                                         &layersId, &compositorOptions);
+                                         &layersId, &compositorOptions, &maxTouchPoints);
   } else {
     nsAutoCString baseURIString;
     float fullZoom;
     OriginAttributes originAttributes;
     rv = GetWindowParamsFromParent(aParent, baseURIString, &fullZoom,
                                    originAttributes);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -860,17 +861,18 @@ ContentChild::ProvideWindowCommon(TabChi
                           originAttributes,
                           fullZoom,
                           &rv,
                           aWindowIsNew,
                           &frameScripts,
                           &urlToLoad,
                           &textureFactoryIdentifier,
                           &layersId,
-                          &compositorOptions)) {
+                          &compositorOptions,
+                          &maxTouchPoints)) {
       PRenderFrameChild::Send__delete__(renderFrame);
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     if (NS_FAILED(rv)) {
       PRenderFrameChild::Send__delete__(renderFrame);
       return rv;
     }
@@ -891,16 +893,18 @@ ContentChild::ProvideWindowCommon(TabChi
   if (opener && (openerShell = opener->GetDocShell())) {
     nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
     showInfo = ShowInfo(EmptyString(), false,
                         context->UsePrivateBrowsing(), true, false,
                         aTabOpener->mDPI, aTabOpener->mRounding,
                         aTabOpener->mDefaultScale);
   }
 
+  newChild->SetMaxTouchPoints(maxTouchPoints);
+
   // Set the opener window for this window before we start loading the document
   // inside of it. We have to do this before loading the remote scripts, because
   // they can poke at the document and cause the nsDocument to be created before
   // the openerwindow
   nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
   if (!aForceNoOpener && windowProxy && aParent) {
     nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
     nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4595,17 +4595,18 @@ ContentParent::RecvCreateWindow(PBrowser
                                 const OriginAttributes& aOpenerOriginAttributes,
                                 const float& aFullZoom,
                                 nsresult* aResult,
                                 bool* aWindowIsNew,
                                 InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                 nsCString* aURLToLoad,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                 uint64_t* aLayersId,
-                                CompositorOptions* aCompositorOptions)
+                                CompositorOptions* aCompositorOptions,
+                                uint32_t* aMaxTouchPoints)
 {
   // We always expect to open a new window here. If we don't, it's an error.
   *aWindowIsNew = true;
   *aResult = NS_OK;
 
   TabParent* newTab = TabParent::GetFrom(aNewTab);
   MOZ_ASSERT(newTab);
 
@@ -4649,16 +4650,19 @@ ContentParent::RecvCreateWindow(PBrowser
 
   RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
   if (!newTab->SetRenderFrame(rfp) ||
       !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
     *aResult = NS_ERROR_FAILURE;
   }
   *aCompositorOptions = rfp->GetCompositorOptions();
 
+  nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
+  *aMaxTouchPoints = widget ? widget->GetMaxTouchPoints() : 0;
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvCreateWindowInDifferentProcess(
   PBrowserParent* aThisTab,
   const uint32_t& aChromeFlags,
   const bool& aCalledFromJS,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -542,17 +542,18 @@ public:
                    const OriginAttributes& aOpenerOriginAttributes,
                    const float& aFullZoom,
                    nsresult* aResult,
                    bool* aWindowIsNew,
                    InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                    nsCString* aURLToLoad,
                    layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
                    uint64_t* aLayersId,
-                   mozilla::layers::CompositorOptions* aCompositorOptions) override;
+                   mozilla::layers::CompositorOptions* aCompositorOptions,
+                   uint32_t* aMaxTouchPoints) override;
 
   virtual mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess(
     PBrowserParent* aThisTab,
     const uint32_t& aChromeFlags,
     const bool& aCalledFromJS,
     const bool& aPositionSpecified,
     const bool& aSizeSpecified,
     const URIParams& aURIToLoad,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -373,21 +373,16 @@ parent:
     sync GetDefaultScale() returns (double value);
 
     /**
      * Gets the rounding of coordinates in the widget.
      */
     sync GetWidgetRounding() returns (int32_t value);
 
     /**
-     * Gets maximum of touch points at current device.
-     */
-    sync GetMaxTouchPoints() returns (uint32_t value);
-
-    /**
      * Set the native cursor.
      * @param value
      *   The widget cursor to set.
      * @param force
      *   Invalidate any locally cached cursor settings and force an
      *   update.
      */
     async SetCursor(uint32_t value, bool force);
@@ -461,17 +456,18 @@ parent:
      *
      * @param opener the PBrowser whose content called window.open.
      */
     sync BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame,
                                 nsString aURL, nsString aName, nsString aFeatures)
       returns (bool windowOpened,
                TextureFactoryIdentifier textureFactoryIdentifier,
                uint64_t layersId,
-               CompositorOptions compositorOptions);
+               CompositorOptions compositorOptions,
+               uint32_t maxTouchPoints);
 
     /**
      * Tells the containing widget whether the given input block results in a
      * swipe. Should be called in response to a WidgetWheelEvent that has
      * mFlags.mCanTriggerSwipe set on it.
      */
     async RespondStartSwipeEvent(uint64_t aInputBlockId, bool aStartSwipe);
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1012,17 +1012,18 @@ parent:
                       OriginAttributes aOpenerOriginAttributes,
                       float aFullZoom)
       returns (nsresult rv,
                bool windowOpened,
                FrameScriptInfo[] frameScripts,
                nsCString urlToLoad,
                TextureFactoryIdentifier textureFactoryIdentifier,
                uint64_t layersId,
-               CompositorOptions compositorOptions);
+               CompositorOptions compositorOptions,
+               uint32_t maxTouchPoints);
 
     async CreateWindowInDifferentProcess(
       PBrowser aThisTab,
       uint32_t aChromeFlags,
       bool aCalledFromJS,
       bool aPositionSpecified,
       bool aSizeSpecified,
       URIParams aURIToLoad,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -371,16 +371,17 @@ TabChild::TabChild(nsIContentChild* aMan
                    dom::TabGroup* aTabGroup,
                    const TabContext& aContext,
                    uint32_t aChromeFlags)
   : TabContext(aContext)
   , mTabGroup(aTabGroup)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
   , mChromeFlags(aChromeFlags)
+  , mMaxTouchPoints(0)
   , mActiveSuppressDisplayport(0)
   , mLayersId(0)
   , mBeforeUnloadListeners(0)
   , mLayersConnected(true)
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
@@ -2701,23 +2702,16 @@ TabChild::GetWidgetRounding(int32_t* aRo
     return;
   }
 
   // Fallback to a sync call if needed.
   SendGetWidgetRounding(aRounding);
 }
 
 void
-TabChild::GetMaxTouchPoints(uint32_t* aTouchPoints)
-{
-  // Fallback to a sync call.
-  SendGetMaxTouchPoints(aTouchPoints);
-}
-
-void
 TabChild::NotifyPainted()
 {
     if (!mNotified) {
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -498,17 +498,25 @@ public:
   void GetDPI(float* aDPI);
 
   void GetDefaultScale(double *aScale);
 
   void GetWidgetRounding(int32_t* aRounding);
 
   bool IsTransparent() const { return mIsTransparent; }
 
-  void GetMaxTouchPoints(uint32_t* aTouchPoints);
+  void GetMaxTouchPoints(uint32_t* aTouchPoints)
+  {
+    *aTouchPoints = mMaxTouchPoints;
+  }
+
+  void SetMaxTouchPoints(uint32_t aMaxTouchPoints)
+  {
+    mMaxTouchPoints = aMaxTouchPoints;
+  }
 
   ScreenOrientationInternal GetOrientation() const { return mOrientation; }
 
   void SetBackgroundColor(const nscolor& aColor);
 
   void NotifyPainted();
 
   void RequestEditCommands(nsIWidget::NativeKeyBindingsType aType,
@@ -782,16 +790,17 @@ private:
   nsCOMPtr<nsIWebNavigation> mWebNav;
   RefPtr<mozilla::dom::TabGroup> mTabGroup;
   RefPtr<PuppetWidget> mPuppetWidget;
   nsCOMPtr<nsIURI> mLastURI;
   RenderFrameChild* mRemoteFrame;
   RefPtr<nsIContentChild> mManager;
   RefPtr<TabChildSHistoryListener> mHistoryListener;
   uint32_t mChromeFlags;
+  uint32_t mMaxTouchPoints;
   int32_t mActiveSuppressDisplayport;
   uint64_t mLayersId;
   int64_t mBeforeUnloadListeners;
   CSSRect mUnscaledOuterRect;
   nscolor mLastBackgroundColor;
   bool mLayersConnected;
   bool mDidFakeShow;
   bool mNotified;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2291,28 +2291,16 @@ TabParent::RecvGetWidgetRounding(int32_t
   TryCacheDPIAndScale();
 
   MOZ_ASSERT(mRounding > 0 || mFrameElement,
              "Must not ask for rounding before OwnerElement is received!");
   *aValue = mRounding;
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-TabParent::RecvGetMaxTouchPoints(uint32_t* aTouchPoints)
-{
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (widget) {
-    *aTouchPoints = widget->GetMaxTouchPoints();
-  } else {
-    *aTouchPoints = 0;
-  }
-  return IPC_OK();
-}
-
 already_AddRefed<nsIWidget>
 TabParent::GetTopLevelWidget()
 {
   nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
   if (content) {
     nsIPresShell* shell = content->OwnerDoc()->GetShell();
     if (shell) {
       nsViewManager* vm = shell->GetViewManager();
@@ -2584,24 +2572,27 @@ mozilla::ipc::IPCResult
 TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                       PRenderFrameParent* aRenderFrame,
                                       const nsString& aURL,
                                       const nsString& aName,
                                       const nsString& aFeatures,
                                       bool* aOutWindowOpened,
                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                       uint64_t* aLayersId,
-                                      CompositorOptions* aCompositorOptions)
+                                      CompositorOptions* aCompositorOptions,
+                                      uint32_t* aMaxTouchPoints)
 {
   BrowserElementParent::OpenWindowResult opened =
     BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener),
                                         this, aRenderFrame, aURL, aName, aFeatures,
                                         aTextureFactoryIdentifier, aLayersId);
   *aCompositorOptions = static_cast<RenderFrameParent*>(aRenderFrame)->GetCompositorOptions();
   *aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  *aMaxTouchPoints = widget ? widget->GetMaxTouchPoints() : 0;
   if (!*aOutWindowOpened) {
     Destroy();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -176,17 +176,18 @@ public:
   virtual mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                                              PRenderFrameParent* aRenderFrame,
                                                              const nsString& aURL,
                                                              const nsString& aName,
                                                              const nsString& aFeatures,
                                                              bool* aOutWindowOpened,
                                                              TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                              uint64_t* aLayersId,
-                                                             CompositorOptions* aCompositorOptions) override;
+                                                             CompositorOptions* aCompositorOptions,
+                                                             uint32_t* aMaxTouchPoints) override;
 
   virtual mozilla::ipc::IPCResult
   RecvSyncMessage(const nsString& aMessage,
                   const ClonedMessageData& aData,
                   InfallibleTArray<CpowEntry>&& aCpows,
                   const IPC::Principal& aPrincipal,
                   nsTArray<ipc::StructuredCloneData>* aRetVal) override;
 
@@ -310,18 +311,16 @@ public:
   virtual mozilla::ipc::IPCResult RecvHideTooltip() override;
 
   virtual mozilla::ipc::IPCResult RecvGetDPI(float* aValue) override;
 
   virtual mozilla::ipc::IPCResult RecvGetDefaultScale(double* aValue) override;
 
   virtual mozilla::ipc::IPCResult RecvGetWidgetRounding(int32_t* aValue) override;
 
-  virtual mozilla::ipc::IPCResult RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override;
-
   virtual mozilla::ipc::IPCResult RecvGetWidgetNativeData(WindowsHandle* aValue) override;
 
   virtual mozilla::ipc::IPCResult RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
 
   virtual mozilla::ipc::IPCResult RecvDispatchFocusToTopLevelWindow() override;
 
   virtual mozilla::ipc::IPCResult RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
                                                              const bool& aStartSwipe) override;
--- a/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
@@ -15,16 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none"></div>
 
 <pre id="test">
 <script class="testbody" type="application/javascript"><![CDATA[
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
+var isDataUnique = SpecialPowers.Services.prefs.getBoolPref("security.data_uri.unique_opaque_origin");
 var B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /**
  * Encodes an array of bytes into a string using the base 64 encoding scheme.
  *
  * @param bytes
  *   An array of bytes to encode.
  */
@@ -135,19 +136,17 @@ function messageReceiver(evt)
       is(evt.origin, "http://mochi.test:8888",
          "wrong origin for event from about:blank #2");
       is(evt.source, aboutBlank2Window, "wrong source");
 
       setupData();
     }
     else if (evt.data === "data-response")
     {
-      // HTML5 defines the origin of a data: URI as the origin of the window or
-      // script that opened the data: URI.
-      is(evt.origin, "http://mochi.test:8888",
+      is(evt.origin, isDataUnique ? "null" : "http://mochi.test:8888",
          "wrong origin for event from data URL (should be the origin of the " +
          "window/script that opened the URL, in this case the origin of this " +
          "file)");
       is(evt.source, dataWindow, "wrong source");
 
       finish();
     }
     else
@@ -294,17 +293,17 @@ function testBlank2()
   script.textContent =
     "window.parent.postMessage('about:blank2-response', " +
     "                          'http://mochi.test:8888');";
   doc.body.appendChild(script);
 }
 
 function testData()
 {
-  dataWindow.postMessage("from-opener", "http://mochi.test:8888");
+  dataWindow.postMessage("from-opener", isDataUnique ? "*" : "http://mochi.test:8888");
 }
 
 window.addEventListener("message", messageReceiver);
 
 addLoadEvent(setupBlank);
 ]]></script>
 </pre>
 </body>
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -32,16 +32,22 @@ static const uint32_t MAX_TAP_TIME = 300
 /**
  * Amount of span or focus change needed to take us from the GESTURE_WAITING_PINCH
  * state to the GESTURE_PINCH state. This is measured as either a change in distance
  * between the fingers used to compute the span ratio, or the a change in
  * position of the focus point between the two fingers.
  */
 static const float PINCH_START_THRESHOLD = 35.0f;
 
+/**
+ * Determines how fast a one touch pinch zooms in and out. The greater the
+ * value, the faster it zooms.
+ */
+static const float ONE_TOUCH_PINCH_SPEED = 0.005f;
+
 static bool sLongTapEnabled = true;
 
 ParentLayerPoint GetCurrentFocus(const MultiTouchInput& aEvent)
 {
   const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
   const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
   return (firstTouch + secondTouch) / 2;
 }
@@ -49,16 +55,23 @@ ParentLayerPoint GetCurrentFocus(const M
 ParentLayerCoord GetCurrentSpan(const MultiTouchInput& aEvent)
 {
   const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
   const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
   ParentLayerPoint delta = secondTouch - firstTouch;
   return delta.Length();
 }
 
+ParentLayerCoord GestureEventListener::GetYSpanFromStartPoint()
+{
+  const ParentLayerPoint start = mTouchStartPosition;
+  const ParentLayerPoint& current = mTouches[0].mLocalScreenPoint;
+  return current.y - start.y;
+}
+
 TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch, TapGestureInput::TapGestureType aType)
 {
   return TapGestureInput(aType,
                          aTouch.mTime,
                          aTouch.mTimeStamp,
                          aTouch.mTouches[0].mScreenPoint,
                          aTouch.modifiers);
 }
@@ -88,38 +101,41 @@ nsEventStatus GestureEventListener::Hand
 
   // Cache the current event since it may become the single or long tap that we
   // send.
   mLastTouchInput = aEvent;
 
   switch (aEvent.mType) {
   case MultiTouchInput::MULTITOUCH_START:
     mTouches.Clear();
+    // Cache every touch.
     for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
       mTouches.AppendElement(aEvent.mTouches[i]);
     }
 
     if (aEvent.mTouches.Length() == 1) {
       rv = HandleInputTouchSingleStart();
     } else {
       rv = HandleInputTouchMultiStart();
     }
     break;
   case MultiTouchInput::MULTITOUCH_MOVE:
+    // Update the screen points of the cached touches.
     for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
       for (size_t j = 0; j < mTouches.Length(); j++) {
         if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
           mTouches[j].mScreenPoint = aEvent.mTouches[i].mScreenPoint;
           mTouches[j].mLocalScreenPoint = aEvent.mTouches[i].mLocalScreenPoint;
         }
       }
     }
     rv = HandleInputTouchMove();
     break;
   case MultiTouchInput::MULTITOUCH_END:
+    // Remove the cache of the touch that ended.
     for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
       for (size_t j = 0; j < mTouches.Length(); j++) {
         if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
           mTouches.RemoveElementAt(j);
           break;
         }
       }
     }
@@ -249,28 +265,55 @@ nsEventStatus GestureEventListener::Hand
     if (MoveDistanceIsLarge()) {
       // So that we don't fire a long-tap-up if the user moves around after a
       // long-tap
       SetState(GESTURE_NONE);
     }
     break;
 
   case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
-  case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
-  case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+  case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
     // If we move too much, bail out of the tap.
     if (MoveDistanceIsLarge()) {
       CancelLongTapTimeoutTask();
       CancelMaxTapTimeoutTask();
       mSingleTapSent = Nothing();
       SetState(GESTURE_NONE);
     }
     break;
   }
 
+  // The user has performed a double tap, but not lifted her finger.
+  case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+    // If touch has moved noticeably (within MAX_TAP_TIME), change state.
+    if (MoveDistanceIsLarge()) {
+      CancelLongTapTimeoutTask();
+      CancelMaxTapTimeoutTask();
+      mSingleTapSent = Nothing();
+      SetState(GESTURE_ONE_TOUCH_PINCH);
+
+      ParentLayerCoord currentSpan = 1.0f;
+      ParentLayerPoint currentFocus = mTouchStartPosition;
+
+      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
+                                   mLastTouchInput.mTime,
+                                   mLastTouchInput.mTimeStamp,
+                                   currentFocus,
+                                   currentSpan,
+                                   currentSpan,
+                                   mLastTouchInput.modifiers);
+
+      rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+
+      mPreviousSpan = currentSpan;
+      mPreviousFocus = currentFocus;
+    }
+    break;
+  }
+
   case GESTURE_MULTI_TOUCH_DOWN: {
     if (mLastTouchInput.mTouches.Length() < 2) {
       NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
       break;
     }
 
     ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
     ParentLayerPoint currentFocus = GetCurrentFocus(mLastTouchInput);
@@ -319,16 +362,40 @@ nsEventStatus GestureEventListener::Hand
                                  mLastTouchInput.modifiers);
 
     rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
     mPreviousSpan = currentSpan;
 
     break;
   }
 
+  case GESTURE_ONE_TOUCH_PINCH: {
+    ParentLayerCoord currentSpan = GetYSpanFromStartPoint();
+    float effectiveSpan = 1.0f + (fabsf(currentSpan.value) * ONE_TOUCH_PINCH_SPEED);
+    ParentLayerPoint currentFocus = mTouchStartPosition;
+
+    // Invert zoom.
+    if (currentSpan.value < 0) {
+      effectiveSpan = 1.0f / effectiveSpan;
+    }
+
+    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
+                                 mLastTouchInput.mTime,
+                                 mLastTouchInput.mTimeStamp,
+                                 currentFocus,
+                                 effectiveSpan,
+                                 mPreviousSpan,
+                                 mLastTouchInput.modifiers);
+
+    rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+    mPreviousSpan = effectiveSpan;
+
+    break;
+  }
+
   default:
     NS_WARNING("Unhandled state upon touch move");
     SetState(GESTURE_NONE);
     break;
   }
 
   return rv;
 }
@@ -406,16 +473,33 @@ nsEventStatus GestureEventListener::Hand
                                    mLastTouchInput.modifiers);
       mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
     }
 
     rv = nsEventStatus_eConsumeNoDefault;
 
     break;
 
+  case GESTURE_ONE_TOUCH_PINCH: {
+    SetState(GESTURE_NONE);
+    ParentLayerPoint point(-1, -1);
+    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
+                                 mLastTouchInput.mTime,
+                                 mLastTouchInput.mTimeStamp,
+                                 point,
+                                 1.0f,
+                                 1.0f,
+                                 mLastTouchInput.modifiers);
+    mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+
+    rv = nsEventStatus_eConsumeNoDefault;
+
+    break;
+  }
+
   default:
     NS_WARNING("Unhandled state upon touch end");
     SetState(GESTURE_NONE);
     break;
   }
 
   return rv;
 }
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -100,34 +100,40 @@ private:
     // After having gotten into this state we clear the timer for MAX_TAP_TIME.
     // Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
     //                      GESTURE_MULTI_TOUCH_DOWN.
     GESTURE_FIRST_SINGLE_TOUCH_UP,
 
     // A user put down her finger again right after a single tap thus the
     // gesture can't be a single tap, but rather a double tap. But we're
     // still not sure about that until the user lifts her finger again.
-    // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE.
+    // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_ONE_TOUCH_PINCH,
+    //                      GESTURE_NONE.
     GESTURE_SECOND_SINGLE_TOUCH_DOWN,
 
     // A long touch has happened, but the user still keeps her finger down.
     // We'll trigger a "long tap up" event when the finger is up.
     // Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
     GESTURE_LONG_TOUCH_DOWN,
 
     // We have detected that two or more fingers are on the screen, but there
     // hasn't been enough movement yet to make us start actually zooming the
     // screen.
     // Allowed next states: GESTURE_PINCH, GESTURE_NONE
     GESTURE_MULTI_TOUCH_DOWN,
 
     // There are two or more fingers on the screen, and the user has already
     // pinched enough for us to start zooming the screen.
     // Allowed next states: GESTURE_NONE
-    GESTURE_PINCH
+    GESTURE_PINCH,
+
+    // The user has double tapped, but not lifted her finger, and moved her
+    // finger more than PINCH_START_THRESHOLD.
+    // Allowed next states: GESTURE_NONE.
+    GESTURE_ONE_TOUCH_PINCH
   };
 
   /**
    * These HandleInput* functions comprise input alphabet of the GEL
    * finite-state machine triggering state transitions.
    */
   nsEventStatus HandleInputTouchSingleStart();
   nsEventStatus HandleInputTouchMultiStart();
@@ -137,16 +143,22 @@ private:
   void HandleInputTimeoutLongTap();
   void HandleInputTimeoutMaxTap(bool aDuringFastFling);
 
   void TriggerSingleTapConfirmedEvent();
 
   bool MoveDistanceIsLarge();
 
   /**
+   * Returns current vertical span, counting from the where the user first put
+   * her finger down.
+   */
+  ParentLayerCoord GetYSpanFromStartPoint();
+
+  /**
    * Do actual state transition and reset substates.
    */
   void SetState(GestureState aState);
 
   RefPtr<AsyncPanZoomController> mAsyncPanZoomController;
 
   /**
    * Array containing all active touches. When a touch happens it, gets added to
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -840,18 +840,16 @@ description =
 [PBrowser::IsParentWindowMainWidgetVisible]
 description =
 [PBrowser::GetDPI]
 description =
 [PBrowser::GetDefaultScale]
 description =
 [PBrowser::GetWidgetRounding]
 description =
-[PBrowser::GetMaxTouchPoints]
-description =
 [PBrowser::BrowserFrameOpenWindow]
 description =
 [PBrowser::RequestNativeKeyBindings]
 description =
 [PBrowser::GetTabCount]
 description =
 [PBrowser::DispatchWheelEvent]
 description =
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -528,62 +528,35 @@ js::regexp_construct(JSContext* cx, unsi
  */
 bool
 js::regexp_construct_raw_flags(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(!args.isConstructing());
 
-    Rooted<RegExpObject*> rx(cx, &args[0].toObject().as<RegExpObject>());
-
     // Step 4.a.
-    RootedAtom sourceAtom(cx, rx->getSource());
+    RootedAtom sourceAtom(cx, AtomizeString(cx, args[0].toString()));
+    if (!sourceAtom)
+        return false;
 
     // Step 4.c.
     int32_t flags = int32_t(args[1].toNumber());
 
     // Step 7.
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
     if (!regexp)
         return false;
 
     // Step 8.
     regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
     args.rval().setObject(*regexp);
     return true;
 }
 
-bool
-js::regexp_clone(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject from(cx, &args[0].toObject());
-
-    RootedAtom sourceAtom(cx);
-    RegExpFlag flags;
-    {
-        RootedRegExpShared g(cx);
-        if (!RegExpToShared(cx, from, &g))
-            return false;
-        sourceAtom = g->getSource();
-        flags = g->getFlags();
-    }
-
-    Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
-    if (!regexp)
-        return false;
-
-    regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
-
-    args.rval().setObject(*regexp);
-    return true;
-}
-
 MOZ_ALWAYS_INLINE bool
 IsRegExpPrototype(HandleValue v)
 {
     if (IsRegExpObject(v) || !v.isObject())
         return false;
 
     // Note: The prototype shares its JSClass with instances.
     return StandardProtoKeyOrNull(&v.toObject()) == JSProto_RegExp;
@@ -1023,21 +996,19 @@ js::RegExpMatcher(JSContext* cx, unsigne
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isNumber());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
-    RootedValue lastIndexVal(cx, args[2]);
 
-    int32_t lastIndex = 0;
-    if (!ToInt32(cx, lastIndexVal, &lastIndex))
-        return false;
+    int32_t lastIndex;
+    MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
 
     /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
     return RegExpMatcherImpl(cx, regexp, string, lastIndex,
                              UpdateRegExpStatics, args.rval());
 }
 
 /*
  * Separate interface for use by IonMonkey.
@@ -1098,21 +1069,19 @@ js::RegExpSearcher(JSContext* cx, unsign
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isNumber());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
-    RootedValue lastIndexVal(cx, args[2]);
 
-    int32_t lastIndex = 0;
-    if (!ToInt32(cx, lastIndexVal, &lastIndex))
-        return false;
+    int32_t lastIndex;
+    MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
 
     /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
     int32_t result = 0;
     if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, UpdateRegExpStatics, &result))
         return false;
 
     args.rval().setInt32(result);
     return true;
@@ -1163,21 +1132,19 @@ js::RegExpTester(JSContext* cx, unsigned
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isNumber());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
-    RootedValue lastIndexVal(cx, args[2]);
 
-    int32_t lastIndex = 0;
-    if (!ToInt32(cx, lastIndexVal, &lastIndex))
-        return false;
+    int32_t lastIndex;
+    MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
 
     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
     size_t endIndex = 0;
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
                                            nullptr, &endIndex, UpdateRegExpStatics);
 
     if (status == RegExpRunStatus_Error)
         return false;
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -83,31 +83,27 @@ regexp_exec_no_statics(JSContext* cx, un
  * Behaves like regexp.test(string), but doesn't set RegExp statics.
  *
  * Usage: does_match = regexp_test_no_statics(regexp, string)
  */
 extern MOZ_MUST_USE bool
 regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp);
 
 /*
- * Behaves like RegExp(pattern, flags).
- * |pattern| should be a RegExp object, |flags| should be a raw integer value.
+ * Behaves like RegExp(source, flags).
+ * |source| must be a valid regular expression pattern, |flags| is a raw
+ * integer value representing the regular expression flags.
  * Must be called without |new|.
- * Dedicated function for RegExp.prototype[@@split] optimized path.
+ *
+ * Dedicated function for RegExp.prototype[@@replace] and
+ * RegExp.prototype[@@split] optimized paths.
  */
 extern MOZ_MUST_USE bool
 regexp_construct_raw_flags(JSContext* cx, unsigned argc, Value* vp);
 
-/*
- * Clone given RegExp object, inheriting pattern and flags, ignoring other
- * properties.
- */
-extern MOZ_MUST_USE bool
-regexp_clone(JSContext* cx, unsigned argc, Value* vp);
-
 extern MOZ_MUST_USE bool
 IsRegExp(JSContext* cx, HandleValue value, bool* result);
 
 extern MOZ_MUST_USE bool
 RegExpCreate(JSContext* cx, HandleValue pattern, HandleValue flags, MutableHandleValue rval);
 
 extern MOZ_MUST_USE bool
 RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp);
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -304,16 +304,18 @@ function RegExpReplace(string, replaceVa
             return RegExpGlobalReplaceOpt(rx, S, lengthS, replaceValue,
                                           fullUnicode);
         }
 
         if (functionalReplace)
             return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue);
         if (firstDollarIndex !== -1)
             return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, firstDollarIndex);
+        if (lengthS < 0x7fff)
+            return RegExpLocalReplaceOptShort(rx, S, lengthS, replaceValue);
         return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue);
     }
 
     // Steps 8-16.
     return RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
                                  functionalReplace, firstDollarIndex);
 }
 
@@ -387,17 +389,16 @@ function RegExpReplaceSlowPath(rx, S, le
 
         // Steps 14.e-f.
         var position = std_Math_max(std_Math_min(ToInteger(result.index), lengthS), 0);
 
         var n, capN, replacement;
         if (functionalReplace || firstDollarIndex !== -1) {
             // Steps 14.g-j.
             replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
                                                       nCaptures, replaceValue,
                                                       functionalReplace, firstDollarIndex);
         } else {
             // Step 14.g, 14.i, 14.i.iv.
             // We don't need captures array, but ToString is visible to script.
             for (n = 1; n <= nCaptures; n++) {
                 // Step 14.i.i-ii.
                 capN = result[n];
@@ -407,40 +408,37 @@ function RegExpReplaceSlowPath(rx, S, le
                     ToString(capN);
             }
             replacement = replaceValue;
         }
 
         // Step 14.l.
         if (position >= nextSourcePosition) {
             // Step 14.l.ii.
-          accumulatedResult += Substring(S, nextSourcePosition,
-                                         position - nextSourcePosition) + replacement;
+            accumulatedResult += Substring(S, nextSourcePosition,
+                                           position - nextSourcePosition) + replacement;
 
             // Step 14.l.iii.
             nextSourcePosition = position + matchLength;
         }
     }
 
     // Step 15.
     if (nextSourcePosition >= lengthS)
         return accumulatedResult;
 
     // Step 16.
     return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
 }
 
 // ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
 // steps 14.g-k.
-// Calculates functional/substitution replaceement from match result.
+// Calculates functional/substitution replacement from match result.
 // Used in the following functions:
-//   * RegExpGlobalReplaceOptFunc
-//   * RegExpGlobalReplaceOptElemBase
 //   * RegExpGlobalReplaceOptSubst
-//   * RegExpLocalReplaceOptFunc
 //   * RegExpLocalReplaceOptSubst
 //   * RegExpReplaceSlowPath
 function RegExpGetComplexReplacement(result, matched, S, position,
                                      nCaptures, replaceValue,
                                      functionalReplace, firstDollarIndex)
 {
     // Step 14.h.
     var captures = [];
@@ -479,26 +477,68 @@ function RegExpGetComplexReplacement(res
           case 3:
             return ToString(replaceValue(matched, SPREAD(captures, 3), position, S));
           case 4:
             return ToString(replaceValue(matched, SPREAD(captures, 4), position, S));
           default:
             // Steps 14.j.ii-v.
             _DefineDataProperty(captures, capturesLength++, position);
             _DefineDataProperty(captures, capturesLength++, S);
-            return ToString(callFunction(std_Function_apply, replaceValue, null, captures));
+            return ToString(callFunction(std_Function_apply, replaceValue, undefined, captures));
         }
     }
 
     // Steps 14.k.i.
     return RegExpGetSubstitution(matched, S, position, captures, replaceValue,
                                  firstDollarIndex);
 }
 
 // ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
+// steps 14.g-j.
+// Calculates functional replacement from match result.
+// Used in the following functions:
+//   * RegExpGlobalReplaceOptFunc
+//   * RegExpGlobalReplaceOptElemBase
+//   * RegExpLocalReplaceOptFunc
+function RegExpGetFunctionalReplacement(result, S, position, replaceValue) {
+    // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
+    // use `std_Function_apply` with all arguments stored in `captures`.
+    assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
+    var nCaptures = result.length - 1;
+
+    switch (nCaptures) {
+      case 0:
+        return ToString(replaceValue(SPREAD(result, 1), position, S));
+      case 1:
+        return ToString(replaceValue(SPREAD(result, 2), position, S));
+      case 2:
+        return ToString(replaceValue(SPREAD(result, 3), position, S));
+      case 3:
+        return ToString(replaceValue(SPREAD(result, 4), position, S));
+      case 4:
+        return ToString(replaceValue(SPREAD(result, 5), position, S));
+    }
+
+    // Steps 14.g-i, 14.j.i-ii.
+    var captures = [];
+    for (var n = 0; n <= nCaptures; n++) {
+        assert(typeof result[n] === "string" || result[n] === undefined,
+               "RegExpMatcher returns only strings and undefined");
+        _DefineDataProperty(captures, n, result[n]);
+    }
+
+    // Step 14.j.iii.
+    _DefineDataProperty(captures, nCaptures + 1, position);
+    _DefineDataProperty(captures, nCaptures + 2, S);
+
+    // Steps 14.j.iv-v.
+    return ToString(callFunction(std_Function_apply, replaceValue, undefined, captures));
+}
+
+// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
 // steps 8.b-16.
 // Optimized path for @@replace with the following conditions:
 //   * global flag is true
 //   * S is a short string (lengthS < 0x7fff)
 //   * replaceValue is a string without "$"
 function RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, fullUnicode)
 {
     // Step 8.b.
@@ -588,16 +628,26 @@ function RegExpGlobalReplaceShortOpt(rx,
 //   * global flag is false
 //   * replaceValue is a string without "$"
 #define FUNC_NAME RegExpLocalReplaceOpt
 #include "RegExpLocalReplaceOpt.h.js"
 #undef FUNC_NAME
 
 // Conditions:
 //   * global flag is false
+//   * S is a short string (lengthS < 0x7fff)
+//   * replaceValue is a string without "$"
+#define FUNC_NAME RegExpLocalReplaceOptShort
+#define SHORT_STRING
+#include "RegExpLocalReplaceOpt.h.js"
+#undef SHORT_STRING
+#undef FUNC_NAME
+
+// Conditions:
+//   * global flag is false
 //   * replaceValue is a function
 #define FUNC_NAME RegExpLocalReplaceOptFunc
 #define FUNCTIONAL
 #include "RegExpLocalReplaceOpt.h.js"
 #undef FUNCTIONAL
 #undef FUNC_NAME
 
 // Conditions:
@@ -727,17 +777,22 @@ function RegExpSplit(string, limit) {
         // Step 5.
         flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
 
         // Steps 6-7.
         unicodeMatching = !!(flags & (REGEXP_UNICODE_FLAG));
 
         // Steps 8-10.
         // If split operation is optimizable, perform non-sticky match.
-        splitter = regexp_construct_raw_flags(rx, flags & ~REGEXP_STICKY_FLAG);
+        if (flags & REGEXP_STICKY_FLAG) {
+            var source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
+            splitter = regexp_construct_raw_flags(source, flags & ~REGEXP_STICKY_FLAG);
+        } else {
+            splitter = rx;
+        }
     } else {
         // Step 5.
         flags = ToString(rx.flags);
 
         // Steps 6-7.
         unicodeMatching = callFunction(std_String_includes, flags, "u");
 
         // Steps 8-9.
@@ -755,17 +810,17 @@ function RegExpSplit(string, limit) {
     var A = [];
 
     // Step 12.
     var lengthA = 0;
 
     // Step 13.
     var lim;
     if (limit === undefined)
-        lim = MAX_NUMERIC_INDEX;
+        lim = MAX_UINT32;
     else
         lim = limit >>> 0;
 
     // Step 15.
     var p = 0;
 
     // Step 16.
     if (lim === 0)
--- a/js/src/builtin/RegExpGlobalReplaceOpt.h.js
+++ b/js/src/builtin/RegExpGlobalReplaceOpt.h.js
@@ -26,20 +26,21 @@ function FUNC_NAME(rx, S, lengthS, repla
                    , elemBase
 #endif
                   )
 {
     // Step 8.b.
     var lastIndex = 0;
     rx.lastIndex = 0;
 
-#if defined(FUNCTIONAL) || defined(SUBSTITUTION)
-    // Clone RegExp object here to avoid the effect of RegExp#compile,
-    // that may be called in replaceValue function.
-    rx = regexp_clone(rx);
+#if defined(FUNCTIONAL)
+    // Save the original source and flags, so we can check if the replacer
+    // function recompiled the regexp.
+    var originalSource = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
+    var originalFlags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
 #endif
 
     // Step 12 (reordered).
     var accumulatedResult = "";
 
     // Step 13 (reordered).
     var nextSourcePosition = 0;
 
@@ -47,64 +48,54 @@ function FUNC_NAME(rx, S, lengthS, repla
     while (true) {
         // Step 11.a.
         var result = RegExpMatcher(rx, S, lastIndex);
 
         // Step 11.b.
         if (result === null)
             break;
 
-        var nCaptures;
-#if defined(FUNCTIONAL) || defined(SUBSTITUTION)
+#if defined(SUBSTITUTION)
         // Steps 14.a-b.
-        nCaptures = std_Math_max(result.length - 1, 0);
+        assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
+        var nCaptures = result.length - 1;
 #endif
 
-        // Step 14.c (reordered).
+        // Step 14.c.
         var matched = result[0];
 
         // Step 14.d.
         var matchLength = matched.length;
 
         // Steps 14.e-f.
         var position = result.index;
         lastIndex = position + matchLength;
 
-        // Steps g-j.
+        // Steps g-k.
         var replacement;
 #if defined(FUNCTIONAL)
-        replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
-                                                  nCaptures, replaceValue,
-                                                  true, -1);
+        replacement = RegExpGetFunctionalReplacement(result, S, position, replaceValue);
 #elif defined(SUBSTITUTION)
         replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
                                                   nCaptures, replaceValue,
                                                   false, firstDollarIndex);
 #elif defined(ELEMBASE)
         if (IsObject(elemBase)) {
             var prop = GetStringDataProperty(elemBase, matched);
             if (prop !== undefined) {
-                assert(typeof prop === "string", "GetStringDataProperty should return either string or undefined");
+                assert(typeof prop === "string",
+                       "GetStringDataProperty should return either string or undefined");
                 replacement = prop;
             } else {
                 elemBase = undefined;
             }
         }
 
-        if (!IsObject(elemBase)) {
-            // Steps 14.a-b (reordered).
-            nCaptures = std_Math_max(result.length - 1, 0);
-
-            replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
-                                                      nCaptures, replaceValue,
-                                                      true, -1);
-        }
+        if (!IsObject(elemBase))
+            replacement = RegExpGetFunctionalReplacement(result, S, position, replaceValue);
 #else
         replacement = replaceValue;
 #endif
 
         // Step 14.l.ii.
         accumulatedResult += Substring(S, nextSourcePosition,
                                        position - nextSourcePosition) + replacement;
 
@@ -112,16 +103,26 @@ function FUNC_NAME(rx, S, lengthS, repla
         nextSourcePosition = lastIndex;
 
         // Step 11.c.iii.2.
         if (matchLength === 0) {
             lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
             if (lastIndex > lengthS)
                 break;
         }
+
+#if defined(FUNCTIONAL)
+        // Ensure the current source and flags match the original regexp, the
+        // replaceValue function may have called RegExp#compile.
+        if (UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT) !== originalSource ||
+            UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT) !== originalFlags)
+        {
+            rx = regexp_construct_raw_flags(originalSource, originalFlags);
+        }
+#endif
     }
 
     // Step 15.
     if (nextSourcePosition >= lengthS)
         return accumulatedResult;
 
     // Step 16.
     return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
--- a/js/src/builtin/RegExpLocalReplaceOpt.h.js
+++ b/js/src/builtin/RegExpLocalReplaceOpt.h.js
@@ -4,16 +4,17 @@
 //   * RegExpLocalReplaceOptSubst
 // Define the following macro and include this file to declare function:
 //   * FUNC_NAME     -- function name (required)
 //       e.g.
 //         #define FUNC_NAME RegExpLocalReplaceOpt
 // Define the following macro (without value) to switch the code:
 //   * SUBSTITUTION     -- replaceValue is a string with "$"
 //   * FUNCTIONAL       -- replaceValue is a function
+//   * SHORT_STRING     -- replaceValue is a string without "$" and lengthS < 0x7fff
 //   * neither of above -- replaceValue is a string without "$"
 
 // ES 2017 draft 6390c2f1b34b309895d31d8c0512eac8660a0210 21.2.5.8
 // steps 11.a-16.
 // Optimized path for @@replace with the following conditions:
 //   * global flag is false
 function FUNC_NAME(rx, S, lengthS, replaceValue
 #ifdef SUBSTITUTION
@@ -42,63 +43,85 @@ function FUNC_NAME(rx, S, lengthS, repla
             // Steps 12-16.
             return S;
         }
     } else {
         // 21.2.5.2.2 RegExpBuiltinExec, step 8.
         lastIndex = 0;
     }
 
+#if !defined(SHORT_STRING)
     // Step 11.a.
     var result = RegExpMatcher(rx, S, lastIndex);
 
     // Step 11.b.
     if (result === null) {
         // 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i.
         if (globalOrSticky)
             rx.lastIndex = 0;
 
         // Steps 12-16.
         return S;
     }
+#else
+    // Step 11.a.
+    var result = RegExpSearcher(rx, S, lastIndex);
+
+    // Step 11.b.
+    if (result === -1) {
+        // 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i.
+        if (globalOrSticky)
+            rx.lastIndex = 0;
+
+        // Steps 12-16.
+        return S;
+    }
+#endif
 
     // Steps 11.c, 12-13, 14.a-b (skipped).
 
-#if defined(FUNCTIONAL) || defined(SUBSTITUTION)
+#if defined(SUBSTITUTION)
     // Steps 14.a-b.
-    var nCaptures = std_Math_max(result.length - 1, 0);
+    assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
+    var nCaptures = result.length - 1;
 #endif
 
+#if !defined(SHORT_STRING)
     // Step 14.c.
     var matched = result[0];
 
     // Step 14.d.
     var matchLength = matched.length;
 
     // Step 14.e-f.
     var position = result.index;
 
     // Step 14.l.iii (reordered)
     // To set rx.lastIndex before RegExpGetComplexReplacement.
     var nextSourcePosition = position + matchLength;
+#else
+    // Steps 14.c-d (skipped).
+
+    // Step 14.e-f.
+    var position = result & 0x7fff;
+
+    // Step 14.l.iii (reordered)
+    var nextSourcePosition = (result >> 15) & 0x7fff;
+#endif
 
     // 21.2.5.2.2 RegExpBuiltinExec, step 15.
     if (globalOrSticky)
        rx.lastIndex = nextSourcePosition;
 
     var replacement;
     // Steps g-j.
 #if defined(FUNCTIONAL)
-    replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
-                                              nCaptures, replaceValue,
-                                              true, -1);
+    replacement = RegExpGetFunctionalReplacement(result, S, position, replaceValue);
 #elif defined(SUBSTITUTION)
     replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
                                               nCaptures, replaceValue,
                                               false, firstDollarIndex);
 #else
     replacement = replaceValue;
 #endif
 
     // Step 14.l.ii.
     var accumulatedResult = Substring(S, 0, position) + replacement;
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -8,16 +8,17 @@
 
 #ifndef builtin_SelfHostingDefines_h
 #define builtin_SelfHostingDefines_h
 
 // Utility macros.
 #define TO_INT32(x) ((x) | 0)
 #define TO_UINT32(x) ((x) >>> 0)
 #define IS_UINT32(x) ((x) >>> 0 === (x))
+#define MAX_UINT32 0xffffffff
 #define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1
 
 // Unforgeable version of Function.prototype.apply.
 #define FUN_APPLY(FUN, RECEIVER, ARGS) \
   callFunction(std_Function_apply, FUN, RECEIVER, ARGS)
 
 // NB: keep this in sync with the copy in vm/ArgumentsObject.h.
 #define MAX_ARGS_LENGTH (500 * 1000)
@@ -78,16 +79,17 @@
 
 // NB: keep these in sync with the copy in jsfriendapi.h.
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_SYMBOLS    0x20  /* also include symbol property keys */
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
 
 
+#define REGEXP_SOURCE_SLOT 1
 #define REGEXP_FLAGS_SLOT 2
 
 #define REGEXP_IGNORECASE_FLAG  0x01
 #define REGEXP_GLOBAL_FLAG      0x02
 #define REGEXP_MULTILINE_FLAG   0x04
 #define REGEXP_STICKY_FLAG      0x08
 #define REGEXP_UNICODE_FLAG     0x10
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -152,33 +152,32 @@ IsThingPoisoned(T* thing)
 static bool
 IsMovingTracer(JSTracer *trc)
 {
     return trc->isCallbackTracer() &&
            trc->asCallbackTracer()->getTracerKind() == JS::CallbackTracer::TracerKind::Moving;
 }
 #endif
 
-template <typename T> bool ThingIsPermanentAtomOrWellKnownSymbol(T* thing) { return false; }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSString>(JSString* str) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSString* str) {
     return str->isPermanentAtom();
 }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSFlatString>(JSFlatString* str) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSFlatString* str) {
     return str->isPermanentAtom();
 }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSLinearString>(JSLinearString* str) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString* str) {
     return str->isPermanentAtom();
 }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSAtom>(JSAtom* atom) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom* atom) {
     return atom->isPermanent();
 }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<PropertyName>(PropertyName* name) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(PropertyName* name) {
     return name->isPermanent();
 }
-template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JS::Symbol>(JS::Symbol* sym) {
+bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol* sym) {
     return sym->isWellKnownSymbol();
 }
 
 template <typename T>
 static inline bool
 IsOwnedByOtherRuntime(JSRuntime* rt, T thing)
 {
     bool other = thing->runtimeFromAnyThread() != rt;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -497,9 +497,22 @@ void
 CheckTracedThing(JSTracer* trc, T* thing);
 
 template<typename T>
 void
 CheckTracedThing(JSTracer* trc, T thing);
 
 } /* namespace js */
 
+namespace JS {
+class Symbol;
+}
+
+// Exported for Tracer.cpp
+inline bool ThingIsPermanentAtomOrWellKnownSymbol(js::gc::Cell* thing) { return false; }
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSString*);
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSFlatString*);
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString*);
+bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom*);
+bool ThingIsPermanentAtomOrWellKnownSymbol(js::PropertyName*);
+bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol*);
+
 #endif /* gc_Marking_h */
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -108,18 +108,23 @@ JS::CallbackTracer::getTracingEdgeName(c
 JS_PUBLIC_API(void)
 JS::TraceChildren(JSTracer* trc, GCCellPtr thing)
 {
     js::TraceChildren(trc, thing.asCell(), thing.kind());
 }
 
 struct TraceChildrenFunctor {
     template <typename T>
-    void operator()(JSTracer* trc, void* thing) {
-        static_cast<T*>(thing)->traceChildren(trc);
+    void operator()(JSTracer* trc, void* thingArg) {
+        T* thing = static_cast<T*>(thingArg);
+        MOZ_ASSERT_IF(thing->runtimeFromAnyThread() != trc->runtime(),
+            ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
+            thing->zoneFromAnyThread()->isSelfHostingZone());
+
+        thing->traceChildren(trc);
     }
 };
 
 void
 js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind)
 {
     MOZ_ASSERT(thing);
     TraceChildrenFunctor f;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -77,18 +77,17 @@ selfHosting_WarningReporter(JSContext* c
 
     PrintError(cx, stderr, JS::ConstUTF8CharsZ(), report, true);
 }
 
 static bool
 intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedValue val(cx, args[0]);
-    RootedObject obj(cx, ToObject(cx, val));
+    JSObject* obj = ToObject(cx, args[0]);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
 intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp)
@@ -139,18 +138,17 @@ intrinsic_ToInteger(JSContext* cx, unsig
     args.rval().setNumber(result);
     return true;
 }
 
 static bool
 intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedString str(cx);
-    str = ToString<CanGC>(cx, args[0]);
+    JSString* str = ToString<CanGC>(cx, args[0]);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 intrinsic_ToSource(JSContext* cx, unsigned argc, Value* vp)
@@ -546,17 +544,17 @@ intrinsic_DecompileArg(JSContext* cx, un
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
     RootedValue value(cx, args[1]);
     ScopedJSFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value));
     if (!str)
         return false;
-    RootedAtom atom(cx, Atomize(cx, str, strlen(str)));
+    JSAtom* atom = Atomize(cx, str, strlen(str));
     if (!atom)
         return false;
     args.rval().setString(atom);
     return true;
 }
 
 static bool
 intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
@@ -733,17 +731,17 @@ intrinsic_GetNextMapEntryForIterator(JSC
 }
 
 static bool
 intrinsic_CreateMapIterationResultPair(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
-    RootedObject result(cx, MapIteratorObject::createResultPair(cx));
+    JSObject* result = MapIteratorObject::createResultPair(cx);
     if (!result)
         return false;
 
     args.rval().setObject(*result);
     return true;
 }
 
 static bool
@@ -762,17 +760,17 @@ intrinsic_GetNextSetEntryForIterator(JSC
 }
 
 static bool
 intrinsic_CreateSetIterationResult(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
-    RootedObject result(cx, SetIteratorObject::createResult(cx));
+    JSObject* result = SetIteratorObject::createResult(cx);
     if (!result)
         return false;
 
     args.rval().setObject(*result);
     return true;
 }
 
 static bool
@@ -796,17 +794,17 @@ intrinsic_NewStringIterator(JSContext* c
 static bool
 intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
     RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
     MOZ_ASSERT(fun->isSelfHostedBuiltin());
-    RootedAtom atom(cx, AtomizeString(cx, args[1].toString()));
+    JSAtom* atom = AtomizeString(cx, args[1].toString());
     if (!atom)
         return false;
 
     fun->setAtom(atom);
 #ifdef DEBUG
     fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(true));
 #endif
     args.rval().setUndefined();
@@ -1134,17 +1132,17 @@ intrinsic_TypedArrayElementShift(JSConte
 
 // Return the value of [[ArrayLength]] internal slot of the TypedArray
 static bool
 intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    RootedObject obj(cx, &args[0].toObject());
+    JSObject* obj = &args[0].toObject();
     MOZ_ASSERT(obj->is<TypedArrayObject>());
     args.rval().setInt32(obj->as<TypedArrayObject>().length());
     return true;
 }
 
 static bool
 intrinsic_PossiblyWrappedTypedArrayLength(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1663,18 +1661,17 @@ js::intrinsic_StringSplitString(JSContex
 
     RootedString string(cx, args[0].toString());
     RootedString sep(cx, args[1].toString());
 
     RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
     if (!group)
         return false;
 
-    RootedObject aobj(cx);
-    aobj = str_split_string(cx, group, string, sep, INT32_MAX);
+    JSObject* aobj = str_split_string(cx, group, string, sep, INT32_MAX);
     if (!aobj)
         return false;
 
     args.rval().setObject(*aobj);
     return true;
 }
 
 static bool
@@ -1689,18 +1686,17 @@ intrinsic_StringSplitStringLimit(JSConte
     // args[2] should be already in UInt32 range, but it could be double typed,
     // because of Ion optimization.
     uint32_t limit = uint32_t(args[2].toNumber());
 
     RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
     if (!group)
         return false;
 
-    RootedObject aobj(cx);
-    aobj = str_split_string(cx, group, string, sep, limit);
+    JSObject* aobj = str_split_string(cx, group, string, sep, limit);
     if (!aobj)
         return false;
 
     args.rval().setObject(*aobj);
     return true;
 }
 
 bool
@@ -1820,17 +1816,17 @@ intrinsic_RuntimeDefaultLocale(JSContext
     CallArgs args = CallArgsFromVp(argc, vp);
 
     const char* locale = cx->runtime()->getDefaultLocale();
     if (!locale) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR);
         return false;
     }
 
-    RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale));
+    JSString* jslocale = JS_NewStringCopyZ(cx, locale);
     if (!jslocale)
         return false;
 
     args.rval().setString(jslocale);
     return true;
 }
 
 using GetOrCreateIntlConstructor = JSFunction* (*)(JSContext*, Handle<GlobalObject*>);
@@ -2092,17 +2088,17 @@ intrinsic_EvaluateModule(JSContext* cx, 
 
 static bool
 intrinsic_NewModuleNamespace(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
     RootedObject exports(cx, &args[1].toObject());
-    RootedObject namespace_(cx, ModuleObject::createNamespace(cx, module, exports));
+    JSObject* namespace_ = ModuleObject::createNamespace(cx, module, exports);
     if (!namespace_)
         return false;
 
     args.rval().setObject(*namespace_);
     return true;
 }
 
 static bool
@@ -2131,41 +2127,41 @@ intrinsic_ModuleNamespaceExports(JSConte
     return true;
 }
 
 static bool
 intrinsic_CreatePendingPromise(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
-    RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx));
+    JSObject* promise = PromiseObject::createSkippingExecutor(cx);
     if (!promise)
         return false;
     args.rval().setObject(*promise);
     return true;
 }
 
 static bool
 intrinsic_CreatePromiseResolvedWith(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
-    RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, args[0]));
+    JSObject* promise = PromiseObject::unforgeableResolve(cx, args[0]);
     if (!promise)
         return false;
     args.rval().setObject(*promise);
     return true;
 }
 
 static bool
 intrinsic_CreatePromiseRejectedWith(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
-    RootedObject promise(cx, PromiseObject::unforgeableReject(cx, args[0]));
+    JSObject* promise = PromiseObject::unforgeableReject(cx, args[0]);
     if (!promise)
         return false;
     args.rval().setObject(*promise);
     return true;
 }
 
 static bool
 intrinsic_ResolvePromise(JSContext* cx, unsigned argc, Value* vp)
@@ -2198,18 +2194,18 @@ intrinsic_CallOriginalPromiseThen(JSCont
     MOZ_ASSERT(args.length() >= 2);
 
     RootedObject promise(cx, &args[0].toObject());
     Value val = args[1];
     RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
     val = args.get(2);
     RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
 
-    RootedObject resultPromise(cx, JS::CallOriginalPromiseThen(cx, promise, onResolvedObj,
-                                                               onRejectedObj));
+    JSObject* resultPromise = JS::CallOriginalPromiseThen(cx, promise, onResolvedObj,
+                                                          onRejectedObj);
     if (!resultPromise)
         return false;
     args.rval().setObject(*resultPromise);
     return true;
 }
 
 static bool
 intrinsic_AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
@@ -2218,18 +2214,17 @@ intrinsic_AddPromiseReactions(JSContext*
     MOZ_ASSERT(args.length() >= 2);
 
     RootedObject promise(cx, &args[0].toObject());
     Value val = args[1];
     RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
     val = args.get(2);
     RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
 
-    bool result = JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj);
-    if (!result)
+    if (!JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj))
         return false;
     args.rval().setUndefined();
     return true;
 }
 
 // The self-hosting global isn't initialized with the normal set of builtins.
 // Instead, individual C++-implemented functions that're required by
 // self-hosted code are defined as global functions. Accessing these
@@ -2593,21 +2588,21 @@ static const JSFunctionSpec intrinsic_fu
           intrinsic_GetBuiltinIntlConstructor<GlobalObject::getOrCreateNumberFormatConstructor>,
           0,0),
 
     JS_INLINABLE_FN("IsRegExpObject",
                     intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
                     IsRegExpObject),
     JS_FN("CallRegExpMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
-    JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0,
+    JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 3,0,
                     RegExpMatcher),
-    JS_INLINABLE_FN("RegExpSearcher", RegExpSearcher, 4,0,
+    JS_INLINABLE_FN("RegExpSearcher", RegExpSearcher, 3,0,
                     RegExpSearcher),
-    JS_INLINABLE_FN("RegExpTester", RegExpTester, 4,0,
+    JS_INLINABLE_FN("RegExpTester", RegExpTester, 3,0,
                     RegExpTester),
     JS_FN("RegExpCreate", intrinsic_RegExpCreate, 2,0),
     JS_INLINABLE_FN("RegExpPrototypeOptimizable", RegExpPrototypeOptimizable, 1,0,
                     RegExpPrototypeOptimizable),
     JS_INLINABLE_FN("RegExpInstanceOptimizable", RegExpInstanceOptimizable, 1,0,
                     RegExpInstanceOptimizable),
     JS_FN("RegExpGetSubstitution", intrinsic_RegExpGetSubstitution, 6,0),
     JS_FN("GetElemBaseForLambda", intrinsic_GetElemBaseForLambda, 1,0),
@@ -2623,17 +2618,16 @@ static const JSFunctionSpec intrinsic_fu
                     IntrinsicStringSplitString),
     JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0),
     JS_FN("WarnDeprecatedStringMethod", intrinsic_WarnDeprecatedStringMethod, 2, 0),
 
     // See builtin/RegExp.h for descriptions of the regexp_* functions.
     JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
     JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
     JS_FN("regexp_construct_raw_flags", regexp_construct_raw_flags, 2,0),
-    JS_FN("regexp_clone", regexp_clone, 1,0),
 
     JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0),
     JS_FN("CallModuleMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),
     JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, 0),
     JS_FN("IsModuleEnvironment", intrinsic_IsInstanceOfBuiltin<ModuleEnvironmentObject>, 1, 0),
     JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 1, 0),
     JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0),
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -150,24 +150,22 @@ public:
       } else {
           mActive = false;
       }
       return NS_OK;
   }
 
   nsresult Dispatch()
   {
-      if (NS_IsMainThread()) {
-          nsCOMPtr<nsIRunnable> self(this);
-          return SystemGroup::Dispatch("AsyncFreeSnowWhite",
-                                       TaskCategory::GarbageCollection,
-                                       self.forget());
-      } else {
-          return NS_DispatchToCurrentThread(this);
+      nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+      if (!thread) {
+          return NS_ERROR_FAILURE;
       }
+      nsCOMPtr<nsIRunnable> self(this);
+      return thread->IdleDispatch(self.forget());
   }
 
   void Start(bool aContinuation = false, bool aPurge = false)
   {
       if (mContinuation) {
           mContinuation = aContinuation;
       }
       mPurge = aPurge;
--- a/layout/reftests/w3c-css/failures.list
+++ b/layout/reftests/w3c-css/failures.list
@@ -10,18 +10,18 @@
 # If a test matches multiple path pattern, the last one wins. Because
 # of this, an item could have zero <failure-type>, which indicates it
 # is expected to pass, and override failure rule above it.
 
 
 #### Selectors 4 ####################################################
 
 # focus-within
-needs-focus selectors-4/focus-within-00*.html
-pref(dom.webcomponents.enabled,true) needs-focus skip-if(stylo) selectors-4/focus-within-shadow-*.html
+needs-focus selectors4/focus-within-00*.html
+pref(dom.webcomponents.enabled,true) needs-focus skip-if(stylo) selectors4/focus-within-shadow-*.html
 
 # Bug 1208113
 fails-if(!stylo) needs-focus selectors-4/focus-within-shadow-001.html
 
 
 #### CSS Values 3 ####################################################
 
 # Fuzzy
@@ -162,26 +162,26 @@ fuzzy(116,467) css-multicol-1/multicol-c
 fuzzy(87,180) css-multicol-1/multicol-columns-001.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-002.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-003.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-004.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-005.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-006.xht
 fuzzy(87,180) css-multicol-1/multicol-columns-007.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-columns-invalid-001.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-columns-invalid-002.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-columns-invalid-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-columns-toolong-001.xht
 fuzzy(116,530) css-multicol-1/multicol-containing-001.xht
 fuzzy(215,241) css-multicol-1/multicol-containing-002.xht
 fuzzy(87,180) css-multicol-1/multicol-count-001.xht
 fails-if(!stylo) css-multicol-1/multicol-count-002.xht
 fails-if(!stylo) css-multicol-1/multicol-count-computed-001.xht
 fails-if(!stylo) css-multicol-1/multicol-count-computed-002.xht
 fails-if(!stylo) css-multicol-1/multicol-count-computed-003.xht
-fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(!stylo)-if(Android) css-multicol-1/multicol-count-computed-004.xht
+fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(Android) css-multicol-1/multicol-count-computed-004.xht
 fails-if(!stylo) css-multicol-1/multicol-count-computed-005.xht
 fails-if(!stylo) css-multicol-1/multicol-count-large-001.xht
 fuzzy(255,132) css-multicol-1/multicol-count-large-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-count-negative-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-count-negative-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-count-non-integer-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-count-non-integer-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-count-non-integer-003.xht
@@ -189,17 +189,17 @@ fuzzy(116,80) css-multicol-1/multicol-fi
 fuzzy(116,3270) css-multicol-1/multicol-fill-auto-003.xht
 fails-if(!stylo) css-multicol-1/multicol-fill-auto.xht
 fuzzy(116,80) css-multicol-1/multicol-fill-balance-001.xht
 fuzzy(116,821) css-multicol-1/multicol-gap-000.xht
 fuzzy(255,290) css-multicol-1/multicol-gap-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-gap-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-gap-003.xht
 fuzzy(107,1823) css-multicol-1/multicol-gap-fraction-001.xht
-fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) fails-if(!stylo)-if(Android) css-multicol-1/multicol-gap-large-001.xht
+fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) fails-if(Android) css-multicol-1/multicol-gap-large-001.xht
 fuzzy(225,920) css-multicol-1/multicol-gap-large-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) css-multicol-1/multicol-gap-negative-001.xht
 fails-if(!stylo) css-multicol-1/multicol-height-block-child-001.xht
 fuzzy(255,3762) css-multicol-1/multicol-inherit-001.xht
 fuzzy(116,1893) css-multicol-1/multicol-inherit-002.xht
 fails-if(!stylo) css-multicol-1/multicol-inherit-003.xht
 fails-if(!stylo) css-multicol-1/multicol-inherit-004.xht
 fuzzy(96,264) css-multicol-1/multicol-list-item-001.xht
@@ -214,39 +214,39 @@ fuzzy(204,2371) fuzzy-if(skiaContent,208
 fuzzy(225,2529) fails-if(stylo) css-multicol-1/multicol-nested-margin-004.xht
 fuzzy(225,2529) fails-if(stylo) css-multicol-1/multicol-nested-margin-005.xht
 fuzzy(116,142) css-multicol-1/multicol-overflow-000.xht
 fuzzy(204,1844) fuzzy-if(skiaContent,208,1844) css-multicol-1/multicol-overflowing-001.xht
 fuzzy-if(OSX,61,2) fuzzy-if(skiaContent,64,2) css-multicol-1/multicol-reduce-000.xht
 fuzzy-if(OSX,8,20) css-multicol-1/multicol-rule-000.xht
 fuzzy(116,1584) css-multicol-1/multicol-rule-001.xht
 fails-if(!stylo) css-multicol-1/multicol-rule-002.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-rule-003.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-rule-color-001.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-rule-003.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-rule-color-001.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-dashed-000.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-dotted-000.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-double-000.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-001.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-002.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-001.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-002.xht
 fails-if(!stylo) css-multicol-1/multicol-rule-fraction-003.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-groove-000.xht
 fuzzy(94,256) css-multicol-1/multicol-rule-hidden-000.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-inset-000.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-outset-000.xht
 fails-if(!stylo) css-multicol-1/multicol-rule-px-001.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-ridge-000.xht
 fuzzy(106,354) css-multicol-1/multicol-rule-solid-000.xht
 fails-if(!stylo) css-multicol-1/multicol-rule-stacking-001.xht
 fails-if(!stylo) css-multicol-1/multicol-shorthand-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-000.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-002.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-003.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-child-001.xht
-fails-if(!stylo)-if(OSX||winWidget) css-multicol-1/multicol-span-all-child-002.xht
+fails-if(OSX||winWidget) css-multicol-1/multicol-span-all-child-002.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-002.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-bottom-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-nested-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-nested-002.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-nested-003.xht
 fails-if(!stylo) css-multicol-1/multicol-span-all-margin-nested-firstchild-001.xht
 fails-if(!stylo) css-multicol-1/multicol-span-float-001.xht
--- a/layout/reftests/w3c-css/import-tests.py
+++ b/layout/reftests/w3c-css/import-tests.py
@@ -30,17 +30,17 @@ import re
 # But for now, let's just import a few sets of tests.
 
 gSubtrees = [
     os.path.join("css-namespaces-3"),
     os.path.join("css-conditional-3"),
     os.path.join("css-values-3"),
     os.path.join("css-multicol-1"),
     os.path.join("css-writing-modes-3"),
-    os.path.join("selectors-4"),
+    os.path.join("selectors4"),
 ]
 
 gPrefixedProperties = [
     "column-count",
     "column-fill",
     "column-gap",
     "column-rule",
     "column-rule-color",
@@ -85,21 +85,30 @@ def log_output_of(subprocess):
     subprocess.wait()
     if (subprocess.returncode != 0):
         raise StandardError("error while running subprocess")
     gLog.write(subprocess.stdout.readline().rstrip())
 
 def write_log_header():
     global gLog, gSrcPath
     gLog.write("Importing revision: ")
-    log_output_of(Popen(["hg", "parent", "--template={node}"],
+    log_output_of(Popen(["git", "rev-parse", "HEAD"],
                   stdout=PIPE, cwd=gSrcPath))
     gLog.write("\nfrom repository: ")
-    log_output_of(Popen(["hg", "paths", "default"],
-                  stdout=PIPE, cwd=gSrcPath))
+    branches = Popen(["git", "branch", "--format",
+                      "%(HEAD)%(upstream:lstrip=2)"],
+                     stdout=PIPE, cwd=gSrcPath)
+    for branch in branches.stdout:
+        if branch[0] == "*":
+            upstream = branch[1:].split("/")[0]
+            break
+    if len(upstream.strip()) == 0:
+        raise StandardError("No upstream repository found")
+    log_output_of(Popen(["git", "remote", "get-url", upstream],
+                        stdout=PIPE, cwd=gSrcPath))
     gLog.write("\n")
 
 def remove_existing_dirs():
     global gDestPath
     # Remove existing directories that we're going to regenerate.  This
     # is necessary so that we can give errors in cases where our import
     # might copy two files to the same location, which we do by giving
     # errors if a copy would overwrite a file.
@@ -269,32 +278,36 @@ def copy_and_prefix(test, aSourceFileNam
 
     newFile.close()
     unPrefixedFile.close()
 
 def read_options():
     global gArgs, gOptions
     op = OptionParser()
     op.usage = \
-    '''%prog <clone of hg repository>
-            Import reftests from a W3C hg repository clone. The location of
-            the local clone of the hg repository must be given on the command
+    '''%prog <clone of git repository>
+            Import CSS reftests from a web-platform-tests git repository clone.
+            The location of the git repository must be given on the command
             line.'''
     (gOptions, gArgs) = op.parse_args()
     if len(gArgs) != 1:
         op.error("Too few arguments specified.")
 
 def setup_paths():
     global gSubtrees, gDestPath, gSrcPath
     # FIXME: generate gSrcPath with consistent trailing / regardless of input.
     # (We currently expect the argument to have a trailing slash.)
     gSrcPath = gArgs[0]
     if not os.path.isdir(gSrcPath) or \
-    not os.path.isdir(os.path.join(gSrcPath, ".hg")):
-        raise StandardError("source path does not appear to be a mercurial clone")
+       not os.path.isdir(os.path.join(gSrcPath, ".git")):
+        raise StandardError("source path does not appear to be a git clone")
+    gSrcPath = os.path.join(gSrcPath, "css") + "/"
+    if not os.path.isdir(gSrcPath):
+        raise StandardError("source path does not appear to be " +
+                            "a wpt clone which contains css tests")
 
     gDestPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "received")
     newSubtrees = []
     for relPath in gSubtrees:
         newSubtrees[len(gSubtrees):] = [os.path.join(gSrcPath, relPath)]
     gSubtrees = newSubtrees
 
 def setup_log():
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1216,16 +1216,24 @@ pref("dom.storage.default_quota",      5
 pref("dom.storage.testing", false);
 
 pref("dom.send_after_paint_to_content", false);
 
 // Timeout clamp in ms for timeouts we clamp
 pref("dom.min_timeout_value", 4);
 // And for background windows
 pref("dom.min_background_timeout_value", 1000);
+// Timeout clamp in ms for tracking timeouts we clamp
+// Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
+pref("dom.min_tracking_timeout_value", 4);
+// And for background windows
+// Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
+pref("dom.min_tracking_background_timeout_value", 10000);
+// Delay in ms from document load until we start throttling tracking timeouts.
+pref("dom.timeout.tracking_throttling_delay", 30000);
 
 // Don't use new input types
 pref("dom.experimental_forms", false);
 
 // Enable <input type=number>:
 pref("dom.forms.number", true);
 
 // Enable <input type=color> by default. It will be turned off for remaining
--- a/netwerk/dns/nsIDNService.cpp
+++ b/netwerk/dns/nsIDNService.cpp
@@ -808,22 +808,24 @@ bool nsIDNService::isLabelSafe(const nsA
   while (current != end) {
     uint32_t ch = *current++;
 
     if (NS_IS_HIGH_SURROGATE(ch) && current != end &&
         NS_IS_LOW_SURROGATE(*current)) {
       ch = SURROGATE_TO_UCS4(ch, *current++);
     }
 
-    // Check for restricted characters; aspirational scripts are permitted
+    // Check for restricted characters; aspirational scripts are NOT permitted,
+    // in anticipation of the category being merged into Limited-Use scripts
+    // in the upcoming (Unicode 10.0-based) revision of UAX #31.
     IdentifierType idType = GetIdentifierType(ch);
-    if (idType == IDTYPE_RESTRICTED) {
+    if (idType == IDTYPE_RESTRICTED || idType == IDTYPE_ASPIRATIONAL) {
       return false;
     }
-    MOZ_ASSERT(idType == IDTYPE_ALLOWED || idType == IDTYPE_ASPIRATIONAL);
+    MOZ_ASSERT(idType == IDTYPE_ALLOWED);
 
     // Check for mixed script
     Script script = GetScriptCode(ch);
     if (script != Script::COMMON &&
         script != Script::INHERITED &&
         script != lastScript) {
       if (illegalScriptCombo(script, savedScript)) {
         return false;
--- a/netwerk/test/unit/test_idn_urls.js
+++ b/netwerk/test/unit/test_idn_urls.js
@@ -33,18 +33,18 @@ const testcases = [
                  "xn---in-russian-1jg071b0a8bb4cpd", false, false, false],
 
     // Mixed script Latin/Cyrillic
     ["war-and-миръ", "xn--war-and--b9g3b7b3h",       false, false, false],
 
     // Cherokee (Restricted script)
     ["ᏣᎳᎩ",     "xn--f9dt7l",                        false, false, false],
 
-    // Yi (Aspirational script)
-    ["ꆈꌠꁱꂷ", "xn--4o7a6e1x64c",                  false, true,  true],
+    // Yi (former Aspirational script, now Restricted per Unicode 10.0 update to UAX 31)
+    ["ꆈꌠꁱꂷ", "xn--4o7a6e1x64c",                  false, false,  false],
 
     // Greek alone
     ["πλάτων",   "xn--hxa3ahjw4a",                   false, true,  true],
 
     // Mixed script Greek/Latin
     ["πλάτωνicrelationship",
                  "xn--icrelationship-96j4t9a3cwe2e", false, false, false],
 
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/releases/bouncer_firefox_devedition.py
@@ -0,0 +1,139 @@
+# lint_ignore=E501
+config = {
+    "shipped-locales-url": "https://hg.mozilla.org/%(repo)s/raw-file/%(revision)s/browser/locales/shipped-locales",
+    "products": {
+        "installer": {
+            "product-name": "Devedition-%(version)s",
+            "check_uptake": True,
+            "alias": "devedition-latest",
+            "ssl-only": False,
+            "add-locales": True,
+            "paths": {
+                "linux": {
+                    "path": "/devedition/releases/%(version)s/linux-i686/:lang/firefox-%(version)s.tar.bz2",
+                    "bouncer-platform": "linux",
+                },
+                "linux64": {
+                    "path": "/devedition/releases/%(version)s/linux-x86_64/:lang/firefox-%(version)s.tar.bz2",
+                    "bouncer-platform": "linux64",
+                },
+                "macosx64": {
+                    "path": "/devedition/releases/%(version)s/mac/:lang/Firefox%%20%(version)s.dmg",
+                    "bouncer-platform": "osx",
+                },
+                "win32": {
+                    "path": "/devedition/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win",
+                },
+                "win64": {
+                    "path": "/devedition/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
+            },
+        },
+        "installer-ssl": {
+            "product-name": "Devedition-%(version)s-SSL",
+            "check_uptake": True,
+            "alias": "devedition-latest-ssl",
+            "ssl-only": True,
+            "add-locales": True,
+            "paths": {
+                "linux": {
+                    "path": "/devedition/releases/%(version)s/linux-i686/:lang/firefox-%(version)s.tar.bz2",
+                    "bouncer-platform": "linux",
+                },
+                "linux64": {
+                    "path": "/devedition/releases/%(version)s/linux-x86_64/:lang/firefox-%(version)s.tar.bz2",
+                    "bouncer-platform": "linux64",
+                },
+                "macosx64": {
+                    "path": "/devedition/releases/%(version)s/mac/:lang/Firefox%%20%(version)s.dmg",
+                    "bouncer-platform": "osx",
+                },
+                "win32": {
+                    "path": "/devedition/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win",
+                },
+                "win64": {
+                    "path": "/devedition/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
+            },
+        },
+        "stub-installer": {
+            "product-name": "Devedition-%(version)s-stub",
+            "check_uptake": True,
+            "alias": "devedition-stub",
+            "ssl-only": True,
+            "add-locales": True,
+            "paths": {
+                "win32": {
+                    "path": "/devedition/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20Stub%%20%(version)s.exe",
+                    "bouncer-platform": "win",
+                },
+                "win64": {
+                    "path": "/devedition/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20Stub%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
+            },
+        },
+        "complete-mar": {
+            "product-name": "Devedition-%(version)s-Complete",
+            "check_uptake": True,
+            "ssl-only": False,
+            "add-locales": True,
+            "paths": {
+                "linux": {
+                    "path": "/devedition/releases/%(version)s/update/linux-i686/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "linux",
+                },
+                "linux64": {
+                    "path": "/devedition/releases/%(version)s/update/linux-x86_64/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "linux64",
+                },
+                "macosx64": {
+                    "path": "/devedition/releases/%(version)s/update/mac/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "osx",
+                },
+                "win32": {
+                    "path": "/devedition/releases/%(version)s/update/win32/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "win",
+                },
+                "win64": {
+                    "path": "/devedition/releases/%(version)s/update/win64/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "win64",
+                },
+            },
+        },
+    },
+    "partials": {
+        "releases-dir": {
+            "product-name": "Devedition-%(version)s-Partial-%(prev_version)s",
+            "check_uptake": True,
+            "ssl-only": False,
+            "add-locales": True,
+            "paths": {
+                "linux": {
+                    "path": "/devedition/releases/%(version)s/update/linux-i686/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "linux",
+                },
+                "linux64": {
+                    "path": "/devedition/releases/%(version)s/update/linux-x86_64/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "linux64",
+                },
+                "macosx64": {
+                    "path": "/devedition/releases/%(version)s/update/mac/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "osx",
+                },
+                "win32": {
+                    "path": "/devedition/releases/%(version)s/update/win32/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "win",
+                },
+                "win64": {
+                    "path": "/devedition/releases/%(version)s/update/win64/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "win64",
+                },
+            },
+        },
+    },
+}
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2968,19 +2968,26 @@ this.XPIProvider = {
    *
    * @returns {Array<object>}
    *   A sorted array of add-on objects. Each value is a copy of the
    *   corresponding value in the `bootstrappedAddons` object, with an
    *   additional `id` property, which corresponds to the key in that
    *   object, which is the same as the add-ons ID.
    */
   sortBootstrappedAddons() {
+    function compare(a, b) {
+      if (a === b) {
+        return 0;
+      }
+      return (a < b) ? -1 : 1;
+    }
+
     // Sort the list so that ordering is deterministic.
     let list = Array.from(XPIStates.bootstrappedAddons());
-    list.sort((a, b) => String.localeCompare(a.id, b.id));
+    list.sort((a, b) => compare(a.id, b.id));
 
     let addons = {};
     for (let entry of list) {
       addons[entry.id] = entry;
     }
 
     let res = new Set();
     let seen = new Set();
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -1,14 +1,21 @@
 /* -*- 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 "nsPrefetchService.h"
+
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/CORSMode.h"
+#include "mozilla/dom/HTMLLinkElement.h"
+#include "mozilla/Preferences.h"
+
 #include "nsICacheEntry.h"
 #include "nsIServiceManager.h"
 #include "nsICategoryManager.h"
 #include "nsIObserverService.h"
 #include "nsIWebProgress.h"
 #include "nsCURILoader.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIHttpChannel.h"
@@ -20,20 +27,16 @@
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsAutoPtr.h"
 #include "prtime.h"
 #include "mozilla/Logging.h"
 #include "plstr.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/CORSMode.h"
-#include "mozilla/dom/HTMLLinkElement.h"
 #include "nsIDOMNode.h"
 #include "nsINode.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 
 using namespace mozilla;
 
 //
@@ -489,23 +492,27 @@ nsPrefetchService::NotifyLoadCompleted(n
 }
 
 void
 nsPrefetchService::DispatchEvent(nsPrefetchNode *node, bool aSuccess)
 {
     for (uint32_t i = 0; i < node->mSources.Length(); i++) {
       nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i));
       if (domNode && domNode->IsInComposedDoc()) {
-        nsContentUtils::DispatchTrustedEvent(domNode->OwnerDoc(),
-                                             domNode,
-                                             aSuccess ?
-                                              NS_LITERAL_STRING("load") :
-                                              NS_LITERAL_STRING("error"),
-                                             /* aCanBubble = */ false,
-                                             /* aCancelable = */ false);
+        // We don't dispatch synchronously since |node| might be in a DocGroup
+        // that we're not allowed to touch. (Our network request happens in the
+        // DocGroup of one of the mSources nodes--not necessarily this one).
+        RefPtr<AsyncEventDispatcher> dispatcher =
+          new AsyncEventDispatcher(domNode,
+                                   aSuccess ?
+                                    NS_LITERAL_STRING("load") :
+                                    NS_LITERAL_STRING("error"),
+                                   /* aCanBubble = */ false);
+        dispatcher->RequireNodeInDocument();
+        dispatcher->PostDOMEvent();
       }
     }
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService <private>
 //-----------------------------------------------------------------------------
 
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1255,26 +1255,21 @@ PuppetWidget::GetWindowPosition()
 LayoutDeviceIntRect
 PuppetWidget::GetScreenBounds()
 {
   return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
 }
 
 uint32_t PuppetWidget::GetMaxTouchPoints() const
 {
-  static uint32_t sTouchPoints = 0;
-  static bool sIsInitialized = false;
-  if (sIsInitialized) {
-    return sTouchPoints;
+  uint32_t maxTouchPoints = 0;
+  if (mTabChild) {
+    mTabChild->GetMaxTouchPoints(&maxTouchPoints);
   }
-  if (mTabChild) {
-    mTabChild->GetMaxTouchPoints(&sTouchPoints);
-    sIsInitialized = true;
-  }
-  return sTouchPoints;
+  return maxTouchPoints;
 }
 
 void
 PuppetWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
 {
   mTabChild->StartScrollbarDrag(aDragMetrics);
 }
 
--- a/xpfe/appshell/nsWindowMediator.cpp
+++ b/xpfe/appshell/nsWindowMediator.cpp
@@ -608,22 +608,20 @@ nsWindowMediator::SetZPosition(
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowMediator::GetZLevel(nsIXULWindow *aWindow, uint32_t *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = nsIXULWindow::normalZ;
+  // This can fail during window destruction.
   nsWindowInfo *info = GetInfoFor(aWindow);
   if (info) {
     *_retval = info->mZLevel;
-  } else {
-    NS_WARNING("getting z level of unregistered window");
-    // this goes off during window destruction
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowMediator::SetZLevel(nsIXULWindow *aWindow, uint32_t aZLevel)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());