Bug 1314707: Replace PDocAccessible::SendCOMProxy with new parameter to PDocAccessibleConstructor and async RecvParentCOMProxy call in child. Sending of a11y events from child to parent is now deferred until DocAccessibleChild::RecvParentCOMProxy is called; r=tbsaunde
authorAaron Klotz <aklotz@mozilla.com>
Thu, 01 Dec 2016 16:28:54 -0700
changeset 326046 2f56b053ab5cffd9e050e5a8aca69840a9d6a611
parent 326045 f497d59d065c0836eeb9d78413aab986a45dca81
child 326075 7c828bc4d59ee94d181687fab56957bb8074c5ac
push id84873
push useraklotz@mozilla.com
push dateFri, 16 Dec 2016 01:37:53 +0000
treeherdermozilla-inbound@2f56b053ab5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs1314707
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1314707: Replace PDocAccessible::SendCOMProxy with new parameter to PDocAccessibleConstructor and async RecvParentCOMProxy call in child. Sending of a11y events from child to parent is now deferred until DocAccessibleChild::RecvParentCOMProxy is called; r=tbsaunde MozReview-Commit-ID: DjxSfLJQtTu
accessible/base/DocManager.cpp
accessible/base/NotificationController.cpp
accessible/ipc/DocAccessibleChildBase.cpp
accessible/ipc/DocAccessibleChildBase.h
accessible/ipc/DocAccessibleParent.cpp
accessible/ipc/DocAccessibleParent.h
accessible/ipc/IPCTypes.h
accessible/ipc/moz.build
accessible/ipc/win/DocAccessibleChild.cpp
accessible/ipc/win/DocAccessibleChild.h
accessible/ipc/win/PDocAccessible.ipdl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -528,29 +528,29 @@ DocManager::CreateDocOrRootAccessible(ns
 
         // XXX We may need to handle the case that we don't have a tab child
         // differently.  It may be that this will cause us to fail to notify
         // the parent process about important accessible documents.
         if (tabChild) {
           DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
           docAcc->SetIPCDoc(ipcDoc);
 
+#if defined(XP_WIN)
+          IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
+#endif
+
           static_cast<TabChild*>(tabChild.get())->
             SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
 #if defined(XP_WIN)
-                                          AccessibleWrap::GetChildIDFor(docAcc)
+                                          AccessibleWrap::GetChildIDFor(docAcc),
+                                          holder
 #else
-                                          0
+                                          0, 0
 #endif
                                           );
-
-#if defined(XP_WIN)
-          IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
-          ipcDoc->SendCOMProxy(holder);
-#endif
         }
       }
     }
   } else {
     parentDocAcc->BindChildDocument(docAcc);
   }
 
 #ifdef A11Y_LOG
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -849,29 +849,30 @@ NotificationController::WillRefresh(mozi
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
       }
 
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
+
+#if defined(XP_WIN)
+      MOZ_ASSERT(parentIPCDoc);
+      parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
+                                                     AccessibleWrap::GetChildIDFor(childDoc));
+#else
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
       if (tabChild) {
         MOZ_ASSERT(parentIPCDoc);
         static_cast<TabChild*>(tabChild.get())->
-          SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id,
-#if defined(XP_WIN)
-                                        AccessibleWrap::GetChildIDFor(childDoc)
-#else
-                                        0
+          SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
+      }
 #endif
-                                        );
-      }
     }
   }
 
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
 
   // Stop further processing if there are no new notifications of any kind or
--- a/accessible/ipc/DocAccessibleChildBase.cpp
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -84,14 +84,14 @@ void
 DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
   uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
   uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
   nsTArray<AccessibleData> shownTree;
   ShowEventData data(parentID, idxInParent, shownTree);
   SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
-  SendShowEvent(data, aShowEvent->IsFromUserInput());
+  MaybeSendShowEvent(data, aShowEvent->IsFromUserInput());
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/DocAccessibleChildBase.h
+++ b/accessible/ipc/DocAccessibleChildBase.h
@@ -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/. */
 
 #ifndef mozilla_a11y_DocAccessibleChildBase_h
 #define mozilla_a11y_DocAccessibleChildBase_h
 
 #include "mozilla/a11y/DocAccessible.h"
 #include "mozilla/a11y/PDocAccessibleChild.h"
+#include "mozilla/Unused.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace a11y {
 
 class Accessible;
 class AccShowEvent;
 
@@ -33,20 +34,19 @@ public:
     MOZ_ASSERT(!mDoc);
     if (mDoc) {
       mDoc->SetIPCDoc(nullptr);
     }
 
     MOZ_COUNT_DTOR(DocAccessibleChildBase);
   }
 
-  void Shutdown()
+  virtual void Shutdown()
   {
-    mDoc->SetIPCDoc(nullptr);
-    mDoc = nullptr;
+    DetachDocument();
     SendShutdown();
   }
 
   void ShowEvent(AccShowEvent* aShowEvent);
 
   virtual void ActorDestroy(ActorDestroyReason) override
   {
     if (!mDoc) {
@@ -56,15 +56,26 @@ public:
     mDoc->SetIPCDoc(nullptr);
     mDoc = nullptr;
   }
 
 protected:
   static uint32_t InterfacesFor(Accessible* aAcc);
   static void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree);
 
+  virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser)
+  { Unused << SendShowEvent(aData, aFromUser); }
+
+  void DetachDocument()
+  {
+    if (mDoc) {
+      mDoc->SetIPCDoc(nullptr);
+      mDoc = nullptr;
+    }
+  }
+
   DocAccessible*  mDoc;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_DocAccessibleChildBase_h
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -462,35 +462,41 @@ DocAccessibleParent::GetXPCAccessible(Pr
   MOZ_ASSERT(doc);
 
   return doc->GetXPCAccessible(aProxy);
 }
 
 #if defined(XP_WIN)
 /**
  * @param aCOMProxy COM Proxy to the document in the content process.
- * @param aParentCOMProxy COM Proxy to the OuterDocAccessible that is
- *        the parent of the document. The content process will use this
- *        proxy when traversing up across the content/chrome boundary.
  */
-mozilla::ipc::IPCResult
-DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
-                                  IAccessibleHolder* aParentCOMProxy)
+void
+DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
 {
-  RefPtr<IAccessible> ptr(aCOMProxy.Get());
-  SetCOMInterface(ptr);
+  SetCOMInterface(aCOMProxy);
+
+  // Make sure that we're not racing with a tab shutdown
+  auto tab = static_cast<dom::TabParent*>(Manager());
+  MOZ_ASSERT(tab);
+  if (tab->IsDestroyed()) {
+    return;
+  }
 
   Accessible* outerDoc = OuterDocOfRemoteBrowser();
+  MOZ_ASSERT(outerDoc);
+
   IAccessible* rawNative = nullptr;
   if (outerDoc) {
     outerDoc->GetNativeInterface((void**) &rawNative);
+    MOZ_ASSERT(rawNative);
   }
 
-  aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative));
-  return IPC_OK();
+  IAccessibleHolder::COMPtrType ptr(rawNative);
+  IAccessibleHolder holder(Move(ptr));
+  Unused << SendParentCOMProxy(holder);
 }
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvGetWindowedPluginIAccessible(
       const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy)
 {
 #if defined(MOZ_CONTENT_SANDBOX)
   // We don't actually want the accessible object for aHwnd, but rather the
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -138,18 +138,17 @@ public:
   const ProxyAccessible* GetAccessible(uintptr_t aID) const
     { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
 
   size_t ChildDocCount() const { return mChildDocs.Length(); }
   const DocAccessibleParent* ChildDocAt(size_t aIdx) const
     { return mChildDocs[aIdx]; }
 
 #if defined(XP_WIN)
-  virtual mozilla::ipc::IPCResult RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
-                                               IAccessibleHolder* aParentCOMProxy) override;
+  void SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy);
 
   virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible(
       const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override;
 #endif
 
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/IPCTypes.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_IPCTypes_h
+#define mozilla_a11y_IPCTypes_h
+
+/**
+ * Since IPDL does not support preprocessing, this header file allows us to
+ * define types used by PDocAccessible differently depending on platform.
+ */
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+
+// So that we don't include a bunch of other Windows junk.
+#if !defined(COM_NO_WINDOWS_H)
+#define COM_NO_WINDOWS_H
+#endif // !defined(COM_NO_WINDOWS_H)
+
+// COM headers pull in MSXML which conflicts with our own XMLDocument class.
+// This define excludes those conflicting definitions.
+#if !defined(__XMLDocument_FWD_DEFINED__)
+#define __XMLDocument_FWD_DEFINED__
+#endif // !defined(__XMLDocument_FWD_DEFINED__)
+
+#include "mozilla/a11y/COMPtrTypes.h"
+
+// This define in rpcndr.h messes up our code, so we must undefine it after
+// COMPtrTypes.h has been included.
+#if defined(small)
+#undef small
+#endif // defined(small)
+
+#else
+
+namespace mozilla {
+namespace a11y {
+
+typedef uint32_t IAccessibleHolder;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+#endif // mozilla_a11y_IPCTypes_h
+
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -24,16 +24,20 @@ else:
         LOCAL_INCLUDES += [
             '/accessible/mac',
         ]
     else:
         LOCAL_INCLUDES += [
             '/accessible/other',
         ]
 
+EXPORTS.mozilla.a11y += [
+    'IPCTypes.h',
+]
+
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
 
 if CONFIG['ACCESSIBILITY']:
     EXPORTS.mozilla.a11y += [
         'DocAccessibleChildBase.h',
         'DocAccessibleParent.h',
         'ProxyAccessibleBase.h',
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -4,40 +4,234 @@
  * 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 "DocAccessibleChild.h"
 
 #include "Accessible-inl.h"
 #include "mozilla/a11y/PlatformChild.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "RootAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 static StaticAutoPtr<PlatformChild> sPlatformChild;
 
 DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc)
   : DocAccessibleChildBase(aDoc)
+  , mIsRemoteConstructed(false)
 {
   MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   if (!sPlatformChild) {
     sPlatformChild = new PlatformChild();
     ClearOnShutdown(&sPlatformChild, ShutdownPhase::Shutdown);
   }
 }
 
 DocAccessibleChild::~DocAccessibleChild()
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
-DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy)
+DocAccessibleChild::Shutdown()
+{
+  if (IsConstructedInParentProcess()) {
+    DocAccessibleChildBase::Shutdown();
+    return;
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedShutdown>(this));
+  DetachDocument();
+}
+
+ipc::IPCResult
+DocAccessibleChild::RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy)
+{
+  MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
+  mParentProxy.reset(const_cast<IAccessibleHolder&>(aParentCOMProxy).Release());
+  SetConstructedInParentProcess();
+
+  for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
+    mDeferredEvents[i]->Dispatch();
+  }
+
+  mDeferredEvents.Clear();
+
+  return IPC_OK();
+}
+
+void
+DocAccessibleChild::PushDeferredEvent(UniquePtr<DeferredEvent> aEvent)
+{
+  DocAccessibleChild* topLevelIPCDoc = nullptr;
+
+  if (mDoc && mDoc->IsRoot()) {
+    topLevelIPCDoc = this;
+  } else {
+    auto tabChild = static_cast<dom::TabChild*>(Manager());
+    if (!tabChild) {
+      return;
+    }
+
+    nsTArray<PDocAccessibleChild*> ipcDocAccs;
+    tabChild->ManagedPDocAccessibleChild(ipcDocAccs);
+
+    // Look for the top-level DocAccessibleChild - there will only be one
+    // per TabChild.
+    for (uint32_t i = 0, l = ipcDocAccs.Length(); i < l; ++i) {
+      auto ipcDocAcc = static_cast<DocAccessibleChild*>(ipcDocAccs[i]);
+      if (ipcDocAcc->mDoc && ipcDocAcc->mDoc->IsRoot()) {
+        topLevelIPCDoc = ipcDocAcc;
+        break;
+      }
+    }
+  }
+
+  if (topLevelIPCDoc) {
+    topLevelIPCDoc->mDeferredEvents.AppendElement(Move(aEvent));
+  }
+}
+
+bool
+DocAccessibleChild::SendEvent(const uint64_t& aID, const uint32_t& aType)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendEvent(aID, aType);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedEvent>(this, aID, aType));
+  return false;
+}
+
+void
+DocAccessibleChild::MaybeSendShowEvent(ShowEventData& aData, bool aFromUser)
+{
+  if (IsConstructedInParentProcess()) {
+    Unused << SendShowEvent(aData, aFromUser);
+    return;
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedShow>(this, aData, aFromUser));
+}
+
+bool
+DocAccessibleChild::SendHideEvent(const uint64_t& aRootID,
+                                  const bool& aFromUser)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendHideEvent(aRootID, aFromUser);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedHide>(this, aRootID, aFromUser));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendStateChangeEvent(const uint64_t& aID,
+                                         const uint64_t& aState,
+                                         const bool& aEnabled)
 {
-  IAccessibleHolder parentProxy;
-  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy);
-  mParentProxy.reset(parentProxy.Release());
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendStateChangeEvent(aID, aState, aEnabled);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedStateChange>(this, aID, aState,
+                                                      aEnabled));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
+                                       const int32_t& aOffset)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendCaretMoveEvent(aID, aOffset);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aOffset));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendTextChangeEvent(const uint64_t& aID,
+                                        const nsString& aStr,
+                                        const int32_t& aStart,
+                                        const uint32_t& aLen,
+                                        const bool& aIsInsert,
+                                        const bool& aFromUser)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendTextChangeEvent(aID, aStr, aStart,
+                                                    aLen, aIsInsert, aFromUser);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedTextChange>(this, aID, aStr, aStart,
+                                                     aLen, aIsInsert, aFromUser));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendSelectionEvent(const uint64_t& aID,
+                                       const uint64_t& aWidgetID,
+                                       const uint32_t& aType)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendSelectionEvent(aID, aWidgetID, aType);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedSelection>(this, aID,
+                                                                aWidgetID,
+                                                                aType));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendRoleChangedEvent(const uint32_t& aRole)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendRoleChangedEvent(aRole);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedRoleChanged>(this, aRole));
+  return true;
+}
+
+bool
+DocAccessibleChild::ConstructChildDocInParentProcess(
+                                        DocAccessibleChild* aNewChildDoc,
+                                        uint64_t aUniqueID, uint32_t aMsaaID)
+{
+  if (IsConstructedInParentProcess()) {
+    // We may send the constructor immediately
+    auto tabChild = static_cast<dom::TabChild*>(Manager());
+    MOZ_ASSERT(tabChild);
+    bool result = tabChild->SendPDocAccessibleConstructor(aNewChildDoc, this,
+                                                          aUniqueID, aMsaaID,
+                                                          IAccessibleHolder());
+    if (result) {
+      aNewChildDoc->SetConstructedInParentProcess();
+    }
+    return result;
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedChildDocConstructor>(aNewChildDoc, this,
+                                                              aUniqueID, aMsaaID));
+  return true;
+}
+
+bool
+DocAccessibleChild::SendBindChildDoc(DocAccessibleChild* aChildDoc,
+                                     const uint64_t& aNewParentID)
+{
+  if (IsConstructedInParentProcess()) {
+    return DocAccessibleChildBase::SendBindChildDoc(aChildDoc, aNewParentID);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedBindChildDoc>(this, aChildDoc,
+                                                       aNewParentID));
+  return true;
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -4,34 +4,315 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_DocAccessibleChild_h
 #define mozilla_a11y_DocAccessibleChild_h
 
 #include "mozilla/a11y/COMPtrTypes.h"
 #include "mozilla/a11y/DocAccessibleChildBase.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/mscom/Ptr.h"
 
 namespace mozilla {
 namespace a11y {
 
 /*
  * These objects handle content side communication for an accessible document,
  * and their lifetime is the same as the document they represent.
  */
 class DocAccessibleChild : public DocAccessibleChildBase
 {
 public:
   explicit DocAccessibleChild(DocAccessible* aDoc);
   ~DocAccessibleChild();
 
-  void SendCOMProxy(const IAccessibleHolder& aProxy);
+  virtual void Shutdown() override;
+
+  virtual ipc::IPCResult
+  RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy) override;
+
   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 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,
+                                        uint64_t aUniqueID, uint32_t aMsaaID);
+
+  bool SendBindChildDoc(DocAccessibleChild* aChildDoc,
+                        const uint64_t& aNewParentID);
+
+protected:
+  virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser) override;
+
 private:
+  void RemoveDeferredConstructor();
+
+  bool IsConstructedInParentProcess() const { return mIsRemoteConstructed; }
+  void SetConstructedInParentProcess() { mIsRemoteConstructed = true; }
+
+  /**
+   * 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.
+   */
+  struct DeferredEvent
+  {
+    void Dispatch()
+    {
+      Dispatch(mTarget);
+    }
+
+    virtual ~DeferredEvent() {}
+
+  protected:
+    explicit DeferredEvent(DocAccessibleChild* aTarget)
+      : mTarget(aTarget)
+    {}
+
+    virtual void Dispatch(DocAccessibleChild* aIPCDoc) = 0;
+
+  private:
+    DocAccessibleChild* mTarget;
+  };
+
+  void PushDeferredEvent(UniquePtr<DeferredEvent> aEvent);
+
+  struct SerializedShow final : public DeferredEvent
+  {
+    SerializedShow(DocAccessibleChild* aTarget,
+                   ShowEventData& aEventData, bool aFromUser)
+      : DeferredEvent(aTarget)
+      , mEventData(aEventData.ID(), aEventData.Idx(), nsTArray<AccessibleData>())
+      , mFromUser(aFromUser)
+    {
+      // Since IPDL doesn't generate a move constructor for ShowEventData,
+      // we move NewTree manually (ugh). We still construct with an empty
+      // NewTree above so that the compiler catches any changes made to the
+      // ShowEventData structure in IPDL.
+      mEventData.NewTree() = Move(aEventData.NewTree());
+    }
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendShowEvent(mEventData, mFromUser);
+    }
+
+    ShowEventData mEventData;
+    bool          mFromUser;
+  };
+
+  struct SerializedHide final : public DeferredEvent
+  {
+    SerializedHide(DocAccessibleChild* aTarget, uint64_t aRootID, bool aFromUser)
+      : DeferredEvent(aTarget)
+      , mRootID(aRootID)
+      , mFromUser(aFromUser)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendHideEvent(mRootID, mFromUser);
+    }
+
+    uint64_t  mRootID;
+    bool      mFromUser;
+  };
+
+  struct SerializedStateChange final : public DeferredEvent
+  {
+    SerializedStateChange(DocAccessibleChild* aTarget, uint64_t aID,
+                          uint64_t aState, bool aEnabled)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mState(aState)
+      , mEnabled(aEnabled)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendStateChangeEvent(mID, mState, mEnabled);
+    }
+
+    uint64_t  mID;
+    uint64_t  mState;
+    bool      mEnabled;
+  };
+
+  struct SerializedCaretMove final : public DeferredEvent
+  {
+    SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID,
+                        int32_t aOffset)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mOffset(aOffset)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendCaretMoveEvent(mID, mOffset);
+    }
+
+    uint64_t  mID;
+    int32_t   mOffset;
+  };
+
+  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)
+      , mID(aID)
+      , mStr(aStr)
+      , mStart(aStart)
+      , mLen(aLen)
+      , mIsInsert(aIsInsert)
+      , mFromUser(aFromUser)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendTextChangeEvent(mID, mStr, mStart, mLen, mIsInsert,
+                                             mFromUser);
+    }
+
+    uint64_t  mID;
+    nsString  mStr;
+    int32_t   mStart;
+    uint32_t  mLen;
+    bool      mIsInsert;
+    bool      mFromUser;
+  };
+
+  struct SerializedSelection final : public DeferredEvent
+  {
+    SerializedSelection(DocAccessibleChild* aTarget, uint64_t aID,
+                        uint64_t aWidgetID, uint32_t aType)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mWidgetID(aWidgetID)
+      , mType(aType)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendSelectionEvent(mID, mWidgetID, mType);
+    }
+
+    uint64_t  mID;
+    uint64_t  mWidgetID;
+    uint32_t  mType;
+  };
+
+  struct SerializedRoleChanged final : public DeferredEvent
+  {
+    explicit SerializedRoleChanged(DocAccessibleChild* aTarget, uint32_t aRole)
+      : DeferredEvent(aTarget)
+      , mRole(aRole)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendRoleChangedEvent(mRole);
+    }
+
+    uint32_t mRole;
+  };
+
+  struct SerializedEvent final : public DeferredEvent
+  {
+    SerializedEvent(DocAccessibleChild* aTarget, uint64_t aID, uint32_t aType)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mType(aType)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendEvent(mID, mType);
+    }
+
+    uint64_t  mID;
+    uint32_t  mType;
+  };
+
+  struct SerializedChildDocConstructor final : public DeferredEvent
+  {
+    SerializedChildDocConstructor(DocAccessibleChild* aIPCDoc,
+                                  DocAccessibleChild* aParentIPCDoc,
+                                  uint64_t aUniqueID, uint32_t aMsaaID)
+      : DeferredEvent(aParentIPCDoc)
+      , mIPCDoc(aIPCDoc)
+      , mUniqueID(aUniqueID)
+      , mMsaaID(aMsaaID)
+    {}
+
+    void Dispatch(DocAccessibleChild* aParentIPCDoc) override
+    {
+      auto tabChild = static_cast<dom::TabChild*>(aParentIPCDoc->Manager());
+      MOZ_ASSERT(tabChild);
+      Unused << tabChild->SendPDocAccessibleConstructor(mIPCDoc, aParentIPCDoc,
+                                                        mUniqueID, mMsaaID,
+                                                        IAccessibleHolder());
+      mIPCDoc->SetConstructedInParentProcess();
+    }
+
+    DocAccessibleChild* mIPCDoc;
+    uint64_t            mUniqueID;
+    uint32_t            mMsaaID;
+  };
+
+  friend struct SerializedChildDocConstructor;
+
+  struct SerializedBindChildDoc final : public DeferredEvent
+  {
+    SerializedBindChildDoc(DocAccessibleChild* aParentDoc,
+                           DocAccessibleChild* aChildDoc, uint64_t aNewParentID)
+      : DeferredEvent(aParentDoc)
+      , mChildDoc(aChildDoc)
+      , mNewParentID(aNewParentID)
+    {}
+
+    void Dispatch(DocAccessibleChild* aParentIPCDoc) override
+    {
+      Unused << aParentIPCDoc->SendBindChildDoc(mChildDoc, mNewParentID);
+    }
+
+    DocAccessibleChild* mChildDoc;
+    uint64_t            mNewParentID;
+  };
+
+  struct SerializedShutdown final : public DeferredEvent
+  {
+    explicit SerializedShutdown(DocAccessibleChild* aTarget)
+      : DeferredEvent(aTarget)
+    {
+    }
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      aIPCDoc->Shutdown();
+    }
+  };
+
+  bool mIsRemoteConstructed;
   mscom::ProxyUniquePtr<IAccessible> mParentProxy;
+  nsTArray<UniquePtr<DeferredEvent>> mDeferredEvents;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_DocAccessibleChild_h
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -60,19 +60,16 @@ parent:
    * 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)
     returns (IAccessibleHolder aPluginCOMProxy);
 
-  // For now we'll add the command to send the proxy here. This might move to
-  // PDocAccessible constructor in PBrowser.
-  sync COMProxy(IAccessibleHolder aDocCOMProxy)
-    returns(IAccessibleHolder aParentCOMProxy);
+child:
+  async ParentCOMProxy(IAccessibleHolder aParentCOMProxy);
 
-child:
   async __delete__();
 };
 
 }
 }
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -71,16 +71,17 @@ using class mozilla::dom::MessagePort fr
 using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
 using mozilla::EventMessage from "mozilla/EventForwards.h";
 using nsEventStatus from "mozilla/EventForwards.h";
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using nsSizeMode from "nsIWidgetListener.h";
 using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
 using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
 using mozilla::FontRange from "ipc/nsGUIEventIPC.h";
+using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
 
 namespace mozilla {
 namespace dom {
 
 struct NativeKeyBinding
 {
   CommandInt[] singleLineCommands;
   CommandInt[] multiLineCommands;
@@ -135,20 +136,21 @@ both:
     async PRenderFrame();
 
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
      * is a child of. aMsaaID is the MSAA id for this content process, and
-     * is only valid on Windows. Set to 0 on other platforms.
+     * is only valid on Windows. Set to 0 on other platforms. aDocCOMProxy
+     * is also Windows-specific and should be set to 0 on other platforms.
      */
     async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc,
-                         uint32_t aMsaaID);
+                         uint32_t aMsaaID, IAccessibleHolder aDocCOMProxy);
 
     /*
      * Creates a new remoted nsIWidget connection for windowed plugins
      * in e10s mode. This is always initiated from the child in response
      * to windowed plugin creation.
      */
     sync PPluginWidget();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1887,17 +1887,17 @@ TabChild::RecvPasteTransferable(const IP
 
   ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params);
   return IPC_OK();
 }
 
 
 a11y::PDocAccessibleChild*
 TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
-                                   const uint32_t&)
+                                   const uint32_t&, const IAccessibleHolder&)
 {
   MOZ_ASSERT(false, "should never call this!");
   return nullptr;
 }
 
 bool
 TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -431,17 +431,17 @@ public:
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData) override;
 
   virtual mozilla::ipc::IPCResult
   RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) override;
 
   virtual PDocAccessibleChild*
   AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
-                           const uint32_t&) override;
+                           const uint32_t&, const IAccessibleHolder&) override;
 
   virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
 
   virtual PDocumentRendererChild*
   AllocPDocumentRendererChild(const nsRect& aDocumentRect,
                               const gfx::Matrix& aTransform,
                               const nsString& aBggcolor,
                               const uint32_t& aRenderFlags,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -862,17 +862,18 @@ TabParent::SetDocShell(nsIDocShell *aDoc
 {
   NS_ENSURE_ARG(aDocShell);
   NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
   return NS_OK;
 }
 
   a11y::PDocAccessibleParent*
 TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent,
-                                     const uint64_t&, const uint32_t&)
+                                     const uint64_t&, const uint32_t&,
+                                     const IAccessibleHolder&)
 {
 #ifdef ACCESSIBILITY
   return new a11y::DocAccessibleParent();
 #else
   return nullptr;
 #endif
 }
 
@@ -884,32 +885,34 @@ TabParent::DeallocPDocAccessibleParent(P
 #endif
   return true;
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                          PDocAccessibleParent* aParentDoc,
                                          const uint64_t& aParentID,
-                                         const uint32_t& aMsaaID)
+                                         const uint32_t& aMsaaID,
+                                         const IAccessibleHolder& aDocCOMProxy)
 {
 #ifdef ACCESSIBILITY
   auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
   if (aParentDoc) {
     // A document should never directly be the parent of another document.
     // There should always be an outer doc accessible child of the outer
     // document containing the child.
     MOZ_ASSERT(aParentID);
     if (!aParentID) {
       return IPC_FAIL_NO_REASON(this);
     }
 
     auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
     bool added = parentDoc->AddChildDoc(doc, aParentID);
 #ifdef XP_WIN
+    MOZ_ASSERT(aDocCOMProxy.IsNull());
     if (added) {
       a11y::WrapperFor(doc)->SetID(aMsaaID);
     }
 #endif
     if (!added) {
       return IPC_FAIL_NO_REASON(this);
     }
     return IPC_OK();
@@ -921,16 +924,19 @@ TabParent::RecvPDocAccessibleConstructor
     if (aParentID) {
       return IPC_FAIL_NO_REASON(this);
     }
 
     doc->SetTopLevel();
     a11y::DocManager::RemoteDocAdded(doc);
 #ifdef XP_WIN
     a11y::WrapperFor(doc)->SetID(aMsaaID);
+    MOZ_ASSERT(!aDocCOMProxy.IsNull());
+    RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
+    doc->SetCOMProxy(proxy);
 #endif
   }
 #endif
   return IPC_OK();
 }
 
 a11y::DocAccessibleParent*
 TabParent::GetTopLevelDocAccessible() const
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -348,25 +348,26 @@ public:
   DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
 
   virtual PDatePickerParent*
   AllocPDatePickerParent(const nsString& aTitle, const nsString& aInitialDate) override;
   virtual bool DeallocPDatePickerParent(PDatePickerParent* aDatePicker) override;
 
   virtual PDocAccessibleParent*
   AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&,
-                            const uint32_t&) override;
+                            const uint32_t&, const IAccessibleHolder&) override;
 
   virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
 
   virtual mozilla::ipc::IPCResult
   RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                 PDocAccessibleParent* aParentDoc,
                                 const uint64_t& aParentID,
-                                const uint32_t& aMsaaID) override;
+                                const uint32_t& aMsaaID,
+                                const IAccessibleHolder& aDocCOMProxy) override;
 
   /**
    * Return the top level doc accessible parent for this tab.
    */
   a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
 
   void LoadURL(nsIURI* aURI);