Bug 1479037 - Assign a 32bit ID to each accessible, and make them retrievable from root 1/4. r=Jamie
authorEitan Isaacson <eitan@monotonous.org>
Thu, 11 Oct 2018 16:20:54 +0000
changeset 496467 7f44f29dc7ae52fbfe50aa8adad1ce9aae2fdc29
parent 496466 7a2f29bbe2a8b484893efffb8f032b53f1794766
child 496468 dab75afa1e1e7e10abb5f7c7931d3fd889778fe8
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJamie
bugs1479037
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1479037 - Assign a 32bit ID to each accessible, and make them retrievable from root 1/4. r=Jamie Differential Revision: https://phabricator.services.mozilla.com/D6681
accessible/android/AccessibleWrap.cpp
accessible/android/AccessibleWrap.h
accessible/android/DocAccessibleWrap.cpp
accessible/android/DocAccessibleWrap.h
accessible/android/Platform.cpp
accessible/android/ProxyAccessibleWrap.cpp
accessible/android/ProxyAccessibleWrap.h
accessible/android/RootAccessibleWrap.cpp
accessible/android/RootAccessibleWrap.h
accessible/android/moz.build
accessible/ipc/ProxyAccessibleBase.h
--- a/accessible/android/AccessibleWrap.cpp
+++ b/accessible/android/AccessibleWrap.cpp
@@ -1,23 +1,61 @@
 /* -*- 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 "AccessibleWrap.h"
 
+#include "DocAccessibleWrap.h"
+#include "IDSet.h"
+#include "nsAccessibilityService.h"
+
 using namespace mozilla::a11y;
 
+// IDs should be a positive 32bit integer.
+IDSet sIDSet(31UL);
+
 //-----------------------------------------------------
 // construction
 //-----------------------------------------------------
 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
   : Accessible(aContent, aDoc)
 {
+  if (aDoc) {
+    mID = AcquireID();
+    DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(aDoc);
+    doc->AddID(mID, this);
+  }
 }
 
 //-----------------------------------------------------
 // destruction
 //-----------------------------------------------------
-AccessibleWrap::~AccessibleWrap()
+AccessibleWrap::~AccessibleWrap() {}
+
+void
+AccessibleWrap::Shutdown()
 {
+  if (mDoc) {
+    if (mID > 0) {
+      if (auto doc = static_cast<DocAccessibleWrap*>(mDoc.get())) {
+        doc->RemoveID(mID);
+      }
+      ReleaseID(mID);
+      mID = 0;
+    }
+  }
+
+  Accessible::Shutdown();
 }
+
+int32_t
+AccessibleWrap::AcquireID()
+{
+  return sIDSet.GetID();
+}
+
+void
+AccessibleWrap::ReleaseID(int32_t aID)
+{
+  sIDSet.ReleaseID(aID);
+}
--- a/accessible/android/AccessibleWrap.h
+++ b/accessible/android/AccessibleWrap.h
@@ -1,25 +1,45 @@
 /* -*- 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/. */
 
 #ifndef mozilla_a11y_AccessibleWrap_h_
 #define mozilla_a11y_AccessibleWrap_h_
 
+#include "Accessible.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 #include "nsCOMPtr.h"
-#include "Accessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 class AccessibleWrap : public Accessible
 {
-public: // construction, destruction
+public:
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~AccessibleWrap();
+
+  virtual void Shutdown() override;
+
+  int32_t VirtualViewID() { return mID; }
+
+  static const int32_t kNoID = -1;
+
+protected:
+  // IDs should be a positive 32bit integer.
+  static int32_t AcquireID();
+  static void ReleaseID(int32_t aID);
+
+  int32_t mID;
 };
 
+static inline AccessibleWrap*
+WrapperFor(const ProxyAccessible* aProxy)
+{
+  return reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
+}
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/accessible/android/DocAccessibleWrap.cpp
@@ -0,0 +1,53 @@
+/* -*- 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 "DocAccessibleWrap.h"
+#include "nsIDocShell.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// DocAccessibleWrap
+////////////////////////////////////////////////////////////////////////////////
+
+DocAccessibleWrap::DocAccessibleWrap(nsIDocument* aDocument,
+                                     nsIPresShell* aPresShell)
+  : DocAccessible(aDocument, aPresShell)
+{
+  nsCOMPtr<nsIDocShellTreeItem> treeItem(aDocument->GetDocShell());
+
+  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
+  treeItem->GetParent(getter_AddRefs(parentTreeItem));
+
+  if (treeItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+      (!parentTreeItem ||
+       parentTreeItem->ItemType() == nsIDocShellTreeItem::typeChrome)) {
+    // The top-level content document gets this special ID.
+    mID = kNoID;
+  } else {
+    mID = AcquireID();
+  }
+}
+
+DocAccessibleWrap::~DocAccessibleWrap() {}
+
+AccessibleWrap*
+DocAccessibleWrap::GetAccessibleByID(int32_t aID) const
+{
+  if (AccessibleWrap* acc = mIDToAccessibleMap.Get(aID)) {
+    return acc;
+  }
+
+  // If the ID is not in the hash table, check the IDs of the child docs.
+  for (uint32_t i = 0; i < ChildDocumentCount(); i++) {
+    auto childDoc = reinterpret_cast<AccessibleWrap*>(GetChildDocumentAt(i));
+    if (childDoc->VirtualViewID() == aID) {
+      return childDoc;
+    }
+  }
+
+  return nullptr;
+}
--- a/accessible/android/DocAccessibleWrap.h
+++ b/accessible/android/DocAccessibleWrap.h
@@ -6,14 +6,35 @@
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "DocAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
-typedef DocAccessible DocAccessibleWrap;
+class DocAccessibleWrap : public DocAccessible
+{
+public:
+  DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
+  virtual ~DocAccessibleWrap();
+
+  /**
+   * Manage the mapping from id to Accessible.
+   */
+  void AddID(uint32_t aID, AccessibleWrap* aAcc)
+  {
+    mIDToAccessibleMap.Put(aID, aAcc);
+  }
+  void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
+  AccessibleWrap* GetAccessibleByID(int32_t aID) const;
+
+protected:
+  /*
+   * This provides a mapping from 32 bit id to accessible objects.
+   */
+  nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
+};
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/android/Platform.cpp
+++ b/accessible/android/Platform.cpp
@@ -1,37 +1,61 @@
 /* -*- 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 "Platform.h"
+#include "ProxyAccessibleWrap.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 void
 a11y::PlatformInit()
 {
 }
 
 void
 a11y::PlatformShutdown()
 {
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible*, uint32_t)
+a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
+  AccessibleWrap* wrapper = nullptr;
+  if (aProxy->IsDoc()) {
+    wrapper = new DocProxyAccessibleWrap(aProxy->AsDoc());
+  } else {
+    wrapper = new ProxyAccessibleWrap(aProxy);
+  }
+
+  wrapper->AddRef();
+  aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper));
 }
 
 void
-a11y::ProxyDestroyed(ProxyAccessible*)
+a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
+  AccessibleWrap* wrapper =
+    reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
+
+  // If aProxy is a document that was created, but
+  // RecvPDocAccessibleConstructor failed then aProxy->GetWrapper() will be
+  // null.
+  if (!wrapper) {
+    return;
+  }
+
+  wrapper->Shutdown();
+  aProxy->SetWrapper(0);
+  wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible*, uint32_t)
 {
 }
 
 void
new file mode 100644
--- /dev/null
+++ b/accessible/android/ProxyAccessibleWrap.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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 "ProxyAccessibleWrap.h"
+#include "nsIPersistentProperties2.h"
+
+using namespace mozilla::a11y;
+
+ProxyAccessibleWrap::ProxyAccessibleWrap(ProxyAccessible* aProxy)
+  : AccessibleWrap(nullptr, nullptr)
+{
+  mType = eProxyType;
+  mBits.proxy = aProxy;
+
+  if (aProxy->mHasValue) {
+    mStateFlags |= eHasNumericValue;
+  }
+
+  if (aProxy->mIsSelection) {
+    mGenericTypes |= eSelect;
+  }
+
+  if (aProxy->mIsHyperText) {
+    mGenericTypes |= eHyperText;
+  }
+
+  auto doc = reinterpret_cast<DocProxyAccessibleWrap*>(
+    Proxy()->Document()->GetWrapper());
+  if (doc) {
+    mID = AcquireID();
+    doc->AddID(mID, this);
+  }
+}
+
+void
+ProxyAccessibleWrap::Shutdown()
+{
+  auto doc = reinterpret_cast<DocProxyAccessibleWrap*>(
+    Proxy()->Document()->GetWrapper());
+  if (mID && doc) {
+    doc->RemoveID(mID);
+    ReleaseID(mID);
+    mID = 0;
+  }
+
+  mBits.proxy = nullptr;
+  mStateFlags |= eIsDefunct;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/android/ProxyAccessibleWrap.h
@@ -0,0 +1,98 @@
+/* -*- 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_ProxyAccessibleWrap_h
+#define MOZILLA_A11Y_ProxyAccessibleWrap_h
+
+#include "AccessibleWrap.h"
+#include "DocAccessibleParent.h"
+
+namespace mozilla {
+namespace a11y {
+
+class ProxyAccessibleWrap : public AccessibleWrap
+{
+public:
+  explicit ProxyAccessibleWrap(ProxyAccessible* aProxy);
+
+  virtual void Shutdown() override;
+};
+
+class DocProxyAccessibleWrap : public ProxyAccessibleWrap
+{
+public:
+  explicit DocProxyAccessibleWrap(DocAccessibleParent* aProxy)
+    : ProxyAccessibleWrap(aProxy)
+  {
+    mGenericTypes |= eDocument;
+
+    if (auto parent = ParentDocument()) {
+      mID = AcquireID();
+      parent->AddID(mID, this);
+    } else {
+      // top level
+      mID = kNoID;
+    }
+  }
+
+  virtual void Shutdown() override
+  {
+    if (mID) {
+      auto parent = ParentDocument();
+      if (parent) {
+        MOZ_ASSERT(mID != kNoID, "A non root accessible always has a parent");
+        parent->RemoveID(mID);
+        ReleaseID(mID);
+      }
+    }
+    mID = 0;
+    mBits.proxy = nullptr;
+    mStateFlags |= eIsDefunct;
+  }
+
+  DocProxyAccessibleWrap* ParentDocument()
+  {
+    DocAccessibleParent* proxy = static_cast<DocAccessibleParent*>(Proxy());
+    MOZ_ASSERT(proxy);
+    if (DocAccessibleParent* parent = proxy->ParentDoc()) {
+      return reinterpret_cast<DocProxyAccessibleWrap*>(parent->GetWrapper());
+    }
+
+    return nullptr;
+  }
+
+  DocProxyAccessibleWrap* GetChildDocumentAt(uint32_t aIndex)
+  {
+    auto doc = Proxy()->AsDoc();
+    if (doc && doc->ChildDocCount() > aIndex) {
+      return reinterpret_cast<DocProxyAccessibleWrap*>(
+        doc->ChildDocAt(aIndex)->GetWrapper());
+    }
+
+    return nullptr;
+  }
+
+  void AddID(uint32_t aID, AccessibleWrap* aAcc)
+  {
+    mIDToAccessibleMap.Put(aID, aAcc);
+  }
+  void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
+  AccessibleWrap* GetAccessibleByID(uint32_t aID) const
+  {
+    return mIDToAccessibleMap.Get(aID);
+  }
+
+private:
+  /*
+   * This provides a mapping from 32 bit id to accessible objects.
+   */
+  nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
+};
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/accessible/android/RootAccessibleWrap.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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 "Accessible-inl.h"
+#include "AccessibleOrProxy.h"
+#include "DocAccessibleParent.h"
+#include "ProxyAccessibleWrap.h"
+#include "SessionAccessibility.h"
+
+using namespace mozilla::a11y;
+
+RootAccessibleWrap::RootAccessibleWrap(nsIDocument* aDoc,
+                                       nsIPresShell* aPresShell)
+  : RootAccessible(aDoc, aPresShell)
+{
+}
+
+RootAccessibleWrap::~RootAccessibleWrap() {}
+
+AccessibleWrap*
+RootAccessibleWrap::GetContentAccessible()
+{
+  if (ProxyAccessible* proxy = GetPrimaryRemoteTopLevelContentDoc()) {
+    return WrapperFor(proxy);
+  }
+
+  // Find first document that is not defunct or hidden.
+  // This is exclusively for Fennec which has a deck of browser elements.
+  // Otherwise, standard GeckoView will only have one browser element.
+  for (size_t i = 0; i < ChildDocumentCount(); i++) {
+    DocAccessible* childDoc = GetChildDocumentAt(i);
+    if (childDoc && !childDoc->IsDefunct() && !childDoc->IsHidden()) {
+      return childDoc;
+    }
+  }
+
+  return nullptr;
+}
+
+AccessibleWrap*
+RootAccessibleWrap::FindAccessibleById(int32_t aID)
+{
+  AccessibleWrap* contentAcc = GetContentAccessible();
+
+  if (!contentAcc) {
+    return nullptr;
+  }
+
+  if (aID == AccessibleWrap::kNoID) {
+    return contentAcc;
+  }
+
+  if (contentAcc->IsProxy()) {
+    return FindAccessibleById(static_cast<DocProxyAccessibleWrap*>(contentAcc),
+                              aID);
+  }
+
+  return FindAccessibleById(
+    static_cast<DocAccessibleWrap*>(contentAcc->AsDoc()), aID);
+}
+
+AccessibleWrap*
+RootAccessibleWrap::FindAccessibleById(DocProxyAccessibleWrap* aDoc,
+                                       int32_t aID)
+{
+  AccessibleWrap* acc = aDoc->GetAccessibleByID(aID);
+  uint32_t index = 0;
+  while (!acc) {
+    auto child =
+      static_cast<DocProxyAccessibleWrap*>(aDoc->GetChildDocumentAt(index++));
+    if (!child) {
+      break;
+    }
+    acc = FindAccessibleById(child, aID);
+  }
+
+  return acc;
+}
+
+AccessibleWrap*
+RootAccessibleWrap::FindAccessibleById(DocAccessibleWrap* aDoc, int32_t aID)
+{
+  AccessibleWrap* acc = aDoc->GetAccessibleByID(aID);
+  uint32_t index = 0;
+  while (!acc) {
+    auto child =
+      static_cast<DocAccessibleWrap*>(aDoc->GetChildDocumentAt(index++));
+    if (!child) {
+      break;
+    }
+    acc = FindAccessibleById(child, aID);
+  }
+
+  return acc;
+}
--- a/accessible/android/RootAccessibleWrap.h
+++ b/accessible/android/RootAccessibleWrap.h
@@ -6,14 +6,32 @@
 #ifndef mozilla_a11y_RootAccessibleWrap_h__
 #define mozilla_a11y_RootAccessibleWrap_h__
 
 #include "RootAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
-typedef RootAccessible RootAccessibleWrap;
+class DocProxyAccessibleWrap;
+
+class RootAccessibleWrap : public RootAccessible
+{
+public:
+  RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
+  virtual ~RootAccessibleWrap();
+
+  AccessibleWrap* GetContentAccessible();
+
+  AccessibleWrap* FindAccessibleById(int32_t aID);
+
+  // Recursively searches for the accessible ID within the document tree.
+  AccessibleWrap* FindAccessibleById(DocAccessibleWrap* aDocument, int32_t aID);
+
+  // Recursively searches for the accessible ID within the proxy document tree.
+  AccessibleWrap* FindAccessibleById(DocProxyAccessibleWrap* aDocument,
+                                     int32_t aID);
+};
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/android/moz.build
+++ b/accessible/android/moz.build
@@ -6,17 +6,20 @@
 
 EXPORTS.mozilla.a11y += ['AccessibleWrap.h',
     'HyperTextAccessibleWrap.h',
     'SessionAccessibility.h',
 ]
 
 SOURCES += [
     'AccessibleWrap.cpp',
+    'DocAccessibleWrap.cpp',
     'Platform.cpp',
+    'ProxyAccessibleWrap.cpp',
+    'RootAccessibleWrap.cpp',
     'SessionAccessibility.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
--- a/accessible/ipc/ProxyAccessibleBase.h
+++ b/accessible/ipc/ProxyAccessibleBase.h
@@ -163,23 +163,24 @@ protected:
     , mWrapper(0)
     , mID(aID)
     , mRole(aRole)
     , mOuterDoc(false)
     , mIsDoc(false)
     , mHasValue(aInterfaces & Interfaces::VALUE)
     , mIsHyperLink(aInterfaces & Interfaces::HYPERLINK)
     , mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
+    , mIsSelection(aInterfaces & Interfaces::SELECTION)
   {
   }
 
   explicit ProxyAccessibleBase(DocAccessibleParent* aThisAsDoc) :
     mParent(kNoParent), mDoc(aThisAsDoc), mWrapper(0), mID(0),
     mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
-    mIsHyperLink(false), mIsHyperText(false)
+    mIsHyperLink(false), mIsHyperText(false), mIsSelection(false)
   {}
 
 protected:
   void SetParent(Derived* aParent);
 
 private:
   uintptr_t mParent;
   static const uintptr_t kNoParent = UINTPTR_MAX;
@@ -199,16 +200,17 @@ protected:
 private:
   bool mOuterDoc : 1;
 
 public:
   const bool mIsDoc: 1;
   const bool mHasValue: 1;
   const bool mIsHyperLink: 1;
   const bool mIsHyperText: 1;
+  const bool mIsSelection: 1;
 };
 
 extern template class ProxyAccessibleBase<ProxyAccessible>;
 
 }
 }
 
 #endif