Merge m-c to autoland. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 22 Aug 2016 09:42:13 -0400
changeset 336346 7fd3ed74db227097b367bf0c683fdad5c98db97f
parent 336345 7239d050d65fbc4dbe4387c59d07cd68649e35c3 (current diff)
parent 336305 194fe275b4e60ded2af6b25173eec421f0dba8ad (diff)
child 336347 1915e2586177ea2e5bf7acb24b2764b85d630337
push id10033
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:50:26 +0000
treeherdermozilla-aurora@5dddbefdf759 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
Merge m-c to autoland. a=merge
accessible/ipc/DocAccessibleChild.cpp
accessible/ipc/DocAccessibleChild.h
accessible/ipc/PDocAccessible.ipdl
accessible/ipc/ProxyAccessible.cpp
accessible/ipc/ProxyAccessible.h
devtools/client/locales/en-US/animationinspector.dtd
--- a/.eslintignore
+++ b/.eslintignore
@@ -108,16 +108,17 @@ devtools/client/webide/**
 devtools/server/*.js
 devtools/server/*.jsm
 !devtools/server/child.js
 !devtools/server/css-logic.js
 !devtools/server/main.js
 !devtools/server/websocket-server.js
 devtools/server/actors/**
 !devtools/server/actors/inspector.js
+!devtools/server/actors/highlighters/css-grid.js
 !devtools/server/actors/highlighters/eye-dropper.js
 !devtools/server/actors/webbrowser.js
 !devtools/server/actors/webextension.js
 !devtools/server/actors/styles.js
 !devtools/server/actors/string.js
 !devtools/server/actors/csscoverage.js
 devtools/server/performance/**
 devtools/server/tests/**
--- a/accessible/atk/moz.build
+++ b/accessible/atk/moz.build
@@ -31,16 +31,17 @@ SOURCES += [
     'UtilInterface.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
+    '/accessible/ipc/other',
     '/accessible/xpcom',
     '/accessible/xul',
     '/other-licenses/atk-1.0',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -510,16 +510,21 @@ 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);
           static_cast<TabChild*>(tabChild.get())->
             SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
+
+#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
@@ -415,16 +415,20 @@ NotificationController::WillRefresh(mozi
 
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
       if (tabChild) {
         static_cast<TabChild*>(tabChild.get())->
           SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
+#if defined(XP_WIN)
+        IAccessibleHolder holder(CreateHolderFromAccessible(childDoc));
+        ipcDoc->SendCOMProxy(holder);
+#endif
       }
     }
   }
 
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
 
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -1,14 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifndef mozilla_a11y_Platform_h
+#define mozilla_a11y_Platform_h
+
 #include <stdint.h>
 
 class nsString;
 
 namespace mozilla {
 namespace a11y {
 
 class ProxyAccessible;
@@ -77,8 +80,9 @@ void ProxyTextChangeEvent(ProxyAccessibl
                           bool aFromUser);
 void ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible* aParent,
                         bool aInsert, bool aFromUser);
 void ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible* aWidget,
                          uint32_t aType);
 } // namespace a11y
 } // namespace mozilla
 
+#endif // mozilla_a11y_Platform_h
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -58,16 +58,28 @@ if CONFIG['A11Y_LOG']:
     UNIFIED_SOURCES += [
         'Logging.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/win',
+    ]
+else:
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/other',
+    ]
+
+LOCAL_INCLUDES += [
     '/accessible/xpcom',
     '/accessible/xul',
     '/dom/base',
     '/dom/xbl',
     '/ipc/chromium/src',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -849,47 +849,47 @@ Accessible::HandleAccEvent(AccEvent* aEv
 
         case nsIAccessibleEvent::EVENT_REORDER:
           // reorder events on the application acc aren't necessary to tell the parent
           // about new top level documents.
           if (!aEvent->GetAccessible()->IsApplication())
             ipcDoc->SendEvent(id, aEvent->GetEventType());
           break;
         case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
-                                                       AccStateChangeEvent* event = downcast_accEvent(aEvent);
-                                                       ipcDoc->SendStateChangeEvent(id, event->GetState(),
-                                                                                    event->IsStateEnabled());
-                                                       break;
-                                                     }
+          AccStateChangeEvent* event = downcast_accEvent(aEvent);
+          ipcDoc->SendStateChangeEvent(id, event->GetState(),
+                                       event->IsStateEnabled());
+          break;
+        }
         case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
           AccCaretMoveEvent* event = downcast_accEvent(aEvent);
           ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset());
           break;
         }
         case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
         case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
           AccTextChangeEvent* event = downcast_accEvent(aEvent);
           ipcDoc->SendTextChangeEvent(id, event->ModifiedText(),
                                       event->GetStartOffset(),
                                       event->GetLength(),
                                       event->IsTextInserted(),
                                       event->IsFromUserInput());
           break;
-                                                     }
+        }
         case nsIAccessibleEvent::EVENT_SELECTION:
         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;
-                                                         }
+        }
         default:
-                                                         ipcDoc->SendEvent(id, aEvent->GetEventType());
+          ipcDoc->SendEvent(id, aEvent->GetEventType());
       }
     }
   }
 
   if (nsCoreUtils::AccEventObserversExist()) {
     nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
   }
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -9,16 +9,17 @@
 #include "mozilla/a11y/AccTypes.h"
 #include "mozilla/a11y/RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "mozilla/a11y/States.h"
 
 #include "mozilla/UniquePtr.h"
 
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 #include "nsRect.h"
 
 struct nsRoleMapEntry;
 
 struct nsRect;
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -572,17 +572,17 @@ protected:
   bool IsLoadEventTarget() const;
 
   /*
    * Set the object responsible for communicating with the main process on
    * behalf of this document.
    */
   void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; }
 
-  friend class DocAccessibleChild;
+  friend class DocAccessibleChildBase;
 
   /**
    * Used to fire scrolling end event after page scroll.
    *
    * @param aTimer    [in] the timer object
    * @param aClosure  [in] the document accessible where scrolling happens
    */
   static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
--- a/accessible/generic/moz.build
+++ b/accessible/generic/moz.build
@@ -23,24 +23,32 @@ UNIFIED_SOURCES += [
     'RootAccessible.cpp',
     'TableCellAccessible.cpp',
     'TextLeafAccessible.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/html',
-    '/accessible/ipc',
     '/accessible/xpcom',
     '/accessible/xul',
     '/dom/base',
     '/layout/generic',
     '/layout/xul',
 ]
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/win',
+    ]
+else:
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/other',
+    ]
+
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     LOCAL_INCLUDES += [
         '/accessible/atk',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     LOCAL_INCLUDES += [
         '/accessible/windows/ia2',
         '/accessible/windows/msaa',
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/a11y/DocAccessibleChildBase.h"
+#include "mozilla/a11y/ProxyAccessible.h"
+
+#include "Accessible-inl.h"
+
+namespace mozilla {
+namespace a11y {
+
+/* static */ uint32_t
+DocAccessibleChildBase::InterfacesFor(Accessible* aAcc)
+{
+  uint32_t interfaces = 0;
+  if (aAcc->IsHyperText() && aAcc->AsHyperText()->IsTextRole())
+    interfaces |= Interfaces::HYPERTEXT;
+
+  if (aAcc->IsLink())
+    interfaces |= Interfaces::HYPERLINK;
+
+  if (aAcc->HasNumericValue())
+    interfaces |= Interfaces::VALUE;
+
+  if (aAcc->IsImage())
+    interfaces |= Interfaces::IMAGE;
+
+  if (aAcc->IsTable()) {
+    interfaces |= Interfaces::TABLE;
+  }
+
+  if (aAcc->IsTableCell())
+    interfaces |= Interfaces::TABLECELL;
+
+  if (aAcc->IsDoc())
+    interfaces |= Interfaces::DOCUMENT;
+
+  if (aAcc->IsSelect()) {
+    interfaces |= Interfaces::SELECTION;
+  }
+
+  if (aAcc->ActionCount()) {
+    interfaces |= Interfaces::ACTION;
+  }
+
+  return interfaces;
+}
+
+/* static */ void
+DocAccessibleChildBase::SerializeTree(Accessible* aRoot,
+                                      nsTArray<AccessibleData>& aTree)
+{
+  uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
+  uint32_t role = aRoot->Role();
+  uint32_t childCount = aRoot->ChildCount();
+  uint32_t interfaces = InterfacesFor(aRoot);
+
+#if defined(XP_WIN)
+  IAccessibleHolder holder(CreateHolderFromAccessible(aRoot));
+#endif
+
+  // OuterDocAccessibles are special because we don't want to serialize the
+  // child doc here, we'll call PDocAccessibleConstructor in
+  // NotificationController.
+  MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
+  if (aRoot->IsOuterDoc()) {
+    childCount = 0;
+  }
+
+#if defined(XP_WIN)
+  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces,
+                                     holder));
+#else
+  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces));
+#endif
+
+  for (uint32_t i = 0; i < childCount; i++) {
+    SerializeTree(aRoot->GetChildAt(i), aTree);
+  }
+}
+
+void
+DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
+{
+  Accessible* parent = aShowEvent->Parent();
+  uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
+  uint32_t idxInParent = aShowEvent->InsertionIndex();
+  nsTArray<AccessibleData> shownTree;
+  ShowEventData data(parentID, idxInParent, shownTree);
+  SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
+  SendShowEvent(data, aShowEvent->IsFromUserInput());
+}
+
+} // namespace a11y
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/DocAccessibleChildBase.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_DocAccessibleChildBase_h
+#define mozilla_a11y_DocAccessibleChildBase_h
+
+#include "mozilla/a11y/DocAccessible.h"
+#include "mozilla/a11y/PDocAccessibleChild.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace a11y {
+
+class Accessible;
+class AccShowEvent;
+
+class DocAccessibleChildBase : public PDocAccessibleChild
+{
+public:
+  explicit DocAccessibleChildBase(DocAccessible* aDoc)
+    : mDoc(aDoc)
+  {
+    MOZ_COUNT_CTOR(DocAccessibleChildBase);
+  }
+
+  ~DocAccessibleChildBase()
+  {
+    // Shutdown() should have been called, but maybe it isn't if the process is
+    // killed?
+    MOZ_ASSERT(!mDoc);
+    if (mDoc) {
+      mDoc->SetIPCDoc(nullptr);
+    }
+
+    MOZ_COUNT_DTOR(DocAccessibleChildBase);
+  }
+
+  void Shutdown()
+  {
+    mDoc->SetIPCDoc(nullptr);
+    mDoc = nullptr;
+    SendShutdown();
+  }
+
+  void ShowEvent(AccShowEvent* aShowEvent);
+
+  virtual void ActorDestroy(ActorDestroyReason) override
+  {
+    if (!mDoc) {
+      return;
+    }
+
+    mDoc->SetIPCDoc(nullptr);
+    mDoc = nullptr;
+  }
+
+protected:
+  static uint32_t InterfacesFor(Accessible* aAcc);
+  static void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree);
+
+  DocAccessible*  mDoc;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_DocAccessibleChildBase_h
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocAccessibleParent.h"
 #include "mozilla/a11y/Platform.h"
-#include "ProxyAccessible.h"
 #include "mozilla/dom/TabParent.h"
 #include "xpcAccessibleDocument.h"
 #include "xpcAccEvents.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 
 namespace mozilla {
 namespace a11y {
@@ -98,19 +97,34 @@ DocAccessibleParent::AddSubtree(ProxyAcc
   }
 
   if (mAccessibles.Contains(newChild.ID())) {
     NS_ERROR("ID already in use");
     return 0;
   }
 
   auto role = static_cast<a11y::role>(newChild.Role());
+
+#if defined(XP_WIN)
+  const IAccessibleHolder& proxyStream = newChild.COMProxy();
+  RefPtr<IAccessible> comPtr(proxyStream.Get());
+  if (!comPtr) {
+    NS_ERROR("Could not obtain remote IAccessible interface");
+    return 0;
+  }
+
+  ProxyAccessible* newProxy =
+    new ProxyAccessible(newChild.ID(), aParent, this, role,
+                        newChild.Interfaces(), comPtr);
+#else
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role,
                         newChild.Interfaces());
+#endif
+
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
   ProxyCreated(newProxy, newChild.Interfaces());
 
   uint32_t accessibles = 1;
   uint32_t kids = newChild.ChildrenCount();
   for (uint32_t i = 0; i < kids; i++) {
     uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
@@ -447,10 +461,36 @@ DocAccessibleParent::CheckDocTree() cons
 xpcAccessibleGeneric*
 DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
 {
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   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.
+ */
+bool
+DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
+                                  IAccessibleHolder* aParentCOMProxy)
+{
+  RefPtr<IAccessible> ptr(aCOMProxy.Get());
+  SetCOMInterface(ptr);
+
+  Accessible* outerDoc = OuterDocOfRemoteBrowser();
+  IAccessible* rawNative = nullptr;
+  if (outerDoc) {
+    outerDoc->GetNativeInterface((void**) &rawNative);
+  }
+
+  aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative));
+  return true;
+}
+#endif // defined(XP_WIN)
+
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -3,18 +3,18 @@
 /* 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_DocAccessibleParent_h
 #define mozilla_a11y_DocAccessibleParent_h
 
 #include "nsAccessibilityService.h"
-#include "ProxyAccessible.h"
 #include "mozilla/a11y/PDocAccessibleParent.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace a11y {
 
 class xpcAccessibleGeneric;
@@ -137,16 +137,21 @@ public:
 
   const ProxyAccessible* GetAccessible(uintptr_t aID) const
     { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
 
   size_t ChildDocCount() const { return mChildDocs.Length(); }
   const DocAccessibleParent* ChildDocAt(size_t aIdx) const
     { return mChildDocs[aIdx]; }
 
+#if defined(XP_WIN)
+  virtual bool RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
+                            IAccessibleHolder* aParentCOMProxy) override;
+#endif
+
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
     explicit ProxyEntry(const void*) : mProxy(nullptr) {}
     ProxyEntry(ProxyEntry&& aOther) :
       mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
rename from accessible/ipc/ProxyAccessible.cpp
rename to accessible/ipc/ProxyAccessibleBase.cpp
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -1,74 +1,78 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ProxyAccessible.h"
-#include "DocAccessibleParent.h"
 #include "DocAccessible.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
 #include "mozilla/a11y/DocManager.h"
+#include "mozilla/a11y/Platform.h"
+#include "mozilla/a11y/ProxyAccessibleBase.h"
+#include "mozilla/a11y/ProxyAccessible.h"
+#include "mozilla/a11y/Role.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
-#include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
-#include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
 namespace mozilla {
 namespace a11y {
 
+template <class Derived>
 void
-ProxyAccessible::Shutdown()
+ProxyAccessibleBase<Derived>::Shutdown()
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
   NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
   xpcAccessibleDocument* xpcDoc =
     GetAccService()->GetCachedXPCDocument(Document());
   if (xpcDoc) {
-    xpcDoc->NotifyOfShutdown(this);
+    xpcDoc->NotifyOfShutdown(static_cast<Derived*>(this));
   }
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
   } else {
     if (mChildren.Length() != 1)
       MOZ_CRASH("outer doc doesn't own adoc!");
 
     mChildren[0]->AsDoc()->Unbind();
   }
 
   mChildren.Clear();
-  ProxyDestroyed(this);
-  mDoc->RemoveAccessible(this);
+  ProxyDestroyed(static_cast<Derived*>(this));
+  mDoc->RemoveAccessible(static_cast<Derived*>(this));
 }
 
+template <class Derived>
 void
-ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent)
+ProxyAccessibleBase<Derived>::SetChildDoc(DocAccessibleParent* aParent)
 {
   if (aParent) {
     MOZ_ASSERT(mChildren.IsEmpty());
     mChildren.AppendElement(aParent);
     mOuterDoc = true;
   } else {
     MOZ_ASSERT(mChildren.Length() == 1);
     mChildren.Clear();
     mOuterDoc = false;
   }
 }
 
+template <class Derived>
 bool
-ProxyAccessible::MustPruneChildren() const
+ProxyAccessibleBase<Derived>::MustPruneChildren() const
 {
   // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
   // kept in sync with that.
   if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION
       && mRole != roles::OPTION && mRole != roles::ENTRY
       && mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT
       && mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON
       && mRole != roles::GRAPHIC && mRole != roles::SLIDER
@@ -77,998 +81,51 @@ ProxyAccessible::MustPruneChildren() con
 
   if (mChildren.Length() != 1)
     return false;
 
   return mChildren[0]->Role() == roles::TEXT_LEAF
     || mChildren[0]->Role() == roles::STATICTEXT;
 }
 
-uint64_t
-ProxyAccessible::State() const
-{
-  uint64_t state = 0;
-  Unused << mDoc->SendState(mID, &state);
-  return state;
-}
-
-uint64_t
-ProxyAccessible::NativeState() const
-{
-  uint64_t state = 0;
-  Unused << mDoc->SendNativeState(mID, &state);
-  return state;
-}
-
-void
-ProxyAccessible::Name(nsString& aName) const
-{
-  Unused << mDoc->SendName(mID, &aName);
-}
-
-void
-ProxyAccessible::Value(nsString& aValue) const
-{
-  Unused << mDoc->SendValue(mID, &aValue);
-}
-
-void
-ProxyAccessible::Help(nsString& aHelp) const
-{
-  Unused << mDoc->SendHelp(mID, &aHelp);
-}
-
-void
-ProxyAccessible::Description(nsString& aDesc) const
-{
-  Unused << mDoc->SendDescription(mID, &aDesc);
-}
-
-void
-ProxyAccessible::Attributes(nsTArray<Attribute> *aAttrs) const
-{
-  Unused << mDoc->SendAttributes(mID, aAttrs);
-}
-
-nsTArray<ProxyAccessible*>
-ProxyAccessible::RelationByType(RelationType aType) const
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendRelationByType(mID, static_cast<uint32_t>(aType),
-                                     &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  nsTArray<ProxyAccessible*> targets(targetCount);
-  for (size_t i = 0; i < targetCount; i++)
-    if (ProxyAccessible* proxy = mDoc->GetAccessible(targetIDs[i]))
-      targets.AppendElement(proxy);
-
-  return Move(targets);
-}
-
-void
-ProxyAccessible::Relations(nsTArray<RelationType>* aTypes,
-                           nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets)
-  const
-{
-  nsTArray<RelationTargets> ipcRelations;
-  Unused << mDoc->SendRelations(mID, &ipcRelations);
-
-  size_t relationCount = ipcRelations.Length();
-  aTypes->SetCapacity(relationCount);
-  aTargetSets->SetCapacity(relationCount);
-  for (size_t i = 0; i < relationCount; i++) {
-    uint32_t type = ipcRelations[i].Type();
-    if (type > static_cast<uint32_t>(RelationType::LAST))
-      continue;
-
-    size_t targetCount = ipcRelations[i].Targets().Length();
-    nsTArray<ProxyAccessible*> targets(targetCount);
-    for (size_t j = 0; j < targetCount; j++)
-      if (ProxyAccessible* proxy = mDoc->GetAccessible(ipcRelations[i].Targets()[j]))
-        targets.AppendElement(proxy);
-
-    if (targets.IsEmpty())
-      continue;
-
-    aTargetSets->AppendElement(Move(targets));
-    aTypes->AppendElement(static_cast<RelationType>(type));
-  }
-}
-
-bool
-ProxyAccessible::IsSearchbox() const
-{
-  bool retVal = false;
-  Unused << mDoc->SendIsSearchbox(mID, &retVal);
-  return retVal;
-}
-
-nsIAtom*
-ProxyAccessible::LandmarkRole() const
-{
-  nsString landmark;
-  Unused << mDoc->SendLandmarkRole(mID, &landmark);
-  return NS_GetStaticAtom(landmark);
-}
-
-nsIAtom*
-ProxyAccessible::ARIARoleAtom() const
-{
-  nsString role;
-  Unused << mDoc->SendARIARoleAtom(mID, &role);
-  return NS_GetStaticAtom(role);
-}
-
-int32_t
-ProxyAccessible::GetLevelInternal()
-{
-  int32_t level = 0;
-  Unused << mDoc->SendGetLevelInternal(mID, &level);
-  return level;
-}
-
-void
-ProxyAccessible::ScrollTo(uint32_t aScrollType)
-{
-  Unused << mDoc->SendScrollTo(mID, aScrollType);
-}
-
-void
-ProxyAccessible::ScrollToPoint(uint32_t aScrollType, int32_t aX, int32_t aY)
-{
-  Unused << mDoc->SendScrollToPoint(mID, aScrollType, aX, aY);
-}
-
-int32_t
-ProxyAccessible::CaretLineNumber()
-{
-  int32_t line = -1;
-  Unused << mDoc->SendCaretOffset(mID, &line);
-  return line;
-}
-
-int32_t
-ProxyAccessible::CaretOffset()
-{
-  int32_t offset = 0;
-  Unused << mDoc->SendCaretOffset(mID, &offset);
-  return offset;
-}
-
-void
-ProxyAccessible::SetCaretOffset(int32_t aOffset)
-{
-  Unused << mDoc->SendSetCaretOffset(mID, aOffset);
-}
-
-int32_t
-ProxyAccessible::CharacterCount()
-{
-  int32_t count = 0;
-  Unused << mDoc->SendCharacterCount(mID, &count);
-  return count;
-}
-
-int32_t
-ProxyAccessible::SelectionCount()
-{
-  int32_t count = 0;
-  Unused << mDoc->SendSelectionCount(mID, &count);
-  return count;
-}
-
-bool
-ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
-                               nsString& aText) const
-{
-  bool valid;
-  Unused << mDoc->SendTextSubstring(mID, aStartOffset, aEndOfset, &aText, &valid);
-  return valid;
-}
-
-void
-ProxyAccessible::GetTextAfterOffset(int32_t aOffset,
-                                    AccessibleTextBoundary aBoundaryType,
-                                    nsString& aText, int32_t* aStartOffset,
-                                    int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextAfterOffset(mID, aOffset, aBoundaryType,
-                                         &aText, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::GetTextAtOffset(int32_t aOffset,
-                                 AccessibleTextBoundary aBoundaryType,
-                                 nsString& aText, int32_t* aStartOffset,
-                                 int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextAtOffset(mID, aOffset, aBoundaryType,
-                                      &aText, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::GetTextBeforeOffset(int32_t aOffset,
-                                     AccessibleTextBoundary aBoundaryType,
-                                     nsString& aText, int32_t* aStartOffset,
-                                     int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextBeforeOffset(mID, aOffset, aBoundaryType,
-                                          &aText, aStartOffset, aEndOffset);
-}
-
-char16_t
-ProxyAccessible::CharAt(int32_t aOffset)
-{
-  uint16_t retval = 0;
-  Unused << mDoc->SendCharAt(mID, aOffset, &retval);
-  return static_cast<char16_t>(retval);
-}
-
-void
-ProxyAccessible::TextAttributes(bool aIncludeDefAttrs,
-                                int32_t aOffset,
-                                nsTArray<Attribute>* aAttributes,
-                                int32_t* aStartOffset,
-                                int32_t* aEndOffset)
-{
-  Unused << mDoc->SendTextAttributes(mID, aIncludeDefAttrs, aOffset,
-                                     aAttributes, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::DefaultTextAttributes(nsTArray<Attribute>* aAttrs)
-{
-  Unused << mDoc->SendDefaultTextAttributes(mID, aAttrs);
-}
-
-nsIntRect
-ProxyAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
-                            uint32_t aCoordType)
-{
-  nsIntRect rect;
-  Unused <<
-    mDoc->SendTextBounds(mID, aStartOffset, aEndOffset, aCoordType, &rect);
-  return rect;
-}
-
-nsIntRect
-ProxyAccessible::CharBounds(int32_t aOffset, uint32_t aCoordType)
-{
-  nsIntRect rect;
-  Unused <<
-    mDoc->SendCharBounds(mID, aOffset, aCoordType, &rect);
-  return rect;
-}
-
-int32_t
-ProxyAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
-{
-  int32_t retVal = -1;
-  Unused << mDoc->SendOffsetAtPoint(mID, aX, aY, aCoordType, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::SelectionBoundsAt(int32_t aSelectionNum,
-                                   nsString& aData,
-                                   int32_t* aStartOffset,
-                                   int32_t* aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendSelectionBoundsAt(mID, aSelectionNum, &retVal, &aData,
-                                        aStartOffset, aEndOffset);
-  return retVal;
-}
-
-bool
-ProxyAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
-                                      int32_t aStartOffset,
-                                      int32_t aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendSetSelectionBoundsAt(mID, aSelectionNum, aStartOffset,
-                                           aEndOffset, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::AddToSelection(int32_t aStartOffset,
-                                int32_t aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendAddToSelection(mID, aStartOffset, aEndOffset, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::RemoveFromSelection(int32_t aSelectionNum)
-{
-  bool retVal = false;
-  Unused << mDoc->SendRemoveFromSelection(mID, aSelectionNum, &retVal);
-  return retVal;
-}
-
-void
-ProxyAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
-                                   uint32_t aScrollType)
-{
-  Unused << mDoc->SendScrollSubstringTo(mID, aStartOffset, aEndOffset, aScrollType);
-}
-
-void
-ProxyAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
-                                        int32_t aEndOffset,
-                                        uint32_t aCoordinateType,
-                                        int32_t aX, int32_t aY)
-{
-  Unused << mDoc->SendScrollSubstringToPoint(mID, aStartOffset, aEndOffset,
-                                             aCoordinateType, aX, aY);
-}
-
-void
-ProxyAccessible::Text(nsString* aText)
-{
-  Unused << mDoc->SendText(mID, aText);
-}
-
-void
-ProxyAccessible::ReplaceText(const nsString& aText)
-{
-  Unused << mDoc->SendReplaceText(mID, aText);
-}
-
-bool
-ProxyAccessible::InsertText(const nsString& aText, int32_t aPosition)
-{
-  bool valid;
-  Unused << mDoc->SendInsertText(mID, aText, aPosition, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::CopyText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendCopyText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::CutText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendCutText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::DeleteText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendDeleteText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::PasteText(int32_t aPosition)
-{
-  bool valid;
-  Unused << mDoc->SendPasteText(mID, aPosition, &valid);
-  return valid;
-}
-
-nsIntPoint
-ProxyAccessible::ImagePosition(uint32_t aCoordType)
-{
-  nsIntPoint retVal;
-  Unused << mDoc->SendImagePosition(mID, aCoordType, &retVal);
-  return retVal;
-}
-
-nsIntSize
-ProxyAccessible::ImageSize()
-{
-  nsIntSize retVal;
-  Unused << mDoc->SendImageSize(mID, &retVal);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::StartOffset(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendStartOffset(mID, &retVal, aOk);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::EndOffset(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendEndOffset(mID, &retVal, aOk);
-  return retVal;
-}
-
-bool
-ProxyAccessible::IsLinkValid()
-{
-  bool retVal = false;
-  Unused << mDoc->SendIsLinkValid(mID, &retVal);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::AnchorCount(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendAnchorCount(mID, &retVal, aOk);
-  return retVal;
-}
-
-void
-ProxyAccessible::AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk)
-{
-  Unused << mDoc->SendAnchorURIAt(mID, aIndex, &aURI, aOk);
-}
-
-ProxyAccessible*
-ProxyAccessible::AnchorAt(uint32_t aIndex)
-{
-  uint64_t id = 0;
-  bool ok = false;
-  Unused << mDoc->SendAnchorAt(mID, aIndex, &id, &ok);
-  return ok ? mDoc->GetAccessible(id) : nullptr;
-}
-
+template <class Derived>
 uint32_t
-ProxyAccessible::LinkCount()
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendLinkCount(mID, &retVal);
-  return retVal;
-}
-
-ProxyAccessible*
-ProxyAccessible::LinkAt(const uint32_t& aIndex)
-{
-  uint64_t linkID = 0;
-  bool ok = false;
-  Unused << mDoc->SendLinkAt(mID, aIndex, &linkID, &ok);
-  return ok ? mDoc->GetAccessible(linkID) : nullptr;
-}
-
-int32_t
-ProxyAccessible::LinkIndexOf(ProxyAccessible* aLink)
-{
-  int32_t retVal = -1;
-  if (aLink) {
-    Unused << mDoc->SendLinkIndexOf(mID, aLink->ID(), &retVal);
-  }
-
-  return retVal;
-}
-
-int32_t
-ProxyAccessible::LinkIndexAtOffset(uint32_t aOffset)
-{
-  int32_t retVal = -1;
-  Unused << mDoc->SendLinkIndexAtOffset(mID, aOffset, &retVal);
-  return retVal;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableOfACell()
-{
-  uint64_t tableID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableOfACell(mID, &tableID, &ok);
-  return ok ? mDoc->GetAccessible(tableID) : nullptr;
-}
-
-uint32_t
-ProxyAccessible::ColIdx()
-{
-  uint32_t index = 0;
-  Unused << mDoc->SendColIdx(mID, &index);
-  return index;
-}
-
-uint32_t
-ProxyAccessible::RowIdx()
-{
-  uint32_t index = 0;
-  Unused << mDoc->SendRowIdx(mID, &index);
-  return index;
-}
-
-uint32_t
-ProxyAccessible::ColExtent()
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendColExtent(mID, &extent);
-  return extent;
-}
-
-uint32_t
-ProxyAccessible::RowExtent()
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendRowExtent(mID, &extent);
-  return extent;
-}
-
-void
-ProxyAccessible::ColHeaderCells(nsTArray<ProxyAccessible*>* aCells)
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendColHeaderCells(mID, &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  for (size_t i = 0; i < targetCount; i++) {
-    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
-  }
-}
-
-void
-ProxyAccessible::RowHeaderCells(nsTArray<ProxyAccessible*>* aCells)
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendRowHeaderCells(mID, &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  for (size_t i = 0; i < targetCount; i++) {
-    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
-  }
-}
-
-bool
-ProxyAccessible::IsCellSelected()
-{
-  bool selected = false;
-  Unused << mDoc->SendIsCellSelected(mID, &selected);
-  return selected;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableCaption()
-{
-  uint64_t captionID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableCaption(mID, &captionID, &ok);
-  return ok ? mDoc->GetAccessible(captionID) : nullptr;
-}
-
-void
-ProxyAccessible::TableSummary(nsString& aSummary)
-{
-  Unused << mDoc->SendTableSummary(mID, &aSummary);
-}
-
-uint32_t
-ProxyAccessible::TableColumnCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableColumnCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableRowCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableRowCount(mID, &count);
-  return count;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableCellAt(uint32_t aRow, uint32_t aCol)
-{
-  uint64_t cellID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableCellAt(mID, aRow, aCol, &cellID, &ok);
-  return ok ? mDoc->GetAccessible(cellID) : nullptr;
-}
-
-int32_t
-ProxyAccessible::TableCellIndexAt(uint32_t aRow, uint32_t aCol)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableCellIndexAt(mID, aRow, aCol, &index);
-  return index;
-}
-
-int32_t
-ProxyAccessible::TableColumnIndexAt(uint32_t aCellIndex)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableColumnIndexAt(mID, aCellIndex, &index);
-  return index;
-}
-
-int32_t
-ProxyAccessible::TableRowIndexAt(uint32_t aCellIndex)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableRowIndexAt(mID, aCellIndex, &index);
-  return index;
-}
-
-void
-ProxyAccessible::TableRowAndColumnIndicesAt(uint32_t aCellIndex,
-                                            int32_t* aRow, int32_t* aCol)
-{
-  Unused << mDoc->SendTableRowAndColumnIndicesAt(mID, aCellIndex, aRow, aCol);
-}
-
-uint32_t
-ProxyAccessible::TableColumnExtentAt(uint32_t aRow, uint32_t aCol)
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendTableColumnExtentAt(mID, aRow, aCol, &extent);
-  return extent;
-}
-
-uint32_t
-ProxyAccessible::TableRowExtentAt(uint32_t aRow, uint32_t aCol)
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendTableRowExtentAt(mID, aRow, aCol, &extent);
-  return extent;
-}
-
-void
-ProxyAccessible::TableColumnDescription(uint32_t aCol, nsString& aDescription)
-{
-  Unused << mDoc->SendTableColumnDescription(mID, aCol, &aDescription);
-}
-
-void
-ProxyAccessible::TableRowDescription(uint32_t aRow, nsString& aDescription)
-{
-  Unused << mDoc->SendTableRowDescription(mID, aRow, &aDescription);
-}
-
-bool
-ProxyAccessible::TableColumnSelected(uint32_t aCol)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableColumnSelected(mID, aCol, &selected);
-  return selected;
-}
-
-bool
-ProxyAccessible::TableRowSelected(uint32_t aRow)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableRowSelected(mID, aRow, &selected);
-  return selected;
-}
-
-bool
-ProxyAccessible::TableCellSelected(uint32_t aRow, uint32_t aCol)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableCellSelected(mID, aRow, aCol, &selected);
-  return selected;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedCellCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedCellCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedColumnCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedColumnCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedRowCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedRowCount(mID, &count);
-  return count;
-}
-
-void
-ProxyAccessible::TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs)
-{
-  AutoTArray<uint64_t, 30> cellIDs;
-  Unused << mDoc->SendTableSelectedCells(mID, &cellIDs);
-  aCellIDs->SetCapacity(cellIDs.Length());
-  for (uint32_t i = 0; i < cellIDs.Length(); ++i) {
-    aCellIDs->AppendElement(mDoc->GetAccessible(cellIDs[i]));
-  }
-}
-
-void
-ProxyAccessible::TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices)
-{
-  Unused << mDoc->SendTableSelectedCellIndices(mID, aCellIndices);
-}
-
-void
-ProxyAccessible::TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices)
-{
-  Unused << mDoc->SendTableSelectedColumnIndices(mID, aColumnIndices);
-}
-
-void
-ProxyAccessible::TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices)
-{
-  Unused << mDoc->SendTableSelectedRowIndices(mID, aRowIndices);
-}
-
-void
-ProxyAccessible::TableSelectColumn(uint32_t aCol)
-{
-  Unused << mDoc->SendTableSelectColumn(mID, aCol);
-}
-
-void
-ProxyAccessible::TableSelectRow(uint32_t aRow)
-{
-  Unused << mDoc->SendTableSelectRow(mID, aRow);
-}
-
-void
-ProxyAccessible::TableUnselectColumn(uint32_t aCol)
-{
-  Unused << mDoc->SendTableUnselectColumn(mID, aCol);
-}
-
-void
-ProxyAccessible::TableUnselectRow(uint32_t aRow)
-{
-  Unused << mDoc->SendTableUnselectRow(mID, aRow);
-}
-
-bool
-ProxyAccessible::TableIsProbablyForLayout()
-{
-  bool forLayout = false;
-  Unused << mDoc->SendTableIsProbablyForLayout(mID, &forLayout);
-  return forLayout;
-}
-
-ProxyAccessible*
-ProxyAccessible::AtkTableColumnHeader(int32_t aCol)
-{
-  uint64_t headerID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAtkTableColumnHeader(mID, aCol, &headerID, &ok);
-  return ok ? mDoc->GetAccessible(headerID) : nullptr;
-}
-
-ProxyAccessible*
-ProxyAccessible::AtkTableRowHeader(int32_t aRow)
-{
-  uint64_t headerID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAtkTableRowHeader(mID, aRow, &headerID, &ok);
-  return ok ? mDoc->GetAccessible(headerID) : nullptr;
-}
-
-void
-ProxyAccessible::SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems)
-{
-  AutoTArray<uint64_t, 10> itemIDs;
-  Unused << mDoc->SendSelectedItems(mID, &itemIDs);
-  aSelectedItems->SetCapacity(itemIDs.Length());
-  for (size_t i = 0; i < itemIDs.Length(); ++i) {
-    aSelectedItems->AppendElement(mDoc->GetAccessible(itemIDs[i]));
-  }
-}
-
-uint32_t
-ProxyAccessible::SelectedItemCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendSelectedItemCount(mID, &count);
-  return count;
-}
-
-ProxyAccessible*
-ProxyAccessible::GetSelectedItem(uint32_t aIndex)
-{
-  uint64_t selectedItemID = 0;
-  bool ok = false;
-  Unused << mDoc->SendGetSelectedItem(mID, aIndex, &selectedItemID, &ok);
-  return ok ? mDoc->GetAccessible(selectedItemID) : nullptr;
-}
-
-bool
-ProxyAccessible::IsItemSelected(uint32_t aIndex)
-{
-  bool selected = false;
-  Unused << mDoc->SendIsItemSelected(mID, aIndex, &selected);
-  return selected;
-}
- 
-bool
-ProxyAccessible::AddItemToSelection(uint32_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendAddItemToSelection(mID, aIndex, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::RemoveItemFromSelection(uint32_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendRemoveItemFromSelection(mID, aIndex, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::SelectAll()
-{
-  bool success = false;
-  Unused << mDoc->SendSelectAll(mID, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::UnselectAll()
-{
-  bool success = false;
-  Unused << mDoc->SendUnselectAll(mID, &success);
-  return success;
-}
-
-void
-ProxyAccessible::TakeSelection()
-{
-  Unused << mDoc->SendTakeSelection(mID);
-}
-
-void
-ProxyAccessible::SetSelected(bool aSelect)
-{
-  Unused << mDoc->SendSetSelected(mID, aSelect);
-}
-
-bool
-ProxyAccessible::DoAction(uint8_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendDoAction(mID, aIndex, &success);
-  return success;
-}
-
-uint8_t
-ProxyAccessible::ActionCount()
-{
-  uint8_t count = 0;
-  Unused << mDoc->SendActionCount(mID, &count);
-  return count;
-}
-
-void
-ProxyAccessible::ActionDescriptionAt(uint8_t aIndex, nsString& aDescription)
-{
-  Unused << mDoc->SendActionDescriptionAt(mID, aIndex, &aDescription);
-}
-
-void
-ProxyAccessible::ActionNameAt(uint8_t aIndex, nsString& aName)
-{
-  Unused << mDoc->SendActionNameAt(mID, aIndex, &aName);
-}
-
-KeyBinding
-ProxyAccessible::AccessKey()
-{
-  uint32_t key = 0;
-  uint32_t modifierMask = 0;
-  Unused << mDoc->SendAccessKey(mID, &key, &modifierMask);
-  return KeyBinding(key, modifierMask);
-}
-
-KeyBinding
-ProxyAccessible::KeyboardShortcut()
-{
-  uint32_t key = 0;
-  uint32_t modifierMask = 0;
-  Unused << mDoc->SendKeyboardShortcut(mID, &key, &modifierMask);
-  return KeyBinding(key, modifierMask);
-}
-
-void
-ProxyAccessible::AtkKeyBinding(nsString& aBinding)
-{
-  Unused << mDoc->SendAtkKeyBinding(mID, &aBinding);
-}
-
-double
-ProxyAccessible::CurValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendCurValue(mID, &val);
-  return val;
-}
-
-bool
-ProxyAccessible::SetCurValue(double aValue)
-{
-  bool success = false;
-  Unused << mDoc->SendSetCurValue(mID, aValue, &success);
-  return success;
-}
-
-double
-ProxyAccessible::MinValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendMinValue(mID, &val);
-  return val;
-}
-
-double
-ProxyAccessible::MaxValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendMaxValue(mID, &val);
-  return val;
-}
-
-double
-ProxyAccessible::Step()
-{
-  double step = UnspecifiedNaN<double>();
-  Unused << mDoc->SendStep(mID, &step);
-  return step;
-}
-
-void
-ProxyAccessible::TakeFocus()
-{
-  Unused << mDoc->SendTakeFocus(mID);
-}
-
-uint32_t
-ProxyAccessible::EmbeddedChildCount() const
+ProxyAccessibleBase<Derived>::EmbeddedChildCount() const
 {
   size_t count = 0, kids = mChildren.Length();
   for (size_t i = 0; i < kids; i++) {
     if (mChildren[i]->IsEmbeddedObject()) {
       count++;
     }
   }
 
   return count;
 }
 
+template <class Derived>
 int32_t
-ProxyAccessible::IndexOfEmbeddedChild(const ProxyAccessible* aChild)
+ProxyAccessibleBase<Derived>::IndexOfEmbeddedChild(const Derived* aChild)
 {
   size_t index = 0, kids = mChildren.Length();
   for (size_t i = 0; i < kids; i++) {
     if (mChildren[i]->IsEmbeddedObject()) {
       if (mChildren[i] == aChild) {
         return index;
       }
 
       index++;
     }
   }
 
   return -1;
 }
 
-ProxyAccessible*
-ProxyAccessible::EmbeddedChildAt(size_t aChildIdx)
+template <class Derived>
+Derived*
+ProxyAccessibleBase<Derived>::EmbeddedChildAt(size_t aChildIdx)
 {
   size_t index = 0, kids = mChildren.Length();
   for (size_t i = 0; i < kids; i++) {
     if (!mChildren[i]->IsEmbeddedObject()) {
       continue;
     }
 
     if (index == aChildIdx) {
@@ -1076,117 +133,27 @@ ProxyAccessible::EmbeddedChildAt(size_t 
     }
 
     index++;
   }
 
   return nullptr;
 }
 
-ProxyAccessible*
-ProxyAccessible::FocusedChild()
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused << mDoc->SendFocusedChild(mID, &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
-}
-
-ProxyAccessible*
-ProxyAccessible::ChildAtPoint(int32_t aX, int32_t aY,
-                              Accessible::EWhichChildAtPoint aWhichChild)
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAccessibleAtPoint(mID, aX, aY, false,
-                                        static_cast<uint32_t>(aWhichChild),
-                                        &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
-}
-
-nsIntRect
-ProxyAccessible::Bounds()
-{
-  nsIntRect rect;
-  Unused << mDoc->SendExtents(mID, false,
-                              &(rect.x), &(rect.y),
-                              &(rect.width), &(rect.height));
-  return rect;
-}
-
-void
-ProxyAccessible::Language(nsString& aLocale)
-{
-  Unused << mDoc->SendLanguage(mID, &aLocale);
-}
-
-void
-ProxyAccessible::DocType(nsString& aType)
-{
-  Unused << mDoc->SendDocType(mID, &aType);
-}
-
-void
-ProxyAccessible::Title(nsString& aTitle)
-{
-  Unused << mDoc->SendTitle(mID, &aTitle);
-}
-
-void
-ProxyAccessible::URL(nsString& aURL)
-{
-  Unused << mDoc->SendURL(mID, &aURL);
-}
-
-void
-ProxyAccessible::MimeType(nsString aMime)
-{
-  Unused << mDoc->SendMimeType(mID, &aMime);
-}
-
-void
-ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
-                                    nsString& aMimeType)
-{
-  Unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType);
-}
-
-ProxyAccessible*
-ProxyAccessible::AccessibleAtPoint(int32_t aX, int32_t aY,
-                                   bool aNeedsScreenCoords)
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused <<
-    mDoc->SendAccessibleAtPoint(mID, aX, aY, aNeedsScreenCoords,
-                                static_cast<uint32_t>(Accessible::eDirectChild),
-                                &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
-}
-
-void
-ProxyAccessible::Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
-                        int32_t* aWidth, int32_t* aHeight)
-{
-  Unused << mDoc->SendExtents(mID, aNeedsScreenCoords, aX, aY, aWidth, aHeight);
-}
-
-void
-ProxyAccessible::DOMNodeID(nsString& aID)
-{
-  Unused << mDoc->SendDOMNodeID(mID, &aID);
-}
-
+template <class Derived>
 Accessible*
-ProxyAccessible::OuterDocOfRemoteBrowser() const
+ProxyAccessibleBase<Derived>::OuterDocOfRemoteBrowser() const
 {
   auto tab = static_cast<dom::TabParent*>(mDoc->Manager());
   dom::Element* frame = tab->GetOwnerElement();
   NS_ASSERTION(frame, "why isn't the tab in a frame!");
   if (!frame)
     return nullptr;
 
   DocAccessible* chromeDoc = GetExistingDocAccessible(frame->OwnerDoc());
 
   return chromeDoc ? chromeDoc->GetAccessible(frame) : nullptr;
 }
-}
-}
+
+template class ProxyAccessibleBase<ProxyAccessible>;
+
+} // namespace a11y
+} // namespace mozilla
rename from accessible/ipc/ProxyAccessible.h
rename to accessible/ipc/ProxyAccessibleBase.h
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessibleBase.h
@@ -1,109 +1,100 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_a11y_ProxyAccessible_h
-#define mozilla_a11y_ProxyAccessible_h
+#ifndef mozilla_a11y_ProxyAccessibleBase_h
+#define mozilla_a11y_ProxyAccessibleBase_h
 
 #include "mozilla/a11y/Role.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleTypes.h"
 #include "Accessible.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsRect.h"
 #include "Accessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 class Accessible;
 class Attribute;
 class DocAccessibleParent;
+class ProxyAccessible;
 enum class RelationType;
 
 enum Interfaces
 {
   HYPERTEXT = 1,
   HYPERLINK = 1 << 1,
   IMAGE = 1 << 2,
   VALUE = 1 << 3,
   TABLE = 1 << 4,
   TABLECELL = 1 << 5,
   DOCUMENT = 1 << 6,
   SELECTION = 1 << 7,
   ACTION = 1 << 8,
 };
 
-class ProxyAccessible
+template <class Derived>
+class ProxyAccessibleBase
 {
 public:
-
-  ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
-                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) :
-     mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
-     mOuterDoc(false), mIsDoc(false),
-     mHasValue(aInterfaces & Interfaces::VALUE),
-     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK),
-     mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
+  ~ProxyAccessibleBase()
   {
-    MOZ_COUNT_CTOR(ProxyAccessible);
-  }
-  ~ProxyAccessible()
-  {
-    MOZ_COUNT_DTOR(ProxyAccessible);
     MOZ_ASSERT(!mWrapper);
   }
 
-  void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
+  void AddChildAt(uint32_t aIdx, Derived* aChild)
   { mChildren.InsertElementAt(aIdx, aChild); }
 
   uint32_t ChildrenCount() const { return mChildren.Length(); }
-  ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
-  ProxyAccessible* FirstChild() const
+  Derived* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
+  Derived* FirstChild() const
     { return mChildren.Length() ? mChildren[0] : nullptr; }
-  ProxyAccessible* LastChild() const
+  Derived* LastChild() const
     { return mChildren.Length() ? mChildren[mChildren.Length() - 1] : nullptr; }
-  ProxyAccessible* PrevSibling() const
+  Derived* PrevSibling() const
   {
     size_t idx = IndexInParent();
     return idx > 0 ? Parent()->mChildren[idx - 1] : nullptr;
   }
-  ProxyAccessible* NextSibling() const
+  Derived* NextSibling() const
   {
     size_t idx = IndexInParent();
     return idx + 1 < Parent()->mChildren.Length() ? Parent()->mChildren[idx + 1]
     : nullptr;
   }
 
   // XXX evaluate if this is fast enough.
-  size_t IndexInParent() const { return Parent()->mChildren.IndexOf(this); }
+  size_t IndexInParent() const { return
+    Parent()->mChildren.IndexOf(static_cast<const Derived*>(this)); }
   uint32_t EmbeddedChildCount() const;
-  int32_t IndexOfEmbeddedChild(const ProxyAccessible*);
-  ProxyAccessible* EmbeddedChildAt(size_t aChildIdx);
+  int32_t IndexOfEmbeddedChild(const Derived* aChild);
+  Derived* EmbeddedChildAt(size_t aChildIdx);
   bool MustPruneChildren() const;
 
   void Shutdown();
 
   void SetChildDoc(DocAccessibleParent*);
 
   /**
    * Remove The given child.
    */
-  void RemoveChild(ProxyAccessible* aChild)
+  void RemoveChild(Derived* aChild)
     { mChildren.RemoveElement(aChild); }
 
   /**
    * Return the proxy for the parent of the wrapped accessible.
    */
-  ProxyAccessible* Parent() const { return mParent; }
+  Derived* Parent() const { return mParent; }
 
   Accessible* OuterDocOfRemoteBrowser() const;
 
   /**
    * Get the role of the accessible we're proxying.
    */
   role Role() const { return mRole; }
 
@@ -113,294 +104,16 @@ public:
   bool IsEmbeddedObject() const
   {
     role role = Role();
     return role != roles::TEXT_LEAF &&
            role != roles::WHITESPACE &&
            role != roles::STATICTEXT;
   }
 
-  /*
-   * Return the states for the proxied accessible.
-   */
-  uint64_t State() const;
-
-  /*
-   * Return the native states for the proxied accessible.
-   */
-  uint64_t NativeState() const;
-
-  /*
-   * Set aName to the name of the proxied accessible.
-   */
-  void Name(nsString& aName) const;
-
-  /*
-   * Set aValue to the value of the proxied accessible.
-   */
-  void Value(nsString& aValue) const;
-
-  /*
-   * Set aHelp to the help string of the proxied accessible.
-   */
-  void Help(nsString& aHelp) const;
-
-  /**
-   * Set aDesc to the description of the proxied accessible.
-   */
-  void Description(nsString& aDesc) const;
-
-  /**
-   * Get the set of attributes on the proxied accessible.
-   */
-  void Attributes(nsTArray<Attribute> *aAttrs) const;
-
-  /**
-   * Return set of targets of given relation type.
-   */
-  nsTArray<ProxyAccessible*> RelationByType(RelationType aType) const;
-
-  /**
-   * Get all relations for this accessible.
-   */
-  void Relations(nsTArray<RelationType>* aTypes,
-                 nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets) const;
-
-  bool IsSearchbox() const;
-
-  nsIAtom* LandmarkRole() const;
-
-  nsIAtom* ARIARoleAtom() const;
-
-  int32_t GetLevelInternal();
-  void ScrollTo(uint32_t aScrollType);
-  void ScrollToPoint(uint32_t aScrollType, int32_t aX, int32_t aY);
-
-  int32_t CaretLineNumber();
-  int32_t CaretOffset();
-  void SetCaretOffset(int32_t aOffset);
-
-  int32_t CharacterCount();
-  int32_t SelectionCount();
-
-  /**
-   * Get the text between the given offsets.
-   */
-  bool TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
-                     nsString& aText) const;
-
-  void GetTextAfterOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                          nsString& aText, int32_t* aStartOffset,
-                          int32_t* aEndOffset);
-
-  void GetTextAtOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                       nsString& aText, int32_t* aStartOffset,
-                       int32_t* aEndOffset);
-
-  void GetTextBeforeOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                           nsString& aText, int32_t* aStartOffset,
-                           int32_t* aEndOffset);
-
-  char16_t CharAt(int32_t aOffset);
-
-  void TextAttributes(bool aIncludeDefAttrs,
-                      const int32_t aOffset,
-                      nsTArray<Attribute>* aAttributes,
-                      int32_t* aStartOffset,
-                      int32_t* aEndOffset);
-  void DefaultTextAttributes(nsTArray<Attribute>* aAttrs);
-
-  nsIntRect TextBounds(int32_t aStartOffset, int32_t aEndOffset,
-                       uint32_t aCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
-
-  nsIntRect CharBounds(int32_t aOffset, uint32_t aCoordType);
-
-  int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType);
-
-  bool SelectionBoundsAt(int32_t aSelectionNum,
-                         nsString& aData,
-                         int32_t* aStartOffset,
-                         int32_t* aEndOffset);
-
-  bool SetSelectionBoundsAt(int32_t aSelectionNum,
-                            int32_t aStartOffset,
-                            int32_t aEndOffset);
-
-  bool AddToSelection(int32_t aStartOffset,
-                      int32_t aEndOffset);
-
-  bool RemoveFromSelection(int32_t aSelectionNum);
-
-  void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
-                         uint32_t aScrollType);
-
-  void ScrollSubstringToPoint(int32_t aStartOffset,
-                              int32_t aEndOffset,
-                              uint32_t aCoordinateType,
-                              int32_t aX, int32_t aY);
-
-  void Text(nsString* aText);
-
-  void ReplaceText(const nsString& aText);
-
-  bool InsertText(const nsString& aText, int32_t aPosition);
-
-  bool CopyText(int32_t aStartPos, int32_t aEndPos);
-
-  bool CutText(int32_t aStartPos, int32_t aEndPos);
-
-  bool DeleteText(int32_t aStartPos, int32_t aEndPos);
-
-  bool PasteText(int32_t aPosition);
-
-  nsIntPoint ImagePosition(uint32_t aCoordType);
-
-  nsIntSize ImageSize();
-
-  uint32_t StartOffset(bool* aOk);
-
-  uint32_t EndOffset(bool* aOk);
-
-  bool IsLinkValid();
-
-  // XXX checking mRole alone may not result in same behavior as Accessibles
-  // due to ARIA roles. See bug 1210477.
-  inline bool IsTable() const
-  {
-    return mRole == roles::TABLE || mRole == roles::MATHML_TABLE;
-  }
-  inline bool IsTableRow() const
-  {
-    return (mRole == roles::ROW ||
-            mRole == roles::MATHML_TABLE_ROW ||
-            mRole == roles::MATHML_LABELED_ROW);
-  }
-  inline bool IsTableCell() const
-  {
-    return (mRole == roles::CELL ||
-            mRole == roles::COLUMNHEADER ||
-            mRole == roles::ROWHEADER ||
-            mRole == roles::GRID_CELL ||
-            mRole == roles::MATHML_CELL);
-  }
-
-  uint32_t AnchorCount(bool* aOk);
-
-  void AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk);
-
-  ProxyAccessible* AnchorAt(uint32_t aIndex);
-
-  uint32_t LinkCount();
-
-  ProxyAccessible* LinkAt(const uint32_t& aIndex);
-
-  int32_t LinkIndexOf(ProxyAccessible* aLink);
-
-  int32_t LinkIndexAtOffset(uint32_t aOffset);
-
-  ProxyAccessible* TableOfACell();
-
-  uint32_t ColIdx();
-
-  uint32_t RowIdx();
-
-  uint32_t ColExtent();
-
-  uint32_t RowExtent();
-
-  void ColHeaderCells(nsTArray<ProxyAccessible*>* aCells);
-
-  void RowHeaderCells(nsTArray<ProxyAccessible*>* aCells);
-
-  bool IsCellSelected();
-
-  ProxyAccessible* TableCaption();
-  void TableSummary(nsString& aSummary);
-  uint32_t TableColumnCount();
-  uint32_t TableRowCount();
-  ProxyAccessible* TableCellAt(uint32_t aRow, uint32_t aCol);
-  int32_t TableCellIndexAt(uint32_t aRow, uint32_t aCol);
-  int32_t TableColumnIndexAt(uint32_t aCellIndex);
-  int32_t TableRowIndexAt(uint32_t aCellIndex);
-  void TableRowAndColumnIndicesAt(uint32_t aCellIndex,
-                                  int32_t* aRow, int32_t* aCol);
-  uint32_t TableColumnExtentAt(uint32_t aRow, uint32_t aCol);
-  uint32_t TableRowExtentAt(uint32_t aRow, uint32_t aCol);
-  void TableColumnDescription(uint32_t aCol, nsString& aDescription);
-  void TableRowDescription(uint32_t aRow, nsString& aDescription);
-  bool TableColumnSelected(uint32_t aCol);
-  bool TableRowSelected(uint32_t aRow);
-  bool TableCellSelected(uint32_t aRow, uint32_t aCol);
-  uint32_t TableSelectedCellCount();
-  uint32_t TableSelectedColumnCount();
-  uint32_t TableSelectedRowCount();
-  void TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs);
-  void TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices);
-  void TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices);
-  void TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices);
-  void TableSelectColumn(uint32_t aCol);
-  void TableSelectRow(uint32_t aRow);
-  void TableUnselectColumn(uint32_t aCol);
-  void TableUnselectRow(uint32_t aRow);
-  bool TableIsProbablyForLayout();
-  ProxyAccessible* AtkTableColumnHeader(int32_t aCol);
-  ProxyAccessible* AtkTableRowHeader(int32_t aRow);
-
-  void SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems);
-  uint32_t SelectedItemCount();
-  ProxyAccessible* GetSelectedItem(uint32_t aIndex);
-  bool IsItemSelected(uint32_t aIndex);
-  bool AddItemToSelection(uint32_t aIndex);
-  bool RemoveItemFromSelection(uint32_t aIndex);
-  bool SelectAll();
-  bool UnselectAll();
-
-  void TakeSelection();
-  void SetSelected(bool aSelect);
-
-  bool DoAction(uint8_t aIndex);
-  uint8_t ActionCount();
-  void ActionDescriptionAt(uint8_t aIndex, nsString& aDescription);
-  void ActionNameAt(uint8_t aIndex, nsString& aName);
-  KeyBinding AccessKey();
-  KeyBinding KeyboardShortcut();
-  void AtkKeyBinding(nsString& aBinding);
-
-  double CurValue();
-  bool SetCurValue(double aValue);
-  double MinValue();
-  double MaxValue();
-  double Step();
-
-  void TakeFocus();
-  ProxyAccessible* FocusedChild();
-  ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
-                                Accessible::EWhichChildAtPoint aWhichChild);
-  nsIntRect Bounds();
-
-  void Language(nsString& aLocale);
-  void DocType(nsString& aType);
-  void Title(nsString& aTitle);
-  void URL(nsString& aURL);
-  void MimeType(nsString aMime);
-  void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
-                          nsString& aMimeType);
-
-  ProxyAccessible* AccessibleAtPoint(int32_t aX, int32_t aY,
-                                     bool aNeedsScreenCoords);
-
-  void Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
-               int32_t* aWidth, int32_t* aHeight);
-
-  /**
-   * Return the id of the dom node this accessible represents.  Note this
-   * should probably only be used for testing.
-   */
-  void DOMNodeID(nsString& aID);
-
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
@@ -415,40 +128,62 @@ public:
 
   /**
    * Return true if this proxy is a DocAccessibleParent.
    */
   bool IsDoc() const { return mIsDoc; }
   DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
 
 protected:
-  explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
+  ProxyAccessibleBase(uint64_t aID, Derived* aParent,
+                      DocAccessibleParent* aDoc, role aRole,
+                      uint32_t aInterfaces)
+    : mParent(aParent)
+    , mDoc(aDoc)
+    , mWrapper(0)
+    , mID(aID)
+    , mRole(aRole)
+    , mOuterDoc(false)
+    , mIsDoc(false)
+    , mHasValue(aInterfaces & Interfaces::VALUE)
+    , mIsHyperLink(aInterfaces & Interfaces::HYPERLINK)
+    , mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
+  {
+  }
+
+  explicit ProxyAccessibleBase(DocAccessibleParent* aThisAsDoc) :
     mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
     mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
     mIsHyperLink(false), mIsHyperText(false)
-  { MOZ_COUNT_CTOR(ProxyAccessible); }
+  {}
 
 protected:
-  ProxyAccessible* mParent;
+  Derived* mParent;
 
 private:
-  nsTArray<ProxyAccessible*> mChildren;
+  friend Derived;
+
+  nsTArray<Derived*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
+
 protected:
   // XXX DocAccessibleParent gets to change this to change the role of
   // documents.
   role mRole : 27;
+
 private:
   bool mOuterDoc : 1;
 
 public:
   const bool mIsDoc: 1;
   const bool mHasValue: 1;
   const bool mIsHyperLink: 1;
   const bool mIsHyperText: 1;
 };
 
+extern template class ProxyAccessibleBase<ProxyAccessible>;
+
 }
 }
 
 #endif
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -1,53 +1,58 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-IPDL_SOURCES += ['PDocAccessible.ipdl']
-
-# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
-# the C++.
-if CONFIG['ACCESSIBILITY']:
-    EXPORTS.mozilla.a11y += [
-        'DocAccessibleChild.h',
-        'DocAccessibleParent.h',
-        'ProxyAccessible.h'
+if CONFIG['OS_ARCH'] == 'WINNT':
+    DIRS += ['win']
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/win',
+        '/accessible/windows/ia2',
+        '/accessible/windows/msaa',
     ]
-
-    SOURCES += [
-        'DocAccessibleChild.cpp',
-        'DocAccessibleParent.cpp',
-        'ProxyAccessible.cpp'
+else:
+    DIRS += ['other']
+    LOCAL_INCLUDES += [
+        '/accessible/ipc/other',
     ]
-
-    LOCAL_INCLUDES += [
-        '../base',
-        '../generic',
-        '../xpcom',
-    ]
-
     if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
         LOCAL_INCLUDES += [
             '/accessible/atk',
         ]
-    elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-        LOCAL_INCLUDES += [
-            '/accessible/windows/ia2',
-            '/accessible/windows/msaa',
-        ]
     elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
         LOCAL_INCLUDES += [
             '/accessible/mac',
         ]
     else:
         LOCAL_INCLUDES += [
             '/accessible/other',
         ]
 
-    FINAL_LIBRARY = 'xul'
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wno-error=shadow']
+
+# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
+# the C++.
+if CONFIG['ACCESSIBILITY']:
+    EXPORTS.mozilla.a11y += [
+        'DocAccessibleChildBase.h',
+        'DocAccessibleParent.h',
+        'ProxyAccessibleBase.h',
+    ]
+
+    UNIFIED_SOURCES += [
+        'DocAccessibleChildBase.cpp',
+        'DocAccessibleParent.cpp',
+        'ProxyAccessibleBase.cpp',
+    ]
+
+    LOCAL_INCLUDES += [
+        '/accessible/base',
+        '/accessible/generic',
+        '/accessible/xpcom',
+    ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
-if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-error=shadow']
+FINAL_LIBRARY = 'xul'
rename from accessible/ipc/DocAccessibleChild.cpp
rename to accessible/ipc/other/DocAccessibleChild.cpp
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/other/DocAccessibleChild.cpp
@@ -19,73 +19,16 @@
 #include "nsAccUtils.h"
 #ifdef MOZ_ACCESSIBILITY_ATK
 #include "AccessibleWrap.h"
 #endif
 
 namespace mozilla {
 namespace a11y {
 
-static uint32_t
-InterfacesFor(Accessible* aAcc)
-{
-  uint32_t interfaces = 0;
-  if (aAcc->IsHyperText() && aAcc->AsHyperText()->IsTextRole())
-    interfaces |= Interfaces::HYPERTEXT;
-
-  if (aAcc->IsLink())
-    interfaces |= Interfaces::HYPERLINK;
-
-  if (aAcc->HasNumericValue())
-    interfaces |= Interfaces::VALUE;
-
-  if (aAcc->IsImage())
-    interfaces |= Interfaces::IMAGE;
-
-  if (aAcc->IsTable()) {
-    interfaces |= Interfaces::TABLE;
-  }
-
-  if (aAcc->IsTableCell())
-    interfaces |= Interfaces::TABLECELL;
-
-  if (aAcc->IsDoc())
-    interfaces |= Interfaces::DOCUMENT;
-
-  if (aAcc->IsSelect()) {
-    interfaces |= Interfaces::SELECTION;
-  }
-
-  if (aAcc->ActionCount()) {
-    interfaces |= Interfaces::ACTION;
-  }
-
-  return interfaces;
-}
-
-static void
-SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
-{
-  uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
-  uint32_t role = aRoot->Role();
-  uint32_t childCount = aRoot->ChildCount();
-  uint32_t interfaces = InterfacesFor(aRoot);
-
-  // OuterDocAccessibles are special because we don't want to serialize the
-  // child doc here, we'll call PDocAccessibleConstructor in
-  // NotificationController.
-  MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
-  if (aRoot->IsOuterDoc())
-    childCount = 0;
-
-  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces));
-  for (uint32_t i = 0; i < childCount; i++)
-    SerializeTree(aRoot->GetChildAt(i), aTree);
-}
-
 Accessible*
 DocAccessibleChild::IdToAccessible(const uint64_t& aID) const
 {
   if (!aID)
     return mDoc;
 
   if (!mDoc)
     return nullptr;
@@ -137,28 +80,16 @@ DocAccessibleChild::IdToTableCellAccessi
 
 TableAccessible*
 DocAccessibleChild::IdToTableAccessible(const uint64_t& aID) const
 {
   Accessible* acc = IdToAccessible(aID);
   return (acc && acc->IsTable()) ? acc->AsTable() : nullptr;
 }
 
-void
-DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
-{
-  Accessible* parent = aShowEvent->Parent();
-  uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
-  uint32_t idxInParent = aShowEvent->InsertionIndex();
-  nsTArray<AccessibleData> shownTree;
-  ShowEventData data(parentID, idxInParent, shownTree);
-  SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
-  SendShowEvent(data, aShowEvent->IsFromUserInput());
-}
-
 bool
 DocAccessibleChild::RecvState(const uint64_t& aID, uint64_t* aState)
 {
   Accessible* acc = IdToAccessible(aID);
   if (!acc) {
     *aState = states::DEFUNCT;
     return true;
   }
rename from accessible/ipc/DocAccessibleChild.h
rename to accessible/ipc/other/DocAccessibleChild.h
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -2,68 +2,46 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_DocAccessibleChild_h
 #define mozilla_a11y_DocAccessibleChild_h
 
-#include "mozilla/a11y/DocAccessible.h"
-#include "mozilla/a11y/PDocAccessibleChild.h"
-#include "nsISupportsImpl.h"
+#include "mozilla/a11y/DocAccessibleChildBase.h"
 
 namespace mozilla {
 namespace a11y {
+
 class Accessible;
 class HyperTextAccessible;
 class TextLeafAccessible;
 class ImageAccessible;
 class TableAccessible;
 class TableCellAccessible;
-class AccShowEvent;
 
-  /*
-   * These objects handle content side communication for an accessible document,
-   * and their lifetime is the same as the document they represent.
-   */
-class DocAccessibleChild : public PDocAccessibleChild
+/*
+ * 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) :
-    mDoc(aDoc)
-  { MOZ_COUNT_CTOR(DocAccessibleChild); }
+  explicit DocAccessibleChild(DocAccessible* aDoc)
+    : DocAccessibleChildBase(aDoc)
+  {
+    MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+  }
+
   ~DocAccessibleChild()
   {
-    // Shutdown() should have been called, but maybe it isn't if the process is
-    // killed?
-    MOZ_ASSERT(!mDoc);
-    if (mDoc)
-      mDoc->SetIPCDoc(nullptr);
-    MOZ_COUNT_DTOR(DocAccessibleChild);
+    MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   }
 
-  void Shutdown()
-  {
-    mDoc->SetIPCDoc(nullptr);
-    mDoc = nullptr;
-    SendShutdown();
-  }
-
-  virtual void ActorDestroy(ActorDestroyReason) override
-  {
-    if (!mDoc)
-      return;
-
-    mDoc->SetIPCDoc(nullptr);
-    mDoc = nullptr;
-  }
-
-  void ShowEvent(AccShowEvent* aShowEvent);
-
   /*
    * Return the state for the accessible with given ID.
    */
   virtual bool RecvState(const uint64_t& aID, uint64_t* aState) override;
 
   /*
    * Return the native state for the accessible with given ID.
    */
@@ -491,16 +469,14 @@ private:
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   TextLeafAccessible* IdToTextLeafAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
   TableCellAccessible* IdToTableCellAccessible(const uint64_t& aID) const;
   TableAccessible* IdToTableAccessible(const uint64_t& aID) const;
 
   bool PersistentPropertiesToArray(nsIPersistentProperties* aProps,
                                    nsTArray<Attribute>* aAttributes);
-
-  DocAccessible* mDoc;
 };
 
 }
 }
 
 #endif
rename from accessible/ipc/PDocAccessible.ipdl
rename to accessible/ipc/other/PDocAccessible.ipdl
copy from accessible/ipc/ProxyAccessible.cpp
copy to accessible/ipc/other/ProxyAccessible.cpp
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/other/ProxyAccessible.cpp
@@ -1,92 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProxyAccessible.h"
-#include "DocAccessibleParent.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
 namespace mozilla {
 namespace a11y {
 
-void
-ProxyAccessible::Shutdown()
-{
-  MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
-  NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
-  xpcAccessibleDocument* xpcDoc =
-    GetAccService()->GetCachedXPCDocument(Document());
-  if (xpcDoc) {
-    xpcDoc->NotifyOfShutdown(this);
-  }
-
-  // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
-  // can be destroyed before the doc they own.
-  if (!mOuterDoc) {
-    uint32_t childCount = mChildren.Length();
-    for (uint32_t idx = 0; idx < childCount; idx++)
-      mChildren[idx]->Shutdown();
-  } else {
-    if (mChildren.Length() != 1)
-      MOZ_CRASH("outer doc doesn't own adoc!");
-
-    mChildren[0]->AsDoc()->Unbind();
-  }
-
-  mChildren.Clear();
-  ProxyDestroyed(this);
-  mDoc->RemoveAccessible(this);
-}
-
-void
-ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent)
-{
-  if (aParent) {
-    MOZ_ASSERT(mChildren.IsEmpty());
-    mChildren.AppendElement(aParent);
-    mOuterDoc = true;
-  } else {
-    MOZ_ASSERT(mChildren.Length() == 1);
-    mChildren.Clear();
-    mOuterDoc = false;
-  }
-}
-
-bool
-ProxyAccessible::MustPruneChildren() const
-{
-  // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
-  // kept in sync with that.
-  if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION
-      && mRole != roles::OPTION && mRole != roles::ENTRY
-      && mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT
-      && mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON
-      && mRole != roles::GRAPHIC && mRole != roles::SLIDER
-      && mRole != roles::PROGRESSBAR && mRole != roles::SEPARATOR)
-    return false;
-
-  if (mChildren.Length() != 1)
-    return false;
-
-  return mChildren[0]->Role() == roles::TEXT_LEAF
-    || mChildren[0]->Role() == roles::STATICTEXT;
-}
-
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
   Unused << mDoc->SendState(mID, &state);
   return state;
 }
 
@@ -1027,65 +964,16 @@ ProxyAccessible::Step()
 }
 
 void
 ProxyAccessible::TakeFocus()
 {
   Unused << mDoc->SendTakeFocus(mID);
 }
 
-uint32_t
-ProxyAccessible::EmbeddedChildCount() const
-{
-  size_t count = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (mChildren[i]->IsEmbeddedObject()) {
-      count++;
-    }
-  }
-
-  return count;
-}
-
-int32_t
-ProxyAccessible::IndexOfEmbeddedChild(const ProxyAccessible* aChild)
-{
-  size_t index = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (mChildren[i]->IsEmbeddedObject()) {
-      if (mChildren[i] == aChild) {
-        return index;
-      }
-
-      index++;
-    }
-  }
-
-  return -1;
-}
-
-ProxyAccessible*
-ProxyAccessible::EmbeddedChildAt(size_t aChildIdx)
-{
-  size_t index = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (!mChildren[i]->IsEmbeddedObject()) {
-      continue;
-    }
-
-    if (index == aChildIdx) {
-      return mChildren[i];
-    }
-
-    index++;
-  }
-
-  return nullptr;
-}
-
 ProxyAccessible*
 ProxyAccessible::FocusedChild()
 {
   uint64_t childID = 0;
   bool ok = false;
   Unused << mDoc->SendFocusedChild(mID, &childID, &ok);
   return ok ? mDoc->GetAccessible(childID) : nullptr;
 }
@@ -1170,23 +1058,10 @@ ProxyAccessible::Extents(bool aNeedsScre
 }
 
 void
 ProxyAccessible::DOMNodeID(nsString& aID)
 {
   Unused << mDoc->SendDOMNodeID(mID, &aID);
 }
 
-Accessible*
-ProxyAccessible::OuterDocOfRemoteBrowser() const
-{
-  auto tab = static_cast<dom::TabParent*>(mDoc->Manager());
-  dom::Element* frame = tab->GetOwnerElement();
-  NS_ASSERTION(frame, "why isn't the tab in a frame!");
-  if (!frame)
-    return nullptr;
-
-  DocAccessible* chromeDoc = GetExistingDocAccessible(frame->OwnerDoc());
-
-  return chromeDoc ? chromeDoc->GetAccessible(frame) : nullptr;
 }
 }
-}
copy from accessible/ipc/ProxyAccessible.h
copy to accessible/ipc/other/ProxyAccessible.h
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/other/ProxyAccessible.h
@@ -2,125 +2,43 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_ProxyAccessible_h
 #define mozilla_a11y_ProxyAccessible_h
 
+#include "Accessible.h"
+#include "mozilla/a11y/ProxyAccessibleBase.h"
 #include "mozilla/a11y/Role.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleTypes.h"
-#include "Accessible.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsRect.h"
-#include "Accessible.h"
 
 namespace mozilla {
 namespace a11y {
 
-class Accessible;
-class Attribute;
-class DocAccessibleParent;
-enum class RelationType;
-
-enum Interfaces
-{
-  HYPERTEXT = 1,
-  HYPERLINK = 1 << 1,
-  IMAGE = 1 << 2,
-  VALUE = 1 << 3,
-  TABLE = 1 << 4,
-  TABLECELL = 1 << 5,
-  DOCUMENT = 1 << 6,
-  SELECTION = 1 << 7,
-  ACTION = 1 << 8,
-};
-
-class ProxyAccessible
+class ProxyAccessible : public ProxyAccessibleBase<ProxyAccessible>
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
-                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) :
-     mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
-     mOuterDoc(false), mIsDoc(false),
-     mHasValue(aInterfaces & Interfaces::VALUE),
-     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK),
-     mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
+                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces)
+    : ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces)
+
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
+
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
-    MOZ_ASSERT(!mWrapper);
-  }
-
-  void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
-  { mChildren.InsertElementAt(aIdx, aChild); }
-
-  uint32_t ChildrenCount() const { return mChildren.Length(); }
-  ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
-  ProxyAccessible* FirstChild() const
-    { return mChildren.Length() ? mChildren[0] : nullptr; }
-  ProxyAccessible* LastChild() const
-    { return mChildren.Length() ? mChildren[mChildren.Length() - 1] : nullptr; }
-  ProxyAccessible* PrevSibling() const
-  {
-    size_t idx = IndexInParent();
-    return idx > 0 ? Parent()->mChildren[idx - 1] : nullptr;
-  }
-  ProxyAccessible* NextSibling() const
-  {
-    size_t idx = IndexInParent();
-    return idx + 1 < Parent()->mChildren.Length() ? Parent()->mChildren[idx + 1]
-    : nullptr;
-  }
-
-  // XXX evaluate if this is fast enough.
-  size_t IndexInParent() const { return Parent()->mChildren.IndexOf(this); }
-  uint32_t EmbeddedChildCount() const;
-  int32_t IndexOfEmbeddedChild(const ProxyAccessible*);
-  ProxyAccessible* EmbeddedChildAt(size_t aChildIdx);
-  bool MustPruneChildren() const;
-
-  void Shutdown();
-
-  void SetChildDoc(DocAccessibleParent*);
-
-  /**
-   * Remove The given child.
-   */
-  void RemoveChild(ProxyAccessible* aChild)
-    { mChildren.RemoveElement(aChild); }
-
-  /**
-   * Return the proxy for the parent of the wrapped accessible.
-   */
-  ProxyAccessible* Parent() const { return mParent; }
-
-  Accessible* OuterDocOfRemoteBrowser() const;
-
-  /**
-   * Get the role of the accessible we're proxying.
-   */
-  role Role() const { return mRole; }
-
-  /**
-   * Return true if this is an embedded object.
-   */
-  bool IsEmbeddedObject() const
-  {
-    role role = Role();
-    return role != roles::TEXT_LEAF &&
-           role != roles::WHITESPACE &&
-           role != roles::STATICTEXT;
   }
 
   /*
    * Return the states for the proxied accessible.
    */
   uint64_t State() const;
 
   /*
@@ -391,64 +309,18 @@ public:
                int32_t* aWidth, int32_t* aHeight);
 
   /**
    * Return the id of the dom node this accessible represents.  Note this
    * should probably only be used for testing.
    */
   void DOMNodeID(nsString& aID);
 
-  /**
-   * Allow the platform to store a pointers worth of data on us.
-   */
-  uintptr_t GetWrapper() const { return mWrapper; }
-  void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
-
-  /*
-   * Return the ID of the accessible being proxied.
-   */
-  uint64_t ID() const { return mID; }
-
-  /**
-   * Return the document containing this proxy, or the proxy itself if it is a
-   * document.
-   */
-  DocAccessibleParent* Document() const { return mDoc; }
-
-  /**
-   * Return true if this proxy is a DocAccessibleParent.
-   */
-  bool IsDoc() const { return mIsDoc; }
-  DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
-
 protected:
-  explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
-    mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
-    mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
-    mIsHyperLink(false), mIsHyperText(false)
+  explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc)
+    : ProxyAccessibleBase(aThisAsDoc)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
-
-protected:
-  ProxyAccessible* mParent;
-
-private:
-  nsTArray<ProxyAccessible*> mChildren;
-  DocAccessibleParent* mDoc;
-  uintptr_t mWrapper;
-  uint64_t mID;
-protected:
-  // XXX DocAccessibleParent gets to change this to change the role of
-  // documents.
-  role mRole : 27;
-private:
-  bool mOuterDoc : 1;
-
-public:
-  const bool mIsDoc: 1;
-  const bool mHasValue: 1;
-  const bool mIsHyperLink: 1;
-  const bool mIsHyperText: 1;
 };
 
 }
 }
 
 #endif
copy from accessible/ipc/moz.build
copy to accessible/ipc/other/moz.build
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/other/moz.build
@@ -6,48 +6,42 @@
 
 IPDL_SOURCES += ['PDocAccessible.ipdl']
 
 # with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
 # the C++.
 if CONFIG['ACCESSIBILITY']:
     EXPORTS.mozilla.a11y += [
         'DocAccessibleChild.h',
-        'DocAccessibleParent.h',
-        'ProxyAccessible.h'
+        'ProxyAccessible.h',
     ]
 
     SOURCES += [
         'DocAccessibleChild.cpp',
-        'DocAccessibleParent.cpp',
-        'ProxyAccessible.cpp'
+        'ProxyAccessible.cpp',
     ]
 
     LOCAL_INCLUDES += [
-        '../base',
-        '../generic',
-        '../xpcom',
+        '../../base',
+        '../../generic',
+        '../../xpcom',
     ]
 
     if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
         LOCAL_INCLUDES += [
             '/accessible/atk',
         ]
-    elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-        LOCAL_INCLUDES += [
-            '/accessible/windows/ia2',
-            '/accessible/windows/msaa',
-        ]
     elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
         LOCAL_INCLUDES += [
             '/accessible/mac',
         ]
     else:
         LOCAL_INCLUDES += [
             '/accessible/other',
         ]
 
-    FINAL_LIBRARY = 'xul'
-
 include('/ipc/chromium/chromium-config.mozbuild')
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
+
+FINAL_LIBRARY = 'xul'
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/COMPtrTypes.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/a11y/COMPtrTypes.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/a11y/Accessible.h"
+#include "mozilla/Move.h"
+#include "mozilla/mscom/MainThreadHandoff.h"
+#include "mozilla/RefPtr.h"
+
+using mozilla::mscom::MainThreadHandoff;
+using mozilla::mscom::STAUniquePtr;
+
+namespace mozilla {
+namespace a11y {
+
+IAccessibleHolder
+CreateHolderFromAccessible(Accessible* aAccToWrap)
+{
+  MOZ_ASSERT(aAccToWrap && NS_IsMainThread());
+  if (!aAccToWrap) {
+    return nullptr;
+  }
+
+  IAccessible* rawNative = nullptr;
+  aAccToWrap->GetNativeInterface((void**)&rawNative);
+  MOZ_ASSERT(rawNative);
+  if (!rawNative) {
+    return nullptr;
+  }
+
+  STAUniquePtr<IAccessible> iaToProxy(rawNative);
+
+  IAccessible* rawIntercepted = nullptr;
+  HRESULT hr = MainThreadHandoff::WrapInterface(iaToProxy, &rawIntercepted);
+  MOZ_ASSERT(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return nullptr;
+  }
+
+  IAccessibleHolder::COMPtrType iaIntercepted(rawIntercepted);
+  return IAccessibleHolder(Move(iaIntercepted));
+}
+
+} // namespace a11y
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/COMPtrTypes.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_COMPtrTypes_h
+#define mozilla_a11y_COMPtrTypes_h
+
+#include "mozilla/mscom/COMPtrHolder.h"
+
+#include <oleacc.h>
+
+namespace mozilla {
+namespace a11y {
+
+typedef mozilla::mscom::COMPtrHolder<IAccessible, IID_IAccessible> IAccessibleHolder;
+
+class Accessible;
+
+IAccessibleHolder
+CreateHolderFromAccessible(Accessible* aAccToWrap);
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_COMPtrTypes_h
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DocAccessibleChild.h"
+
+#include "Accessible-inl.h"
+#include "mozilla/a11y/PlatformChild.h"
+#include "mozilla/ClearOnShutdown.h"
+
+namespace mozilla {
+namespace a11y {
+
+static StaticAutoPtr<PlatformChild> sPlatformChild;
+
+DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc)
+  : DocAccessibleChildBase(aDoc)
+{
+  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)
+{
+  IAccessibleHolder parentProxy;
+  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy);
+  mParentProxy.reset(parentProxy.Release());
+}
+
+} // namespace a11y
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_DocAccessibleChild_h
+#define mozilla_a11y_DocAccessibleChild_h
+
+#include "mozilla/a11y/COMPtrTypes.h"
+#include "mozilla/a11y/DocAccessibleChildBase.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);
+  IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
+
+private:
+  mscom::ProxyUniquePtr<IAccessible> mParentProxy;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_DocAccessibleChild_h
copy from accessible/ipc/PDocAccessible.ipdl
copy to accessible/ipc/win/PDocAccessible.ipdl
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -1,54 +1,44 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-include protocol PFileDescriptorSet;
 include protocol PBrowser;
 
-include "mozilla/GfxMessageUtils.h";
-
-using nsIntRect from "nsRect.h";
-using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
-using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/COMPtrTypes.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   uint32_t Role;
   uint32_t ChildrenCount;
   uint32_t Interfaces;
+  IAccessibleHolder COMProxy;
 };
 
 struct ShowEventData
 {
   uint64_t ID;
   uint32_t Idx;
   AccessibleData[] NewTree;
 };
 
 struct Attribute
 {
   nsCString Name;
   nsString Value;
 };
 
-struct RelationTargets
-{
-  uint32_t Type;
-  uint64_t[] Targets;
-};
-
-prio(normal upto high) sync protocol PDocAccessible
+sync protocol PDocAccessible
 {
   manager PBrowser;
 
 parent:
   async Shutdown();
 
   /*
    * Notify the parent process the document in the child process is firing an
@@ -65,197 +55,19 @@ parent:
   async RoleChangedEvent(uint32_t aRole);
 
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
+  // For now we'll add the command to send the proxy here. This might move to
+  // PDocAccessible constructor in PBrowser.
+  sync COMProxy(IAccessibleHolder aDocCOMProxy)
+    returns(IAccessibleHolder aParentCOMProxy);
+
 child:
   async __delete__();
-
-  // Accessible
-  prio(high) sync State(uint64_t aID) returns(uint64_t states);
-  prio(high) sync NativeState(uint64_t aID) returns(uint64_t states);
-  prio(high) sync Name(uint64_t aID) returns(nsString name);
-  prio(high) sync Value(uint64_t aID) returns(nsString value);
-  prio(high) sync Help(uint64_t aID) returns(nsString help);
-  prio(high) sync Description(uint64_t aID) returns(nsString desc);
-  prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
-  prio(high) sync RelationByType(uint64_t aID, uint32_t aRelationType)
-    returns(uint64_t[] targets);
-  prio(high) sync Relations(uint64_t aID) returns(RelationTargets[] relations);
-  prio(high) sync IsSearchbox(uint64_t aID) returns(bool retval);
-  prio(high) sync LandmarkRole(uint64_t aID) returns(nsString landmark);
-  prio(high) sync ARIARoleAtom(uint64_t aID) returns(nsString role);
-  prio(high) sync GetLevelInternal(uint64_t aID) returns(int32_t aLevel);
-  async ScrollTo(uint64_t aID, uint32_t aScrollType);
-  async ScrollToPoint(uint64_t aID, uint32_t aScrollType, int32_t aX,
-                      int32_t aY);
-
-  // AccessibleText
-
-  // TextSubstring is getText in IDL.
-  prio(high) sync CaretLineNumber(uint64_t aID) returns(int32_t aLineNumber);
-  prio(high) sync CaretOffset(uint64_t aID) returns(int32_t aOffset);
-   async SetCaretOffset(uint64_t aID, int32_t aOffset);
-  prio(high) sync CharacterCount(uint64_t aID) returns(int32_t aCount);
-  prio(high) sync SelectionCount(uint64_t aID) returns(int32_t aCount);
-  prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
-                                aEndOffset) returns(nsString aText, bool aValid);
-  prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
-    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
-  prio(high) sync GetTextAtOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
-    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
-
-  prio(high) sync GetTextBeforeOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
-    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
-  prio(high) sync CharAt(uint64_t aID, int32_t aOffset) returns(uint16_t aChar);
-
-  prio(high) sync TextAttributes(uint64_t aID, bool aIncludeDefAttrs, int32_t aOffset)
-    returns(Attribute[] aAttributes, int32_t aStartOffset, int32_t aEndOffset);
-  prio(high) sync DefaultTextAttributes(uint64_t aID) returns(Attribute[] aAttributes);
-
-  prio(high) sync TextBounds(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset,
-                             uint32_t aCoordType)
-    returns(nsIntRect aRetVal);
-  prio(high) sync CharBounds(uint64_t aID, int32_t aOffset, uint32_t aCoordType)
-    returns(nsIntRect aRetVal);
-
-  prio(high) sync OffsetAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aCoordType)
-    returns(int32_t aRetVal);
-
-  prio(high) sync SelectionBoundsAt(uint64_t aID, int32_t aSelectionNum)
-    returns(bool aSucceeded, nsString aData, int32_t aStartOffset, int32_t aEndOffset);
-  prio(high) sync SetSelectionBoundsAt(uint64_t aID, int32_t aSelectionNum,
-                                       int32_t aStartOffset, int32_t aEndOffset)
-    returns(bool aSucceeded);
-  prio(high) sync AddToSelection(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset)
-    returns(bool aSucceeded);
-  prio(high) sync RemoveFromSelection(uint64_t aID, int32_t aSelectionNum)
-    returns(bool aSucceeded);
-
-  async ScrollSubstringTo(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset,
-                          uint32_t aScrollType);
-  async ScrollSubstringToPoint(uint64_t aID,
-                               int32_t aStartOffset,
-                               int32_t aEndOffset,
-                               uint32_t aCoordinateType,
-                               int32_t aX, int32_t aY);
-
-  prio(high) sync Text(uint64_t aID) returns(nsString aText);
-  prio(high) sync ReplaceText(uint64_t aID, nsString aText);
-  prio(high) sync InsertText(uint64_t aID, nsString aText, int32_t aPosition)
-    returns(bool aValid);
-  prio(high) sync CopyText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
-    returns(bool aValid);
-  prio(high) sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
-    returns(bool aValid);
-  prio(high) sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
-    returns(bool aValid);
-  prio(high) sync PasteText(uint64_t aID, int32_t aPosition)
-    returns(bool aValid);
-
-  prio(high) sync ImagePosition(uint64_t aID, uint32_t aCoordType) returns(IntPoint aRetVal);
-  prio(high) sync ImageSize(uint64_t aID) returns(IntSize aRetVal);
-
-  prio(high) sync StartOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
-  prio(high) sync EndOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
-  prio(high) sync IsLinkValid(uint64_t aID) returns(bool aRetVal);
-  prio(high) sync AnchorCount(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
-  prio(high) sync AnchorURIAt(uint64_t aID, uint32_t aIndex) returns(nsCString aURI, bool aOk);
-  prio(high) sync AnchorAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfAnchor, bool aOk);
-
-  prio(high) sync LinkCount(uint64_t aID) returns(uint32_t aCount);
-  prio(high) sync LinkAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfLink, bool aOk);
-  prio(high) sync LinkIndexOf(uint64_t aID, uint64_t aLinkID) returns(int32_t aIndex);
-  prio(high) sync LinkIndexAtOffset(uint64_t aID, uint32_t aOffset) returns(int32_t aIndex);
-
-  prio(high) sync TableOfACell(uint64_t aID) returns(uint64_t aTableID, bool aOk);
-  prio(high) sync ColIdx(uint64_t aID) returns(uint32_t aIndex);
-  prio(high) sync RowIdx(uint64_t aID) returns(uint32_t aIndex);
-  prio(high) sync ColExtent(uint64_t aID) returns(uint32_t aExtent);
-  prio(high) sync RowExtent(uint64_t aID) returns(uint32_t aExtent);
-  prio(high) sync ColHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
-  prio(high) sync RowHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
-  prio(high) sync IsCellSelected(uint64_t aID) returns(bool aSelected);
-
-  prio(high) sync TableCaption(uint64_t aID) returns(uint64_t aCaptionID, bool aOk);
-  prio(high) sync TableSummary(uint64_t aID) returns(nsString aSummary);
-  prio(high) sync TableColumnCount(uint64_t aID) returns(uint32_t aColCount);
-  prio(high) sync TableRowCount(uint64_t aID) returns(uint32_t aRowCount);
-  prio(high) sync TableCellAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint64_t aCellID, bool aOk);
-  prio(high) sync TableCellIndexAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(int32_t aIndex);
-  prio(high) sync TableColumnIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aCol);
-  prio(high) sync TableRowIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow);
-  prio(high) sync TableRowAndColumnIndicesAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow, int32_t aCol);
-  prio(high) sync TableColumnExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
-  prio(high) sync TableRowExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
-  prio(high) sync TableColumnDescription(uint64_t aID, uint32_t aCol) returns(nsString aDescription);
-  prio(high) sync TableRowDescription(uint64_t aID, uint32_t aRow) returns(nsString aDescription);
-  prio(high) sync TableColumnSelected(uint64_t aID, uint32_t aCol) returns(bool aSelected);
-  prio(high) sync TableRowSelected(uint64_t aID, uint32_t aRow) returns(bool aSelected);
-  prio(high) sync TableCellSelected(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(bool aSelected);
-  prio(high) sync TableSelectedCellCount(uint64_t aID) returns(uint32_t aSelectedCells);
-  prio(high) sync TableSelectedColumnCount(uint64_t aID) returns(uint32_t aSelectedColumns);
-  prio(high) sync TableSelectedRowCount(uint64_t aID) returns(uint32_t aSelectedRows);
-  prio(high) sync TableSelectedCells(uint64_t aID) returns(uint64_t[] aCellIDs);
-  prio(high) sync TableSelectedCellIndices(uint64_t aID) returns(uint32_t[] aCellIndeces);
-  prio(high) sync TableSelectedColumnIndices(uint64_t aID) returns(uint32_t[] aColumnIndeces);
-  prio(high) sync TableSelectedRowIndices(uint64_t aID) returns(uint32_t[] aRowIndeces);
-  prio(high) sync TableSelectColumn(uint64_t aID, uint32_t aCol);
-  prio(high) sync TableSelectRow(uint64_t aID, uint32_t aRow);
-  prio(high) sync TableUnselectColumn(uint64_t aID, uint32_t aCol);
-  prio(high) sync TableUnselectRow(uint64_t aID, uint32_t aRow);
-  prio(high) sync TableIsProbablyForLayout(uint64_t aID) returns(bool aForLayout);
-  prio(high) sync AtkTableColumnHeader(uint64_t aID, int32_t aCol)
-    returns(uint64_t aHeaderID, bool aOk);
-  prio(high) sync AtkTableRowHeader(uint64_t aID, int32_t aRow)
-    returns(uint64_t aHeaderID, bool aOk);
-
-  prio(high) sync SelectedItems(uint64_t aID) returns(uint64_t[] aSelectedItemIDs);
-  prio(high) sync SelectedItemCount(uint64_t aID) returns(uint32_t aCount);
-  prio(high) sync GetSelectedItem(uint64_t aID, uint32_t aIndex) returns(uint64_t aSelected, bool aOk);
-  prio(high) sync IsItemSelected(uint64_t aID, uint32_t aIndex) returns(bool aSelected);
-  prio(high) sync AddItemToSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
-  prio(high) sync RemoveItemFromSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
-  prio(high) sync SelectAll(uint64_t aID) returns(bool aSuccess);
-  prio(high) sync UnselectAll(uint64_t aID) returns(bool aSuccess);
-
-  async TakeSelection(uint64_t aID);
-  async SetSelected(uint64_t aID, bool aSelected);
-
-  prio(high) sync DoAction(uint64_t aID, uint8_t aIndex) returns(bool aSuccess);
-  prio(high) sync ActionCount(uint64_t aID) returns(uint8_t aCount);
-  prio(high) sync ActionDescriptionAt(uint64_t aID, uint8_t aIndex) returns(nsString aDescription);
-  prio(high) sync ActionNameAt(uint64_t aID, uint8_t aIndex) returns(nsString aName);
-  prio(high) sync AccessKey(uint64_t aID) returns(uint32_t aKey, uint32_t aModifierMask);
-  prio(high) sync KeyboardShortcut(uint64_t aID) returns(uint32_t aKey, uint32_t aModifierMask);
-  prio(high) sync AtkKeyBinding(uint64_t aID) returns(nsString aResult);
-
-  prio(high) sync CurValue(uint64_t aID) returns(double aValue);
-  prio(high) sync SetCurValue(uint64_t aID, double aValue) returns(bool aRetVal);
-  prio(high) sync MinValue(uint64_t aID) returns(double aValue);
-  prio(high) sync MaxValue(uint64_t aID) returns(double aValue);
-  prio(high) sync Step(uint64_t aID) returns(double aStep);
-
-  async TakeFocus(uint64_t aID);
-  prio(high) sync FocusedChild(uint64_t aID)
-    returns(uint64_t aChild, bool aOk);
-
-  prio(high) sync Language(uint64_t aID) returns(nsString aLocale);
-  prio(high) sync DocType(uint64_t aID) returns(nsString aType);
-  prio(high) sync Title(uint64_t aID) returns(nsString aTitle);
-  prio(high) sync URL(uint64_t aID) returns(nsString aURL);
-  prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
-  prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
-
-  prio(high) sync AccessibleAtPoint(uint64_t aID, int32_t aX, int32_t aY, bool aNeedsScreenCoords, uint32_t aWhich)
-    returns(uint64_t aResult, bool aOk);
-
-  prio(high) sync Extents(uint64_t aID, bool aNeedsScreenCoords)
-    returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
-  prio(high) sync DOMNodeID(uint64_t aID) returns(nsString aDOMNodeID);
 };
 
 }
 }
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/a11y/PlatformChild.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#include "mozilla/mscom/InterceptorLog.h"
+
+#include "Accessible2.h"
+#include "Accessible2_2.h"
+#include "AccessibleHypertext2.h"
+#include "AccessibleTableCell.h"
+
+#include "AccessibleHypertext2_i.c"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Unfortunately the COM interceptor does not intrinsically handle array
+ * outparams. Instead we manually define the relevant metadata here, and
+ * register it in a call to mozilla::mscom::RegisterArrayData.
+ * @see mozilla::mscom::ArrayData
+ */
+static const mozilla::mscom::ArrayData sPlatformChildArrayData[] = {
+  {IID_IEnumVARIANT, 3, 1, VT_DISPATCH, IID_IDispatch, 2},
+  {IID_IAccessible2, 30, 1, VT_UNKNOWN | VT_BYREF, IID_IAccessibleRelation, 2},
+  {IID_IAccessibleRelation, 7, 1, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 2},
+  {IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 3},
+  {IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1},
+  {IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1},
+  {IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1}
+};
+
+// Type libraries are thread-neutral, so we can register those from any
+// apartment. OTOH, proxies must be registered from within the apartment where
+// we intend to instantiate them. Therefore RegisterProxy() must be called
+// via EnsureMTA.
+PlatformChild::PlatformChild()
+  : mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
+        mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
+  , mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
+{
+  mozilla::mscom::InterceptorLog::Init();
+  mozilla::mscom::RegisterArrayData(sPlatformChildArrayData);
+
+  UniquePtr<mozilla::mscom::RegisteredProxy> ia2Proxy;
+  mozilla::mscom::EnsureMTA([&ia2Proxy]() -> void {
+    ia2Proxy = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
+  });
+  mIA2Proxy = Move(ia2Proxy);
+}
+
+} // namespace a11y
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/PlatformChild.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_PlatformChild_h
+#define mozilla_a11y_PlatformChild_h
+
+#include "mozilla/mscom/Registration.h"
+
+namespace mozilla {
+namespace a11y {
+
+class PlatformChild
+{
+public:
+  PlatformChild();
+
+  PlatformChild(PlatformChild&) = delete;
+  PlatformChild(PlatformChild&&) = delete;
+  PlatformChild& operator=(PlatformChild&) = delete;
+  PlatformChild& operator=(PlatformChild&&) = delete;
+
+private:
+  UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
+  UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
+  UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
+};
+
+} // namespace mozilla
+} // namespace a11y
+
+#endif // mozilla_a11y_PlatformChild_h
+
copy from accessible/ipc/ProxyAccessible.cpp
copy to accessible/ipc/win/ProxyAccessible.cpp
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -1,1192 +1,280 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "Accessible2.h"
 #include "ProxyAccessible.h"
-#include "DocAccessibleParent.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
 #include "mozilla/a11y/Role.h"
 #include "xpcAccessibleDocument.h"
 
+#include <comutil.h>
+
 namespace mozilla {
 namespace a11y {
 
-void
-ProxyAccessible::Shutdown()
+bool
+ProxyAccessible::GetCOMInterface(void** aOutAccessible) const
 {
-  MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
-  NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
-  xpcAccessibleDocument* xpcDoc =
-    GetAccService()->GetCachedXPCDocument(Document());
-  if (xpcDoc) {
-    xpcDoc->NotifyOfShutdown(this);
+  if (!aOutAccessible) {
+    return false;
+  }
+  RefPtr<IAccessible> addRefed = mCOMProxy;
+  addRefed.forget(aOutAccessible);
+  return !!mCOMProxy;
+}
+
+void
+ProxyAccessible::Name(nsString& aName) const
+{
+  aName.Truncate();
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return;
   }
 
-  // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
-  // can be destroyed before the doc they own.
-  if (!mOuterDoc) {
-    uint32_t childCount = mChildren.Length();
-    for (uint32_t idx = 0; idx < childCount; idx++)
-      mChildren[idx]->Shutdown();
-  } else {
-    if (mChildren.Length() != 1)
-      MOZ_CRASH("outer doc doesn't own adoc!");
-
-    mChildren[0]->AsDoc()->Unbind();
+  VARIANT id;
+  id.vt = VT_I4;
+  id.lVal = CHILDID_SELF;
+  BSTR result;
+  HRESULT hr = acc->get_accName(id, &result);
+  _bstr_t resultWrap(result, false);
+  if (FAILED(hr)) {
+    return;
   }
-
-  mChildren.Clear();
-  ProxyDestroyed(this);
-  mDoc->RemoveAccessible(this);
+  aName = (wchar_t*)resultWrap;
 }
 
 void
-ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent)
+ProxyAccessible::Value(nsString& aValue) const
 {
-  if (aParent) {
-    MOZ_ASSERT(mChildren.IsEmpty());
-    mChildren.AppendElement(aParent);
-    mOuterDoc = true;
-  } else {
-    MOZ_ASSERT(mChildren.Length() == 1);
-    mChildren.Clear();
-    mOuterDoc = false;
+  aValue.Truncate();
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return;
   }
+
+  VARIANT id;
+  id.vt = VT_I4;
+  id.lVal = CHILDID_SELF;
+  BSTR result;
+  HRESULT hr = acc->get_accValue(id, &result);
+  _bstr_t resultWrap(result, false);
+  if (FAILED(hr)) {
+    return;
+  }
+  aValue = (wchar_t*)resultWrap;
 }
 
-bool
-ProxyAccessible::MustPruneChildren() const
+void
+ProxyAccessible::Description(nsString& aDesc) const
 {
-  // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
-  // kept in sync with that.
-  if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION
-      && mRole != roles::OPTION && mRole != roles::ENTRY
-      && mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT
-      && mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON
-      && mRole != roles::GRAPHIC && mRole != roles::SLIDER
-      && mRole != roles::PROGRESSBAR && mRole != roles::SEPARATOR)
-    return false;
+  aDesc.Truncate();
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return;
+  }
 
-  if (mChildren.Length() != 1)
-    return false;
-
-  return mChildren[0]->Role() == roles::TEXT_LEAF
-    || mChildren[0]->Role() == roles::STATICTEXT;
+  VARIANT id;
+  id.vt = VT_I4;
+  id.lVal = CHILDID_SELF;
+  BSTR result;
+  HRESULT hr = acc->get_accDescription(id, &result);
+  _bstr_t resultWrap(result, false);
+  if (FAILED(hr)) {
+    return;
+  }
+  aDesc = (wchar_t*)resultWrap;
 }
 
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
-  Unused << mDoc->SendState(mID, &state);
-  return state;
-}
-
-uint64_t
-ProxyAccessible::NativeState() const
-{
-  uint64_t state = 0;
-  Unused << mDoc->SendNativeState(mID, &state);
-  return state;
-}
-
-void
-ProxyAccessible::Name(nsString& aName) const
-{
-  Unused << mDoc->SendName(mID, &aName);
-}
-
-void
-ProxyAccessible::Value(nsString& aValue) const
-{
-  Unused << mDoc->SendValue(mID, &aValue);
-}
-
-void
-ProxyAccessible::Help(nsString& aHelp) const
-{
-  Unused << mDoc->SendHelp(mID, &aHelp);
-}
-
-void
-ProxyAccessible::Description(nsString& aDesc) const
-{
-  Unused << mDoc->SendDescription(mID, &aDesc);
-}
-
-void
-ProxyAccessible::Attributes(nsTArray<Attribute> *aAttrs) const
-{
-  Unused << mDoc->SendAttributes(mID, aAttrs);
-}
-
-nsTArray<ProxyAccessible*>
-ProxyAccessible::RelationByType(RelationType aType) const
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendRelationByType(mID, static_cast<uint32_t>(aType),
-                                     &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  nsTArray<ProxyAccessible*> targets(targetCount);
-  for (size_t i = 0; i < targetCount; i++)
-    if (ProxyAccessible* proxy = mDoc->GetAccessible(targetIDs[i]))
-      targets.AppendElement(proxy);
-
-  return Move(targets);
-}
-
-void
-ProxyAccessible::Relations(nsTArray<RelationType>* aTypes,
-                           nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets)
-  const
-{
-  nsTArray<RelationTargets> ipcRelations;
-  Unused << mDoc->SendRelations(mID, &ipcRelations);
-
-  size_t relationCount = ipcRelations.Length();
-  aTypes->SetCapacity(relationCount);
-  aTargetSets->SetCapacity(relationCount);
-  for (size_t i = 0; i < relationCount; i++) {
-    uint32_t type = ipcRelations[i].Type();
-    if (type > static_cast<uint32_t>(RelationType::LAST))
-      continue;
-
-    size_t targetCount = ipcRelations[i].Targets().Length();
-    nsTArray<ProxyAccessible*> targets(targetCount);
-    for (size_t j = 0; j < targetCount; j++)
-      if (ProxyAccessible* proxy = mDoc->GetAccessible(ipcRelations[i].Targets()[j]))
-        targets.AppendElement(proxy);
-
-    if (targets.IsEmpty())
-      continue;
-
-    aTargetSets->AppendElement(Move(targets));
-    aTypes->AppendElement(static_cast<RelationType>(type));
-  }
-}
-
-bool
-ProxyAccessible::IsSearchbox() const
-{
-  bool retVal = false;
-  Unused << mDoc->SendIsSearchbox(mID, &retVal);
-  return retVal;
-}
-
-nsIAtom*
-ProxyAccessible::LandmarkRole() const
-{
-  nsString landmark;
-  Unused << mDoc->SendLandmarkRole(mID, &landmark);
-  return NS_GetStaticAtom(landmark);
-}
-
-nsIAtom*
-ProxyAccessible::ARIARoleAtom() const
-{
-  nsString role;
-  Unused << mDoc->SendARIARoleAtom(mID, &role);
-  return NS_GetStaticAtom(role);
-}
-
-int32_t
-ProxyAccessible::GetLevelInternal()
-{
-  int32_t level = 0;
-  Unused << mDoc->SendGetLevelInternal(mID, &level);
-  return level;
-}
-
-void
-ProxyAccessible::ScrollTo(uint32_t aScrollType)
-{
-  Unused << mDoc->SendScrollTo(mID, aScrollType);
-}
-
-void
-ProxyAccessible::ScrollToPoint(uint32_t aScrollType, int32_t aX, int32_t aY)
-{
-  Unused << mDoc->SendScrollToPoint(mID, aScrollType, aX, aY);
-}
-
-int32_t
-ProxyAccessible::CaretLineNumber()
-{
-  int32_t line = -1;
-  Unused << mDoc->SendCaretOffset(mID, &line);
-  return line;
-}
-
-int32_t
-ProxyAccessible::CaretOffset()
-{
-  int32_t offset = 0;
-  Unused << mDoc->SendCaretOffset(mID, &offset);
-  return offset;
-}
-
-void
-ProxyAccessible::SetCaretOffset(int32_t aOffset)
-{
-  Unused << mDoc->SendSetCaretOffset(mID, aOffset);
-}
-
-int32_t
-ProxyAccessible::CharacterCount()
-{
-  int32_t count = 0;
-  Unused << mDoc->SendCharacterCount(mID, &count);
-  return count;
-}
-
-int32_t
-ProxyAccessible::SelectionCount()
-{
-  int32_t count = 0;
-  Unused << mDoc->SendSelectionCount(mID, &count);
-  return count;
-}
-
-bool
-ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
-                               nsString& aText) const
-{
-  bool valid;
-  Unused << mDoc->SendTextSubstring(mID, aStartOffset, aEndOfset, &aText, &valid);
-  return valid;
-}
-
-void
-ProxyAccessible::GetTextAfterOffset(int32_t aOffset,
-                                    AccessibleTextBoundary aBoundaryType,
-                                    nsString& aText, int32_t* aStartOffset,
-                                    int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextAfterOffset(mID, aOffset, aBoundaryType,
-                                         &aText, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::GetTextAtOffset(int32_t aOffset,
-                                 AccessibleTextBoundary aBoundaryType,
-                                 nsString& aText, int32_t* aStartOffset,
-                                 int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextAtOffset(mID, aOffset, aBoundaryType,
-                                      &aText, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::GetTextBeforeOffset(int32_t aOffset,
-                                     AccessibleTextBoundary aBoundaryType,
-                                     nsString& aText, int32_t* aStartOffset,
-                                     int32_t* aEndOffset)
-{
-  Unused << mDoc->SendGetTextBeforeOffset(mID, aOffset, aBoundaryType,
-                                          &aText, aStartOffset, aEndOffset);
-}
-
-char16_t
-ProxyAccessible::CharAt(int32_t aOffset)
-{
-  uint16_t retval = 0;
-  Unused << mDoc->SendCharAt(mID, aOffset, &retval);
-  return static_cast<char16_t>(retval);
-}
-
-void
-ProxyAccessible::TextAttributes(bool aIncludeDefAttrs,
-                                int32_t aOffset,
-                                nsTArray<Attribute>* aAttributes,
-                                int32_t* aStartOffset,
-                                int32_t* aEndOffset)
-{
-  Unused << mDoc->SendTextAttributes(mID, aIncludeDefAttrs, aOffset,
-                                     aAttributes, aStartOffset, aEndOffset);
-}
-
-void
-ProxyAccessible::DefaultTextAttributes(nsTArray<Attribute>* aAttrs)
-{
-  Unused << mDoc->SendDefaultTextAttributes(mID, aAttrs);
-}
-
-nsIntRect
-ProxyAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
-                            uint32_t aCoordType)
-{
-  nsIntRect rect;
-  Unused <<
-    mDoc->SendTextBounds(mID, aStartOffset, aEndOffset, aCoordType, &rect);
-  return rect;
-}
-
-nsIntRect
-ProxyAccessible::CharBounds(int32_t aOffset, uint32_t aCoordType)
-{
-  nsIntRect rect;
-  Unused <<
-    mDoc->SendCharBounds(mID, aOffset, aCoordType, &rect);
-  return rect;
-}
-
-int32_t
-ProxyAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
-{
-  int32_t retVal = -1;
-  Unused << mDoc->SendOffsetAtPoint(mID, aX, aY, aCoordType, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::SelectionBoundsAt(int32_t aSelectionNum,
-                                   nsString& aData,
-                                   int32_t* aStartOffset,
-                                   int32_t* aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendSelectionBoundsAt(mID, aSelectionNum, &retVal, &aData,
-                                        aStartOffset, aEndOffset);
-  return retVal;
-}
-
-bool
-ProxyAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
-                                      int32_t aStartOffset,
-                                      int32_t aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendSetSelectionBoundsAt(mID, aSelectionNum, aStartOffset,
-                                           aEndOffset, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::AddToSelection(int32_t aStartOffset,
-                                int32_t aEndOffset)
-{
-  bool retVal = false;
-  Unused << mDoc->SendAddToSelection(mID, aStartOffset, aEndOffset, &retVal);
-  return retVal;
-}
-
-bool
-ProxyAccessible::RemoveFromSelection(int32_t aSelectionNum)
-{
-  bool retVal = false;
-  Unused << mDoc->SendRemoveFromSelection(mID, aSelectionNum, &retVal);
-  return retVal;
-}
-
-void
-ProxyAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
-                                   uint32_t aScrollType)
-{
-  Unused << mDoc->SendScrollSubstringTo(mID, aStartOffset, aEndOffset, aScrollType);
-}
-
-void
-ProxyAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
-                                        int32_t aEndOffset,
-                                        uint32_t aCoordinateType,
-                                        int32_t aX, int32_t aY)
-{
-  Unused << mDoc->SendScrollSubstringToPoint(mID, aStartOffset, aEndOffset,
-                                             aCoordinateType, aX, aY);
-}
-
-void
-ProxyAccessible::Text(nsString* aText)
-{
-  Unused << mDoc->SendText(mID, aText);
-}
-
-void
-ProxyAccessible::ReplaceText(const nsString& aText)
-{
-  Unused << mDoc->SendReplaceText(mID, aText);
-}
-
-bool
-ProxyAccessible::InsertText(const nsString& aText, int32_t aPosition)
-{
-  bool valid;
-  Unused << mDoc->SendInsertText(mID, aText, aPosition, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::CopyText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendCopyText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::CutText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendCutText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::DeleteText(int32_t aStartPos, int32_t aEndPos)
-{
-  bool valid;
-  Unused << mDoc->SendDeleteText(mID, aStartPos, aEndPos, &valid);
-  return valid;
-}
-
-bool
-ProxyAccessible::PasteText(int32_t aPosition)
-{
-  bool valid;
-  Unused << mDoc->SendPasteText(mID, aPosition, &valid);
-  return valid;
-}
-
-nsIntPoint
-ProxyAccessible::ImagePosition(uint32_t aCoordType)
-{
-  nsIntPoint retVal;
-  Unused << mDoc->SendImagePosition(mID, aCoordType, &retVal);
-  return retVal;
-}
-
-nsIntSize
-ProxyAccessible::ImageSize()
-{
-  nsIntSize retVal;
-  Unused << mDoc->SendImageSize(mID, &retVal);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::StartOffset(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendStartOffset(mID, &retVal, aOk);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::EndOffset(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendEndOffset(mID, &retVal, aOk);
-  return retVal;
-}
-
-bool
-ProxyAccessible::IsLinkValid()
-{
-  bool retVal = false;
-  Unused << mDoc->SendIsLinkValid(mID, &retVal);
-  return retVal;
-}
-
-uint32_t
-ProxyAccessible::AnchorCount(bool* aOk)
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendAnchorCount(mID, &retVal, aOk);
-  return retVal;
-}
-
-void
-ProxyAccessible::AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk)
-{
-  Unused << mDoc->SendAnchorURIAt(mID, aIndex, &aURI, aOk);
-}
-
-ProxyAccessible*
-ProxyAccessible::AnchorAt(uint32_t aIndex)
-{
-  uint64_t id = 0;
-  bool ok = false;
-  Unused << mDoc->SendAnchorAt(mID, aIndex, &id, &ok);
-  return ok ? mDoc->GetAccessible(id) : nullptr;
-}
-
-uint32_t
-ProxyAccessible::LinkCount()
-{
-  uint32_t retVal = 0;
-  Unused << mDoc->SendLinkCount(mID, &retVal);
-  return retVal;
-}
-
-ProxyAccessible*
-ProxyAccessible::LinkAt(const uint32_t& aIndex)
-{
-  uint64_t linkID = 0;
-  bool ok = false;
-  Unused << mDoc->SendLinkAt(mID, aIndex, &linkID, &ok);
-  return ok ? mDoc->GetAccessible(linkID) : nullptr;
-}
-
-int32_t
-ProxyAccessible::LinkIndexOf(ProxyAccessible* aLink)
-{
-  int32_t retVal = -1;
-  if (aLink) {
-    Unused << mDoc->SendLinkIndexOf(mID, aLink->ID(), &retVal);
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return state;
   }
 
-  return retVal;
-}
-
-int32_t
-ProxyAccessible::LinkIndexAtOffset(uint32_t aOffset)
-{
-  int32_t retVal = -1;
-  Unused << mDoc->SendLinkIndexAtOffset(mID, aOffset, &retVal);
-  return retVal;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableOfACell()
-{
-  uint64_t tableID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableOfACell(mID, &tableID, &ok);
-  return ok ? mDoc->GetAccessible(tableID) : nullptr;
-}
-
-uint32_t
-ProxyAccessible::ColIdx()
-{
-  uint32_t index = 0;
-  Unused << mDoc->SendColIdx(mID, &index);
-  return index;
-}
-
-uint32_t
-ProxyAccessible::RowIdx()
-{
-  uint32_t index = 0;
-  Unused << mDoc->SendRowIdx(mID, &index);
-  return index;
-}
-
-uint32_t
-ProxyAccessible::ColExtent()
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendColExtent(mID, &extent);
-  return extent;
-}
-
-uint32_t
-ProxyAccessible::RowExtent()
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendRowExtent(mID, &extent);
-  return extent;
-}
-
-void
-ProxyAccessible::ColHeaderCells(nsTArray<ProxyAccessible*>* aCells)
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendColHeaderCells(mID, &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  for (size_t i = 0; i < targetCount; i++) {
-    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
-  }
-}
-
-void
-ProxyAccessible::RowHeaderCells(nsTArray<ProxyAccessible*>* aCells)
-{
-  nsTArray<uint64_t> targetIDs;
-  Unused << mDoc->SendRowHeaderCells(mID, &targetIDs);
-
-  size_t targetCount = targetIDs.Length();
-  for (size_t i = 0; i < targetCount; i++) {
-    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
-  }
-}
-
-bool
-ProxyAccessible::IsCellSelected()
-{
-  bool selected = false;
-  Unused << mDoc->SendIsCellSelected(mID, &selected);
-  return selected;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableCaption()
-{
-  uint64_t captionID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableCaption(mID, &captionID, &ok);
-  return ok ? mDoc->GetAccessible(captionID) : nullptr;
-}
-
-void
-ProxyAccessible::TableSummary(nsString& aSummary)
-{
-  Unused << mDoc->SendTableSummary(mID, &aSummary);
-}
-
-uint32_t
-ProxyAccessible::TableColumnCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableColumnCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableRowCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableRowCount(mID, &count);
-  return count;
-}
-
-ProxyAccessible*
-ProxyAccessible::TableCellAt(uint32_t aRow, uint32_t aCol)
-{
-  uint64_t cellID = 0;
-  bool ok = false;
-  Unused << mDoc->SendTableCellAt(mID, aRow, aCol, &cellID, &ok);
-  return ok ? mDoc->GetAccessible(cellID) : nullptr;
-}
-
-int32_t
-ProxyAccessible::TableCellIndexAt(uint32_t aRow, uint32_t aCol)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableCellIndexAt(mID, aRow, aCol, &index);
-  return index;
-}
-
-int32_t
-ProxyAccessible::TableColumnIndexAt(uint32_t aCellIndex)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableColumnIndexAt(mID, aCellIndex, &index);
-  return index;
-}
-
-int32_t
-ProxyAccessible::TableRowIndexAt(uint32_t aCellIndex)
-{
-  int32_t index = 0;
-  Unused << mDoc->SendTableRowIndexAt(mID, aCellIndex, &index);
-  return index;
-}
-
-void
-ProxyAccessible::TableRowAndColumnIndicesAt(uint32_t aCellIndex,
-                                            int32_t* aRow, int32_t* aCol)
-{
-  Unused << mDoc->SendTableRowAndColumnIndicesAt(mID, aCellIndex, aRow, aCol);
-}
-
-uint32_t
-ProxyAccessible::TableColumnExtentAt(uint32_t aRow, uint32_t aCol)
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendTableColumnExtentAt(mID, aRow, aCol, &extent);
-  return extent;
-}
-
-uint32_t
-ProxyAccessible::TableRowExtentAt(uint32_t aRow, uint32_t aCol)
-{
-  uint32_t extent = 0;
-  Unused << mDoc->SendTableRowExtentAt(mID, aRow, aCol, &extent);
-  return extent;
-}
-
-void
-ProxyAccessible::TableColumnDescription(uint32_t aCol, nsString& aDescription)
-{
-  Unused << mDoc->SendTableColumnDescription(mID, aCol, &aDescription);
-}
-
-void
-ProxyAccessible::TableRowDescription(uint32_t aRow, nsString& aDescription)
-{
-  Unused << mDoc->SendTableRowDescription(mID, aRow, &aDescription);
-}
-
-bool
-ProxyAccessible::TableColumnSelected(uint32_t aCol)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableColumnSelected(mID, aCol, &selected);
-  return selected;
-}
-
-bool
-ProxyAccessible::TableRowSelected(uint32_t aRow)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableRowSelected(mID, aRow, &selected);
-  return selected;
-}
-
-bool
-ProxyAccessible::TableCellSelected(uint32_t aRow, uint32_t aCol)
-{
-  bool selected = false;
-  Unused << mDoc->SendTableCellSelected(mID, aRow, aCol, &selected);
-  return selected;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedCellCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedCellCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedColumnCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedColumnCount(mID, &count);
-  return count;
-}
-
-uint32_t
-ProxyAccessible::TableSelectedRowCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendTableSelectedRowCount(mID, &count);
-  return count;
-}
-
-void
-ProxyAccessible::TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs)
-{
-  AutoTArray<uint64_t, 30> cellIDs;
-  Unused << mDoc->SendTableSelectedCells(mID, &cellIDs);
-  aCellIDs->SetCapacity(cellIDs.Length());
-  for (uint32_t i = 0; i < cellIDs.Length(); ++i) {
-    aCellIDs->AppendElement(mDoc->GetAccessible(cellIDs[i]));
+  VARIANT id;
+  id.vt = VT_I4;
+  id.lVal = CHILDID_SELF;
+  VARIANT varState;
+  HRESULT hr = acc->get_accState(id, &varState);
+  if (FAILED(hr)) {
+    return state;
   }
-}
-
-void
-ProxyAccessible::TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices)
-{
-  Unused << mDoc->SendTableSelectedCellIndices(mID, aCellIndices);
-}
-
-void
-ProxyAccessible::TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices)
-{
-  Unused << mDoc->SendTableSelectedColumnIndices(mID, aColumnIndices);
-}
-
-void
-ProxyAccessible::TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices)
-{
-  Unused << mDoc->SendTableSelectedRowIndices(mID, aRowIndices);
-}
-
-void
-ProxyAccessible::TableSelectColumn(uint32_t aCol)
-{
-  Unused << mDoc->SendTableSelectColumn(mID, aCol);
-}
-
-void
-ProxyAccessible::TableSelectRow(uint32_t aRow)
-{
-  Unused << mDoc->SendTableSelectRow(mID, aRow);
-}
-
-void
-ProxyAccessible::TableUnselectColumn(uint32_t aCol)
-{
-  Unused << mDoc->SendTableUnselectColumn(mID, aCol);
-}
-
-void
-ProxyAccessible::TableUnselectRow(uint32_t aRow)
-{
-  Unused << mDoc->SendTableUnselectRow(mID, aRow);
-}
-
-bool
-ProxyAccessible::TableIsProbablyForLayout()
-{
-  bool forLayout = false;
-  Unused << mDoc->SendTableIsProbablyForLayout(mID, &forLayout);
-  return forLayout;
-}
-
-ProxyAccessible*
-ProxyAccessible::AtkTableColumnHeader(int32_t aCol)
-{
-  uint64_t headerID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAtkTableColumnHeader(mID, aCol, &headerID, &ok);
-  return ok ? mDoc->GetAccessible(headerID) : nullptr;
-}
-
-ProxyAccessible*
-ProxyAccessible::AtkTableRowHeader(int32_t aRow)
-{
-  uint64_t headerID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAtkTableRowHeader(mID, aRow, &headerID, &ok);
-  return ok ? mDoc->GetAccessible(headerID) : nullptr;
-}
-
-void
-ProxyAccessible::SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems)
-{
-  AutoTArray<uint64_t, 10> itemIDs;
-  Unused << mDoc->SendSelectedItems(mID, &itemIDs);
-  aSelectedItems->SetCapacity(itemIDs.Length());
-  for (size_t i = 0; i < itemIDs.Length(); ++i) {
-    aSelectedItems->AppendElement(mDoc->GetAccessible(itemIDs[i]));
-  }
-}
-
-uint32_t
-ProxyAccessible::SelectedItemCount()
-{
-  uint32_t count = 0;
-  Unused << mDoc->SendSelectedItemCount(mID, &count);
-  return count;
-}
-
-ProxyAccessible*
-ProxyAccessible::GetSelectedItem(uint32_t aIndex)
-{
-  uint64_t selectedItemID = 0;
-  bool ok = false;
-  Unused << mDoc->SendGetSelectedItem(mID, aIndex, &selectedItemID, &ok);
-  return ok ? mDoc->GetAccessible(selectedItemID) : nullptr;
-}
-
-bool
-ProxyAccessible::IsItemSelected(uint32_t aIndex)
-{
-  bool selected = false;
-  Unused << mDoc->SendIsItemSelected(mID, aIndex, &selected);
-  return selected;
-}
- 
-bool
-ProxyAccessible::AddItemToSelection(uint32_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendAddItemToSelection(mID, aIndex, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::RemoveItemFromSelection(uint32_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendRemoveItemFromSelection(mID, aIndex, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::SelectAll()
-{
-  bool success = false;
-  Unused << mDoc->SendSelectAll(mID, &success);
-  return success;
-}
-
-bool
-ProxyAccessible::UnselectAll()
-{
-  bool success = false;
-  Unused << mDoc->SendUnselectAll(mID, &success);
-  return success;
-}
-
-void
-ProxyAccessible::TakeSelection()
-{
-  Unused << mDoc->SendTakeSelection(mID);
-}
-
-void
-ProxyAccessible::SetSelected(bool aSelect)
-{
-  Unused << mDoc->SendSetSelected(mID, aSelect);
-}
-
-bool
-ProxyAccessible::DoAction(uint8_t aIndex)
-{
-  bool success = false;
-  Unused << mDoc->SendDoAction(mID, aIndex, &success);
-  return success;
-}
-
-uint8_t
-ProxyAccessible::ActionCount()
-{
-  uint8_t count = 0;
-  Unused << mDoc->SendActionCount(mID, &count);
-  return count;
-}
-
-void
-ProxyAccessible::ActionDescriptionAt(uint8_t aIndex, nsString& aDescription)
-{
-  Unused << mDoc->SendActionDescriptionAt(mID, aIndex, &aDescription);
-}
-
-void
-ProxyAccessible::ActionNameAt(uint8_t aIndex, nsString& aName)
-{
-  Unused << mDoc->SendActionNameAt(mID, aIndex, &aName);
-}
-
-KeyBinding
-ProxyAccessible::AccessKey()
-{
-  uint32_t key = 0;
-  uint32_t modifierMask = 0;
-  Unused << mDoc->SendAccessKey(mID, &key, &modifierMask);
-  return KeyBinding(key, modifierMask);
-}
-
-KeyBinding
-ProxyAccessible::KeyboardShortcut()
-{
-  uint32_t key = 0;
-  uint32_t modifierMask = 0;
-  Unused << mDoc->SendKeyboardShortcut(mID, &key, &modifierMask);
-  return KeyBinding(key, modifierMask);
-}
-
-void
-ProxyAccessible::AtkKeyBinding(nsString& aBinding)
-{
-  Unused << mDoc->SendAtkKeyBinding(mID, &aBinding);
-}
-
-double
-ProxyAccessible::CurValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendCurValue(mID, &val);
-  return val;
-}
-
-bool
-ProxyAccessible::SetCurValue(double aValue)
-{
-  bool success = false;
-  Unused << mDoc->SendSetCurValue(mID, aValue, &success);
-  return success;
-}
-
-double
-ProxyAccessible::MinValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendMinValue(mID, &val);
-  return val;
-}
-
-double
-ProxyAccessible::MaxValue()
-{
-  double val = UnspecifiedNaN<double>();
-  Unused << mDoc->SendMaxValue(mID, &val);
-  return val;
-}
-
-double
-ProxyAccessible::Step()
-{
-  double step = UnspecifiedNaN<double>();
-  Unused << mDoc->SendStep(mID, &step);
-  return step;
-}
-
-void
-ProxyAccessible::TakeFocus()
-{
-  Unused << mDoc->SendTakeFocus(mID);
-}
-
-uint32_t
-ProxyAccessible::EmbeddedChildCount() const
-{
-  size_t count = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (mChildren[i]->IsEmbeddedObject()) {
-      count++;
-    }
-  }
-
-  return count;
-}
-
-int32_t
-ProxyAccessible::IndexOfEmbeddedChild(const ProxyAccessible* aChild)
-{
-  size_t index = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (mChildren[i]->IsEmbeddedObject()) {
-      if (mChildren[i] == aChild) {
-        return index;
-      }
-
-      index++;
-    }
-  }
-
-  return -1;
-}
-
-ProxyAccessible*
-ProxyAccessible::EmbeddedChildAt(size_t aChildIdx)
-{
-  size_t index = 0, kids = mChildren.Length();
-  for (size_t i = 0; i < kids; i++) {
-    if (!mChildren[i]->IsEmbeddedObject()) {
-      continue;
-    }
-
-    if (index == aChildIdx) {
-      return mChildren[i];
-    }
-
-    index++;
-  }
-
-  return nullptr;
-}
-
-ProxyAccessible*
-ProxyAccessible::FocusedChild()
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused << mDoc->SendFocusedChild(mID, &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
-}
-
-ProxyAccessible*
-ProxyAccessible::ChildAtPoint(int32_t aX, int32_t aY,
-                              Accessible::EWhichChildAtPoint aWhichChild)
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused << mDoc->SendAccessibleAtPoint(mID, aX, aY, false,
-                                        static_cast<uint32_t>(aWhichChild),
-                                        &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
+  return uint64_t(varState.lVal);
 }
 
 nsIntRect
 ProxyAccessible::Bounds()
 {
   nsIntRect rect;
-  Unused << mDoc->SendExtents(mID, false,
-                              &(rect.x), &(rect.y),
-                              &(rect.width), &(rect.height));
+
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return rect;
+  }
+
+  long left;
+  long top;
+  long width;
+  long height;
+  VARIANT id;
+  id.vt = VT_I4;
+  id.lVal = CHILDID_SELF;
+  HRESULT hr = acc->accLocation(&left, &top, &width, &height, id);
+  if (FAILED(hr)) {
+    return rect;
+  }
+  rect.x = left;
+  rect.y = top;
+  rect.width = width;
+  rect.height = height;
   return rect;
 }
 
 void
 ProxyAccessible::Language(nsString& aLocale)
 {
-  Unused << mDoc->SendLanguage(mID, &aLocale);
+  aLocale.Truncate();
+
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return;
+  }
+
+  RefPtr<IAccessible2> acc2;
+  if (FAILED(acc->QueryInterface(IID_IAccessible2, (void**)getter_AddRefs(acc2)))) {
+    return;
+  }
+
+  IA2Locale locale;
+  HRESULT hr = acc2->get_locale(&locale);
+
+  _bstr_t langWrap(locale.language, false);
+  _bstr_t countryWrap(locale.country, false);
+  _bstr_t variantWrap(locale.variant, false);
+
+  if (FAILED(hr)) {
+    return;
+  }
+
+  // The remaining code should essentially be the inverse of the
+  // ia2Accessible::get_locale conversion to IA2Locale.
+
+  if (!!variantWrap) {
+    aLocale = (wchar_t*)variantWrap;
+    return;
+  }
+
+  if (!!langWrap) {
+    aLocale = (wchar_t*)langWrap;
+    if (!!countryWrap) {
+      aLocale += L"-";
+      aLocale += (wchar_t*)countryWrap;
+    }
+  }
 }
 
-void
-ProxyAccessible::DocType(nsString& aType)
+static bool
+IsEscapedChar(const wchar_t c)
 {
-  Unused << mDoc->SendDocType(mID, &aType);
+  return c == L'\\' || c == L':' || c == ',' || c == '=' || c == ';';
 }
 
-void
-ProxyAccessible::Title(nsString& aTitle)
+static bool
+ConvertBSTRAttributesToArray(const nsAString& aStr,
+                             nsTArray<Attribute>* aAttrs)
 {
-  Unused << mDoc->SendTitle(mID, &aTitle);
-}
+  if (!aAttrs) {
+    return false;
+  }
+
+  enum
+  {
+    eName = 0,
+    eValue = 1,
+    eNumStates
+  } state;
+  nsAutoString tokens[eNumStates];
+  auto itr = aStr.BeginReading(), end = aStr.EndReading();
 
-void
-ProxyAccessible::URL(nsString& aURL)
-{
-  Unused << mDoc->SendURL(mID, &aURL);
-}
-
-void
-ProxyAccessible::MimeType(nsString aMime)
-{
-  Unused << mDoc->SendMimeType(mID, &aMime);
+  state = eName;
+  while (itr != end) {
+    switch (*itr) {
+      case L'\\':
+        // Skip the backslash so that we're looking at the escaped char
+        ++itr;
+        if (itr == end || !IsEscapedChar(*itr)) {
+          // Invalid state
+          return false;
+        }
+        break;
+      case L':':
+        if (state != eName) {
+          // Bad, should be looking at name
+          return false;
+        }
+        state = eValue;
+        ++itr;
+        continue;
+      case L';':
+        if (state != eValue) {
+          // Bad, should be looking at value
+          return false;
+        }
+        state = eName;
+        aAttrs->AppendElement(Attribute(NS_ConvertUTF16toUTF8(tokens[eName]),
+                                        tokens[eValue]));
+        tokens[eName].Truncate();
+        tokens[eValue].Truncate();
+        ++itr;
+        continue;
+      default:
+        break;
+    }
+    tokens[state] += *itr;
+  }
+  return true;
 }
 
 void
-ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
-                                    nsString& aMimeType)
+ProxyAccessible::Attributes(nsTArray<Attribute>* aAttrs) const
 {
-  Unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType);
-}
+  aAttrs->Clear();
+
+  RefPtr<IAccessible> acc;
+  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return;
+  }
 
-ProxyAccessible*
-ProxyAccessible::AccessibleAtPoint(int32_t aX, int32_t aY,
-                                   bool aNeedsScreenCoords)
-{
-  uint64_t childID = 0;
-  bool ok = false;
-  Unused <<
-    mDoc->SendAccessibleAtPoint(mID, aX, aY, aNeedsScreenCoords,
-                                static_cast<uint32_t>(Accessible::eDirectChild),
-                                &childID, &ok);
-  return ok ? mDoc->GetAccessible(childID) : nullptr;
+  RefPtr<IAccessible2> acc2;
+  if (FAILED(acc->QueryInterface(IID_IAccessible2, (void**)getter_AddRefs(acc2)))) {
+    return;
+  }
+
+  BSTR attrs;
+  HRESULT hr = acc2->get_attributes(&attrs);
+  _bstr_t attrsWrap(attrs, false);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  ConvertBSTRAttributesToArray(nsDependentString((wchar_t*)attrs,
+                                                 attrsWrap.length()),
+                               aAttrs);
 }
 
-void
-ProxyAccessible::Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
-                        int32_t* aWidth, int32_t* aHeight)
-{
-  Unused << mDoc->SendExtents(mID, aNeedsScreenCoords, aX, aY, aWidth, aHeight);
-}
-
-void
-ProxyAccessible::DOMNodeID(nsString& aID)
-{
-  Unused << mDoc->SendDOMNodeID(mID, &aID);
-}
-
-Accessible*
-ProxyAccessible::OuterDocOfRemoteBrowser() const
-{
-  auto tab = static_cast<dom::TabParent*>(mDoc->Manager());
-  dom::Element* frame = tab->GetOwnerElement();
-  NS_ASSERTION(frame, "why isn't the tab in a frame!");
-  if (!frame)
-    return nullptr;
-
-  DocAccessible* chromeDoc = GetExistingDocAccessible(frame->OwnerDoc());
-
-  return chromeDoc ? chromeDoc->GetAccessible(frame) : nullptr;
-}
-}
-}
+} // namespace a11y
+} // namespace mozilla
copy from accessible/ipc/ProxyAccessible.h
copy to accessible/ipc/win/ProxyAccessible.h
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/win/ProxyAccessible.h
@@ -2,453 +2,87 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_ProxyAccessible_h
 #define mozilla_a11y_ProxyAccessible_h
 
+#include "Accessible.h"
+#include "mozilla/a11y/ProxyAccessibleBase.h"
 #include "mozilla/a11y/Role.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleTypes.h"
-#include "Accessible.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsRect.h"
-#include "Accessible.h"
+
+#include <oleacc.h>
 
 namespace mozilla {
 namespace a11y {
 
-class Accessible;
-class Attribute;
-class DocAccessibleParent;
-enum class RelationType;
-
-enum Interfaces
-{
-  HYPERTEXT = 1,
-  HYPERLINK = 1 << 1,
-  IMAGE = 1 << 2,
-  VALUE = 1 << 3,
-  TABLE = 1 << 4,
-  TABLECELL = 1 << 5,
-  DOCUMENT = 1 << 6,
-  SELECTION = 1 << 7,
-  ACTION = 1 << 8,
-};
-
-class ProxyAccessible
+class ProxyAccessible : public ProxyAccessibleBase<ProxyAccessible>
 {
 public:
+  ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
+                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces,
+                  const RefPtr<IAccessible>& aIAccessible)
+    : ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces)
+    , mCOMProxy(aIAccessible)
 
-  ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
-                  DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) :
-     mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
-     mOuterDoc(false), mIsDoc(false),
-     mHasValue(aInterfaces & Interfaces::VALUE),
-     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK),
-     mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
+
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
-    MOZ_ASSERT(!mWrapper);
-  }
-
-  void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
-  { mChildren.InsertElementAt(aIdx, aChild); }
-
-  uint32_t ChildrenCount() const { return mChildren.Length(); }
-  ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
-  ProxyAccessible* FirstChild() const
-    { return mChildren.Length() ? mChildren[0] : nullptr; }
-  ProxyAccessible* LastChild() const
-    { return mChildren.Length() ? mChildren[mChildren.Length() - 1] : nullptr; }
-  ProxyAccessible* PrevSibling() const
-  {
-    size_t idx = IndexInParent();
-    return idx > 0 ? Parent()->mChildren[idx - 1] : nullptr;
-  }
-  ProxyAccessible* NextSibling() const
-  {
-    size_t idx = IndexInParent();
-    return idx + 1 < Parent()->mChildren.Length() ? Parent()->mChildren[idx + 1]
-    : nullptr;
-  }
-
-  // XXX evaluate if this is fast enough.
-  size_t IndexInParent() const { return Parent()->mChildren.IndexOf(this); }
-  uint32_t EmbeddedChildCount() const;
-  int32_t IndexOfEmbeddedChild(const ProxyAccessible*);
-  ProxyAccessible* EmbeddedChildAt(size_t aChildIdx);
-  bool MustPruneChildren() const;
-
-  void Shutdown();
-
-  void SetChildDoc(DocAccessibleParent*);
-
-  /**
-   * Remove The given child.
-   */
-  void RemoveChild(ProxyAccessible* aChild)
-    { mChildren.RemoveElement(aChild); }
-
-  /**
-   * Return the proxy for the parent of the wrapped accessible.
-   */
-  ProxyAccessible* Parent() const { return mParent; }
-
-  Accessible* OuterDocOfRemoteBrowser() const;
-
-  /**
-   * Get the role of the accessible we're proxying.
-   */
-  role Role() const { return mRole; }
-
-  /**
-   * Return true if this is an embedded object.
-   */
-  bool IsEmbeddedObject() const
-  {
-    role role = Role();
-    return role != roles::TEXT_LEAF &&
-           role != roles::WHITESPACE &&
-           role != roles::STATICTEXT;
   }
 
   /*
    * Return the states for the proxied accessible.
    */
   uint64_t State() const;
 
   /*
-   * Return the native states for the proxied accessible.
-   */
-  uint64_t NativeState() const;
-
-  /*
    * Set aName to the name of the proxied accessible.
    */
   void Name(nsString& aName) const;
 
   /*
    * Set aValue to the value of the proxied accessible.
    */
   void Value(nsString& aValue) const;
 
-  /*
-   * Set aHelp to the help string of the proxied accessible.
-   */
-  void Help(nsString& aHelp) const;
-
   /**
    * Set aDesc to the description of the proxied accessible.
    */
   void Description(nsString& aDesc) const;
 
   /**
    * Get the set of attributes on the proxied accessible.
    */
   void Attributes(nsTArray<Attribute> *aAttrs) const;
 
-  /**
-   * Return set of targets of given relation type.
-   */
-  nsTArray<ProxyAccessible*> RelationByType(RelationType aType) const;
-
-  /**
-   * Get all relations for this accessible.
-   */
-  void Relations(nsTArray<RelationType>* aTypes,
-                 nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets) const;
-
-  bool IsSearchbox() const;
-
-  nsIAtom* LandmarkRole() const;
-
-  nsIAtom* ARIARoleAtom() const;
-
-  int32_t GetLevelInternal();
-  void ScrollTo(uint32_t aScrollType);
-  void ScrollToPoint(uint32_t aScrollType, int32_t aX, int32_t aY);
-
-  int32_t CaretLineNumber();
-  int32_t CaretOffset();
-  void SetCaretOffset(int32_t aOffset);
-
-  int32_t CharacterCount();
-  int32_t SelectionCount();
-
-  /**
-   * Get the text between the given offsets.
-   */
-  bool TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
-                     nsString& aText) const;
-
-  void GetTextAfterOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                          nsString& aText, int32_t* aStartOffset,
-                          int32_t* aEndOffset);
-
-  void GetTextAtOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                       nsString& aText, int32_t* aStartOffset,
-                       int32_t* aEndOffset);
-
-  void GetTextBeforeOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
-                           nsString& aText, int32_t* aStartOffset,
-                           int32_t* aEndOffset);
-
-  char16_t CharAt(int32_t aOffset);
-
-  void TextAttributes(bool aIncludeDefAttrs,
-                      const int32_t aOffset,
-                      nsTArray<Attribute>* aAttributes,
-                      int32_t* aStartOffset,
-                      int32_t* aEndOffset);
-  void DefaultTextAttributes(nsTArray<Attribute>* aAttrs);
-
-  nsIntRect TextBounds(int32_t aStartOffset, int32_t aEndOffset,
-                       uint32_t aCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
-
-  nsIntRect CharBounds(int32_t aOffset, uint32_t aCoordType);
-
-  int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType);
-
-  bool SelectionBoundsAt(int32_t aSelectionNum,
-                         nsString& aData,
-                         int32_t* aStartOffset,
-                         int32_t* aEndOffset);
-
-  bool SetSelectionBoundsAt(int32_t aSelectionNum,
-                            int32_t aStartOffset,
-                            int32_t aEndOffset);
-
-  bool AddToSelection(int32_t aStartOffset,
-                      int32_t aEndOffset);
-
-  bool RemoveFromSelection(int32_t aSelectionNum);
-
-  void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
-                         uint32_t aScrollType);
-
-  void ScrollSubstringToPoint(int32_t aStartOffset,
-                              int32_t aEndOffset,
-                              uint32_t aCoordinateType,
-                              int32_t aX, int32_t aY);
-
-  void Text(nsString* aText);
-
-  void ReplaceText(const nsString& aText);
-
-  bool InsertText(const nsString& aText, int32_t aPosition);
-
-  bool CopyText(int32_t aStartPos, int32_t aEndPos);
-
-  bool CutText(int32_t aStartPos, int32_t aEndPos);
-
-  bool DeleteText(int32_t aStartPos, int32_t aEndPos);
-
-  bool PasteText(int32_t aPosition);
-
-  nsIntPoint ImagePosition(uint32_t aCoordType);
-
-  nsIntSize ImageSize();
-
-  uint32_t StartOffset(bool* aOk);
-
-  uint32_t EndOffset(bool* aOk);
-
-  bool IsLinkValid();
-
-  // XXX checking mRole alone may not result in same behavior as Accessibles
-  // due to ARIA roles. See bug 1210477.
-  inline bool IsTable() const
-  {
-    return mRole == roles::TABLE || mRole == roles::MATHML_TABLE;
-  }
-  inline bool IsTableRow() const
-  {
-    return (mRole == roles::ROW ||
-            mRole == roles::MATHML_TABLE_ROW ||
-            mRole == roles::MATHML_LABELED_ROW);
-  }
-  inline bool IsTableCell() const
-  {
-    return (mRole == roles::CELL ||
-            mRole == roles::COLUMNHEADER ||
-            mRole == roles::ROWHEADER ||
-            mRole == roles::GRID_CELL ||
-            mRole == roles::MATHML_CELL);
-  }
-
-  uint32_t AnchorCount(bool* aOk);
-
-  void AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk);
-
-  ProxyAccessible* AnchorAt(uint32_t aIndex);
-
-  uint32_t LinkCount();
-
-  ProxyAccessible* LinkAt(const uint32_t& aIndex);
-
-  int32_t LinkIndexOf(ProxyAccessible* aLink);
-
-  int32_t LinkIndexAtOffset(uint32_t aOffset);
-
-  ProxyAccessible* TableOfACell();
-
-  uint32_t ColIdx();
-
-  uint32_t RowIdx();
-
-  uint32_t ColExtent();
-
-  uint32_t RowExtent();
-
-  void ColHeaderCells(nsTArray<ProxyAccessible*>* aCells);
-
-  void RowHeaderCells(nsTArray<ProxyAccessible*>* aCells);
-
-  bool IsCellSelected();
-
-  ProxyAccessible* TableCaption();
-  void TableSummary(nsString& aSummary);
-  uint32_t TableColumnCount();
-  uint32_t TableRowCount();
-  ProxyAccessible* TableCellAt(uint32_t aRow, uint32_t aCol);
-  int32_t TableCellIndexAt(uint32_t aRow, uint32_t aCol);
-  int32_t TableColumnIndexAt(uint32_t aCellIndex);
-  int32_t TableRowIndexAt(uint32_t aCellIndex);
-  void TableRowAndColumnIndicesAt(uint32_t aCellIndex,
-                                  int32_t* aRow, int32_t* aCol);
-  uint32_t TableColumnExtentAt(uint32_t aRow, uint32_t aCol);
-  uint32_t TableRowExtentAt(uint32_t aRow, uint32_t aCol);
-  void TableColumnDescription(uint32_t aCol, nsString& aDescription);
-  void TableRowDescription(uint32_t aRow, nsString& aDescription);
-  bool TableColumnSelected(uint32_t aCol);
-  bool TableRowSelected(uint32_t aRow);
-  bool TableCellSelected(uint32_t aRow, uint32_t aCol);
-  uint32_t TableSelectedCellCount();
-  uint32_t TableSelectedColumnCount();
-  uint32_t TableSelectedRowCount();
-  void TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs);
-  void TableSelectedCellIndices(nsTArray<uint32_t>* aCellIndices);
-  void TableSelectedColumnIndices(nsTArray<uint32_t>* aColumnIndices);
-  void TableSelectedRowIndices(nsTArray<uint32_t>* aRowIndices);
-  void TableSelectColumn(uint32_t aCol);
-  void TableSelectRow(uint32_t aRow);
-  void TableUnselectColumn(uint32_t aCol);
-  void TableUnselectRow(uint32_t aRow);
-  bool TableIsProbablyForLayout();
-  ProxyAccessible* AtkTableColumnHeader(int32_t aCol);
-  ProxyAccessible* AtkTableRowHeader(int32_t aRow);
-
-  void SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems);
-  uint32_t SelectedItemCount();
-  ProxyAccessible* GetSelectedItem(uint32_t aIndex);
-  bool IsItemSelected(uint32_t aIndex);
-  bool AddItemToSelection(uint32_t aIndex);
-  bool RemoveItemFromSelection(uint32_t aIndex);
-  bool SelectAll();
-  bool UnselectAll();
-
-  void TakeSelection();
-  void SetSelected(bool aSelect);
-
-  bool DoAction(uint8_t aIndex);
-  uint8_t ActionCount();
-  void ActionDescriptionAt(uint8_t aIndex, nsString& aDescription);
-  void ActionNameAt(uint8_t aIndex, nsString& aName);
-  KeyBinding AccessKey();
-  KeyBinding KeyboardShortcut();
-  void AtkKeyBinding(nsString& aBinding);
-
-  double CurValue();
-  bool SetCurValue(double aValue);
-  double MinValue();
-  double MaxValue();
-  double Step();
-
-  void TakeFocus();
-  ProxyAccessible* FocusedChild();
-  ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
-                                Accessible::EWhichChildAtPoint aWhichChild);
   nsIntRect Bounds();
 
   void Language(nsString& aLocale);
-  void DocType(nsString& aType);
-  void Title(nsString& aTitle);
-  void URL(nsString& aURL);
-  void MimeType(nsString aMime);
-  void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
-                          nsString& aMimeType);
 
-  ProxyAccessible* AccessibleAtPoint(int32_t aX, int32_t aY,
-                                     bool aNeedsScreenCoords);
-
-  void Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
-               int32_t* aWidth, int32_t* aHeight);
-
-  /**
-   * Return the id of the dom node this accessible represents.  Note this
-   * should probably only be used for testing.
-   */
-  void DOMNodeID(nsString& aID);
-
-  /**
-   * Allow the platform to store a pointers worth of data on us.
-   */
-  uintptr_t GetWrapper() const { return mWrapper; }
-  void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
-
-  /*
-   * Return the ID of the accessible being proxied.
-   */
-  uint64_t ID() const { return mID; }
-
-  /**
-   * Return the document containing this proxy, or the proxy itself if it is a
-   * document.
-   */
-  DocAccessibleParent* Document() const { return mDoc; }
-
-  /**
-   * Return true if this proxy is a DocAccessibleParent.
-   */
-  bool IsDoc() const { return mIsDoc; }
-  DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
+  bool GetCOMInterface(void** aOutAccessible) const;
 
 protected:
-  explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
-    mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
-    mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
-    mIsHyperLink(false), mIsHyperText(false)
-  { MOZ_COUNT_CTOR(ProxyAccessible); }
+  explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc)
+    : ProxyAccessibleBase(aThisAsDoc)
+  { MOZ_COUNT_CTOR(ProxyAccessibleBase); }
 
-protected:
-  ProxyAccessible* mParent;
+  void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible)
+  { mCOMProxy = aIAccessible; }
 
 private:
-  nsTArray<ProxyAccessible*> mChildren;
-  DocAccessibleParent* mDoc;
-  uintptr_t mWrapper;
-  uint64_t mID;
-protected:
-  // XXX DocAccessibleParent gets to change this to change the role of
-  // documents.
-  role mRole : 27;
-private:
-  bool mOuterDoc : 1;
-
-public:
-  const bool mIsDoc: 1;
-  const bool mHasValue: 1;
-  const bool mIsHyperLink: 1;
-  const bool mIsHyperText: 1;
+  RefPtr<IAccessible> mCOMProxy;
 };
 
 }
 }
 
 #endif
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/moz.build
@@ -0,0 +1,38 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ['typelib']
+
+IPDL_SOURCES += ['PDocAccessible.ipdl']
+
+# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
+# the C++.
+if CONFIG['ACCESSIBILITY']:
+    EXPORTS.mozilla.a11y += [
+        'COMPtrTypes.h',
+        'DocAccessibleChild.h',
+        'PlatformChild.h',
+        'ProxyAccessible.h'
+    ]
+
+    SOURCES += [
+        'COMPtrTypes.cpp',
+        'DocAccessibleChild.cpp',
+        'PlatformChild.cpp',
+        'ProxyAccessible.cpp',
+    ]
+
+    LOCAL_INCLUDES += [
+        '/accessible/base',
+        '/accessible/generic',
+        '/accessible/windows/ia2',
+        '/accessible/windows/msaa',
+        '/accessible/xpcom',
+    ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/typelib/Accessible.idl
@@ -0,0 +1,16 @@
+/* -*- 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/. */
+
+import "oaidl.idl";
+import "servprov.idl";
+
+[uuid(b4d37cda-0dac-45e6-b613-158a5eb94293)]
+library Accessible
+{
+  interface IEnumVARIANT;
+  interface IServiceProvider;
+};
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/typelib/Makefile.in
@@ -0,0 +1,31 @@
+# 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/.
+
+GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
+
+MIDL_GENERATED_FILES = \
+  Accessible.h \
+  Accessible_i.c \
+  Accessible_p.c \
+  Accessible.tlb \
+  $(NULL)
+
+$(MIDL_GENERATED_FILES): done_gen
+
+done_gen: Accessible.idl
+	$(MIDL) $(MIDL_FLAGS) -Oicf $(srcdir)/Accessible.idl
+	touch $@
+
+export:: done_gen
+
+midl_exports := \
+  Accessible.tlb \
+  $(NULL)
+
+INSTALL_TARGETS += midl_exports
+midl_exports_FILES := $(midl_exports)
+midl_exports_DEST = $(DIST)/bin
+midl_exports_TARGET := export
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/typelib/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_TARGET_FILES += [
+    '!Accessible.tlb',
+]
+
+GENERATED_FILES += [
+    'Accessible.tlb',
+]
--- a/accessible/mac/moz.build
+++ b/accessible/mac/moz.build
@@ -27,16 +27,17 @@ UNIFIED_SOURCES += [
     'RootAccessibleWrap.mm',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
+    '/accessible/ipc/other',
     '/accessible/xul',
     '/layout/generic',
     '/layout/xul',
     '/widget',
     '/widget/cocoa',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/accessible/windows/ProxyWrappers.h
+++ b/accessible/windows/ProxyWrappers.h
@@ -10,45 +10,55 @@
 
 #include "HyperTextAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 class ProxyAccessibleWrap : public AccessibleWrap
 {
-  public:
+public:
   ProxyAccessibleWrap(ProxyAccessible* aProxy) :
     AccessibleWrap(nullptr, nullptr)
   {
     mType = eProxyType;
     mBits.proxy = aProxy;
   }
 
   virtual void Shutdown() override
   {
     mBits.proxy = nullptr;
     mStateFlags |= eIsDefunct;
   }
+
+  virtual void GetNativeInterface(void** aOutAccessible) override
+  {
+    mBits.proxy->GetCOMInterface(aOutAccessible);
+  }
 };
 
 class HyperTextProxyAccessibleWrap : public HyperTextAccessibleWrap
 {
 public:
   HyperTextProxyAccessibleWrap(ProxyAccessible* aProxy) :
     HyperTextAccessibleWrap(nullptr, nullptr)
   {
     mType = eProxyType;
     mBits.proxy = aProxy;
   }
 
   virtual void Shutdown() override
   {
     mBits.proxy = nullptr;
- mStateFlags |= eIsDefunct;
+    mStateFlags |= eIsDefunct;
+  }
+
+  virtual void GetNativeInterface(void** aOutAccessible) override
+  {
+    mBits.proxy->GetCOMInterface(aOutAccessible);
   }
 };
 
 class DocProxyAccessibleWrap : public HyperTextProxyAccessibleWrap
 {
 public:
   DocProxyAccessibleWrap(ProxyAccessible* aProxy) :
     HyperTextProxyAccessibleWrap(aProxy)
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -68,24 +68,17 @@ ia2Accessible::get_nRelations(long* aNRe
   if (!aNRelations)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (acc->IsProxy()) {
-    // XXX evaluate performance of collecting all relation targets.
-    nsTArray<RelationType> types;
-    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
-    acc->Proxy()->Relations(&types, &targetSets);
-    *aNRelations = types.Length();
-    return S_OK;
-  }
+  MOZ_ASSERT(!acc->IsProxy());
 
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     Relation rel = acc->RelationByType(sRelationTypePairs[idx].first);
     if (rel.Next())
       (*aNRelations)++;
@@ -104,43 +97,17 @@ ia2Accessible::get_relation(long aRelati
   if (!aRelation || aRelationIndex < 0)
     return E_INVALIDARG;
   *aRelation = nullptr;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (acc->IsProxy()) {
-    nsTArray<RelationType> types;
-    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
-    acc->Proxy()->Relations(&types, &targetSets);
-
-    size_t targetSetCount = targetSets.Length();
-    for (size_t i = 0; i < targetSetCount; i++) {
-      uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
-      MOZ_ASSERT(sRelationTypePairs[relTypeIdx].first == types[i]);
-      if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
-        continue;
-
-      if (static_cast<size_t>(aRelationIndex) == i) {
-        nsTArray<RefPtr<Accessible>> targets;
-        size_t targetCount = targetSets[i].Length();
-        for (size_t j = 0; j < targetCount; j++)
-          targets.AppendElement(WrapperFor(targetSets[i][j]));
-
-        RefPtr<ia2AccessibleRelation> rel =
-          new ia2AccessibleRelation(types[i], Move(targets));
-        rel.forget(aRelation);
-        return S_OK;
-      }
-    }
-
-    return E_INVALIDARG;
-  }
+  MOZ_ASSERT(!acc->IsProxy());
 
   long relIdx = 0;
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
@@ -171,43 +138,17 @@ ia2Accessible::get_relations(long aMaxRe
   if (!aRelation || !aNRelations || aMaxRelations <= 0)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (acc->IsProxy()) {
-    nsTArray<RelationType> types;
-    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
-    acc->Proxy()->Relations(&types, &targetSets);
-
-    size_t count = std::min(targetSets.Length(),
-                            static_cast<size_t>(aMaxRelations));
-    size_t i = 0;
-    while (i < count) {
-      uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
-      if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
-        continue;
-
-      size_t targetCount = targetSets[i].Length();
-      nsTArray<RefPtr<Accessible>> targets(targetCount);
-      for (size_t j = 0; j < targetCount; j++)
-        targets.AppendElement(WrapperFor(targetSets[i][j]));
-
-      RefPtr<ia2AccessibleRelation> rel =
-        new ia2AccessibleRelation(types[i], Move(targets));
-      rel.forget(aRelation + i);
-      i++;
-    }
-
-    *aNRelations = i;
-    return S_OK;
-  }
+  MOZ_ASSERT(!acc->IsProxy());
 
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) &&
        *aNRelations < aMaxRelations; idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
@@ -238,62 +179,52 @@ ia2Accessible::role(long* aRole)
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     *aRole = ia2Role; \
     break;
 
   a11y::role geckoRole;
-  if (acc->IsProxy())
-    geckoRole = acc->Proxy()->Role();
-  else
-    geckoRole = acc->Role();
+  MOZ_ASSERT(!acc->IsProxy());
+  geckoRole = acc->Role();
   switch (geckoRole) {
 #include "RoleMap.h"
     default:
       MOZ_CRASH("Unknown role.");
   }
 
 #undef ROLE
 
   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call
   // the IA2 role a ROLE_OUTLINEITEM.
-  if (acc->IsProxy()) {
-    if (geckoRole == roles::ROW && acc->Proxy()->Parent() &&
-        acc->Proxy()->Parent()->Role() == roles::TREE_TABLE)
+  MOZ_ASSERT(!acc->IsProxy());
+  if (geckoRole == roles::ROW) {
+    Accessible* xpParent = acc->Parent();
+    if (xpParent && xpParent->Role() == roles::TREE_TABLE)
       *aRole = ROLE_SYSTEM_OUTLINEITEM;
-  } else {
-    if (geckoRole == roles::ROW) {
-      Accessible* xpParent = acc->Parent();
-      if (xpParent && xpParent->Role() == roles::TREE_TABLE)
-        *aRole = ROLE_SYSTEM_OUTLINEITEM;
-    }
   }
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2Accessible::scrollTo(enum IA2ScrollType aScrollType)
 {
   A11Y_TRYBLOCK_BEGIN
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (acc->IsProxy()) {
-    acc->Proxy()->ScrollTo(aScrollType);
-  } else {
-    nsCoreUtils::ScrollTo(acc->Document()->PresShell(), acc->GetContent(),
-                          aScrollType);
-  }
+  MOZ_ASSERT(!acc->IsProxy());
+  nsCoreUtils::ScrollTo(acc->Document()->PresShell(), acc->GetContent(),
+                        aScrollType);
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType,
@@ -304,21 +235,18 @@ ia2Accessible::scrollToPoint(enum IA2Coo
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
     nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
     nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
 
-  if (acc->IsProxy()) {
-    acc->Proxy()->ScrollToPoint(geckoCoordType, aX, aY);
-  } else {
-    acc->ScrollToPoint(geckoCoordType, aX, aY);
-  }
+  MOZ_ASSERT(!acc->IsProxy());
+  acc->ScrollToPoint(geckoCoordType, aX, aY);
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2Accessible::get_groupPosition(long* aGroupLevel,
@@ -368,20 +296,18 @@ ia2Accessible::get_states(AccessibleStat
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct()) {
     *aStates = IA2_STATE_DEFUNCT;
     return S_OK;
   }
 
   uint64_t state;
-  if (acc->IsProxy())
-    state = acc->Proxy()->State();
-  else
-    state = acc->State();
+  MOZ_ASSERT(!acc->IsProxy());
+  state = acc->State();
 
   if (state & states::INVALID)
     *aStates |= IA2_STATE_INVALID_ENTRY;
   if (state & states::REQUIRED)
     *aStates |= IA2_STATE_REQUIRED;
 
   // The following IA2 states are not supported by Gecko
   // IA2_STATE_ARMED
@@ -543,20 +469,18 @@ ia2Accessible::get_indexInParent(long* a
   if (!aIndexInParent)
     return E_INVALIDARG;
   *aIndexInParent = -1;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (acc->IsProxy())
-    *aIndexInParent = acc->Proxy()->IndexInParent();
-  else
-    *aIndexInParent = acc->IndexInParent();
+  MOZ_ASSERT(!acc->IsProxy());
+  *aIndexInParent = acc->IndexInParent();
 
   if (*aIndexInParent == -1)
     return S_FALSE;
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -627,19 +551,18 @@ ia2Accessible::get_attributes(BSTR* aAtt
 
   // The format is name:value;name:value; with \ for escaping these
   // characters ":;=,\".
   if (!acc->IsProxy()) {
     nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
     return ConvertToIA2Attributes(attributes, aAttributes);
   }
 
-  nsTArray<Attribute> attrs;
-  acc->Proxy()->Attributes(&attrs);
-  return ConvertToIA2Attributes(&attrs, aAttributes);
+  MOZ_ASSERT(!acc->IsProxy());
+  return E_UNEXPECTED;
 
   A11Y_TRYBLOCK_END
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // IAccessible2_2
 
 STDMETHODIMP
@@ -714,31 +637,22 @@ ia2Accessible::get_relationTargetsOfType
   if (!relationType)
     return E_INVALIDARG;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsTArray<Accessible*> targets;
-  if (acc->IsProxy()) {
-    nsTArray<ProxyAccessible*> targetProxies =
-      acc->Proxy()->RelationByType(*relationType);
-
-    size_t targetCount = aMaxTargets;
-    if (targetProxies.Length() < targetCount)
-      targetCount = targetProxies.Length();
-    for (size_t i = 0; i < targetCount; i++)
-      targets.AppendElement(WrapperFor(targetProxies[i]));
-  } else {
-    Relation rel = acc->RelationByType(*relationType);
-    Accessible* target = nullptr;
-    while ((target = rel.Next()) &&
-           static_cast<long>(targets.Length()) <= aMaxTargets)
-      targets.AppendElement(target);
+  MOZ_ASSERT(!acc->IsProxy());
+  Relation rel = acc->RelationByType(*relationType);
+  Accessible* target = nullptr;
+  while ((target = rel.Next()) &&
+         static_cast<long>(targets.Length()) <= aMaxTargets) {
+    targets.AppendElement(target);
   }
 
   *aNTargets = targets.Length();
   *aTargets = static_cast<IUnknown**>(
     ::CoTaskMemAlloc(sizeof(IUnknown*) * *aNTargets));
   if (!*aTargets)
     return E_OUTOFMEMORY;
 
--- a/accessible/windows/ia2/ia2AccessibleEditableText.cpp
+++ b/accessible/windows/ia2/ia2AccessibleEditableText.cpp
@@ -19,19 +19,17 @@ using namespace mozilla::a11y;
 
 // IAccessibleEditableText
 
 STDMETHODIMP
 ia2AccessibleEditableText::copyText(long aStartOffset, long aEndOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->CopyText(aStartOffset, aEndOffset) ? S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidRange(aStartOffset, aEndOffset))
     return E_INVALIDARG;
 
@@ -41,19 +39,17 @@ ia2AccessibleEditableText::copyText(long
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleEditableText::deleteText(long aStartOffset, long aEndOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-    if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-      return proxy->DeleteText(aStartOffset, aEndOffset) ? S_OK : E_INVALIDARG;
-    }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidRange(aStartOffset, aEndOffset))
     return E_INVALIDARG;
 
@@ -65,19 +61,17 @@ ia2AccessibleEditableText::deleteText(lo
 
 STDMETHODIMP
 ia2AccessibleEditableText::insertText(long aOffset, BSTR *aText)
 {
   A11Y_TRYBLOCK_BEGIN
 
   uint32_t length = ::SysStringLen(*aText);
   nsAutoString text(*aText, length);
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->InsertText(text, aOffset) ? S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidOffset(aOffset))
     return E_INVALIDARG;
 
@@ -87,19 +81,17 @@ ia2AccessibleEditableText::insertText(lo
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleEditableText::cutText(long aStartOffset, long aEndOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->CutText(aStartOffset, aEndOffset) ? S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidRange(aStartOffset, aEndOffset))
     return E_INVALIDARG;
 
@@ -109,19 +101,17 @@ ia2AccessibleEditableText::cutText(long 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleEditableText::pasteText(long aOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->PasteText(aOffset) ? S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidOffset(aOffset))
     return E_INVALIDARG;
 
--- a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
@@ -48,27 +48,17 @@ ia2AccessibleHyperlink::get_anchor(long 
   A11Y_TRYBLOCK_BEGIN
 
   if (!aAnchor)
     return E_INVALIDARG;
 
   VariantInit(aAnchor);
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
-  if (thisObj->IsProxy()) {
-    ProxyAccessible* anchor = thisObj->Proxy()->AnchorAt(aIndex);
-    if (!anchor)
-      return S_FALSE;
-
-    IUnknown* tmp = static_cast<IAccessibleHyperlink*>(WrapperFor(anchor));
-    tmp->AddRef();
-    aAnchor->punkVal = tmp;
-    aAnchor->vt = VT_UNKNOWN;
-    return S_OK;
-  }
+  MOZ_ASSERT(!thisObj->IsProxy());
 
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
     return E_INVALIDARG;
 
   if (!thisObj->IsLink())
@@ -92,46 +82,45 @@ ia2AccessibleHyperlink::get_anchor(long 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (!aAnchorTarget)
+  if (!aAnchorTarget) {
     return E_INVALIDARG;
+  }
 
   VariantInit(aAnchorTarget);
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   nsAutoCString uriStr;
-  if (thisObj->IsProxy()) {
-    bool ok;
-    thisObj->Proxy()->AnchorURIAt(aIndex, uriStr, &ok);
-    if (!ok)
-      return S_FALSE;
+  MOZ_ASSERT(!thisObj->IsProxy());
+  if (thisObj->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-  } else {
-    if (thisObj->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount())) {
+    return E_INVALIDARG;
+  }
 
-    if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
-      return E_INVALIDARG;
-
-    if (!thisObj->IsLink())
-      return S_FALSE;
+  if (!thisObj->IsLink()) {
+    return S_FALSE;
+  }
 
-    nsCOMPtr<nsIURI> uri = thisObj->AnchorURIAt(aIndex);
-    if (!uri)
-      return S_FALSE;
+  nsCOMPtr<nsIURI> uri = thisObj->AnchorURIAt(aIndex);
+  if (!uri) {
+    return S_FALSE;
+  }
 
-    nsresult rv = uri->GetSpec(uriStr);
-    if (NS_FAILED(rv))
-      return GetHRESULT(rv);
+  nsresult rv = uri->GetSpec(uriStr);
+  if (NS_FAILED(rv)) {
+    return GetHRESULT(rv);
   }
 
   nsAutoString stringURI;
   AppendUTF8toUTF16(uriStr, stringURI);
 
   aAnchorTarget->vt = VT_BSTR;
   aAnchorTarget->bstrVal = ::SysAllocStringLen(stringURI.get(),
                                                stringURI.Length());
@@ -145,21 +134,17 @@ ia2AccessibleHyperlink::get_startIndex(l
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aIndex)
     return E_INVALIDARG;
 
   *aIndex = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    bool valid;
-    *aIndex = proxy->StartOffset(&valid);
-    return valid ? S_OK : S_FALSE;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
@@ -174,21 +159,17 @@ ia2AccessibleHyperlink::get_endIndex(lon
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aIndex)
     return E_INVALIDARG;
 
   *aIndex = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    bool valid;
-    *aIndex = proxy->EndOffset(&valid);
-    return valid ? S_OK : S_FALSE;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
@@ -203,20 +184,17 @@ ia2AccessibleHyperlink::get_valid(boolea
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aValid)
     return E_INVALIDARG;
 
   *aValid = false;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aValid = proxy->IsLinkValid();
-    return S_OK;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!thisObj->IsLink())
     return S_FALSE;
 
--- a/accessible/windows/ia2/ia2AccessibleHypertext.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHypertext.cpp
@@ -21,20 +21,17 @@ ia2AccessibleHypertext::get_nHyperlinks(
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aHyperlinkCount)
     return E_INVALIDARG;
 
   *aHyperlinkCount = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aHyperlinkCount = proxy->LinkCount();
-    return S_OK;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessibleWrap* hyperText = static_cast<HyperTextAccessibleWrap*>(this);
   if (hyperText->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   *aHyperlinkCount = hyperText->LinkCount();
   return S_OK;
 
@@ -48,29 +45,23 @@ ia2AccessibleHypertext::get_hyperlink(lo
   A11Y_TRYBLOCK_BEGIN
 
   if (!aHyperlink)
     return E_INVALIDARG;
 
   *aHyperlink = nullptr;
 
   AccessibleWrap* hyperLink;
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    ProxyAccessible* link = proxy->LinkAt(aLinkIndex);
-    if (!link)
-      return E_FAIL;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessibleWrap* hyperText = static_cast<HyperTextAccessibleWrap*>(this);
+  if (hyperText->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    hyperLink = WrapperFor(link);
-  } else {
-    HyperTextAccessibleWrap* hyperText = static_cast<HyperTextAccessibleWrap*>(this);
-    if (hyperText->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
-
-    hyperLink = static_cast<AccessibleWrap*>(hyperText->LinkAt(aLinkIndex));
-  }
+  hyperLink = static_cast<AccessibleWrap*>(hyperText->LinkAt(aLinkIndex));
 
   if (!hyperLink)
     return E_FAIL;
 
   *aHyperlink =
     static_cast<IAccessibleHyperlink*>(hyperLink);
   (*aHyperlink)->AddRef();
   return S_OK;
@@ -83,20 +74,17 @@ ia2AccessibleHypertext::get_hyperlinkInd
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aHyperlinkIndex)
     return E_INVALIDARG;
 
   *aHyperlinkIndex = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aHyperlinkIndex = proxy->LinkIndexAtOffset(aCharIndex);
-    return S_OK;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessibleWrap* hyperAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (hyperAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   *aHyperlinkIndex = hyperAcc->LinkIndexAtOffset(aCharIndex);
   return S_OK;
 
--- a/accessible/windows/ia2/ia2AccessibleText.cpp
+++ b/accessible/windows/ia2/ia2AccessibleText.cpp
@@ -25,20 +25,17 @@ bool ia2AccessibleText::sLastTextChangeW
 
 // IAccessibleText
 
 STDMETHODIMP
 ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->AddToSelection(aStartOffset, aEndOffset) ?
-      S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   return textAcc->AddToSelection(aStartOffset, aEndOffset) ?
     S_OK : E_INVALIDARG;
 
@@ -55,31 +52,26 @@ ia2AccessibleText::get_attributes(long a
     return E_INVALIDARG;
 
   *aStartOffset = 0;
   *aEndOffset = 0;
   *aTextAttributes = nullptr;
 
   int32_t startOffset = 0, endOffset = 0;
   HRESULT hr;
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    AutoTArray<Attribute, 10> attrs;
-    proxy->TextAttributes(true, aOffset, &attrs, &startOffset, &endOffset);
-    hr = AccessibleWrap::ConvertToIA2Attributes(&attrs, aTextAttributes);
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
-
-    nsCOMPtr<nsIPersistentProperties> attributes =
-      textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset);
-
-    hr = AccessibleWrap::ConvertToIA2Attributes(attributes, aTextAttributes);
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
   }
 
+  nsCOMPtr<nsIPersistentProperties> attributes =
+    textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset);
+
+  hr = AccessibleWrap::ConvertToIA2Attributes(attributes, aTextAttributes);
   if (FAILED(hr))
     return hr;
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
 
   return S_OK;
 
@@ -91,25 +83,23 @@ ia2AccessibleText::get_caretOffset(long 
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aOffset)
     return E_INVALIDARG;
 
   *aOffset = -1;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aOffset = proxy->CaretOffset();
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    *aOffset = textAcc->CaretOffset();
-  }
+  *aOffset = textAcc->CaretOffset();
 
   return *aOffset != -1 ? S_OK : S_FALSE;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleText::get_characterExtents(long aOffset,
@@ -122,25 +112,22 @@ ia2AccessibleText::get_characterExtents(
   if (!aX || !aY || !aWidth || !aHeight)
     return E_INVALIDARG;
   *aX = *aY = *aWidth = *aHeight = 0;
 
   uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
     nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
     nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
   nsIntRect rect;
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    rect = proxy->CharBounds(aOffset, geckoCoordType);
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct())
+    return CO_E_OBJNOTCONNECTED;
 
-    rect = textAcc->CharBounds(aOffset, geckoCoordType);
-  }
+  rect = textAcc->CharBounds(aOffset, geckoCoordType);
 
   *aX = rect.x;
   *aY = rect.y;
   *aWidth = rect.width;
   *aHeight = rect.height;
   return S_OK;
 
   A11Y_TRYBLOCK_END
@@ -150,25 +137,23 @@ STDMETHODIMP
 ia2AccessibleText::get_nSelections(long* aNSelections)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aNSelections)
     return E_INVALIDARG;
   *aNSelections = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aNSelections = proxy->SelectionCount();
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    *aNSelections = textAcc->SelectionCount();
-  }
+  *aNSelections = textAcc->SelectionCount();
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleText::get_offsetAtPoint(long aX, long aY,
@@ -180,25 +165,23 @@ ia2AccessibleText::get_offsetAtPoint(lon
   if (!aOffset)
     return E_INVALIDARG;
   *aOffset = 0;
 
   uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
     nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
     nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aOffset = proxy->OffsetAtPoint(aX, aY, geckoCoordType);
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType);
-  }
+  *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType);
 
   return *aOffset == -1 ? S_FALSE : S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset,
@@ -206,28 +189,24 @@ ia2AccessibleText::get_selection(long aS
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aStartOffset || !aEndOffset)
     return E_INVALIDARG;
   *aStartOffset = *aEndOffset = 0;
 
   int32_t startOffset = 0, endOffset = 0;
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    nsString unused;
-    if (!proxy->SelectionBoundsAt(aSelectionIndex, unused, &startOffset,
-                                  &endOffset))
-      return E_INVALIDARG;
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset))
-      return E_INVALIDARG;
+  if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset)) {
+    return E_INVALIDARG;
   }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -238,30 +217,27 @@ ia2AccessibleText::get_text(long aStartO
   A11Y_TRYBLOCK_BEGIN
 
   if (!aText)
     return E_INVALIDARG;
 
   *aText = nullptr;
 
   nsAutoString text;
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    if (!proxy->TextSubstring(aStartOffset, aEndOffset, text)) {
-      return E_INVALIDARG;
-    }
-  } else {
-    HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
-    if (textAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!HyperTextProxyFor(this));
+  HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
+  if (textAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    if (!textAcc->IsValidRange(aStartOffset, aEndOffset))
-      return E_INVALIDARG;
+  if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) {
+    return E_INVALIDARG;
+  }
 
-    textAcc->TextSubstring(aStartOffset, aEndOffset, text);
-  }
+  textAcc->TextSubstring(aStartOffset, aEndOffset, text);
 
   if (text.IsEmpty())
     return S_FALSE;
 
   *aText = ::SysAllocStringLen(text.get(), text.Length());
   return *aText ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
@@ -409,39 +385,34 @@ ia2AccessibleText::get_textAtOffset(long
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleText::removeSelection(long aSelectionIndex)
 {
   A11Y_TRYBLOCK_BEGIN
 
-    if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-      return proxy->RemoveFromSelection(aSelectionIndex) ? S_OK : E_INVALIDARG;
-    }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   return textAcc->RemoveFromSelection(aSelectionIndex) ?
     S_OK : E_INVALIDARG;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleText::setCaretOffset(long aOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-    if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-      proxy->SetCaretOffset(aOffset);
-      return S_OK;
-    }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidOffset(aOffset))
     return E_INVALIDARG;
 
@@ -452,20 +423,17 @@ ia2AccessibleText::setCaretOffset(long a
 }
 
 STDMETHODIMP
 ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset,
                                 long aEndOffset)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    return proxy->SetSelectionBoundsAt(aSelectionIndex, aStartOffset,
-                                       aEndOffset) ? S_OK : E_INVALIDARG;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset, aEndOffset) ?
     S_OK : E_INVALIDARG;
 
@@ -476,20 +444,17 @@ STDMETHODIMP
 ia2AccessibleText::get_nCharacters(long* aNCharacters)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aNCharacters)
     return E_INVALIDARG;
   *aNCharacters = 0;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    *aNCharacters = proxy->CharacterCount();
-    return S_OK;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   *aNCharacters  = textAcc->CharacterCount();
   return S_OK;
 
@@ -497,20 +462,17 @@ ia2AccessibleText::get_nCharacters(long*
 }
 
 STDMETHODIMP
 ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
                                      enum IA2ScrollType aScrollType)
 {
   A11Y_TRYBLOCK_BEGIN
 
-    if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-      proxy->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType);
-      return S_OK;
-    }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidRange(aStartIndex, aEndIndex))
     return E_INVALIDARG;
 
@@ -526,21 +488,17 @@ ia2AccessibleText::scrollSubstringToPoin
                                           long aX, long aY)
 {
   A11Y_TRYBLOCK_BEGIN
 
   uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
     nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
     nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
 
-  if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
-    proxy->ScrollSubstringToPoint(aStartIndex, aEndIndex, geckoCoordType, aX,
-                                  aY);
-    return S_OK;
-  }
+  MOZ_ASSERT(!HyperTextProxyFor(this));
 
   HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
   if (textAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!textAcc->IsValidRange(aStartIndex, aEndIndex))
     return E_INVALIDARG;
 
--- a/accessible/windows/ia2/ia2AccessibleValue.cpp
+++ b/accessible/windows/ia2/ia2AccessibleValue.cpp
@@ -50,24 +50,22 @@ ia2AccessibleValue::get_currentValue(VAR
 
   if (!aCurrentValue)
     return E_INVALIDARG;
 
   VariantInit(aCurrentValue);
 
   AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this);
   double currentValue;
-  if (valueAcc->IsProxy()) {
-    currentValue = valueAcc->Proxy()->CurValue();
-  } else {
-    if (valueAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!valueAcc->IsProxy());
+  if (valueAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    currentValue = valueAcc->CurValue();
-  }
+  currentValue = valueAcc->CurValue();
 
   if (IsNaN(currentValue))
     return S_FALSE;
 
   aCurrentValue->vt = VT_R8;
   aCurrentValue->dblVal = currentValue;
   return S_OK;
 
@@ -78,18 +76,17 @@ STDMETHODIMP
 ia2AccessibleValue::setCurrentValue(VARIANT aValue)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (aValue.vt != VT_R8)
     return E_INVALIDARG;
 
   AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this);
-  if (valueAcc->IsProxy())
-    return valueAcc->Proxy()->SetCurValue(aValue.dblVal) ? S_OK : E_FAIL;
+  MOZ_ASSERT(!valueAcc->IsProxy());
 
   if (valueAcc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   return valueAcc->SetCurValue(aValue.dblVal) ? S_OK : E_FAIL;
 
   A11Y_TRYBLOCK_END
 }
@@ -101,24 +98,22 @@ ia2AccessibleValue::get_maximumValue(VAR
 
   if (!aMaximumValue)
     return E_INVALIDARG;
 
   VariantInit(aMaximumValue);
 
   AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this);
   double maximumValue;
-  if (valueAcc->IsProxy()) {
-    maximumValue = valueAcc->Proxy()->MaxValue();
-  } else {
-    if (valueAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!valueAcc->IsProxy());
+  if (valueAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    maximumValue = valueAcc->MaxValue();
-  }
+  maximumValue = valueAcc->MaxValue();
 
   if (IsNaN(maximumValue))
     return S_FALSE;
 
   aMaximumValue->vt = VT_R8;
   aMaximumValue->dblVal = maximumValue;
   return S_OK;
 
@@ -132,24 +127,22 @@ ia2AccessibleValue::get_minimumValue(VAR
 
   if (!aMinimumValue)
     return E_INVALIDARG;
 
   VariantInit(aMinimumValue);
 
   AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this);
   double minimumValue;
-  if (valueAcc->IsProxy()) {
-    minimumValue = valueAcc->Proxy()->MinValue();
-  } else {
-    if (valueAcc->IsDefunct())
-      return CO_E_OBJNOTCONNECTED;
+  MOZ_ASSERT(!valueAcc->IsProxy());
+  if (valueAcc->IsDefunct()) {
+    return CO_E_OBJNOTCONNECTED;
+  }
 
-    minimumValue = valueAcc->MinValue();
-  }
+  minimumValue = valueAcc->MinValue();
 
   if (IsNaN(minimumValue))
     return S_FALSE;
 
   aMinimumValue->vt = VT_R8;
   aMinimumValue->dblVal = minimumValue;
   return S_OK;
 
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -182,26 +182,16 @@ AccessibleWrap::get_accParent( IDispatch
   if (!ppdispParent)
     return E_INVALIDARG;
 
   *ppdispParent = nullptr;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (IsProxy()) {
-    ProxyAccessible* proxy = Proxy();
-    ProxyAccessible* parent = proxy->Parent();
-    if (!parent)
-      return S_FALSE;
-
-    *ppdispParent = NativeAccessible(WrapperFor(parent));
-    return S_OK;
-  }
-
   DocAccessible* doc = AsDoc();
   if (doc) {
     // Return window system accessible object for root document and tab document
     // accessibles.
     if (!doc->ParentDocument() ||
         (nsWinUtils::IsWindowEmulationStarted() &&
          nsCoreUtils::IsTabDocument(doc->DocumentNode()))) {
       HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
@@ -231,25 +221,16 @@ AccessibleWrap::get_accChildCount( long 
   if (!pcountChildren)
     return E_INVALIDARG;
 
   *pcountChildren = 0;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  if (IsProxy()) {
-    ProxyAccessible* proxy = Proxy();
-    if (proxy->MustPruneChildren())
-      return S_OK;
-
-    *pcountChildren = proxy->ChildrenCount();
-    return S_OK;
-  }
-
   if (nsAccUtils::MustPrune(this))
     return S_OK;
 
   *pcountChildren = ChildCount();
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -304,20 +285,17 @@ AccessibleWrap::get_accName(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString name;
-  if (xpAccessible->IsProxy())
-    xpAccessible->Proxy()->Name(name);
-  else
-    xpAccessible->Name(name);
+  xpAccessible->Name(name);
 
   // The name was not provided, e.g. no alt attribute for an image. A screen
   // reader may choose to invent its own accessible name, e.g. from an image src
   // attribute. Refer to eNoNameOnPurpose return value.
   if (name.IsVoid())
     return S_FALSE;
 
   *pszName = ::SysAllocStringLen(name.get(), name.Length());
@@ -346,20 +324,16 @@ AccessibleWrap::get_accValue(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  // TODO make this work with proxies.
-  if (IsProxy())
-    return E_NOTIMPL;
-
   nsAutoString value;
   xpAccessible->Value(value);
 
   // See bug 438784: need to expose URL on doc's value attribute. For this,
   // reverting part of fix for bug 425693 to make this MSAA method behave
   // IAccessible2-style.
   if (value.IsEmpty())
     return S_FALSE;
@@ -389,20 +363,17 @@ AccessibleWrap::get_accDescription(VARIA
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString description;
-  if (IsProxy())
-    xpAccessible->Proxy()->Description(description);
-  else
-    xpAccessible->Description(description);
+  xpAccessible->Description(description);
 
   *pszDescription = ::SysAllocStringLen(description.get(),
                                         description.Length());
   return *pszDescription ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -424,26 +395,22 @@ AccessibleWrap::get_accRole(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   a11y::role geckoRole;
-  if (xpAccessible->IsProxy()) {
-    geckoRole = xpAccessible->Proxy()->Role();
-  } else {
 #ifdef DEBUG
-    NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
-                 "Does not support Text when it should");
+  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
+               "Does not support Text when it should");
 #endif
 
-    geckoRole = xpAccessible->Role();
-  }
+  geckoRole = xpAccessible->Role();
 
   uint32_t msaaRole = 0;
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              _msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     msaaRole = _msaaRole; \
     break;
@@ -454,39 +421,29 @@ AccessibleWrap::get_accRole(
       MOZ_CRASH("Unknown role.");
   }
 
 #undef ROLE
 
   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
   // a ROLE_OUTLINEITEM for consistency and compatibility.
   // We need this because ARIA has a role of "row" for both grid and treegrid
-  if (xpAccessible->IsProxy()) {
-      if (geckoRole == roles::ROW
-          && xpAccessible->Proxy()->Parent()->Role() == roles::TREE_TABLE)
-        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
-  } else {
-    if (geckoRole == roles::ROW) {
-      Accessible* xpParent = Parent();
-      if (xpParent && xpParent->Role() == roles::TREE_TABLE)
-        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
-    }
+  if (geckoRole == roles::ROW) {
+    Accessible* xpParent = Parent();
+    if (xpParent && xpParent->Role() == roles::TREE_TABLE)
+      msaaRole = ROLE_SYSTEM_OUTLINEITEM;
   }
-  
+
   // -- Try enumerated role
   if (msaaRole != USE_ROLE_STRING) {
     pvarRole->vt = VT_I4;
     pvarRole->lVal = msaaRole;  // Normal enumerated role
     return S_OK;
   }
 
-  // XXX bug 798492 make this work with proxies?
-  if (IsProxy())
-  return E_FAIL;
-
   // -- Try BSTR role
   // Could not map to known enumerated MSAA role like ROLE_BUTTON
   // Use BSTR role to expose role attribute or tag name + namespace
   nsIContent *content = xpAccessible->GetContent();
   if (!content)
     return E_FAIL;
 
   if (content->IsElement()) {
@@ -547,21 +504,17 @@ AccessibleWrap::get_accState(
   // MSAA only has 31 states and the lowest 31 bits of our state bit mask
   // are the same states as MSAA.
   // Note: we map the following Gecko states to different MSAA states:
   //   REQUIRED -> ALERT_LOW
   //   ALERT -> ALERT_MEDIUM
   //   INVALID -> ALERT_HIGH
   //   CHECKABLE -> MARQUEED
 
-  uint64_t state;
-  if (xpAccessible->IsProxy())
-    state = xpAccessible->Proxy()->State();
-  else
-    state = State();
+  uint64_t state = State();
 
   uint32_t msaaState = 0;
   nsAccUtils::To32States(state, &msaaState, nullptr);
   pvarState->lVal = msaaState;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -617,20 +570,16 @@ AccessibleWrap::get_accKeyboardShortcut(
 
   Accessible* acc = GetXPAccessibleFor(varChild);
   if (!acc)
     return E_INVALIDARG;
 
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  // TODO make this work with proxies.
-  if (acc->IsProxy())
-    return E_NOTIMPL;
-
   KeyBinding keyBinding = acc->AccessKey();
   if (keyBinding.IsEmpty())
     keyBinding = acc->KeyboardShortcut();
 
   nsAutoString shortcut;
   keyBinding.ToString(shortcut);
 
   *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
@@ -656,23 +605,17 @@ AccessibleWrap::get_accFocus(
   // VT_I4:       lVal is CHILDID_SELF. The object itself has the keyboard focus.
   // VT_I4:       lVal contains the child ID of the child element with the keyboard focus.
   // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
   //              for the child object with the keyboard focus.
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   // Return the current IAccessible child that has focus
-  Accessible* focusedAccessible;
-  if (IsProxy()) {
-    ProxyAccessible* proxy = Proxy()->FocusedChild();
-    focusedAccessible = proxy ? WrapperFor(proxy) : nullptr;
-  } else {
-    focusedAccessible = FocusedChild();
-  }
+  Accessible* focusedAccessible = FocusedChild();
 
   if (focusedAccessible == this) {
     pvarChild->vt = VT_I4;
     pvarChild->lVal = CHILDID_SELF;
   }
   else if (focusedAccessible) {
     pvarChild->vt = VT_DISPATCH;
     pvarChild->pdispVal = NativeAccessible(focusedAccessible);
@@ -824,33 +767,19 @@ AccessibleWrap::get_accSelection(VARIANT
     return E_INVALIDARG;
 
   VariantInit(pvarChildren);
   pvarChildren->vt = VT_EMPTY;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  // TODO make this work with proxies.
-  if (IsProxy())
-    return E_NOTIMPL;
-
   if (IsSelect()) {
     AutoTArray<Accessible*, 10> selectedItems;
-    if (IsProxy()) {
-      nsTArray<ProxyAccessible*> proxies;
-      Proxy()->SelectedItems(&proxies);
-
-      uint32_t selectedCount = proxies.Length();
-      for (uint32_t i = 0; i < selectedCount; i++) {
-        selectedItems.AppendElement(WrapperFor(proxies[i]));
-      }
-    } else {
-      SelectedItems(&selectedItems);
-    }
+    SelectedItems(&selectedItems);
 
     // 1) Create and initialize the enumeration
     RefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
     pvarChildren->vt = VT_UNKNOWN;    // this must be VT_UNKNOWN for an IEnumVARIANT
     NS_ADDREF(pvarChildren->punkVal = pEnum);
   }
   return S_OK;
 
@@ -875,21 +804,17 @@ AccessibleWrap::get_accDefaultAction(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString defaultAction;
-  if (xpAccessible->IsProxy()) {
-    xpAccessible->Proxy()->ActionNameAt(0, defaultAction);
-  } else {
-    xpAccessible->ActionNameAt(0, defaultAction);
-  }
+  xpAccessible->ActionNameAt(0, defaultAction);
 
   *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
                                           defaultAction.Length());
   return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -907,52 +832,32 @@ AccessibleWrap::accSelect(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (flagsSelect & SELFLAG_TAKEFOCUS) {
-    if (xpAccessible->IsProxy()) {
-      xpAccessible->Proxy()->TakeFocus();
-    } else {
-      xpAccessible->TakeFocus();
-    }
-
+    xpAccessible->TakeFocus();
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_TAKESELECTION) {
-    if (xpAccessible->IsProxy()) {
-      xpAccessible->Proxy()->TakeSelection();
-    } else {
-      xpAccessible->TakeSelection();
-    }
-
+    xpAccessible->TakeSelection();
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_ADDSELECTION) {
-    if (xpAccessible->IsProxy()) {
-      xpAccessible->Proxy()->SetSelected(true);
-    } else {
-      xpAccessible->SetSelected(true);
-    }
-
+    xpAccessible->SetSelected(true);
     return S_OK;
   }
 
   if (flagsSelect & SELFLAG_REMOVESELECTION) {
-    if (xpAccessible->IsProxy()) {
-      xpAccessible->Proxy()->SetSelected(false);
-    } else {
-      xpAccessible->SetSelected(false);
-    }
-
+    xpAccessible->SetSelected(false);
     return S_OK;
   }
 
   return E_FAIL;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -979,22 +884,17 @@ AccessibleWrap::accLocation(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  nsIntRect rect;
-  if (xpAccessible->IsProxy()) {
-    rect = xpAccessible->Proxy()->Bounds();
-  } else {
-    rect = xpAccessible->Bounds();
-  }
+  nsIntRect rect = xpAccessible->Bounds();
 
   *pxLeft = rect.x;
   *pyTop = rect.y;
   *pcxWidth = rect.width;
   *pcyHeight = rect.height;
   return S_OK;
 
   A11Y_TRYBLOCK_END
@@ -1075,26 +975,18 @@ AccessibleWrap::accNavigate(
       return E_INVALIDARG;
   }
 
 #undef RELATIONTYPE
 
   pvarEndUpAt->vt = VT_EMPTY;
 
   if (xpRelation) {
-    if (accessible->IsProxy()) {
-      nsTArray<ProxyAccessible*> targets =
-        accessible->Proxy()->RelationByType(*xpRelation);
-      if (targets.Length()) {
-        navAccessible = WrapperFor(targets[0]);
-      }
-    } else {
-      Relation rel = RelationByType(*xpRelation);
-      navAccessible = rel.Next();
-    }
+    Relation rel = RelationByType(*xpRelation);
+    navAccessible = rel.Next();
   }
 
   if (!navAccessible)
     return E_FAIL;
 
   pvarEndUpAt->pdispVal = NativeAccessible(navAccessible);
   pvarEndUpAt->vt = VT_DISPATCH;
   return S_OK;
@@ -1113,25 +1005,17 @@ AccessibleWrap::accHitTest(
   if (!pvarChild)
     return E_INVALIDARG;
 
   VariantInit(pvarChild);
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  Accessible* accessible = nullptr;
-  if (IsProxy()) {
-    ProxyAccessible* proxy = Proxy()->ChildAtPoint(xLeft, yTop, eDirectChild);
-    if (proxy) {
-      accessible = WrapperFor(proxy);
-    }
-  } else {
-    accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
-  }
+  Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
 
   // if we got a child
   if (accessible) {
     // if the child is us
     if (accessible == this) {
       pvarChild->vt = VT_I4;
       pvarChild->lVal = CHILDID_SELF;
     } else { // its not create an Accessible for it.
@@ -1159,20 +1043,16 @@ AccessibleWrap::accDoDefaultAction(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  // TODO make this work with proxies.
-  if (xpAccessible->IsProxy())
-    return xpAccessible->Proxy()->DoAction(0) ? S_OK : E_INVALIDARG;
-
   return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::put_accName(
       /* [optional][in] */ VARIANT varChild,
new file mode 100644
--- /dev/null
+++ b/accessible/windows/msaa/ChildIDThunk.cpp
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ChildIDThunk.h"
+#include "MainThreadUtils.h"
+#include "mozilla/mscom/InterceptorLog.h"
+
+using namespace mozilla::mscom;
+
+namespace mozilla {
+namespace a11y {
+
+ChildIDThunk::ChildIDThunk()
+  : mRefCnt(0)
+{
+}
+
+HRESULT
+ChildIDThunk::QueryInterface(REFIID riid, void** ppv)
+{
+  if (!ppv) {
+    return E_INVALIDARG;
+  }
+
+  if (riid == IID_IUnknown || riid == IID_ICallFrameEvents ||
+      riid == IID_IInterceptorSink) {
+    *ppv = static_cast<IInterceptorSink*>(this);
+    AddRef();
+    return S_OK;
+  }
+
+  *ppv = nullptr;
+  return E_NOINTERFACE;
+}
+
+ULONG
+ChildIDThunk::AddRef()
+{
+  return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
+}
+
+ULONG
+ChildIDThunk::Release()
+{
+  ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
+  if (newRefCnt == 0) {
+    MOZ_ASSERT(NS_IsMainThread());
+    delete this;
+  }
+  return newRefCnt;
+}
+
+enum IAccessibleVTableIndex
+{
+  eget_accName = 10,
+  eget_accValue = 11,
+  eget_accDescription = 12,
+  eget_accRole = 13,
+  eget_accState = 14,
+  eget_accHelp = 15,
+  eget_accHelpTopic = 16,
+  eget_accKeyboardShortcut = 17,
+  eget_accDefaultAction = 20,
+  eaccSelect = 21,
+  eaccLocation = 22,
+  eaccNavigate = 23,
+  eaccDoDefaultAction = 25,
+  eput_accName = 26,
+  eput_accValue = 27
+};
+
+Maybe<ULONG>
+ChildIDThunk::IsMethodInteresting(const ULONG aMethodIdx)
+{
+  switch (aMethodIdx) {
+    case eget_accName:
+    case eget_accValue:
+    case eget_accDescription:
+    case eget_accRole:
+    case eget_accState:
+    case eget_accHelp:
+    case eget_accKeyboardShortcut:
+    case eget_accDefaultAction:
+    case eaccDoDefaultAction:
+    case eput_accName:
+    case eput_accValue:
+      return Some(0UL);
+    case eget_accHelpTopic:
+    case eaccNavigate:
+      return Some(1UL);
+    case eaccSelect:
+      return Some(2UL);
+    case eaccLocation:
+      return Some(4UL);
+    default:
+      return Nothing();
+  }
+}
+
+bool
+ChildIDThunk::IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIDIdx,
+                            LONG& aOutChildID)
+{
+  VARIANT paramValue;
+  HRESULT hr = aFrame->GetParam(aChildIDIdx, &paramValue);
+  MOZ_ASSERT(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  const bool isVariant = paramValue.vt & VT_VARIANT;
+  MOZ_ASSERT(isVariant);
+  if (!isVariant) {
+    return false;
+  }
+
+  VARIANT& childID = *(paramValue.pvarVal);
+  if (childID.vt != VT_I4) {
+    return false;
+  }
+
+  aOutChildID = childID.lVal;
+  return childID.lVal == CHILDID_SELF;
+}
+
+/**
+ * ICallFrame::SetParam always returns E_NOTIMPL, so we'll just have to work
+ * around this by manually poking at the parameter's location on the stack.
+ */
+/* static */ HRESULT
+ChildIDThunk::SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
+                              const LONG aChildID)
+{
+  void* stackBase = aFrame->GetStackLocation();
+  MOZ_ASSERT(stackBase);
+  if (!stackBase) {
+    return E_UNEXPECTED;
+  }
+
+  CALLFRAMEPARAMINFO paramInfo;
+  HRESULT hr = aFrame->GetParamInfo(aParamIdx, &paramInfo);
+  MOZ_ASSERT(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // We expect this childID to be a VARIANT in-parameter
+  MOZ_ASSERT(paramInfo.fIn);
+  MOZ_ASSERT(paramInfo.cbParam == sizeof(VARIANT));
+  if (!paramInfo.fIn || paramInfo.cbParam != sizeof(VARIANT)) {
+    return E_UNEXPECTED;
+  }
+
+  // Given the stack base and param offset, calculate the address of the param
+  // and replace its value
+  VARIANT* pInParam = reinterpret_cast<VARIANT*>(
+      reinterpret_cast<PBYTE>(stackBase) + paramInfo.stackOffset);
+  MOZ_ASSERT(pInParam->vt == VT_I4);
+  if (pInParam->vt != VT_I4) {
+    return E_UNEXPECTED;
+  }
+  pInParam->lVal = aChildID;
+  return S_OK;
+}
+
+HRESULT
+ChildIDThunk::ResolveTargetInterface(REFIID aIid, IUnknown** aOut)
+{
+  MOZ_ASSERT(aOut);
+  *aOut = nullptr;
+
+  RefPtr<IInterceptor> interceptor;
+  HRESULT hr = mInterceptor->Resolve(IID_IInterceptor,
+                                     (void**)getter_AddRefs(interceptor));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  InterceptorTargetPtr target;
+  hr = interceptor->GetTargetForIID(aIid, target);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  RefPtr<IUnknown> refTarget = target.get();
+  refTarget.forget(aOut);
+  return S_OK;
+}
+
+HRESULT
+ChildIDThunk::OnCall(ICallFrame* aFrame)
+{
+#if defined(MOZILLA_INTERNAL_API)
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_IsParentProcess());
+#endif
+
+  IID iid;
+  ULONG method;
+  HRESULT hr = aFrame->GetIIDAndMethod(&iid, &method);
+  MOZ_ASSERT(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  RefPtr<IUnknown> target;
+  hr = ResolveTargetInterface(iid, getter_AddRefs(target));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  Maybe<ULONG> childIDIdx;
+  LONG childID;
+  if (iid != IID_IAccessible ||
+      (childIDIdx = IsMethodInteresting(method)).isNothing() ||
+      IsChildIDSelf(aFrame, childIDIdx.value(), childID)) {
+    // We're only interested in methods which accept a child ID parameter which
+    // is not equal to CHILDID_SELF. Otherwise we just invoke against the
+    // original target interface.
+    hr = aFrame->Invoke(target);
+    if (SUCCEEDED(hr)) {
+      InterceptorLog::Event(aFrame, target);
+    }
+    return hr;
+  }
+
+  // Otherwise we need to resolve the child node...
+  RefPtr<IAccessible> acc;
+  hr = target->QueryInterface(iid, (void**)getter_AddRefs(acc));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  VARIANT vChildID;
+  VariantInit(&vChildID);
+  vChildID.vt = VT_I4;
+  vChildID.lVal = childID;
+
+  RefPtr<IDispatch> disp;
+  hr = acc->get_accChild(vChildID, getter_AddRefs(disp));
+  if (FAILED(hr)) {
+    aFrame->SetReturnValue(hr);
+    return S_OK;
+  }
+
+  RefPtr<IAccessible> directAccessible;
+  // Yes, given our implementation we could just downcast, but that's not very COMy
+  hr = disp->QueryInterface(IID_IAccessible,
+                            (void**)getter_AddRefs(directAccessible));
+  if (FAILED(hr)) {
+    aFrame->SetReturnValue(hr);
+    return S_OK;
+  }
+
+  // Now that we have the IAccessible for the child ID we need to replace it
+  // in the activation record with a self-referant child ID
+  hr = SetChildIDParam(aFrame, childIDIdx.value(), CHILDID_SELF);
+  MOZ_ASSERT(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = aFrame->Invoke(directAccessible);
+  if (SUCCEEDED(hr)) {
+    InterceptorLog::Event(aFrame, directAccessible);
+  }
+  return hr;
+}
+
+HRESULT
+ChildIDThunk::SetInterceptor(IWeakReference* aInterceptor)
+{
+  mInterceptor = aInterceptor;
+  return S_OK;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/accessible/windows/msaa/ChildIDThunk.h
@@ -0,0 +1,54 @@
+/* -*- 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_ChildIDThunk_h
+#define mozilla_a11y_ChildIDThunk_h
+
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+
+#include <oleacc.h>
+#include <callobj.h>
+
+namespace mozilla {
+namespace a11y {
+
+class ChildIDThunk : public mozilla::mscom::IInterceptorSink
+{
+public:
+  ChildIDThunk();
+
+  // IUnknown
+  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+  STDMETHODIMP_(ULONG) AddRef() override;
+  STDMETHODIMP_(ULONG) Release() override;
+
+  // ICallFrameEvents
+  STDMETHODIMP OnCall(ICallFrame* aFrame) override;
+
+  // IInterceptorSink
+  STDMETHODIMP SetInterceptor(mozilla::mscom::IWeakReference* aInterceptor) override;
+
+private:
+  HRESULT ResolveTargetInterface(REFIID aIid, IUnknown** aOut);
+
+  static mozilla::Maybe<ULONG> IsMethodInteresting(const ULONG aMethodIdx);
+  static bool IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIdIdx,
+                            LONG& aOutChildID);
+  static HRESULT SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
+                                 const LONG aChildID);
+
+private:
+  ULONG mRefCnt;
+  RefPtr<mozilla::mscom::IWeakReference> mInterceptor;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_ChildIDThunk_h
+
--- a/accessible/windows/msaa/DocAccessibleWrap.cpp
+++ b/accessible/windows/msaa/DocAccessibleWrap.cpp
@@ -2,16 +2,17 @@
 /* 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 "DocAccessibleWrap.h"
 
 #include "Compatibility.h"
+#include "DocAccessibleChild.h"
 #include "nsWinUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "sdnDocAccessible.h"
 #include "Statistics.h"
 
 #include "nsIDocShell.h"
@@ -39,16 +40,40 @@ IMPL_IUNKNOWN_QUERY_HEAD(DocAccessibleWr
     statistics::ISimpleDOMUsed();
     *aInstancePtr = static_cast<ISimpleDOMDocument*>(new sdnDocAccessible(this));
     static_cast<IUnknown*>(*aInstancePtr)->AddRef();
     return S_OK;
   }
 IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(HyperTextAccessibleWrap)
 
 STDMETHODIMP
+DocAccessibleWrap::get_accParent(
+      /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
+{
+  HRESULT hr = DocAccessible::get_accParent(ppdispParent);
+  if (*ppdispParent) {
+    return hr;
+  }
+
+  // We might be a top-level document in a content process.
+  DocAccessibleChild* ipcDoc = IPCDoc();
+  if (!ipcDoc) {
+    return S_FALSE;
+  }
+  IAccessible* dispParent = ipcDoc->GetParentIAccessible();
+  if (!dispParent) {
+    return S_FALSE;
+  }
+
+  dispParent->AddRef();
+  *ppdispParent = static_cast<IDispatch*>(dispParent);
+  return S_OK;
+}
+
+STDMETHODIMP
 DocAccessibleWrap::get_accValue(VARIANT aVarChild, BSTR __RPC_FAR* aValue)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aValue)
     return E_INVALIDARG;
   *aValue = nullptr;
 
--- a/accessible/windows/msaa/DocAccessibleWrap.h
+++ b/accessible/windows/msaa/DocAccessibleWrap.h
@@ -17,20 +17,24 @@ class DocAccessibleWrap : public DocAcce
 public:
   DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
   virtual ~DocAccessibleWrap();
 
   DECL_IUNKNOWN_INHERITED
 
   // IAccessible
 
-    // Override get_accValue to provide URL when no other value is available
-    virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue( 
-        /* [optional][in] */ VARIANT varChild,
-        /* [retval][out] */ BSTR __RPC_FAR *pszValue);
+  // Override get_accParent for e10s
+  virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accParent(
+      /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) override;
+
+  // Override get_accValue to provide URL when no other value is available
+  virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue(
+      /* [optional][in] */ VARIANT varChild,
+      /* [retval][out] */ BSTR __RPC_FAR *pszValue) override;
 
   // Accessible
   virtual void Shutdown();
 
   // DocAccessible
   virtual void* GetNativeWindow() const;
 
   /**
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -5,38 +5,63 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Platform.h"
 
 #include "AccEvent.h"
 #include "Compatibility.h"
 #include "HyperTextAccessibleWrap.h"
 #include "ia2AccessibleText.h"
+#include "nsIXULRuntime.h"
 #include "nsWinUtils.h"
 #include "mozilla/a11y/ProxyAccessible.h"
+#include "mozilla/mscom/InterceptorLog.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/StaticPtr.h"
 #include "ProxyWrappers.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
+using namespace mozilla::mscom;
+
+static StaticAutoPtr<RegisteredProxy> gRegProxy;
+static StaticAutoPtr<RegisteredProxy> gRegAccTlb;
+static StaticAutoPtr<RegisteredProxy> gRegMiscTlb;
 
 void
 a11y::PlatformInit()
 {
   Compatibility::Init();
 
   nsWinUtils::MaybeStartWindowEmulation();
   ia2AccessibleText::InitTextChangeData();
+  if (BrowserTabsRemoteAutostart()) {
+    mscom::InterceptorLog::Init();
+    UniquePtr<RegisteredProxy> regProxy(
+        mscom::RegisterProxy(L"ia2marshal.dll"));
+    gRegProxy = regProxy.release();
+    UniquePtr<RegisteredProxy> regAccTlb(
+        mscom::RegisterTypelib(L"oleacc.dll",
+                               RegistrationFlags::eUseSystemDirectory));
+    gRegAccTlb = regAccTlb.release();
+    UniquePtr<RegisteredProxy> regMiscTlb(
+        mscom::RegisterTypelib(L"Accessible.tlb"));
+    gRegMiscTlb = regMiscTlb.release();
+  }
 }
 
 void
 a11y::PlatformShutdown()
 {
   ::DestroyCaret();
 
   nsWinUtils::ShutdownWindowEmulation();
+  gRegProxy = nullptr;
+  gRegAccTlb = nullptr;
+  gRegMiscTlb = nullptr;
 }
 
 void
 a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
   AccessibleWrap* wrapper = nullptr;
   if (aInterfaces & Interfaces::DOCUMENT) {
     wrapper = new DocProxyAccessibleWrap(aProxy);
--- a/accessible/windows/msaa/RootAccessibleWrap.cpp
+++ b/accessible/windows/msaa/RootAccessibleWrap.cpp
@@ -1,35 +1,92 @@
 /* -*- 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 "RootAccessibleWrap.h"
 
+#include "ChildIDThunk.h"
 #include "Compatibility.h"
+#include "mozilla/mscom/interceptor.h"
 #include "nsCoreUtils.h"
+#include "nsIXULRuntime.h"
 #include "nsWinUtils.h"
 
 using namespace mozilla::a11y;
+using namespace mozilla::mscom;
 
 ////////////////////////////////////////////////////////////////////////////////
-// Constructor/desctructor
+// Constructor/destructor
 
 RootAccessibleWrap::
   RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
   RootAccessible(aDocument, aPresShell)
 {
 }
 
 RootAccessibleWrap::~RootAccessibleWrap()
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// Accessible
+
+void
+RootAccessibleWrap::GetNativeInterface(void** aOutAccessible)
+{
+  MOZ_ASSERT(aOutAccessible);
+  if (!aOutAccessible) {
+    return;
+  }
+
+  if (mInterceptor &&
+      SUCCEEDED(mInterceptor->Resolve(IID_IAccessible, aOutAccessible))) {
+    return;
+  }
+
+  *aOutAccessible = nullptr;
+
+  RefPtr<IAccessible> rootAccessible;
+  RootAccessible::GetNativeInterface((void**)getter_AddRefs(rootAccessible));
+
+  if (!mozilla::BrowserTabsRemoteAutostart() || XRE_IsContentProcess()) {
+    // We only need to wrap this interface if our process is non-content e10s
+    rootAccessible.forget(aOutAccessible);
+    return;
+  }
+
+  // Otherwise, we need to wrap that IAccessible with an interceptor
+  RefPtr<IInterceptorSink> eventSink(MakeAndAddRef<ChildIDThunk>());
+
+  RefPtr<IAccessible> interceptor;
+  HRESULT hr = CreateInterceptor<IAccessible>(
+      STAUniquePtr<IAccessible>(rootAccessible.forget().take()), eventSink,
+      getter_AddRefs(interceptor));
+  if (FAILED(hr)) {
+    return;
+  }
+
+  RefPtr<IWeakReferenceSource> weakRefSrc;
+  hr = interceptor->QueryInterface(IID_IWeakReferenceSource,
+                                   (void**)getter_AddRefs(weakRefSrc));
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = weakRefSrc->GetWeakReference(getter_AddRefs(mInterceptor));
+  if (FAILED(hr)) {
+    return;
+  }
+
+  interceptor.forget(aOutAccessible);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // RootAccessible
 
 void
 RootAccessibleWrap::DocumentActivated(DocAccessible* aDocument)
 {
   if (Compatibility::IsDolphin() &&
       nsCoreUtils::IsTabDocument(aDocument->DocumentNode())) {
     uint32_t count = mChildDocuments.Length();
--- a/accessible/windows/msaa/RootAccessibleWrap.h
+++ b/accessible/windows/msaa/RootAccessibleWrap.h
@@ -4,24 +4,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_RootAccessibleWrap_h__
 #define mozilla_a11y_RootAccessibleWrap_h__
 
 #include "RootAccessible.h"
 
 namespace mozilla {
+namespace mscom {
+
+struct IWeakReference;
+
+} // namespace mscom
+
 namespace a11y {
 
 class RootAccessibleWrap : public RootAccessible
 {
 public:
   RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
   virtual ~RootAccessibleWrap();
 
+  // Accessible
+  virtual void GetNativeInterface(void** aOutAccessible) override;
+
   // RootAccessible
   virtual void DocumentActivated(DocAccessible* aDocument);
+
+private:
+  RefPtr<mscom::IWeakReference> mInterceptor;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/windows/msaa/moz.build
+++ b/accessible/windows/msaa/moz.build
@@ -14,16 +14,17 @@ EXPORTS.mozilla.a11y += [
     'HyperTextAccessibleWrap.h',
     'IDSet.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleWrap.cpp',
     'ApplicationAccessibleWrap.cpp',
     'ARIAGridAccessibleWrap.cpp',
+    'ChildIDThunk.cpp',
     'Compatibility.cpp',
     'DocAccessibleWrap.cpp',
     'EnumVariant.cpp',
     'HTMLTableAccessibleWrap.cpp',
     'HTMLWin32ObjectAccessible.cpp',
     'HyperTextAccessibleWrap.cpp',
     'ImageAccessibleWrap.cpp',
     'IUnknownImpl.cpp',
@@ -44,16 +45,18 @@ if CONFIG['MOZ_XUL']:
         'XULMenuAccessibleWrap.cpp',
         'XULTreeGridAccessibleWrap.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
+    '/accessible/ipc',
+    '/accessible/ipc/win',
     '/accessible/windows',
     '/accessible/windows/ia2',
     '/accessible/windows/sdn',
     '/accessible/windows/uia',
     '/accessible/xpcom',
     '/accessible/xul',
     '/dom/base',
     '/layout/style',
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -190,26 +190,30 @@ xpcAccessible::GetDOMNode(nsIDOMNode** a
     CallQueryInterface(node, aDOMNode);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetId(nsAString& aID)
 {
+#if defined(XP_WIN)
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
   ProxyAccessible* proxy = IntlGeneric().AsProxy();
   if (!proxy) {
     return NS_ERROR_FAILURE;
   }
 
   nsString id;
   proxy->DOMNodeID(id);
   aID.Assign(id);
 
   return NS_OK;
+#endif
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetDocument(nsIAccessibleDocument** aDocument)
 {
   NS_ENSURE_ARG_POINTER(aDocument);
   *aDocument = nullptr;
 
@@ -473,22 +477,26 @@ xpcAccessible::GetRelationByType(uint32_
     return NS_ERROR_FAILURE;
 
   if (IntlGeneric().IsAccessible()) {
     Relation rel = Intl()->RelationByType(static_cast<RelationType>(aType));
     NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel));
     return NS_OK;
   }
 
+#if defined(XP_WIN)
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
   ProxyAccessible* proxy = IntlGeneric().AsProxy();
   nsTArray<ProxyAccessible*> targets =
     proxy->RelationByType(static_cast<RelationType>(aType));
   NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &targets));
 
   return NS_OK;
+#endif
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetRelations(nsIArray** aRelations)
 {
   NS_ENSURE_ARG_POINTER(aRelations);
   *aRelations = nullptr;
 
--- a/accessible/xpcom/xpcAccessibleDocument.h
+++ b/accessible/xpcom/xpcAccessibleDocument.h
@@ -91,16 +91,17 @@ private:
     }
 
     mCache.Remove(aProxy);
   }
 
   friend class DocManager;
   friend class DocAccessible;
   friend class ProxyAccessible;
+  friend class ProxyAccessibleBase<ProxyAccessible>;
 
   xpcAccessibleDocument(const xpcAccessibleDocument&) = delete;
   xpcAccessibleDocument& operator =(const xpcAccessibleDocument&) = delete;
 
   nsRefPtrHashtable<nsPtrHashKey<const void>, xpcAccessibleGeneric> mCache;
   bool mRemote;
 };
 
--- a/accessible/xpcom/xpcAccessibleHyperLink.cpp
+++ b/accessible/xpcom/xpcAccessibleHyperLink.cpp
@@ -16,69 +16,81 @@ xpcAccessibleHyperLink::GetStartIndex(in
   NS_ENSURE_ARG_POINTER(aStartIndex);
   *aStartIndex = 0;
 
   if (Intl().IsNull())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     *aStartIndex = Intl().AsAccessible()->StartOffset();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     bool isIndexValid = false;
     uint32_t startOffset = Intl().AsProxy()->StartOffset(&isIndexValid);
     if (!isIndexValid)
       return NS_ERROR_FAILURE;
 
     *aStartIndex = startOffset;
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperLink::GetEndIndex(int32_t* aEndIndex)
 {
   NS_ENSURE_ARG_POINTER(aEndIndex);
   *aEndIndex = 0;
 
   if (Intl().IsNull())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     *aEndIndex = Intl().AsAccessible()->EndOffset();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     bool isIndexValid = false;
     uint32_t endOffset = Intl().AsProxy()->EndOffset(&isIndexValid);
     if (!isIndexValid)
       return NS_ERROR_FAILURE;
 
     *aEndIndex = endOffset;
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperLink::GetAnchorCount(int32_t* aAnchorCount)
 {
   NS_ENSURE_ARG_POINTER(aAnchorCount);
   *aAnchorCount = 0;
 
   if (Intl().IsNull())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     *aAnchorCount = Intl().AsAccessible()->AnchorCount();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     bool isCountValid = false;
     uint32_t anchorCount = Intl().AsProxy()->AnchorCount(&isCountValid);
     if (!isCountValid)
       return NS_ERROR_FAILURE;
 
     *aAnchorCount = anchorCount;
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperLink::GetURI(int32_t aIndex, nsIURI** aURI)
 {
@@ -91,27 +103,31 @@ xpcAccessibleHyperLink::GetURI(int32_t a
     return NS_ERROR_INVALID_ARG;
 
   if (Intl().IsAccessible()) {
     if (aIndex >= static_cast<int32_t>(Intl().AsAccessible()->AnchorCount()))
       return NS_ERROR_INVALID_ARG;
 
     RefPtr<nsIURI>(Intl().AsAccessible()->AnchorURIAt(aIndex)).forget(aURI);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsCString spec;
     bool isURIValid = false;
     Intl().AsProxy()->AnchorURIAt(aIndex, spec, &isURIValid);
     if (!isURIValid)
       return NS_ERROR_FAILURE;
 
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uri.forget(aURI);
+#endif
   }
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 xpcAccessibleHyperLink::GetAnchor(int32_t aIndex, nsIAccessible** aAccessible)
@@ -126,17 +142,21 @@ xpcAccessibleHyperLink::GetAnchor(int32_
     return NS_ERROR_INVALID_ARG;
 
   if (Intl().IsAccessible()) {
     if (aIndex >= static_cast<int32_t>(Intl().AsAccessible()->AnchorCount()))
       return NS_ERROR_INVALID_ARG;
 
     NS_IF_ADDREF(*aAccessible = ToXPC(Intl().AsAccessible()->AnchorAt(aIndex)));
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     NS_IF_ADDREF(*aAccessible = ToXPC(Intl().AsProxy()->AnchorAt(aIndex)));
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperLink::GetValid(bool* aValid)
 {
@@ -144,13 +164,17 @@ xpcAccessibleHyperLink::GetValid(bool* a
   *aValid = false;
 
   if (Intl().IsNull())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     *aValid = Intl().AsAccessible()->IsLinkValid();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aValid = Intl().AsProxy()->IsLinkValid();
+#endif
   }
 
   return NS_OK;
 }
--- a/accessible/xpcom/xpcAccessibleHyperText.cpp
+++ b/accessible/xpcom/xpcAccessibleHyperText.cpp
@@ -42,36 +42,44 @@ xpcAccessibleHyperText::GetCharacterCoun
   *aCharacterCount = 0;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aCharacterCount = Intl()->CharacterCount();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aCharacterCount = mIntl.AsProxy()->CharacterCount();
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetText(int32_t aStartOffset, int32_t aEndOffset,
                                 nsAString& aText)
 {
   aText.Truncate();
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextSubstring(aStartOffset, aEndOffset, aText);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text;
     mIntl.AsProxy()->TextSubstring(aStartOffset, aEndOffset, text);
     aText = text;
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextBeforeOffset(int32_t aOffset,
                                             AccessibleTextBoundary aBoundaryType,
                                             int32_t* aStartOffset,
@@ -85,20 +93,24 @@ xpcAccessibleHyperText::GetTextBeforeOff
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                              aText);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text;
     mIntl.AsProxy()->GetTextBeforeOffset(aOffset, aBoundaryType, text,
                                          aStartOffset, aEndOffset);
     aText = text;
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAtOffset(int32_t aOffset,
                                         AccessibleTextBoundary aBoundaryType,
                                         int32_t* aStartOffset,
@@ -111,20 +123,24 @@ xpcAccessibleHyperText::GetTextAtOffset(
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                          aText);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text;
     mIntl.AsProxy()->GetTextAtOffset(aOffset, aBoundaryType, text, 
                                      aStartOffset, aEndOffset);
     aText = text;
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAfterOffset(int32_t aOffset,
                                            AccessibleTextBoundary aBoundaryType,
                                            int32_t* aStartOffset,
@@ -137,20 +153,24 @@ xpcAccessibleHyperText::GetTextAfterOffs
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                             aText);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text;
     mIntl.AsProxy()->GetTextAfterOffset(aOffset, aBoundaryType, text, 
                                         aStartOffset, aEndOffset);
     aText = text;
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterAtOffset(int32_t aOffset,
                                              char16_t* aCharacter)
 {
@@ -158,17 +178,21 @@ xpcAccessibleHyperText::GetCharacterAtOf
   *aCharacter = L'\0';
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aCharacter = Intl()->CharAt(aOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aCharacter = mIntl.AsProxy()->CharAt(aOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAttributes(bool aIncludeDefAttrs,
                                           int32_t aOffset,
                                           int32_t* aStartOffset,
@@ -184,24 +208,28 @@ xpcAccessibleHyperText::GetTextAttribute
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIPersistentProperties> props;
   if (mIntl.IsAccessible()) {
     props = Intl()->TextAttributes(aIncludeDefAttrs, aOffset, aStartOffset,
                                    aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     AutoTArray<Attribute, 10> attrs;
     mIntl.AsProxy()->TextAttributes(aIncludeDefAttrs, aOffset, &attrs,
         aStartOffset, aEndOffset);
     uint32_t attrCount = attrs.Length();
     nsAutoString unused;
     for (uint32_t i = 0; i < attrCount; i++) {
       props->SetStringProperty(attrs[i].Name(), attrs[i].Value(), unused);
     }
+#endif
   }
   props.forget(aAttributes);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetDefaultTextAttributes(nsIPersistentProperties** aAttributes)
@@ -211,23 +239,27 @@ xpcAccessibleHyperText::GetDefaultTextAt
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIPersistentProperties> props;
   if (mIntl.IsAccessible()) {
     props = Intl()->DefaultTextAttributes();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     AutoTArray<Attribute, 10> attrs;
     mIntl.AsProxy()->DefaultTextAttributes(&attrs);
     uint32_t attrCount = attrs.Length();
     nsAutoString unused;
     for (uint32_t i = 0; i < attrCount; i++) {
       props->SetStringProperty(attrs[i].Name(), attrs[i].Value(), unused);
     }
+#endif
   }
   props.forget(aAttributes);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterExtents(int32_t aOffset,
@@ -243,17 +275,21 @@ xpcAccessibleHyperText::GetCharacterExte
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsIntRect rect;
   if (mIntl.IsAccessible()) {
     rect = Intl()->CharBounds(aOffset, aCoordType);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     rect = mIntl.AsProxy()->CharBounds(aOffset, aCoordType);
+#endif
   }
   *aX = rect.x; *aY = rect.y;
   *aWidth = rect.width; *aHeight = rect.height;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetRangeExtents(int32_t aStartOffset, int32_t aEndOffset,
@@ -269,17 +305,21 @@ xpcAccessibleHyperText::GetRangeExtents(
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsIntRect rect;
   if (mIntl.IsAccessible()) {
     rect = Intl()->TextBounds(aStartOffset, aEndOffset, aCoordType);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     rect = mIntl.AsProxy()->TextBounds(aStartOffset, aEndOffset, aCoordType);
+#endif
   }
   *aX = rect.x; *aY = rect.y;
   *aWidth = rect.width; *aHeight = rect.height;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetOffsetAtPoint(int32_t aX, int32_t aY,
@@ -289,65 +329,81 @@ xpcAccessibleHyperText::GetOffsetAtPoint
   *aOffset = -1;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aOffset = Intl()->OffsetAtPoint(aX, aY, aCoordType);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aOffset = mIntl.AsProxy()->OffsetAtPoint(aX, aY, aCoordType);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCaretOffset(int32_t* aCaretOffset)
 {
   NS_ENSURE_ARG_POINTER(aCaretOffset);
   *aCaretOffset = -1;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aCaretOffset = Intl()->CaretOffset();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aCaretOffset = mIntl.AsProxy()->CaretOffset();
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetCaretOffset(int32_t aCaretOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->SetCaretOffset(aCaretOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->SetCaretOffset(aCaretOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetSelectionCount(int32_t* aSelectionCount)
 {
   NS_ENSURE_ARG_POINTER(aSelectionCount);
   *aSelectionCount = 0;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aSelectionCount = Intl()->SelectionCount();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aSelectionCount = mIntl.AsProxy()->SelectionCount();
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetSelectionBounds(int32_t aSelectionNum,
                                            int32_t* aStartOffset,
                                            int32_t* aEndOffset)
@@ -363,19 +419,23 @@ xpcAccessibleHyperText::GetSelectionBoun
     return NS_ERROR_INVALID_ARG;
 
   if (mIntl.IsAccessible()) {
     if (aSelectionNum >= Intl()->SelectionCount())
       return NS_ERROR_INVALID_ARG;
       
     Intl()->SelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString unused;
     mIntl.AsProxy()->SelectionBoundsAt(aSelectionNum, unused, aStartOffset, 
                                        aEndOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetSelectionBounds(int32_t aSelectionNum,
                                            int32_t aStartOffset,
                                            int32_t aEndOffset)
@@ -387,64 +447,80 @@ xpcAccessibleHyperText::SetSelectionBoun
     return NS_ERROR_INVALID_ARG;
 
   if (mIntl.IsAccessible()) {
       if (!Intl()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, 
                                         aEndOffset)) {
         return NS_ERROR_INVALID_ARG;
       }
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
       if (!mIntl.AsProxy()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, 
                                                 aEndOffset)) {
         return NS_ERROR_INVALID_ARG;
       }
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::AddSelection(int32_t aStartOffset, int32_t aEndOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->AddToSelection(aStartOffset, aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->AddToSelection(aStartOffset, aEndOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::RemoveSelection(int32_t aSelectionNum)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->RemoveFromSelection(aSelectionNum);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->RemoveFromSelection(aSelectionNum);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringTo(int32_t aStartOffset,
                                           int32_t aEndOffset,
                                           uint32_t aScrollType)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringToPoint(int32_t aStartOffset,
                                                int32_t aEndOffset,
                                                uint32_t aCoordinateType,
@@ -452,18 +528,22 @@ xpcAccessibleHyperText::ScrollSubstringT
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType,
                                    aX, aY);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->ScrollSubstringToPoint(aStartOffset, aEndOffset,
                                             aCoordinateType, aX, aY);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetEnclosingRange(nsIAccessibleTextRange** aRange)
 {
   NS_ENSURE_ARG_POINTER(aRange);
@@ -578,89 +658,113 @@ NS_IMETHODIMP
 xpcAccessibleHyperText::SetTextContents(const nsAString& aText)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->ReplaceText(aText);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text(aText);
     mIntl.AsProxy()->ReplaceText(text);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::InsertText(const nsAString& aText, int32_t aOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->InsertText(aText, aOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     nsString text(aText);
     mIntl.AsProxy()->InsertText(text, aOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::CopyText(int32_t aStartOffset, int32_t aEndOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->CopyText(aStartOffset, aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->CopyText(aStartOffset, aEndOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::CutText(int32_t aStartOffset, int32_t aEndOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->CutText(aStartOffset, aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->CutText(aStartOffset, aEndOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::DeleteText(int32_t aStartOffset, int32_t aEndOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->DeleteText(aStartOffset, aEndOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->DeleteText(aStartOffset, aEndOffset);
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::PasteText(int32_t aOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->PasteText(aOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     mIntl.AsProxy()->PasteText(aOffset);
+#endif
   }
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleHyperText
 
 NS_IMETHODIMP
@@ -670,34 +774,42 @@ xpcAccessibleHyperText::GetLinkCount(int
   *aLinkCount = 0;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aLinkCount = Intl()->LinkCount();
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aLinkCount = mIntl.AsProxy()->LinkCount();
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkAt(int32_t aIndex, nsIAccessibleHyperLink** aLink)
 {
   NS_ENSURE_ARG_POINTER(aLink);
   *aLink = nullptr;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     NS_IF_ADDREF(*aLink = ToXPC(Intl()->LinkAt(aIndex)));
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     NS_IF_ADDREF(*aLink = ToXPC(mIntl.AsProxy()->LinkAt(aIndex)));
+#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkIndex(nsIAccessibleHyperLink* aLink,
                                      int32_t* aIndex)
 {
@@ -707,22 +819,26 @@ xpcAccessibleHyperText::GetLinkIndex(nsI
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIAccessible> xpcLink(do_QueryInterface(aLink));
   if (Accessible* accLink = xpcLink->ToInternalAccessible()) {
     *aIndex = Intl()->LinkIndexOf(accLink);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     xpcAccessibleHyperText* linkHyperText =
       static_cast<xpcAccessibleHyperText*>(xpcLink.get());
     ProxyAccessible* proxyLink = linkHyperText->mIntl.AsProxy();
     if (proxyLink) {
       *aIndex = mIntl.AsProxy()->LinkIndexOf(proxyLink);
     }
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkIndexAtOffset(int32_t aOffset,
                                              int32_t* aLinkIndex)
@@ -731,12 +847,16 @@ xpcAccessibleHyperText::GetLinkIndexAtOf
   *aLinkIndex = -1; // API says this magic value means 'not found'
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aLinkIndex = Intl()->LinkIndexAtOffset(aOffset);
   } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     *aLinkIndex = mIntl.AsProxy()->LinkIndexAtOffset(aOffset);
+#endif
   }
   return NS_OK;
 }
--- a/accessible/xpcom/xpcAccessibleValue.cpp
+++ b/accessible/xpcom/xpcAccessibleValue.cpp
@@ -20,18 +20,22 @@ xpcAccessibleValue::GetMaximumValue(doub
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->MaxValue();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     value = Intl().AsProxy()->MaxValue();
+#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -45,18 +49,22 @@ xpcAccessibleValue::GetMinimumValue(doub
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->MinValue();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     value = Intl().AsProxy()->MinValue();
+#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -70,18 +78,22 @@ xpcAccessibleValue::GetCurrentValue(doub
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->CurValue();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     value = Intl().AsProxy()->CurValue();
+#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -91,18 +103,22 @@ xpcAccessibleValue::SetCurrentValue(doub
   if (Intl().IsNull())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     Intl().AsAccessible()->SetCurValue(aValue);
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     Intl().AsProxy()->SetCurValue(aValue);
+#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleValue::GetMinimumIncrement(double* aValue)
 {
@@ -113,17 +129,21 @@ xpcAccessibleValue::GetMinimumIncrement(
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->Step();
-  } else { 
+  } else {
+#if defined(XP_WIN)
+    return NS_ERROR_NOT_IMPLEMENTED;
+#else
     value = Intl().AsProxy()->Step();
+#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3304,16 +3304,17 @@ var PrintPreviewListener = {
     this._tabBeforePrintPreview = null;
     gInPrintPreviewMode = false;
     this._toggleAffectedChrome();
     if (this._simplifyPageTab) {
       gBrowser.removeTab(this._simplifyPageTab);
       this._simplifyPageTab = null;
     }
     gBrowser.removeTab(this._printPreviewTab);
+    gBrowser.deactivatePrintPreviewBrowsers();
     this._printPreviewTab = null;
   },
   _toggleAffectedChrome: function () {
     gNavToolbox.collapsed = gInPrintPreviewMode;
 
     if (gInPrintPreviewMode)
       this._hideChrome();
     else
@@ -3359,17 +3360,21 @@ var PrintPreviewListener = {
     if (this._chromeState.globalNotificationsOpen)
       document.getElementById("global-notificationbox").notificationsHidden = false;
 
     if (this._chromeState.syncNotificationsOpen)
       document.getElementById("sync-notifications").notificationsHidden = false;
 
     if (this._chromeState.sidebarOpen)
       SidebarUI.show(this._sidebarCommand);
-  }
+  },
+
+  activateBrowser(browser) {
+    gBrowser.activateBrowserForPrintPreview(browser);
+  },
 }
 
 function getMarkupDocumentViewer()
 {
   return gBrowser.markupDocumentViewer;
 }
 
 // This function is obsolete. Newer code should use <tooltip page="true"/> instead.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1593,18 +1593,16 @@
 
             let wasActive = document.activeElement == aBrowser;
 
             // Unmap the old outerWindowID.
             this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
 
             // Unhook our progress listener.
             let tab = this.getTabForBrowser(aBrowser);
-            // aBrowser needs to be inserted now if it hasn't been already.
-            this._insertBrowser(tab);
             let filter = this._tabFilters.get(tab);
             let listener = this._tabListeners.get(tab);
             aBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(listener);
 
             // We'll be creating a new listener, so destroy the old one.
             listener.destroy();
 
@@ -1629,18 +1627,17 @@
             }
 
             aBrowser.droppedLinkHandler = droppedLinkHandler;
 
             // Switching a browser's remoteness will create a new frameLoader.
             // As frameLoaders start out with an active docShell we have to
             // deactivate it if this is not the selected tab's browser or the
             // browser window is minimized.
-            aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
-                                         window.windowState != window.STATE_MINIMIZED);
+            aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);
 
             // Create a new tab progress listener for the new browser we just injected,
             // since tab progress listeners have logic for handling the initial about:blank
             // load
             listener = this.mTabProgressListener(tab, aBrowser, false, false);
             this._tabListeners.set(tab, listener);
             filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
 
@@ -1778,22 +1775,22 @@
         </body>
       </method>
 
       <method name="_createBrowser">
         <parameter name="aParams"/>
         <body>
           <![CDATA[
             // Supported parameters:
-            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank, relatedBrowser
+            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank, permanentKey
 
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let b = document.createElementNS(NS_XUL, "browser");
-            b.permanentKey = {};
+            b.permanentKey = aParams.permanentKey || {};
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
             if (aParams.userContextId) {
               b.setAttribute("usercontextid", aParams.userContextId);
@@ -1852,105 +1849,68 @@
               b.setAttribute("nodefaultsrc", "true");
             }
 
             return b;
           ]]>
         </body>
       </method>
 
-      <!--
-           `_createLazyBrowser` will define properties on the unbound lazy
-           browser which correspond to properties defined in XBL which will be
-           bound to the browser when it is inserted into the document.  If any
-           of these properties are accessed by consumers, `_insertBrowser` is
-           called and the browser is inserted to ensure that things don't break.
-           This list provides the names of properties that may be called while
-           the browser is in its unbound (lazy) state.
-      -->
-      <field name="_browserBindingProperties">
-        [
-          "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
-          "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
-          "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
-          "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
-          "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
-           "characterSet", "fullZoom", "textZoom", "webProgress",
-          "addProgressListener", "removeProgressListener",
-          "audioPlaybackStarted", "audioPlaybackStopped", "adjustPriority",
-          "pauseMedia", "stopMedia", "blockMedia", "resumeMedia",
-          "audioMuted", "mute", "unmute", "blockedPopups", "mIconURL", "lastURI",
-          "userTypedValue", "purgeSessionHistory", "stopScroll", "startScroll"
-        ]
-      </field>
-
-      <method name="_createLazyBrowser">
+      <method name="_linkBrowserToTab">
         <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            let browser = aTab.linkedBrowser;
-
-            let names = this._browserBindingProperties;
-
-            for (let i = 0; i < names.length; i++) {
-              let name = names[i];
-              let getter;
-              let setter;
-              switch (name) {
-                default:
-                  getter = () => {
-                    this._insertBrowser(aTab);
-                    return browser[name];
-                  };
-                  setter = (value) => {
-                    this._insertBrowser(aTab);
-                    return browser[name] = value;
-                  };
-              }
-              Object.defineProperty(browser, name, {
-                get: getter,
-                set: setter,
-                configurable: true,
-                enumerable: true
-              });
-            }
-
-          ]]>
-        </body>
-      </method>
-
-      <method name="_insertBrowser">
-        <parameter name="aTab"/>
+        <parameter name="aURI"/>
+        <parameter name="aParams"/>
         <body>
           <![CDATA[
             "use strict";
 
-            // If browser is already inserted, don't do anything.
-            if (aTab.linkedPanel) {
-              return;
-            }
-
-            let browser = aTab.linkedBrowser;
-
-            // If browser is a lazy browser delete the substitute properties.
-            if (this._browserBindingProperties[0] in browser) {
-              for (let name of this._browserBindingProperties) {
-                delete(browser[name]);
+            // Supported parameters:
+            // forceNotRemote, userContextId
+
+            let uriIsAboutBlank = !aURI || aURI == "about:blank";
+
+            // The new browser should be remote if this is an e10s window and
+            // the uri to load can be loaded remotely.
+            let remote = gMultiProcessBrowser &&
+                         !aParams.forceNotRemote &&
+                         E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
+
+            let browser;
+            let usingPreloadedContent = false;
+
+            // If we open a new tab with the newtab URL in the default
+            // userContext, check if there is a preloaded browser ready.
+            // Private windows are not included because both the label and the
+            // icon for the tab would be set incorrectly (see bug 1195981).
+            if (aURI == BROWSER_NEW_TAB_URL &&
+                !aParams.userContextId &&
+                !PrivateBrowsingUtils.isWindowPrivate(window)) {
+              browser = this._getPreloadedBrowser();
+              if (browser) {
+                usingPreloadedContent = true;
+                aTab.permanentKey = browser.permanentKey;
               }
             }
 
-            let { uri, remote, usingPreloadedContent } = aTab._browserParams;
-            delete(aTab._browserParams);
-
-            let uriIsAboutBlank = !uri || uri == "about:blank";
+            if (!browser) {
+              // No preloaded browser found, create one.
+              browser = this._createBrowser({permanentKey: aTab.permanentKey,
+                                             remote: remote,
+                                             uriIsAboutBlank: uriIsAboutBlank,
+                                             userContextId: aParams.userContextId,
+                                             relatedBrowser: aParams.relatedBrowser});
+            }
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
+            aTab.linkedBrowser = browser;
+            aTab.hasBrowser = true;
+            this._tabForBrowser.set(browser, aTab);
 
             // Inject the <browser> into the DOM if necessary.
             if (!notificationbox.parentNode) {
               // NB: this appendChild call causes us to run constructors for the
               // browser element, which fires off a bunch of notifications. Some
               // of those notifications can cause code to run that inspects our
               // state, so it is important that the tab element is fully
               // initialized by this point.
@@ -1976,18 +1936,20 @@
             // set the "nodefaultsrc" attribute that prevents a frameLoader
             // from being created as soon as the linked <browser> is inserted
             // into the DOM. We thus have to register the new outerWindowID
             // for non-remote browsers after we have called browser.loadURI().
             if (!remote) {
               this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
             }
 
-            var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} });
+            var evt = new CustomEvent("TabBrowserCreated", { bubbles: true, detail: {} });
             aTab.dispatchEvent(evt);
+
+            return { usingPreloadedContent: usingPreloadedContent };
           ]]>
         </body>
       </method>
 
       <method name="addTab">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
@@ -2031,16 +1993,18 @@
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
+            var uriIsAboutBlank = !aURI || aURI == "about:blank";
+
             if (!aURI || isBlankPageURL(aURI)) {
               t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
             } else if (aURI.toLowerCase().startsWith("javascript:")) {
               // This can go away when bug 672618 or bug 55696 are fixed.
               t.setAttribute("label", aURI);
             }
 
             if (aUserContextId) {
@@ -2073,67 +2037,35 @@
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             var position = this.tabs.length - 1;
             t._tPos = position;
+            t.permanentKey = {};
             this.tabContainer._setPositionalAttributes();
 
             this.tabContainer.updateVisibility();
 
-            // The new browser should be remote if this is an e10s window and
-            // the uri to load can be loaded remotely.
-            let remote = gMultiProcessBrowser &&
-                         !aForceNotRemote &&
-                         E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
-
-            let b;
-            let usingPreloadedContent = false;
-            let uriIsAboutBlank = !aURI || aURI == "about:blank";
-
-            // If we open a new tab with the newtab URL in the default
-            // userContext, check if there is a preloaded browser ready.
-            // Private windows are not included because both the label and the
-            // icon for the tab would be set incorrectly (see bug 1195981).
-            if (aURI == BROWSER_NEW_TAB_URL &&
-                !aUserContextId &&
-                !PrivateBrowsingUtils.isWindowPrivate(window)) {
-              b = this._getPreloadedBrowser();
-              if (b) {
-                usingPreloadedContent = true;
-              }
-            }
-
-            if (!b) {
-              // No preloaded browser found, create one.
-              b = this._createBrowser({remote: remote,
-                                       uriIsAboutBlank: uriIsAboutBlank,
-                                       userContextId: aUserContextId,
-                                       relatedBrowser: aRelatedBrowser});
-            }
-
-            t.linkedBrowser = b;
-            this._tabForBrowser.set(b, t);
-            t.permanentKey = b.permanentKey;
-            t._browserParams = {
-              uri: aURI,
-              remote: remote,
-              usingPreloadedContent: usingPreloadedContent,
+            // Currently in this incarnation of bug 906076, we are forcing the
+            // browser to immediately be linked.  In future incarnations of this
+            // bug this will be removed so we can leave the tab in its "lazy"
+            // state to be exploited for startup optimization.  Note that for
+            // now this must occur before "TabOpen" event is fired, as that will
+            // trigger SessionStore.jsm to run code that expects the existence
+            // of tab.linkedBrowser.
+            let browserParams = {
+              forceNotRemote: aForceNotRemote,
+              userContextId:  aUserContextId,
+              relatedBrowser: aRelatedBrowser
             };
-
-            // If we're creating a blank tab, create a lazy browser.
-            // Otherwise fully instantiate the browser now.
-            if (uriIsAboutBlank) {
-              this._createLazyBrowser(t);
-            } else {
-              this._insertBrowser(t);
-            }
+            let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
+            let b = t.linkedBrowser;
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
@@ -2779,19 +2711,16 @@
         </body>
       </method>
 
       <method name="_swapBrowserDocShells">
         <parameter name="aOurTab"/>
         <parameter name="aOtherBrowser"/>
         <body>
           <![CDATA[
-            // aOurTab's browser needs to be inserted now if it hasn't already.
-            this._insertBrowser(aOurTab);
-
             // Unhook our progress listener
             const filter = this._tabFilters.get(aOurTab);
             let tabListener = this._tabListeners.get(aOurTab);
             let ourBrowser = this.getBrowserForTab(aOurTab);
             ourBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(tabListener);
 
             // Make sure to unregister any open URIs.
@@ -2799,18 +2728,21 @@
 
             // Unmap old outerWindowIDs.
             this._outerWindowIDBrowserMap.delete(ourBrowser.outerWindowID);
             let remoteBrowser = aOtherBrowser.ownerDocument.defaultView.gBrowser;
             if (remoteBrowser) {
               remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
             }
 
-            aOtherBrowser.docShellIsActive = (ourBrowser == this.selectedBrowser &&
-                                              window.windowState != window.STATE_MINIMIZED);
+            // If switcher is active, it will intercept swap events and
+            // react as needed.
+            if (!this._switcher) {
+              aOtherBrowser.docShellIsActive = this.shouldActivateDocShell(ourBrowser);
+            }
 
             // Swap the docshells
             ourBrowser.swapDocShells(aOtherBrowser);
 
             if (ourBrowser.isRemoteBrowser) {
               // Switch outerWindowIDs for remote browsers.
               let ourOuterWindowID = ourBrowser._outerWindowID;
               ourBrowser._outerWindowID = aOtherBrowser._outerWindowID;
@@ -3306,16 +3238,66 @@
         <body>
           <![CDATA[
             return SessionStore.duplicateTab(window, aTab, 0, aRestoreTabImmediately);
           ]]>
         </body>
       </method>
 
       <!--
+        List of browsers whose docshells must be active in order for print preview
+        to work.
+      -->
+      <field name="_printPreviewBrowsers">
+        new Set()
+      </field>
+
+      <method name="activateBrowserForPrintPreview">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
+            this._printPreviewBrowsers.add(aBrowser);
+            if (this._switcher) {
+              this._switcher.activateBrowserForPrintPreview(aBrowser);
+            }
+            aBrowser.docShellIsActive = true;
+          ]]>
+        </body>
+      </method>
+
+      <method name="deactivatePrintPreviewBrowsers">
+        <body>
+          <![CDATA[
+            let browsers = this._printPreviewBrowsers;
+            this._printPreviewBrowsers = new Set();
+            for (let browser of browsers) {
+              browser.docShellIsActive = this.shouldActivateDocShell(browser);
+            }
+          ]]>
+        </body>
+      </method>
+
+      <!--
+        Returns true if a given browser's docshell should be active.
+      -->
+      <method name="shouldActivateDocShell">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
+            if (this._switcher) {
+              return this._switcher.shouldActivateDocShell(aBrowser);
+            }
+            return (aBrowser == this.selectedBrowser &&
+                    window.windowState != window.STATE_MINIMIZED) ||
+                   this._printPreviewBrowsers.has(aBrowser);
+          ]]>
+        </body>
+      </method>
+
+      <!--
         The tab switcher is responsible for asynchronously switching
         tabs in e10s. It waits until the new tab is ready (i.e., the
         layer tree is available) before switching to it. Then it
         unloads the layer tree for the old tab.
 
         The tab switcher is a state machine. For each tab, it
         maintains state about whether the layer tree for the tab is
         available, being loaded, being unloaded, or unavailable. It
@@ -3336,45 +3318,22 @@
 
         3. We discard layer trees on a delay. This way, if the user is
         switching among the same tabs frequently, we don't continually
         load the same tabs.
 
         It's important that we always show either the spinner or a tab
         whose layers are available. Otherwise the compositor will draw
         an entirely black frame, which is very jarring. To ensure this
-        never happens, we do the following:
-
-        1. When switching away from a tab, we assume the old tab might
-        still be drawn until a MozAfterPaint event occurs. Because
-        layout and compositing happen asynchronously, we don't have
-        any other way of knowing when the switch actually takes
-        place. Therefore, we don't unload the old tab until the next
-        MozAfterPaint event.
-
-        2. Suppose that the user switches from tab A to B and then
-        back to A. Suppose that we ask for tab A's layers to be
-        unloaded via message M1 after the first switch and then
-        request them again via message M2 once the second switch
-        happens. Both loading and unloading of layers happens
-        asynchronously, and this can cause problems. It's possible
-        that the content process publishes one last layer tree before
-        M1 is received. The parent process doesn't know that this
-        layer tree was published before M1 and not after M2, so it
-        will display the tab. However, once M1 arrives, the content
-        process will destroy the layer tree for A and now we will
-        display black for it.
-
-        To counter this problem, we keep tab A in a separate
-        "unloading" state until the layer tree is actually dropped in
-        the compositor thread. While the tab is in the "unloading"
-        state, we're not allowed to request layers for it. Once the
-        layers are dropped in the compositor, an event will fire and
-        we will transition the tab to the "unloaded" state. Then we
-        are free to request the tab's layers again.
+        never happens when switching away from a tab, we assume the
+        old tab might still be drawn until a MozAfterPaint event
+        occurs. Because layout and compositing happen asynchronously,
+        we don't have any other way of knowing when the switch
+        actually takes place. Therefore, we don't unload the old tab
+        until the next MozAfterPaint event.
       -->
       <field name="_switcher">null</field>
       <method name="_getSwitcher">
         <body><![CDATA[
           if (this._switcher) {
             return this._switcher;
           }
 
@@ -3411,21 +3370,16 @@
             unloadTimer: null, // UNLOAD_DELAY nsITimer instance.
 
             // Map from tabs to STATE_* (below).
             tabState: new Map(),
 
             // True if we're in the midst of switching tabs.
             switchInProgress: false,
 
-            // Keep an exact list of content processes (tabParent) in which
-            // we're actively suppressing the display port. This gives a robust
-            // way to make sure we don't forget to un-suppress.
-            activeSuppressDisplayport: new Set(),
-
             // Set of tabs that might be visible right now. We maintain
             // this set because we can't be sure when a tab is actually
             // drawn. A tab is added to this set when we ask to make it
             // visible. All tabs but the most recently shown tab are
             // removed from the set upon MozAfterPaint.
             maybeVisibleTabs: new Set([this.selectedTab]),
 
             STATE_UNLOADED: 0,
@@ -3457,79 +3411,93 @@
             getTabState: function(tab) {
               let state = this.tabState.get(tab);
               if (state === undefined) {
                 return this.STATE_UNLOADED;
               }
               return state;
             },
 
-            setTabState: function(tab, state) {
+            setTabStateNoAction(tab, state) {
               if (state == this.STATE_UNLOADED) {
                 this.tabState.delete(tab);
               } else {
                 this.tabState.set(tab, state);
               }
+            },
+
+            setTabState: function(tab, state) {
+              this.setTabStateNoAction(tab, state);
 
               let browser = tab.linkedBrowser;
-              let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+              let {tabParent} = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
               if (state == this.STATE_LOADING) {
-                // Ask for a MozLayerTreeReady event.
-                fl.requestNotifyLayerTreeReady();
+                this.assert(!this.minimized);
                 browser.docShellIsActive = true;
+                if (!tabParent) {
+                  this.onLayersReady(browser);
+		}
               } else if (state == this.STATE_UNLOADING) {
-                // Ask for MozLayerTreeCleared event.
-                fl.requestNotifyLayerTreeCleared();
                 browser.docShellIsActive = false;
+                if (!tabParent) {
+                  this.onLayersCleared(browser);
+                }
               }
             },
 
+            get minimized() {
+              return window.windowState == window.STATE_MINIMIZED;
+            },
+
             init: function() {
               this.log("START");
 
               window.addEventListener("MozAfterPaint", this);
               window.addEventListener("MozLayerTreeReady", this);
               window.addEventListener("MozLayerTreeCleared", this);
               window.addEventListener("TabRemotenessChange", this);
-              this.setTabState(this.requestedTab, this.STATE_LOADED);
+              window.addEventListener("sizemodechange", this);
+              window.addEventListener("SwapDocShells", this, true);
+              window.addEventListener("EndSwapDocShells", this, true);
+              if (!this.minimized) {
+                this.setTabState(this.requestedTab, this.STATE_LOADED);
+              }
             },
 
             destroy: function() {
               if (this.unloadTimer) {
                 this.clearTimer(this.unloadTimer);
                 this.unloadTimer = null;
               }
               if (this.loadTimer) {
                 this.clearTimer(this.loadTimer);
                 this.loadTimer = null;
               }
 
               window.removeEventListener("MozAfterPaint", this);
               window.removeEventListener("MozLayerTreeReady", this);
               window.removeEventListener("MozLayerTreeCleared", this);
               window.removeEventListener("TabRemotenessChange", this);
+              window.removeEventListener("sizemodechange", this);
+              window.removeEventListener("SwapDocShells", this, true);
+              window.removeEventListener("EndSwapDocShells", this, true);
 
               this.tabbrowser._switcher = null;
-
-              this.activeSuppressDisplayport.forEach(function(tabParent) {
-                tabParent.suppressDisplayport(false);
-              });
-              this.activeSuppressDisplayport.clear();
             },
 
             finish: function() {
               this.log("FINISH");
 
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
               this.assert(!this.spinnerTab);
               this.assert(!this.loadTimer);
               this.assert(!this.loadingTab);
               this.assert(this.lastVisibleTab === this.requestedTab);
-              this.assert(this.getTabState(this.requestedTab) == this.STATE_LOADED);
+              this.assert(this.minimized || this.getTabState(this.requestedTab) == this.STATE_LOADED);
 
               this.destroy();
 
               let toBrowser = this.requestedTab.linkedBrowser;
               toBrowser.setAttribute("type", "content-primary");
 
               this.tabbrowser._adjustFocusAfterTabSwitch(this.requestedTab);
 
@@ -3559,17 +3527,17 @@
                 // available, show it.
                 showTab = this.lastVisibleTab;
               } else {
                 // Show the requested tab. If it's not available, we'll show the spinner.
                 showTab = this.requestedTab;
               }
 
               // Show or hide the spinner as needed.
-              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED;
+              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED && !this.minimized;
               if (!needSpinner && this.spinnerTab) {
                 this.spinnerHidden();
                 this.tabbrowser.removeAttribute("pendingpaint");
                 this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
                 this.spinnerTab = null;
               } else if (needSpinner && this.spinnerTab !== showTab) {
                 if (this.spinnerTab) {
                   this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
@@ -3614,24 +3582,25 @@
                 dump("Assertion failure\n" + Error().stack);
                 throw new Error("Assertion failure");
               }
             },
 
             // We've decided to try to load requestedTab.
             loadRequestedTab: function() {
               this.assert(!this.loadTimer);
+              this.assert(!this.minimized);
 
               // loadingTab can be non-null here if we timed out loading the current tab.
               // In that case we just overwrite it with a different tab; it's had its chance.
               this.loadingTab = this.requestedTab;
               this.log("Loading tab " + this.tinfo(this.loadingTab));
 
+              this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
               this.setTabState(this.requestedTab, this.STATE_LOADING);
-              this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
             },
 
             // This function runs before every event. It fixes up the state
             // to account for closed tabs.
             preActions: function() {
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
 
@@ -3666,23 +3635,31 @@
                           this.getTabState(this.loadingTab) == this.STATE_LOADING);
 
               // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
               // the timer is set only when we're loading something.
               this.assert(!this.loadTimer || this.loadingTab);
               this.assert(!this.loadingTab || this.loadTimer);
 
               // If we're not loading anything, try loading the requested tab.
-              if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
+              let requestedState = this.getTabState(this.requestedTab);
+              if (!this.loadTimer && !this.minimized &&
+                  (requestedState == this.STATE_UNLOADED ||
+                   requestedState == this.STATE_UNLOADING)) {
                 this.loadRequestedTab();
               }
 
               // See how many tabs still have work to do.
               let numPending = 0;
               for (let [tab, state] of this.tabState) {
+                // Skip print preview browsers since they shouldn't affect tab switching.
+                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                  continue;
+                }
+
                 if (state == this.STATE_LOADED && tab !== this.requestedTab) {
                   numPending++;
                 }
                 if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
                   numPending++;
                 }
               }
 
@@ -3707,16 +3684,20 @@
               this.logState("onUnloadTimeout");
               this.unloadTimer = null;
               this.preActions();
 
               let numPending = 0;
 
               // Unload any tabs that can be unloaded.
               for (let [tab, state] of this.tabState) {
+                if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                  continue;
+                }
+
                 if (state == this.STATE_LOADED &&
                     !this.maybeVisibleTabs.has(tab) &&
                     tab !== this.lastVisibleTab &&
                     tab !== this.loadingTab &&
                     tab !== this.requestedTab)
                 {
                   this.setTabState(tab, this.STATE_UNLOADING);
                 }
@@ -3740,19 +3721,25 @@
               this.preActions();
               this.loadTimer = null;
               this.loadingTab = null;
               this.postActions();
             },
 
             // Fires when the layers become available for a tab.
             onLayersReady: function(browser) {
-              this.logState("onLayersReady");
-
               let tab = this.tabbrowser.getTabForBrowser(browser);
+              if (!tab) {
+                // Devtools sometimes makes its own remote browsers.
+                return;
+              }
+              this.logState(`onLayersReady(${tab._tPos})`);
+
+              this.assert(this.getTabState(tab) == this.STATE_LOADING ||
+                          this.getTabState(tab) == this.STATE_LOADED);
               this.setTabState(tab, this.STATE_LOADED);
 
               this.maybeFinishTabSwitch();
 
               if (this.loadingTab === tab) {
                 this.clearTimer(this.loadTimer);
                 this.loadTimer = null;
                 this.loadingTab = null;
@@ -3764,29 +3751,30 @@
             // around.
             onPaint: function() {
               this.maybeVisibleTabs.clear();
               this.maybeFinishTabSwitch();
             },
 
             // Called when we're done clearing the layers for a tab.
             onLayersCleared: function(browser) {
-              this.logState("onLayersCleared");
-
               let tab = this.tabbrowser.getTabForBrowser(browser);
               if (tab) {
+                this.logState(`onLayersCleared(${tab._tPos})`);
+                this.assert(this.getTabState(tab) == this.STATE_UNLOADING ||
+                            this.getTabState(tab) == this.STATE_UNLOADED);
                 this.setTabState(tab, this.STATE_UNLOADED);
               }
             },
 
             // Called when a tab switches from remote to non-remote. In this case
             // a MozLayerTreeReady notification that we requested may never fire,
             // so we need to simulate it.
             onRemotenessChange: function(tab) {
-              this.logState("onRemotenessChange");
+              this.logState(`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`);
               if (!tab.linkedBrowser.isRemoteBrowser) {
                 if (this.getTabState(tab) == this.STATE_LOADING) {
                   this.onLayersReady(tab.linkedBrowser);
                 } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
                   this.onLayersCleared(tab.linkedBrowser);
                 }
               }
             },
@@ -3799,44 +3787,108 @@
                 // going to be removed during this tick of the event loop.
                 // This will cause us to show a tab spinner instead.
                 this.preActions();
                 this.lastVisibleTab = null;
                 this.postActions();
               }
             },
 
+            onSizeModeChange() {
+              if (this.minimized) {
+                for (let [tab, state] of this.tabState) {
+                  // Skip print preview browsers since they shouldn't affect tab switching.
+                  if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
+                    continue;
+                  }
+
+                  if (state == this.STATE_LOADING || state == this.STATE_LOADED) {
+                    this.setTabState(tab, this.STATE_UNLOADING);
+                  }
+                  if (this.loadTimer) {
+                    this.clearTimer(this.loadTimer);
+                    this.loadTimer = null;
+                  }
+                  this.loadingTab = null;
+                }
+              } else {
+                // Do nothing. We'll automatically start loading the requested tab in
+                // postActions.
+              }
+            },
+
+            onSwapDocShells(ourBrowser, otherBrowser) {
+              // This event fires before the swap. ourBrowser is from
+              // our window. We save the state of otherBrowser since ourBrowser
+              // needs to take on that state at the end of the swap.
+
+              let otherTabbrowser = otherBrowser.ownerDocument.defaultView.gBrowser;
+              let otherState;
+              if (otherTabbrowser && otherTabbrowser._switcher) {
+                let otherTab = otherTabbrowser.getTabForBrowser(otherBrowser);
+                otherState = otherTabbrowser._switcher.getTabState(otherTab);
+              } else {
+                otherState = (otherBrowser.docShellIsActive
+                              ? this.STATE_LOADED
+                              : this.STATE_UNLOADED);
+              }
+
+              if (!this.swapMap) {
+                this.swapMap = new WeakMap();
+              }
+              this.swapMap.set(otherBrowser, otherState);
+            },
+
+            onEndSwapDocShells(ourBrowser, otherBrowser) {
+              // The swap has happened. We reset the loadingTab in
+              // case it has been swapped. We also set ourBrowser's state
+              // to whatever otherBrowser's state was before the swap.
+
+              if (this.loadTimer) {
+                // Clearing the load timer means that we will
+                // immediately display a spinner if ourBrowser isn't
+                // ready yet. Typically it will already be ready
+                // though. If it's not, we're probably in a new window,
+                // in which case we have no other tabs to display anyway.
+                this.clearTimer(this.loadTimer);
+                this.loadTimer = null;
+              }
+              this.loadingTab = null;
+
+              let otherState = this.swapMap.get(otherBrowser);
+              this.swapMap.delete(otherBrowser);
+
+              let ourTab = this.tabbrowser.getTabForBrowser(ourBrowser);
+              if (ourTab) {
+                this.setTabStateNoAction(ourTab, otherState);
+              }
+            },
+
+            shouldActivateDocShell(browser) {
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              let state = this.getTabState(tab);
+              return state == this.STATE_LOADING || state == this.STATE_LOADED;
+            },
+
+            activateBrowserForPrintPreview(browser) {
+	      let tab = this.tabbrowser.getTabForBrowser(browser);
+	      this.setTabState(tab, this.STATE_LOADING);
+	    },
+
             // Called when the user asks to switch to a given tab.
             requestTab: function(tab) {
               if (tab === this.requestedTab) {
                 return;
               }
 
-              // Instrumentation to figure out bug 1166351 - if the binding
-              // on the browser we're switching to has gone away, try to find out
-              // why. We should remove this and the checkBrowserBindingAlive
-              // method once bug 1166351 has been closed.
-              if (this.tabbrowser.AppConstants.E10S_TESTING_ONLY &&
-                  !this.checkBrowserBindingAlive(tab)) {
-                Cu.reportError("Please report the above errors in bug 1166351.");
-                return;
-              }
-
               this.logState("requestTab " + this.tinfo(tab));
               this.startTabSwitch();
 
               this.requestedTab = tab;
 
-              let browser = this.requestedTab.linkedBrowser;
-              let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
-              if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
-                fl.tabParent.suppressDisplayport(true);
-                this.activeSuppressDisplayport.add(fl.tabParent);
-              }
-
               this.preActions();
 
               if (this.unloadTimer) {
                 this.clearTimer(this.unloadTimer);
               }
               this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
 
               this.postActions();
@@ -3858,16 +3910,22 @@
               if (event.type == "MozLayerTreeReady") {
                 this.onLayersReady(event.originalTarget);
               } if (event.type == "MozAfterPaint") {
                 this.onPaint();
               } else if (event.type == "MozLayerTreeCleared") {
                 this.onLayersCleared(event.originalTarget);
               } else if (event.type == "TabRemotenessChange") {
                 this.onRemotenessChange(event.target);
+              } else if (event.type == "sizemodechange") {
+                this.onSizeModeChange();
+              } else if (event.type == "SwapDocShells") {
+                this.onSwapDocShells(event.originalTarget, event.detail);
+              } else if (event.type == "EndSwapDocShells") {
+                this.onEndSwapDocShells(event.originalTarget, event.detail);
               }
 
               this.postActions();
               this._processing = false;
             },
 
             /*
              * Telemetry and Profiler related helpers for recording tab switch
@@ -3941,52 +3999,16 @@
                 result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming");
               } catch (ex) {
               }
               this._shouldLog = result;
               this._logInit = true;
               return this._shouldLog;
             },
 
-            // Instrumentation for bug 1166351
-            checkBrowserBindingAlive: function(tab) {
-              let err = Cu.reportError;
-
-              if (!tab.linkedBrowser) {
-                err("Attempting to switch to tab that has no linkedBrowser.");
-                return false;
-              }
-
-              let b = tab.linkedBrowser;
-
-              if (!b._alive) {
-                // The browser binding has been removed. Dump a bunch of
-                // diagnostic information to the browser console.
-                let utils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-                let results = utils.getBindingURLs(b);
-                let urls = [];
-
-                for (let i = 0; i < results.length; ++i) {
-                  urls.push(results.queryElementAt(i, Ci.nsIURI).spec);
-                }
-                err("The browser has the following bindings:");
-                err(urls);
-                err("MozBinding is currently: " +
-                    window.getComputedStyle(b).MozBinding);
-                if (!b.parentNode) {
-                  err("Browser was removed from the DOM.");
-                } else {
-                  err("Parent is: " + b.parentNode.outerHTML);
-                }
-                return false;
-              }
-
-              return true;
-            },
-
             tinfo: function(tab) {
               if (tab) {
                 return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
               }
               return "null";
             },
 
             log: function(s) {
@@ -4345,17 +4367,17 @@
               this._handleKeyDownEvent(aEvent);
               break;
             case "keypress":
               this._handleKeyPressEventMac(aEvent);
               break;
             case "sizemodechange":
               if (aEvent.target == window) {
                 this.mCurrentBrowser.setDocShellIsActiveAndForeground(
-                  window.windowState != window.STATE_MINIMIZED);
+                  this.shouldActivateDocShell(this.mCurrentBrowser));
               }
               break;
           }
         ]]></body>
       </method>
 
       <method name="receiveMessage">
         <parameter name="aMessage"/>
@@ -4554,16 +4576,17 @@
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
+          this.mCurrentTab.hasBrowser = true;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
           this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
@@ -5749,31 +5772,16 @@
               let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
               Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
             }
           }
         ]]>
         </body>
       </method>
 
-      <method name="getRelatedElement">
-        <parameter name="aTab"/>
-        <body>
-        <![CDATA[
-          if (!aTab)
-            return null;
-          // If the tab's browser is lazy, we need to `_insertBrowser` in order
-          // to have a linkedPanel.  This will also serve to bind the browser
-          // and make it ready to use when the tab is selected.
-          this.tabbrowser._insertBrowser(aTab);
-          return document.getElementById(aTab.linkedPanel);
-        ]]>
-        </body>
-      </method>
-
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <property name="mAllTabsPopup" readonly="true"
                 onget="return document.getElementById('alltabs-popup');"/>
     </implementation>
 
     <handlers>
       <handler event="TabSelect" action="this._handleTabSelect();"/>
 
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -3232,17 +3232,27 @@ this.CustomizableUI = {
    *                  passing the document from which it was removed. This is
    *                  useful especially for 'view' type widgets that need to
    *                  cleanup after views that were constructed on the fly.
    * - onCommand(aEvt): Only useful for button widgets; a function that will be
    *                    invoked when the user activates the button.
    * - onClick(aEvt): Attached to all widgets; a function that will be invoked
    *                  when the user clicks the widget.
    * - onViewShowing(aEvt): Only useful for views; a function that will be
-   *                  invoked when a user shows your view.
+   *                  invoked when a user shows your view. If any event
+   *                  handler calls aEvt.preventDefault(), the view will
+   *                  not be shown.
+   *
+   *                  The event's `detail` property is an object with an
+   *                  `addBlocker` method. Handlers which need to
+   *                  perform asynchronous operations before the view is
+   *                  shown may pass this method a Promise, which will
+   *                  prevent the view from showing until it resolves.
+   *                  Additionally, if the promise resolves to the exact
+   *                  value `false`, the view will not be shown.
    * - onViewHiding(aEvt): Only useful for views; a function that will be
    *                  invoked when a user hides your view.
    * - tooltiptext:   string to use for the tooltip of the widget
    * - label:         string to use for the label of the widget
    * - removable:     whether the widget is removable (optional, default: true)
    *                  NB: if you specify false here, you must provide a
    *                  defaultArea, too.
    * - overflows:     whether widget can overflow when in an overflowable
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -176,17 +176,17 @@ const CustomizableWidgets = [
     type: "view",
     viewId: "PanelUI-history",
     shortcutId: "key_gotoHistory",
     tooltiptext: "history-panelmenu.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onViewShowing: function(aEvent) {
       // Populate our list of history
       const kMaxResults = 15;
-      let doc = aEvent.detail.ownerDocument;
+      let doc = aEvent.target.ownerDocument;
       let win = doc.defaultView;
 
       let options = PlacesUtils.history.getNewQueryOptions();
       options.excludeQueries = true;
       options.queryType = options.QUERY_TYPE_HISTORY;
       options.sortingMode = options.SORT_BY_DATE_DESCENDING;
       options.maxResults = kMaxResults;
       let query = PlacesUtils.history.getNewQuery();
@@ -904,17 +904,17 @@ const CustomizableWidgets = [
       if (feeds && feeds.length == 1 && isClick) {
         aEvent.preventDefault();
         aEvent.stopPropagation();
         win.FeedHandler.subscribeToFeed(feeds[0].href, aEvent);
         CustomizableUI.hidePanelForNode(aEvent.target);
       }
     },
     onViewShowing: function(aEvent) {
-      let doc = aEvent.detail.ownerDocument;
+      let doc = aEvent.target.ownerDocument;
       let container = doc.getElementById("PanelUI-feeds");
       let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true);
 
       // For no feeds or only a single one, don't show the panel.
       if (!gotView) {
         aEvent.preventDefault();
         aEvent.stopPropagation();
         return;
@@ -1123,17 +1123,17 @@ const CustomizableWidgets = [
       this.updateVisibility(aNode);
 
       if (!this.hasObserver) {
         Services.prefs.addObserver("privacy.userContext.enabled", this, true);
         this.hasObserver = true;
       }
     },
     onViewShowing: function(aEvent) {
-      let doc = aEvent.detail.ownerDocument;
+      let doc = aEvent.target.ownerDocument;
 
       let items = doc.getElementById("PanelUI-containersItems");
 
       while (items.firstChild) {
         items.firstChild.remove();
       }
 
       let fragment = doc.createDocumentFragment();
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -298,42 +298,33 @@ const PanelUI = {
 
   /**
    * Shows a subview in the panel with a given ID.
    *
    * @param aViewId the ID of the subview to show.
    * @param aAnchor the element that spawned the subview.
    * @param aPlacementArea the CustomizableUI area that aAnchor is in.
    */
-  showSubView: function(aViewId, aAnchor, aPlacementArea) {
+  showSubView: Task.async(function*(aViewId, aAnchor, aPlacementArea) {
     this._ensureEventListenersAdded();
     let viewNode = document.getElementById(aViewId);
     if (!viewNode) {
       Cu.reportError("Could not show panel subview with id: " + aViewId);
       return;
     }
 
     if (!aAnchor) {
       Cu.reportError("Expected an anchor when opening subview with id: " + aViewId);
       return;
     }
 
     if (aPlacementArea == CustomizableUI.AREA_PANEL) {
       this.multiView.showSubView(aViewId, aAnchor);
     } else if (!aAnchor.open) {
       aAnchor.open = true;
-      // Emit the ViewShowing event so that the widget definition has a chance
-      // to lazily populate the subview with things.
-      let evt = document.createEvent("CustomEvent");
-      evt.initCustomEvent("ViewShowing", true, true, viewNode);
-      viewNode.dispatchEvent(evt);
-      if (evt.defaultPrevented) {
-        aAnchor.open = false;
-        return;
-      }
 
       let tempPanel = document.createElement("panel");
       tempPanel.setAttribute("type", "arrow");
       tempPanel.setAttribute("id", "customizationui-widget-panel");
       tempPanel.setAttribute("class", "cui-widget-panel");
       tempPanel.setAttribute("viewId", aViewId);
       if (aAnchor.getAttribute("tabspecific")) {
         tempPanel.setAttribute("tabspecific", true);
@@ -349,41 +340,75 @@ const PanelUI = {
 
       let multiView = document.createElement("panelmultiview");
       multiView.setAttribute("id", "customizationui-widget-multiview");
       multiView.setAttribute("nosubviews", "true");
       tempPanel.appendChild(multiView);
       multiView.setAttribute("mainViewIsSubView", "true");
       multiView.setMainView(viewNode);
       viewNode.classList.add("cui-widget-panelview");
-      CustomizableUI.addPanelCloseListeners(tempPanel);
 
-      let panelRemover = function() {
-        tempPanel.removeEventListener("popuphidden", panelRemover);
+      let viewShown = false;
+      let panelRemover = () => {
         viewNode.classList.remove("cui-widget-panelview");
-        CustomizableUI.removePanelCloseListeners(tempPanel);
-        let evt = new CustomEvent("ViewHiding", {detail: viewNode});
-        viewNode.dispatchEvent(evt);
+        if (viewShown) {
+          CustomizableUI.removePanelCloseListeners(tempPanel);
+          tempPanel.removeEventListener("popuphidden", panelRemover);
+
+          let evt = new CustomEvent("ViewHiding", {detail: viewNode});
+          viewNode.dispatchEvent(evt);
+        }
         aAnchor.open = false;
 
         this.multiView.appendChild(viewNode);
-        tempPanel.parentElement.removeChild(tempPanel);
-      }.bind(this);
+        tempPanel.remove();
+      };
+
+      // Emit the ViewShowing event so that the widget definition has a chance
+      // to lazily populate the subview with things.
+      let detail = {
+        blockers: new Set(),
+        addBlocker(aPromise) {
+          this.blockers.add(aPromise);
+        },
+      };
+
+      let evt = new CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
+      viewNode.dispatchEvent(evt);
+
+      let cancel = evt.defaultPrevented;
+      if (detail.blockers.size) {
+        try {
+          let results = yield Promise.all(detail.blockers);
+          cancel = cancel || results.some(val => val === false);
+        } catch (e) {
+          Components.utils.reportError(e);
+          cancel = true;
+        }
+      }
+
+      if (cancel) {
+        panelRemover();
+        return;
+      }
+
+      viewShown = true;
+      CustomizableUI.addPanelCloseListeners(tempPanel);
       tempPanel.addEventListener("popuphidden", panelRemover);
 
       let iconAnchor =
         document.getAnonymousElementByAttribute(aAnchor, "class",
                                                 "toolbarbutton-icon");
 
       if (iconAnchor && aAnchor.id) {
         iconAnchor.setAttribute("consumeanchor", aAnchor.id);
       }
       tempPanel.openPopup(iconAnchor || aAnchor, "bottomcenter topright");
     }
-  },
+  }),
 
   /**
    * NB: The enable- and disableSingleSubviewPanelAnimations methods only
    * affect the hiding/showing animations of single-subview panels (tempPanel
    * in the showSubView method).
    */
   disableSingleSubviewPanelAnimations: function() {
     this._disableAnimations = true;
--- a/browser/components/customizableui/content/panelUI.xml
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -171,54 +171,74 @@
           this._shiftMainView();
         ]]></body>
       </method>
 
       <method name="showSubView">
         <parameter name="aViewId"/>
         <parameter name="aAnchor"/>
         <body><![CDATA[
-          let viewNode = this.querySelector("#" + aViewId);
-          viewNode.setAttribute("current", true);
-          // Emit the ViewShowing event so that the widget definition has a chance
-          // to lazily populate the subview with things.
-          let evt = document.createEvent("CustomEvent");
-          evt.initCustomEvent("ViewShowing", true, true, viewNode);
-          viewNode.dispatchEvent(evt);
-          if (evt.defaultPrevented) {
-            return;
-          }
+          Task.spawn(function*() {
+            let viewNode = this.querySelector("#" + aViewId);
+            viewNode.setAttribute("current", true);
+            // Emit the ViewShowing event so that the widget definition has a chance
+            // to lazily populate the subview with things.
+            let detail = {
+              blockers: new Set(),
+              addBlocker(aPromise) {
+                this.blockers.add(aPromise);
+              },
+            };
 
-          this._currentSubView = viewNode;
+            let evt = new CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
+            viewNode.dispatchEvent(evt);
+
+            let cancel = evt.defaultPrevented;
+            if (detail.blockers.size) {
+              try {
+                let results = yield Promise.all(detail.blockers);
+                cancel = cancel || results.some(val => val === false);
+              } catch (e) {
+                Components.utils.reportError(e);
+                cancel = true;
+              }
+            }
 
-          // Now we have to transition the panel. There are a few parts to this:
-          //
-          // 1) The main view content gets shifted so that the center of the anchor
-          //    node is at the left-most edge of the panel.
-          // 2) The subview deck slides in so that it takes up almost all of the
-          //    panel.
-          // 3) If the subview is taller then the main panel contents, then the panel
-          //    must grow to meet that new height. Otherwise, it must shrink.
-          //
-          // All three of these actions make use of CSS transformations, so they
-          // should all occur simultaneously.
-          this.setAttribute("viewtype", "subview");
-          this._shiftMainView(aAnchor);
+            if (cancel) {
+              return;
+            }
+
+            this._currentSubView = viewNode;
 
-          this._mainViewHeight = this._viewStack.clientHeight;
-
-          let newHeight = this._heightOfSubview(viewNode, this._subViews);
-          this._setViewContainerHeight(newHeight);
+            // Now we have to transition the panel. There are a few parts to this:
+            //
+            // 1) The main view content gets shifted so that the center of the anchor
+            //    node is at the left-most edge of the panel.
+            // 2) The subview deck slides in so that it takes up almost all of the
+            //    panel.
+            // 3) If the subview is taller then the main panel contents, then the panel
+            //    must grow to meet that new height. Otherwise, it must shrink.
+            //
+            // All three of these actions make use of CSS transformations, so they
+            // should all occur simultaneously.
+            this.setAttribute("viewtype", "subview");
+            this._shiftMainView(aAnchor);
 
-          this._subViewObserver.observe(viewNode, {
-            attributes: true,
-            characterData: true,
-            childList: true,
-            subtree: true
-          });
+            this._mainViewHeight = this._viewStack.clientHeight;
+
+            let newHeight = this._heightOfSubview(viewNode, this._subViews);
+            this._setViewContainerHeight(newHeight);
+
+            this._subViewObserver.observe(viewNode, {
+              attributes: true,
+              characterData: true,
+              childList: true,
+              subtree: true
+            });
+          }.bind(this));
         ]]></body>
       </method>
 
       <method name="_setViewContainerHeight">
         <parameter name="aHeight"/>
         <body><![CDATA[
           let container = this._viewContainer;
           this._transitioning = true;
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -1,42 +1,51 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
+                                  "resource://gre/modules/Timer.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+                                  "resource://gre/modules/Timer.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "colorUtils", () => {
   return require("devtools/shared/css-color").colorUtils;
 });
 
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   IconDetails,
 } = ExtensionUtils;
 
+const POPUP_PRELOAD_TIMEOUT_MS = 200;
+
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // WeakMap[Extension -> BrowserAction]
 var browserActionMap = new WeakMap();
 
 // Responsible for the browser_action section of the manifest as well
 // as the associated popup.
 function BrowserAction(options, extension) {
   this.extension = extension;
 
   let widgetId = makeWidgetId(extension.id);
   this.id = `${widgetId}-browser-action`;
   this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
   this.widget = null;
 
+  this.pendingPopup = null;
+  this.pendingPopupTimeout = null;
+
   this.tabManager = TabManager.for(extension);
 
   this.defaults = {
     enabled: true,
     title: options.default_title || extension.name,
     badgeText: "",
     badgeBackgroundColor: null,
     icon: IconDetails.normalize({path: options.default_icon}, extension),
@@ -81,16 +90,18 @@ BrowserAction.prototype = {
         }
       },
 
       onCreated: node => {
         node.classList.add("badged-button");
         node.classList.add("webextension-browser-action");
         node.setAttribute("constrain-size", "true");
 
+        node.onmousedown = event => this.handleEvent(event);
+
         this.updateButton(node, this.defaults);
       },
 
       onViewShowing: event => {
         let document = event.target.ownerDocument;
         let tabbrowser = document.defaultView.gBrowser;
 
         let tab = tabbrowser.selectedTab;
@@ -98,17 +109,18 @@ BrowserAction.prototype = {
         this.tabManager.addActiveTabPermission(tab);
 
         // If the widget has a popup URL defined, we open a popup, but do not
         // dispatch a click event to the extension.
         // If it has no popup URL defined, we dispatch a click event, but do not
         // open a popup.
         if (popupURL) {
           try {
-            new ViewPopup(this.extension, event.target, popupURL, this.browserStyle);
+            let popup = this.getPopup(document.defaultView, popupURL);
+            event.detail.addBlocker(popup.attach(event.target));
           } catch (e) {
             Cu.reportError(e);
             event.preventDefault();
           }
         } else {
           // This isn't not a hack, but it seems to provide the correct behavior
           // with the fewest complications.
           event.preventDefault();
@@ -118,16 +130,110 @@ BrowserAction.prototype = {
     });
 
     this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
                        (evt, tab) => { this.updateWindow(tab.ownerGlobal); });
 
     this.widget = widget;
   },
 
+  handleEvent(event) {
+    let button = event.target;
+    let window = button.ownerDocument.defaultView;
+
+    switch (event.type) {
+      case "mousedown":
+        if (event.button == 0) {
+          // Begin pre-loading the browser for the popup, so it's more likely to
+          // be ready by the time we get a complete click.
+          let tab = window.gBrowser.selectedTab;
+          let popupURL = this.getProperty(tab, "popup");
+
+          if (popupURL) {
+            this.pendingPopup = this.getPopup(window, popupURL);
+            window.addEventListener("mouseup", this, true);
+          } else {
+            this.clearPopup();
+          }
+        }
+        break;
+
+      case "mouseup":
+        if (event.button == 0) {
+          this.clearPopupTimeout();
+          // If we have a pending pre-loaded popup, cancel it after we've waited
+          // long enough that we can be relatively certain it won't be opening.
+          if (this.pendingPopup) {
+            if (event.target === this.widget.forWindow(window).node) {
+              this.pendingPopupTimeout = setTimeout(() => this.clearPopup(),
+                                                    POPUP_PRELOAD_TIMEOUT_MS);
+            } else {
+              this.clearPopup();
+            }
+          }
+        }
+        break;
+    }
+  },
+
+  /**
+   * Returns a potentially pre-loaded popup for the given URL in the given
+   * window. If a matching pre-load popup already exists, returns that.
+   * Otherwise, initializes a new one.
+   *
+   * If a pre-load popup exists which does not match, it is destroyed before a
+   * new one is created.
+   *
+   * @param {Window} window
+   *        The browser window in which to create the popup.
+   * @param {string} popupURL
+   *        The URL to load into the popup.
+   * @returns {ViewPopup}
+   */
+  getPopup(window, popupURL) {
+    this.clearPopupTimeout();
+    let {pendingPopup} = this;
+    this.pendingPopup = null;
+
+    if (pendingPopup) {
+      if (pendingPopup.window === window && pendingPopup.popupURL === popupURL) {
+        return pendingPopup;
+      }
+      pendingPopup.destroy();
+    }
+
+    let fixedWidth = this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL;
+    return new ViewPopup(this.extension, window, popupURL, this.browserStyle, fixedWidth);
+  },
+
+  /**
+   * Clears any pending pre-loaded popup and related timeouts.
+   */
+  clearPopup() {
+    this.clearPopupTimeout();
+    if (this.pendingPopup) {
+      this.pendingPopup.destroy();
+      this.pendingPopup = null;
+    }
+  },
+
+  /**
+   * Clears any pending timeouts to clear stale, pre-loaded popups.
+   */
+  clearPopupTimeout() {
+    if (this.pendingPopup) {
+      this.pendingPopup.window.removeEventListener("mouseup", this, true);
+    }
+
+    if (this.pendingPopupTimeout) {
+      clearTimeout(this.pendingPopupTimeout);
+      this.pendingPopupTimeout = null;
+    }
+  },
+
   // Update the toolbar button |node| with the tab context data
   // in |tabData|.
   updateButton(node, tabData) {
     let title = tabData.title || this.extension.name;
     node.setAttribute("tooltiptext", title);
     node.setAttribute("label", title);
 
     if (tabData.badgeText) {
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -3,28 +3,34 @@
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+                                  "resource://gre/modules/Timer.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
                                    "@mozilla.org/content/style-sheet-service;1",
                                    "nsIStyleSheetService");
 
 XPCOMUtils.defineLazyGetter(this, "colorUtils", () => {
   return require("devtools/shared/css-color").colorUtils;
 });
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
+const POPUP_LOAD_TIMEOUT_MS = 200;
+
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // Minimum time between two resizes.
 const RESIZE_TIMEOUT = 100;
 
 var {
   EventManager,
 } = ExtensionUtils;
@@ -81,72 +87,76 @@ XPCOMUtils.defineLazyGetter(this, "stand
     let winStyleSheet = styleSheetService.preloadSheet(styleSheetURI,
                                                        styleSheetService.AGENT_SHEET);
     stylesheets.push(winStyleSheet);
   }
   return stylesheets;
 });
 
 class BasePopup {
-  constructor(extension, viewNode, popupURL, browserStyle) {
-    let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
-
-    Services.scriptSecurityManager.checkLoadURIWithPrincipal(
-      extension.principal, popupURI,
-      Services.scriptSecurityManager.DISALLOW_SCRIPT);
-
+  constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false) {
     this.extension = extension;
-    this.popupURI = popupURI;
+    this.popupURL = popupURL;
     this.viewNode = viewNode;
     this.browserStyle = browserStyle;
     this.window = viewNode.ownerGlobal;
+    this.destroyed = false;
+    this.fixedWidth = fixedWidth;
+    this.ignoreResizes = true;
 
     this.contentReady = new Promise(resolve => {
       this._resolveContentReady = resolve;
     });
 
     this.viewNode.addEventListener(this.DESTROY_EVENT, this);
 
     let doc = viewNode.ownerDocument;
     let arrowContent = doc.getAnonymousElementByAttribute(this.panel, "class", "panel-arrowcontent");
     this.borderColor = doc.defaultView.getComputedStyle(arrowContent).borderTopColor;
 
     this.browser = null;
-    this.browserReady = this.createBrowser(viewNode, popupURI);
+    this.browserLoaded = new Promise((resolve, reject) => {
+      this.browserLoadedDeferred = {resolve, reject};
+    });
+    this.browserReady = this.createBrowser(viewNode, popupURL);
   }
 
   destroy() {
-    this.browserReady.then(() => {
-      this.browser.removeEventListener("DOMWindowCreated", this, true);
-      this.browser.removeEventListener("load", this, true);
-      this.browser.removeEventListener("DOMTitleChanged", this, true);
-      this.browser.removeEventListener("DOMWindowClose", this, true);
-      this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
+    this.destroyed = true;
+    this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
+    return this.browserReady.then(() => {
+      this.destroyBrowser(this.browser);
+      this.browser.remove();
+
       this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
       this.viewNode.style.maxHeight = "";
-      this.browser.remove();
 
-      this.panel.style.setProperty("--panel-arrowcontent-background", "");
-      this.panel.style.setProperty("--panel-arrow-image-vertical", "");
+      this.panel.style.removeProperty("--panel-arrowcontent-background");
+      this.panel.style.removeProperty("--panel-arrow-image-vertical");
 
       this.browser = null;
       this.viewNode = null;
     });
   }
 
+  destroyBrowser(browser) {
+    browser.removeEventListener("DOMWindowCreated", this, true);
+    browser.removeEventListener("load", this, true);
+    browser.removeEventListener("DOMContentLoaded", this, true);
+    browser.removeEventListener("DOMTitleChanged", this, true);
+    browser.removeEventListener("DOMWindowClose", this, true);
+    browser.removeEventListener("MozScrolledAreaChanged", this, true);
+  }
+
   // Returns the name of the event fired on `viewNode` when the popup is being
   // destroyed. This must be implemented by every subclass.
   get DESTROY_EVENT() {
     throw new Error("Not implemented");
   }
 
-  get fixedWidth() {
-    return false;
-  }
-
   get panel() {
     let panel = this.viewNode;
     while (panel.localName != "panel") {
       panel = panel.parentNode;
     }
     return panel;
   }
 
@@ -181,24 +191,29 @@ class BasePopup {
           this.closePopup();
         }
         break;
 
       case "DOMTitleChanged":
         this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
         break;
 
+      case "DOMContentLoaded":
+        this.browserLoadedDeferred.resolve();
+        this.resizeBrowser(true);
+        break;
+
       case "load":
         // We use a capturing listener, so we get this event earlier than any
         // load listeners in the content page. Resizing after a timeout ensures
         // that we calculate the size after the entire event cycle has completed
         // (unless someone spins the event loop, anyway), and hopefully after
         // the content has made any modifications.
         Promise.resolve().then(() => {
-          this.resizeBrowser();
+          this.resizeBrowser(true);
         });
 
         // Mutation observer to make sure the panel shrinks when the content does.
         new this.browser.contentWindow.MutationObserver(this.resizeBrowser.bind(this)).observe(
           this.browser.contentDocument.documentElement, {
             attributes: true,
             characterData: true,
             childList: true,
@@ -207,17 +222,17 @@ class BasePopup {
         break;
 
       case "MozScrolledAreaChanged":
         this.resizeBrowser();
         break;
     }
   }
 
-  createBrowser(viewNode, popupURI) {
+  createBrowser(viewNode, popupURL = null) {
     let document = viewNode.ownerDocument;
     this.browser = document.createElementNS(XUL_NS, "browser");
     this.browser.setAttribute("type", "content");
     this.browser.setAttribute("disableglobalhistory", "true");
     this.browser.setAttribute("transparent", "true");
     this.browser.setAttribute("class", "webextension-popup-browser");
     this.browser.setAttribute("webextension-view-type", "popup");
 
@@ -228,61 +243,78 @@ class BasePopup {
 
     // Note: When using noautohide panels, the popup manager will add width and
     // height attributes to the panel, breaking our resize code, if the browser
     // starts out smaller than 30px by 10px. This isn't an issue now, but it
     // will be if and when we popup debugging.
 
     viewNode.appendChild(this.browser);
 
+    let initBrowser = browser => {
+      browser.addEventListener("DOMWindowCreated", this, true);
+      browser.addEventListener("load", this, true);
+      browser.addEventListener("DOMContentLoaded", this, true);
+      browser.addEventListener("DOMTitleChanged", this, true);
+      browser.addEventListener("DOMWindowClose", this, true);
+      browser.addEventListener("MozScrolledAreaChanged", this, true);
+    };
+
+    if (!popupURL) {
+      initBrowser(this.browser);
+      return this.browser;
+    }
+
     return new Promise(resolve => {
       // The first load event is for about:blank.
       // We can't finish setting up the browser until the binding has fully
       // initialized. Waiting for the first load event guarantees that it has.
       let loadListener = event => {
         this.browser.removeEventListener("load", loadListener, true);
         resolve();
       };
       this.browser.addEventListener("load", loadListener, true);
     }).then(() => {
+      initBrowser(this.browser);
+
       let {contentWindow} = this.browser;
 
       contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)
                    .allowScriptsToClose();
 
-      this.browser.setAttribute("src", popupURI.spec);
-
-      this.browser.addEventListener("DOMWindowCreated", this, true);
-      this.browser.addEventListener("load", this, true);
-      this.browser.addEventListener("DOMTitleChanged", this, true);
-      this.browser.addEventListener("DOMWindowClose", this, true);
-      this.browser.addEventListener("MozScrolledAreaChanged", this, true);
+      this.browser.setAttribute("src", popupURL);
     });
   }
+
   // Resizes the browser to match the preferred size of the content (debounced).
-  resizeBrowser() {
+  resizeBrowser(ignoreThrottling = false) {
+    if (this.ignoreResizes) {
+      return;
+    }
+
+    if (ignoreThrottling && this.resizeTimeout) {
+      this.window.clearTimeout(this.resizeTimeout);
+      this.resizeTimeout = null;
+    }
+
     if (this.resizeTimeout == null) {
       this.resizeTimeout = this.window.setTimeout(() => {
         try {
           this._resizeBrowser();
         } finally {
           this.resizeTimeout = null;
         }
       }, RESIZE_TIMEOUT);
-      this._resizeBrowser(false);
+
+      this._resizeBrowser();
     }
   }
 
-  _resizeBrowser(clearTimeout = true) {
-    if (!this.browser) {
-      return;
-    }
-
-    let doc = this.browser.contentDocument;
+  _resizeBrowser() {
+    let doc = this.browser && this.browser.contentDocument;
     if (!doc || !doc.documentElement) {
       return;
     }
 
     let root = doc.documentElement;
     let body = doc.body;
     if (!body || doc.compatMode == "BackCompat") {
       // In quirks mode, the root element is used as the scroll frame, and the
@@ -388,16 +420,18 @@ global.PanelPopup = class PanelPopup ext
     panel.setAttribute("class", "browser-extension-panel");
     panel.setAttribute("type", "arrow");
     panel.setAttribute("role", "group");
 
     document.getElementById("mainPopupSet").appendChild(panel);
 
     super(extension, panel, popupURL, browserStyle);
 
+    this.ignoreResizes = false;
+
     this.contentReady.then(() => {
       panel.openPopup(imageNode, "bottomcenter topright", 0, 0, false, false);
     });
   }
 
   get DESTROY_EVENT() {
     return "popuphidden";
   }
@@ -413,48 +447,125 @@ global.PanelPopup = class PanelPopup ext
       if (this.viewNode) {
         this.viewNode.hidePopup();
       }
     });
   }
 };
 
 global.ViewPopup = class ViewPopup extends BasePopup {
-  constructor(...args) {
-    super(...args);
+  constructor(extension, window, popupURL, browserStyle, fixedWidth) {
+    let document = window.document;
+
+    // Create a temporary panel to hold the browser while it pre-loads its
+    // content. This panel will never be shown, but the browser's docShell will
+    // be swapped with the browser in the real panel when it's ready.
+    let panel = document.createElement("panel");
+    panel.setAttribute("type", "arrow");
+    document.getElementById("mainPopupSet").appendChild(panel);
+
+    super(extension, panel, popupURL, browserStyle, fixedWidth);
+
+    this.attached = false;
+    this.tempPanel = panel;
+
+    this.browser.classList.add("webextension-preload-browser");
+  }
 
-    // Store the initial height of the view, so that we never resize menu panel
-    // sub-views smaller than the initial height of the menu.
-    this.viewHeight = this.viewNode.boxObject.height;
+  /**
+   * Attaches the pre-loaded browser to the given view node, and reserves a
+   * promise which resolves when the browser is ready.
+   *
+   * @param {Element} viewNode
+   *        The node to attach the browser to.
+   * @returns {Promise<boolean>}
+   *        Resolves when the browser is ready. Resolves to `false` if the
+   *        browser was destroyed before it was fully loaded, and the popup
+   *        should be closed, or `true` otherwise.
+   */
+  attach(viewNode) {
+    return Task.spawn(function* () {
+      this.viewNode = viewNode;
+      this.viewNode.addEventListener(this.DESTROY_EVENT, this);
+
+      // Wait until the browser element is fully initialized, and give it at least
+      // a short grace period to finish loading its initial content, if necessary.
+      //
+      // In practice, the browser that was created by the mousdown handler should
+      // nearly always be ready by this point.
+      yield Promise.all([
+        this.browserReady,
+        Promise.race([
+          // This promise may be rejected if the popup calls window.close()
+          // before it has fully loaded.
+          this.browserLoaded.catch(() => {}),
+          new Promise(resolve => setTimeout(resolve, POPUP_LOAD_TIMEOUT_MS)),
+        ]),
+      ]);
 
-    // Calculate the extra height available on the screen above and below the
-    // menu panel. Use that to calculate the how much the sub-view may grow.
-    let popupRect = this.panel.getBoundingClientRect();
+      if (this.destroyed) {
+        return false;