summaryrefslogtreecommitdiffstats
path: root/accessible/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/ipc')
-rw-r--r--accessible/ipc/DocAccessibleChildBase.cpp257
-rw-r--r--accessible/ipc/DocAccessibleChildBase.h114
-rw-r--r--accessible/ipc/DocAccessibleParent.cpp1465
-rw-r--r--accessible/ipc/DocAccessibleParent.h442
-rw-r--r--accessible/ipc/DocAccessibleTypes.ipdlh19
-rw-r--r--accessible/ipc/IPCTypes.h233
-rw-r--r--accessible/ipc/RemoteAccessibleBase.cpp1775
-rw-r--r--accessible/ipc/RemoteAccessibleBase.h482
-rw-r--r--accessible/ipc/RemoteAccessibleShared.h206
-rw-r--r--accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp75
-rw-r--r--accessible/ipc/extension/android/DocAccessiblePlatformExtChild.h44
-rw-r--r--accessible/ipc/extension/android/DocAccessiblePlatformExtParent.cpp45
-rw-r--r--accessible/ipc/extension/android/DocAccessiblePlatformExtParent.h22
-rw-r--r--accessible/ipc/extension/android/PDocAccessiblePlatformExt.ipdl35
-rw-r--r--accessible/ipc/extension/android/moz.build29
-rw-r--r--accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.cpp281
-rw-r--r--accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.h87
-rw-r--r--accessible/ipc/extension/mac/DocAccessiblePlatformExtParent.h19
-rw-r--r--accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl73
-rw-r--r--accessible/ipc/extension/mac/moz.build28
-rw-r--r--accessible/ipc/extension/moz.build14
-rw-r--r--accessible/ipc/extension/other/DocAccessiblePlatformExtChild.h20
-rw-r--r--accessible/ipc/extension/other/DocAccessiblePlatformExtParent.h18
-rw-r--r--accessible/ipc/extension/other/PDocAccessiblePlatformExt.ipdl20
-rw-r--r--accessible/ipc/extension/other/moz.build17
-rw-r--r--accessible/ipc/moz.build70
-rw-r--r--accessible/ipc/other/DocAccessibleChild.cpp1590
-rw-r--r--accessible/ipc/other/DocAccessibleChild.h465
-rw-r--r--accessible/ipc/other/PDocAccessible.ipdl357
-rw-r--r--accessible/ipc/other/RemoteAccessible.cpp989
-rw-r--r--accessible/ipc/other/RemoteAccessible.h107
-rw-r--r--accessible/ipc/other/moz.build51
-rw-r--r--accessible/ipc/win/COMPtrTypes.cpp75
-rw-r--r--accessible/ipc/win/COMPtrTypes.h78
-rw-r--r--accessible/ipc/win/DocAccessibleChild.cpp341
-rw-r--r--accessible/ipc/win/DocAccessibleChild.h392
-rw-r--r--accessible/ipc/win/HandlerProvider.cpp974
-rw-r--r--accessible/ipc/win/HandlerProvider.h145
-rw-r--r--accessible/ipc/win/PDocAccessible.ipdl155
-rw-r--r--accessible/ipc/win/PlatformChild.cpp87
-rw-r--r--accessible/ipc/win/PlatformChild.h39
-rw-r--r--accessible/ipc/win/RemoteAccessible.cpp943
-rw-r--r--accessible/ipc/win/RemoteAccessible.h76
-rw-r--r--accessible/ipc/win/handler/AccessibleHandler.cpp2189
-rw-r--r--accessible/ipc/win/handler/AccessibleHandler.def12
-rw-r--r--accessible/ipc/win/handler/AccessibleHandler.h336
-rw-r--r--accessible/ipc/win/handler/AccessibleHandler.rc5
-rw-r--r--accessible/ipc/win/handler/AccessibleHandlerControl.cpp221
-rw-r--r--accessible/ipc/win/handler/AccessibleHandlerControl.h103
-rw-r--r--accessible/ipc/win/handler/HandlerChildEnumerator.cpp170
-rw-r--r--accessible/ipc/win/handler/HandlerChildEnumerator.h51
-rw-r--r--accessible/ipc/win/handler/HandlerData.acf11
-rw-r--r--accessible/ipc/win/handler/HandlerData.idl155
-rw-r--r--accessible/ipc/win/handler/HandlerDataCleanup.h95
-rw-r--r--accessible/ipc/win/handler/HandlerDataUUID.h.in16
-rw-r--r--accessible/ipc/win/handler/HandlerRelation.cpp138
-rw-r--r--accessible/ipc/win/handler/HandlerRelation.h46
-rw-r--r--accessible/ipc/win/handler/HandlerTextLeaf.cpp337
-rw-r--r--accessible/ipc/win/handler/HandlerTextLeaf.h110
-rw-r--r--accessible/ipc/win/handler/moz.build132
-rw-r--r--accessible/ipc/win/moz.build63
-rw-r--r--accessible/ipc/win/typelib/Accessible.idl15
-rw-r--r--accessible/ipc/win/typelib/moz.build16
63 files changed, 16975 insertions, 0 deletions
diff --git a/accessible/ipc/DocAccessibleChildBase.cpp b/accessible/ipc/DocAccessibleChildBase.cpp
new file mode 100644
index 0000000000..98c4a67c6a
--- /dev/null
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -0,0 +1,257 @@
+/* -*- 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/CacheConstants.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+
+#include "LocalAccessible-inl.h"
+#ifdef A11Y_LOG
+# include "Logging.h"
+#endif
+
+namespace mozilla {
+namespace a11y {
+
+/* static */
+void DocAccessibleChildBase::FlattenTree(LocalAccessible* aRoot,
+ nsTArray<LocalAccessible*>& aTree) {
+ MOZ_ASSERT(!aRoot->IsDoc(), "documents shouldn't be serialized");
+
+ aTree.AppendElement(aRoot);
+ // OuterDocAccessibles are special because we don't want to serialize the
+ // child doc here, we'll call PDocAccessibleConstructor in
+ // NotificationController.
+ uint32_t childCount = aRoot->IsOuterDoc() ? 0 : aRoot->ChildCount();
+
+ for (uint32_t i = 0; i < childCount; i++) {
+ FlattenTree(aRoot->LocalChildAt(i), aTree);
+ }
+}
+
+/* static */
+void DocAccessibleChildBase::SerializeTree(nsTArray<LocalAccessible*>& aTree,
+ nsTArray<AccessibleData>& aData) {
+ for (LocalAccessible* acc : aTree) {
+ uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
+#if defined(XP_WIN)
+ int32_t msaaId = StaticPrefs::accessibility_cache_enabled_AtStartup()
+ ? 0
+ : MsaaAccessible::GetChildIDFor(acc);
+#endif
+ a11y::role role = acc->Role();
+ uint32_t childCount = acc->IsOuterDoc() ? 0 : acc->ChildCount();
+
+ uint32_t genericTypes = acc->mGenericTypes;
+ if (acc->ARIAHasNumericValue()) {
+ // XXX: We need to do this because this requires a state check.
+ genericTypes |= eNumericValue;
+ }
+ if (acc->IsTextLeaf() || acc->IsImage()) {
+ // Ideally, we'd set eActionable for any Accessible with an ancedstor
+ // action. However, that requires an ancestor walk which is too expensive
+ // here. eActionable is only used by ATK. For now, we only expose ancestor
+ // actions on text leaf and image Accessibles. This means that we don't
+ // support "click ancestor" for ATK.
+ if (acc->ActionCount()) {
+ genericTypes |= eActionable;
+ }
+ } else if (acc->HasPrimaryAction()) {
+ genericTypes |= eActionable;
+ }
+
+#if defined(XP_WIN)
+ aData.AppendElement(AccessibleData(
+ id, msaaId, role, childCount, static_cast<AccType>(acc->mType),
+ static_cast<AccGenericType>(genericTypes), acc->mRoleMapEntryIndex));
+#else
+ aData.AppendElement(AccessibleData(
+ id, role, childCount, static_cast<AccType>(acc->mType),
+ static_cast<AccGenericType>(genericTypes), acc->mRoleMapEntryIndex));
+#endif
+ }
+}
+
+void DocAccessibleChildBase::InsertIntoIpcTree(LocalAccessible* aParent,
+ LocalAccessible* aChild,
+ uint32_t aIdxInParent,
+ bool aSuppressShowEvent) {
+ uint64_t parentID =
+ aParent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(aParent->UniqueID());
+ nsTArray<LocalAccessible*> shownTree;
+ FlattenTree(aChild, shownTree);
+ ShowEventData data(parentID, aIdxInParent,
+ nsTArray<AccessibleData>(shownTree.Length()),
+ aSuppressShowEvent ||
+ StaticPrefs::accessibility_cache_enabled_AtStartup());
+ SerializeTree(shownTree, data.NewTree());
+ if (ipc::ProcessChild::ExpectingShutdown()) {
+ return;
+ }
+ MaybeSendShowEvent(data, false);
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ nsTArray<CacheData> cache(shownTree.Length());
+ for (LocalAccessible* acc : shownTree) {
+ if (mDoc->IsAccessibleBeingMoved(acc)) {
+ // Even though we send moves as a hide and a show, we don't want to
+ // push the cache again for moves.
+ continue;
+ }
+ RefPtr<AccAttributes> fields =
+ acc->BundleFieldsForCache(CacheDomain::All, CacheUpdateType::Initial);
+ if (fields->Count()) {
+ uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
+ cache.AppendElement(CacheData(id, fields));
+ }
+ }
+ // The cache array might be empty if there were only moved Accessibles or if
+ // no Accessibles generated any cache data.
+ if (!cache.IsEmpty()) {
+ Unused << SendCache(CacheUpdateType::Initial, cache, !aSuppressShowEvent);
+ }
+ }
+}
+
+void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) {
+ LocalAccessible* child = aShowEvent->GetAccessible();
+ InsertIntoIpcTree(aShowEvent->LocalParent(), child, child->IndexInParent(),
+ false);
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvTakeFocus(
+ const uint64_t& aID) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->TakeFocus();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvScrollTo(
+ const uint64_t& aID, const uint32_t& aScrollType) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ RefPtr<PresShell> presShell = acc->Document()->PresShellPtr();
+ nsCOMPtr<nsIContent> content = acc->GetContent();
+ nsCoreUtils::ScrollTo(presShell, content, aScrollType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvTakeSelection(
+ const uint64_t& aID) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->TakeSelection();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvSetSelected(
+ const uint64_t& aID, const bool& aSelect) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->SetSelected(aSelect);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvVerifyCache(
+ const uint64_t& aID, const uint64_t& aCacheDomain, AccAttributes* aFields) {
+#ifdef A11Y_LOG
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ RefPtr<AccAttributes> localFields =
+ acc->BundleFieldsForCache(aCacheDomain, CacheUpdateType::Update);
+ bool mismatches = false;
+
+ for (auto prop : *localFields) {
+ if (prop.Value<DeleteEntry>()) {
+ if (aFields->HasAttribute(prop.Name())) {
+ if (!mismatches) {
+ logging::MsgBegin("Mismatch!", "Local and remote values differ");
+ logging::AccessibleInfo("", acc);
+ mismatches = true;
+ }
+ nsAutoCString propName;
+ prop.Name()->ToUTF8String(propName);
+ nsAutoString val;
+ aFields->GetAttribute(prop.Name(), val);
+ logging::MsgEntry(
+ "Remote value for %s should be empty, but instead it is '%s'",
+ propName.get(), NS_ConvertUTF16toUTF8(val).get());
+ }
+ continue;
+ }
+
+ nsAutoString localVal;
+ prop.ValueAsString(localVal);
+ nsAutoString remoteVal;
+ aFields->GetAttribute(prop.Name(), remoteVal);
+ if (!localVal.Equals(remoteVal)) {
+ if (!mismatches) {
+ logging::MsgBegin("Mismatch!", "Local and remote values differ");
+ logging::AccessibleInfo("", acc);
+ mismatches = true;
+ }
+ nsAutoCString propName;
+ prop.Name()->ToUTF8String(propName);
+ logging::MsgEntry("Fields differ: %s '%s' != '%s'", propName.get(),
+ NS_ConvertUTF16toUTF8(remoteVal).get(),
+ NS_ConvertUTF16toUTF8(localVal).get());
+ }
+ }
+ if (mismatches) {
+ logging::MsgEnd();
+ }
+#endif // A11Y_LOG
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvDoActionAsync(
+ const uint64_t& aID, const uint8_t& aIndex) {
+ if (LocalAccessible* acc = IdToAccessible(aID)) {
+ Unused << acc->DoAction(aIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChildBase::RecvSetCaretOffset(
+ const uint64_t& aID, const int32_t& aOffset) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole() && acc->IsValidOffset(aOffset)) {
+ acc->SetCaretOffset(aOffset);
+ }
+ return IPC_OK();
+}
+
+LocalAccessible* DocAccessibleChildBase::IdToAccessible(
+ const uint64_t& aID) const {
+ if (!aID) return mDoc;
+
+ if (!mDoc) return nullptr;
+
+ return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
+}
+
+HyperTextAccessible* DocAccessibleChildBase::IdToHyperTextAccessible(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return acc && acc->IsHyperText() ? acc->AsHyperText() : nullptr;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/DocAccessibleChildBase.h b/accessible/ipc/DocAccessibleChildBase.h
new file mode 100644
index 0000000000..2573675048
--- /dev/null
+++ b/accessible/ipc/DocAccessibleChildBase.h
@@ -0,0 +1,114 @@
+/* -*- 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 "mozilla/Unused.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace a11y {
+
+class LocalAccessible;
+class AccShowEvent;
+
+class DocAccessibleChildBase : public PDocAccessibleChild {
+ public:
+ explicit DocAccessibleChildBase(DocAccessible* aDoc)
+ : mDoc(aDoc), mIsRemoteConstructed(false) {
+ 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);
+ }
+
+ virtual void Shutdown() {
+ DetachDocument();
+ SendShutdown();
+ }
+
+ /**
+ * Serializes a shown tree and sends it to the chrome process.
+ */
+ void InsertIntoIpcTree(LocalAccessible* aParent, LocalAccessible* aChild,
+ uint32_t aIdxInParent, bool aSuppressShowEvent);
+ void ShowEvent(AccShowEvent* aShowEvent);
+
+ virtual void ActorDestroy(ActorDestroyReason) override {
+ if (!mDoc) {
+ return;
+ }
+
+ mDoc->SetIPCDoc(nullptr);
+ mDoc = nullptr;
+ }
+
+ virtual mozilla::ipc::IPCResult RecvTakeFocus(const uint64_t& aID) override;
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual mozilla::ipc::IPCResult RecvScrollTo(
+ const uint64_t& aID, const uint32_t& aScrollType) override;
+
+ virtual mozilla::ipc::IPCResult RecvTakeSelection(
+ const uint64_t& aID) override;
+ virtual mozilla::ipc::IPCResult RecvSetSelected(const uint64_t& aID,
+ const bool& aSelect) override;
+
+ virtual mozilla::ipc::IPCResult RecvVerifyCache(
+ const uint64_t& aID, const uint64_t& aCacheDomain,
+ AccAttributes* aFields) override;
+
+ virtual mozilla::ipc::IPCResult RecvDoActionAsync(
+ const uint64_t& aID, const uint8_t& aIndex) override;
+
+ virtual mozilla::ipc::IPCResult RecvSetCaretOffset(
+ const uint64_t& aID, const int32_t& aOffset) override;
+
+ protected:
+ static void FlattenTree(LocalAccessible* aRoot,
+ nsTArray<LocalAccessible*>& aTree);
+
+ static void SerializeTree(nsTArray<LocalAccessible*>& aTree,
+ nsTArray<AccessibleData>& aData);
+
+ virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser) {
+ Unused << SendShowEvent(aData, aFromUser);
+ }
+
+ void DetachDocument() {
+ if (mDoc) {
+ mDoc->SetIPCDoc(nullptr);
+ mDoc = nullptr;
+ }
+ }
+
+ bool IsConstructedInParentProcess() const { return mIsRemoteConstructed; }
+ void SetConstructedInParentProcess() { mIsRemoteConstructed = true; }
+
+ LocalAccessible* IdToAccessible(const uint64_t& aID) const;
+ HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
+
+ DocAccessible* mDoc;
+ bool mIsRemoteConstructed;
+
+ friend void DocAccessible::DoInitialUpdate();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_DocAccessibleChildBase_h
diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp
new file mode 100644
index 0000000000..54af3eb3c0
--- /dev/null
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -0,0 +1,1465 @@
+/* -*- 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 "CachedTableAccessible.h"
+#include "DocAccessibleParent.h"
+#include "mozilla/a11y/Platform.h"
+#include "mozilla/Components.h" // for mozilla::components
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+#include "xpcAccessibleDocument.h"
+#include "xpcAccEvents.h"
+#include "nsAccUtils.h"
+#include "nsIIOService.h"
+#include "TextRange.h"
+#include "Relation.h"
+#include "RootAccessible.h"
+
+#if defined(XP_WIN)
+# include "AccessibleWrap.h"
+# include "Compatibility.h"
+# include "mozilla/mscom/PassthruProxy.h"
+# include "mozilla/mscom/Ptr.h"
+# include "nsWinUtils.h"
+#else
+# include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
+#endif
+
+#if defined(ANDROID)
+# define ACQUIRE_ANDROID_LOCK \
+ MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
+#else
+# define ACQUIRE_ANDROID_LOCK \
+ do { \
+ } while (0);
+#endif
+
+namespace mozilla {
+
+#if defined(XP_WIN)
+namespace mscom {
+namespace detail {
+// Needed by mscom::PassthruProxy::Wrap<IAccessible>.
+template <>
+struct VTableSizer<IAccessible> {
+ // 3 methods in IUnknown + 4 in IDispatch + 21 in IAccessible = 28 total
+ enum { Size = 28 };
+};
+} // namespace detail
+} // namespace mscom
+#endif // defined (XP_WIN)
+
+namespace a11y {
+uint64_t DocAccessibleParent::sMaxDocID = 0;
+
+DocAccessibleParent::DocAccessibleParent()
+ : RemoteAccessible(this),
+ mParentDoc(kNoParentDoc),
+#if defined(XP_WIN)
+ mEmulatedWindowHandle(nullptr),
+#endif // defined(XP_WIN)
+ mTopLevel(false),
+ mTopLevelInContentProcess(false),
+ mShutdown(false),
+ mFocus(0),
+ mCaretId(0),
+ mCaretOffset(-1),
+ mIsCaretAtEndOfLine(false) {
+ sMaxDocID++;
+ mActorID = sMaxDocID;
+ MOZ_ASSERT(!LiveDocs().Get(mActorID));
+ LiveDocs().InsertOrUpdate(mActorID, this);
+}
+
+DocAccessibleParent::~DocAccessibleParent() {
+ UnregisterWeakMemoryReporter(this);
+ LiveDocs().Remove(mActorID);
+ MOZ_ASSERT(mChildDocs.Length() == 0);
+ MOZ_ASSERT(!ParentDoc());
+}
+
+already_AddRefed<DocAccessibleParent> DocAccessibleParent::New() {
+ RefPtr<DocAccessibleParent> dap(new DocAccessibleParent());
+ // We need to do this with a non-zero reference count. The easiest way is to
+ // do it in this static method and hide the constructor.
+ RegisterWeakMemoryReporter(dap);
+ return dap.forget();
+}
+
+void DocAccessibleParent::SetBrowsingContext(
+ dom::CanonicalBrowsingContext* aBrowsingContext) {
+ mBrowsingContext = aBrowsingContext;
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvShowEvent(
+ const ShowEventData& aData, const bool& aFromUser) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) return IPC_OK();
+
+ MOZ_ASSERT(CheckDocTree());
+
+ if (aData.NewTree().IsEmpty()) {
+ return IPC_FAIL(this, "No children being added");
+ }
+
+ RemoteAccessible* parent = GetAccessible(aData.ID());
+
+ // XXX This should really never happen, but sometimes we fail to fire the
+ // required show events.
+ if (!parent) {
+ NS_ERROR("adding child to unknown accessible");
+#ifdef DEBUG
+ return IPC_FAIL(this, "unknown parent accessible");
+#else
+ return IPC_OK();
+#endif
+ }
+
+ uint32_t newChildIdx = aData.Idx();
+ if (newChildIdx > parent->ChildCount()) {
+ NS_ERROR("invalid index to add child at");
+#ifdef DEBUG
+ return IPC_FAIL(this, "invalid index");
+#else
+ return IPC_OK();
+#endif
+ }
+
+ uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
+ MOZ_ASSERT(consumed == aData.NewTree().Length());
+
+ // XXX This shouldn't happen, but if we failed to add children then the below
+ // is pointless and can crash.
+ if (!consumed) {
+ return IPC_FAIL(this, "failed to add children");
+ }
+
+#ifdef DEBUG
+ for (uint32_t i = 0; i < consumed; i++) {
+ uint64_t id = aData.NewTree()[i].ID();
+ MOZ_ASSERT(mAccessibles.GetEntry(id));
+ }
+#endif
+
+ MOZ_ASSERT(CheckDocTree());
+
+ // Just update, no events.
+ if (aData.EventSuppressed()) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = parent->RemoteChildAt(newChildIdx);
+ ProxyShowHideEvent(target, parent, true, aFromUser);
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ RefPtr<xpcAccEvent> event =
+ new xpcAccEvent(type, xpcAcc, doc, node, aFromUser);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+uint32_t DocAccessibleParent::AddSubtree(
+ RemoteAccessible* aParent, const nsTArray<a11y::AccessibleData>& aNewTree,
+ uint32_t aIdx, uint32_t aIdxInParent) {
+ if (aNewTree.Length() <= aIdx) {
+ NS_ERROR("bad index in serialized tree!");
+ return 0;
+ }
+
+ const AccessibleData& newChild = aNewTree[aIdx];
+
+ RemoteAccessible* newProxy;
+ if ((newProxy = GetAccessible(newChild.ID()))) {
+ // This is a move. Reuse the Accessible; don't destroy it.
+ MOZ_ASSERT(!newProxy->RemoteParent());
+ aParent->AddChildAt(aIdxInParent, newProxy);
+ newProxy->SetParent(aParent);
+ } else {
+ newProxy = new RemoteAccessible(
+ newChild.ID(), aParent, this, newChild.Role(), newChild.Type(),
+ newChild.GenericTypes(), newChild.RoleMapEntryIndex());
+
+ aParent->AddChildAt(aIdxInParent, newProxy);
+ mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
+ ProxyCreated(newProxy);
+
+#if defined(XP_WIN)
+ if (!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ MsaaAccessible::GetFrom(newProxy)->SetID(newChild.MsaaID());
+ }
+#endif
+
+ mPendingOOPChildDocs.RemoveIf([&](dom::BrowserBridgeParent* bridge) {
+ MOZ_ASSERT(bridge->GetBrowserParent(),
+ "Pending BrowserBridgeParent should be alive");
+ if (bridge->GetEmbedderAccessibleId() != newChild.ID()) {
+ return false;
+ }
+ MOZ_ASSERT(bridge->GetEmbedderAccessibleDoc() == this);
+ if (DocAccessibleParent* childDoc = bridge->GetDocAccessibleParent()) {
+ AddChildDoc(childDoc, newChild.ID(), false);
+ }
+ return true;
+ });
+ }
+
+ if (newProxy->IsTableCell()) {
+ CachedTableAccessible::Invalidate(newProxy);
+ }
+
+ DebugOnly<bool> isOuterDoc = newProxy->ChildCount() == 1;
+
+ 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);
+ if (!consumed) return 0;
+
+ accessibles += consumed;
+ }
+
+ MOZ_ASSERT((isOuterDoc && kids == 0) || newProxy->ChildCount() == kids);
+
+ return accessibles;
+}
+
+void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) {
+ uint64_t id = aAcc->ID();
+ if (!mMovingIDs.Contains(id)) {
+ // This Accessible is being removed.
+ aAcc->Shutdown();
+ return;
+ }
+ // This is a move. Moves are sent as a hide and then a show, but for a move,
+ // we want to keep the Accessible alive for reuse later.
+ if (aAcc->IsTable() || aAcc->IsTableCell()) {
+ // For table cells, it's important that we do this before the parent is
+ // cleared because CachedTableAccessible::Invalidate needs the ancestry.
+ CachedTableAccessible::Invalidate(aAcc);
+ }
+ if (aAcc->IsHyperText()) {
+ aAcc->InvalidateCachedHyperTextOffsets();
+ }
+ aAcc->SetParent(nullptr);
+ mMovingIDs.EnsureRemoved(id);
+ if (aAcc->IsOuterDoc()) {
+ // Leave child documents alone. They are added and removed differently to
+ // normal children.
+ return;
+ }
+ // Some children might be removed. Handle children the same way.
+ for (RemoteAccessible* child : aAcc->mChildren) {
+ ShutdownOrPrepareForMove(child);
+ }
+ // Even if some children are kept, those will be re-attached when we handle
+ // the show event. For now, clear all of them.
+ aAcc->mChildren.Clear();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvHideEvent(
+ const uint64_t& aRootID, const bool& aFromUser) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) return IPC_OK();
+
+ MOZ_ASSERT(CheckDocTree());
+
+ // We shouldn't actually need this because mAccessibles shouldn't have an
+ // entry for the document itself, but it doesn't hurt to be explicit.
+ if (!aRootID) {
+ return IPC_FAIL(this, "Trying to hide entire document?");
+ }
+
+ ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
+ if (!rootEntry) {
+ NS_ERROR("invalid root being removed!");
+ return IPC_OK();
+ }
+
+ RemoteAccessible* root = rootEntry->mProxy;
+ if (!root) {
+ NS_ERROR("invalid root being removed!");
+ return IPC_OK();
+ }
+
+ RemoteAccessible* parent = root->RemoteParent();
+ ProxyShowHideEvent(root, parent, false, aFromUser);
+
+ RefPtr<xpcAccHideEvent> event = nullptr;
+ if (nsCoreUtils::AccEventObserversExist()) {
+ uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
+ xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
+ RemoteAccessible* next = root->RemoteNextSibling();
+ xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
+ RemoteAccessible* prev = root->RemotePrevSibling();
+ xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
+ xpcNext, xpcPrev);
+ }
+
+ parent->RemoveChild(root);
+ ShutdownOrPrepareForMove(root);
+
+ MOZ_ASSERT(CheckDocTree());
+
+ if (event) {
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvEvent(
+ const uint64_t& aID, const uint32_t& aEventType) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* remote = GetAccessible(aID);
+ if (!remote) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+ FireEvent(remote, aEventType);
+ return IPC_OK();
+}
+
+void DocAccessibleParent::FireEvent(RemoteAccessible* aAcc,
+ const uint32_t& aEventType) {
+ if (aEventType == nsIAccessibleEvent::EVENT_FOCUS) {
+#ifdef ANDROID
+ if (FocusMgr()) {
+ FocusMgr()->SetFocusedRemoteDoc(this);
+ }
+#endif
+ mFocus = aAcc->ID();
+ }
+
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ if (aEventType == nsIAccessibleEvent::EVENT_REORDER ||
+ aEventType == nsIAccessibleEvent::EVENT_INNER_REORDER) {
+ for (RemoteAccessible* child = aAcc->RemoteFirstChild(); child;
+ child = child->RemoteNextSibling()) {
+ child->InvalidateGroupInfo();
+ }
+ } else if (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE &&
+ aAcc == this) {
+ // A DocAccessible gets the STALE state while it is still loading, but we
+ // don't fire a state change for that. That state might have been
+ // included in the initial cache push, so clear it here.
+ // We also clear the BUSY state here. Although we do fire a state change
+ // for that, we fire it after doc load complete. It doesn't make sense
+ // for the document to report BUSY after doc load complete and doing so
+ // confuses JAWS.
+ UpdateStateCache(states::STALE | states::BUSY, false);
+ }
+ }
+
+ ProxyEvent(aAcc, aEventType);
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return;
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(aAcc);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ bool fromUser = true; // XXX fix me
+ RefPtr<xpcAccEvent> event =
+ new xpcAccEvent(aEventType, xpcAcc, doc, node, fromUser);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvStateChangeEvent(
+ const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ if (!target) {
+ NS_ERROR("we don't know about the target of a state change event!");
+ return IPC_OK();
+ }
+
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ target->UpdateStateCache(aState, aEnabled);
+ }
+ ProxyStateChangeEvent(target, aState, aEnabled);
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
+ bool extra;
+ uint32_t state = nsAccUtils::To32States(aState, &extra);
+ bool fromUser = true; // XXX fix this
+ nsINode* node = nullptr; // XXX can we do better?
+ RefPtr<xpcAccStateChangeEvent> event = new xpcAccStateChangeEvent(
+ type, xpcAcc, doc, node, fromUser, state, extra, aEnabled);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
+ const uint64_t& aID,
+#if defined(XP_WIN)
+ const LayoutDeviceIntRect& aCaretRect,
+#endif // defined (XP_WIN)
+ const int32_t& aOffset, const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine, const int32_t& aGranularity) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* proxy = GetAccessible(aID);
+ if (!proxy) {
+ NS_ERROR("unknown caret move event target!");
+ return IPC_OK();
+ }
+
+ mCaretId = aID;
+ mCaretOffset = aOffset;
+ mIsCaretAtEndOfLine = aIsAtEndOfLine;
+ if (aIsSelectionCollapsed) {
+ // We don't fire selection events for collapsed selections, but we need to
+ // ensure we don't have a stale cached selection; e.g. when selecting
+ // forward and then unselecting backward.
+ mTextSelections.ClearAndRetainStorage();
+ mTextSelections.AppendElement(TextRangeData(aID, aID, aOffset, aOffset));
+ }
+
+#if defined(XP_WIN)
+ ProxyCaretMoveEvent(proxy, aCaretRect, aGranularity);
+#else
+ ProxyCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity);
+#endif
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ bool fromUser = true; // XXX fix me
+ uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
+ RefPtr<xpcAccCaretMoveEvent> event = new xpcAccCaretMoveEvent(
+ type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed,
+ aIsAtEndOfLine, aGranularity);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvTextChangeEvent(
+ const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
+ const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ if (!target) {
+ NS_ERROR("text change event target is unknown!");
+ return IPC_OK();
+ }
+
+ ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED
+ : nsIAccessibleEvent::EVENT_TEXT_REMOVED;
+ nsINode* node = nullptr;
+ RefPtr<xpcAccTextChangeEvent> event = new xpcAccTextChangeEvent(
+ type, xpcAcc, doc, node, aFromUser, aStart, aLen, aIsInsert, aStr);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvSyncTextChangeEvent(
+ const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
+ const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
+ return RecvTextChangeEvent(aID, aStr, aStart, aLen, aIsInsert, aFromUser);
+}
+
+#endif // defined(XP_WIN)
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectionEvent(
+ const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ RemoteAccessible* widget = GetAccessible(aWidgetID);
+ if (!target || !widget) {
+ NS_ERROR("invalid id in selection event");
+ return IPC_OK();
+ }
+
+ ProxySelectionEvent(target, widget, aType);
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+ xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
+ xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
+ RefPtr<xpcAccEvent> event =
+ new xpcAccEvent(aType, xpcTarget, xpcDoc, nullptr, false);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvVirtualCursorChangeEvent(
+ const uint64_t& aID, const uint64_t& aOldPositionID,
+ const int32_t& aOldStartOffset, const int32_t& aOldEndOffset,
+ const uint64_t& aNewPositionID, const int32_t& aNewStartOffset,
+ const int32_t& aNewEndOffset, const int16_t& aReason,
+ const int16_t& aBoundaryType, const bool& aFromUser) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ RemoteAccessible* oldPosition = GetAccessible(aOldPositionID);
+ RemoteAccessible* newPosition = GetAccessible(aNewPositionID);
+
+ if (!target) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+#if defined(ANDROID)
+ ProxyVirtualCursorChangeEvent(
+ target, oldPosition, aOldStartOffset, aOldEndOffset, newPosition,
+ aNewStartOffset, aNewEndOffset, aReason, aBoundaryType, aFromUser);
+#endif
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ RefPtr<xpcAccVirtualCursorChangeEvent> event =
+ new xpcAccVirtualCursorChangeEvent(
+ nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED,
+ GetXPCAccessible(target), doc, nullptr, aFromUser,
+ GetXPCAccessible(oldPosition), aOldStartOffset, aOldEndOffset,
+ GetXPCAccessible(newPosition), aNewStartOffset, aNewEndOffset,
+ aReason, aBoundaryType);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
+ const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
+ const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
+ const uint32_t& aMaxScrollY) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ if (!target) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+#if defined(ANDROID)
+ ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
+ aMaxScrollY);
+#else
+ ProxyEvent(target, aType);
+#endif
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ bool fromUser = true; // XXX: Determine if this was from user input.
+ RefPtr<xpcAccScrollingEvent> event =
+ new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
+ aScrollY, aMaxScrollX, aMaxScrollY);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvCache(
+ const mozilla::a11y::CacheUpdateType& aUpdateType,
+ nsTArray<CacheData>&& aData, const bool& aDispatchShowEvent) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ for (auto& entry : aData) {
+ RemoteAccessible* remote = GetAccessible(entry.ID());
+ if (!remote) {
+ MOZ_ASSERT_UNREACHABLE("No remote found!");
+ continue;
+ }
+
+ remote->ApplyCache(aUpdateType, entry.Fields());
+ }
+
+ if (aDispatchShowEvent && !aData.IsEmpty()) {
+ // We might need to dispatch a show event for an initial cache push. We
+ // should never dispatch a show event for a (non-initial) cache update.
+ MOZ_ASSERT(aUpdateType == CacheUpdateType::Initial);
+ RemoteAccessible* target = GetAccessible(aData.ElementAt(0).ID());
+ if (!target) {
+ MOZ_ASSERT_UNREACHABLE("No remote found for initial cache push!");
+ return IPC_OK();
+ }
+ // We never dispatch a show event for the doc itself.
+ MOZ_ASSERT(!target->IsDoc() && target->RemoteParent());
+
+ ProxyShowHideEvent(target, target->RemoteParent(), true, false);
+
+ if (nsCoreUtils::AccEventObserversExist()) {
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ RefPtr<xpcAccEvent> event = new xpcAccEvent(
+ nsIAccessibleEvent::EVENT_SHOW, xpcAcc, doc, node, false);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+ }
+ }
+
+ if (nsCOMPtr<nsIObserverService> obsService =
+ services::GetObserverService()) {
+ obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectedAccessiblesChanged(
+ nsTArray<uint64_t>&& aSelectedIDs, nsTArray<uint64_t>&& aUnselectedIDs) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ for (auto& id : aSelectedIDs) {
+ RemoteAccessible* remote = GetAccessible(id);
+ if (!remote) {
+ MOZ_ASSERT_UNREACHABLE("No remote found!");
+ continue;
+ }
+
+ remote->UpdateStateCache(states::SELECTED, true);
+ }
+
+ for (auto& id : aUnselectedIDs) {
+ RemoteAccessible* remote = GetAccessible(id);
+ if (!remote) {
+ MOZ_ASSERT_UNREACHABLE("No remote found!");
+ continue;
+ }
+
+ remote->UpdateStateCache(states::SELECTED, false);
+ }
+
+ if (nsCOMPtr<nsIObserverService> obsService =
+ services::GetObserverService()) {
+ obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvAccessiblesWillMove(
+ nsTArray<uint64_t>&& aIDs) {
+ for (uint64_t id : aIDs) {
+ mMovingIDs.EnsureInserted(id);
+ }
+ return IPC_OK();
+}
+
+#if !defined(XP_WIN)
+mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
+ const uint64_t& aID, const nsAString& aAnnouncement,
+ const uint16_t& aPriority) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ if (!target) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+# if defined(ANDROID)
+ ProxyAnnouncementEvent(target, aAnnouncement, aPriority);
+# endif
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ RefPtr<xpcAccAnnouncementEvent> event = new xpcAccAnnouncementEvent(
+ nsIAccessibleEvent::EVENT_ANNOUNCEMENT, xpcAcc, doc, nullptr, false,
+ aAnnouncement, aPriority);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+#endif // !defined(XP_WIN)
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvTextSelectionChangeEvent(
+ const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* target = GetAccessible(aID);
+ if (!target) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ mTextSelections.ClearAndRetainStorage();
+ mTextSelections.AppendElements(aSelection);
+ }
+
+#ifdef MOZ_WIDGET_COCOA
+ ProxyTextSelectionChangeEvent(target, aSelection);
+#else
+ ProxyEvent(target, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED);
+#endif
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
+ xpcAccessibleDocument* doc = nsAccessibilityService::GetXPCDocument(this);
+ nsINode* node = nullptr;
+ bool fromUser = true; // XXX fix me
+ RefPtr<xpcAccEvent> event =
+ new xpcAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, xpcAcc,
+ doc, node, fromUser);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
+ const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ mRole = aRole;
+ mRoleMapEntryIndex = aRoleMapEntryIndex;
+
+#ifdef MOZ_WIDGET_COCOA
+ ProxyRoleChangedEvent(this, aRole, aRoleMapEntryIndex);
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
+ PDocAccessibleParent* aChildDoc, const uint64_t& aID) {
+ ACQUIRE_ANDROID_LOCK
+ // One document should never directly be the child of another.
+ // We should always have at least an outer doc accessible in between.
+ MOZ_ASSERT(aID);
+ if (!aID) return IPC_FAIL(this, "ID is 0!");
+
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(CheckDocTree());
+
+ auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
+ childDoc->Unbind();
+ ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
+ MOZ_ASSERT(result);
+ MOZ_ASSERT(CheckDocTree());
+#ifdef DEBUG
+ if (!result) {
+ return result;
+ }
+#else
+ result = IPC_OK();
+#endif
+
+ return result;
+}
+
+ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
+ uint64_t aParentID,
+ bool aCreating) {
+ // We do not use GetAccessible here because we want to be sure to not get the
+ // document it self.
+ ProxyEntry* e = mAccessibles.GetEntry(aParentID);
+ if (!e) {
+#ifndef FUZZING_SNAPSHOT
+ // This diagnostic assert and the one down below expect a well-behaved
+ // child process. In IPC fuzzing, we directly fuzz parameters of each
+ // method over IPDL and the asserts are not valid under these conditions.
+ MOZ_DIAGNOSTIC_ASSERT(false, "Binding to nonexistent proxy!");
+#endif
+ return IPC_FAIL(this, "binding to nonexistant proxy!");
+ }
+
+ RemoteAccessible* outerDoc = e->mProxy;
+ MOZ_ASSERT(outerDoc);
+
+ // OuterDocAccessibles are expected to only have a document as a child.
+ // However for compatibility we tolerate replacing one document with another
+ // here.
+ if (!outerDoc->IsOuterDoc() || outerDoc->ChildCount() > 1 ||
+ (outerDoc->ChildCount() == 1 && !outerDoc->RemoteChildAt(0)->IsDoc())) {
+#ifndef FUZZING_SNAPSHOT
+ MOZ_DIAGNOSTIC_ASSERT(false,
+ "Binding to parent that isn't a valid OuterDoc!");
+#endif
+ return IPC_FAIL(this, "Binding to parent that isn't a valid OuterDoc!");
+ }
+
+ if (outerDoc->ChildCount() == 1) {
+ MOZ_ASSERT(outerDoc->RemoteChildAt(0)->AsDoc());
+ outerDoc->RemoteChildAt(0)->AsDoc()->Unbind();
+ }
+
+ aChildDoc->SetParent(outerDoc);
+ outerDoc->SetChildDoc(aChildDoc);
+ mChildDocs.AppendElement(aChildDoc->mActorID);
+ aChildDoc->mParentDoc = mActorID;
+
+ if (aCreating) {
+ ProxyCreated(aChildDoc);
+ }
+
+ if (aChildDoc->IsTopLevelInContentProcess()) {
+ // aChildDoc is an embedded document in a different content process to
+ // this document.
+ auto embeddedBrowser =
+ static_cast<dom::BrowserParent*>(aChildDoc->Manager());
+ dom::BrowserBridgeParent* bridge =
+ embeddedBrowser->GetBrowserBridgeParent();
+ if (bridge) {
+#if defined(XP_WIN)
+ if (!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Send a COM proxy for the embedded document to the embedder process
+ // hosting the iframe. This will be returned as the child of the
+ // embedder OuterDocAccessible.
+ RefPtr<IDispatch> docAcc;
+ aChildDoc->GetCOMInterface((void**)getter_AddRefs(docAcc));
+ MOZ_ASSERT(docAcc);
+ if (docAcc) {
+ RefPtr<IDispatch> docWrapped(
+ mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(docAcc)));
+ IDispatchHolder::COMPtrType docPtr(
+ mscom::ToProxyUniquePtr(std::move(docWrapped)));
+ IDispatchHolder docHolder(std::move(docPtr));
+ if (bridge->SendSetEmbeddedDocAccessibleCOMProxy(docHolder)) {
+# if defined(MOZ_SANDBOX)
+ aChildDoc->mDocProxyStream = docHolder.GetPreservedStream();
+# endif // defined(MOZ_SANDBOX)
+ }
+ }
+ // Send a COM proxy for the embedder OuterDocAccessible to the embedded
+ // document process. This will be returned as the parent of the
+ // embedded document.
+ aChildDoc->SendParentCOMProxy(outerDoc);
+ if (nsWinUtils::IsWindowEmulationStarted()) {
+ // The embedded document should use the same emulated window handle as
+ // its embedder. It will return the embedder document (not a window
+ // accessible) as the parent accessible, so we pass a null accessible
+ // when sending the window to the embedded document.
+ Unused << aChildDoc->SendEmulatedWindow(
+ reinterpret_cast<uintptr_t>(mEmulatedWindowHandle), nullptr);
+ }
+ // Send a COM proxy for the top level document to the embedded document
+ // process. This will be returned when the client calls QueryService
+ // with SID_IAccessibleContentDocument on an accessible in the embedded
+ // document.
+ DocAccessibleParent* topDoc = this;
+ while (DocAccessibleParent* parentDoc = topDoc->ParentDoc()) {
+ topDoc = parentDoc;
+ }
+ MOZ_ASSERT(topDoc && topDoc->IsTopLevel());
+ RefPtr<IAccessible> topDocAcc;
+ topDoc->GetCOMInterface((void**)getter_AddRefs(topDocAcc));
+ MOZ_ASSERT(topDocAcc);
+ if (topDocAcc) {
+ RefPtr<IAccessible> topDocWrapped(
+ mscom::PassthruProxy::Wrap<IAccessible>(WrapNotNull(topDocAcc)));
+ IAccessibleHolder::COMPtrType topDocPtr(
+ mscom::ToProxyUniquePtr(std::move(topDocWrapped)));
+ IAccessibleHolder topDocHolder(std::move(topDocPtr));
+ if (aChildDoc->SendTopLevelDocCOMProxy(topDocHolder)) {
+# if defined(MOZ_SANDBOX)
+ aChildDoc->mTopLevelDocProxyStream =
+ topDocHolder.GetPreservedStream();
+# endif // defined(MOZ_SANDBOX)
+ }
+ }
+ }
+ if (nsWinUtils::IsWindowEmulationStarted()) {
+ aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
+ }
+#endif // defined(XP_WIN)
+ // We need to fire a reorder event on the outer doc accessible.
+ // For same-process documents, this is fired by the content process, but
+ // this isn't possible when the document is in a different process to its
+ // embedder.
+ // FireEvent fires both OS and XPCOM events.
+ FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER);
+ }
+ }
+
+ return IPC_OK();
+}
+
+ipc::IPCResult DocAccessibleParent::AddChildDoc(
+ dom::BrowserBridgeParent* aBridge) {
+ MOZ_ASSERT(aBridge->GetEmbedderAccessibleDoc() == this);
+ uint64_t parentId = aBridge->GetEmbedderAccessibleId();
+ MOZ_ASSERT(parentId);
+ if (!mAccessibles.GetEntry(parentId)) {
+ // Sometimes, this gets called before the embedder sends us the
+ // OuterDocAccessible. We must add the child when the OuterDocAccessible
+ // gets created later.
+ mPendingOOPChildDocs.Insert(aBridge);
+ return IPC_OK();
+ }
+ return AddChildDoc(aBridge->GetDocAccessibleParent(), parentId,
+ /* aCreating */ false);
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvShutdown() {
+ ACQUIRE_ANDROID_LOCK
+ Destroy();
+
+ auto mgr = static_cast<dom::BrowserParent*>(Manager());
+ if (!mgr->IsDestroyed()) {
+ if (!PDocAccessibleParent::Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+ }
+
+ return IPC_OK();
+}
+
+void DocAccessibleParent::Destroy() {
+ // If we are already shutdown that is because our containing tab parent is
+ // shutting down in which case we don't need to do anything.
+ if (mShutdown) {
+ return;
+ }
+
+ mShutdown = true;
+ mBrowsingContext = nullptr;
+
+#ifdef ANDROID
+ if (FocusMgr() && FocusMgr()->IsFocusedRemoteDoc(this)) {
+ FocusMgr()->SetFocusedRemoteDoc(nullptr);
+ }
+#endif
+
+ MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
+ uint32_t childDocCount = mChildDocs.Length();
+ for (uint32_t i = 0; i < childDocCount; i++) {
+ for (uint32_t j = i + 1; j < childDocCount; j++) {
+ MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
+ }
+ }
+
+ // XXX This indirection through the hash map of live documents shouldn't be
+ // needed, but be paranoid for now.
+ int32_t actorID = mActorID;
+ for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
+ DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+ MOZ_ASSERT(thisDoc);
+ if (!thisDoc) {
+ return;
+ }
+
+ thisDoc->ChildDocAt(i)->Destroy();
+ }
+
+ for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
+ RemoteAccessible* acc = iter.Get()->mProxy;
+ MOZ_ASSERT(acc != this);
+ if (acc->IsTable()) {
+ CachedTableAccessible::Invalidate(acc);
+ }
+ ProxyDestroyed(acc);
+ iter.Remove();
+ }
+
+ DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+ MOZ_ASSERT(thisDoc);
+ if (!thisDoc) {
+ return;
+ }
+
+ mChildren.Clear();
+ // The code above should have already completely cleared these, but to be
+ // extra safe make sure they are cleared here.
+ thisDoc->mAccessibles.Clear();
+ thisDoc->mChildDocs.Clear();
+
+ DocManager::NotifyOfRemoteDocShutdown(thisDoc);
+ thisDoc = LiveDocs().Get(actorID);
+ MOZ_ASSERT(thisDoc);
+ if (!thisDoc) {
+ return;
+ }
+
+ ProxyDestroyed(thisDoc);
+ thisDoc = LiveDocs().Get(actorID);
+ MOZ_ASSERT(thisDoc);
+ if (!thisDoc) {
+ return;
+ }
+
+ if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc()) {
+ parentDoc->RemoveChildDoc(thisDoc);
+ } else if (IsTopLevel()) {
+ GetAccService()->RemoteDocShutdown(this);
+ }
+}
+
+void DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(CheckDocTree());
+ if (!mShutdown) {
+ ACQUIRE_ANDROID_LOCK
+ Destroy();
+ }
+}
+
+DocAccessibleParent* DocAccessibleParent::ParentDoc() const {
+ if (mParentDoc == kNoParentDoc) {
+ return nullptr;
+ }
+
+ return LiveDocs().Get(mParentDoc);
+}
+
+bool DocAccessibleParent::CheckDocTree() const {
+ size_t childDocs = mChildDocs.Length();
+ for (size_t i = 0; i < childDocs; i++) {
+ const DocAccessibleParent* childDoc = ChildDocAt(i);
+ if (!childDoc || childDoc->ParentDoc() != this) return false;
+
+ if (!childDoc->CheckDocTree()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+xpcAccessibleGeneric* DocAccessibleParent::GetXPCAccessible(
+ RemoteAccessible* aProxy) {
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ MOZ_ASSERT(doc);
+
+ return doc->GetAccessible(aProxy);
+}
+
+#if defined(XP_WIN)
+void DocAccessibleParent::MaybeInitWindowEmulation() {
+ if (!nsWinUtils::IsWindowEmulationStarted()) {
+ return;
+ }
+
+ // XXX get the bounds from the browserParent instead of poking at accessibles
+ // which might not exist yet.
+ LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
+ if (!outerDoc) {
+ return;
+ }
+
+ RootAccessible* rootDocument = outerDoc->RootAccessible();
+ MOZ_ASSERT(rootDocument);
+
+ bool isActive = true;
+ LayoutDeviceIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
+ if (Compatibility::IsDolphin()) {
+ rect = Bounds();
+ LayoutDeviceIntRect rootRect = rootDocument->Bounds();
+ rect.MoveToX(rootRect.X() - rect.X());
+ rect.MoveToY(rect.Y() - rootRect.Y());
+
+ auto browserParent = static_cast<dom::BrowserParent*>(Manager());
+ isActive = browserParent->GetDocShellIsActive();
+ }
+
+ // onCreate is guaranteed to be called synchronously by
+ // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
+ // However, static analysis complains without it.
+ RefPtr<DocAccessibleParent> thisRef = this;
+ nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void {
+ IDispatchHolder hWndAccHolder;
+
+ ::SetPropW(aHwnd, kPropNameDocAccParent,
+ reinterpret_cast<HANDLE>(thisRef.get()));
+
+ thisRef->SetEmulatedWindowHandle(aHwnd);
+
+ RefPtr<IAccessible> hwndAcc;
+ if (SUCCEEDED(::AccessibleObjectFromWindow(
+ aHwnd, OBJID_WINDOW, IID_IAccessible, getter_AddRefs(hwndAcc)))) {
+ RefPtr<IDispatch> wrapped(
+ mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(hwndAcc)));
+ hWndAccHolder.Set(IDispatchHolder::COMPtrType(
+ mscom::ToProxyUniquePtr(std::move(wrapped))));
+ }
+
+ Unused << thisRef->SendEmulatedWindow(
+ reinterpret_cast<uintptr_t>(thisRef->mEmulatedWindowHandle),
+ hWndAccHolder);
+ });
+
+ HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
+ DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(
+ kClassNameTabContent, parentWnd, rect.X(), rect.Y(), rect.Width(),
+ rect.Height(), isActive, &onCreate);
+ MOZ_ASSERT(hWnd);
+}
+
+void DocAccessibleParent::SendParentCOMProxy(Accessible* aOuterDoc) {
+ // Make sure that we're not racing with a tab shutdown
+ auto tab = static_cast<dom::BrowserParent*>(Manager());
+ MOZ_ASSERT(tab);
+ if (tab->IsDestroyed()) {
+ return;
+ }
+
+ RefPtr<IDispatch> nativeAcc =
+ already_AddRefed<IDispatch>(MsaaAccessible::NativeAccessible(aOuterDoc));
+ if (NS_WARN_IF(!nativeAcc)) {
+ // Couldn't get a COM proxy for the outer doc. That probably means it died,
+ // but the parent process hasn't received a message to remove it from the
+ // RemoteAccessible tree yet.
+ return;
+ }
+
+ RefPtr<IDispatch> wrapped(
+ mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc)));
+
+ IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(std::move(wrapped)));
+ IDispatchHolder holder(std::move(ptr));
+ if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
+ return;
+ }
+
+# if defined(MOZ_SANDBOX)
+ mParentProxyStream = holder.GetPreservedStream();
+# endif // defined(MOZ_SANDBOX)
+}
+
+void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) {
+ if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
+ ::DestroyWindow(mEmulatedWindowHandle);
+ }
+ mEmulatedWindowHandle = aWindowHandle;
+}
+
+mozilla::ipc::IPCResult DocAccessibleParent::RecvFocusEvent(
+ const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) {
+ ACQUIRE_ANDROID_LOCK
+ if (mShutdown) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* proxy = GetAccessible(aID);
+ if (!proxy) {
+ NS_ERROR("no proxy for event!");
+ return IPC_OK();
+ }
+
+ mFocus = aID;
+ ProxyFocusEvent(proxy, aCaretRect);
+
+ if (!nsCoreUtils::AccEventObserversExist()) {
+ return IPC_OK();
+ }
+
+ xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
+ xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+ nsINode* node = nullptr;
+ bool fromUser = true; // XXX fix me
+ RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
+ xpcAcc, doc, node, fromUser);
+ nsCoreUtils::DispatchAccEvent(std::move(event));
+
+ return IPC_OK();
+}
+
+#endif // defined(XP_WIN)
+
+#if !defined(XP_WIN)
+mozilla::ipc::IPCResult DocAccessibleParent::RecvBatch(
+ const uint64_t& aBatchType, nsTArray<BatchData>&& aData) {
+ // Only do something in Android. We can't ifdef the entire protocol out in
+ // the ipdl because it doesn't allow preprocessing.
+# if defined(ANDROID)
+ if (mShutdown) {
+ return IPC_OK();
+ }
+ nsTArray<RemoteAccessible*> proxies(aData.Length());
+ for (size_t i = 0; i < aData.Length(); i++) {
+ DocAccessibleParent* doc = static_cast<DocAccessibleParent*>(
+ aData.ElementAt(i).Document().get_PDocAccessibleParent());
+ MOZ_ASSERT(doc);
+
+ if (doc->IsShutdown()) {
+ continue;
+ }
+
+ RemoteAccessible* proxy = doc->GetAccessible(aData.ElementAt(i).ID());
+ if (!proxy) {
+ MOZ_ASSERT_UNREACHABLE("No proxy found!");
+ continue;
+ }
+
+ proxies.AppendElement(proxy);
+ }
+ ProxyBatch(this, aBatchType, proxies, aData);
+# endif // defined(XP_WIN)
+ return IPC_OK();
+}
+
+bool DocAccessibleParent::DeallocPDocAccessiblePlatformExtParent(
+ PDocAccessiblePlatformExtParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+PDocAccessiblePlatformExtParent*
+DocAccessibleParent::AllocPDocAccessiblePlatformExtParent() {
+ return new DocAccessiblePlatformExtParent();
+}
+
+DocAccessiblePlatformExtParent* DocAccessibleParent::GetPlatformExtension() {
+ return static_cast<DocAccessiblePlatformExtParent*>(
+ SingleManagedOrNull(ManagedPDocAccessiblePlatformExtParent()));
+}
+
+#endif // !defined(XP_WIN)
+
+void DocAccessibleParent::SelectionRanges(nsTArray<TextRange>* aRanges) const {
+ for (const auto& data : mTextSelections) {
+ // Selection ranges should usually be in sync with the tree. However, tree
+ // and selection updates happen using separate IPDL calls, so it's possible
+ // for a client selection query to arrive between them. Thus, we validate
+ // the Accessibles and offsets here.
+ auto* startAcc =
+ const_cast<RemoteAccessible*>(GetAccessible(data.StartID()));
+ auto* endAcc = const_cast<RemoteAccessible*>(GetAccessible(data.EndID()));
+ if (!startAcc || !endAcc) {
+ continue;
+ }
+ uint32_t startCount = startAcc->CharacterCount();
+ if (startCount == 0 ||
+ data.StartOffset() > static_cast<int32_t>(startCount)) {
+ continue;
+ }
+ uint32_t endCount = endAcc->CharacterCount();
+ if (endCount == 0 || data.EndOffset() > static_cast<int32_t>(endCount)) {
+ continue;
+ }
+ aRanges->AppendElement(TextRange(const_cast<DocAccessibleParent*>(this),
+ startAcc, data.StartOffset(), endAcc,
+ data.EndOffset()));
+ }
+}
+
+Accessible* DocAccessibleParent::FocusedChild() {
+ LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
+ if (!outerDoc) {
+ return nullptr;
+ }
+
+ RootAccessible* rootDocument = outerDoc->RootAccessible();
+ return rootDocument->FocusedChild();
+}
+
+void DocAccessibleParent::URL(nsACString& aURL) const {
+ if (!mBrowsingContext) {
+ return;
+ }
+ nsCOMPtr<nsIURI> uri = mBrowsingContext->GetCurrentURI();
+ if (!uri) {
+ return;
+ }
+ // Let's avoid treating too long URI in the main process for avoiding
+ // memory fragmentation as far as possible.
+ if (uri->SchemeIs("data") || uri->SchemeIs("blob")) {
+ return;
+ }
+ nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service();
+ if (NS_WARN_IF(!io)) {
+ return;
+ }
+ nsCOMPtr<nsIURI> exposableURI;
+ if (NS_FAILED(io->CreateExposableURI(uri, getter_AddRefs(exposableURI))) ||
+ MOZ_UNLIKELY(!exposableURI)) {
+ return;
+ }
+ exposableURI->GetSpec(aURL);
+}
+
+void DocAccessibleParent::URL(nsAString& aURL) const {
+ nsAutoCString url;
+ URL(url);
+ CopyUTF8toUTF16(url, aURL);
+}
+
+Relation DocAccessibleParent::RelationByType(RelationType aType) const {
+ // If the accessible is top-level, provide the NODE_CHILD_OF relation so that
+ // MSAA clients can easily get to true parent instead of getting to oleacc's
+ // ROLE_WINDOW accessible when window emulation is enabled which will prevent
+ // us from going up further (because it is system generated and has no idea
+ // about the hierarchy above it).
+ if (aType == RelationType::NODE_CHILD_OF && IsTopLevel()) {
+ return Relation(Parent());
+ }
+
+ return RemoteAccessibleBase<RemoteAccessible>::RelationByType(aType);
+}
+
+DocAccessibleParent* DocAccessibleParent::GetFrom(
+ dom::BrowsingContext* aBrowsingContext) {
+ if (!aBrowsingContext) {
+ return nullptr;
+ }
+
+ dom::BrowserParent* bp = aBrowsingContext->Canonical()->GetBrowserParent();
+ if (!bp) {
+ return nullptr;
+ }
+
+ const ManagedContainer<PDocAccessibleParent>& docs =
+ bp->ManagedPDocAccessibleParent();
+ for (auto* key : docs) {
+ // Iterate over our docs until we find one with a browsing
+ // context that matches the one we passed in. Return that
+ // document.
+ auto* doc = static_cast<a11y::DocAccessibleParent*>(key);
+ if (doc->GetBrowsingContext() == aBrowsingContext) {
+ return doc;
+ }
+ }
+
+ return nullptr;
+}
+
+size_t DocAccessibleParent::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
+ size_t size = 0;
+
+ size += RemoteAccessibleBase::SizeOfExcludingThis(aMallocSizeOf);
+
+ size += mReverseRelations.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto i = mReverseRelations.Iter(); !i.Done(); i.Next()) {
+ size += i.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto j = i.Data().Iter(); !j.Done(); j.Next()) {
+ size += j.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ }
+
+ size += mOnScreenAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ size += mChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ size += mAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto i = mAccessibles.Iter(); !i.Done(); i.Next()) {
+ size += i.Get()->mProxy->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ size += mPendingOOPChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ // The mTextSelections array contains structs of integers. We can count them
+ // by counting the size of the array - there's no deep structure here.
+ size += mTextSelections.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return size;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOfAccessibilityCache);
+
+NS_IMETHODIMP
+DocAccessibleParent::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnon) {
+ nsAutoCString path;
+
+ if (aAnon) {
+ path = nsPrintfCString("explicit/a11y/cache(%" PRIu64 ")", mActorID);
+ } else {
+ nsCString url;
+ URL(url);
+ url.ReplaceChar(
+ '/', '\\'); // Tell the memory reporter this is not a path seperator.
+ path = nsPrintfCString("explicit/a11y/cache(%s)", url.get());
+ }
+
+ aHandleReport->Callback(
+ /* process */ ""_ns, path, KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(MallocSizeOfAccessibilityCache),
+ nsLiteralCString("Size of the accessability cache for this document."),
+ aData);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(DocAccessibleParent, nsIMemoryReporter);
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h
new file mode 100644
index 0000000000..c1f07c7979
--- /dev/null
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -0,0 +1,442 @@
+/* -*- 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_DocAccessibleParent_h
+#define mozilla_a11y_DocAccessibleParent_h
+
+#include "nsAccessibilityService.h"
+#include "mozilla/a11y/PDocAccessibleParent.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+class CanonicalBrowsingContext;
+}
+
+namespace a11y {
+
+class TextRange;
+class xpcAccessibleGeneric;
+
+#if !defined(XP_WIN)
+class DocAccessiblePlatformExtParent;
+#endif
+
+/*
+ * These objects live in the main process and comunicate with and represent
+ * an accessible document in a content process.
+ */
+class DocAccessibleParent : public RemoteAccessible,
+ public PDocAccessibleParent,
+ public nsIMemoryReporter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+ private:
+ DocAccessibleParent();
+
+ public:
+ static already_AddRefed<DocAccessibleParent> New();
+
+ /**
+ * Set this as a top level document; i.e. it is not embedded by another remote
+ * document. This also means it is a top level document in its content
+ * process.
+ * Tab documents are top level documents.
+ */
+ void SetTopLevel() {
+ mTopLevel = true;
+ mTopLevelInContentProcess = true;
+ }
+ bool IsTopLevel() const { return mTopLevel; }
+
+ /**
+ * Set this as a top level document in its content process.
+ * Note that this could be an out-of-process iframe embedded by a remote
+ * embedder document. In that case, IsToplevel() will return false, but
+ * IsTopLevelInContentProcess() will return true.
+ */
+ void SetTopLevelInContentProcess() { mTopLevelInContentProcess = true; }
+ bool IsTopLevelInContentProcess() const { return mTopLevelInContentProcess; }
+
+ /**
+ * Determine whether this is an out-of-process iframe document, embedded by a
+ * remote embedder document.
+ */
+ bool IsOOPIframeDoc() const {
+ return !mTopLevel && mTopLevelInContentProcess;
+ }
+
+ bool IsShutdown() const { return mShutdown; }
+
+ /**
+ * Mark this actor as shutdown without doing any cleanup. This should only
+ * be called on actors that have just been initialized, so probably only from
+ * RecvPDocAccessibleConstructor.
+ */
+ void MarkAsShutdown() {
+ MOZ_ASSERT(mChildDocs.IsEmpty());
+ MOZ_ASSERT(mAccessibles.Count() == 0);
+ MOZ_ASSERT(!mBrowsingContext);
+ mShutdown = true;
+ }
+
+ void SetBrowsingContext(dom::CanonicalBrowsingContext* aBrowsingContext);
+
+ dom::CanonicalBrowsingContext* GetBrowsingContext() const {
+ return mBrowsingContext;
+ }
+
+ /*
+ * Called when a message from a document in a child process notifies the main
+ * process it is firing an event.
+ */
+ virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID,
+ const uint32_t& aType) override;
+
+ virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData,
+ const bool& aFromUser) override;
+ virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID,
+ const bool& aFromUser) override;
+ mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID,
+ const uint64_t& aState,
+ const bool& aEnabled) final;
+
+ mozilla::ipc::IPCResult RecvCaretMoveEvent(
+ const uint64_t& aID,
+#if defined(XP_WIN)
+ const LayoutDeviceIntRect& aCaretRect,
+#endif
+ const int32_t& aOffset, const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine, const int32_t& aGranularity) final;
+
+ virtual mozilla::ipc::IPCResult RecvTextChangeEvent(
+ const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
+ const uint32_t& aLen, const bool& aIsInsert,
+ const bool& aFromUser) override;
+
+#if defined(XP_WIN)
+ virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent(
+ const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
+ const uint32_t& aLen, const bool& aIsInsert,
+ const bool& aFromUser) override;
+
+ virtual mozilla::ipc::IPCResult RecvFocusEvent(
+ const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) override;
+#endif // defined(XP_WIN)
+
+ virtual mozilla::ipc::IPCResult RecvSelectionEvent(
+ const uint64_t& aID, const uint64_t& aWidgetID,
+ const uint32_t& aType) override;
+
+ virtual mozilla::ipc::IPCResult RecvVirtualCursorChangeEvent(
+ const uint64_t& aID, const uint64_t& aOldPositionID,
+ const int32_t& aOldStartOffset, const int32_t& aOldEndOffset,
+ const uint64_t& aNewPositionID, const int32_t& aNewStartOffset,
+ const int32_t& aNewEndOffset, const int16_t& aReason,
+ const int16_t& aBoundaryType, const bool& aFromUser) override;
+
+ virtual mozilla::ipc::IPCResult RecvScrollingEvent(
+ const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
+ const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
+ const uint32_t& aMaxScrollY) override;
+
+ virtual mozilla::ipc::IPCResult RecvCache(
+ const mozilla::a11y::CacheUpdateType& aUpdateType,
+ nsTArray<CacheData>&& aData, const bool& aDispatchShowEvent) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectedAccessiblesChanged(
+ nsTArray<uint64_t>&& aSelectedIDs,
+ nsTArray<uint64_t>&& aUnselectedIDs) override;
+
+ virtual mozilla::ipc::IPCResult RecvAccessiblesWillMove(
+ nsTArray<uint64_t>&& aIDs) override;
+
+#if !defined(XP_WIN)
+ virtual mozilla::ipc::IPCResult RecvAnnouncementEvent(
+ const uint64_t& aID, const nsAString& aAnnouncement,
+ const uint16_t& aPriority) override;
+#endif
+
+ virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent(
+ const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override;
+
+ mozilla::ipc::IPCResult RecvRoleChangedEvent(
+ const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) final;
+
+ virtual mozilla::ipc::IPCResult RecvBindChildDoc(
+ PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
+
+ void Unbind() {
+ if (DocAccessibleParent* parent = ParentDoc()) {
+ parent->RemoveChildDoc(this);
+ }
+
+ SetParent(nullptr);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvShutdown() override;
+ void Destroy();
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ /*
+ * Return the main processes representation of the parent document (if any)
+ * of the document this object represents.
+ */
+ DocAccessibleParent* ParentDoc() const;
+ static const uint64_t kNoParentDoc = UINT64_MAX;
+
+ /**
+ * Called when a document in a content process notifies the main process of a
+ * new child document.
+ * Although this is called internally for OOP child documents, these should be
+ * added via the BrowserBridgeParent version of this method, as the parent id
+ * might not exist yet in that case.
+ */
+ ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID,
+ bool aCreating = true);
+
+ /**
+ * Called when a document in a content process notifies the main process of a
+ * new OOP child document.
+ */
+ ipc::IPCResult AddChildDoc(dom::BrowserBridgeParent* aBridge);
+
+ void RemovePendingOOPChildDoc(dom::BrowserBridgeParent* aBridge) {
+ mPendingOOPChildDocs.Remove(aBridge);
+ }
+
+ /*
+ * Called when the document in the content process this object represents
+ * notifies the main process a child document has been removed.
+ */
+ void RemoveChildDoc(DocAccessibleParent* aChildDoc) {
+ RemoteAccessible* parent = aChildDoc->RemoteParent();
+ MOZ_ASSERT(parent);
+ if (parent) {
+ aChildDoc->RemoteParent()->ClearChildDoc(aChildDoc);
+ }
+ DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
+ aChildDoc->mParentDoc = kNoParentDoc;
+ MOZ_ASSERT(result);
+ }
+
+ void RemoveAccessible(RemoteAccessible* aAccessible) {
+ MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
+ mAccessibles.RemoveEntry(aAccessible->ID());
+ }
+
+ /**
+ * Return the accessible for given id.
+ */
+ RemoteAccessible* GetAccessible(uintptr_t aID) {
+ if (!aID) return this;
+
+ ProxyEntry* e = mAccessibles.GetEntry(aID);
+ return e ? e->mProxy : nullptr;
+ }
+
+ const RemoteAccessible* 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 const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx);
+ }
+ DocAccessibleParent* ChildDocAt(size_t aIdx) {
+ return LiveDocs().Get(mChildDocs[aIdx]);
+ }
+
+#if defined(XP_WIN)
+ void MaybeInitWindowEmulation();
+
+ /**
+ * Note that an OuterDocAccessible can be created before the
+ * DocAccessibleParent or vice versa. Therefore, this must be conditionally
+ * called when either of these is created.
+ * @param aOuterDoc The OuterDocAccessible to be returned as the parent of
+ * this document.
+ */
+ void SendParentCOMProxy(Accessible* aOuterDoc);
+
+ /**
+ * Set emulated native window handle for a document.
+ * @param aWindowHandle emulated native window handle
+ */
+ void SetEmulatedWindowHandle(HWND aWindowHandle);
+ HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
+#endif
+
+#if !defined(XP_WIN)
+ virtual mozilla::ipc::IPCResult RecvBatch(
+ const uint64_t& aBatchType, nsTArray<BatchData>&& aData) override;
+
+ virtual bool DeallocPDocAccessiblePlatformExtParent(
+ PDocAccessiblePlatformExtParent* aActor) override;
+
+ virtual PDocAccessiblePlatformExtParent*
+ AllocPDocAccessiblePlatformExtParent() override;
+
+ DocAccessiblePlatformExtParent* GetPlatformExtension();
+#endif
+
+ // Accessible
+ virtual Accessible* Parent() const override {
+ if (IsTopLevel()) {
+ return OuterDocOfRemoteBrowser();
+ }
+ return RemoteParent();
+ }
+
+ virtual int32_t IndexInParent() const override {
+ if (IsTopLevel() && OuterDocOfRemoteBrowser()) {
+ // An OuterDoc can only have 1 child.
+ return 0;
+ }
+ return RemoteAccessible::IndexInParent();
+ }
+
+ /**
+ * Get the focused Accessible in this document, if any.
+ */
+ RemoteAccessible* GetFocusedAcc() const {
+ return const_cast<DocAccessibleParent*>(this)->GetAccessible(mFocus);
+ }
+
+ /**
+ * Get the HyperText Accessible containing the caret and the offset of the
+ * caret within. If there is no caret in this document, returns
+ * {nullptr, -1}.
+ */
+ std::pair<RemoteAccessible*, int32_t> GetCaret() const {
+ if (mCaretOffset == -1) {
+ return {nullptr, -1};
+ }
+ RemoteAccessible* acc =
+ const_cast<DocAccessibleParent*>(this)->GetAccessible(mCaretId);
+ if (!acc) {
+ return {nullptr, -1};
+ }
+ return {acc, mCaretOffset};
+ }
+
+ bool IsCaretAtEndOfLine() const { return mIsCaretAtEndOfLine; }
+
+ virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override;
+
+ virtual Accessible* FocusedChild() override;
+
+ void URL(nsAString& aURL) const;
+ void URL(nsACString& aURL) const;
+
+ virtual Relation RelationByType(RelationType aType) const override;
+
+ // Tracks cached reverse relations (ie. those not set explicitly by an
+ // attribute like aria-labelledby) for accessibles in this doc. This map is of
+ // the form: {accID, {relationType, [targetAccID, targetAccID, ...]}}
+ nsTHashMap<uint64_t, nsTHashMap<RelationType, nsTArray<uint64_t>>>
+ mReverseRelations;
+
+ // Computed from the viewport cache, the accs referenced by these ids
+ // are currently on screen (making any acc not in this list offscreen).
+ nsTHashSet<uint64_t> mOnScreenAccessibles;
+
+ static DocAccessibleParent* GetFrom(dom::BrowsingContext* aBrowsingContext);
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) override;
+
+ private:
+ ~DocAccessibleParent();
+
+ class ProxyEntry : public PLDHashEntryHdr {
+ public:
+ explicit ProxyEntry(const void*) : mProxy(nullptr) {}
+ ProxyEntry(ProxyEntry&& aOther) : mProxy(aOther.mProxy) {
+ aOther.mProxy = nullptr;
+ }
+ ~ProxyEntry() { delete mProxy; }
+
+ typedef uint64_t KeyType;
+ typedef const void* KeyTypePointer;
+
+ bool KeyEquals(const void* aKey) const {
+ return mProxy->ID() == (uint64_t)aKey;
+ }
+
+ static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; }
+
+ static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ RemoteAccessible* mProxy;
+ };
+
+ uint32_t AddSubtree(RemoteAccessible* aParent,
+ const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
+ uint32_t aIdxInParent);
+ [[nodiscard]] bool CheckDocTree() const;
+ xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy);
+
+ void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType);
+
+ /**
+ * If this Accessible is being moved, prepare it for reuse. Otherwise, it is
+ * being removed, so shut it down.
+ */
+ void ShutdownOrPrepareForMove(RemoteAccessible* aAcc);
+
+ nsTArray<uint64_t> mChildDocs;
+ uint64_t mParentDoc;
+
+#if defined(XP_WIN)
+ // The handle associated with the emulated window that contains this document
+ HWND mEmulatedWindowHandle;
+
+# if defined(MOZ_SANDBOX)
+ mscom::PreservedStreamPtr mParentProxyStream;
+ mscom::PreservedStreamPtr mDocProxyStream;
+ mscom::PreservedStreamPtr mTopLevelDocProxyStream;
+# endif // defined(MOZ_SANDBOX)
+#endif // defined(XP_WIN)
+
+ /*
+ * Conceptually this is a map from IDs to proxies, but we store the ID in the
+ * proxy object so we can't use a real map.
+ */
+ nsTHashtable<ProxyEntry> mAccessibles;
+ nsTHashSet<uint64_t> mMovingIDs;
+ uint64_t mActorID;
+ bool mTopLevel;
+ bool mTopLevelInContentProcess;
+ bool mShutdown;
+ RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext;
+
+ nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs;
+
+ uint64_t mFocus;
+ uint64_t mCaretId;
+ int32_t mCaretOffset;
+ bool mIsCaretAtEndOfLine;
+ nsTArray<TextRangeData> mTextSelections;
+
+ static uint64_t sMaxDocID;
+ static nsTHashMap<nsUint64HashKey, DocAccessibleParent*>& LiveDocs() {
+ static nsTHashMap<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
+ return sLiveDocs;
+ }
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/DocAccessibleTypes.ipdlh b/accessible/ipc/DocAccessibleTypes.ipdlh
new file mode 100644
index 0000000000..df2ca043ea
--- /dev/null
+++ b/accessible/ipc/DocAccessibleTypes.ipdlh
@@ -0,0 +1,19 @@
+/* -*- 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/. */
+
+[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
+
+namespace mozilla {
+namespace a11y {
+
+struct CacheData
+{
+ uint64_t ID;
+ AccAttributes Fields;
+};
+
+}
+}
diff --git a/accessible/ipc/IPCTypes.h b/accessible/ipc/IPCTypes.h
new file mode 100644
index 0000000000..c044523551
--- /dev/null
+++ b/accessible/ipc/IPCTypes.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_IPCTypes_h
+#define mozilla_a11y_IPCTypes_h
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/AccAttributes.h"
+# include "mozilla/a11y/AccTypes.h"
+# include "mozilla/a11y/CacheConstants.h"
+# include "mozilla/a11y/Role.h"
+# include "mozilla/a11y/AccGroupInfo.h"
+# include "mozilla/GfxMessageUtils.h"
+# include "ipc/EnumSerializer.h"
+# include "ipc/IPCMessageUtilsSpecializations.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::a11y::role>
+ : public ContiguousEnumSerializerInclusive<mozilla::a11y::role,
+ mozilla::a11y::role::NOTHING,
+ mozilla::a11y::role::LAST_ROLE> {
+};
+
+template <>
+struct ParamTraits<mozilla::a11y::AccType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::a11y::AccType, mozilla::a11y::AccType::eNoType,
+ mozilla::a11y::AccType::eLastAccType> {};
+
+template <>
+struct ParamTraits<mozilla::a11y::AccGenericType>
+ : public BitFlagsEnumSerializer<
+ mozilla::a11y::AccGenericType,
+ mozilla::a11y::AccGenericType::eAllGenericTypes> {};
+
+template <>
+struct ParamTraits<mozilla::a11y::CacheUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::a11y::CacheUpdateType,
+ mozilla::a11y::CacheUpdateType::Initial,
+ mozilla::a11y::CacheUpdateType::Update> {};
+
+template <>
+struct ParamTraits<mozilla::a11y::FontSize> {
+ typedef mozilla::a11y::FontSize paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mValue);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &(aResult->mValue));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::a11y::DeleteEntry> {
+ typedef mozilla::a11y::DeleteEntry paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mValue);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &(aResult->mValue));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::a11y::AccGroupInfo> {
+ typedef mozilla::a11y::AccGroupInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ MOZ_ASSERT_UNREACHABLE("Cannot serialize AccGroupInfo");
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ MOZ_ASSERT_UNREACHABLE("Cannot de-serialize AccGroupInfo");
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::a11y::Color> {
+ typedef mozilla::a11y::Color paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mValue);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &(aResult->mValue));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::a11y::AccAttributes*> {
+ typedef mozilla::a11y::AccAttributes paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType* aParam) {
+ if (!aParam) {
+ WriteParam(aWriter, true);
+ return;
+ }
+
+ WriteParam(aWriter, false);
+ uint32_t count = aParam->mData.Count();
+ WriteParam(aWriter, count);
+ for (auto iter = aParam->mData.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<nsAtom> key = iter.Key();
+ WriteParam(aWriter, key);
+ const paramType::AttrValueType& data = iter.Data();
+ WriteParam(aWriter, data);
+ }
+ }
+
+ static bool Read(MessageReader* aReader, RefPtr<paramType>* aResult) {
+ bool isNull = false;
+ if (!ReadParam(aReader, &isNull)) {
+ return false;
+ }
+
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ *aResult = mozilla::MakeRefPtr<mozilla::a11y::AccAttributes>();
+ uint32_t count;
+ if (!ReadParam(aReader, &count)) {
+ return false;
+ }
+ for (uint32_t i = 0; i < count; ++i) {
+ RefPtr<nsAtom> key;
+ if (!ReadParam(aReader, &key)) {
+ return false;
+ }
+ paramType::AttrValueType val(0);
+ if (!ReadParam(aReader, &val)) {
+ return false;
+ }
+ (*aResult)->mData.InsertOrUpdate(key, std::move(val));
+ }
+ return true;
+ }
+};
+
+} // namespace IPC
+#else
+namespace mozilla {
+namespace a11y {
+typedef uint32_t role;
+} // namespace a11y
+} // namespace mozilla
+#endif // ACCESSIBILITY
+
+/**
+ * Since IPDL does not support preprocessing, this header file allows us to
+ * define types used by PDocAccessible differently depending on platform.
+ */
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+
+// So that we don't include a bunch of other Windows junk.
+# if !defined(COM_NO_WINDOWS_H)
+# define COM_NO_WINDOWS_H
+# endif // !defined(COM_NO_WINDOWS_H)
+
+// COM headers pull in MSXML which conflicts with our own XMLDocument class.
+// This define excludes those conflicting definitions.
+# if !defined(__XMLDocument_FWD_DEFINED__)
+# define __XMLDocument_FWD_DEFINED__
+# endif // !defined(__XMLDocument_FWD_DEFINED__)
+
+# include <combaseapi.h>
+
+# include "mozilla/a11y/COMPtrTypes.h"
+
+// This define in rpcndr.h messes up our code, so we must undefine it after
+// COMPtrTypes.h has been included.
+# if defined(small)
+# undef small
+# endif // defined(small)
+
+#else
+
+namespace mozilla {
+namespace a11y {
+
+typedef uint32_t IAccessibleHolder;
+typedef uint32_t IDispatchHolder;
+typedef uint32_t IHandlerControlHolder;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+#if defined(MOZ_WIDGET_COCOA)
+# if defined(ACCESSIBILITY)
+# include "mozilla/a11y/PlatformExtTypes.h"
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::a11y::EWhichRange>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::a11y::EWhichRange, mozilla::a11y::EWhichRange::eLeftWord,
+ mozilla::a11y::EWhichRange::eStyle> {};
+
+template <>
+struct ParamTraits<mozilla::a11y::EWhichPostFilter>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::a11y::EWhichPostFilter,
+ mozilla::a11y::EWhichPostFilter::eContainsText,
+ mozilla::a11y::EWhichPostFilter::eContainsText> {};
+
+} // namespace IPC
+
+# else
+namespace mozilla {
+namespace a11y {
+typedef uint32_t EWhichRange;
+} // namespace a11y
+} // namespace mozilla
+# endif // defined(ACCESSIBILITY)
+#endif // defined(MOZ_WIDGET_COCOA)
+
+#endif // mozilla_a11y_IPCTypes_h
diff --git a/accessible/ipc/RemoteAccessibleBase.cpp b/accessible/ipc/RemoteAccessibleBase.cpp
new file mode 100644
index 0000000000..b8ea930aa6
--- /dev/null
+++ b/accessible/ipc/RemoteAccessibleBase.cpp
@@ -0,0 +1,1775 @@
+/* -*- 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 "ARIAMap.h"
+#include "CachedTableAccessible.h"
+#include "DocAccessible.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "mozilla/a11y/DocManager.h"
+#include "mozilla/a11y/Platform.h"
+#include "mozilla/a11y/RemoteAccessibleBase.h"
+#include "mozilla/a11y/RemoteAccessible.h"
+#include "mozilla/a11y/Role.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+#include "mozilla/Unused.h"
+#include "nsAccUtils.h"
+#include "nsTextEquivUtils.h"
+#include "Pivot.h"
+#include "Relation.h"
+#include "RelationType.h"
+#include "xpcAccessibleDocument.h"
+
+#ifdef A11Y_LOG
+# include "Logging.h"
+# define VERIFY_CACHE(domain) \
+ if (logging::IsEnabled(logging::eCache)) { \
+ Unused << mDoc->SendVerifyCache(mID, domain, mCachedFields); \
+ }
+#else
+# define VERIFY_CACHE(domain) \
+ do { \
+ } while (0)
+
+#endif
+
+namespace mozilla {
+namespace a11y {
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::Shutdown() {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
+ xpcAccessibleDocument* xpcDoc =
+ GetAccService()->GetCachedXPCDocument(Document());
+ if (xpcDoc) {
+ xpcDoc->NotifyOfShutdown(static_cast<Derived*>(this));
+ }
+
+ if (IsTable() || IsTableCell()) {
+ CachedTableAccessible::Invalidate(this);
+ }
+
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Remove this acc's relation map from the doc's map of
+ // reverse relations. Prune forward relations associated with this
+ // acc's reverse relations. This also removes the acc's map of reverse
+ // rels from the mDoc's mReverseRelations.
+ PruneRelationsOnShutdown();
+ }
+
+ // XXX Ideally this wouldn't be necessary, but it seems OuterDoc
+ // accessibles can be destroyed before the doc they own.
+ uint32_t childCount = mChildren.Length();
+ if (!IsOuterDoc()) {
+ for (uint32_t idx = 0; idx < childCount; idx++) mChildren[idx]->Shutdown();
+ } else {
+ if (childCount > 1) {
+ MOZ_CRASH("outer doc has too many documents!");
+ } else if (childCount == 1) {
+ mChildren[0]->AsDoc()->Unbind();
+ }
+ }
+
+ mChildren.Clear();
+ ProxyDestroyed(static_cast<Derived*>(this));
+ mDoc->RemoveAccessible(static_cast<Derived*>(this));
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::SetChildDoc(
+ DocAccessibleParent* aChildDoc) {
+ MOZ_ASSERT(aChildDoc);
+ MOZ_ASSERT(mChildren.Length() == 0);
+ mChildren.AppendElement(aChildDoc);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ClearChildDoc(
+ DocAccessibleParent* aChildDoc) {
+ MOZ_ASSERT(aChildDoc);
+ // This is possible if we're replacing one document with another: Doc 1
+ // has not had a chance to remove itself, but was already replaced by Doc 2
+ // in SetChildDoc(). This could result in two subsequent calls to
+ // ClearChildDoc() even though mChildren.Length() == 1.
+ MOZ_ASSERT(mChildren.Length() <= 1);
+ mChildren.RemoveElement(aChildDoc);
+}
+
+template <class Derived>
+uint32_t RemoteAccessibleBase<Derived>::EmbeddedChildCount() {
+ 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 RemoteAccessibleBase<Derived>::IndexOfEmbeddedChild(
+ Accessible* 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;
+}
+
+template <class Derived>
+Accessible* RemoteAccessibleBase<Derived>::EmbeddedChildAt(uint32_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;
+}
+
+template <class Derived>
+LocalAccessible* RemoteAccessibleBase<Derived>::OuterDocOfRemoteBrowser()
+ const {
+ auto tab = static_cast<dom::BrowserParent*>(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 Derived>
+void RemoteAccessibleBase<Derived>::SetParent(Derived* aParent) {
+ if (!aParent) {
+ mParent = kNoParent;
+ } else {
+ MOZ_ASSERT(!IsDoc() || !aParent->IsDoc());
+ mParent = aParent->ID();
+ }
+}
+
+template <class Derived>
+Derived* RemoteAccessibleBase<Derived>::RemoteParent() const {
+ if (mParent == kNoParent) {
+ return nullptr;
+ }
+
+ // if we are not a document then are parent is another proxy in the same
+ // document. That means we can just ask our document for the proxy with our
+ // parent id.
+ if (!IsDoc()) {
+ return Document()->GetAccessible(mParent);
+ }
+
+ // If we are a top level document then our parent is not a proxy.
+ if (AsDoc()->IsTopLevel()) {
+ return nullptr;
+ }
+
+ // Finally if we are a non top level document then our parent id is for a
+ // proxy in our parent document so get the proxy from there.
+ DocAccessibleParent* parentDoc = AsDoc()->ParentDoc();
+ MOZ_ASSERT(parentDoc);
+ MOZ_ASSERT(mParent);
+ return parentDoc->GetAccessible(mParent);
+}
+
+template <class Derived>
+ENameValueFlag RemoteAccessibleBase<Derived>::Name(nsString& aName) const {
+ ENameValueFlag nameFlag = eNameOK;
+ if (mCachedFields) {
+ if (IsText()) {
+ mCachedFields->GetAttribute(nsGkAtoms::text, aName);
+ return eNameOK;
+ }
+ auto cachedNameFlag =
+ mCachedFields->GetAttribute<int32_t>(nsGkAtoms::explicit_name);
+ if (cachedNameFlag) {
+ nameFlag = static_cast<ENameValueFlag>(*cachedNameFlag);
+ }
+ if (mCachedFields->GetAttribute(nsGkAtoms::name, aName)) {
+ VERIFY_CACHE(CacheDomain::NameAndDescription);
+ return nameFlag;
+ }
+ }
+
+ MOZ_ASSERT(aName.IsEmpty());
+ aName.SetIsVoid(true);
+ return nameFlag;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::Description(nsString& aDescription) const {
+ if (mCachedFields) {
+ mCachedFields->GetAttribute(nsGkAtoms::description, aDescription);
+ VERIFY_CACHE(CacheDomain::NameAndDescription);
+ }
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::Value(nsString& aValue) const {
+ if (mCachedFields) {
+ if (mCachedFields->HasAttribute(nsGkAtoms::aria_valuetext)) {
+ mCachedFields->GetAttribute(nsGkAtoms::aria_valuetext, aValue);
+ VERIFY_CACHE(CacheDomain::Value);
+ return;
+ }
+
+ if (HasNumericValue()) {
+ double checkValue = CurValue();
+ if (!IsNaN(checkValue)) {
+ aValue.AppendFloat(checkValue);
+ }
+ return;
+ }
+
+ const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+ // Value of textbox is a textified subtree.
+ if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::textbox)) {
+ nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
+ return;
+ }
+
+ if (IsCombobox()) {
+ // For combo boxes, rely on selection state to determine the value.
+ const Accessible* option =
+ const_cast<RemoteAccessibleBase<Derived>*>(this)->GetSelectedItem(0);
+ if (option) {
+ option->Name(aValue);
+ } else {
+ // If no selected item, determine the value from descendant elements.
+ nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
+ }
+ return;
+ }
+
+ if (IsTextLeaf() || IsImage()) {
+ if (const Accessible* actionAcc = ActionAncestor()) {
+ if (const_cast<Accessible*>(actionAcc)->State() & states::LINKED) {
+ // Text and image descendants of links expose the link URL as the
+ // value.
+ return actionAcc->Value(aValue);
+ }
+ }
+ }
+ }
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::CurValue() const {
+ if (mCachedFields) {
+ if (auto value = mCachedFields->GetAttribute<double>(nsGkAtoms::value)) {
+ VERIFY_CACHE(CacheDomain::Value);
+ return *value;
+ }
+ }
+
+ return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::MinValue() const {
+ if (mCachedFields) {
+ if (auto min = mCachedFields->GetAttribute<double>(nsGkAtoms::min)) {
+ VERIFY_CACHE(CacheDomain::Value);
+ return *min;
+ }
+ }
+
+ return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::MaxValue() const {
+ if (mCachedFields) {
+ if (auto max = mCachedFields->GetAttribute<double>(nsGkAtoms::max)) {
+ VERIFY_CACHE(CacheDomain::Value);
+ return *max;
+ }
+ }
+
+ return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::Step() const {
+ if (mCachedFields) {
+ if (auto step = mCachedFields->GetAttribute<double>(nsGkAtoms::step)) {
+ VERIFY_CACHE(CacheDomain::Value);
+ return *step;
+ }
+ }
+
+ return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+Accessible* RemoteAccessibleBase<Derived>::ChildAtPoint(
+ int32_t aX, int32_t aY, LocalAccessible::EWhichChildAtPoint aWhichChild) {
+ if (IsOuterDoc() && aWhichChild == EWhichChildAtPoint::DirectChild) {
+ // This is an iframe, which is as deep as the viewport cache goes. The
+ // caller wants a direct child, which can only be the embedded document.
+ if (Bounds().Contains(aX, aY)) {
+ return RemoteFirstChild();
+ }
+ return nullptr;
+ }
+
+ RemoteAccessible* lastMatch = nullptr;
+ // If `this` is a document, use its viewport cache instead of
+ // the cache of its parent document.
+ if (DocAccessibleParent* doc = IsDoc() ? AsDoc() : mDoc) {
+ if (!doc->mCachedFields) {
+ // A client call might arrive after we've constructed doc but before we
+ // get a cache push for it.
+ return nullptr;
+ }
+ if (auto maybeViewportCache =
+ doc->mCachedFields->GetAttribute<nsTArray<uint64_t>>(
+ nsGkAtoms::viewport)) {
+ // The retrieved viewport cache contains acc IDs in hittesting order.
+ // That is, items earlier in the list have z-indexes that are larger than
+ // those later in the list. If you were to build a tree by z-index, where
+ // chilren have larger z indices than their parents, iterating this list
+ // is essentially a postorder tree traversal.
+ const nsTArray<uint64_t>& viewportCache = *maybeViewportCache;
+
+ for (auto id : viewportCache) {
+ RemoteAccessible* acc = doc->GetAccessible(id);
+ if (!acc) {
+ // This can happen if the acc died in between
+ // pushing the viewport cache and doing this hittest
+ continue;
+ }
+
+ if (acc->IsOuterDoc() &&
+ aWhichChild == EWhichChildAtPoint::DeepestChild &&
+ acc->Bounds().Contains(aX, aY)) {
+ // acc is an iframe, which is as deep as the viewport cache goes. This
+ // iframe contains the requested point.
+ RemoteAccessible* innerDoc = acc->RemoteFirstChild();
+ if (innerDoc) {
+ MOZ_ASSERT(innerDoc->IsDoc());
+ // Search the embedded document's viewport cache so we return the
+ // deepest descendant in that embedded document.
+ Accessible* deepestAcc = innerDoc->ChildAtPoint(
+ aX, aY, EWhichChildAtPoint::DeepestChild);
+ MOZ_ASSERT(!deepestAcc || deepestAcc->IsRemote());
+ lastMatch = deepestAcc ? deepestAcc->AsRemote() : nullptr;
+ break;
+ }
+ // If there is no embedded document, the iframe itself is the deepest
+ // descendant.
+ lastMatch = acc;
+ break;
+ }
+
+ if (acc == this) {
+ MOZ_ASSERT(!acc->IsOuterDoc());
+ // Even though we're searching from the doc's cache
+ // this call shouldn't pass the boundary defined by
+ // the acc this call originated on. If we hit `this`,
+ // return our most recent match.
+ break;
+ }
+
+ if (acc->Bounds().Contains(aX, aY)) {
+ // Because our rects are in hittesting order, the
+ // first match we encounter is guaranteed to be the
+ // deepest match.
+ lastMatch = acc;
+ break;
+ }
+ }
+ }
+ }
+
+ if (aWhichChild == EWhichChildAtPoint::DirectChild && lastMatch) {
+ // lastMatch is the deepest match. Walk up to the direct child of this.
+ RemoteAccessible* parent = lastMatch->RemoteParent();
+ for (;;) {
+ if (parent == this) {
+ break;
+ }
+ if (!parent || parent->IsDoc()) {
+ // `this` is not an ancestor of lastMatch. Ignore lastMatch.
+ lastMatch = nullptr;
+ break;
+ }
+ lastMatch = parent;
+ parent = parent->RemoteParent();
+ }
+ } else if (aWhichChild == EWhichChildAtPoint::DeepestChild && lastMatch &&
+ !IsDoc() && !IsAncestorOf(lastMatch)) {
+ // If we end up with a match that is not in the ancestor chain
+ // of the accessible this call originated on, we should ignore it.
+ // This can happen when the aX, aY given are outside `this`.
+ lastMatch = nullptr;
+ }
+
+ if (!lastMatch && Bounds().Contains(aX, aY)) {
+ // Even though the hit target isn't inside `this`, the point is still
+ // within our bounds, so fall back to `this`.
+ return this;
+ }
+
+ return lastMatch;
+}
+
+template <class Derived>
+Maybe<nsRect> RemoteAccessibleBase<Derived>::RetrieveCachedBounds() const {
+ if (!mCachedFields) {
+ return Nothing();
+ }
+
+ Maybe<const nsTArray<int32_t>&> maybeArray =
+ mCachedFields->GetAttribute<nsTArray<int32_t>>(nsGkAtoms::relativeBounds);
+ if (maybeArray) {
+ const nsTArray<int32_t>& relativeBoundsArr = *maybeArray;
+ MOZ_ASSERT(relativeBoundsArr.Length() == 4,
+ "Incorrectly sized bounds array");
+ nsRect relativeBoundsRect(relativeBoundsArr[0], relativeBoundsArr[1],
+ relativeBoundsArr[2], relativeBoundsArr[3]);
+ return Some(relativeBoundsRect);
+ }
+
+ return Nothing();
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ApplyCrossDocOffset(nsRect& aBounds) const {
+ Accessible* parentAcc = Parent();
+ if (!parentAcc || !parentAcc->IsRemote() || !parentAcc->IsOuterDoc()) {
+ return;
+ }
+
+ if (!IsDoc()) {
+ // We should only apply cross-doc offsets to documents. If we're anything
+ // else, return early here.
+ return;
+ }
+
+ Maybe<const nsTArray<int32_t>&> maybeOffset =
+ parentAcc->AsRemote()->mCachedFields->GetAttribute<nsTArray<int32_t>>(
+ nsGkAtoms::crossorigin);
+ if (!maybeOffset) {
+ return;
+ }
+
+ MOZ_ASSERT(maybeOffset->Length() == 2);
+ const nsTArray<int32_t>& offset = *maybeOffset;
+ // Our retrieved value is in app units, so we don't need to do any
+ // unit conversion here.
+ aBounds.MoveBy(offset[0], offset[1]);
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::ApplyTransform(
+ nsRect& aCumulativeBounds, const nsRect& aParentRelativeBounds) const {
+ // First, attempt to retrieve the transform from the cache.
+ Maybe<const UniquePtr<gfx::Matrix4x4>&> maybeTransform =
+ mCachedFields->GetAttribute<UniquePtr<gfx::Matrix4x4>>(
+ nsGkAtoms::transform);
+ if (!maybeTransform) {
+ return false;
+ }
+
+ // The transform matrix we cache is meant to operate on rects
+ // within the coordinate space of the frame to which the
+ // transform is applied (self-relative rects). We cache bounds
+ // relative to some ancestor. Remove the relative offset before
+ // transforming. The transform matrix will add it back in.
+ aCumulativeBounds.MoveBy(-aParentRelativeBounds.TopLeft());
+
+ auto mtxInPixels = gfx::Matrix4x4Typed<CSSPixel, CSSPixel>::FromUnknownMatrix(
+ *(*maybeTransform));
+
+ // Our matrix is in CSS Pixels, so we need our rect to be in CSS
+ // Pixels too. Convert before applying.
+ auto boundsInPixels = CSSRect::FromAppUnits(aCumulativeBounds);
+ boundsInPixels = mtxInPixels.TransformBounds(boundsInPixels);
+ aCumulativeBounds = CSSRect::ToAppUnits(boundsInPixels);
+
+ return true;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ApplyScrollOffset(nsRect& aBounds) const {
+ Maybe<const nsTArray<int32_t>&> maybeScrollPosition =
+ mCachedFields->GetAttribute<nsTArray<int32_t>>(nsGkAtoms::scrollPosition);
+
+ if (!maybeScrollPosition || maybeScrollPosition->Length() != 2) {
+ return;
+ }
+ // Our retrieved value is in app units, so we don't need to do any
+ // unit conversion here.
+ const nsTArray<int32_t>& scrollPosition = *maybeScrollPosition;
+
+ // Scroll position is an inverse representation of scroll offset (since the
+ // further the scroll bar moves down the page, the further the page content
+ // moves up/closer to the origin).
+ nsPoint scrollOffset(-scrollPosition[0], -scrollPosition[1]);
+
+ aBounds.MoveBy(scrollOffset.x, scrollOffset.y);
+}
+
+template <class Derived>
+nsRect RemoteAccessibleBase<Derived>::BoundsInAppUnits() const {
+ if (dom::CanonicalBrowsingContext* cbc = mDoc->GetBrowsingContext()->Top()) {
+ if (dom::BrowserParent* bp = cbc->GetBrowserParent()) {
+ DocAccessibleParent* topDoc = bp->GetTopLevelDocAccessible();
+ if (topDoc && topDoc->mCachedFields) {
+ auto appUnitsPerDevPixel = topDoc->mCachedFields->GetAttribute<int32_t>(
+ nsGkAtoms::_moz_device_pixel_ratio);
+ MOZ_ASSERT(appUnitsPerDevPixel);
+ return LayoutDeviceIntRect::ToAppUnits(Bounds(), *appUnitsPerDevPixel);
+ }
+ }
+ }
+ return LayoutDeviceIntRect::ToAppUnits(Bounds(), AppUnitsPerCSSPixel());
+}
+
+template <class Derived>
+LayoutDeviceIntRect RemoteAccessibleBase<Derived>::BoundsWithOffset(
+ Maybe<nsRect> aOffset) const {
+ Maybe<nsRect> maybeBounds = RetrieveCachedBounds();
+ if (maybeBounds) {
+ nsRect bounds = *maybeBounds;
+ const DocAccessibleParent* topDoc = IsDoc() ? AsDoc() : nullptr;
+
+ if (aOffset.isSome()) {
+ // The rect we've passed in is in app units, so no conversion needed.
+ nsRect internalRect = *aOffset;
+ bounds.SetRectX(bounds.x + internalRect.x, internalRect.width);
+ bounds.SetRectY(bounds.y + internalRect.y, internalRect.height);
+ }
+
+ ApplyCrossDocOffset(bounds);
+
+ Unused << ApplyTransform(bounds, *maybeBounds);
+
+ LayoutDeviceIntRect devPxBounds;
+ const Accessible* acc = Parent();
+ while (acc && acc->IsRemote()) {
+ RemoteAccessible* remoteAcc = const_cast<Accessible*>(acc)->AsRemote();
+
+ if (Maybe<nsRect> maybeRemoteBounds = remoteAcc->RetrieveCachedBounds()) {
+ nsRect remoteBounds = *maybeRemoteBounds;
+ // We need to take into account a non-1 resolution set on the
+ // presshell. This happens with async pinch zooming, among other
+ // things. We can't reliably query this value in the parent process,
+ // so we retrieve it from the document's cache.
+ if (remoteAcc->IsDoc()) {
+ // Apply the document's resolution to the bounds we've gathered
+ // thus far. We do this before applying the document's offset
+ // because document accs should not have their bounds scaled by
+ // their own resolution. They should be scaled by the resolution
+ // of their containing document (if any).
+ Maybe<float> res =
+ remoteAcc->AsDoc()->mCachedFields->GetAttribute<float>(
+ nsGkAtoms::resolution);
+ MOZ_ASSERT(res, "No cached document resolution found.");
+ bounds.ScaleRoundOut(res.valueOr(1.0f));
+
+ topDoc = remoteAcc->AsDoc();
+ }
+
+ // We don't account for the document offset of iframes when computing
+ // parent-relative bounds. Instead, we store this value separately on
+ // all iframes and apply it here. See the comments in
+ // LocalAccessible::BundleFieldsForCache where we set the
+ // nsGkAtoms::crossorigin attribute.
+ remoteAcc->ApplyCrossDocOffset(remoteBounds);
+
+ // Apply scroll offset, if applicable. Only the contents of an
+ // element are affected by its scroll offset, which is why this call
+ // happens in this loop instead of both inside and outside of
+ // the loop (like ApplyTransform).
+ remoteAcc->ApplyScrollOffset(remoteBounds);
+
+ // Regardless of whether this is a doc, we should offset `bounds`
+ // by the bounds retrieved here. This is how we build screen
+ // coordinates from relative coordinates.
+ bounds.MoveBy(remoteBounds.X(), remoteBounds.Y());
+ Unused << remoteAcc->ApplyTransform(bounds, remoteBounds);
+ }
+ acc = acc->Parent();
+ }
+
+ MOZ_ASSERT(topDoc);
+ if (topDoc) {
+ // We use the top documents app-units-per-dev-pixel even though
+ // theoretically nested docs can have different values. Practically,
+ // that isn't likely since we only offer zoom controls for the top
+ // document and all subdocuments inherit from it.
+ auto appUnitsPerDevPixel = topDoc->mCachedFields->GetAttribute<int32_t>(
+ nsGkAtoms::_moz_device_pixel_ratio);
+ MOZ_ASSERT(appUnitsPerDevPixel);
+ if (appUnitsPerDevPixel) {
+ // Convert our existing `bounds` rect from app units to dev pixels
+ devPxBounds = LayoutDeviceIntRect::FromAppUnitsToNearest(
+ bounds, *appUnitsPerDevPixel);
+ }
+ }
+
+#if !defined(ANDROID)
+ // This block is not thread safe because it queries a LocalAccessible.
+ // It is also not needed in Android since the only local accessible is
+ // the outer doc browser that has an offset of 0.
+ // acc could be null if the OuterDocAccessible died before the top level
+ // DocAccessibleParent.
+ if (LocalAccessible* localAcc =
+ acc ? const_cast<Accessible*>(acc)->AsLocal() : nullptr) {
+ // LocalAccessible::Bounds returns screen-relative bounds in
+ // dev pixels.
+ LayoutDeviceIntRect localBounds = localAcc->Bounds();
+
+ // The root document will always have an APZ resolution of 1,
+ // so we don't factor in its scale here. We also don't scale
+ // by GetFullZoom because LocalAccessible::Bounds already does
+ // that.
+ devPxBounds.MoveBy(localBounds.X(), localBounds.Y());
+ }
+#endif
+
+ return devPxBounds;
+ }
+
+ return LayoutDeviceIntRect();
+}
+
+template <class Derived>
+LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
+ return BoundsWithOffset(Nothing());
+}
+
+template <class Derived>
+Relation RemoteAccessibleBase<Derived>::RelationByType(
+ RelationType aType) const {
+ // We are able to handle some relations completely in the
+ // parent process, without the help of the cache. Those
+ // relations are enumerated here. Other relations, whose
+ // types are stored in kRelationTypeAtoms, are processed
+ // below using the cache.
+ if (aType == RelationType::CONTAINING_TAB_PANE) {
+ if (dom::CanonicalBrowsingContext* cbc = mDoc->GetBrowsingContext()) {
+ if (dom::CanonicalBrowsingContext* topCbc = cbc->Top()) {
+ if (dom::BrowserParent* bp = topCbc->GetBrowserParent()) {
+ return Relation(bp->GetTopLevelDocAccessible());
+ }
+ }
+ }
+ return Relation();
+ }
+
+ if (aType == RelationType::LINKS_TO && Role() == roles::LINK) {
+ Pivot p = Pivot(mDoc);
+ nsString href;
+ Value(href);
+ int32_t i = href.FindChar('#');
+ int32_t len = static_cast<int32_t>(href.Length());
+ if (i != -1 && i < (len - 1)) {
+ nsDependentSubstring anchorName = Substring(href, i + 1, len);
+ MustPruneSameDocRule rule;
+ Accessible* nameMatch = nullptr;
+ for (Accessible* match = p.Next(mDoc, rule); match;
+ match = p.Next(match, rule)) {
+ nsString currID;
+ match->DOMNodeID(currID);
+ MOZ_ASSERT(match->IsRemote());
+ if (anchorName.Equals(currID)) {
+ return Relation(match->AsRemote());
+ }
+ if (!nameMatch) {
+ nsString currName = match->AsRemote()->GetCachedHTMLNameAttribute();
+ if (match->TagName() == nsGkAtoms::a && anchorName.Equals(currName)) {
+ // If we find an element with a matching ID, we should return
+ // that, but if we don't we should return the first anchor with
+ // a matching name. To avoid doing two traversals, store the first
+ // name match here.
+ nameMatch = match;
+ }
+ }
+ }
+ return nameMatch ? Relation(nameMatch->AsRemote()) : Relation();
+ }
+
+ return Relation();
+ }
+
+ // Handle ARIA tree, treegrid parent/child relations. Each of these cases
+ // relies on cached group info. To find the parent of an accessible, use the
+ // unified conceptual parent.
+ if (aType == RelationType::NODE_CHILD_OF) {
+ const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+ if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
+ roleMapEntry->role == roles::LISTITEM ||
+ roleMapEntry->role == roles::ROW)) {
+ if (const AccGroupInfo* groupInfo =
+ const_cast<RemoteAccessibleBase<Derived>*>(this)
+ ->GetOrCreateGroupInfo()) {
+ return Relation(groupInfo->ConceptualParent());
+ }
+ }
+ return Relation();
+ }
+
+ // To find the children of a parent, provide an iterator through its items.
+ if (aType == RelationType::NODE_PARENT_OF) {
+ const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
+ if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
+ roleMapEntry->role == roles::LISTITEM ||
+ roleMapEntry->role == roles::ROW ||
+ roleMapEntry->role == roles::OUTLINE ||
+ roleMapEntry->role == roles::LIST ||
+ roleMapEntry->role == roles::TREE_TABLE)) {
+ return Relation(new ItemIterator(this));
+ }
+ return Relation();
+ }
+
+ if (aType == RelationType::MEMBER_OF) {
+ Relation rel = Relation();
+ // HTML radio buttons with cached names should be grouped.
+ if (IsHTMLRadioButton()) {
+ nsString name = GetCachedHTMLNameAttribute();
+ if (name.IsEmpty()) {
+ return rel;
+ }
+
+ RemoteAccessible* ancestor = RemoteParent();
+ while (ancestor && ancestor->Role() != roles::FORM && ancestor != mDoc) {
+ ancestor = ancestor->RemoteParent();
+ }
+ if (ancestor) {
+ // Sometimes we end up with an unparented acc here, potentially
+ // because the acc is being moved. See bug 1807639.
+ // Pivot expects to be created with a non-null mRoot.
+ Pivot p = Pivot(ancestor);
+ PivotRadioNameRule rule(name);
+ Accessible* match = p.Next(ancestor, rule);
+ while (match) {
+ rel.AppendTarget(match->AsRemote());
+ match = p.Next(match, rule);
+ }
+ }
+ return rel;
+ }
+
+ if (IsARIARole(nsGkAtoms::radio)) {
+ // ARIA radio buttons should be grouped by their radio group
+ // parent, if one exists.
+ RemoteAccessible* currParent = RemoteParent();
+ while (currParent && currParent->Role() != roles::RADIO_GROUP) {
+ currParent = currParent->RemoteParent();
+ }
+
+ if (currParent && currParent->Role() == roles::RADIO_GROUP) {
+ // If we found a radiogroup parent, search for all
+ // roles::RADIOBUTTON children and add them to our relation.
+ // This search will include the radio button this method
+ // was called from, which is expected.
+ Pivot p = Pivot(currParent);
+ PivotRoleRule rule(roles::RADIOBUTTON);
+ Accessible* match = p.Next(currParent, rule);
+ while (match) {
+ MOZ_ASSERT(match->IsRemote(),
+ "We should only be traversing the remote tree.");
+ rel.AppendTarget(match->AsRemote());
+ match = p.Next(match, rule);
+ }
+ }
+ }
+ // By webkit's standard, aria radio buttons do not get grouped
+ // if they lack a group parent, so we return an empty
+ // relation here if the above check fails.
+ return rel;
+ }
+
+ Relation rel;
+ if (!mCachedFields) {
+ return rel;
+ }
+
+ for (const auto& data : kRelationTypeAtoms) {
+ if (data.mType != aType ||
+ (data.mValidTag && TagName() != data.mValidTag)) {
+ continue;
+ }
+
+ if (auto maybeIds =
+ mCachedFields->GetAttribute<nsTArray<uint64_t>>(data.mAtom)) {
+ rel.AppendIter(new RemoteAccIterator(*maybeIds, Document()));
+ }
+ // Each relation type has only one relevant cached attribute,
+ // so break after we've handled the attr for this type,
+ // even if we didn't find any targets.
+ break;
+ }
+
+ if (auto accRelMapEntry = mDoc->mReverseRelations.Lookup(ID())) {
+ if (auto reverseIdsEntry = accRelMapEntry.Data().Lookup(aType)) {
+ rel.AppendIter(new RemoteAccIterator(reverseIdsEntry.Data(), Document()));
+ }
+ }
+
+ return rel;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::AppendTextTo(nsAString& aText,
+ uint32_t aStartOffset,
+ uint32_t aLength) {
+ if (IsText()) {
+ if (mCachedFields) {
+ if (auto text = mCachedFields->GetAttribute<nsString>(nsGkAtoms::text)) {
+ aText.Append(Substring(*text, aStartOffset, aLength));
+ }
+ VERIFY_CACHE(CacheDomain::Text);
+ }
+ return;
+ }
+
+ if (aStartOffset != 0 || aLength == 0) {
+ return;
+ }
+
+ if (IsHTMLBr()) {
+ aText += kForcedNewLineChar;
+ } else if (RemoteParent() && nsAccUtils::MustPrune(RemoteParent())) {
+ // Expose the embedded object accessible as imaginary embedded object
+ // character if its parent hypertext accessible doesn't expose children to
+ // AT.
+ aText += kImaginaryEmbeddedObjectChar;
+ } else {
+ aText += kEmbeddedObjectChar;
+ }
+}
+
+template <class Derived>
+nsTArray<bool> RemoteAccessibleBase<Derived>::PreProcessRelations(
+ AccAttributes* aFields) {
+ nsTArray<bool> updateTracker(ArrayLength(kRelationTypeAtoms));
+ for (auto const& data : kRelationTypeAtoms) {
+ if (data.mValidTag) {
+ // The relation we're currently processing only applies to particular
+ // elements. Check to see if we're one of them.
+ nsAtom* tag = TagName();
+ if (!tag) {
+ // TagName() returns null on an initial cache push -- check aFields
+ // for a tag name instead.
+ if (auto maybeTag =
+ aFields->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::tag)) {
+ tag = *maybeTag;
+ }
+ }
+ MOZ_ASSERT(
+ tag || IsTextLeaf() || IsDoc(),
+ "Could not fetch tag via TagName() or from initial cache push!");
+ if (tag != data.mValidTag) {
+ // If this rel doesn't apply to us, do no pre-processing. Also,
+ // note in our updateTracker that we should do no post-processing.
+ updateTracker.AppendElement(false);
+ continue;
+ }
+ }
+
+ nsStaticAtom* const relAtom = data.mAtom;
+ auto newRelationTargets =
+ aFields->GetAttribute<nsTArray<uint64_t>>(relAtom);
+ bool shouldAddNewImplicitRels =
+ newRelationTargets && newRelationTargets->Length();
+
+ // Remove existing implicit relations if we need to perform an update, or
+ // if we've recieved a DeleteEntry(). Only do this if mCachedFields is
+ // initialized. If mCachedFields is not initialized, we still need to
+ // construct the update array so we correctly handle reverse rels in
+ // PostProcessRelations.
+ if ((shouldAddNewImplicitRels ||
+ aFields->GetAttribute<DeleteEntry>(relAtom)) &&
+ mCachedFields) {
+ if (auto maybeOldIDs =
+ mCachedFields->GetAttribute<nsTArray<uint64_t>>(relAtom)) {
+ for (uint64_t id : *maybeOldIDs) {
+ // For each target, fetch its reverse relation map
+ // We need to call `Lookup` here instead of `LookupOrInsert` because
+ // it's possible the ID we're querying is from an acc that has since
+ // been Shutdown(), and so has intentionally removed its reverse rels
+ // from the doc's reverse rel cache.
+ if (auto reverseRels = Document()->mReverseRelations.Lookup(id)) {
+ // Then fetch its reverse relation's ID list. This should be safe
+ // to do via LookupOrInsert because by the time we've gotten here,
+ // we know the acc and `this` are still alive in the doc. If we hit
+ // the following assert, we don't have parity on implicit/explicit
+ // rels and something is wrong.
+ nsTArray<uint64_t>& reverseRelIDs =
+ reverseRels->LookupOrInsert(data.mReverseType);
+ // There might be other reverse relations stored for this acc, so
+ // remove our ID instead of deleting the array entirely.
+ DebugOnly<bool> removed = reverseRelIDs.RemoveElement(ID());
+ MOZ_ASSERT(removed, "Can't find old reverse relation");
+ }
+ }
+ }
+ }
+
+ updateTracker.AppendElement(shouldAddNewImplicitRels);
+ }
+
+ return updateTracker;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::PostProcessRelations(
+ const nsTArray<bool>& aToUpdate) {
+ size_t updateCount = aToUpdate.Length();
+ MOZ_ASSERT(updateCount == ArrayLength(kRelationTypeAtoms),
+ "Did not note update status for every relation type!");
+ for (size_t i = 0; i < updateCount; i++) {
+ if (aToUpdate.ElementAt(i)) {
+ // Since kRelationTypeAtoms was used to generate aToUpdate, we
+ // know the ith entry of aToUpdate corresponds to the relation type in
+ // the ith entry of kRelationTypeAtoms. Fetch the related data here.
+ auto const& data = kRelationTypeAtoms[i];
+
+ const nsTArray<uint64_t>& newIDs =
+ *mCachedFields->GetAttribute<nsTArray<uint64_t>>(data.mAtom);
+ for (uint64_t id : newIDs) {
+ nsTHashMap<RelationType, nsTArray<uint64_t>>& relations =
+ Document()->mReverseRelations.LookupOrInsert(id);
+ nsTArray<uint64_t>& ids = relations.LookupOrInsert(data.mReverseType);
+ ids.AppendElement(ID());
+ }
+ }
+ }
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::PruneRelationsOnShutdown() {
+ auto reverseRels = mDoc->mReverseRelations.Lookup(ID());
+ if (!reverseRels) {
+ return;
+ }
+ for (auto const& data : kRelationTypeAtoms) {
+ // Fetch the list of targets for this reverse relation
+ auto reverseTargetList = reverseRels->Lookup(data.mReverseType);
+ if (!reverseTargetList) {
+ continue;
+ }
+ for (uint64_t id : *reverseTargetList) {
+ // For each target, retrieve its corresponding forward relation target
+ // list
+ RemoteAccessible* affectedAcc = mDoc->GetAccessible(id);
+ if (!affectedAcc) {
+ // It's possible the affect acc also shut down, in which case
+ // we don't have anything to update.
+ continue;
+ }
+ if (auto forwardTargetList =
+ affectedAcc->mCachedFields
+ ->GetMutableAttribute<nsTArray<uint64_t>>(data.mAtom)) {
+ forwardTargetList->RemoveElement(ID());
+ if (!forwardTargetList->Length()) {
+ // The ID we removed was the only thing in the list, so remove the
+ // entry from the cache entirely -- don't leave an empty array.
+ affectedAcc->mCachedFields->Remove(data.mAtom);
+ }
+ }
+ }
+ }
+ // Remove this ID from the document's map of reverse relations.
+ reverseRels.Remove();
+}
+
+template <class Derived>
+uint32_t RemoteAccessibleBase<Derived>::GetCachedTextLength() {
+ MOZ_ASSERT(!HasChildren());
+ if (!mCachedFields) {
+ return 0;
+ }
+ VERIFY_CACHE(CacheDomain::Text);
+ auto text = mCachedFields->GetAttribute<nsString>(nsGkAtoms::text);
+ if (!text) {
+ return 0;
+ }
+ return text->Length();
+}
+
+template <class Derived>
+Maybe<const nsTArray<int32_t>&>
+RemoteAccessibleBase<Derived>::GetCachedTextLines() {
+ MOZ_ASSERT(!HasChildren());
+ if (!mCachedFields) {
+ return Nothing();
+ }
+ VERIFY_CACHE(CacheDomain::Text);
+ return mCachedFields->GetAttribute<nsTArray<int32_t>>(nsGkAtoms::line);
+}
+
+template <class Derived>
+Maybe<nsTArray<nsRect>> RemoteAccessibleBase<Derived>::GetCachedCharData() {
+ MOZ_ASSERT(IsText());
+ if (!mCachedFields) {
+ return Nothing();
+ }
+
+ if (Maybe<const nsTArray<int32_t>&> maybeCharData =
+ mCachedFields->GetAttribute<nsTArray<int32_t>>(
+ nsGkAtoms::characterData)) {
+ const nsTArray<int32_t>& charData = *maybeCharData;
+ nsTArray<nsRect> rects;
+ for (int i = 0; i < static_cast<int32_t>(charData.Length()); i += 4) {
+ nsRect r(charData[i], charData[i + 1], charData[i + 2], charData[i + 3]);
+ rects.AppendElement(r);
+ }
+ return Some(std::move(rects));
+ }
+
+ return Nothing();
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::DOMNodeID(nsString& aID) const {
+ if (mCachedFields) {
+ mCachedFields->GetAttribute(nsGkAtoms::id, aID);
+ VERIFY_CACHE(CacheDomain::DOMNodeID);
+ }
+}
+
+template <class Derived>
+RefPtr<const AccAttributes>
+RemoteAccessibleBase<Derived>::GetCachedTextAttributes() {
+ MOZ_ASSERT(IsText() || IsHyperText());
+ if (mCachedFields) {
+ auto attrs =
+ mCachedFields->GetAttributeRefPtr<AccAttributes>(nsGkAtoms::style);
+ VERIFY_CACHE(CacheDomain::Text);
+ return attrs;
+ }
+ return nullptr;
+}
+
+template <class Derived>
+already_AddRefed<AccAttributes>
+RemoteAccessibleBase<Derived>::DefaultTextAttributes() {
+ RefPtr<const AccAttributes> attrs = GetCachedTextAttributes();
+ RefPtr<AccAttributes> result = new AccAttributes();
+ if (attrs) {
+ attrs->CopyTo(result);
+ }
+ return result.forget();
+}
+
+template <class Derived>
+RefPtr<const AccAttributes>
+RemoteAccessibleBase<Derived>::GetCachedARIAAttributes() const {
+ if (mCachedFields) {
+ auto attrs =
+ mCachedFields->GetAttributeRefPtr<AccAttributes>(nsGkAtoms::aria);
+ VERIFY_CACHE(CacheDomain::ARIA);
+ return attrs;
+ }
+ return nullptr;
+}
+
+template <class Derived>
+nsString RemoteAccessibleBase<Derived>::GetCachedHTMLNameAttribute() const {
+ if (mCachedFields) {
+ if (auto maybeName =
+ mCachedFields->GetAttribute<nsString>(nsGkAtoms::attributeName)) {
+ return *maybeName;
+ }
+ }
+ return nsString();
+}
+
+template <class Derived>
+uint64_t RemoteAccessibleBase<Derived>::State() {
+ uint64_t state = 0;
+ if (mCachedFields) {
+ if (auto rawState =
+ mCachedFields->GetAttribute<uint64_t>(nsGkAtoms::state)) {
+ VERIFY_CACHE(CacheDomain::State);
+ state = *rawState;
+ // Handle states that are derived from other states.
+ if (!(state & states::UNAVAILABLE)) {
+ state |= states::ENABLED | states::SENSITIVE;
+ }
+ if (state & states::EXPANDABLE && !(state & states::EXPANDED)) {
+ state |= states::COLLAPSED;
+ }
+ }
+
+ ApplyImplicitState(state);
+
+ auto* cbc = mDoc->GetBrowsingContext();
+ if (cbc && !cbc->IsActive()) {
+ // If our browsing context is _not_ active, we're in a background tab
+ // and inherently offscreen.
+ state |= states::OFFSCREEN;
+ } else {
+ // If we're in an active browsing context, there are a few scenarios we
+ // need to address:
+ // - We are an iframe document in the visual viewport
+ // - We are an iframe document out of the visual viewport
+ // - We are non-iframe content in the visual viewport
+ // - We are non-iframe content out of the visual viewport
+ // We assume top level tab docs are on screen if their BC is active, so
+ // we don't need additional handling for them here.
+ if (!mDoc->IsTopLevel()) {
+ // Here we handle iframes and iframe content.
+ // We use an iframe's outer doc's position in the embedding document's
+ // viewport to determine if the iframe has been scrolled offscreen.
+ Accessible* docParent = mDoc->Parent();
+ // In rare cases, we might not have an outer doc yet. Return if that's
+ // the case.
+ if (NS_WARN_IF(!docParent || !docParent->IsRemote())) {
+ return state;
+ }
+
+ RemoteAccessible* outerDoc = docParent->AsRemote();
+ DocAccessibleParent* embeddingDocument = outerDoc->Document();
+ if (embeddingDocument &&
+ !embeddingDocument->mOnScreenAccessibles.Contains(outerDoc->ID())) {
+ // Our embedding document's viewport cache doesn't contain the ID of
+ // our outer doc, so this iframe (and any of its content) is
+ // offscreen.
+ state |= states::OFFSCREEN;
+ } else if (this != mDoc && !mDoc->mOnScreenAccessibles.Contains(ID())) {
+ // Our embedding document's viewport cache contains the ID of our
+ // outer doc, but the iframe's viewport cache doesn't contain our ID.
+ // We are offscreen.
+ state |= states::OFFSCREEN;
+ }
+ } else if (this != mDoc && !mDoc->mOnScreenAccessibles.Contains(ID())) {
+ // We are top level tab content (but not a top level tab doc).
+ // If our tab doc's viewport cache doesn't contain our ID, we're
+ // offscreen.
+ state |= states::OFFSCREEN;
+ }
+ }
+ }
+
+ return state;
+}
+
+template <class Derived>
+already_AddRefed<AccAttributes> RemoteAccessibleBase<Derived>::Attributes() {
+ RefPtr<AccAttributes> attributes = new AccAttributes();
+ nsAccessibilityService* accService = GetAccService();
+ if (!accService) {
+ // The service can be shut down before RemoteAccessibles. If it is shut
+ // down, we can't calculate some attributes. We're about to die anyway.
+ return attributes.forget();
+ }
+
+ if (mCachedFields) {
+ // We use GetAttribute instead of GetAttributeRefPtr because we need
+ // nsAtom, not const nsAtom.
+ if (auto tag =
+ mCachedFields->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::tag)) {
+ attributes->SetAttribute(nsGkAtoms::tag, *tag);
+ }
+
+ GroupPos groupPos = GroupPosition();
+ nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level, groupPos.setSize,
+ groupPos.posInSet);
+
+ bool hierarchical = false;
+ uint32_t itemCount = AccGroupInfo::TotalItemCount(this, &hierarchical);
+ if (itemCount) {
+ attributes->SetAttribute(nsGkAtoms::child_item_count,
+ static_cast<int32_t>(itemCount));
+ }
+
+ if (hierarchical) {
+ attributes->SetAttribute(nsGkAtoms::tree, true);
+ }
+
+ if (auto inputType = mCachedFields->GetAttribute<RefPtr<nsAtom>>(
+ nsGkAtoms::textInputType)) {
+ attributes->SetAttribute(nsGkAtoms::textInputType, *inputType);
+ }
+
+ if (RefPtr<nsAtom> display = DisplayStyle()) {
+ attributes->SetAttribute(nsGkAtoms::display, display);
+ }
+
+ if (TableCellAccessibleBase* cell = AsTableCellBase()) {
+ TableAccessibleBase* table = cell->Table();
+ uint32_t row = cell->RowIdx();
+ uint32_t col = cell->ColIdx();
+ int32_t cellIdx = table->CellIndexAt(row, col);
+ if (cellIdx != -1) {
+ attributes->SetAttribute(nsGkAtoms::tableCellIndex, cellIdx);
+ }
+ }
+
+ if (bool layoutGuess = TableIsProbablyForLayout()) {
+ attributes->SetAttribute(nsGkAtoms::layout_guess, layoutGuess);
+ }
+
+ accService->MarkupAttributes(this, attributes);
+
+ const nsRoleMapEntry* roleMap = ARIARoleMap();
+ nsAutoString role;
+ mCachedFields->GetAttribute(nsGkAtoms::role, role);
+ if (role.IsEmpty()) {
+ if (roleMap && roleMap->roleAtom != nsGkAtoms::_empty) {
+ // Single, known role.
+ attributes->SetAttribute(nsGkAtoms::xmlroles, roleMap->roleAtom);
+ } else if (nsAtom* landmark = LandmarkRole()) {
+ // Landmark role from markup; e.g. HTML <main>.
+ attributes->SetAttribute(nsGkAtoms::xmlroles, landmark);
+ }
+ } else {
+ // Unknown role or multiple roles.
+ attributes->SetAttribute(nsGkAtoms::xmlroles, std::move(role));
+ }
+
+ if (roleMap) {
+ nsAutoString live;
+ if (nsAccUtils::GetLiveAttrValue(roleMap->liveAttRule, live)) {
+ attributes->SetAttribute(nsGkAtoms::aria_live, std::move(live));
+ }
+ }
+
+ if (auto ariaAttrs = GetCachedARIAAttributes()) {
+ ariaAttrs->CopyTo(attributes);
+ }
+
+ nsAccUtils::SetLiveContainerAttributes(attributes, this);
+
+ nsString id;
+ DOMNodeID(id);
+ if (!id.IsEmpty()) {
+ attributes->SetAttribute(nsGkAtoms::id, std::move(id));
+ }
+ }
+
+ nsAutoString name;
+ if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
+ attributes->SetAttribute(nsGkAtoms::explicit_name, true);
+ }
+
+ // Expose the string value via the valuetext attribute. We test for the value
+ // interface because we don't want to expose traditional Value() information
+ // such as URLs on links and documents, or text in an input.
+ // XXX This is only needed for ATK, since other APIs have native ways to
+ // retrieve value text. We should probably move this into ATK specific code.
+ // For now, we do this because LocalAccessible does it.
+ if (HasNumericValue()) {
+ nsString valuetext;
+ Value(valuetext);
+ attributes->SetAttribute(nsGkAtoms::aria_valuetext, std::move(valuetext));
+ }
+
+ return attributes.forget();
+}
+
+template <class Derived>
+nsAtom* RemoteAccessibleBase<Derived>::TagName() const {
+ if (mCachedFields) {
+ if (auto tag =
+ mCachedFields->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::tag)) {
+ return *tag;
+ }
+ }
+
+ return nullptr;
+}
+
+template <class Derived>
+already_AddRefed<nsAtom> RemoteAccessibleBase<Derived>::DisplayStyle() const {
+ if (mCachedFields) {
+ if (auto display =
+ mCachedFields->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::display)) {
+ RefPtr<nsAtom> result = *display;
+ return result.forget();
+ }
+ }
+ return nullptr;
+}
+
+template <class Derived>
+float RemoteAccessibleBase<Derived>::Opacity() const {
+ if (mCachedFields) {
+ if (auto opacity = mCachedFields->GetAttribute<float>(nsGkAtoms::opacity)) {
+ return *opacity;
+ }
+ }
+
+ return 1.0f;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::LiveRegionAttributes(
+ nsAString* aLive, nsAString* aRelevant, Maybe<bool>* aAtomic,
+ nsAString* aBusy) const {
+ if (!mCachedFields) {
+ return;
+ }
+ RefPtr<const AccAttributes> attrs = GetCachedARIAAttributes();
+ if (!attrs) {
+ return;
+ }
+ if (aLive) {
+ attrs->GetAttribute(nsGkAtoms::aria_live, *aLive);
+ }
+ if (aRelevant) {
+ attrs->GetAttribute(nsGkAtoms::aria_relevant, *aRelevant);
+ }
+ if (aAtomic) {
+ if (auto value =
+ attrs->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::aria_atomic)) {
+ *aAtomic = Some(*value == nsGkAtoms::_true);
+ }
+ }
+ if (aBusy) {
+ attrs->GetAttribute(nsGkAtoms::aria_busy, *aBusy);
+ }
+}
+
+template <class Derived>
+Maybe<bool> RemoteAccessibleBase<Derived>::ARIASelected() const {
+ if (mCachedFields) {
+ return mCachedFields->GetAttribute<bool>(nsGkAtoms::aria_selected);
+ }
+ return Nothing();
+}
+
+template <class Derived>
+nsAtom* RemoteAccessibleBase<Derived>::GetPrimaryAction() const {
+ if (mCachedFields) {
+ if (auto action =
+ mCachedFields->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::action)) {
+ return *action;
+ }
+ }
+
+ return nullptr;
+}
+
+template <class Derived>
+uint8_t RemoteAccessibleBase<Derived>::ActionCount() const {
+ uint8_t actionCount = 0;
+ if (mCachedFields) {
+ if (HasPrimaryAction() || ActionAncestor()) {
+ actionCount++;
+ }
+
+ if (mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
+ actionCount++;
+ }
+ VERIFY_CACHE(CacheDomain::Actions);
+ }
+
+ return actionCount;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ActionNameAt(uint8_t aIndex,
+ nsAString& aName) {
+ if (mCachedFields) {
+ aName.Truncate();
+ nsAtom* action = GetPrimaryAction();
+ bool hasActionAncestor = !action && ActionAncestor();
+
+ switch (aIndex) {
+ case 0:
+ if (action) {
+ action->ToString(aName);
+ } else if (hasActionAncestor) {
+ aName.AssignLiteral("click ancestor");
+ } else if (mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
+ aName.AssignLiteral("showlongdesc");
+ }
+ break;
+ case 1:
+ if ((action || hasActionAncestor) &&
+ mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
+ aName.AssignLiteral("showlongdesc");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ VERIFY_CACHE(CacheDomain::Actions);
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::DoAction(uint8_t aIndex) const {
+ if (ActionCount() < aIndex + 1) {
+ return false;
+ }
+
+ Unused << mDoc->SendDoActionAsync(mID, aIndex);
+ return true;
+}
+
+template <class Derived>
+KeyBinding RemoteAccessibleBase<Derived>::AccessKey() const {
+ if (mCachedFields) {
+ if (auto value =
+ mCachedFields->GetAttribute<uint64_t>(nsGkAtoms::accesskey)) {
+ return KeyBinding(*value);
+ }
+ }
+ return KeyBinding();
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::SelectionRanges(
+ nsTArray<TextRange>* aRanges) const {
+ Document()->SelectionRanges(aRanges);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ARIAGroupPosition(
+ int32_t* aLevel, int32_t* aSetSize, int32_t* aPosInSet) const {
+ if (!mCachedFields) {
+ return;
+ }
+
+ if (aLevel) {
+ if (auto level =
+ mCachedFields->GetAttribute<int32_t>(nsGkAtoms::aria_level)) {
+ *aLevel = *level;
+ }
+ }
+ if (aSetSize) {
+ if (auto setsize =
+ mCachedFields->GetAttribute<int32_t>(nsGkAtoms::aria_setsize)) {
+ *aSetSize = *setsize;
+ }
+ }
+ if (aPosInSet) {
+ if (auto posinset =
+ mCachedFields->GetAttribute<int32_t>(nsGkAtoms::aria_posinset)) {
+ *aPosInSet = *posinset;
+ }
+ }
+}
+
+template <class Derived>
+AccGroupInfo* RemoteAccessibleBase<Derived>::GetGroupInfo() const {
+ if (!mCachedFields) {
+ return nullptr;
+ }
+
+ if (auto groupInfo = mCachedFields->GetAttribute<UniquePtr<AccGroupInfo>>(
+ nsGkAtoms::group)) {
+ return groupInfo->get();
+ }
+
+ return nullptr;
+}
+
+template <class Derived>
+AccGroupInfo* RemoteAccessibleBase<Derived>::GetOrCreateGroupInfo() {
+ AccGroupInfo* groupInfo = GetGroupInfo();
+ if (groupInfo) {
+ return groupInfo;
+ }
+
+ groupInfo = AccGroupInfo::CreateGroupInfo(this);
+ if (groupInfo) {
+ if (!mCachedFields) {
+ mCachedFields = new AccAttributes();
+ }
+
+ mCachedFields->SetAttribute(nsGkAtoms::group, groupInfo);
+ }
+
+ return groupInfo;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::InvalidateGroupInfo() {
+ if (mCachedFields) {
+ mCachedFields->Remove(nsGkAtoms::group);
+ }
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::GetPositionAndSetSize(int32_t* aPosInSet,
+ int32_t* aSetSize) {
+ if (IsHTMLRadioButton()) {
+ *aSetSize = 0;
+ Relation rel = RelationByType(RelationType::MEMBER_OF);
+ while (Accessible* radio = rel.Next()) {
+ ++*aSetSize;
+ if (radio == this) {
+ *aPosInSet = *aSetSize;
+ }
+ }
+ return;
+ }
+
+ Accessible::GetPositionAndSetSize(aPosInSet, aSetSize);
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::HasPrimaryAction() const {
+ return mCachedFields && mCachedFields->HasAttribute(nsGkAtoms::action);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::TakeFocus() const {
+ Unused << mDoc->SendTakeFocus(mID);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::ScrollTo(uint32_t aHow) const {
+ Unused << mDoc->SendScrollTo(mID, aHow);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SelectAccessible
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::SelectedItems(
+ nsTArray<Accessible*>* aItems) {
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTED);
+ for (Accessible* selected = p.First(rule); selected;
+ selected = p.Next(selected, rule)) {
+ aItems->AppendElement(selected);
+ }
+}
+
+template <class Derived>
+uint32_t RemoteAccessibleBase<Derived>::SelectedItemCount() {
+ uint32_t count = 0;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTED);
+ for (Accessible* selected = p.First(rule); selected;
+ selected = p.Next(selected, rule)) {
+ count++;
+ }
+
+ return count;
+}
+
+template <class Derived>
+Accessible* RemoteAccessibleBase<Derived>::GetSelectedItem(uint32_t aIndex) {
+ uint32_t index = 0;
+ Accessible* selected = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTED);
+ for (selected = p.First(rule); selected && index < aIndex;
+ selected = p.Next(selected, rule)) {
+ index++;
+ }
+
+ return selected;
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::IsItemSelected(uint32_t aIndex) {
+ uint32_t index = 0;
+ Accessible* selectable = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTABLE);
+ for (selectable = p.First(rule); selectable && index < aIndex;
+ selectable = p.Next(selectable, rule)) {
+ index++;
+ }
+
+ return selectable && selectable->State() & states::SELECTED;
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::AddItemToSelection(uint32_t aIndex) {
+ uint32_t index = 0;
+ Accessible* selectable = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTABLE);
+ for (selectable = p.First(rule); selectable && index < aIndex;
+ selectable = p.Next(selectable, rule)) {
+ index++;
+ }
+
+ if (selectable) selectable->SetSelected(true);
+
+ return static_cast<bool>(selectable);
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::RemoveItemFromSelection(uint32_t aIndex) {
+ uint32_t index = 0;
+ Accessible* selectable = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTABLE);
+ for (selectable = p.First(rule); selectable && index < aIndex;
+ selectable = p.Next(selectable, rule)) {
+ index++;
+ }
+
+ if (selectable) selectable->SetSelected(false);
+
+ return static_cast<bool>(selectable);
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::SelectAll() {
+ if ((State() & states::MULTISELECTABLE) == 0) {
+ return false;
+ }
+
+ bool success = false;
+ Accessible* selectable = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTABLE);
+ for (selectable = p.First(rule); selectable;
+ selectable = p.Next(selectable, rule)) {
+ success = true;
+ selectable->SetSelected(true);
+ }
+ return success;
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::UnselectAll() {
+ if ((State() & states::MULTISELECTABLE) == 0) {
+ return false;
+ }
+
+ bool success = false;
+ Accessible* selectable = nullptr;
+ Pivot p = Pivot(this);
+ PivotStateRule rule(states::SELECTABLE);
+ for (selectable = p.First(rule); selectable;
+ selectable = p.Next(selectable, rule)) {
+ success = true;
+ selectable->SetSelected(false);
+ }
+ return success;
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::TakeSelection() {
+ Unused << mDoc->SendTakeSelection(mID);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::SetSelected(bool aSelect) {
+ Unused << mDoc->SendSetSelected(mID, aSelect);
+}
+
+template <class Derived>
+TableAccessibleBase* RemoteAccessibleBase<Derived>::AsTableBase() {
+ MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup());
+ if (IsTable()) {
+ return CachedTableAccessible::GetFrom(this);
+ }
+ return nullptr;
+}
+
+template <class Derived>
+TableCellAccessibleBase* RemoteAccessibleBase<Derived>::AsTableCellBase() {
+ MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup());
+ if (IsTableCell()) {
+ return CachedTableCellAccessible::GetFrom(this);
+ }
+ return nullptr;
+}
+
+template <class Derived>
+bool RemoteAccessibleBase<Derived>::TableIsProbablyForLayout() {
+ MOZ_ASSERT(StaticPrefs::accessibility_cache_enabled_AtStartup());
+ if (mCachedFields) {
+ if (auto layoutGuess =
+ mCachedFields->GetAttribute<bool>(nsGkAtoms::layout_guess)) {
+ return *layoutGuess;
+ }
+ }
+ return false;
+}
+
+template <class Derived>
+nsTArray<int32_t>& RemoteAccessibleBase<Derived>::GetCachedHyperTextOffsets() {
+ if (mCachedFields) {
+ if (auto offsets = mCachedFields->GetMutableAttribute<nsTArray<int32_t>>(
+ nsGkAtoms::offset)) {
+ return *offsets;
+ }
+ }
+ nsTArray<int32_t> newOffsets;
+ if (!mCachedFields) {
+ mCachedFields = new AccAttributes();
+ }
+ mCachedFields->SetAttribute(nsGkAtoms::offset, std::move(newOffsets));
+ return *mCachedFields->GetMutableAttribute<nsTArray<int32_t>>(
+ nsGkAtoms::offset);
+}
+
+template <class Derived>
+void RemoteAccessibleBase<Derived>::SetCaretOffset(int32_t aOffset) {
+ Unused << mDoc->SendSetCaretOffset(mID, aOffset);
+}
+
+template <class Derived>
+Maybe<int32_t> RemoteAccessibleBase<Derived>::GetIntARIAAttr(
+ nsAtom* aAttrName) const {
+ if (RefPtr<const AccAttributes> attrs = GetCachedARIAAttributes()) {
+ if (auto val = attrs->GetAttribute<int32_t>(aAttrName)) {
+ return val;
+ }
+ }
+ return Nothing();
+}
+
+template <class Derived>
+size_t RemoteAccessibleBase<Derived>::SizeOfIncludingThis(
+ MallocSizeOf aMallocSizeOf) {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+template <class Derived>
+size_t RemoteAccessibleBase<Derived>::SizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) {
+ size_t size = 0;
+
+ // Count attributes.
+ if (mCachedFields) {
+ size += mCachedFields->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ // We don't recurse into mChildren because they're already counted in their
+ // document's mAccessibles.
+ size += mChildren.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return size;
+}
+
+template class RemoteAccessibleBase<RemoteAccessible>;
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/RemoteAccessibleBase.h b/accessible/ipc/RemoteAccessibleBase.h
new file mode 100644
index 0000000000..57407297b5
--- /dev/null
+++ b/accessible/ipc/RemoteAccessibleBase.h
@@ -0,0 +1,482 @@
+/* -*- 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_RemoteAccessibleBase_h
+#define mozilla_a11y_RemoteAccessibleBase_h
+
+#include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/CacheConstants.h"
+#include "mozilla/a11y/HyperTextAccessibleBase.h"
+#include "mozilla/a11y/Role.h"
+#include "AccAttributes.h"
+#include "nsIAccessibleText.h"
+#include "nsIAccessibleTypes.h"
+#include "nsTArray.h"
+#include "nsRect.h"
+#include "LocalAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+class Attribute;
+class DocAccessibleParent;
+class RemoteAccessible;
+enum class RelationType;
+
+/**
+ * The base type for an accessibility tree node that originated in the parent
+ * process.
+ */
+template <class Derived>
+class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
+ public:
+ virtual ~RemoteAccessibleBase() { MOZ_ASSERT(!mWrapper); }
+
+ virtual bool IsRemote() const override { return true; }
+
+ void AddChildAt(uint32_t aIdx, Derived* aChild) {
+ mChildren.InsertElementAt(aIdx, aChild);
+ if (IsHyperText()) {
+ InvalidateCachedHyperTextOffsets();
+ }
+ }
+
+ virtual uint32_t ChildCount() const override { return mChildren.Length(); }
+ Derived* RemoteChildAt(uint32_t aIdx) const {
+ return mChildren.SafeElementAt(aIdx);
+ }
+ Derived* RemoteFirstChild() const {
+ return mChildren.Length() ? mChildren[0] : nullptr;
+ }
+ Derived* RemoteLastChild() const {
+ return mChildren.Length() ? mChildren[mChildren.Length() - 1] : nullptr;
+ }
+ Derived* RemotePrevSibling() const {
+ if (IsDoc()) {
+ // The normal code path doesn't work for documents because the parent
+ // might be a local OuterDoc, but IndexInParent() will return 1.
+ // A document is always a single child of an OuterDoc anyway.
+ return nullptr;
+ }
+ int32_t idx = IndexInParent();
+ if (idx == -1) {
+ return nullptr; // No parent.
+ }
+ return idx > 0 ? RemoteParent()->mChildren[idx - 1] : nullptr;
+ }
+ Derived* RemoteNextSibling() const {
+ if (IsDoc()) {
+ // The normal code path doesn't work for documents because the parent
+ // might be a local OuterDoc, but IndexInParent() will return 1.
+ // A document is always a single child of an OuterDoc anyway.
+ return nullptr;
+ }
+ int32_t idx = IndexInParent();
+ if (idx == -1) {
+ return nullptr; // No parent.
+ }
+ MOZ_ASSERT(idx >= 0);
+ size_t newIdx = idx + 1;
+ return newIdx < RemoteParent()->mChildren.Length()
+ ? RemoteParent()->mChildren[newIdx]
+ : nullptr;
+ }
+
+ // Accessible hierarchy method overrides
+
+ virtual Accessible* Parent() const override { return RemoteParent(); }
+
+ virtual Accessible* ChildAt(uint32_t aIndex) const override {
+ return RemoteChildAt(aIndex);
+ }
+
+ virtual Accessible* NextSibling() const override {
+ return RemoteNextSibling();
+ }
+
+ virtual Accessible* PrevSibling() const override {
+ return RemotePrevSibling();
+ }
+
+ // XXX evaluate if this is fast enough.
+ virtual int32_t IndexInParent() const override {
+ Derived* parent = RemoteParent();
+ if (!parent) {
+ return -1;
+ }
+ return parent->mChildren.IndexOf(static_cast<const Derived*>(this));
+ }
+ virtual uint32_t EmbeddedChildCount() override;
+ virtual int32_t IndexOfEmbeddedChild(Accessible* aChild) override;
+ virtual Accessible* EmbeddedChildAt(uint32_t aChildIdx) override;
+
+ void Shutdown();
+
+ void SetChildDoc(DocAccessibleParent* aChildDoc);
+ void ClearChildDoc(DocAccessibleParent* aChildDoc);
+
+ /**
+ * Remove The given child.
+ */
+ void RemoveChild(Derived* aChild) {
+ mChildren.RemoveElement(aChild);
+ if (IsHyperText()) {
+ InvalidateCachedHyperTextOffsets();
+ }
+ }
+
+ /**
+ * Return the proxy for the parent of the wrapped accessible.
+ */
+ Derived* RemoteParent() const;
+
+ LocalAccessible* OuterDocOfRemoteBrowser() const;
+
+ /**
+ * Get the role of the accessible we're proxying.
+ */
+ virtual role Role() const override { return mRole; }
+
+ /**
+ * Return true if this is an embedded object.
+ */
+ bool IsEmbeddedObject() const { return !IsText(); }
+
+ virtual bool IsLink() const override {
+ if (IsHTMLLink()) {
+ // XXX: HTML links always return true for IsLink.
+ return true;
+ }
+
+ if (IsText()) {
+ return false;
+ }
+
+ if (Accessible* parent = Parent()) {
+ return parent->IsHyperText();
+ }
+
+ return false;
+ }
+
+ virtual bool HasNumericValue() const override {
+ // XXX: We combine the aria and native "has numeric value" field
+ // when we serialize the local accessible into eNumericValue.
+ return HasGenericType(eNumericValue);
+ }
+
+ // Methods that potentially access a cache.
+
+ virtual ENameValueFlag Name(nsString& aName) const override;
+ virtual void Description(nsString& aDescription) const override;
+ virtual void Value(nsString& aValue) const override;
+
+ virtual double CurValue() const override;
+ virtual double MinValue() const override;
+ virtual double MaxValue() const override;
+ virtual double Step() const override;
+
+ virtual Accessible* ChildAtPoint(
+ int32_t aX, int32_t aY,
+ LocalAccessible::EWhichChildAtPoint aWhichChild) override;
+
+ virtual LayoutDeviceIntRect Bounds() const override;
+
+ virtual nsRect BoundsInAppUnits() const override;
+
+ virtual Relation RelationByType(RelationType aType) const override;
+
+ virtual uint64_t State() override;
+
+ virtual already_AddRefed<AccAttributes> Attributes() override;
+
+ virtual nsAtom* TagName() const override;
+
+ virtual already_AddRefed<nsAtom> DisplayStyle() const override;
+
+ virtual float Opacity() const override;
+
+ virtual void LiveRegionAttributes(nsAString* aLive, nsAString* aRelevant,
+ Maybe<bool>* aAtomic,
+ nsAString* aBusy) const override;
+
+ virtual Maybe<bool> ARIASelected() const override;
+
+ virtual uint8_t ActionCount() const override;
+
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+
+ virtual bool DoAction(uint8_t aIndex) const override;
+
+ virtual KeyBinding AccessKey() const override;
+
+ virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override;
+
+ virtual Maybe<int32_t> GetIntARIAAttr(nsAtom* aAttrName) const override;
+
+ //////////////////////////////////////////////////////////////////////////////
+ // SelectAccessible
+
+ virtual void SelectedItems(nsTArray<Accessible*>* aItems) override;
+
+ virtual uint32_t SelectedItemCount() override;
+
+ virtual Accessible* GetSelectedItem(uint32_t aIndex) override;
+
+ virtual bool IsItemSelected(uint32_t aIndex) override;
+
+ virtual bool AddItemToSelection(uint32_t aIndex) override;
+
+ virtual bool RemoveItemFromSelection(uint32_t aIndex) override;
+
+ virtual bool SelectAll() override;
+
+ virtual bool UnselectAll() override;
+
+ virtual void TakeSelection() override;
+
+ virtual void SetSelected(bool aSelect) override;
+
+ // Methods that interact with content.
+
+ virtual void TakeFocus() const override;
+ virtual void ScrollTo(uint32_t aHow) const override;
+ virtual void SetCaretOffset(int32_t aOffset) override;
+
+ /**
+ * 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; }
+
+ virtual uint64_t ID() const override { return mID; }
+
+ /**
+ * Return the document containing this proxy, or the proxy itself if it is a
+ * document.
+ */
+ DocAccessibleParent* Document() const { return mDoc; }
+
+ DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
+
+ void ApplyCache(CacheUpdateType aUpdateType, AccAttributes* aFields) {
+ const nsTArray<bool> relUpdatesNeeded = PreProcessRelations(aFields);
+ if (auto maybeViewportCache =
+ aFields->GetAttribute<nsTArray<uint64_t>>(nsGkAtoms::viewport)) {
+ // Updating the viewport cache means the offscreen state of this
+ // document's accessibles has changed. Update the HashSet we use for
+ // checking offscreen state here.
+ MOZ_ASSERT(IsDoc(),
+ "Fetched the viewport cache from a non-doc accessible?");
+ AsDoc()->mOnScreenAccessibles.Clear();
+ for (auto id : *maybeViewportCache) {
+ AsDoc()->mOnScreenAccessibles.Insert(id);
+ }
+ }
+
+ if (aUpdateType == CacheUpdateType::Initial) {
+ mCachedFields = aFields;
+ } else {
+ if (!mCachedFields) {
+ // The fields cache can be uninitialized if there were no cache-worthy
+ // fields in the initial cache push.
+ // We don't do a simple assign because we don't want to store the
+ // DeleteEntry entries.
+ mCachedFields = new AccAttributes();
+ }
+ mCachedFields->Update(aFields);
+ }
+
+ if (IsTextLeaf()) {
+ Derived* parent = RemoteParent();
+ if (parent && parent->IsHyperText()) {
+ parent->InvalidateCachedHyperTextOffsets();
+ }
+ }
+
+ PostProcessRelations(relUpdatesNeeded);
+ }
+
+ void UpdateStateCache(uint64_t aState, bool aEnabled) {
+ if (aState & kRemoteCalculatedStates) {
+ return;
+ }
+ uint64_t state = 0;
+ if (mCachedFields) {
+ if (auto oldState =
+ mCachedFields->GetAttribute<uint64_t>(nsGkAtoms::state)) {
+ state = *oldState;
+ }
+ } else {
+ mCachedFields = new AccAttributes();
+ }
+ if (aEnabled) {
+ state |= aState;
+ } else {
+ state &= ~aState;
+ }
+ mCachedFields->SetAttribute(nsGkAtoms::state, state);
+ }
+
+ void InvalidateGroupInfo();
+
+ virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
+ uint32_t aLength = UINT32_MAX) override;
+
+ virtual bool TableIsProbablyForLayout();
+
+ /**
+ * Iterates through each atom in kRelationTypeAtoms, checking to see
+ * if it is present in aFields. If it is present (or if aFields contains
+ * a DeleteEntry() for this atom) and mCachedFields is initialized,
+ * fetches the old rel targets and removes their existing reverse relations
+ * stored in mReverseRelations.
+ * Returns an array of bools where the ith array entry corresponds
+ * to whether or not the rel at the ith entry of kRelationTypeAtoms
+ * requires a post-processing update.
+ */
+ nsTArray<bool> PreProcessRelations(AccAttributes* aFields);
+
+ /**
+ * Takes in the array returned from PreProcessRelations.
+ * For each entry requiring an update, fetches the new relation
+ * targets stored in mCachedFields and appropriately
+ * updates their reverse relations in mReverseRelations.
+ */
+ void PostProcessRelations(const nsTArray<bool>& aToUpdate);
+
+ /**
+ * This method is called during shutdown, before we clear our
+ * reverse rel map from the document's mReverseRelations cache.
+ * Here, we traverse our reverse relations, removing our ID from
+ * the corresponding forward relation's target list. This ensures
+ * the stored forward relations do not reference defunct accessibles.
+ */
+ void PruneRelationsOnShutdown();
+
+ uint32_t GetCachedTextLength();
+ Maybe<const nsTArray<int32_t>&> GetCachedTextLines();
+ Maybe<nsTArray<nsRect>> GetCachedCharData();
+ RefPtr<const AccAttributes> GetCachedTextAttributes();
+ RefPtr<const AccAttributes> GetCachedARIAAttributes() const;
+
+ nsString GetCachedHTMLNameAttribute() const;
+
+ virtual HyperTextAccessibleBase* AsHyperTextBase() override {
+ return IsHyperText() ? static_cast<HyperTextAccessibleBase*>(this)
+ : nullptr;
+ }
+
+ virtual TableAccessibleBase* AsTableBase() override;
+ virtual TableCellAccessibleBase* AsTableCellBase() override;
+
+ virtual void DOMNodeID(nsString& aID) const override;
+
+ // HyperTextAccessibleBase
+ virtual already_AddRefed<AccAttributes> DefaultTextAttributes() override;
+
+ /**
+ * Invalidate cached HyperText offsets. This should be called whenever a
+ * child is added or removed or the text of a text leaf child is changed.
+ * Although GetChildOffset can either fully or partially invalidate the
+ * offsets cache, calculating which offset to invalidate is not worthwhile
+ * because a client might not even query offsets. This is in contrast to
+ * LocalAccessible, where the offsets are always needed to fire text change
+ * events. For RemoteAccessible, it's cheaper overall to just rebuild the
+ * offsets cache when a client next needs it.
+ */
+ void InvalidateCachedHyperTextOffsets() {
+ if (mCachedFields) {
+ mCachedFields->Remove(nsGkAtoms::offset);
+ }
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf);
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf);
+
+ protected:
+ RemoteAccessibleBase(uint64_t aID, Derived* aParent,
+ DocAccessibleParent* aDoc, role aRole, AccType aType,
+ AccGenericType aGenericTypes, uint8_t aRoleMapEntryIndex)
+ : Accessible(aType, aGenericTypes, aRoleMapEntryIndex),
+ mParent(aParent->ID()),
+ mDoc(aDoc),
+ mWrapper(0),
+ mID(aID),
+ mCachedFields(nullptr),
+ mRole(aRole) {}
+
+ explicit RemoteAccessibleBase(DocAccessibleParent* aThisAsDoc)
+ : Accessible(),
+ mParent(kNoParent),
+ mDoc(aThisAsDoc),
+ mWrapper(0),
+ mID(0),
+ mCachedFields(nullptr),
+ mRole(roles::DOCUMENT) {
+ mGenericTypes = eDocument | eHyperText;
+ }
+
+ protected:
+ void SetParent(Derived* aParent);
+ Maybe<nsRect> RetrieveCachedBounds() const;
+ bool ApplyTransform(nsRect& aCumulativeBounds,
+ const nsRect& aParentRelativeBounds) const;
+ void ApplyScrollOffset(nsRect& aBounds) const;
+ void ApplyCrossDocOffset(nsRect& aBounds) const;
+ LayoutDeviceIntRect BoundsWithOffset(Maybe<nsRect> aOffset) const;
+
+ virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,
+ int32_t* aPosInSet) const override;
+
+ virtual AccGroupInfo* GetGroupInfo() const override;
+
+ virtual AccGroupInfo* GetOrCreateGroupInfo() override;
+
+ virtual void GetPositionAndSetSize(int32_t* aPosInSet,
+ int32_t* aSetSize) override;
+
+ virtual bool HasPrimaryAction() const override;
+
+ nsAtom* GetPrimaryAction() const;
+
+ virtual nsTArray<int32_t>& GetCachedHyperTextOffsets() override;
+
+ private:
+ uintptr_t mParent;
+ static const uintptr_t kNoParent = UINTPTR_MAX;
+
+ friend Derived;
+ friend DocAccessibleParent;
+ friend TextLeafPoint;
+ friend HyperTextAccessibleBase;
+ friend class xpcAccessible;
+ friend class CachedTableCellAccessible;
+#ifdef XP_WIN
+ friend class sdnAccessible;
+#endif
+
+ nsTArray<Derived*> mChildren;
+ DocAccessibleParent* mDoc;
+ uintptr_t mWrapper;
+ uint64_t mID;
+
+ protected:
+ virtual const Accessible* Acc() const override { return this; }
+
+ RefPtr<AccAttributes> mCachedFields;
+
+ // XXX DocAccessibleParent gets to change this to change the role of
+ // documents.
+ role mRole : 27;
+};
+
+extern template class RemoteAccessibleBase<RemoteAccessible>;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/RemoteAccessibleShared.h b/accessible/ipc/RemoteAccessibleShared.h
new file mode 100644
index 0000000000..f91b446b37
--- /dev/null
+++ b/accessible/ipc/RemoteAccessibleShared.h
@@ -0,0 +1,206 @@
+/* -*- 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_RemoteAccessibleShared_h
+#define mozilla_a11y_RemoteAccessibleShared_h
+
+/**
+ * These are function declarations shared between win/RemoteAccessible.h and
+ * other/RemoteAccessible.h.
+ */
+
+/*
+ * Return the states for the proxied accessible.
+ */
+virtual uint64_t State() override;
+
+/*
+ * Return the native states for the proxied accessible.
+ */
+uint64_t NativeState() const;
+
+/*
+ * Set aName to the name of the proxied accessible.
+ * Return the ENameValueFlag passed from Accessible::Name
+ */
+ENameValueFlag Name(nsString& aName) const override;
+
+/*
+ * Set aValue to the value of the proxied accessible.
+ */
+void Value(nsString& aValue) const override;
+
+/*
+ * 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 override;
+
+/**
+ * Get the set of attributes on the proxied accessible.
+ */
+virtual already_AddRefed<AccAttributes> Attributes() override;
+
+virtual Relation RelationByType(RelationType aType) const override;
+
+bool IsSearchbox() const;
+
+virtual mozilla::a11y::GroupPos GroupPosition() override;
+void ScrollToPoint(uint32_t aScrollType, int32_t aX, int32_t aY);
+
+void Announce(const nsString& aAnnouncement, uint16_t aPriority);
+
+int32_t CaretLineNumber();
+virtual int32_t CaretOffset() const override;
+
+virtual void TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
+ nsAString& aText) const override;
+
+virtual void TextAfterOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset, int32_t* aEndOffset,
+ nsAString& aText) override;
+
+virtual void TextAtOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset, int32_t* aEndOffset,
+ nsAString& aText) override;
+
+virtual void TextBeforeOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset, int32_t* aEndOffset,
+ nsAString& aText) override;
+
+char16_t CharAt(int32_t aOffset);
+
+virtual int32_t OffsetAtPoint(int32_t aX, int32_t aY,
+ uint32_t aCoordType) override;
+
+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);
+
+LayoutDeviceIntPoint ImagePosition(uint32_t aCoordType);
+
+LayoutDeviceIntSize ImageSize();
+
+bool IsLinkValid();
+
+uint32_t AnchorCount(bool* aOk);
+
+void AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk);
+
+RemoteAccessible* AnchorAt(uint32_t aIndex);
+
+uint32_t LinkCount();
+
+RemoteAccessible* LinkAt(const uint32_t& aIndex);
+
+RemoteAccessible* TableOfACell();
+
+uint32_t ColIdx();
+
+uint32_t RowIdx();
+
+void GetPosition(uint32_t* aRowIdx, uint32_t* aColIdx);
+
+uint32_t ColExtent();
+
+uint32_t RowExtent();
+
+void GetColRowExtents(uint32_t* aColIdx, uint32_t* aRowIdx,
+ uint32_t* aColExtent, uint32_t* aRowExtent);
+
+void ColHeaderCells(nsTArray<RemoteAccessible*>* aCells);
+
+void RowHeaderCells(nsTArray<RemoteAccessible*>* aCells);
+
+bool IsCellSelected();
+
+RemoteAccessible* TableCaption();
+void TableSummary(nsString& aSummary);
+uint32_t TableColumnCount();
+uint32_t TableRowCount();
+RemoteAccessible* 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<RemoteAccessible*>* 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);
+RemoteAccessible* AtkTableColumnHeader(int32_t aCol);
+RemoteAccessible* AtkTableRowHeader(int32_t aRow);
+
+void AtkKeyBinding(nsString& aBinding);
+
+double CurValue() const override;
+double MinValue() const override;
+double MaxValue() const override;
+double Step() const override;
+bool SetCurValue(double aValue);
+
+Accessible* ChildAtPoint(
+ int32_t aX, int32_t aY,
+ LocalAccessible::EWhichChildAtPoint aWhichChild) override;
+LayoutDeviceIntRect Bounds() const override;
+virtual nsIntRect BoundsInCSSPixels() const override;
+
+void Language(nsString& aLocale);
+void DocType(nsString& aType);
+void Title(nsString& aTitle);
+void MimeType(nsString aMime);
+void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+ nsString& aMimeType);
+
+void Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight);
+
+virtual void DOMNodeID(nsString& aID) const override;
+
+#endif
diff --git a/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp b/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp
new file mode 100644
index 0000000000..ad29ae900c
--- /dev/null
+++ b/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp
@@ -0,0 +1,75 @@
+/* -*- 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 "DocAccessiblePlatformExtChild.h"
+
+#include "DocAccessibleChild.h"
+#include "AccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvPivotTo(
+ uint64_t aID, int32_t aGranularity, bool aForward, bool aInclusive) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->PivotTo(aGranularity, aForward, aInclusive);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvNavigateText(
+ uint64_t aID, int32_t aGranularity, int32_t aStartOffset,
+ int32_t aEndOffset, bool aForward, bool aSelect) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->NavigateText(aGranularity, aStartOffset, aEndOffset, aForward,
+ aSelect);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvSetSelection(
+ uint64_t aID, int32_t aStart, int32_t aEnd) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->SetSelection(aStart, aEnd);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvCut(uint64_t aID) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->Cut();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvCopy(uint64_t aID) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->Copy();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvPaste(uint64_t aID) {
+ if (auto acc = IdToAccessibleWrap(aID)) {
+ acc->Paste();
+ }
+
+ return IPC_OK();
+}
+
+AccessibleWrap* DocAccessiblePlatformExtChild::IdToAccessibleWrap(
+ const uint64_t& aID) const {
+ return static_cast<AccessibleWrap*>(
+ static_cast<DocAccessibleChild*>(Manager())->IdToAccessible(aID));
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.h b/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.h
new file mode 100644
index 0000000000..60108213e1
--- /dev/null
+++ b/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.h
@@ -0,0 +1,44 @@
+/* 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_DocAccessiblePlatformExtChild_h
+#define mozilla_a11y_DocAccessiblePlatformExtChild_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtChild.h"
+
+namespace mozilla {
+namespace a11y {
+
+class AccessibleWrap;
+class DocAccessibleChild;
+
+class DocAccessiblePlatformExtChild : public PDocAccessiblePlatformExtChild {
+ public:
+ mozilla::ipc::IPCResult RecvPivotTo(uint64_t aID, int32_t aGranularity,
+ bool aForward, bool aInclusive);
+
+ mozilla::ipc::IPCResult RecvNavigateText(uint64_t aID, int32_t aGranularity,
+ int32_t aStartOffset,
+ int32_t aEndOffset, bool aForward,
+ bool aSelect);
+
+ mozilla::ipc::IPCResult RecvSetSelection(uint64_t aID, int32_t aStart,
+ int32_t aEnd);
+
+ mozilla::ipc::IPCResult RecvCut(uint64_t aID);
+
+ mozilla::ipc::IPCResult RecvCopy(uint64_t aID);
+
+ mozilla::ipc::IPCResult RecvPaste(uint64_t aID);
+
+ mozilla::ipc::IPCResult RecvExploreByTouch(uint64_t aID, float aX, float aY);
+
+ private:
+ AccessibleWrap* IdToAccessibleWrap(const uint64_t& aID) const;
+};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.cpp b/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.cpp
new file mode 100644
index 0000000000..dbc16b7401
--- /dev/null
+++ b/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.cpp
@@ -0,0 +1,45 @@
+/* -*- 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 "DocAccessiblePlatformExtParent.h"
+
+#include "AccessibleWrap.h"
+#include "SessionAccessibility.h"
+
+#include "mozilla/a11y/DocAccessibleParent.h"
+
+namespace mozilla {
+namespace a11y {
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtParent::RecvSetPivotBoundaries(
+ PDocAccessibleParent* aFirstDoc, uint64_t aFirst,
+ PDocAccessibleParent* aLastDoc, uint64_t aLast) {
+ MOZ_ASSERT(aFirstDoc);
+ MOZ_ASSERT(aLastDoc);
+
+ RefPtr<SessionAccessibility> sessionAcc =
+ SessionAccessibility::GetInstanceFor(
+ static_cast<DocAccessibleParent*>(Manager()));
+ if (!sessionAcc) {
+ return IPC_OK();
+ }
+
+ RemoteAccessible* first =
+ static_cast<DocAccessibleParent*>(aFirstDoc)->GetAccessible(aFirst);
+ RemoteAccessible* last =
+ static_cast<DocAccessibleParent*>(aLastDoc)->GetAccessible(aLast);
+
+ // We may not have proxy accessibles available yet for those accessibles
+ // in the parent process.
+ if (first && last) {
+ sessionAcc->UpdateAccessibleFocusBoundaries(first, last);
+ }
+
+ return IPC_OK();
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.h b/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.h
new file mode 100644
index 0000000000..b9278599bb
--- /dev/null
+++ b/accessible/ipc/extension/android/DocAccessiblePlatformExtParent.h
@@ -0,0 +1,22 @@
+/* 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_DocAccessiblePlatformExtParent_h
+#define mozilla_a11y_DocAccessiblePlatformExtParent_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtParent.h"
+
+namespace mozilla {
+namespace a11y {
+class DocAccessiblePlatformExtParent : public PDocAccessiblePlatformExtParent {
+ public:
+ mozilla::ipc::IPCResult RecvSetPivotBoundaries(
+ PDocAccessibleParent* aFirstDoc, uint64_t aFirst,
+ PDocAccessibleParent* aLastDoc, uint64_t aLast);
+};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/android/PDocAccessiblePlatformExt.ipdl b/accessible/ipc/extension/android/PDocAccessiblePlatformExt.ipdl
new file mode 100644
index 0000000000..92b3d5f259
--- /dev/null
+++ b/accessible/ipc/extension/android/PDocAccessiblePlatformExt.ipdl
@@ -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/. */
+
+include protocol PDocAccessible;
+
+namespace mozilla {
+namespace a11y {
+
+[ManualDealloc]
+protocol PDocAccessiblePlatformExt {
+ manager PDocAccessible;
+
+child:
+ async __delete__();
+
+ async PivotTo(uint64_t aID, int32_t aGranularity, bool aForward, bool aInclusive);
+
+ async NavigateText(uint64_t aID, int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset, bool aForward, bool aSelect);
+
+ async SetSelection(uint64_t aID, int32_t aStart, int32_t aEnd);
+
+ async Cut(uint64_t aID);
+
+ async Copy(uint64_t aID);
+
+ async Paste(uint64_t aID);
+
+parent:
+ async SetPivotBoundaries(PDocAccessible aFirstDoc, uint64_t aFirst, PDocAccessible aLastDoc, uint64_t aLast);
+ };
+}
+}
diff --git a/accessible/ipc/extension/android/moz.build b/accessible/ipc/extension/android/moz.build
new file mode 100644
index 0000000000..50871e326b
--- /dev/null
+++ b/accessible/ipc/extension/android/moz.build
@@ -0,0 +1,29 @@
+# -*- 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/.
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += ["PDocAccessiblePlatformExt.ipdl"]
+
+ EXPORTS.mozilla.a11y += [
+ "DocAccessiblePlatformExtChild.h",
+ "DocAccessiblePlatformExtParent.h",
+ ]
+
+ SOURCES += [
+ "DocAccessiblePlatformExtChild.cpp",
+ "DocAccessiblePlatformExtParent.cpp",
+ ]
+
+ LOCAL_INCLUDES += [
+ "/accessible/android",
+ "/accessible/generic",
+ "/accessible/ipc/other",
+ "/widget/android",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.cpp b/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.cpp
new file mode 100644
index 0000000000..c435758240
--- /dev/null
+++ b/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 "DocAccessiblePlatformExtChild.h"
+
+#include "AccAttributes.h"
+#include "DocAccessibleChild.h"
+#include "HyperTextAccessibleWrap.h"
+#include "nsAccUtils.h"
+
+#define UNIQUE_ID(acc) \
+ !acc || acc->Document() == acc ? 0 \
+ : reinterpret_cast<uint64_t>(acc->UniqueID())
+
+namespace mozilla {
+namespace a11y {
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvRangeAt(
+ const uint64_t& aID, const int32_t& aOffset, const EWhichRange& aRangeType,
+ uint64_t* aStartContainer, int32_t* aStartOffset, uint64_t* aEndContainer,
+ int32_t* aEndOffset) {
+ *aStartContainer = 0;
+ *aStartOffset = 0;
+ *aEndContainer = 0;
+ *aEndOffset = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ HyperTextAccessible* startContainer = nullptr;
+ HyperTextAccessible* endContainer = nullptr;
+
+ acc->RangeAt(aOffset, aRangeType, &startContainer, aStartOffset,
+ &endContainer, aEndOffset);
+
+ MOZ_ASSERT(!startContainer || startContainer->Document() == acc->Document());
+ MOZ_ASSERT(!endContainer || endContainer->Document() == acc->Document());
+
+ *aStartContainer = UNIQUE_ID(startContainer);
+ *aEndContainer = UNIQUE_ID(endContainer);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvNextClusterAt(
+ const uint64_t& aID, const int32_t& aOffset, uint64_t* aNextContainer,
+ int32_t* aNextOffset) {
+ *aNextContainer = 0;
+ *aNextOffset = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ HyperTextAccessible* nextContainer = nullptr;
+
+ acc->NextClusterAt(aOffset, &nextContainer, aNextOffset);
+
+ MOZ_ASSERT(!nextContainer || nextContainer->Document() == acc->Document());
+
+ *aNextContainer = UNIQUE_ID(nextContainer);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvPreviousClusterAt(
+ const uint64_t& aID, const int32_t& aOffset, uint64_t* aPrevContainer,
+ int32_t* aPrevOffset) {
+ *aPrevContainer = 0;
+ *aPrevOffset = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ HyperTextAccessible* prevContainer = nullptr;
+
+ acc->PreviousClusterAt(aOffset, &prevContainer, aPrevOffset);
+
+ MOZ_ASSERT(!prevContainer || prevContainer->Document() == acc->Document());
+
+ *aPrevContainer = UNIQUE_ID(prevContainer);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvTextForRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset, nsString* aText) {
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ HyperTextAccessibleWrap* endContainer =
+ IdToHyperTextAccessibleWrap(aEndContainer);
+ if (!acc || !endContainer) {
+ return IPC_OK();
+ }
+
+ acc->TextForRange(*aText, aStartOffset, endContainer, aEndOffset);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvBoundsForRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset,
+ LayoutDeviceIntRect* aBounds) {
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ HyperTextAccessibleWrap* endContainer =
+ IdToHyperTextAccessibleWrap(aEndContainer);
+ if (!acc || !endContainer) {
+ *aBounds = LayoutDeviceIntRect();
+ return IPC_OK();
+ }
+
+ *aBounds = acc->BoundsForRange(aStartOffset, endContainer, aEndOffset);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvLengthForRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset,
+ int32_t* aLength) {
+ *aLength = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ HyperTextAccessibleWrap* endContainer =
+ IdToHyperTextAccessibleWrap(aEndContainer);
+ if (!acc || !endContainer) {
+ return IPC_OK();
+ }
+
+ *aLength = acc->LengthForRange(aStartOffset, endContainer, aEndOffset);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvOffsetAtIndex(
+ const uint64_t& aID, const int32_t& aIndex, uint64_t* aContainer,
+ int32_t* aOffset) {
+ *aContainer = 0;
+ *aOffset = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ HyperTextAccessible* container = nullptr;
+
+ acc->OffsetAtIndex(aIndex, &container, aOffset);
+
+ MOZ_ASSERT(!container || container->Document() == acc->Document());
+
+ *aContainer = UNIQUE_ID(container);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvRangeOfChild(
+ const uint64_t& aID, const uint64_t& aChild, int32_t* aStartOffset,
+ int32_t* aEndOffset) {
+ *aStartOffset = 0;
+ *aEndOffset = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ LocalAccessible* child =
+ static_cast<DocAccessibleChild*>(Manager())->IdToAccessible(aChild);
+ if (!acc || !child) {
+ return IPC_OK();
+ }
+
+ acc->RangeOfChild(child, aStartOffset, aEndOffset);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvLeafAtOffset(
+ const uint64_t& aID, const int32_t& aOffset, uint64_t* aLeaf) {
+ *aLeaf = 0;
+
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ LocalAccessible* leaf = acc->LeafAtOffset(aOffset);
+
+ MOZ_ASSERT(!leaf || leaf->Document() == acc->Document());
+
+ *aLeaf = UNIQUE_ID(leaf);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+DocAccessiblePlatformExtChild::RecvAttributedTextForRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset,
+ nsTArray<TextAttributesRun>* aAttributes) {
+ HyperTextAccessibleWrap* acc = IdToHyperTextAccessibleWrap(aID);
+ HyperTextAccessibleWrap* endContainer =
+ IdToHyperTextAccessibleWrap(aEndContainer);
+ if (!acc || !endContainer) {
+ return IPC_OK();
+ }
+
+ nsTArray<nsString> texts;
+ nsTArray<LocalAccessible*> containers;
+ nsTArray<RefPtr<AccAttributes>> props;
+
+ acc->AttributedTextForRange(texts, props, containers, aStartOffset,
+ endContainer, aEndOffset);
+
+ MOZ_ASSERT(texts.Length() == props.Length() &&
+ texts.Length() == containers.Length());
+
+ for (size_t i = 0; i < texts.Length(); i++) {
+ aAttributes->AppendElement(TextAttributesRun(
+ texts.ElementAt(i), UNIQUE_ID(containers.ElementAt(i)),
+ props.ElementAt(i)));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvSelectRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset) {
+ RefPtr<HyperTextAccessibleWrap> acc = IdToHyperTextAccessibleWrap(aID);
+ RefPtr<HyperTextAccessibleWrap> endContainer =
+ IdToHyperTextAccessibleWrap(aEndContainer);
+ if (!acc || !endContainer) {
+ return IPC_OK();
+ }
+
+ acc->SelectRange(aStartOffset, endContainer, aEndOffset);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+DocAccessiblePlatformExtChild::RecvApplyPostSearchFilter(
+ const nsTArray<uint64_t>& aAccessibles, const int32_t& aLimit,
+ const EWhichPostFilter& aSearchKey, const nsString& aSearchText,
+ nsTArray<uint64_t>* aMatches) {
+ if (aSearchKey != EWhichPostFilter::eContainsText) {
+ return IPC_OK();
+ }
+
+ DocAccessibleChild* ipcDoc = static_cast<DocAccessibleChild*>(Manager());
+ for (size_t i = 0; i < aAccessibles.Length(); i++) {
+ AccessibleWrap* acc = static_cast<AccessibleWrap*>(
+ ipcDoc->IdToAccessible(aAccessibles.ElementAt(i)));
+ if (!acc) {
+ continue;
+ }
+
+ if (acc->ApplyPostFilter(aSearchKey, aSearchText)) {
+ aMatches->AppendElement(UNIQUE_ID(acc));
+ }
+ }
+
+ return IPC_OK();
+}
+
+HyperTextAccessibleWrap*
+DocAccessiblePlatformExtChild::IdToHyperTextAccessibleWrap(
+ const uint64_t& aID) const {
+ return static_cast<HyperTextAccessibleWrap*>(
+ static_cast<DocAccessibleChild*>(Manager())->IdToHyperTextAccessible(
+ aID));
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.h b/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.h
new file mode 100644
index 0000000000..c6f2580ccc
--- /dev/null
+++ b/accessible/ipc/extension/mac/DocAccessiblePlatformExtChild.h
@@ -0,0 +1,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_DocAccessiblePlatformExtChild_h
+#define mozilla_a11y_DocAccessiblePlatformExtChild_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtChild.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HyperTextAccessibleWrap;
+class DocAccessibleChild;
+
+class DocAccessiblePlatformExtChild : public PDocAccessiblePlatformExtChild {
+ public:
+ mozilla::ipc::IPCResult RecvRangeAt(
+ const uint64_t& aID, const int32_t& aOffset,
+ const EWhichRange& aRangeType, uint64_t* aStartContainer,
+ int32_t* aStartOffset, uint64_t* aEndContainer, int32_t* aEndOffset);
+
+ mozilla::ipc::IPCResult RecvNextClusterAt(const uint64_t& aID,
+ const int32_t& aOffset,
+ uint64_t* aNextContainer,
+ int32_t* aNextOffset);
+
+ mozilla::ipc::IPCResult RecvPreviousClusterAt(const uint64_t& aID,
+ const int32_t& aOffset,
+ uint64_t* aPrevContainer,
+ int32_t* aPrevOffset);
+
+ mozilla::ipc::IPCResult RecvTextForRange(const uint64_t& aID,
+ const int32_t& aStartOffset,
+ const uint64_t& aEndContainer,
+ const int32_t& aEndOffset,
+ nsString* aText);
+
+ mozilla::ipc::IPCResult RecvBoundsForRange(const uint64_t& aID,
+ const int32_t& aStartOffset,
+ const uint64_t& aEndContainer,
+ const int32_t& aEndOffset,
+ LayoutDeviceIntRect* aBounds);
+
+ mozilla::ipc::IPCResult RecvLengthForRange(const uint64_t& aID,
+ const int32_t& aStartOffset,
+ const uint64_t& aEndContainer,
+ const int32_t& aEndOffset,
+ int32_t* aLength);
+
+ mozilla::ipc::IPCResult RecvOffsetAtIndex(const uint64_t& aID,
+ const int32_t& aIndex,
+ uint64_t* aContainer,
+ int32_t* aOffset);
+
+ mozilla::ipc::IPCResult RecvRangeOfChild(const uint64_t& aID,
+ const uint64_t& aChild,
+ int32_t* aStartOffset,
+ int32_t* aEndOffset);
+
+ mozilla::ipc::IPCResult RecvLeafAtOffset(const uint64_t& aID,
+ const int32_t& aOffset,
+ uint64_t* aLeaf);
+
+ mozilla::ipc::IPCResult RecvAttributedTextForRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset,
+ nsTArray<TextAttributesRun>* aAttributes);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvSelectRange(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const uint64_t& aEndContainer, const int32_t& aEndOffset);
+
+ mozilla::ipc::IPCResult RecvApplyPostSearchFilter(
+ const nsTArray<uint64_t>& aAccessibles, const int32_t& aLimit,
+ const EWhichPostFilter& aSearchKey, const nsString& aSearchText,
+ nsTArray<uint64_t>* aMatches);
+
+ private:
+ HyperTextAccessibleWrap* IdToHyperTextAccessibleWrap(
+ const uint64_t& aID) const;
+};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/mac/DocAccessiblePlatformExtParent.h b/accessible/ipc/extension/mac/DocAccessiblePlatformExtParent.h
new file mode 100644
index 0000000000..07e65c6e2d
--- /dev/null
+++ b/accessible/ipc/extension/mac/DocAccessiblePlatformExtParent.h
@@ -0,0 +1,19 @@
+/* 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_DocAccessiblePlatformExtParent_h
+#define mozilla_a11y_DocAccessiblePlatformExtParent_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtParent.h"
+
+namespace mozilla {
+namespace a11y {
+class DocAccessiblePlatformExtParent : public PDocAccessiblePlatformExtParent {
+
+};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl b/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl
new file mode 100644
index 0000000000..82f9d18b47
--- /dev/null
+++ b/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl
@@ -0,0 +1,73 @@
+/* -*- 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 PDocAccessible;
+
+include "mozilla/GfxMessageUtils.h";
+
+using mozilla::a11y::EWhichRange from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::EWhichPostFilter from "mozilla/a11y/IPCTypes.h";
+[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+
+namespace mozilla {
+namespace a11y {
+
+struct TextAttributesRun {
+ nsString Text;
+ uint64_t ContainerID;
+ AccAttributes TextAttributes;
+};
+
+
+[ManualDealloc, NestedUpTo=inside_sync] sync protocol PDocAccessiblePlatformExt {
+ manager PDocAccessible;
+
+child:
+ async __delete__();
+
+ [Nested=inside_sync] sync RangeAt(uint64_t aID, int32_t aOffset, EWhichRange aRangeType)
+ returns(uint64_t aStartContainer, int32_t aStartOffset,
+ uint64_t aEndContainer, int32_t aEndOffset);
+
+ [Nested=inside_sync] sync NextClusterAt(uint64_t aID, int32_t aOffset)
+ returns(uint64_t aNextContainer, int32_t aNextOffset);
+
+ [Nested=inside_sync] sync PreviousClusterAt(uint64_t aID, int32_t aOffset)
+ returns(uint64_t aNextContainer, int32_t aNextOffset);
+
+ [Nested=inside_sync] sync TextForRange(uint64_t aID, int32_t aStartOffset, uint64_t aEndContainer, int32_t aEndOffset)
+ returns(nsString aText);
+
+ [Nested=inside_sync] sync BoundsForRange(uint64_t aID, int32_t aStartOffset, uint64_t aEndContainer, int32_t aEndOffset)
+ returns(LayoutDeviceIntRect aRetVal);
+
+ [Nested=inside_sync] sync LengthForRange(uint64_t aID, int32_t aStartOffset, uint64_t aEndContainer, int32_t aEndOffset)
+ returns(int32_t aLength);
+
+ [Nested=inside_sync] sync OffsetAtIndex(uint64_t aID, int32_t aIndex)
+ returns(uint64_t aContainer, int32_t aOffset);
+
+ [Nested=inside_sync] sync RangeOfChild(uint64_t aID, uint64_t aChild)
+ returns(int32_t aStartOffset, int32_t aEndOffset);
+
+ [Nested=inside_sync] sync LeafAtOffset(uint64_t aID, int32_t aOffset)
+ returns(uint64_t aLeaf);
+
+ [Nested=inside_sync] sync AttributedTextForRange(uint64_t aID, int32_t aStartOffset, uint64_t aEndContainer, int32_t aEndOffset)
+ returns(TextAttributesRun[] aAttributedText);
+
+ async SelectRange(uint64_t aID, int32_t aStartOffset, uint64_t aEndContainer, int32_t aEndOffset);
+
+ // A filter that can be applied to search predicate results.
+ [Nested=inside_sync] sync ApplyPostSearchFilter(uint64_t[] aAccessibles, int32_t aLimit,
+ EWhichPostFilter aSearchKey, nsString aSearchText)
+ returns(uint64_t[] aMatches);
+
+};
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/extension/mac/moz.build b/accessible/ipc/extension/mac/moz.build
new file mode 100644
index 0000000000..e7a7bf3bf8
--- /dev/null
+++ b/accessible/ipc/extension/mac/moz.build
@@ -0,0 +1,28 @@
+# -*- 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/.
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += ["PDocAccessiblePlatformExt.ipdl"]
+
+ EXPORTS.mozilla.a11y += [
+ "DocAccessiblePlatformExtChild.h",
+ "DocAccessiblePlatformExtParent.h",
+ ]
+
+ SOURCES += [
+ "DocAccessiblePlatformExtChild.cpp",
+ ]
+
+ LOCAL_INCLUDES += [
+ "/accessible/base",
+ "/accessible/generic",
+ "/accessible/ipc/other",
+ "/accessible/mac",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/accessible/ipc/extension/moz.build b/accessible/ipc/extension/moz.build
new file mode 100644
index 0000000000..3c82fd90b4
--- /dev/null
+++ b/accessible/ipc/extension/moz.build
@@ -0,0 +1,14 @@
+# -*- 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/.
+
+toolkit = CONFIG["MOZ_WIDGET_TOOLKIT"]
+
+if toolkit == "android":
+ DIRS += ["android"]
+elif toolkit == "cocoa":
+ DIRS += ["mac"]
+else:
+ DIRS += ["other"]
diff --git a/accessible/ipc/extension/other/DocAccessiblePlatformExtChild.h b/accessible/ipc/extension/other/DocAccessiblePlatformExtChild.h
new file mode 100644
index 0000000000..6a6542bf71
--- /dev/null
+++ b/accessible/ipc/extension/other/DocAccessiblePlatformExtChild.h
@@ -0,0 +1,20 @@
+/* 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_DocAccessiblePlatformExtChild_h
+#define mozilla_a11y_DocAccessiblePlatformExtChild_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtChild.h"
+
+namespace mozilla {
+namespace a11y {
+
+class DocAccessibleChild;
+
+class DocAccessiblePlatformExtChild : public PDocAccessiblePlatformExtChild {};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/other/DocAccessiblePlatformExtParent.h b/accessible/ipc/extension/other/DocAccessiblePlatformExtParent.h
new file mode 100644
index 0000000000..e2e1cd907f
--- /dev/null
+++ b/accessible/ipc/extension/other/DocAccessiblePlatformExtParent.h
@@ -0,0 +1,18 @@
+/* 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_DocAccessiblePlatformExtParent_h
+#define mozilla_a11y_DocAccessiblePlatformExtParent_h
+
+#include "mozilla/a11y/PDocAccessiblePlatformExtParent.h"
+
+namespace mozilla {
+namespace a11y {
+class DocAccessiblePlatformExtParent : public PDocAccessiblePlatformExtParent {
+};
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/extension/other/PDocAccessiblePlatformExt.ipdl b/accessible/ipc/extension/other/PDocAccessiblePlatformExt.ipdl
new file mode 100644
index 0000000000..738505a743
--- /dev/null
+++ b/accessible/ipc/extension/other/PDocAccessiblePlatformExt.ipdl
@@ -0,0 +1,20 @@
+/* -*- 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 PDocAccessible;
+
+namespace mozilla {
+namespace a11y {
+
+[ManualDealloc]
+protocol PDocAccessiblePlatformExt {
+ manager PDocAccessible;
+
+child:
+ async __delete__();
+};
+
+}} \ No newline at end of file
diff --git a/accessible/ipc/extension/other/moz.build b/accessible/ipc/extension/other/moz.build
new file mode 100644
index 0000000000..dbbc98702c
--- /dev/null
+++ b/accessible/ipc/extension/other/moz.build
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += ["PDocAccessiblePlatformExt.ipdl"]
+
+ EXPORTS.mozilla.a11y += [
+ "DocAccessiblePlatformExtChild.h",
+ "DocAccessiblePlatformExtParent.h",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build
new file mode 100644
index 0000000000..24c5bfadc0
--- /dev/null
+++ b/accessible/ipc/moz.build
@@ -0,0 +1,70 @@
+# -*- 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/.
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ DIRS += ["win"]
+ LOCAL_INCLUDES += [
+ "/accessible/ipc/win",
+ "/accessible/windows/ia2",
+ "/accessible/windows/msaa",
+ ]
+else:
+ DIRS += ["other", "extension"]
+ LOCAL_INCLUDES += [
+ "/accessible/ipc/other",
+ ]
+ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ LOCAL_INCLUDES += [
+ "/accessible/atk",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ LOCAL_INCLUDES += [
+ "/accessible/mac",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ LOCAL_INCLUDES += [
+ "/accessible/android",
+ ]
+ else:
+ LOCAL_INCLUDES += [
+ "/accessible/other",
+ ]
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += [
+ "DocAccessibleTypes.ipdlh",
+ ]
+
+EXPORTS.mozilla.a11y += [
+ "IPCTypes.h",
+]
+
+if CONFIG["ACCESSIBILITY"]:
+ EXPORTS.mozilla.a11y += [
+ "DocAccessibleChildBase.h",
+ "DocAccessibleParent.h",
+ "RemoteAccessibleBase.h",
+ "RemoteAccessibleShared.h",
+ ]
+
+ UNIFIED_SOURCES += [
+ "DocAccessibleChildBase.cpp",
+ "DocAccessibleParent.cpp",
+ "RemoteAccessibleBase.cpp",
+ ]
+
+ LOCAL_INCLUDES += [
+ "/accessible/base",
+ "/accessible/generic",
+ "/accessible/xpcom",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
diff --git a/accessible/ipc/other/DocAccessibleChild.cpp b/accessible/ipc/other/DocAccessibleChild.cpp
new file mode 100644
index 0000000000..6a0987d07b
--- /dev/null
+++ b/accessible/ipc/other/DocAccessibleChild.cpp
@@ -0,0 +1,1590 @@
+/* -*- 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 "AccAttributes.h"
+#include "nsAccessibilityService.h"
+#include "LocalAccessible-inl.h"
+#include "RemoteAccessible.h"
+#include "Relation.h"
+#include "HyperTextAccessible-inl.h"
+#include "TextLeafAccessible.h"
+#include "ImageAccessible.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
+#include "nsAccUtils.h"
+#ifdef MOZ_ACCESSIBILITY_ATK
+# include "AccessibleWrap.h"
+#endif
+#include "AccAttributes.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/a11y/DocAccessiblePlatformExtChild.h"
+
+namespace mozilla {
+namespace a11y {
+
+LocalAccessible* DocAccessibleChild::IdToAccessibleLink(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return acc && acc->IsLink() ? acc : nullptr;
+}
+
+LocalAccessible* DocAccessibleChild::IdToAccessibleSelect(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return acc && acc->IsSelect() ? acc : nullptr;
+}
+
+TextLeafAccessible* DocAccessibleChild::IdToTextLeafAccessible(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return acc && acc->IsTextLeaf() ? acc->AsTextLeaf() : nullptr;
+}
+
+ImageAccessible* DocAccessibleChild::IdToImageAccessible(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return (acc && acc->IsImage()) ? acc->AsImage() : nullptr;
+}
+
+TableCellAccessible* DocAccessibleChild::IdToTableCellAccessible(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return (acc && acc->IsTableCell()) ? acc->AsTableCell() : nullptr;
+}
+
+TableAccessible* DocAccessibleChild::IdToTableAccessible(
+ const uint64_t& aID) const {
+ LocalAccessible* acc = IdToAccessible(aID);
+ return (acc && acc->IsTable()) ? acc->AsTable() : nullptr;
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvState(const uint64_t& aID,
+ uint64_t* aState) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ *aState = states::DEFUNCT;
+ return IPC_OK();
+ }
+
+ *aState = acc->State();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvNativeState(const uint64_t& aID,
+ uint64_t* aState) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ *aState = states::DEFUNCT;
+ return IPC_OK();
+ }
+
+ *aState = acc->NativeState();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvName(const uint64_t& aID,
+ nsString* aName,
+ uint32_t* aFlag) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+ *aFlag = acc->Name(*aName);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvValue(const uint64_t& aID,
+ nsString* aValue) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ acc->Value(*aValue);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvHelp(const uint64_t& aID,
+ nsString* aHelp) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ acc->Help(*aHelp);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDescription(const uint64_t& aID,
+ nsString* aDesc) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+ acc->Description(*aDesc);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAttributes(
+ const uint64_t& aID, RefPtr<AccAttributes>* aAttributes) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+ *aAttributes = acc->Attributes();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRelationByType(
+ const uint64_t& aID, const uint32_t& aType, nsTArray<uint64_t>* aTargets) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+ auto type = static_cast<RelationType>(aType);
+ Relation rel = acc->RelationByType(type);
+ while (LocalAccessible* target = rel.LocalNext()) {
+ aTargets->AppendElement(reinterpret_cast<uint64_t>(target->UniqueID()));
+ }
+
+ return IPC_OK();
+}
+
+static void AddRelation(LocalAccessible* aAcc, RelationType aType,
+ nsTArray<RelationTargets>* aTargets) {
+ Relation rel = aAcc->RelationByType(aType);
+ nsTArray<uint64_t> targets;
+ while (LocalAccessible* target = rel.LocalNext()) {
+ targets.AppendElement(reinterpret_cast<uint64_t>(target->UniqueID()));
+ }
+
+ if (!targets.IsEmpty()) {
+ RelationTargets* newRelation = aTargets->AppendElement(
+ RelationTargets(static_cast<uint32_t>(aType), nsTArray<uint64_t>()));
+ newRelation->Targets() = std::move(targets);
+ }
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRelations(
+ const uint64_t& aID, nsTArray<RelationTargets>* aRelations) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+#define RELATIONTYPE(gecko, s, a, m, i) \
+ AddRelation(acc, RelationType::gecko, aRelations);
+
+#include "RelationTypeMap.h"
+#undef RELATIONTYPE
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvIsSearchbox(const uint64_t& aID,
+ bool* aRetVal) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) return IPC_OK();
+
+ *aRetVal = acc->IsSearchbox();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvLandmarkRole(
+ const uint64_t& aID, nsString* aLandmark) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ if (nsAtom* roleAtom = acc->LandmarkRole()) {
+ roleAtom->ToString(*aLandmark);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGroupPosition(
+ const uint64_t& aID, int32_t* aLevel, int32_t* aSimilarItemsInGroup,
+ int32_t* aPositionInGroup) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ GroupPos groupPos = acc->GroupPosition();
+ *aLevel = groupPos.level;
+ *aSimilarItemsInGroup = groupPos.setSize;
+ *aPositionInGroup = groupPos.posInSet;
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollToPoint(
+ const uint64_t& aID, const uint32_t& aScrollType, const int32_t& aX,
+ const int32_t& aY) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->ScrollToPoint(aScrollType, aX, aY);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAnnounce(
+ const uint64_t& aID, const nsAString& aAnnouncement,
+ const uint16_t& aPriority) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->Announce(aAnnouncement, aPriority);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCaretLineNumber(
+ const uint64_t& aID, int32_t* aLineNumber) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aLineNumber = acc && acc->IsTextRole() ? acc->CaretLineNumber() : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCaretOffset(const uint64_t& aID,
+ int32_t* aOffset) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aOffset = acc && acc->IsTextRole() ? acc->CaretOffset() : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCharacterCount(
+ const uint64_t& aID, int32_t* aCount) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aCount = acc ? acc->CharacterCount() : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSelectionCount(
+ const uint64_t& aID, int32_t* aCount) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aCount = acc ? acc->SelectionCount() : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTextSubstring(
+ const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
+ nsString* aText, bool* aValid) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ TextLeafAccessible* leaf = acc->AsTextLeaf();
+ if (leaf) {
+ if (aStartOffset != 0 || aEndOffset != -1) {
+ // We don't support fetching partial text from a leaf.
+ *aValid = false;
+ return IPC_OK();
+ }
+ *aValid = true;
+ *aText = leaf->Text();
+ return IPC_OK();
+ }
+
+ HyperTextAccessible* hyper = acc->AsHyperText();
+ if (!hyper) {
+ return IPC_OK();
+ }
+
+ *aValid = hyper->IsValidRange(aStartOffset, aEndOffset);
+ hyper->TextSubstring(aStartOffset, aEndOffset, *aText);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetTextAfterOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) {
+ *aStartOffset = 0;
+ *aEndOffset = 0;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ acc->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset,
+ *aText);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetTextAtOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) {
+ *aStartOffset = 0;
+ *aEndOffset = 0;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ acc->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, *aText);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetTextBeforeOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) {
+ *aStartOffset = 0;
+ *aEndOffset = 0;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ acc->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset,
+ *aText);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCharAt(const uint64_t& aID,
+ const int32_t& aOffset,
+ uint16_t* aChar) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aChar = acc && acc->IsTextRole()
+ ? static_cast<uint16_t>(acc->CharAt(aOffset))
+ : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTextAttributes(
+ const uint64_t& aID, const bool& aIncludeDefAttrs, const int32_t& aOffset,
+ RefPtr<AccAttributes>* aAttributes, int32_t* aStartOffset,
+ int32_t* aEndOffset) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (!acc || !acc->IsTextRole()) {
+ return IPC_OK();
+ }
+
+ *aAttributes =
+ acc->TextAttributes(aIncludeDefAttrs, aOffset, aStartOffset, aEndOffset);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDefaultTextAttributes(
+ const uint64_t& aID, RefPtr<AccAttributes>* aAttributes) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (!acc || !acc->IsTextRole()) {
+ return IPC_OK();
+ }
+
+ *aAttributes = acc->DefaultTextAttributes();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTextBounds(
+ const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
+ const uint32_t& aCoordType, LayoutDeviceIntRect* aRetVal) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aRetVal = acc->TextBounds(aStartOffset, aEndOffset, aCoordType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCharBounds(
+ const uint64_t& aID, const int32_t& aOffset, const uint32_t& aCoordType,
+ LayoutDeviceIntRect* aRetVal) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aRetVal = acc->CharBounds(aOffset, aCoordType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvOffsetAtPoint(
+ const uint64_t& aID, const int32_t& aX, const int32_t& aY,
+ const uint32_t& aCoordType, int32_t* aRetVal) {
+ *aRetVal = -1;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aRetVal = acc->OffsetAtPoint(aX, aY, aCoordType);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSelectionBoundsAt(
+ const uint64_t& aID, const int32_t& aSelectionNum, bool* aSucceeded,
+ nsString* aData, int32_t* aStartOffset, int32_t* aEndOffset) {
+ *aSucceeded = false;
+ *aStartOffset = 0;
+ *aEndOffset = 0;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aSucceeded =
+ acc->SelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
+ if (*aSucceeded) {
+ acc->TextSubstring(*aStartOffset, *aEndOffset, *aData);
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSetSelectionBoundsAt(
+ const uint64_t& aID, const int32_t& aSelectionNum,
+ const int32_t& aStartOffset, const int32_t& aEndOffset, bool* aSucceeded) {
+ *aSucceeded = false;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aSucceeded =
+ acc->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAddToSelection(
+ const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
+ bool* aSucceeded) {
+ *aSucceeded = false;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aSucceeded = acc->AddToSelection(aStartOffset, aEndOffset);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRemoveFromSelection(
+ const uint64_t& aID, const int32_t& aSelectionNum, bool* aSucceeded) {
+ *aSucceeded = false;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aSucceeded = acc->RemoveFromSelection(aSelectionNum);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollSubstringTo(
+ const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
+ const uint32_t& aScrollType) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ acc->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvScrollSubstringToPoint(
+ const uint64_t& aID, const int32_t& aStartOffset, const int32_t& aEndOffset,
+ const uint32_t& aCoordinateType, const int32_t& aX, const int32_t& aY) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ acc->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType, aX,
+ aY);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvText(const uint64_t& aID,
+ nsString* aText) {
+ TextLeafAccessible* acc = IdToTextLeafAccessible(aID);
+ if (acc) {
+ *aText = acc->Text();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvReplaceText(
+ const uint64_t& aID, const nsAString& aText) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ acc->ReplaceText(aText);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvInsertText(
+ const uint64_t& aID, const nsAString& aText, const int32_t& aPosition,
+ bool* aValid) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aValid = acc->IsValidOffset(aPosition);
+ acc->InsertText(aText, aPosition);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCopyText(
+ const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos,
+ bool* aValid) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ acc->CopyText(aStartPos, aEndPos);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCutText(
+ const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos,
+ bool* aValid) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aValid = acc->IsValidRange(aStartPos, aEndPos);
+ acc->CutText(aStartPos, aEndPos);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDeleteText(
+ const uint64_t& aID, const int32_t& aStartPos, const int32_t& aEndPos,
+ bool* aValid) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aValid = acc->IsValidRange(aStartPos, aEndPos);
+ acc->DeleteText(aStartPos, aEndPos);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvPasteText(
+ const uint64_t& aID, const int32_t& aPosition, bool* aValid) {
+ RefPtr<HyperTextAccessible> acc = IdToHyperTextAccessible(aID);
+ if (acc && acc->IsTextRole()) {
+ *aValid = acc->IsValidOffset(aPosition);
+ acc->PasteText(aPosition);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvImagePosition(
+ const uint64_t& aID, const uint32_t& aCoordType,
+ LayoutDeviceIntPoint* aRetVal) {
+ ImageAccessible* acc = IdToImageAccessible(aID);
+ if (acc) {
+ *aRetVal = acc->Position(aCoordType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvImageSize(
+ const uint64_t& aID, LayoutDeviceIntSize* aRetVal) {
+ ImageAccessible* acc = IdToImageAccessible(aID);
+ if (acc) {
+ *aRetVal = acc->Size();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvStartOffset(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) {
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ if (acc) {
+ *aRetVal = acc->StartOffset();
+ *aOk = true;
+ } else {
+ *aRetVal = 0;
+ *aOk = false;
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvEndOffset(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) {
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ if (acc) {
+ *aRetVal = acc->EndOffset();
+ *aOk = true;
+ } else {
+ *aRetVal = 0;
+ *aOk = false;
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvIsLinkValid(const uint64_t& aID,
+ bool* aRetVal) {
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ if (acc) {
+ *aRetVal = acc->IsLinkValid();
+ } else {
+ *aRetVal = false;
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAnchorCount(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) {
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ if (acc) {
+ *aRetVal = acc->AnchorCount();
+ *aOk = true;
+ } else {
+ *aRetVal = 0;
+ *aOk = false;
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAnchorURIAt(
+ const uint64_t& aID, const uint32_t& aIndex, nsCString* aURI, bool* aOk) {
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ *aOk = false;
+ if (acc) {
+ nsCOMPtr<nsIURI> uri = acc->AnchorURIAt(aIndex);
+ if (uri) {
+ uri->GetSpec(*aURI);
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAnchorAt(const uint64_t& aID,
+ const uint32_t& aIndex,
+ uint64_t* aIDOfAnchor,
+ bool* aOk) {
+ *aIDOfAnchor = 0;
+ *aOk = false;
+ LocalAccessible* acc = IdToAccessibleLink(aID);
+ if (acc) {
+ LocalAccessible* anchor = acc->AnchorAt(aIndex);
+ if (anchor) {
+ *aIDOfAnchor = reinterpret_cast<uint64_t>(anchor->UniqueID());
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvLinkCount(const uint64_t& aID,
+ uint32_t* aCount) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aCount = acc ? acc->LinkCount() : 0;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvLinkAt(const uint64_t& aID,
+ const uint32_t& aIndex,
+ uint64_t* aIDOfLink,
+ bool* aOk) {
+ *aIDOfLink = 0;
+ *aOk = false;
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ if (acc) {
+ LocalAccessible* link = acc->LinkAt(aIndex);
+ if (link) {
+ *aIDOfLink = reinterpret_cast<uint64_t>(link->UniqueID());
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvLinkIndexAtOffset(
+ const uint64_t& aID, const uint32_t& aOffset, int32_t* aIndex) {
+ HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+ *aIndex = acc ? acc->LinkIndexAtOffset(aOffset) : -1;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableOfACell(
+ const uint64_t& aID, uint64_t* aTableID, bool* aOk) {
+ *aTableID = 0;
+ *aOk = false;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ TableAccessible* table = acc->Table();
+ if (table) {
+ *aTableID = reinterpret_cast<uint64_t>(table->AsAccessible()->UniqueID());
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvColIdx(const uint64_t& aID,
+ uint32_t* aIndex) {
+ *aIndex = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aIndex = acc->ColIdx();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRowIdx(const uint64_t& aID,
+ uint32_t* aIndex) {
+ *aIndex = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aIndex = acc->RowIdx();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetPosition(const uint64_t& aID,
+ uint32_t* aRowIdx,
+ uint32_t* aColIdx) {
+ *aColIdx = 0;
+ *aRowIdx = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aColIdx = acc->ColIdx();
+ *aRowIdx = acc->RowIdx();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetColRowExtents(
+ const uint64_t& aID, uint32_t* aColIdx, uint32_t* aRowIdx,
+ uint32_t* aColExtent, uint32_t* aRowExtent) {
+ *aColIdx = 0;
+ *aRowIdx = 0;
+ *aColExtent = 0;
+ *aRowExtent = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aColIdx = acc->ColIdx();
+ *aRowIdx = acc->RowIdx();
+ *aColExtent = acc->ColExtent();
+ *aRowExtent = acc->RowExtent();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvColExtent(const uint64_t& aID,
+ uint32_t* aExtent) {
+ *aExtent = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aExtent = acc->ColExtent();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRowExtent(const uint64_t& aID,
+ uint32_t* aExtent) {
+ *aExtent = 0;
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ *aExtent = acc->RowExtent();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvColHeaderCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCells) {
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ AutoTArray<Accessible*, 10> headerCells;
+ acc->ColHeaderCells(&headerCells);
+ aCells->SetCapacity(headerCells.Length());
+ for (Accessible* header : headerCells) {
+ aCells->AppendElement(header->ID());
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRowHeaderCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCells) {
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ if (acc) {
+ AutoTArray<Accessible*, 10> headerCells;
+ acc->RowHeaderCells(&headerCells);
+ aCells->SetCapacity(headerCells.Length());
+ for (Accessible* header : headerCells) {
+ aCells->AppendElement(header->ID());
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvIsCellSelected(
+ const uint64_t& aID, bool* aSelected) {
+ TableCellAccessible* acc = IdToTableCellAccessible(aID);
+ *aSelected = acc && acc->Selected();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableCaption(
+ const uint64_t& aID, uint64_t* aCaptionID, bool* aOk) {
+ *aCaptionID = 0;
+ *aOk = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ LocalAccessible* caption = acc->Caption();
+ if (caption) {
+ *aCaptionID = reinterpret_cast<uint64_t>(caption->UniqueID());
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSummary(
+ const uint64_t& aID, nsString* aSummary) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->Summary(*aSummary);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableColumnCount(
+ const uint64_t& aID, uint32_t* aColCount) {
+ *aColCount = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aColCount = acc->ColCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowCount(
+ const uint64_t& aID, uint32_t* aRowCount) {
+ *aRowCount = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aRowCount = acc->RowCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableCellAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ uint64_t* aCellID, bool* aOk) {
+ *aCellID = 0;
+ *aOk = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ LocalAccessible* cell = acc->CellAt(aRow, aCol);
+ if (cell) {
+ *aCellID = reinterpret_cast<uint64_t>(cell->UniqueID());
+ *aOk = true;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableCellIndexAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ int32_t* aIndex) {
+ *aIndex = -1;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aIndex = acc->CellIndexAt(aRow, aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableColumnIndexAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aCol) {
+ *aCol = -1;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aCol = acc->ColIndexAt(aCellIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowIndexAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aRow) {
+ *aRow = -1;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aRow = acc->RowIndexAt(aCellIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowAndColumnIndicesAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aRow,
+ int32_t* aCol) {
+ *aRow = -1;
+ *aCol = -1;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->RowAndColIndicesAt(aCellIndex, aRow, aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableColumnExtentAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ uint32_t* aExtent) {
+ *aExtent = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aExtent = acc->ColExtentAt(aRow, aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowExtentAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ uint32_t* aExtent) {
+ *aExtent = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aExtent = acc->RowExtentAt(aRow, aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableColumnDescription(
+ const uint64_t& aID, const uint32_t& aCol, nsString* aDescription) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->ColDescription(aCol, *aDescription);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowDescription(
+ const uint64_t& aID, const uint32_t& aRow, nsString* aDescription) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->RowDescription(aRow, *aDescription);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableColumnSelected(
+ const uint64_t& aID, const uint32_t& aCol, bool* aSelected) {
+ *aSelected = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelected = acc->IsColSelected(aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableRowSelected(
+ const uint64_t& aID, const uint32_t& aRow, bool* aSelected) {
+ *aSelected = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelected = acc->IsRowSelected(aRow);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableCellSelected(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ bool* aSelected) {
+ *aSelected = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelected = acc->IsCellSelected(aRow, aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedCellCount(
+ const uint64_t& aID, uint32_t* aSelectedCells) {
+ *aSelectedCells = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelectedCells = acc->SelectedCellCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedColumnCount(
+ const uint64_t& aID, uint32_t* aSelectedColumns) {
+ *aSelectedColumns = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelectedColumns = acc->SelectedColCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedRowCount(
+ const uint64_t& aID, uint32_t* aSelectedRows) {
+ *aSelectedRows = 0;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aSelectedRows = acc->SelectedRowCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCellIDs) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ AutoTArray<Accessible*, 30> cells;
+ acc->SelectedCells(&cells);
+ aCellIDs->SetCapacity(cells.Length());
+ for (Accessible* cell : cells) {
+ aCellIDs->AppendElement(cell->ID());
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedCellIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aCellIndices) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->SelectedCellIndices(aCellIndices);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedColumnIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aColumnIndices) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->SelectedColIndices(aColumnIndices);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectedRowIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aRowIndices) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->SelectedRowIndices(aRowIndices);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectColumn(
+ const uint64_t& aID, const uint32_t& aCol) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->SelectCol(aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableSelectRow(
+ const uint64_t& aID, const uint32_t& aRow) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->SelectRow(aRow);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableUnselectColumn(
+ const uint64_t& aID, const uint32_t& aCol) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->UnselectCol(aCol);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableUnselectRow(
+ const uint64_t& aID, const uint32_t& aRow) {
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ acc->UnselectRow(aRow);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTableIsProbablyForLayout(
+ const uint64_t& aID, bool* aForLayout) {
+ *aForLayout = false;
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ *aForLayout = acc->IsProbablyLayoutTable();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAtkTableColumnHeader(
+ const uint64_t& aID, const int32_t& aCol, uint64_t* aHeader, bool* aOk) {
+ *aHeader = 0;
+ *aOk = false;
+
+#ifdef MOZ_ACCESSIBILITY_ATK
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ Accessible* header = AccessibleWrap::GetColumnHeader(acc, aCol);
+ if (header) {
+ *aHeader = header->ID();
+ *aOk = true;
+ }
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAtkTableRowHeader(
+ const uint64_t& aID, const int32_t& aRow, uint64_t* aHeader, bool* aOk) {
+ *aHeader = 0;
+ *aOk = false;
+
+#ifdef MOZ_ACCESSIBILITY_ATK
+ TableAccessible* acc = IdToTableAccessible(aID);
+ if (acc) {
+ Accessible* header = AccessibleWrap::GetRowHeader(acc, aRow);
+ if (header) {
+ *aHeader = header->ID();
+ *aOk = true;
+ }
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSelectedItems(
+ const uint64_t& aID, nsTArray<uint64_t>* aSelectedItemIDs) {
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ AutoTArray<Accessible*, 10> selectedItems;
+ acc->SelectedItems(&selectedItems);
+ aSelectedItemIDs->SetCapacity(selectedItems.Length());
+ for (Accessible* item : selectedItems) {
+ aSelectedItemIDs->AppendElement(
+ reinterpret_cast<uint64_t>(item->AsLocal()->UniqueID()));
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSelectedItemCount(
+ const uint64_t& aID, uint32_t* aCount) {
+ *aCount = 0;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aCount = acc->SelectedItemCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvGetSelectedItem(
+ const uint64_t& aID, const uint32_t& aIndex, uint64_t* aSelected,
+ bool* aOk) {
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ Accessible* selected = acc->GetSelectedItem(aIndex);
+ *aSelected = reinterpret_cast<uint64_t>(selected->AsLocal()->UniqueID());
+ }
+
+ *aOk = !!acc;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvIsItemSelected(
+ const uint64_t& aID, const uint32_t& aIndex, bool* aSelected) {
+ *aSelected = false;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aSelected = acc->IsItemSelected(aIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAddItemToSelection(
+ const uint64_t& aID, const uint32_t& aIndex, bool* aSuccess) {
+ *aSuccess = false;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aSuccess = acc->AddItemToSelection(aIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRemoveItemFromSelection(
+ const uint64_t& aID, const uint32_t& aIndex, bool* aSuccess) {
+ *aSuccess = false;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aSuccess = acc->RemoveItemFromSelection(aIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSelectAll(const uint64_t& aID,
+ bool* aSuccess) {
+ *aSuccess = false;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aSuccess = acc->SelectAll();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvUnselectAll(const uint64_t& aID,
+ bool* aSuccess) {
+ *aSuccess = false;
+ LocalAccessible* acc = IdToAccessibleSelect(aID);
+ if (acc) {
+ *aSuccess = acc->UnselectAll();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDoAction(const uint64_t& aID,
+ const uint8_t& aIndex,
+ bool* aSuccess) {
+ *aSuccess = false;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aSuccess = acc->DoAction(aIndex);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvActionCount(const uint64_t& aID,
+ uint8_t* aCount) {
+ *aCount = 0;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aCount = acc->ActionCount();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvActionNameAt(
+ const uint64_t& aID, const uint8_t& aIndex, nsString* aName) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->ActionNameAt(aIndex, *aName);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAccessKey(
+ const uint64_t& aID, uint32_t* aKey, uint32_t* aModifierMask) {
+ *aKey = 0;
+ *aModifierMask = 0;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ KeyBinding kb = acc->AccessKey();
+ *aKey = kb.Key();
+ *aModifierMask = kb.ModifierMask();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvAtkKeyBinding(
+ const uint64_t& aID, nsString* aResult) {
+#ifdef MOZ_ACCESSIBILITY_ATK
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ AccessibleWrap::GetKeyBinding(acc, *aResult);
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvCurValue(const uint64_t& aID,
+ double* aValue) {
+ *aValue = UnspecifiedNaN<double>();
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aValue = acc->CurValue();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvSetCurValue(
+ const uint64_t& aID, const double& aValue, bool* aRetVal) {
+ *aRetVal = false;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aRetVal = acc->SetCurValue(aValue);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvMinValue(const uint64_t& aID,
+ double* aValue) {
+ *aValue = UnspecifiedNaN<double>();
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aValue = acc->MinValue();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvMaxValue(const uint64_t& aID,
+ double* aValue) {
+ *aValue = UnspecifiedNaN<double>();
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aValue = acc->MaxValue();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvStep(const uint64_t& aID,
+ double* aStep) {
+ *aStep = UnspecifiedNaN<double>();
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ *aStep = acc->Step();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvLanguage(const uint64_t& aID,
+ nsString* aLocale) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ acc->Language(*aLocale);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDocType(const uint64_t& aID,
+ nsString* aType) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && acc->IsDoc()) {
+ acc->AsDoc()->DocType(*aType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvTitle(const uint64_t& aID,
+ nsString* aTitle) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc) {
+ mozilla::ErrorResult rv;
+ acc->GetContent()->GetTextContent(*aTitle, rv);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvMimeType(const uint64_t& aID,
+ nsString* aMime) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && acc->IsDoc()) {
+ acc->AsDoc()->MimeType(*aMime);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvURLDocTypeMimeType(
+ const uint64_t& aID, nsString* aURL, nsString* aDocType,
+ nsString* aMimeType) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && acc->IsDoc()) {
+ DocAccessible* doc = acc->AsDoc();
+ doc->URL(*aURL);
+ doc->DocType(*aDocType);
+ doc->MimeType(*aMimeType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvChildAtPoint(
+ const uint64_t& aID, const int32_t& aX, const int32_t& aY,
+ const uint32_t& aWhich, PDocAccessibleChild** aResultDoc,
+ uint64_t* aResultID) {
+ *aResultDoc = nullptr;
+ *aResultID = 0;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && !acc->IsDefunct()) {
+ int32_t x = aX;
+ int32_t y = aY;
+ LocalAccessible* result = acc->LocalChildAtPoint(
+ x, y, static_cast<LocalAccessible::EWhichChildAtPoint>(aWhich));
+ if (result) {
+ // LocalAccessible::ChildAtPoint can return a LocalAccessible from a
+ // descendant document.
+ DocAccessibleChild* resultDoc = result->Document()->IPCDoc();
+ // We've sent the constructor for this document to the parent process.
+ // However, because the constructor is async, the parent process might
+ // get the result of this (sync) method before it runs the constructor.
+ // If we send this document in this case, the parent process will crash.
+ // Therefore, we only do this if the parent process has explicitly told
+ // us that the document has been constructed there.
+ if (resultDoc && resultDoc->IsConstructedInParentProcess()) {
+ *aResultDoc = resultDoc;
+ *aResultID = result->IsDoc()
+ ? 0
+ : reinterpret_cast<uint64_t>(result->UniqueID());
+ }
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvExtents(
+ const uint64_t& aID, const bool& aNeedsScreenCoords, int32_t* aX,
+ int32_t* aY, int32_t* aWidth, int32_t* aHeight) {
+ *aX = 0;
+ *aY = 0;
+ *aWidth = 0;
+ *aHeight = 0;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && !acc->IsDefunct()) {
+ LayoutDeviceIntRect screenRect = acc->Bounds();
+ if (!screenRect.IsEmpty()) {
+ if (aNeedsScreenCoords) {
+ LayoutDeviceIntPoint winCoords =
+ nsAccUtils::GetScreenCoordsForWindow(acc);
+ screenRect.x -= winCoords.x;
+ screenRect.y -= winCoords.y;
+ }
+
+ *aWidth = screenRect.width;
+ *aHeight = screenRect.height;
+ }
+ // We should always report the position of our acc, even if
+ // the returned screenRect is empty.
+ *aX = screenRect.x;
+ *aY = screenRect.y;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvExtentsInCSSPixels(
+ const uint64_t& aID, int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight) {
+ *aX = 0;
+ *aY = 0;
+ *aWidth = 0;
+ *aHeight = 0;
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (acc && !acc->IsDefunct()) {
+ nsIntRect screenRect = acc->BoundsInCSSPixels();
+ if (!screenRect.IsEmpty()) {
+ *aWidth = screenRect.width;
+ *aHeight = screenRect.height;
+ }
+ // We should always report the position of our acc, even if
+ // the returned screenRect is empty.
+ *aX = screenRect.x;
+ *aY = screenRect.y;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvDOMNodeID(
+ const uint64_t& aID, nsString* aDOMNodeID) {
+ LocalAccessible* acc = IdToAccessible(aID);
+ if (!acc) {
+ return IPC_OK();
+ }
+
+ nsIContent* content = acc->GetContent();
+ if (!content) {
+ return IPC_OK();
+ }
+
+ nsAtom* id = content->GetID();
+ if (id) {
+ id->ToString(*aDOMNodeID);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvConstructedInParentProcess() {
+ SetConstructedInParentProcess();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult DocAccessibleChild::RecvRestoreFocus() {
+ FocusMgr()->ForceFocusEvent();
+ return IPC_OK();
+}
+
+bool DocAccessibleChild::DeallocPDocAccessiblePlatformExtChild(
+ PDocAccessiblePlatformExtChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+PDocAccessiblePlatformExtChild*
+DocAccessibleChild::AllocPDocAccessiblePlatformExtChild() {
+ return new DocAccessiblePlatformExtChild();
+}
+
+DocAccessiblePlatformExtChild* DocAccessibleChild::GetPlatformExtension() {
+ return static_cast<DocAccessiblePlatformExtChild*>(
+ SingleManagedOrNull(ManagedPDocAccessiblePlatformExtChild()));
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/other/DocAccessibleChild.h b/accessible/ipc/other/DocAccessibleChild.h
new file mode 100644
index 0000000000..8973cb6eba
--- /dev/null
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -0,0 +1,465 @@
+/* -*- 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/DocAccessibleChildBase.h"
+
+namespace mozilla {
+namespace a11y {
+
+class LocalAccessible;
+class DocAccessiblePlatformExtChild;
+class HyperTextAccessible;
+class TextLeafAccessible;
+class ImageAccessible;
+class TableAccessible;
+class TableCellAccessible;
+
+/*
+ * 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 {
+ friend DocAccessiblePlatformExtChild;
+
+ public:
+ DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
+ : DocAccessibleChildBase(aDoc) {
+ MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+ SetManager(aManager);
+ }
+
+ ~DocAccessibleChild() {
+ MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvConstructedInParentProcess() override;
+ virtual mozilla::ipc::IPCResult RecvRestoreFocus() override;
+
+ /*
+ * Return the state for the accessible with given ID.
+ */
+ virtual mozilla::ipc::IPCResult RecvState(const uint64_t& aID,
+ uint64_t* aState) override;
+
+ /*
+ * Return the native state for the accessible with given ID.
+ */
+ virtual mozilla::ipc::IPCResult RecvNativeState(const uint64_t& aID,
+ uint64_t* aState) override;
+
+ /*
+ * Get the name for the accessible with given id.
+ */
+ virtual mozilla::ipc::IPCResult RecvName(const uint64_t& aID, nsString* aName,
+ uint32_t* aFlag) override;
+
+ virtual mozilla::ipc::IPCResult RecvValue(const uint64_t& aID,
+ nsString* aValue) override;
+
+ virtual mozilla::ipc::IPCResult RecvHelp(const uint64_t& aID,
+ nsString* aHelp) override;
+
+ /*
+ * Get the description for the accessible with given id.
+ */
+ virtual mozilla::ipc::IPCResult RecvDescription(const uint64_t& aID,
+ nsString* aDesc) override;
+ virtual mozilla::ipc::IPCResult RecvRelationByType(
+ const uint64_t& aID, const uint32_t& aType,
+ nsTArray<uint64_t>* aTargets) override;
+ virtual mozilla::ipc::IPCResult RecvRelations(
+ const uint64_t& aID, nsTArray<RelationTargets>* aRelations) override;
+
+ virtual mozilla::ipc::IPCResult RecvIsSearchbox(const uint64_t& aID,
+ bool* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvLandmarkRole(
+ const uint64_t& aID, nsString* aLandmark) override;
+
+ virtual mozilla::ipc::IPCResult RecvGroupPosition(
+ const uint64_t& aID, int32_t* aLevel, int32_t* aSimilarItemsInGroup,
+ int32_t* aPositionInGroup) override;
+
+ virtual mozilla::ipc::IPCResult RecvAttributes(
+ const uint64_t& aID, RefPtr<AccAttributes>* aAttributes) override;
+ virtual mozilla::ipc::IPCResult RecvScrollToPoint(const uint64_t& aID,
+ const uint32_t& aScrollType,
+ const int32_t& aX,
+ const int32_t& aY) override;
+
+ virtual mozilla::ipc::IPCResult RecvAnnounce(
+ const uint64_t& aID, const nsAString& aAnnouncement,
+ const uint16_t& aPriority) override;
+
+ virtual mozilla::ipc::IPCResult RecvCaretLineNumber(
+ const uint64_t& aID, int32_t* aLineNumber) override;
+ virtual mozilla::ipc::IPCResult RecvCaretOffset(const uint64_t& aID,
+ int32_t* aOffset) override;
+
+ virtual mozilla::ipc::IPCResult RecvCharacterCount(const uint64_t& aID,
+ int32_t* aCount) override;
+ virtual mozilla::ipc::IPCResult RecvSelectionCount(const uint64_t& aID,
+ int32_t* aCount) override;
+
+ virtual mozilla::ipc::IPCResult RecvTextSubstring(const uint64_t& aID,
+ const int32_t& aStartOffset,
+ const int32_t& aEndOffset,
+ nsString* aText,
+ bool* aValid) override;
+
+ virtual mozilla::ipc::IPCResult RecvGetTextAfterOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) override;
+ virtual mozilla::ipc::IPCResult RecvGetTextAtOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) override;
+ virtual mozilla::ipc::IPCResult RecvGetTextBeforeOffset(
+ const uint64_t& aID, const int32_t& aOffset, const int32_t& aBoundaryType,
+ nsString* aText, int32_t* aStartOffset, int32_t* aEndOffset) override;
+
+ virtual mozilla::ipc::IPCResult RecvCharAt(const uint64_t& aID,
+ const int32_t& aOffset,
+ uint16_t* aChar) override;
+
+ virtual mozilla::ipc::IPCResult RecvTextAttributes(
+ const uint64_t& aID, const bool& aIncludeDefAttrs, const int32_t& aOffset,
+ RefPtr<AccAttributes>* aAttributes, int32_t* aStartOffset,
+ int32_t* aEndOffset) override;
+
+ virtual mozilla::ipc::IPCResult RecvDefaultTextAttributes(
+ const uint64_t& aID, RefPtr<AccAttributes>* aAttributes) override;
+
+ virtual mozilla::ipc::IPCResult RecvTextBounds(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const int32_t& aEndOffset, const uint32_t& aCoordType,
+ LayoutDeviceIntRect* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvCharBounds(
+ const uint64_t& aID, const int32_t& aOffset, const uint32_t& aCoordType,
+ LayoutDeviceIntRect* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvOffsetAtPoint(const uint64_t& aID,
+ const int32_t& aX,
+ const int32_t& aY,
+ const uint32_t& aCoordType,
+ int32_t* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectionBoundsAt(
+ const uint64_t& aID, const int32_t& aSelectionNum, bool* aSucceeded,
+ nsString* aData, int32_t* aStartOffset, int32_t* aEndOffset) override;
+
+ virtual mozilla::ipc::IPCResult RecvSetSelectionBoundsAt(
+ const uint64_t& aID, const int32_t& aSelectionNum,
+ const int32_t& aStartOffset, const int32_t& aEndOffset,
+ bool* aSucceeded) override;
+
+ virtual mozilla::ipc::IPCResult RecvAddToSelection(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const int32_t& aEndOffset, bool* aSucceeded) override;
+
+ virtual mozilla::ipc::IPCResult RecvRemoveFromSelection(
+ const uint64_t& aID, const int32_t& aSelectionNum,
+ bool* aSucceeded) override;
+
+ virtual mozilla::ipc::IPCResult RecvScrollSubstringTo(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const int32_t& aEndOffset, const uint32_t& aScrollType) override;
+
+ virtual mozilla::ipc::IPCResult RecvScrollSubstringToPoint(
+ const uint64_t& aID, const int32_t& aStartOffset,
+ const int32_t& aEndOffset, const uint32_t& aCoordinateType,
+ const int32_t& aX, const int32_t& aY) override;
+
+ virtual mozilla::ipc::IPCResult RecvText(const uint64_t& aID,
+ nsString* aText) override;
+
+ virtual mozilla::ipc::IPCResult RecvReplaceText(
+ const uint64_t& aID, const nsAString& aText) override;
+
+ virtual mozilla::ipc::IPCResult RecvInsertText(const uint64_t& aID,
+ const nsAString& aText,
+ const int32_t& aPosition,
+ bool* aValid) override;
+
+ virtual mozilla::ipc::IPCResult RecvCopyText(const uint64_t& aID,
+ const int32_t& aStartPos,
+ const int32_t& aEndPos,
+ bool* aValid) override;
+
+ virtual mozilla::ipc::IPCResult RecvCutText(const uint64_t& aID,
+ const int32_t& aStartPos,
+ const int32_t& aEndPos,
+ bool* aValid) override;
+
+ virtual mozilla::ipc::IPCResult RecvDeleteText(const uint64_t& aID,
+ const int32_t& aStartPos,
+ const int32_t& aEndPos,
+ bool* aValid) override;
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual mozilla::ipc::IPCResult RecvPasteText(const uint64_t& aID,
+ const int32_t& aPosition,
+ bool* aValid) override;
+
+ virtual mozilla::ipc::IPCResult RecvImagePosition(
+ const uint64_t& aID, const uint32_t& aCoordType,
+ LayoutDeviceIntPoint* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvImageSize(
+ const uint64_t& aID, LayoutDeviceIntSize* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvStartOffset(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvEndOffset(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvIsLinkValid(const uint64_t& aID,
+ bool* aRetVal) override;
+ virtual mozilla::ipc::IPCResult RecvAnchorCount(const uint64_t& aID,
+ uint32_t* aRetVal,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvAnchorURIAt(const uint64_t& aID,
+ const uint32_t& aIndex,
+ nsCString* aURI,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvAnchorAt(const uint64_t& aID,
+ const uint32_t& aIndex,
+ uint64_t* aIDOfAnchor,
+ bool* aOk) override;
+
+ virtual mozilla::ipc::IPCResult RecvLinkCount(const uint64_t& aID,
+ uint32_t* aCount) override;
+
+ virtual mozilla::ipc::IPCResult RecvLinkAt(const uint64_t& aID,
+ const uint32_t& aIndex,
+ uint64_t* aIDOfLink,
+ bool* aOk) override;
+
+ virtual mozilla::ipc::IPCResult RecvLinkIndexAtOffset(
+ const uint64_t& aID, const uint32_t& aOffset, int32_t* aIndex) override;
+
+ virtual mozilla::ipc::IPCResult RecvTableOfACell(const uint64_t& aID,
+ uint64_t* aTableID,
+ bool* aOk) override;
+
+ virtual mozilla::ipc::IPCResult RecvColIdx(const uint64_t& aID,
+ uint32_t* aIndex) override;
+
+ virtual mozilla::ipc::IPCResult RecvRowIdx(const uint64_t& aID,
+ uint32_t* aIndex) override;
+
+ virtual mozilla::ipc::IPCResult RecvColExtent(const uint64_t& aID,
+ uint32_t* aExtent) override;
+
+ virtual mozilla::ipc::IPCResult RecvGetPosition(const uint64_t& aID,
+ uint32_t* aRowIdx,
+ uint32_t* aColIdx) override;
+
+ virtual mozilla::ipc::IPCResult RecvGetColRowExtents(
+ const uint64_t& aID, uint32_t* aColIdx, uint32_t* aRowIdx,
+ uint32_t* aColExtent, uint32_t* aRowExtent) override;
+
+ virtual mozilla::ipc::IPCResult RecvRowExtent(const uint64_t& aID,
+ uint32_t* aExtent) override;
+
+ virtual mozilla::ipc::IPCResult RecvColHeaderCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCells) override;
+
+ virtual mozilla::ipc::IPCResult RecvRowHeaderCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCells) override;
+
+ virtual mozilla::ipc::IPCResult RecvIsCellSelected(const uint64_t& aID,
+ bool* aSelected) override;
+
+ virtual mozilla::ipc::IPCResult RecvTableCaption(const uint64_t& aID,
+ uint64_t* aCaptionID,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvTableSummary(const uint64_t& aID,
+ nsString* aSummary) override;
+ virtual mozilla::ipc::IPCResult RecvTableColumnCount(
+ const uint64_t& aID, uint32_t* aColCount) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowCount(
+ const uint64_t& aID, uint32_t* aRowCount) override;
+ virtual mozilla::ipc::IPCResult RecvTableCellAt(const uint64_t& aID,
+ const uint32_t& aRow,
+ const uint32_t& aCol,
+ uint64_t* aCellID,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvTableCellIndexAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ int32_t* aIndex) override;
+ virtual mozilla::ipc::IPCResult RecvTableColumnIndexAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aCol) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowIndexAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aRow) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowAndColumnIndicesAt(
+ const uint64_t& aID, const uint32_t& aCellIndex, int32_t* aRow,
+ int32_t* aCol) override;
+ virtual mozilla::ipc::IPCResult RecvTableColumnExtentAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ uint32_t* aExtent) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowExtentAt(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ uint32_t* aExtent) override;
+ virtual mozilla::ipc::IPCResult RecvTableColumnDescription(
+ const uint64_t& aID, const uint32_t& aCol,
+ nsString* aDescription) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowDescription(
+ const uint64_t& aID, const uint32_t& aRow,
+ nsString* aDescription) override;
+ virtual mozilla::ipc::IPCResult RecvTableColumnSelected(
+ const uint64_t& aID, const uint32_t& aCol, bool* aSelected) override;
+ virtual mozilla::ipc::IPCResult RecvTableRowSelected(
+ const uint64_t& aID, const uint32_t& aRow, bool* aSelected) override;
+ virtual mozilla::ipc::IPCResult RecvTableCellSelected(
+ const uint64_t& aID, const uint32_t& aRow, const uint32_t& aCol,
+ bool* aSelected) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedCellCount(
+ const uint64_t& aID, uint32_t* aSelectedCells) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedColumnCount(
+ const uint64_t& aID, uint32_t* aSelectedColumns) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedRowCount(
+ const uint64_t& aID, uint32_t* aSelectedRows) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedCells(
+ const uint64_t& aID, nsTArray<uint64_t>* aCellIDs) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedCellIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aCellIndices) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedColumnIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aColumnIndices) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectedRowIndices(
+ const uint64_t& aID, nsTArray<uint32_t>* aRowIndices) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectColumn(
+ const uint64_t& aID, const uint32_t& aCol) override;
+ virtual mozilla::ipc::IPCResult RecvTableSelectRow(
+ const uint64_t& aID, const uint32_t& aRow) override;
+ virtual mozilla::ipc::IPCResult RecvTableUnselectColumn(
+ const uint64_t& aID, const uint32_t& aCol) override;
+ virtual mozilla::ipc::IPCResult RecvTableUnselectRow(
+ const uint64_t& aID, const uint32_t& aRow) override;
+ virtual mozilla::ipc::IPCResult RecvTableIsProbablyForLayout(
+ const uint64_t& aID, bool* aForLayout) override;
+ virtual mozilla::ipc::IPCResult RecvAtkTableColumnHeader(const uint64_t& aID,
+ const int32_t& aCol,
+ uint64_t* aHeader,
+ bool* aOk) override;
+ virtual mozilla::ipc::IPCResult RecvAtkTableRowHeader(const uint64_t& aID,
+ const int32_t& aRow,
+ uint64_t* aHeader,
+ bool* aOk) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectedItems(
+ const uint64_t& aID, nsTArray<uint64_t>* aSelectedItemIDs) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectedItemCount(
+ const uint64_t& aID, uint32_t* aCount) override;
+
+ virtual mozilla::ipc::IPCResult RecvGetSelectedItem(const uint64_t& aID,
+ const uint32_t& aIndex,
+ uint64_t* aSelected,
+ bool* aOk) override;
+
+ virtual mozilla::ipc::IPCResult RecvIsItemSelected(const uint64_t& aID,
+ const uint32_t& aIndex,
+ bool* aSelected) override;
+
+ virtual mozilla::ipc::IPCResult RecvAddItemToSelection(
+ const uint64_t& aID, const uint32_t& aIndex, bool* aSuccess) override;
+
+ virtual mozilla::ipc::IPCResult RecvRemoveItemFromSelection(
+ const uint64_t& aID, const uint32_t& aIndex, bool* aSuccess) override;
+
+ virtual mozilla::ipc::IPCResult RecvSelectAll(const uint64_t& aID,
+ bool* aSuccess) override;
+
+ virtual mozilla::ipc::IPCResult RecvUnselectAll(const uint64_t& aID,
+ bool* aSuccess) override;
+
+ virtual mozilla::ipc::IPCResult RecvDoAction(const uint64_t& aID,
+ const uint8_t& aIndex,
+ bool* aSuccess) override;
+
+ virtual mozilla::ipc::IPCResult RecvActionCount(const uint64_t& aID,
+ uint8_t* aCount) override;
+
+ virtual mozilla::ipc::IPCResult RecvActionNameAt(const uint64_t& aID,
+ const uint8_t& aIndex,
+ nsString* aName) override;
+
+ virtual mozilla::ipc::IPCResult RecvAccessKey(
+ const uint64_t& aID, uint32_t* aKey, uint32_t* aModifierMask) override;
+
+ virtual mozilla::ipc::IPCResult RecvAtkKeyBinding(const uint64_t& aID,
+ nsString* aResult) override;
+
+ virtual mozilla::ipc::IPCResult RecvCurValue(const uint64_t& aID,
+ double* aValue) override;
+
+ virtual mozilla::ipc::IPCResult RecvSetCurValue(const uint64_t& aID,
+ const double& aValue,
+ bool* aRetVal) override;
+
+ virtual mozilla::ipc::IPCResult RecvMinValue(const uint64_t& aID,
+ double* aValue) override;
+
+ virtual mozilla::ipc::IPCResult RecvMaxValue(const uint64_t& aID,
+ double* aValue) override;
+
+ virtual mozilla::ipc::IPCResult RecvStep(const uint64_t& aID,
+ double* aStep) override;
+
+ virtual mozilla::ipc::IPCResult RecvLanguage(const uint64_t& aID,
+ nsString* aLocale) override;
+ virtual mozilla::ipc::IPCResult RecvDocType(const uint64_t& aID,
+ nsString* aType) override;
+ virtual mozilla::ipc::IPCResult RecvTitle(const uint64_t& aID,
+ nsString* aTitle) override;
+ virtual mozilla::ipc::IPCResult RecvMimeType(const uint64_t& aID,
+ nsString* aMime) override;
+ virtual mozilla::ipc::IPCResult RecvURLDocTypeMimeType(
+ const uint64_t& aID, nsString* aURL, nsString* aDocType,
+ nsString* aMimeType) override;
+
+ virtual mozilla::ipc::IPCResult RecvChildAtPoint(
+ const uint64_t& aID, const int32_t& aX, const int32_t& aY,
+ const uint32_t& aWhich, PDocAccessibleChild** aResultDoc,
+ uint64_t* aResultID) override;
+
+ virtual mozilla::ipc::IPCResult RecvExtents(const uint64_t& aID,
+ const bool& aNeedsScreenCoords,
+ int32_t* aX, int32_t* aY,
+ int32_t* aWidth,
+ int32_t* aHeight) override;
+ virtual mozilla::ipc::IPCResult RecvExtentsInCSSPixels(
+ const uint64_t& aID, int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight) override;
+ virtual mozilla::ipc::IPCResult RecvDOMNodeID(const uint64_t& aID,
+ nsString* aDOMNodeID) override;
+
+ virtual bool DeallocPDocAccessiblePlatformExtChild(
+ PDocAccessiblePlatformExtChild* aActor) override;
+
+ virtual PDocAccessiblePlatformExtChild* AllocPDocAccessiblePlatformExtChild()
+ override;
+
+ DocAccessiblePlatformExtChild* GetPlatformExtension();
+
+ private:
+ LocalAccessible* IdToAccessibleLink(const uint64_t& aID) const;
+ LocalAccessible* IdToAccessibleSelect(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;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/other/PDocAccessible.ipdl b/accessible/ipc/other/PDocAccessible.ipdl
new file mode 100644
index 0000000000..13a9056cac
--- /dev/null
+++ b/accessible/ipc/other/PDocAccessible.ipdl
@@ -0,0 +1,357 @@
+/* -*- 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 PBrowser;
+include protocol PDocAccessiblePlatformExt;
+
+include DocAccessibleTypes;
+
+include "mozilla/GfxMessageUtils.h";
+
+using LayoutDeviceIntRect from "Units.h";
+using LayoutDeviceIntPoint from "Units.h";
+using LayoutDeviceIntSize from "Units.h";
+using mozilla::a11y::role from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::AccType from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::AccGenericType from "mozilla/a11y/IPCTypes.h";
+[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+
+namespace mozilla {
+namespace a11y {
+
+struct AccessibleData
+{
+ uint64_t ID;
+ role Role;
+ uint32_t ChildrenCount;
+ AccType Type;
+ AccGenericType GenericTypes;
+ uint8_t RoleMapEntryIndex;
+};
+
+union OriginDocument
+{
+ PDocAccessible;
+};
+
+struct BatchData
+{
+ OriginDocument Document;
+ uint64_t ID;
+ uint64_t State;
+ LayoutDeviceIntRect Bounds;
+ uint8_t ActionCount;
+ nsString Name;
+ nsString TextValue;
+ nsString DOMNodeID;
+ nsString Description;
+ double CurValue;
+ double MinValue;
+ double MaxValue;
+ double Step;
+ AccAttributes Attributes;
+};
+
+struct ShowEventData
+{
+ uint64_t ID;
+ uint32_t Idx;
+ AccessibleData[] NewTree;
+ bool EventSuppressed;
+};
+
+struct RelationTargets
+{
+ uint32_t Type;
+ uint64_t[] Targets;
+};
+
+struct TextRangeData
+{
+ uint64_t StartID;
+ uint64_t EndID;
+ int32_t StartOffset;
+ int32_t EndOffset;
+};
+
+[ManualDealloc, NestedUpTo=inside_sync, ChildImpl=virtual, ParentImpl=virtual]
+sync protocol PDocAccessible
+{
+ manager PBrowser;
+ manages PDocAccessiblePlatformExt;
+
+parent:
+ async PDocAccessiblePlatformExt();
+ async Shutdown();
+
+ /*
+ * Notify the parent process the document in the child process is firing an
+ * event.
+ */
+ async Event(uint64_t aID, uint32_t type);
+ async ShowEvent(ShowEventData data, bool aFromuser);
+ async HideEvent(uint64_t aRootID, bool aFromUser);
+ async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
+ async CaretMoveEvent(uint64_t aID, int32_t aOffset,
+ bool aIsSelectionCollapsed, bool aIsAtEndOfLine,
+ int32_t aGranularity);
+ async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
+ bool aIsInsert, bool aFromUser);
+ async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);
+ async RoleChangedEvent(role aRole, uint8_t aRoleMapEntryIndex);
+ async VirtualCursorChangeEvent(uint64_t aID,
+ uint64_t aOldPosition,
+ int32_t aOldStartOffset, int32_t aOldEndOffset,
+ uint64_t aPosition,
+ int32_t aStartOffset, int32_t aEndOffset,
+ int16_t aReason, int16_t aBoundaryType,
+ bool aFromUservcEvent);
+ async ScrollingEvent(uint64_t aID, uint64_t aType,
+ uint32_t aScrollX, uint32_t aScrollY,
+ uint32_t aMaxScrollX, uint32_t aMaxScrollY);
+ async AnnouncementEvent(uint64_t aID,
+ nsString aAnnouncement,
+ uint16_t aPriority);
+ async TextSelectionChangeEvent(uint64_t aID, TextRangeData[] aSelection);
+
+ /*
+ * Tell the parent document to bind the existing document as a new child
+ * document.
+ */
+ async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
+
+ // Android
+ async Batch(uint64_t aBatchType, BatchData[] aData);
+
+ /*
+ * Cache The World
+ * aDispatchShowEvent is true when a show event with the first accessible in
+ * the cache list as the target should be dispatched after the cache is
+ * populated. The show event will have a from-user flag value of false.
+ */
+ async Cache(CacheUpdateType aUpdateType, CacheData[] aData, bool aDispatchShowEvent);
+
+ /*
+ * Lists of accessibles that either gained or lost a selected state.
+ */
+ async SelectedAccessiblesChanged(uint64_t[] aSelectedIDs, uint64_t[] aUnselectedIDs);
+
+ /*
+ * Tell the parent process that the given Accessibles are about to be moved
+ * via subsequent hide and show events.
+ */
+ async AccessiblesWillMove(uint64_t[] aIDs);
+
+child:
+ /*
+ * Notify the content process that the PDocAccessible has finished being
+ * constructed in the parent process.
+ */
+ async ConstructedInParentProcess();
+
+ async __delete__();
+
+ /*
+ * Called as a result of focus shifting from chrome to content
+ * elements through keyboard navigation.
+ */
+ async RestoreFocus();
+
+ // LocalAccessible
+ [Nested=inside_sync] sync State(uint64_t aID) returns(uint64_t states);
+ [Nested=inside_sync] sync NativeState(uint64_t aID) returns(uint64_t states);
+ [Nested=inside_sync] sync Name(uint64_t aID) returns(nsString name, uint32_t flag);
+ [Nested=inside_sync] sync Value(uint64_t aID) returns(nsString value);
+ [Nested=inside_sync] sync Help(uint64_t aID) returns(nsString help);
+ [Nested=inside_sync] sync Description(uint64_t aID) returns(nsString desc);
+ [Nested=inside_sync] sync Attributes(uint64_t aID) returns(AccAttributes attributes);
+ [Nested=inside_sync] sync RelationByType(uint64_t aID, uint32_t aRelationType)
+ returns(uint64_t[] targets);
+ [Nested=inside_sync] sync Relations(uint64_t aID) returns(RelationTargets[] relations);
+ [Nested=inside_sync] sync IsSearchbox(uint64_t aID) returns(bool retval);
+ [Nested=inside_sync] sync LandmarkRole(uint64_t aID) returns(nsString landmark);
+ [Nested=inside_sync] sync GroupPosition(uint64_t aID)
+ returns(int32_t groupLevel, int32_t similarItemsInGroup, int32_t positionInGroup);
+ async ScrollTo(uint64_t aID, uint32_t aScrollType);
+ async ScrollToPoint(uint64_t aID, uint32_t aScrollType, int32_t aX,
+ int32_t aY);
+ async Announce(uint64_t aID, nsString aAnnouncement, uint16_t aPriority);
+
+ // AccessibleText
+
+ // TextSubstring is getText in IDL.
+ [Nested=inside_sync] sync CaretLineNumber(uint64_t aID) returns(int32_t aLineNumber);
+ [Nested=inside_sync] sync CaretOffset(uint64_t aID) returns(int32_t aOffset);
+ async SetCaretOffset(uint64_t aID, int32_t aOffset);
+ [Nested=inside_sync] sync CharacterCount(uint64_t aID) returns(int32_t aCount);
+ [Nested=inside_sync] sync SelectionCount(uint64_t aID) returns(int32_t aCount);
+ [Nested=inside_sync] sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
+ aEndOffset) returns(nsString aText, bool aValid);
+ [Nested=inside_sync] sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+ returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
+ [Nested=inside_sync] sync GetTextAtOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+ returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
+
+ [Nested=inside_sync] sync GetTextBeforeOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+ returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
+ [Nested=inside_sync] sync CharAt(uint64_t aID, int32_t aOffset) returns(uint16_t aChar);
+
+ [Nested=inside_sync] sync TextAttributes(uint64_t aID, bool aIncludeDefAttrs, int32_t aOffset)
+ returns(AccAttributes aAttributes, int32_t aStartOffset, int32_t aEndOffset);
+ [Nested=inside_sync] sync DefaultTextAttributes(uint64_t aID) returns(AccAttributes aAttributes);
+
+ [Nested=inside_sync] sync TextBounds(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset,
+ uint32_t aCoordType)
+ returns(LayoutDeviceIntRect aRetVal);
+ [Nested=inside_sync] sync CharBounds(uint64_t aID, int32_t aOffset, uint32_t aCoordType)
+ returns(LayoutDeviceIntRect aRetVal);
+
+ [Nested=inside_sync] sync OffsetAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aCoordType)
+ returns(int32_t aRetVal);
+
+ [Nested=inside_sync] sync SelectionBoundsAt(uint64_t aID, int32_t aSelectionNum)
+ returns(bool aSucceeded, nsString aData, int32_t aStartOffset, int32_t aEndOffset);
+ [Nested=inside_sync] sync SetSelectionBoundsAt(uint64_t aID, int32_t aSelectionNum,
+ int32_t aStartOffset, int32_t aEndOffset)
+ returns(bool aSucceeded);
+ [Nested=inside_sync] sync AddToSelection(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset)
+ returns(bool aSucceeded);
+ [Nested=inside_sync] 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);
+
+ [Nested=inside_sync] sync Text(uint64_t aID) returns(nsString aText);
+ [Nested=inside_sync] sync ReplaceText(uint64_t aID, nsString aText);
+ [Nested=inside_sync] sync InsertText(uint64_t aID, nsString aText, int32_t aPosition)
+ returns(bool aValid);
+ [Nested=inside_sync] sync CopyText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
+ returns(bool aValid);
+ [Nested=inside_sync] sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
+ returns(bool aValid);
+ [Nested=inside_sync] sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
+ returns(bool aValid);
+ [Nested=inside_sync] sync PasteText(uint64_t aID, int32_t aPosition)
+ returns(bool aValid);
+
+ [Nested=inside_sync] sync ImagePosition(uint64_t aID, uint32_t aCoordType) returns(LayoutDeviceIntPoint aRetVal);
+ [Nested=inside_sync] sync ImageSize(uint64_t aID) returns(LayoutDeviceIntSize aRetVal);
+
+ [Nested=inside_sync] sync StartOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+ [Nested=inside_sync] sync EndOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+ [Nested=inside_sync] sync IsLinkValid(uint64_t aID) returns(bool aRetVal);
+ [Nested=inside_sync] sync AnchorCount(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
+ [Nested=inside_sync] sync AnchorURIAt(uint64_t aID, uint32_t aIndex) returns(nsCString aURI, bool aOk);
+ [Nested=inside_sync] sync AnchorAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfAnchor, bool aOk);
+
+ [Nested=inside_sync] sync LinkCount(uint64_t aID) returns(uint32_t aCount);
+ [Nested=inside_sync] sync LinkAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfLink, bool aOk);
+ [Nested=inside_sync] sync LinkIndexAtOffset(uint64_t aID, uint32_t aOffset) returns(int32_t aIndex);
+
+ [Nested=inside_sync] sync TableOfACell(uint64_t aID) returns(uint64_t aTableID, bool aOk);
+ [Nested=inside_sync] sync ColIdx(uint64_t aID) returns(uint32_t aIndex);
+ [Nested=inside_sync] sync RowIdx(uint64_t aID) returns(uint32_t aIndex);
+ [Nested=inside_sync] sync GetPosition(uint64_t aID) returns(uint32_t aRow, uint32_t aCol);
+ [Nested=inside_sync] sync ColExtent(uint64_t aID) returns(uint32_t aExtent);
+ [Nested=inside_sync] sync RowExtent(uint64_t aID) returns(uint32_t aExtent);
+ [Nested=inside_sync] sync GetColRowExtents(uint64_t aID)
+ returns(uint32_t aCol, uint32_t aRow, uint32_t aColExtent, uint32_t aRowExtent);
+ [Nested=inside_sync] sync ColHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
+ [Nested=inside_sync] sync RowHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
+ [Nested=inside_sync] sync IsCellSelected(uint64_t aID) returns(bool aSelected);
+
+ [Nested=inside_sync] sync TableCaption(uint64_t aID) returns(uint64_t aCaptionID, bool aOk);
+ [Nested=inside_sync] sync TableSummary(uint64_t aID) returns(nsString aSummary);
+ [Nested=inside_sync] sync TableColumnCount(uint64_t aID) returns(uint32_t aColCount);
+ [Nested=inside_sync] sync TableRowCount(uint64_t aID) returns(uint32_t aRowCount);
+ [Nested=inside_sync] sync TableCellAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint64_t aCellID, bool aOk);
+ [Nested=inside_sync] sync TableCellIndexAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(int32_t aIndex);
+ [Nested=inside_sync] sync TableColumnIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aCol);
+ [Nested=inside_sync] sync TableRowIndexAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow);
+ [Nested=inside_sync] sync TableRowAndColumnIndicesAt(uint64_t aID, uint32_t aCellIndex) returns(int32_t aRow, int32_t aCol);
+ [Nested=inside_sync] sync TableColumnExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
+ [Nested=inside_sync] sync TableRowExtentAt(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(uint32_t aExtent);
+ [Nested=inside_sync] sync TableColumnDescription(uint64_t aID, uint32_t aCol) returns(nsString aDescription);
+ [Nested=inside_sync] sync TableRowDescription(uint64_t aID, uint32_t aRow) returns(nsString aDescription);
+ [Nested=inside_sync] sync TableColumnSelected(uint64_t aID, uint32_t aCol) returns(bool aSelected);
+ [Nested=inside_sync] sync TableRowSelected(uint64_t aID, uint32_t aRow) returns(bool aSelected);
+ [Nested=inside_sync] sync TableCellSelected(uint64_t aID, uint32_t aRow, uint32_t aCol) returns(bool aSelected);
+ [Nested=inside_sync] sync TableSelectedCellCount(uint64_t aID) returns(uint32_t aSelectedCells);
+ [Nested=inside_sync] sync TableSelectedColumnCount(uint64_t aID) returns(uint32_t aSelectedColumns);
+ [Nested=inside_sync] sync TableSelectedRowCount(uint64_t aID) returns(uint32_t aSelectedRows);
+ [Nested=inside_sync] sync TableSelectedCells(uint64_t aID) returns(uint64_t[] aCellIDs);
+ [Nested=inside_sync] sync TableSelectedCellIndices(uint64_t aID) returns(uint32_t[] aCellIndeces);
+ [Nested=inside_sync] sync TableSelectedColumnIndices(uint64_t aID) returns(uint32_t[] aColumnIndeces);
+ [Nested=inside_sync] sync TableSelectedRowIndices(uint64_t aID) returns(uint32_t[] aRowIndeces);
+ [Nested=inside_sync] sync TableSelectColumn(uint64_t aID, uint32_t aCol);
+ [Nested=inside_sync] sync TableSelectRow(uint64_t aID, uint32_t aRow);
+ [Nested=inside_sync] sync TableUnselectColumn(uint64_t aID, uint32_t aCol);
+ [Nested=inside_sync] sync TableUnselectRow(uint64_t aID, uint32_t aRow);
+ [Nested=inside_sync] sync TableIsProbablyForLayout(uint64_t aID) returns(bool aForLayout);
+ [Nested=inside_sync] sync AtkTableColumnHeader(uint64_t aID, int32_t aCol)
+ returns(uint64_t aHeaderID, bool aOk);
+ [Nested=inside_sync] sync AtkTableRowHeader(uint64_t aID, int32_t aRow)
+ returns(uint64_t aHeaderID, bool aOk);
+
+ [Nested=inside_sync] sync SelectedItems(uint64_t aID) returns(uint64_t[] aSelectedItemIDs);
+ [Nested=inside_sync] sync SelectedItemCount(uint64_t aID) returns(uint32_t aCount);
+ [Nested=inside_sync] sync GetSelectedItem(uint64_t aID, uint32_t aIndex) returns(uint64_t aSelected, bool aOk);
+ [Nested=inside_sync] sync IsItemSelected(uint64_t aID, uint32_t aIndex) returns(bool aSelected);
+ [Nested=inside_sync] sync AddItemToSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
+ [Nested=inside_sync] sync RemoveItemFromSelection(uint64_t aID, uint32_t aIndex) returns(bool aSuccess);
+ [Nested=inside_sync] sync SelectAll(uint64_t aID) returns(bool aSuccess);
+ [Nested=inside_sync] sync UnselectAll(uint64_t aID) returns(bool aSuccess);
+
+ async TakeSelection(uint64_t aID);
+ async SetSelected(uint64_t aID, bool aSelected);
+
+ [Nested=inside_sync] sync DoAction(uint64_t aID, uint8_t aIndex) returns(bool aSuccess);
+ async DoActionAsync(uint64_t aID, uint8_t aIndex);
+ [Nested=inside_sync] sync ActionCount(uint64_t aID) returns(uint8_t aCount);
+ [Nested=inside_sync] sync ActionNameAt(uint64_t aID, uint8_t aIndex) returns(nsString aName);
+ [Nested=inside_sync] sync AccessKey(uint64_t aID) returns(uint32_t aKey, uint32_t aModifierMask);
+ [Nested=inside_sync] sync AtkKeyBinding(uint64_t aID) returns(nsString aResult);
+
+ [Nested=inside_sync] sync CurValue(uint64_t aID) returns(double aValue);
+ [Nested=inside_sync] sync SetCurValue(uint64_t aID, double aValue) returns(bool aRetVal);
+ [Nested=inside_sync] sync MinValue(uint64_t aID) returns(double aValue);
+ [Nested=inside_sync] sync MaxValue(uint64_t aID) returns(double aValue);
+ [Nested=inside_sync] sync Step(uint64_t aID) returns(double aStep);
+
+ async TakeFocus(uint64_t aID);
+
+ [Nested=inside_sync] sync Language(uint64_t aID) returns(nsString aLocale);
+ [Nested=inside_sync] sync DocType(uint64_t aID) returns(nsString aType);
+ [Nested=inside_sync] sync Title(uint64_t aID) returns(nsString aTitle);
+ [Nested=inside_sync] sync MimeType(uint64_t aID) returns(nsString aMime);
+ [Nested=inside_sync] sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
+
+ [Nested=inside_sync] sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich)
+ returns(nullable PDocAccessible aResultDoc, uint64_t aResultID);
+
+ [Nested=inside_sync] sync Extents(uint64_t aID, bool aNeedsScreenCoords)
+ returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+ [Nested=inside_sync] sync ExtentsInCSSPixels(uint64_t aID)
+ returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+ [Nested=inside_sync] sync DOMNodeID(uint64_t aID) returns(nsString aDOMNodeID);
+
+ /*
+ * Verify the cache. Used for testing purposes.
+ */
+ async VerifyCache(uint64_t aID, uint64_t aCacheDomain, AccAttributes aFields);
+
+};
+
+}
+}
diff --git a/accessible/ipc/other/RemoteAccessible.cpp b/accessible/ipc/other/RemoteAccessible.cpp
new file mode 100644
index 0000000000..d720ff2530
--- /dev/null
+++ b/accessible/ipc/other/RemoteAccessible.cpp
@@ -0,0 +1,989 @@
+/* -*- 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 "RemoteAccessible.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "DocAccessible.h"
+#include "AccAttributes.h"
+#include "mozilla/a11y/DocManager.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/Unused.h"
+#include "mozilla/a11y/Platform.h"
+#include "Relation.h"
+#include "RelationType.h"
+#include "mozilla/a11y/Role.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+
+namespace mozilla {
+namespace a11y {
+
+uint64_t RemoteAccessible::State() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::State();
+ }
+ uint64_t state = 0;
+ Unused << mDoc->SendState(mID, &state);
+ return state;
+}
+
+uint64_t RemoteAccessible::NativeState() const {
+ uint64_t state = 0;
+ Unused << mDoc->SendNativeState(mID, &state);
+ return state;
+}
+
+ENameValueFlag RemoteAccessible::Name(nsString& aName) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Name(aName);
+ }
+
+ uint32_t flag = 0;
+ Unused << mDoc->SendName(mID, &aName, &flag);
+ return static_cast<ENameValueFlag>(flag);
+}
+
+void RemoteAccessible::Value(nsString& aValue) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ RemoteAccessibleBase<RemoteAccessible>::Value(aValue);
+ return;
+ }
+
+ Unused << mDoc->SendValue(mID, &aValue);
+}
+
+void RemoteAccessible::Help(nsString& aHelp) const {
+ Unused << mDoc->SendHelp(mID, &aHelp);
+}
+
+void RemoteAccessible::Description(nsString& aDesc) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ RemoteAccessibleBase<RemoteAccessible>::Description(aDesc);
+ return;
+ }
+
+ Unused << mDoc->SendDescription(mID, &aDesc);
+}
+
+already_AddRefed<AccAttributes> RemoteAccessible::Attributes() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Attributes();
+ }
+ RefPtr<AccAttributes> attrs;
+ Unused << mDoc->SendAttributes(mID, &attrs);
+ return attrs.forget();
+}
+
+Relation RemoteAccessible::RelationByType(RelationType aType) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::RelationByType(aType);
+ }
+
+ nsTArray<uint64_t> targetIDs;
+ Unused << mDoc->SendRelationByType(mID, static_cast<uint32_t>(aType),
+ &targetIDs);
+ return Relation(new RemoteAccIterator(std::move(targetIDs), Document()));
+}
+
+void RemoteAccessible::Relations(
+ nsTArray<RelationType>* aTypes,
+ nsTArray<nsTArray<RemoteAccessible*>>* 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<RemoteAccessible*> targets(targetCount);
+ for (size_t j = 0; j < targetCount; j++) {
+ if (RemoteAccessible* proxy =
+ mDoc->GetAccessible(ipcRelations[i].Targets()[j])) {
+ targets.AppendElement(proxy);
+ }
+ }
+
+ if (targets.IsEmpty()) continue;
+
+ aTargetSets->AppendElement(std::move(targets));
+ aTypes->AppendElement(static_cast<RelationType>(type));
+ }
+}
+
+bool RemoteAccessible::IsSearchbox() const {
+ bool retVal = false;
+ Unused << mDoc->SendIsSearchbox(mID, &retVal);
+ return retVal;
+}
+
+nsAtom* RemoteAccessible::LandmarkRole() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::LandmarkRole();
+ }
+
+ nsString landmark;
+ Unused << mDoc->SendLandmarkRole(mID, &landmark);
+ return NS_GetStaticAtom(landmark);
+}
+
+GroupPos RemoteAccessible::GroupPosition() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::GroupPosition();
+ }
+
+ GroupPos groupPos;
+ Unused << mDoc->SendGroupPosition(mID, &groupPos.level, &groupPos.setSize,
+ &groupPos.posInSet);
+ return groupPos;
+}
+
+void RemoteAccessible::ScrollToPoint(uint32_t aScrollType, int32_t aX,
+ int32_t aY) {
+ Unused << mDoc->SendScrollToPoint(mID, aScrollType, aX, aY);
+}
+
+void RemoteAccessible::Announce(const nsString& aAnnouncement,
+ uint16_t aPriority) {
+ Unused << mDoc->SendAnnounce(mID, aAnnouncement, aPriority);
+}
+
+int32_t RemoteAccessible::CaretLineNumber() {
+ int32_t line = -1;
+ Unused << mDoc->SendCaretOffset(mID, &line);
+ return line;
+}
+
+int32_t RemoteAccessible::CaretOffset() const {
+ int32_t offset = 0;
+ Unused << mDoc->SendCaretOffset(mID, &offset);
+ return offset;
+}
+
+uint32_t RemoteAccessible::CharacterCount() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::CharacterCount();
+ }
+ int32_t count = 0;
+ Unused << mDoc->SendCharacterCount(mID, &count);
+ return count;
+}
+
+int32_t RemoteAccessible::SelectionCount() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::SelectionCount();
+ }
+ int32_t count = 0;
+ Unused << mDoc->SendSelectionCount(mID, &count);
+ return count;
+}
+
+void RemoteAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
+ nsAString& aText) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextSubstring(
+ aStartOffset, aEndOffset, aText);
+ }
+
+ bool valid;
+ nsString text;
+ Unused << mDoc->SendTextSubstring(mID, aStartOffset, aEndOffset, &text,
+ &valid);
+ aText = std::move(text);
+}
+
+void RemoteAccessible::TextAfterOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset,
+ int32_t* aEndOffset, nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextAfterOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ nsString text;
+ Unused << mDoc->SendGetTextAfterOffset(mID, aOffset, aBoundaryType, &text,
+ aStartOffset, aEndOffset);
+ aText = std::move(text);
+}
+
+void RemoteAccessible::TextAtOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset, int32_t* aEndOffset,
+ nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextAtOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ nsString text;
+ Unused << mDoc->SendGetTextAtOffset(mID, aOffset, aBoundaryType, &text,
+ aStartOffset, aEndOffset);
+ aText = std::move(text);
+}
+
+void RemoteAccessible::TextBeforeOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset,
+ int32_t* aEndOffset, nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextBeforeOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ nsString text;
+ Unused << mDoc->SendGetTextBeforeOffset(mID, aOffset, aBoundaryType, &text,
+ aStartOffset, aEndOffset);
+ aText = std::move(text);
+}
+
+char16_t RemoteAccessible::CharAt(int32_t aOffset) {
+ uint16_t retval = 0;
+ Unused << mDoc->SendCharAt(mID, aOffset, &retval);
+ return static_cast<char16_t>(retval);
+}
+
+already_AddRefed<AccAttributes> RemoteAccessible::TextAttributes(
+ bool aIncludeDefAttrs, int32_t aOffset, int32_t* aStartOffset,
+ int32_t* aEndOffset) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextAttributes(
+ aIncludeDefAttrs, aOffset, aStartOffset, aEndOffset);
+ }
+ RefPtr<AccAttributes> attrs;
+ Unused << mDoc->SendTextAttributes(mID, aIncludeDefAttrs, aOffset, &attrs,
+ aStartOffset, aEndOffset);
+ return attrs.forget();
+}
+
+already_AddRefed<AccAttributes> RemoteAccessible::DefaultTextAttributes() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::DefaultTextAttributes();
+ }
+ RefPtr<AccAttributes> attrs;
+ Unused << mDoc->SendDefaultTextAttributes(mID, &attrs);
+ return attrs.forget();
+}
+
+LayoutDeviceIntRect RemoteAccessible::TextBounds(int32_t aStartOffset,
+ int32_t aEndOffset,
+ uint32_t aCoordType) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ MOZ_ASSERT(IsHyperText(), "is not hypertext?");
+ return RemoteAccessibleBase<RemoteAccessible>::TextBounds(
+ aStartOffset, aEndOffset, aCoordType);
+ }
+
+ LayoutDeviceIntRect rect;
+ Unused << mDoc->SendTextBounds(mID, aStartOffset, aEndOffset, aCoordType,
+ &rect);
+ return rect;
+}
+
+LayoutDeviceIntRect RemoteAccessible::CharBounds(int32_t aOffset,
+ uint32_t aCoordType) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ MOZ_ASSERT(IsHyperText(), "is not hypertext?");
+ return RemoteAccessibleBase<RemoteAccessible>::CharBounds(aOffset,
+ aCoordType);
+ }
+
+ LayoutDeviceIntRect rect;
+ Unused << mDoc->SendCharBounds(mID, aOffset, aCoordType, &rect);
+ return rect;
+}
+
+int32_t RemoteAccessible::OffsetAtPoint(int32_t aX, int32_t aY,
+ uint32_t aCoordType) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ MOZ_ASSERT(IsHyperText(), "is not hypertext?");
+ return RemoteAccessibleBase<RemoteAccessible>::OffsetAtPoint(aX, aY,
+ aCoordType);
+ }
+
+ int32_t retVal = -1;
+ Unused << mDoc->SendOffsetAtPoint(mID, aX, aY, aCoordType, &retVal);
+ return retVal;
+}
+
+bool RemoteAccessible::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 RemoteAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
+ int32_t aStartOffset,
+ int32_t aEndOffset) {
+ bool retVal = false;
+ Unused << mDoc->SendSetSelectionBoundsAt(mID, aSelectionNum, aStartOffset,
+ aEndOffset, &retVal);
+ return retVal;
+}
+
+bool RemoteAccessible::AddToSelection(int32_t aStartOffset,
+ int32_t aEndOffset) {
+ bool retVal = false;
+ Unused << mDoc->SendAddToSelection(mID, aStartOffset, aEndOffset, &retVal);
+ return retVal;
+}
+
+bool RemoteAccessible::RemoveFromSelection(int32_t aSelectionNum) {
+ bool retVal = false;
+ Unused << mDoc->SendRemoveFromSelection(mID, aSelectionNum, &retVal);
+ return retVal;
+}
+
+void RemoteAccessible::ScrollSubstringTo(int32_t aStartOffset,
+ int32_t aEndOffset,
+ uint32_t aScrollType) {
+ Unused << mDoc->SendScrollSubstringTo(mID, aStartOffset, aEndOffset,
+ aScrollType);
+}
+
+void RemoteAccessible::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 RemoteAccessible::Text(nsString* aText) {
+ Unused << mDoc->SendText(mID, aText);
+}
+
+void RemoteAccessible::ReplaceText(const nsString& aText) {
+ Unused << mDoc->SendReplaceText(mID, aText);
+}
+
+bool RemoteAccessible::InsertText(const nsString& aText, int32_t aPosition) {
+ bool valid;
+ Unused << mDoc->SendInsertText(mID, aText, aPosition, &valid);
+ return valid;
+}
+
+bool RemoteAccessible::CopyText(int32_t aStartPos, int32_t aEndPos) {
+ bool valid;
+ Unused << mDoc->SendCopyText(mID, aStartPos, aEndPos, &valid);
+ return valid;
+}
+
+bool RemoteAccessible::CutText(int32_t aStartPos, int32_t aEndPos) {
+ bool valid;
+ Unused << mDoc->SendCutText(mID, aStartPos, aEndPos, &valid);
+ return valid;
+}
+
+bool RemoteAccessible::DeleteText(int32_t aStartPos, int32_t aEndPos) {
+ bool valid;
+ Unused << mDoc->SendDeleteText(mID, aStartPos, aEndPos, &valid);
+ return valid;
+}
+
+bool RemoteAccessible::PasteText(int32_t aPosition) {
+ bool valid;
+ Unused << mDoc->SendPasteText(mID, aPosition, &valid);
+ return valid;
+}
+
+uint32_t RemoteAccessible::StartOffset() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::StartOffset();
+ }
+ uint32_t retVal = 0;
+ bool ok;
+ Unused << mDoc->SendStartOffset(mID, &retVal, &ok);
+ return retVal;
+}
+
+uint32_t RemoteAccessible::EndOffset() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::EndOffset();
+ }
+ bool ok;
+ uint32_t retVal = 0;
+ Unused << mDoc->SendEndOffset(mID, &retVal, &ok);
+ return retVal;
+}
+
+bool RemoteAccessible::IsLinkValid() {
+ bool retVal = false;
+ Unused << mDoc->SendIsLinkValid(mID, &retVal);
+ return retVal;
+}
+
+uint32_t RemoteAccessible::AnchorCount(bool* aOk) {
+ uint32_t retVal = 0;
+ Unused << mDoc->SendAnchorCount(mID, &retVal, aOk);
+ return retVal;
+}
+
+void RemoteAccessible::AnchorURIAt(uint32_t aIndex, nsCString& aURI,
+ bool* aOk) {
+ Unused << mDoc->SendAnchorURIAt(mID, aIndex, &aURI, aOk);
+}
+
+RemoteAccessible* RemoteAccessible::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 RemoteAccessible::LinkCount() {
+ uint32_t retVal = 0;
+ Unused << mDoc->SendLinkCount(mID, &retVal);
+ return retVal;
+}
+
+RemoteAccessible* RemoteAccessible::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 RemoteAccessible::LinkIndexAtOffset(uint32_t aOffset) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::LinkIndexAtOffset(aOffset);
+ }
+ int32_t retVal = -1;
+ Unused << mDoc->SendLinkIndexAtOffset(mID, aOffset, &retVal);
+ return retVal;
+}
+
+RemoteAccessible* RemoteAccessible::TableOfACell() {
+ uint64_t tableID = 0;
+ bool ok = false;
+ Unused << mDoc->SendTableOfACell(mID, &tableID, &ok);
+ return ok ? mDoc->GetAccessible(tableID) : nullptr;
+}
+
+uint32_t RemoteAccessible::ColIdx() {
+ uint32_t index = 0;
+ Unused << mDoc->SendColIdx(mID, &index);
+ return index;
+}
+
+uint32_t RemoteAccessible::RowIdx() {
+ uint32_t index = 0;
+ Unused << mDoc->SendRowIdx(mID, &index);
+ return index;
+}
+
+void RemoteAccessible::GetColRowExtents(uint32_t* aColIdx, uint32_t* aRowIdx,
+ uint32_t* aColExtent,
+ uint32_t* aRowExtent) {
+ Unused << mDoc->SendGetColRowExtents(mID, aColIdx, aRowIdx, aColExtent,
+ aRowExtent);
+}
+
+void RemoteAccessible::GetPosition(uint32_t* aRowIdx, uint32_t* aColIdx) {
+ Unused << mDoc->SendGetPosition(mID, aRowIdx, aColIdx);
+}
+
+uint32_t RemoteAccessible::ColExtent() {
+ uint32_t extent = 0;
+ Unused << mDoc->SendColExtent(mID, &extent);
+ return extent;
+}
+
+uint32_t RemoteAccessible::RowExtent() {
+ uint32_t extent = 0;
+ Unused << mDoc->SendRowExtent(mID, &extent);
+ return extent;
+}
+
+void RemoteAccessible::ColHeaderCells(nsTArray<RemoteAccessible*>* 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 RemoteAccessible::RowHeaderCells(nsTArray<RemoteAccessible*>* 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 RemoteAccessible::IsCellSelected() {
+ bool selected = false;
+ Unused << mDoc->SendIsCellSelected(mID, &selected);
+ return selected;
+}
+
+RemoteAccessible* RemoteAccessible::TableCaption() {
+ uint64_t captionID = 0;
+ bool ok = false;
+ Unused << mDoc->SendTableCaption(mID, &captionID, &ok);
+ return ok ? mDoc->GetAccessible(captionID) : nullptr;
+}
+
+void RemoteAccessible::TableSummary(nsString& aSummary) {
+ Unused << mDoc->SendTableSummary(mID, &aSummary);
+}
+
+uint32_t RemoteAccessible::TableColumnCount() {
+ uint32_t count = 0;
+ Unused << mDoc->SendTableColumnCount(mID, &count);
+ return count;
+}
+
+uint32_t RemoteAccessible::TableRowCount() {
+ uint32_t count = 0;
+ Unused << mDoc->SendTableRowCount(mID, &count);
+ return count;
+}
+
+RemoteAccessible* RemoteAccessible::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 RemoteAccessible::TableCellIndexAt(uint32_t aRow, uint32_t aCol) {
+ int32_t index = 0;
+ Unused << mDoc->SendTableCellIndexAt(mID, aRow, aCol, &index);
+ return index;
+}
+
+int32_t RemoteAccessible::TableColumnIndexAt(uint32_t aCellIndex) {
+ int32_t index = 0;
+ Unused << mDoc->SendTableColumnIndexAt(mID, aCellIndex, &index);
+ return index;
+}
+
+int32_t RemoteAccessible::TableRowIndexAt(uint32_t aCellIndex) {
+ int32_t index = 0;
+ Unused << mDoc->SendTableRowIndexAt(mID, aCellIndex, &index);
+ return index;
+}
+
+void RemoteAccessible::TableRowAndColumnIndicesAt(uint32_t aCellIndex,
+ int32_t* aRow,
+ int32_t* aCol) {
+ Unused << mDoc->SendTableRowAndColumnIndicesAt(mID, aCellIndex, aRow, aCol);
+}
+
+uint32_t RemoteAccessible::TableColumnExtentAt(uint32_t aRow, uint32_t aCol) {
+ uint32_t extent = 0;
+ Unused << mDoc->SendTableColumnExtentAt(mID, aRow, aCol, &extent);
+ return extent;
+}
+
+uint32_t RemoteAccessible::TableRowExtentAt(uint32_t aRow, uint32_t aCol) {
+ uint32_t extent = 0;
+ Unused << mDoc->SendTableRowExtentAt(mID, aRow, aCol, &extent);
+ return extent;
+}
+
+void RemoteAccessible::TableColumnDescription(uint32_t aCol,
+ nsString& aDescription) {
+ Unused << mDoc->SendTableColumnDescription(mID, aCol, &aDescription);
+}
+
+void RemoteAccessible::TableRowDescription(uint32_t aRow,
+ nsString& aDescription) {
+ Unused << mDoc->SendTableRowDescription(mID, aRow, &aDescription);
+}
+
+bool RemoteAccessible::TableColumnSelected(uint32_t aCol) {
+ bool selected = false;
+ Unused << mDoc->SendTableColumnSelected(mID, aCol, &selected);
+ return selected;
+}
+
+bool RemoteAccessible::TableRowSelected(uint32_t aRow) {
+ bool selected = false;
+ Unused << mDoc->SendTableRowSelected(mID, aRow, &selected);
+ return selected;
+}
+
+bool RemoteAccessible::TableCellSelected(uint32_t aRow, uint32_t aCol) {
+ bool selected = false;
+ Unused << mDoc->SendTableCellSelected(mID, aRow, aCol, &selected);
+ return selected;
+}
+
+uint32_t RemoteAccessible::TableSelectedCellCount() {
+ uint32_t count = 0;
+ Unused << mDoc->SendTableSelectedCellCount(mID, &count);
+ return count;
+}
+
+uint32_t RemoteAccessible::TableSelectedColumnCount() {
+ uint32_t count = 0;
+ Unused << mDoc->SendTableSelectedColumnCount(mID, &count);
+ return count;
+}
+
+uint32_t RemoteAccessible::TableSelectedRowCount() {
+ uint32_t count = 0;
+ Unused << mDoc->SendTableSelectedRowCount(mID, &count);
+ return count;
+}
+
+void RemoteAccessible::TableSelectedCells(
+ nsTArray<RemoteAccessible*>* 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 RemoteAccessible::TableSelectedCellIndices(
+ nsTArray<uint32_t>* aCellIndices) {
+ Unused << mDoc->SendTableSelectedCellIndices(mID, aCellIndices);
+}
+
+void RemoteAccessible::TableSelectedColumnIndices(
+ nsTArray<uint32_t>* aColumnIndices) {
+ Unused << mDoc->SendTableSelectedColumnIndices(mID, aColumnIndices);
+}
+
+void RemoteAccessible::TableSelectedRowIndices(
+ nsTArray<uint32_t>* aRowIndices) {
+ Unused << mDoc->SendTableSelectedRowIndices(mID, aRowIndices);
+}
+
+void RemoteAccessible::TableSelectColumn(uint32_t aCol) {
+ Unused << mDoc->SendTableSelectColumn(mID, aCol);
+}
+
+void RemoteAccessible::TableSelectRow(uint32_t aRow) {
+ Unused << mDoc->SendTableSelectRow(mID, aRow);
+}
+
+void RemoteAccessible::TableUnselectColumn(uint32_t aCol) {
+ Unused << mDoc->SendTableUnselectColumn(mID, aCol);
+}
+
+void RemoteAccessible::TableUnselectRow(uint32_t aRow) {
+ Unused << mDoc->SendTableUnselectRow(mID, aRow);
+}
+
+bool RemoteAccessible::TableIsProbablyForLayout() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TableIsProbablyForLayout();
+ }
+
+ bool forLayout = false;
+ Unused << mDoc->SendTableIsProbablyForLayout(mID, &forLayout);
+ return forLayout;
+}
+
+RemoteAccessible* RemoteAccessible::AtkTableColumnHeader(int32_t aCol) {
+ uint64_t headerID = 0;
+ bool ok = false;
+ Unused << mDoc->SendAtkTableColumnHeader(mID, aCol, &headerID, &ok);
+ return ok ? mDoc->GetAccessible(headerID) : nullptr;
+}
+
+RemoteAccessible* RemoteAccessible::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 RemoteAccessible::SelectedItems(nsTArray<Accessible*>* aSelectedItems) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ RemoteAccessibleBase<RemoteAccessible>::SelectedItems(aSelectedItems);
+ return;
+ }
+
+ 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 RemoteAccessible::SelectedItemCount() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::SelectedItemCount();
+ }
+
+ uint32_t count = 0;
+ Unused << mDoc->SendSelectedItemCount(mID, &count);
+ return count;
+}
+
+Accessible* RemoteAccessible::GetSelectedItem(uint32_t aIndex) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::GetSelectedItem(aIndex);
+ }
+
+ uint64_t selectedItemID = 0;
+ bool ok = false;
+ Unused << mDoc->SendGetSelectedItem(mID, aIndex, &selectedItemID, &ok);
+ return ok ? mDoc->GetAccessible(selectedItemID) : nullptr;
+}
+
+bool RemoteAccessible::IsItemSelected(uint32_t aIndex) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::IsItemSelected(aIndex);
+ }
+
+ bool selected = false;
+ Unused << mDoc->SendIsItemSelected(mID, aIndex, &selected);
+ return selected;
+}
+
+bool RemoteAccessible::AddItemToSelection(uint32_t aIndex) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::AddItemToSelection(aIndex);
+ }
+
+ bool success = false;
+ Unused << mDoc->SendAddItemToSelection(mID, aIndex, &success);
+ return success;
+}
+
+bool RemoteAccessible::RemoveItemFromSelection(uint32_t aIndex) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::RemoveItemFromSelection(
+ aIndex);
+ }
+
+ bool success = false;
+ Unused << mDoc->SendRemoveItemFromSelection(mID, aIndex, &success);
+ return success;
+}
+
+bool RemoteAccessible::SelectAll() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::SelectAll();
+ }
+
+ bool success = false;
+ Unused << mDoc->SendSelectAll(mID, &success);
+ return success;
+}
+
+bool RemoteAccessible::UnselectAll() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::UnselectAll();
+ }
+
+ bool success = false;
+ Unused << mDoc->SendUnselectAll(mID, &success);
+ return success;
+}
+
+bool RemoteAccessible::DoAction(uint8_t aIndex) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::DoAction(aIndex);
+ }
+
+ bool success = false;
+ Unused << mDoc->SendDoAction(mID, aIndex, &success);
+ return success;
+}
+
+uint8_t RemoteAccessible::ActionCount() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::ActionCount();
+ }
+
+ uint8_t count = 0;
+ Unused << mDoc->SendActionCount(mID, &count);
+ return count;
+}
+
+void RemoteAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ RemoteAccessibleBase<RemoteAccessible>::ActionNameAt(aIndex, aName);
+ return;
+ }
+
+ nsAutoString name;
+ Unused << mDoc->SendActionNameAt(mID, aIndex, &name);
+
+ aName.Assign(name);
+}
+
+KeyBinding RemoteAccessible::AccessKey() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::AccessKey();
+ }
+ uint32_t key = 0;
+ uint32_t modifierMask = 0;
+ Unused << mDoc->SendAccessKey(mID, &key, &modifierMask);
+ return KeyBinding(key, modifierMask);
+}
+
+void RemoteAccessible::AtkKeyBinding(nsString& aBinding) {
+ Unused << mDoc->SendAtkKeyBinding(mID, &aBinding);
+}
+
+double RemoteAccessible::CurValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::CurValue();
+ }
+
+ double val = UnspecifiedNaN<double>();
+ Unused << mDoc->SendCurValue(mID, &val);
+ return val;
+}
+
+bool RemoteAccessible::SetCurValue(double aValue) {
+ bool success = false;
+ Unused << mDoc->SendSetCurValue(mID, aValue, &success);
+ return success;
+}
+
+double RemoteAccessible::MinValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::MinValue();
+ }
+
+ double val = UnspecifiedNaN<double>();
+ Unused << mDoc->SendMinValue(mID, &val);
+ return val;
+}
+
+double RemoteAccessible::MaxValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::MaxValue();
+ }
+
+ double val = UnspecifiedNaN<double>();
+ Unused << mDoc->SendMaxValue(mID, &val);
+ return val;
+}
+
+double RemoteAccessible::Step() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Step();
+ }
+
+ double step = UnspecifiedNaN<double>();
+ Unused << mDoc->SendStep(mID, &step);
+ return step;
+}
+
+Accessible* RemoteAccessible::ChildAtPoint(
+ int32_t aX, int32_t aY, LocalAccessible::EWhichChildAtPoint aWhichChild) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::ChildAtPoint(aX, aY,
+ aWhichChild);
+ }
+
+ RemoteAccessible* target = this;
+ do {
+ if (target->IsOuterDoc()) {
+ if (target->ChildCount() == 0) {
+ // Return the OuterDoc if the requested point is within its bounds.
+ LayoutDeviceIntRect rect = target->Bounds();
+ if (rect.Contains(aX, aY)) {
+ return target;
+ }
+ return nullptr;
+ }
+ DocAccessibleParent* childDoc = target->RemoteChildAt(0)->AsDoc();
+ MOZ_ASSERT(childDoc);
+ if (childDoc->IsTopLevelInContentProcess()) {
+ // This is an OOP iframe. Remote calls can only work within their
+ // process, so they stop at OOP iframes.
+ if (aWhichChild == Accessible::EWhichChildAtPoint::DirectChild) {
+ // Return the child document if it's within the bounds of the iframe.
+ LayoutDeviceIntRect docRect = target->Bounds();
+ if (docRect.Contains(aX, aY)) {
+ return childDoc;
+ }
+ return nullptr;
+ }
+ // We must continue the search from the child document.
+ target = childDoc;
+ }
+ }
+ PDocAccessibleParent* resultDoc = nullptr;
+ uint64_t resultID = 0;
+ Unused << target->mDoc->SendChildAtPoint(target->mID, aX, aY,
+ static_cast<uint32_t>(aWhichChild),
+ &resultDoc, &resultID);
+ // If resultDoc is null, this means there is no child at this point.
+ auto useDoc = static_cast<DocAccessibleParent*>(resultDoc);
+ target = resultDoc ? useDoc->GetAccessible(resultID) : nullptr;
+ } while (target && target->IsOuterDoc() &&
+ aWhichChild == Accessible::EWhichChildAtPoint::DeepestChild);
+ return target;
+}
+
+LayoutDeviceIntRect RemoteAccessible::Bounds() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Bounds();
+ }
+
+ LayoutDeviceIntRect rect;
+ Unused << mDoc->SendExtents(mID, false, &(rect.x), &(rect.y), &(rect.width),
+ &(rect.height));
+ return rect;
+}
+
+nsIntRect RemoteAccessible::BoundsInCSSPixels() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::BoundsInCSSPixels();
+ }
+
+ nsIntRect rect;
+ Unused << mDoc->SendExtentsInCSSPixels(mID, &rect.x, &rect.y, &rect.width,
+ &rect.height);
+ return rect;
+}
+
+void RemoteAccessible::Language(nsString& aLocale) {
+ Unused << mDoc->SendLanguage(mID, &aLocale);
+}
+
+void RemoteAccessible::DocType(nsString& aType) {
+ Unused << mDoc->SendDocType(mID, &aType);
+}
+
+void RemoteAccessible::Title(nsString& aTitle) {
+ Unused << mDoc->SendTitle(mID, &aTitle);
+}
+
+void RemoteAccessible::MimeType(nsString aMime) {
+ Unused << mDoc->SendMimeType(mID, &aMime);
+}
+
+void RemoteAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+ nsString& aMimeType) {
+ Unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType);
+}
+
+void RemoteAccessible::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 RemoteAccessible::DOMNodeID(nsString& aID) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::DOMNodeID(aID);
+ }
+ Unused << mDoc->SendDOMNodeID(mID, &aID);
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/other/RemoteAccessible.h b/accessible/ipc/other/RemoteAccessible.h
new file mode 100644
index 0000000000..57ba4db328
--- /dev/null
+++ b/accessible/ipc/other/RemoteAccessible.h
@@ -0,0 +1,107 @@
+/* -*- 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_RemoteAccessible_h
+#define mozilla_a11y_RemoteAccessible_h
+
+#include "LocalAccessible.h"
+#include "mozilla/a11y/RemoteAccessibleBase.h"
+#include "mozilla/a11y/Role.h"
+#include "nsIAccessibleText.h"
+#include "nsIAccessibleTypes.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Functionality common across Linux and macOS for an accessibility tree node
+ * that originated in the parent process.
+ */
+class RemoteAccessible : public RemoteAccessibleBase<RemoteAccessible> {
+ public:
+ RemoteAccessible(uint64_t aID, RemoteAccessible* aParent,
+ DocAccessibleParent* aDoc, role aRole, AccType aType,
+ AccGenericType aGenericTypes, uint8_t aRoleMapEntryIndex)
+ : RemoteAccessibleBase(aID, aParent, aDoc, aRole, aType, aGenericTypes,
+ aRoleMapEntryIndex) {
+ MOZ_COUNT_CTOR(RemoteAccessible);
+ }
+
+ MOZ_COUNTED_DTOR(RemoteAccessible)
+
+#include "mozilla/a11y/RemoteAccessibleShared.h"
+
+ virtual uint32_t CharacterCount() const override;
+
+ LayoutDeviceIntRect TextBounds(
+ int32_t aStartOffset, int32_t aEndOffset,
+ uint32_t aCoordType =
+ nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) override;
+
+ LayoutDeviceIntRect CharBounds(int32_t aOffset, uint32_t aCoordType) override;
+
+ virtual already_AddRefed<AccAttributes> TextAttributes(
+ bool aIncludeDefAttrs, int32_t aOffset, int32_t* aStartOffset,
+ int32_t* aEndOffset) override;
+
+ virtual already_AddRefed<AccAttributes> DefaultTextAttributes() override;
+
+ virtual uint32_t StartOffset() override;
+ virtual uint32_t EndOffset() override;
+
+ virtual int32_t LinkIndexAtOffset(uint32_t aOffset) override;
+
+ virtual bool DoAction(uint8_t aIndex) const override;
+ virtual uint8_t ActionCount() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ virtual KeyBinding AccessKey() const override;
+
+ virtual void SelectedItems(nsTArray<Accessible*>* aSelectedItems) override;
+ virtual uint32_t SelectedItemCount() override;
+ virtual Accessible* GetSelectedItem(uint32_t aIndex) override;
+ virtual bool IsItemSelected(uint32_t aIndex) override;
+ virtual bool AddItemToSelection(uint32_t aIndex) override;
+ virtual bool RemoveItemFromSelection(uint32_t aIndex) override;
+ virtual bool SelectAll() override;
+ virtual bool UnselectAll() override;
+
+ virtual nsAtom* LandmarkRole() const override;
+
+ virtual int32_t SelectionCount() override;
+
+ using RemoteAccessibleBase<RemoteAccessible>::SelectionBoundsAt;
+ bool SelectionBoundsAt(int32_t aSelectionNum, nsString& aData,
+ int32_t* aStartOffset, int32_t* aEndOffset);
+
+ virtual bool TableIsProbablyForLayout() override;
+
+ /**
+ * Get all relations for this accessible.
+ */
+ void Relations(nsTArray<RelationType>* aTypes,
+ nsTArray<nsTArray<RemoteAccessible*>>* aTargetSets) const;
+
+ protected:
+ explicit RemoteAccessible(DocAccessibleParent* aThisAsDoc)
+ : RemoteAccessibleBase(aThisAsDoc) {
+ MOZ_COUNT_CTOR(RemoteAccessible);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// RemoteAccessible downcasting method
+
+inline RemoteAccessible* Accessible::AsRemote() {
+ return IsRemote() ? static_cast<RemoteAccessible*>(this) : nullptr;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/other/moz.build b/accessible/ipc/other/moz.build
new file mode 100644
index 0000000000..2ea27c20c8
--- /dev/null
+++ b/accessible/ipc/other/moz.build
@@ -0,0 +1,51 @@
+# -*- 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/.
+
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += [
+ "PDocAccessible.ipdl",
+ ]
+
+ EXPORTS.mozilla.a11y += [
+ "DocAccessibleChild.h",
+ "RemoteAccessible.h",
+ ]
+
+ SOURCES += [
+ "DocAccessibleChild.cpp",
+ "RemoteAccessible.cpp",
+ ]
+
+ LOCAL_INCLUDES += [
+ "../../base",
+ "../../generic",
+ "../../xpcom",
+ ]
+
+ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ LOCAL_INCLUDES += [
+ "/accessible/atk",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ LOCAL_INCLUDES += [
+ "/accessible/mac",
+ ]
+ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ LOCAL_INCLUDES += [
+ "/accessible/android",
+ ]
+ else:
+ LOCAL_INCLUDES += [
+ "/accessible/other",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/accessible/ipc/win/COMPtrTypes.cpp b/accessible/ipc/win/COMPtrTypes.cpp
new file mode 100644
index 0000000000..16b3a24cbe
--- /dev/null
+++ b/accessible/ipc/win/COMPtrTypes.cpp
@@ -0,0 +1,75 @@
+/* -*- 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 <utility>
+
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/a11y/LocalAccessible.h"
+#include "mozilla/a11y/HandlerProvider.h"
+#include "mozilla/a11y/Platform.h"
+#include "mozilla/mscom/MainThreadHandoff.h"
+#include "mozilla/mscom/Utils.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::mscom::MainThreadHandoff;
+using mozilla::mscom::ProxyUniquePtr;
+using mozilla::mscom::STAUniquePtr;
+
+namespace mozilla {
+namespace a11y {
+
+IAccessibleHolder CreateHolderFromAccessible(
+ NotNull<LocalAccessible*> aAccToWrap) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ STAUniquePtr<IAccessible> iaToProxy;
+ aAccToWrap->GetNativeInterface(mscom::getter_AddRefs(iaToProxy));
+ MOZ_DIAGNOSTIC_ASSERT(iaToProxy);
+ if (!iaToProxy) {
+ return nullptr;
+ }
+
+ static const bool useHandler =
+ Preferences::GetBool("accessibility.handler.enabled", false) &&
+ IsHandlerRegistered();
+
+ RefPtr<HandlerProvider> payload;
+ if (useHandler) {
+ payload = new HandlerProvider(IID_IAccessible,
+ mscom::ToInterceptorTargetPtr(iaToProxy));
+ }
+
+ ProxyUniquePtr<IAccessible> intercepted;
+ HRESULT hr = MainThreadHandoff::WrapInterface(
+ std::move(iaToProxy), payload,
+ (IAccessible**)mscom::getter_AddRefs(intercepted));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ return IAccessibleHolder(std::move(intercepted));
+}
+
+IHandlerControlHolder CreateHolderFromHandlerControl(
+ mscom::ProxyUniquePtr<IHandlerControl> aHandlerControl) {
+ MOZ_ASSERT(aHandlerControl);
+ MOZ_ASSERT(XRE_IsContentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!aHandlerControl) {
+ return nullptr;
+ }
+
+ return IHandlerControlHolder(std::move(aHandlerControl));
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/COMPtrTypes.h b/accessible/ipc/win/COMPtrTypes.h
new file mode 100644
index 0000000000..85c1f16f7c
--- /dev/null
+++ b/accessible/ipc/win/COMPtrTypes.h
@@ -0,0 +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/. */
+
+#ifndef mozilla_a11y_COMPtrTypes_h
+#define mozilla_a11y_COMPtrTypes_h
+
+#include "mozilla/a11y/AccessibleHandler.h"
+#include "mozilla/mscom/ActCtxResource.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/mscom/ActivationContext.h"
+#include "mozilla/mscom/COMPtrHolder.h"
+#include "mozilla/NotNull.h"
+
+#include <oleacc.h>
+
+namespace mozilla {
+namespace a11y {
+
+class MOZ_RAII IAccessibleEnvironment : public mscom::ProxyStream::Environment {
+ public:
+ IAccessibleEnvironment() = default;
+
+ bool Push() override {
+ mActCtxRgn = GetActCtx();
+ return !!mActCtxRgn;
+ }
+
+ bool Pop() override { return mActCtxRgn.Deactivate(); }
+
+ private:
+ static const mscom::ActivationContext& GetActCtx() {
+ static const mscom::ActivationContext sActCtx(
+ mscom::ActCtxResource::GetAccessibilityResource());
+ MOZ_DIAGNOSTIC_ASSERT(sActCtx);
+ return sActCtx;
+ }
+
+ private:
+ mscom::ActivationContextRegion mActCtxRgn;
+};
+
+} // namespace a11y
+
+namespace mscom {
+namespace detail {
+
+template <>
+struct EnvironmentSelector<IAccessible> {
+ typedef a11y::IAccessibleEnvironment Type;
+};
+
+} // namespace detail
+} // namespace mscom
+
+namespace a11y {
+
+typedef mozilla::mscom::COMPtrHolder<IAccessible, IID_IAccessible>
+ IAccessibleHolder;
+typedef mozilla::mscom::COMPtrHolder<IDispatch, IID_IDispatch> IDispatchHolder;
+
+class LocalAccessible;
+
+IAccessibleHolder CreateHolderFromAccessible(
+ NotNull<LocalAccessible*> aAccToWrap);
+
+typedef mozilla::mscom::COMPtrHolder<IHandlerControl, IID_IHandlerControl>
+ IHandlerControlHolder;
+
+IHandlerControlHolder CreateHolderFromHandlerControl(
+ mscom::ProxyUniquePtr<IHandlerControl> aHandlerControl);
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_COMPtrTypes_h
diff --git a/accessible/ipc/win/DocAccessibleChild.cpp b/accessible/ipc/win/DocAccessibleChild.cpp
new file mode 100644
index 0000000000..3ecffb676c
--- /dev/null
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -0,0 +1,341 @@
+/* -*- 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 "nsAccessibilityService.h"
+#include "LocalAccessible-inl.h"
+#include "mozilla/a11y/PlatformChild.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+#include "RootAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+static StaticAutoPtr<PlatformChild> sPlatformChild;
+
+DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
+ : DocAccessibleChildBase(aDoc), mEmulatedWindowHandle(nullptr) {
+ MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+ if (!sPlatformChild) {
+ sPlatformChild = new PlatformChild();
+ ClearOnShutdown(&sPlatformChild, ShutdownPhase::XPCOMShutdown);
+ }
+
+ SetManager(aManager);
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // If the cache is enabled, we don't need to care whether this is
+ // constructed in the parent process. We must still set this flag because we
+ // defer sending any events unless it is set.
+ SetConstructedInParentProcess();
+ }
+}
+
+DocAccessibleChild::~DocAccessibleChild() {
+ MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+}
+
+void DocAccessibleChild::Shutdown() {
+ if (IsConstructedInParentProcess()) {
+ DocAccessibleChildBase::Shutdown();
+ return;
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedShutdown>(this));
+ DetachDocument();
+}
+
+ipc::IPCResult DocAccessibleChild::RecvParentCOMProxy(
+ const IDispatchHolder& aParentCOMProxy) {
+ MOZ_ASSERT(!aParentCOMProxy.IsNull());
+ mParentProxy.reset(const_cast<IDispatchHolder&>(aParentCOMProxy).Release());
+ SetConstructedInParentProcess();
+
+ for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
+ mDeferredEvents[i]->Dispatch();
+ }
+
+ mDeferredEvents.Clear();
+
+ return IPC_OK();
+}
+
+ipc::IPCResult DocAccessibleChild::RecvEmulatedWindow(
+ const WindowsHandle& aEmulatedWindowHandle,
+ const IDispatchHolder& aEmulatedWindowCOMProxy) {
+ mEmulatedWindowHandle = reinterpret_cast<HWND>(aEmulatedWindowHandle);
+ if (!aEmulatedWindowCOMProxy.IsNull()) {
+ MOZ_ASSERT(!mEmulatedWindowProxy);
+ mEmulatedWindowProxy.reset(
+ const_cast<IDispatchHolder&>(aEmulatedWindowCOMProxy).Release());
+ }
+
+ return IPC_OK();
+}
+
+ipc::IPCResult DocAccessibleChild::RecvTopLevelDocCOMProxy(
+ const IAccessibleHolder& aCOMProxy) {
+ MOZ_ASSERT(!aCOMProxy.IsNull());
+ mTopLevelDocProxy.reset(const_cast<IAccessibleHolder&>(aCOMProxy).Release());
+ return IPC_OK();
+}
+
+HWND DocAccessibleChild::GetNativeWindowHandle() const {
+ if (mEmulatedWindowHandle) {
+ return mEmulatedWindowHandle;
+ }
+
+ auto browser = static_cast<dom::BrowserChild*>(Manager());
+ MOZ_ASSERT(browser);
+ // Iframe documents use the same window handle as their top level document.
+ auto topDoc = static_cast<DocAccessibleChild*>(
+ browser->GetTopLevelDocAccessibleChild());
+ MOZ_ASSERT(topDoc);
+ if (topDoc != this && topDoc->mEmulatedWindowHandle) {
+ return topDoc->mEmulatedWindowHandle;
+ }
+
+ return reinterpret_cast<HWND>(browser->GetNativeWindowHandle());
+}
+
+void DocAccessibleChild::PushDeferredEvent(UniquePtr<DeferredEvent> aEvent) {
+ DocAccessibleChild* topLevelIPCDoc = nullptr;
+
+ if (mDoc && mDoc->IsRoot()) {
+ topLevelIPCDoc = this;
+ } else {
+ auto browserChild = static_cast<dom::BrowserChild*>(Manager());
+ if (!browserChild) {
+ return;
+ }
+
+ topLevelIPCDoc = static_cast<DocAccessibleChild*>(
+ browserChild->GetTopLevelDocAccessibleChild());
+ }
+
+ if (topLevelIPCDoc) {
+ topLevelIPCDoc->mDeferredEvents.AppendElement(std::move(aEvent));
+ }
+}
+
+bool DocAccessibleChild::SendEvent(const uint64_t& aID, const uint32_t& aType) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendEvent(aID, aType);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedEvent>(this, aID, aType));
+ return false;
+}
+
+void DocAccessibleChild::MaybeSendShowEvent(ShowEventData& aData,
+ bool aFromUser) {
+ if (IsConstructedInParentProcess()) {
+ Unused << SendShowEvent(aData, aFromUser);
+ return;
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedShow>(this, aData, aFromUser));
+}
+
+bool DocAccessibleChild::SendHideEvent(const uint64_t& aRootID,
+ const bool& aFromUser) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendHideEvent(aRootID, aFromUser);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedHide>(this, aRootID, aFromUser));
+ return true;
+}
+
+bool DocAccessibleChild::SendStateChangeEvent(const uint64_t& aID,
+ const uint64_t& aState,
+ const bool& aEnabled) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendStateChangeEvent(aID, aState, aEnabled);
+ }
+
+ PushDeferredEvent(
+ MakeUnique<SerializedStateChange>(this, aID, aState, aEnabled));
+ return true;
+}
+
+LayoutDeviceIntRect DocAccessibleChild::GetCaretRectFor(const uint64_t& aID) {
+ LocalAccessible* target;
+
+ if (aID) {
+ target = reinterpret_cast<LocalAccessible*>(aID);
+ } else {
+ target = mDoc;
+ }
+
+ MOZ_ASSERT(target);
+
+ HyperTextAccessible* text = target->AsHyperText();
+ if (!text) {
+ return LayoutDeviceIntRect();
+ }
+
+ nsIWidget* widget = nullptr;
+ return text->GetCaretRect(&widget);
+}
+
+bool DocAccessibleChild::SendFocusEvent(const uint64_t& aID) {
+ return SendFocusEvent(aID, GetCaretRectFor(aID));
+}
+
+bool DocAccessibleChild::SendFocusEvent(const uint64_t& aID,
+ const LayoutDeviceIntRect& aCaretRect) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendFocusEvent(aID, aCaretRect);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedFocus>(this, aID, aCaretRect));
+ return true;
+}
+
+bool DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
+ const int32_t& aOffset,
+ const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine,
+ const int32_t& aGranularity) {
+ return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset,
+ aIsSelectionCollapsed, aIsAtEndOfLine,
+ aGranularity);
+}
+
+bool DocAccessibleChild::SendCaretMoveEvent(
+ const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect,
+ const int32_t& aOffset, const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine, const int32_t& aGranularity) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendCaretMoveEvent(
+ aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine,
+ aGranularity);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedCaretMove>(
+ this, aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine,
+ aGranularity));
+ return true;
+}
+
+bool DocAccessibleChild::SendTextChangeEvent(
+ const uint64_t& aID, const nsString& aStr, const int32_t& aStart,
+ const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser,
+ const bool aDoSync) {
+ if (IsConstructedInParentProcess()) {
+ if (aDoSync) {
+ // The AT is going to need to reenter content while the event is being
+ // dispatched synchronously.
+ return PDocAccessibleChild::SendSyncTextChangeEvent(
+ aID, aStr, aStart, aLen, aIsInsert, aFromUser);
+ }
+ return PDocAccessibleChild::SendTextChangeEvent(aID, aStr, aStart, aLen,
+ aIsInsert, aFromUser);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedTextChange>(
+ this, aID, aStr, aStart, aLen, aIsInsert, aFromUser));
+ return true;
+}
+
+bool DocAccessibleChild::SendSelectionEvent(const uint64_t& aID,
+ const uint64_t& aWidgetID,
+ const uint32_t& aType) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendSelectionEvent(aID, aWidgetID, aType);
+ }
+
+ PushDeferredEvent(
+ MakeUnique<SerializedSelection>(this, aID, aWidgetID, aType));
+ return true;
+}
+
+bool DocAccessibleChild::SendRoleChangedEvent(const a11y::role& aRole,
+ uint8_t aRoleMapEntryIndex) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendRoleChangedEvent(aRole, aRoleMapEntryIndex);
+ }
+
+ PushDeferredEvent(
+ MakeUnique<SerializedRoleChanged>(this, aRole, aRoleMapEntryIndex));
+ return true;
+}
+
+bool DocAccessibleChild::SendScrollingEvent(const uint64_t& aID,
+ const uint64_t& aType,
+ const uint32_t& aScrollX,
+ const uint32_t& aScrollY,
+ const uint32_t& aMaxScrollX,
+ const uint32_t& aMaxScrollY) {
+ if (IsConstructedInParentProcess()) {
+ return PDocAccessibleChild::SendScrollingEvent(
+ aID, aType, aScrollX, aScrollY, aMaxScrollX, aMaxScrollY);
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedScrolling>(
+ this, aID, aType, aScrollX, aScrollY, aMaxScrollX, aMaxScrollY));
+ return true;
+}
+
+bool DocAccessibleChild::ConstructChildDocInParentProcess(
+ DocAccessibleChild* aNewChildDoc, uint64_t aUniqueID, uint32_t aMsaaID) {
+ if (IsConstructedInParentProcess()) {
+ // We may send the constructor immediately
+ auto browserChild = static_cast<dom::BrowserChild*>(Manager());
+ MOZ_ASSERT(browserChild);
+ bool result = browserChild->SendPDocAccessibleConstructor(
+ aNewChildDoc, this, aUniqueID,
+ aNewChildDoc->mDoc->DocumentNode()->GetBrowsingContext(), aMsaaID,
+ IAccessibleHolder());
+ if (result) {
+ aNewChildDoc->SetConstructedInParentProcess();
+ }
+ return result;
+ }
+
+ PushDeferredEvent(MakeUnique<SerializedChildDocConstructor>(
+ aNewChildDoc, this, aUniqueID,
+ aNewChildDoc->mDoc->DocumentNode()->GetBrowsingContext(), aMsaaID));
+ return true;
+}
+
+bool DocAccessibleChild::SendBindChildDoc(DocAccessibleChild* aChildDoc,
+ const uint64_t& aNewParentID) {
+ if (IsConstructedInParentProcess()) {
+ return DocAccessibleChildBase::SendBindChildDoc(aChildDoc, aNewParentID);
+ }
+
+ PushDeferredEvent(
+ MakeUnique<SerializedBindChildDoc>(this, aChildDoc, aNewParentID));
+ return true;
+}
+
+ipc::IPCResult DocAccessibleChild::RecvRestoreFocus() {
+ FocusMgr()->ForceFocusEvent();
+ return IPC_OK();
+}
+
+void DocAccessibleChild::SetEmbedderOnBridge(dom::BrowserBridgeChild* aBridge,
+ uint64_t aID) {
+ DocAccessibleChild* doc = aID ? this : nullptr;
+ if (IsConstructedInParentProcess()) {
+ MOZ_ASSERT(CanSend());
+ aBridge->SetEmbedderAccessible(doc, aID);
+ return;
+ }
+ // Even though this doesn't fire an event, we must ensure this is sent in
+ // the correct order with insertions/removals, which are deferred until
+ // we are notified about parent process construction. Otherwise, the
+ // parent process might bind a child document to the wrong accessible if
+ // ids get reused.
+ PushDeferredEvent(MakeUnique<SerializedSetEmbedder>(aBridge, doc, aID));
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/DocAccessibleChild.h b/accessible/ipc/win/DocAccessibleChild.h
new file mode 100644
index 0000000000..2f4eef00d7
--- /dev/null
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -0,0 +1,392 @@
+/* -*- 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/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/BrowserChild.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:
+ DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager);
+ ~DocAccessibleChild();
+
+ virtual void Shutdown() override;
+
+ virtual ipc::IPCResult RecvParentCOMProxy(
+ const IDispatchHolder& aParentCOMProxy) override;
+ virtual ipc::IPCResult RecvEmulatedWindow(
+ const WindowsHandle& aEmulatedWindowHandle,
+ const IDispatchHolder& aEmulatedWindowCOMProxy) override;
+ virtual ipc::IPCResult RecvTopLevelDocCOMProxy(
+ const IAccessibleHolder& aCOMProxy) override;
+ virtual ipc::IPCResult RecvRestoreFocus() override;
+
+ HWND GetNativeWindowHandle() const;
+ IDispatch* GetEmulatedWindowIAccessible() const {
+ return mEmulatedWindowProxy.get();
+ }
+
+ IDispatch* GetParentIAccessible() const { return mParentProxy.get(); }
+ IAccessible* GetTopLevelDocIAccessible() const {
+ return mTopLevelDocProxy.get();
+ }
+
+ bool SendEvent(const uint64_t& aID, const uint32_t& type);
+ bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser);
+ bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState,
+ const bool& aEnabled);
+ bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset,
+ const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine,
+ const int32_t& aGranularity);
+ bool SendCaretMoveEvent(const uint64_t& aID,
+ const LayoutDeviceIntRect& aCaretRect,
+ const int32_t& aOffset,
+ const bool& aIsSelectionCollapsed,
+ const bool& aIsAtEndOfLine,
+ const int32_t& aGranularity);
+ bool SendFocusEvent(const uint64_t& aID);
+ bool SendFocusEvent(const uint64_t& aID,
+ const LayoutDeviceIntRect& aCaretRect);
+ bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr,
+ const int32_t& aStart, const uint32_t& aLen,
+ const bool& aIsInsert, const bool& aFromUser,
+ const bool aDoSync = false);
+ bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID,
+ const uint32_t& aType);
+ bool SendRoleChangedEvent(const a11y::role& aRole,
+ uint8_t aRoleMapEntryIndex);
+ bool SendScrollingEvent(const uint64_t& aID, const uint64_t& aType,
+ const uint32_t& aScrollX, const uint32_t& aScrollY,
+ const uint32_t& aMaxScrollX,
+ const uint32_t& aMaxScrollY);
+
+ bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc,
+ uint64_t aUniqueID, uint32_t aMsaaID);
+
+ bool SendBindChildDoc(DocAccessibleChild* aChildDoc,
+ const uint64_t& aNewParentID);
+
+ /**
+ * Set the embedder accessible on a BrowserBridgeChild to an accessible in
+ * this document.
+ * Sending this will be deferred if this DocAccessibleChild hasn't been
+ * constructed in the parent process yet.
+ */
+ void SetEmbedderOnBridge(dom::BrowserBridgeChild* aBridge, uint64_t aID);
+
+ protected:
+ virtual void MaybeSendShowEvent(ShowEventData& aData,
+ bool aFromUser) override;
+
+ private:
+ void RemoveDeferredConstructor();
+
+ LayoutDeviceIntRect GetCaretRectFor(const uint64_t& aID);
+
+ /**
+ * DocAccessibleChild should not fire events until it has asynchronously
+ * received the COM proxy for its parent. OTOH, content a11y may still be
+ * attempting to fire events during this window of time. If this object does
+ * not yet have its parent proxy, instead of immediately sending the events to
+ * our parent, we enqueue them to mDeferredEvents. As soon as
+ * RecvParentCOMProxy is called, we play back mDeferredEvents.
+ */
+ struct DeferredEvent {
+ void Dispatch() { Dispatch(mTarget); }
+
+ virtual ~DeferredEvent() {}
+
+ protected:
+ explicit DeferredEvent(DocAccessibleChild* aTarget) : mTarget(aTarget) {}
+
+ virtual void Dispatch(DocAccessibleChild* aIPCDoc) = 0;
+
+ private:
+ DocAccessibleChild* mTarget;
+ };
+
+ void PushDeferredEvent(UniquePtr<DeferredEvent> aEvent);
+
+ struct SerializedShow final : public DeferredEvent {
+ SerializedShow(DocAccessibleChild* aTarget, ShowEventData& aEventData,
+ bool aFromUser)
+ : DeferredEvent(aTarget),
+ mEventData(aEventData.ID(), aEventData.Idx(),
+ nsTArray<AccessibleData>(), aEventData.EventSuppressed()),
+ mFromUser(aFromUser) {
+ // Since IPDL doesn't generate a move constructor for ShowEventData,
+ // we move NewTree manually (ugh). We still construct with an empty
+ // NewTree above so that the compiler catches any changes made to the
+ // ShowEventData structure in IPDL.
+ mEventData.NewTree() = std::move(aEventData.NewTree());
+ }
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendShowEvent(mEventData, mFromUser);
+ }
+
+ ShowEventData mEventData;
+ bool mFromUser;
+ };
+
+ struct SerializedHide final : public DeferredEvent {
+ SerializedHide(DocAccessibleChild* aTarget, uint64_t aRootID,
+ bool aFromUser)
+ : DeferredEvent(aTarget), mRootID(aRootID), mFromUser(aFromUser) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendHideEvent(mRootID, mFromUser);
+ }
+
+ uint64_t mRootID;
+ bool mFromUser;
+ };
+
+ struct SerializedStateChange final : public DeferredEvent {
+ SerializedStateChange(DocAccessibleChild* aTarget, uint64_t aID,
+ uint64_t aState, bool aEnabled)
+ : DeferredEvent(aTarget),
+ mID(aID),
+ mState(aState),
+ mEnabled(aEnabled) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendStateChangeEvent(mID, mState, mEnabled);
+ }
+
+ uint64_t mID;
+ uint64_t mState;
+ bool mEnabled;
+ };
+
+ struct SerializedCaretMove final : public DeferredEvent {
+ SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID,
+ const LayoutDeviceIntRect& aCaretRect, int32_t aOffset,
+ bool aIsSelectionCollapsed, bool aIsAtEndOfLine,
+ int32_t aGranularity)
+ : DeferredEvent(aTarget),
+ mID(aID),
+ mCaretRect(aCaretRect),
+ mOffset(aOffset),
+ mIsSelectionCollapsed(aIsSelectionCollapsed),
+ mIsAtEndOfLine(aIsAtEndOfLine),
+ mGranularity(aGranularity) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset,
+ mIsSelectionCollapsed,
+ mIsAtEndOfLine, mGranularity);
+ }
+
+ uint64_t mID;
+ LayoutDeviceIntRect mCaretRect;
+ int32_t mOffset;
+ bool mIsSelectionCollapsed;
+ bool mIsAtEndOfLine;
+ int32_t mGranularity;
+ };
+
+ struct SerializedFocus final : public DeferredEvent {
+ SerializedFocus(DocAccessibleChild* aTarget, uint64_t aID,
+ const LayoutDeviceIntRect& aCaretRect)
+ : DeferredEvent(aTarget), mID(aID), mCaretRect(aCaretRect) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendFocusEvent(mID, mCaretRect);
+ }
+
+ uint64_t mID;
+ LayoutDeviceIntRect mCaretRect;
+ };
+
+ struct SerializedTextChange final : public DeferredEvent {
+ SerializedTextChange(DocAccessibleChild* aTarget, uint64_t aID,
+ const nsString& aStr, int32_t aStart, uint32_t aLen,
+ bool aIsInsert, bool aFromUser)
+ : DeferredEvent(aTarget),
+ mID(aID),
+ mStr(aStr),
+ mStart(aStart),
+ mLen(aLen),
+ mIsInsert(aIsInsert),
+ mFromUser(aFromUser) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendTextChangeEvent(mID, mStr, mStart, mLen, mIsInsert,
+ mFromUser, false);
+ }
+
+ uint64_t mID;
+ nsString mStr;
+ int32_t mStart;
+ uint32_t mLen;
+ bool mIsInsert;
+ bool mFromUser;
+ };
+
+ struct SerializedSelection final : public DeferredEvent {
+ SerializedSelection(DocAccessibleChild* aTarget, uint64_t aID,
+ uint64_t aWidgetID, uint32_t aType)
+ : DeferredEvent(aTarget),
+ mID(aID),
+ mWidgetID(aWidgetID),
+ mType(aType) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendSelectionEvent(mID, mWidgetID, mType);
+ }
+
+ uint64_t mID;
+ uint64_t mWidgetID;
+ uint32_t mType;
+ };
+
+ struct SerializedRoleChanged final : public DeferredEvent {
+ explicit SerializedRoleChanged(DocAccessibleChild* aTarget,
+ a11y::role aRole, uint8_t aRoleMapEntryIndex)
+ : DeferredEvent(aTarget),
+ mRole(aRole),
+ mRoleMapEntryIndex(aRoleMapEntryIndex) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendRoleChangedEvent(mRole, mRoleMapEntryIndex);
+ }
+
+ a11y::role mRole;
+ uint8_t mRoleMapEntryIndex;
+ };
+
+ struct SerializedScrolling final : public DeferredEvent {
+ explicit SerializedScrolling(DocAccessibleChild* aTarget, uint64_t aID,
+ uint64_t aType, uint32_t aScrollX,
+ uint32_t aScrollY, uint32_t aMaxScrollX,
+ uint32_t aMaxScrollY)
+ : DeferredEvent(aTarget),
+ mID(aID),
+ mType(aType),
+ mScrollX(aScrollX),
+ mScrollY(aScrollY),
+ mMaxScrollX(aMaxScrollX),
+ mMaxScrollY(aMaxScrollY) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendScrollingEvent(mID, mType, mScrollX, mScrollY,
+ mMaxScrollX, mMaxScrollY);
+ }
+
+ uint64_t mID;
+ uint64_t mType;
+ uint32_t mScrollX;
+ uint32_t mScrollY;
+ uint32_t mMaxScrollX;
+ uint32_t mMaxScrollY;
+ };
+
+ struct SerializedEvent final : public DeferredEvent {
+ SerializedEvent(DocAccessibleChild* aTarget, uint64_t aID, uint32_t aType)
+ : DeferredEvent(aTarget), mID(aID), mType(aType) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override {
+ Unused << aIPCDoc->SendEvent(mID, mType);
+ }
+
+ uint64_t mID;
+ uint32_t mType;
+ };
+
+ struct SerializedChildDocConstructor final : public DeferredEvent {
+ SerializedChildDocConstructor(DocAccessibleChild* aIPCDoc,
+ DocAccessibleChild* aParentIPCDoc,
+ uint64_t aUniqueID,
+ dom::BrowsingContext* aBrowsingContext,
+ uint32_t aMsaaID)
+ : DeferredEvent(aParentIPCDoc),
+ mIPCDoc(aIPCDoc),
+ mUniqueID(aUniqueID),
+ mBrowsingContext(aBrowsingContext),
+ mMsaaID(aMsaaID) {}
+
+ void Dispatch(DocAccessibleChild* aParentIPCDoc) override {
+ auto browserChild =
+ static_cast<dom::BrowserChild*>(aParentIPCDoc->Manager());
+ MOZ_ASSERT(browserChild);
+ Unused << browserChild->SendPDocAccessibleConstructor(
+ mIPCDoc, aParentIPCDoc, mUniqueID, mBrowsingContext, mMsaaID,
+ IAccessibleHolder());
+ mIPCDoc->SetConstructedInParentProcess();
+ }
+
+ DocAccessibleChild* mIPCDoc;
+ uint64_t mUniqueID;
+ // By the time we replay this, the document and its BrowsingContext might
+ // be dead, so we use MaybeDiscardedBrowsingContext here. Ideally, we just
+ // wouldn't replay this, but this is tricky because IPDL should manage the
+ // lifetime of DocAccessibleChild, which means we must send the constructor.
+ dom::MaybeDiscardedBrowsingContext mBrowsingContext;
+ uint32_t mMsaaID;
+ };
+
+ friend struct SerializedChildDocConstructor;
+
+ struct SerializedBindChildDoc final : public DeferredEvent {
+ SerializedBindChildDoc(DocAccessibleChild* aParentDoc,
+ DocAccessibleChild* aChildDoc, uint64_t aNewParentID)
+ : DeferredEvent(aParentDoc),
+ mChildDoc(aChildDoc),
+ mNewParentID(aNewParentID) {}
+
+ void Dispatch(DocAccessibleChild* aParentIPCDoc) override {
+ Unused << aParentIPCDoc->SendBindChildDoc(mChildDoc, mNewParentID);
+ }
+
+ DocAccessibleChild* mChildDoc;
+ uint64_t mNewParentID;
+ };
+
+ struct SerializedShutdown final : public DeferredEvent {
+ explicit SerializedShutdown(DocAccessibleChild* aTarget)
+ : DeferredEvent(aTarget) {}
+
+ void Dispatch(DocAccessibleChild* aIPCDoc) override { aIPCDoc->Shutdown(); }
+ };
+
+ struct SerializedSetEmbedder final : public DeferredEvent {
+ SerializedSetEmbedder(dom::BrowserBridgeChild* aBridge,
+ DocAccessibleChild* aDoc, uint64_t aID)
+ : DeferredEvent(aDoc), mBridge(aBridge), mID(aID) {}
+
+ void Dispatch(DocAccessibleChild* aDoc) override {
+ mBridge->SetEmbedderAccessible(aDoc, mID);
+ }
+
+ RefPtr<dom::BrowserBridgeChild> mBridge;
+ uint64_t mID;
+ };
+
+ mscom::ProxyUniquePtr<IDispatch> mParentProxy;
+ mscom::ProxyUniquePtr<IDispatch> mEmulatedWindowProxy;
+ mscom::ProxyUniquePtr<IAccessible> mTopLevelDocProxy;
+ nsTArray<UniquePtr<DeferredEvent>> mDeferredEvents;
+ HWND mEmulatedWindowHandle;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_DocAccessibleChild_h
diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp
new file mode 100644
index 0000000000..6e8e5e52c3
--- /dev/null
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -0,0 +1,974 @@
+/* -*- 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 "mozilla/a11y/HandlerProvider.h"
+
+#include <memory.h>
+
+#include <utility>
+
+#include "Accessible2_3.h"
+#include "AccessibleApplication.h"
+#include "AccessibleDocument.h"
+#include "AccessibleEditableText.h"
+#include "AccessibleImage.h"
+#include "AccessibleRelation.h"
+#include "AccessibleTable.h"
+#include "AccessibleTable2.h"
+#include "AccessibleTableCell.h"
+#include "HandlerData.h"
+#include "HandlerData_i.c"
+#include "ISimpleDOM.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/mscom/AgileReference.h"
+#include "mozilla/mscom/FastMarshaler.h"
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/MainThreadHandoff.h"
+#include "mozilla/mscom/MainThreadInvoker.h"
+#include "mozilla/mscom/Ptr.h"
+#include "mozilla/mscom/StructStream.h"
+#include "mozilla/mscom/Utils.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "uiautomation.h"
+
+namespace mozilla {
+namespace a11y {
+
+HandlerProvider::HandlerProvider(REFIID aIid,
+ mscom::InterceptorTargetPtr<IUnknown> aTarget)
+ : mRefCnt(0),
+ mMutex("mozilla::a11y::HandlerProvider::mMutex"),
+ mTargetUnkIid(aIid),
+ mTargetUnk(std::move(aTarget)),
+ mPayloadMutex("mozilla::a11y::HandlerProvider::mPayloadMutex") {}
+
+HRESULT
+HandlerProvider::QueryInterface(REFIID riid, void** ppv) {
+ if (!ppv) {
+ return E_INVALIDARG;
+ }
+
+ if (riid == IID_IUnknown || riid == IID_IGeckoBackChannel) {
+ RefPtr<IUnknown> punk(static_cast<IGeckoBackChannel*>(this));
+ punk.forget(ppv);
+ return S_OK;
+ }
+
+ if (riid == IID_IMarshal) {
+ if (!mFastMarshalUnk) {
+ HRESULT hr =
+ mscom::FastMarshaler::Create(static_cast<IGeckoBackChannel*>(this),
+ getter_AddRefs(mFastMarshalUnk));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ return mFastMarshalUnk->QueryInterface(riid, ppv);
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG
+HandlerProvider::AddRef() { return ++mRefCnt; }
+
+ULONG
+HandlerProvider::Release() {
+ ULONG result = --mRefCnt;
+ if (!result) {
+ delete this;
+ }
+ return result;
+}
+
+HRESULT
+HandlerProvider::GetHandler(NotNull<CLSID*> aHandlerClsid) {
+ if (!IsTargetInterfaceCacheable()) {
+ return E_NOINTERFACE;
+ }
+
+ *aHandlerClsid = CLSID_AccessibleHandler;
+ return S_OK;
+}
+
+void HandlerProvider::GetAndSerializePayload(
+ const MutexAutoLock&, NotNull<mscom::IInterceptor*> aInterceptor) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (mSerializer) {
+ return;
+ }
+
+ IA2PayloadPtr payload;
+ { // Scope for lock
+ MutexAutoLock lock(mPayloadMutex);
+ if (mPayload) {
+ // The payload was already built by prebuildPayload() called during a
+ // bulk fetch operation.
+ payload = std::move(mPayload);
+ }
+ }
+
+ if (!payload) {
+ // We don't have a pre-built payload, so build it now.
+ payload.reset(new IA2Payload());
+ if (!mscom::InvokeOnMainThread(
+ "HandlerProvider::BuildInitialIA2Data", this,
+ &HandlerProvider::BuildInitialIA2Data,
+ std::forward<NotNull<mscom::IInterceptor*>>(aInterceptor),
+ std::forward<StaticIA2Data*>(&payload->mStaticData),
+ std::forward<DynamicIA2Data*>(&payload->mDynamicData)) ||
+ !payload->mDynamicData.mUniqueId) {
+ return;
+ }
+ }
+
+ // But we set mGeckoBackChannel on the current thread which resides in the
+ // MTA. This is important to ensure that COM always invokes
+ // IGeckoBackChannel methods in an MTA background thread.
+ RefPtr<IGeckoBackChannel> payloadRef(this);
+ // AddRef/Release pair for this reference is handled by payloadRef
+ payload->mGeckoBackChannel = this;
+
+ mSerializer = MakeUnique<mscom::StructToStream>(*payload, &IA2Payload_Encode);
+}
+
+HRESULT
+HandlerProvider::GetHandlerPayloadSize(
+ NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<DWORD*> aOutPayloadSize) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (!IsTargetInterfaceCacheable()) {
+ // No handler, so no payload for this instance.
+ return E_NOTIMPL;
+ }
+
+ MutexAutoLock lock(mMutex);
+
+ GetAndSerializePayload(lock, aInterceptor);
+
+ if (!mSerializer || !(*mSerializer)) {
+ // Failed payload serialization is non-fatal
+ *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
+ return S_OK;
+ }
+
+ *aOutPayloadSize = mSerializer->GetSize();
+ return S_OK;
+}
+
+void HandlerProvider::BuildStaticIA2Data(
+ NotNull<mscom::IInterceptor*> aInterceptor, StaticIA2Data* aOutData) {
+ MOZ_ASSERT(aOutData);
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTargetUnk);
+ MOZ_ASSERT(IsTargetInterfaceCacheable());
+
+ // Include interfaces the client is likely to request.
+ // This is cheap here and saves multiple cross-process calls later.
+ // These interfaces must be released in ReleaseStaticIA2DataInterfaces!
+
+ // If the target is already an IAccessible2, this pointer is redundant.
+ // However, the target might be an IAccessibleHyperlink, etc., in which
+ // case the client will almost certainly QI for IAccessible2.
+ HRESULT hr = aInterceptor->GetInterceptorForIID(NEWEST_IA2_IID,
+ (void**)&aOutData->mIA2);
+ if (FAILED(hr)) {
+ // IA2 should always be present, so something has
+ // gone very wrong if this fails.
+ aOutData->mIA2 = nullptr;
+ return;
+ }
+
+ // Some of these interfaces aren't present on all accessibles,
+ // so it's not a failure if these interfaces can't be fetched.
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHypertext2,
+ (void**)&aOutData->mIAHypertext);
+ if (FAILED(hr)) {
+ aOutData->mIAHypertext = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHyperlink,
+ (void**)&aOutData->mIAHyperlink);
+ if (FAILED(hr)) {
+ aOutData->mIAHyperlink = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable,
+ (void**)&aOutData->mIATable);
+ if (FAILED(hr)) {
+ aOutData->mIATable = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable2,
+ (void**)&aOutData->mIATable2);
+ if (FAILED(hr)) {
+ aOutData->mIATable2 = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTableCell,
+ (void**)&aOutData->mIATableCell);
+ if (FAILED(hr)) {
+ aOutData->mIATableCell = nullptr;
+ }
+}
+
+void HandlerProvider::BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data,
+ bool aMarshaledByCom) {
+ MOZ_ASSERT(aOutIA2Data);
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsTargetInterfaceCacheable());
+
+ if (!mTargetUnk) {
+ return;
+ }
+
+ RefPtr<NEWEST_IA2_INTERFACE> target;
+ HRESULT hr =
+ mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID, getter_AddRefs(target));
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = E_UNEXPECTED;
+
+ auto hasFailed = [&hr]() -> bool { return FAILED(hr); };
+
+ auto cleanup = [aOutIA2Data, aMarshaledByCom]() -> void {
+ CleanupDynamicIA2Data(*aOutIA2Data, aMarshaledByCom);
+ };
+
+ mscom::ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed,
+ cleanup);
+
+ // When allocating memory to be returned to the client, you *must* use
+ // allocMem, not CoTaskMemAlloc!
+ auto allocMem = [aMarshaledByCom](size_t aSize) {
+ if (aMarshaledByCom) {
+ return ::CoTaskMemAlloc(aSize);
+ }
+ // We use midl_user_allocate rather than CoTaskMemAlloc because this
+ // struct is being marshaled by RPC, not COM.
+ return ::midl_user_allocate(aSize);
+ };
+
+ const VARIANT kChildIdSelf = {VT_I4};
+ VARIANT varVal;
+
+ hr = target->accLocation(&aOutIA2Data->mLeft, &aOutIA2Data->mTop,
+ &aOutIA2Data->mWidth, &aOutIA2Data->mHeight,
+ kChildIdSelf);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accRole(kChildIdSelf, &aOutIA2Data->mRole);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accState(kChildIdSelf, &varVal);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ aOutIA2Data->mState = varVal.lVal;
+
+ hr = target->get_accKeyboardShortcut(kChildIdSelf,
+ &aOutIA2Data->mKeyboardShortcut);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accName(kChildIdSelf, &aOutIA2Data->mName);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accDescription(kChildIdSelf, &aOutIA2Data->mDescription);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accDefaultAction(kChildIdSelf, &aOutIA2Data->mDefaultAction);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accChildCount(&aOutIA2Data->mChildCount);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accValue(kChildIdSelf, &aOutIA2Data->mValue);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_states(&aOutIA2Data->mIA2States);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_attributes(&aOutIA2Data->mAttributes);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ HWND hwnd;
+ hr = target->get_windowHandle(&hwnd);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ aOutIA2Data->mHwnd = PtrToLong(hwnd);
+
+ hr = target->get_locale(&aOutIA2Data->mIA2Locale);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->role(&aOutIA2Data->mIA2Role);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ RefPtr<IAccessibleAction> action;
+ // It is not an error if this fails.
+ hr = mTargetUnk.get()->QueryInterface(IID_IAccessibleAction,
+ getter_AddRefs(action));
+ if (SUCCEEDED(hr)) {
+ hr = action->nActions(&aOutIA2Data->mNActions);
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+
+ RefPtr<IAccessibleTableCell> cell;
+ // It is not an error if this fails.
+ hr = mTargetUnk.get()->QueryInterface(IID_IAccessibleTableCell,
+ getter_AddRefs(cell));
+ if (SUCCEEDED(hr)) {
+ hr = cell->get_rowColumnExtents(
+ &aOutIA2Data->mRowIndex, &aOutIA2Data->mColumnIndex,
+ &aOutIA2Data->mRowExtent, &aOutIA2Data->mColumnExtent,
+ &aOutIA2Data->mCellIsSelected);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ // Because the same headers can apply to many cells, include the ids of
+ // header cells, rather than the actual objects. Otherwise, we might
+ // end up marshaling the same objects (and their payloads) many times.
+ IUnknown** headers = nullptr;
+ hr = cell->get_rowHeaderCells(&headers, &aOutIA2Data->mNRowHeaderCells);
+ if (FAILED(hr)) {
+ return;
+ }
+ if (aOutIA2Data->mNRowHeaderCells > 0) {
+ aOutIA2Data->mRowHeaderCellIds = static_cast<long*>(
+ allocMem(sizeof(long) * aOutIA2Data->mNRowHeaderCells));
+ for (long i = 0; i < aOutIA2Data->mNRowHeaderCells; ++i) {
+ RefPtr<IAccessible2> headerAcc;
+ hr = headers[i]->QueryInterface(IID_IAccessible2,
+ getter_AddRefs(headerAcc));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ headers[i]->Release();
+ hr = headerAcc->get_uniqueID(&aOutIA2Data->mRowHeaderCellIds[i]);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ }
+ }
+ ::CoTaskMemFree(headers);
+
+ hr = cell->get_columnHeaderCells(&headers,
+ &aOutIA2Data->mNColumnHeaderCells);
+ if (FAILED(hr)) {
+ return;
+ }
+ if (aOutIA2Data->mNColumnHeaderCells > 0) {
+ aOutIA2Data->mColumnHeaderCellIds = static_cast<long*>(
+ allocMem(sizeof(long) * aOutIA2Data->mNColumnHeaderCells));
+ for (long i = 0; i < aOutIA2Data->mNColumnHeaderCells; ++i) {
+ RefPtr<IAccessible2> headerAcc;
+ hr = headers[i]->QueryInterface(IID_IAccessible2,
+ getter_AddRefs(headerAcc));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ headers[i]->Release();
+ hr = headerAcc->get_uniqueID(&aOutIA2Data->mColumnHeaderCellIds[i]);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ }
+ }
+ ::CoTaskMemFree(headers);
+ }
+
+ // NB: get_uniqueID should be the final property retrieved in this method,
+ // as its presence is used to determine whether the rest of this data
+ // retrieval was successful.
+ hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
+}
+
+void HandlerProvider::BuildInitialIA2Data(
+ NotNull<mscom::IInterceptor*> aInterceptor, StaticIA2Data* aOutStaticData,
+ DynamicIA2Data* aOutDynamicData) {
+ BuildStaticIA2Data(aInterceptor, aOutStaticData);
+ if (!aOutStaticData->mIA2) {
+ return;
+ }
+ BuildDynamicIA2Data(aOutDynamicData);
+}
+
+bool HandlerProvider::IsTargetInterfaceCacheable() {
+ return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID ||
+ mTargetUnkIid == IID_IAccessibleHyperlink;
+}
+
+HRESULT
+HandlerProvider::WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<IStream*> aStream) {
+ if (!IsTargetInterfaceCacheable()) {
+ // No handler, so no payload for this instance.
+ return E_NOTIMPL;
+ }
+
+ MutexAutoLock lock(mMutex);
+
+ if (!mSerializer || !(*mSerializer)) {
+ // Failed payload serialization is non-fatal
+ mscom::StructToStream emptyStruct;
+ return emptyStruct.Write(aStream);
+ }
+
+ HRESULT hr = mSerializer->Write(aStream);
+
+ mSerializer.reset();
+
+ return hr;
+}
+
+REFIID
+HandlerProvider::MarshalAs(REFIID aIid) {
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if (aIid == IID_IDispatch || aIid == IID_IAccessible ||
+ aIid == IID_IAccessible2 || aIid == IID_IAccessible2_2 ||
+ aIid == IID_IAccessible2_3) {
+ // This should always be the newest IA2 interface ID
+ return NEWEST_IA2_IID;
+ }
+ // Otherwise we juse return the identity.
+ return aIid;
+}
+
+HRESULT
+HandlerProvider::DisconnectHandlerRemotes() {
+ // If a handlerProvider call is pending on another thread,
+ // CoDisconnectObject won't release this HandlerProvider immediately.
+ // However, the interceptor and its target (mTargetUnk) might be destroyed.
+ mTargetUnk = nullptr;
+
+ IUnknown* unk = static_cast<IGeckoBackChannel*>(this);
+ return ::CoDisconnectObject(unk, 0);
+}
+
+HRESULT
+HandlerProvider::IsInterfaceMaybeSupported(REFIID aIid) {
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if (aIid == IID_IUnknown || aIid == IID_IDispatch ||
+ aIid == IID_IAccessible || aIid == IID_IServiceProvider ||
+ aIid == IID_IEnumVARIANT || aIid == IID_IAccessible2 ||
+ aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2_3 ||
+ aIid == IID_IAccessibleAction || aIid == IID_IAccessibleApplication ||
+ aIid == IID_IAccessibleComponent || aIid == IID_IAccessibleDocument ||
+ aIid == IID_IAccessibleEditableText || aIid == IID_IAccessibleHyperlink ||
+ aIid == IID_IAccessibleHypertext || aIid == IID_IAccessibleHypertext2 ||
+ aIid == IID_IAccessibleImage || aIid == IID_IAccessibleRelation ||
+ aIid == IID_IAccessibleTable || aIid == IID_IAccessibleTable2 ||
+ aIid == IID_IAccessibleTableCell || aIid == IID_IAccessibleText ||
+ aIid == IID_IAccessibleValue || aIid == IID_ISimpleDOMNode ||
+ aIid == IID_ISimpleDOMDocument || aIid == IID_ISimpleDOMText ||
+ aIid == IID_IAccessibleEx || aIid == IID_IRawElementProviderSimple) {
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+REFIID
+HandlerProvider::GetEffectiveOutParamIid(REFIID aCallIid, ULONG aCallMethod) {
+ if (aCallIid == IID_IAccessibleTable || aCallIid == IID_IAccessibleTable2 ||
+ aCallIid == IID_IAccessibleDocument ||
+ aCallIid == IID_IAccessibleTableCell ||
+ aCallIid == IID_IAccessibleRelation) {
+ return NEWEST_IA2_IID;
+ }
+
+ // IAccessible2_2::accessibleWithCaret
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if ((aCallIid == IID_IAccessible2_2 || aCallIid == IID_IAccessible2_3) &&
+ aCallMethod == 47) {
+ return NEWEST_IA2_IID;
+ }
+
+ // IAccessible::get_accSelection
+ if ((aCallIid == IID_IAccessible || aCallIid == IID_IAccessible2 ||
+ aCallIid == IID_IAccessible2_2 || aCallIid == IID_IAccessible2_3) &&
+ aCallMethod == 19) {
+ return IID_IEnumVARIANT;
+ }
+
+ MOZ_ASSERT(false);
+ return IID_IUnknown;
+}
+
+HRESULT
+HandlerProvider::NewInstance(
+ REFIID aIid, mscom::InterceptorTargetPtr<IUnknown> aTarget,
+ NotNull<mscom::IHandlerProvider**> aOutNewPayload) {
+ RefPtr<IHandlerProvider> newPayload(
+ new HandlerProvider(aIid, std::move(aTarget)));
+ newPayload.forget(aOutNewPayload.get());
+ return S_OK;
+}
+
+void HandlerProvider::SetHandlerControlOnMainThread(
+ DWORD aPid, mscom::ProxyUniquePtr<IHandlerControl> aCtrl) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto content = dom::ContentChild::GetSingleton();
+ MOZ_ASSERT(content);
+
+ IHandlerControlHolder holder(
+ CreateHolderFromHandlerControl(std::move(aCtrl)));
+ Unused << content->SendA11yHandlerControl(aPid, holder);
+}
+
+HRESULT
+HandlerProvider::put_HandlerControl(long aPid, IHandlerControl* aCtrl) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (!aCtrl) {
+ return E_INVALIDARG;
+ }
+
+ auto ptrProxy = mscom::ToProxyUniquePtr(aCtrl);
+
+ if (!mscom::InvokeOnMainThread(
+ "HandlerProvider::SetHandlerControlOnMainThread", this,
+ &HandlerProvider::SetHandlerControlOnMainThread,
+ static_cast<DWORD>(aPid), std::move(ptrProxy))) {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT
+HandlerProvider::Refresh(DynamicIA2Data* aOutData) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (!mTargetUnk) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+
+ if (!mscom::InvokeOnMainThread("HandlerProvider::BuildDynamicIA2Data", this,
+ &HandlerProvider::BuildDynamicIA2Data,
+ std::forward<DynamicIA2Data*>(aOutData),
+ /* aMarshaledByCom */ true)) {
+ return E_FAIL;
+ }
+
+ if (!aOutData->mUniqueId) {
+ // BuildDynamicIA2Data failed.
+ if (!mTargetUnk) {
+ // Even though we checked this before, the accessible can be shut down
+ // before BuildDynamicIA2Data executes on the main thread.
+ return CO_E_OBJNOTCONNECTED;
+ }
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+void HandlerProvider::PrebuildPayload(
+ NotNull<mscom::IInterceptor*> aInterceptor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mPayloadMutex);
+ mPayload.reset(new IA2Payload());
+ BuildInitialIA2Data(aInterceptor, &mPayload->mStaticData,
+ &mPayload->mDynamicData);
+ if (!mPayload->mDynamicData.mUniqueId) {
+ // Building the payload failed.
+ mPayload.reset();
+ }
+}
+
+template <typename Interface>
+HRESULT HandlerProvider::ToWrappedObject(Interface** aObj) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mscom::STAUniquePtr<Interface> inObj(*aObj);
+ RefPtr<HandlerProvider> hprov = new HandlerProvider(
+ __uuidof(Interface), mscom::ToInterceptorTargetPtr(inObj));
+ HRESULT hr =
+ mscom::MainThreadHandoff::WrapInterface(std::move(inObj), hprov, aObj);
+ if (FAILED(hr)) {
+ *aObj = nullptr;
+ return hr;
+ }
+ // Build the payload for this object now to avoid a cross-thread call when
+ // marshaling it later.
+ RefPtr<mscom::IInterceptor> interceptor;
+ hr = (*aObj)->QueryInterface(mscom::IID_IInterceptor,
+ getter_AddRefs(interceptor));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ // Even though we created a new HandlerProvider, that won't be used if
+ // there's an existing Interceptor. Therefore, we must get the
+ // HandlerProvider from the Interceptor.
+ RefPtr<mscom::IInterceptorSink> interceptorSink;
+ interceptor->GetEventSink(getter_AddRefs(interceptorSink));
+ MOZ_ASSERT(interceptorSink);
+ RefPtr<mscom::IMainThreadHandoff> handoff;
+ hr = interceptorSink->QueryInterface(mscom::IID_IMainThreadHandoff,
+ getter_AddRefs(handoff));
+ // If a11y Interceptors stop using MainThreadHandoff as their event sink, we
+ // *really* want to know about it ASAP.
+ MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr),
+ "A11y Interceptor isn't using MainThreadHandoff");
+ RefPtr<mscom::IHandlerProvider> usedIHprov;
+ handoff->GetHandlerProvider(getter_AddRefs(usedIHprov));
+ MOZ_ASSERT(usedIHprov);
+ auto usedHprov = static_cast<HandlerProvider*>(usedIHprov.get());
+ usedHprov->PrebuildPayload(WrapNotNull(interceptor));
+ return hr;
+}
+
+void HandlerProvider::GetAllTextInfoMainThread(
+ BSTR* aText, IAccessibleHyperlink*** aHyperlinks, long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns, long* aNAttribRuns, HRESULT* result) {
+ MOZ_ASSERT(aText);
+ MOZ_ASSERT(aHyperlinks);
+ MOZ_ASSERT(aNHyperlinks);
+ MOZ_ASSERT(aAttribRuns);
+ MOZ_ASSERT(aNAttribRuns);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mTargetUnk) {
+ *result = CO_E_OBJNOTCONNECTED;
+ return;
+ }
+
+ RefPtr<IAccessibleHypertext2> ht;
+ HRESULT hr =
+ mTargetUnk->QueryInterface(IID_IAccessibleHypertext2, getter_AddRefs(ht));
+ if (FAILED(hr)) {
+ *result = hr;
+ return;
+ }
+
+ hr = ht->get_text(0, IA2_TEXT_OFFSET_LENGTH, aText);
+ if (FAILED(hr)) {
+ *result = hr;
+ return;
+ }
+
+ if (hr == S_FALSE) {
+ // No text.
+ *aHyperlinks = nullptr;
+ *aNHyperlinks = 0;
+ *aAttribRuns = nullptr;
+ *aNAttribRuns = 0;
+ *result = S_FALSE;
+ return;
+ }
+
+ hr = ht->get_hyperlinks(aHyperlinks, aNHyperlinks);
+ if (FAILED(hr)) {
+ *aHyperlinks = nullptr;
+ // -1 signals to the handler that it should call hyperlinks itself.
+ *aNHyperlinks = -1;
+ }
+ // We must wrap these hyperlinks in an interceptor.
+ for (long index = 0; index < *aNHyperlinks; ++index) {
+ ToWrappedObject(&(*aHyperlinks)[index]);
+ }
+
+ // Fetch all attribute runs.
+ nsTArray<IA2TextSegment> attribRuns;
+ long end = 0;
+ long length = ::SysStringLen(*aText);
+ while (end < length) {
+ long offset = end;
+ long start;
+ BSTR attribs;
+ // The (exclusive) end of the last run is the start of the next run.
+ hr = ht->get_attributes(offset, &start, &end, &attribs);
+ // Bug 1421873: Gecko can return end <= offset in some rare cases, which
+ // isn't valid. This is perhaps because the text mutated during the loop
+ // for some reason, making this offset invalid.
+ if (FAILED(hr) || end <= offset) {
+ break;
+ }
+ attribRuns.AppendElement(IA2TextSegment({attribs, start, end}));
+ }
+
+ // Put the attribute runs in a COM array.
+ *aNAttribRuns = attribRuns.Length();
+ *aAttribRuns = static_cast<IA2TextSegment*>(
+ ::CoTaskMemAlloc(sizeof(IA2TextSegment) * *aNAttribRuns));
+ for (long index = 0; index < *aNAttribRuns; ++index) {
+ (*aAttribRuns)[index] = attribRuns[index];
+ }
+
+ *result = S_OK;
+}
+
+HRESULT
+HandlerProvider::get_AllTextInfo(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (!mTargetUnk) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+
+ HRESULT hr;
+ if (!mscom::InvokeOnMainThread(
+ "HandlerProvider::GetAllTextInfoMainThread", this,
+ &HandlerProvider::GetAllTextInfoMainThread,
+ std::forward<BSTR*>(aText),
+ std::forward<IAccessibleHyperlink***>(aHyperlinks),
+ std::forward<long*>(aNHyperlinks),
+ std::forward<IA2TextSegment**>(aAttribRuns),
+ std::forward<long*>(aNAttribRuns), std::forward<HRESULT*>(&hr))) {
+ return E_FAIL;
+ }
+
+ return hr;
+}
+
+void HandlerProvider::GetRelationsInfoMainThread(IARelationData** aRelations,
+ long* aNRelations,
+ HRESULT* hr) {
+ MOZ_ASSERT(aRelations);
+ MOZ_ASSERT(aNRelations);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mTargetUnk) {
+ *hr = CO_E_OBJNOTCONNECTED;
+ return;
+ }
+
+ RefPtr<NEWEST_IA2_INTERFACE> acc;
+ *hr = mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID, getter_AddRefs(acc));
+ if (FAILED(*hr)) {
+ return;
+ }
+
+ *hr = acc->get_nRelations(aNRelations);
+ if (FAILED(*hr)) {
+ return;
+ }
+
+ auto rawRels = MakeUnique<IAccessibleRelation*[]>(*aNRelations);
+ *hr = acc->get_relations(*aNRelations, rawRels.get(), aNRelations);
+ if (FAILED(*hr)) {
+ return;
+ }
+
+ *aRelations = static_cast<IARelationData*>(
+ ::CoTaskMemAlloc(sizeof(IARelationData) * *aNRelations));
+ for (long index = 0; index < *aNRelations; ++index) {
+ IAccessibleRelation* rawRel = rawRels[index];
+ IARelationData& relData = (*aRelations)[index];
+ *hr = rawRel->get_relationType(&relData.mType);
+ if (FAILED(*hr)) {
+ relData.mType = nullptr;
+ }
+ *hr = rawRel->get_nTargets(&relData.mNTargets);
+ if (FAILED(*hr)) {
+ relData.mNTargets = -1;
+ }
+ rawRel->Release();
+ }
+
+ *hr = S_OK;
+}
+
+HRESULT
+HandlerProvider::get_RelationsInfo(IARelationData** aRelations,
+ long* aNRelations) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ if (!mTargetUnk) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+
+ HRESULT hr;
+ if (!mscom::InvokeOnMainThread(
+ "HandlerProvider::GetRelationsInfoMainThread", this,
+ &HandlerProvider::GetRelationsInfoMainThread,
+ std::forward<IARelationData**>(aRelations),
+ std::forward<long*>(aNRelations), std::forward<HRESULT*>(&hr))) {
+ return E_FAIL;
+ }
+
+ return hr;
+}
+
+// Helper function for GetAllChildrenMainThread.
+static bool SetChildDataForTextLeaf(NEWEST_IA2_INTERFACE* acc,
+ AccChildData& data) {
+ const VARIANT kChildIdSelf = {VT_I4};
+ VARIANT varVal;
+
+ // 1. Check whether this is a text leaf.
+
+ // 1.1. A text leaf always has ROLE_SYSTEM_TEXT or ROLE_SYSTEM_WHITESPACE.
+ HRESULT hr = acc->get_accRole(kChildIdSelf, &varVal);
+ if (FAILED(hr)) {
+ return false;
+ }
+ if (varVal.vt != VT_I4) {
+ return false;
+ }
+ long role = varVal.lVal;
+ if (role != ROLE_SYSTEM_TEXT && role != ROLE_SYSTEM_WHITESPACE) {
+ return false;
+ }
+
+ // 1.2. A text leaf doesn't support IAccessibleText.
+ RefPtr<IAccessibleText> iaText;
+ hr = acc->QueryInterface(IID_IAccessibleText, getter_AddRefs(iaText));
+ if (SUCCEEDED(hr)) {
+ return false;
+ }
+
+ // 1.3. A text leaf doesn't have children.
+ long count;
+ hr = acc->get_accChildCount(&count);
+ if (FAILED(hr) || count != 0) {
+ return false;
+ }
+
+ // 2. Update |data| with the data for this text leaf.
+ // Because marshaling objects is more expensive than marshaling other data,
+ // we just marshal the data we need for text leaf children, rather than
+ // marshaling the full accessible object.
+
+ // |data| has already been zeroed, so we don't need to do anything if these
+ // calls fail.
+ acc->get_accName(kChildIdSelf, &data.mText);
+ data.mTextRole = role;
+ acc->get_uniqueID(&data.mTextId);
+ acc->get_accState(kChildIdSelf, &varVal);
+ data.mTextState = varVal.lVal;
+ acc->accLocation(&data.mTextLeft, &data.mTextTop, &data.mTextWidth,
+ &data.mTextHeight, kChildIdSelf);
+
+ return true;
+}
+
+void HandlerProvider::GetAllChildrenMainThread(AccChildData** aChildren,
+ ULONG* aNChildren, HRESULT* hr) {
+ MOZ_ASSERT(aChildren);
+ MOZ_ASSERT(aNChildren);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mTargetUnk) {
+ *hr = CO_E_OBJNOTCONNECTED;
+ return;
+ }
+
+ RefPtr<NEWEST_IA2_INTERFACE> acc;
+ *hr = mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID, getter_AddRefs(acc));
+ if (FAILED(*hr)) {
+ return;
+ }
+
+ long count;
+ *hr = acc->get_accChildCount(&count);
+ if (FAILED(*hr)) {
+ return;
+ }
+ MOZ_ASSERT(count >= 0);
+
+ if (count == 0) {
+ *aChildren = nullptr;
+ *aNChildren = 0;
+ return;
+ }
+
+ RefPtr<IEnumVARIANT> enumVar;
+ *hr = mTargetUnk.get()->QueryInterface(IID_IEnumVARIANT,
+ getter_AddRefs(enumVar));
+ if (FAILED(*hr)) {
+ return;
+ }
+
+ auto rawChildren = MakeUnique<VARIANT[]>(count);
+ *hr = enumVar->Next((ULONG)count, rawChildren.get(), aNChildren);
+ if (FAILED(*hr)) {
+ *aChildren = nullptr;
+ *aNChildren = 0;
+ return;
+ }
+
+ *aChildren = static_cast<AccChildData*>(
+ ::CoTaskMemAlloc(sizeof(AccChildData) * *aNChildren));
+ for (ULONG index = 0; index < *aNChildren; ++index) {
+ (*aChildren)[index] = {};
+ AccChildData& child = (*aChildren)[index];
+
+ MOZ_ASSERT(rawChildren[index].vt == VT_DISPATCH);
+ MOZ_ASSERT(rawChildren[index].pdispVal);
+ RefPtr<NEWEST_IA2_INTERFACE> childAcc;
+ *hr = rawChildren[index].pdispVal->QueryInterface(NEWEST_IA2_IID,
+ getter_AddRefs(childAcc));
+ rawChildren[index].pdispVal->Release();
+ MOZ_ASSERT(SUCCEEDED(*hr));
+ if (FAILED(*hr)) {
+ continue;
+ }
+
+ if (!SetChildDataForTextLeaf(childAcc, child)) {
+ // This isn't a text leaf. Marshal the accessible.
+ childAcc.forget(&child.mAccessible);
+ // We must wrap this accessible in an Interceptor.
+ ToWrappedObject(&child.mAccessible);
+ }
+ }
+
+ *hr = S_OK;
+}
+
+HRESULT
+HandlerProvider::get_AllChildren(AccChildData** aChildren, ULONG* aNChildren) {
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ HRESULT hr;
+ if (!mscom::InvokeOnMainThread(
+ "HandlerProvider::GetAllChildrenMainThread", this,
+ &HandlerProvider::GetAllChildrenMainThread,
+ std::forward<AccChildData**>(aChildren),
+ std::forward<ULONG*>(aNChildren), std::forward<HRESULT*>(&hr))) {
+ return E_FAIL;
+ }
+
+ return hr;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/HandlerProvider.h b/accessible/ipc/win/HandlerProvider.h
new file mode 100644
index 0000000000..f517a0f991
--- /dev/null
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -0,0 +1,145 @@
+/* -*- 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_HandlerProvider_h
+#define mozilla_a11y_HandlerProvider_h
+
+#include "mozilla/a11y/AccessibleHandler.h"
+#include "mozilla/a11y/HandlerDataCleanup.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/mscom/IHandlerProvider.h"
+#include "mozilla/mscom/Ptr.h"
+#include "mozilla/mscom/StructStream.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "HandlerData.h"
+
+struct NEWEST_IA2_INTERFACE;
+
+namespace mozilla {
+
+namespace mscom {
+
+class StructToStream;
+
+} // namespace mscom
+
+namespace a11y {
+
+class HandlerProvider final : public IGeckoBackChannel,
+ public mscom::IHandlerProvider {
+ public:
+ HandlerProvider(REFIID aIid, mscom::InterceptorTargetPtr<IUnknown> aTarget);
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IHandlerProvider
+ STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
+ STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<DWORD*> aOutPayloadSize) override;
+ STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<IStream*> aStream) override;
+ STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
+ STDMETHODIMP DisconnectHandlerRemotes() override;
+ STDMETHODIMP IsInterfaceMaybeSupported(REFIID aIid) override;
+ STDMETHODIMP_(REFIID)
+ GetEffectiveOutParamIid(REFIID aCallIid, ULONG aCallMethod) override;
+ STDMETHODIMP NewInstance(
+ REFIID aIid, mscom::InterceptorTargetPtr<IUnknown> aTarget,
+ NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
+
+ // IGeckoBackChannel
+ STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
+ STDMETHODIMP Refresh(DynamicIA2Data* aOutData) override;
+ STDMETHODIMP get_AllTextInfo(BSTR* aText, IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks, IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns) override;
+ STDMETHODIMP get_RelationsInfo(IARelationData** aRelations,
+ long* aNRelations) override;
+ STDMETHODIMP get_AllChildren(AccChildData** aChildren,
+ ULONG* aNChildren) override;
+
+ private:
+ ~HandlerProvider() = default;
+
+ void SetHandlerControlOnMainThread(
+ DWORD aPid, mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
+ void GetAndSerializePayload(const MutexAutoLock&,
+ NotNull<mscom::IInterceptor*> aInterceptor);
+ void BuildStaticIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutData);
+ /**
+ * Pass true for aMarshaledByCom if this struct is being directly marshaled as
+ * an out parameter of a COM method, currently only
+ * IGeckoBackChannel::Refresh.
+ * When aMarshaledByCom is false, this means the struct is being marshaled
+ * by RPC encoding functions. This means we must allocate memory differently,
+ * even though we're using this as part of a COM handler payload.
+ */
+ void BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data,
+ bool aMarshaledByCom = false);
+ void BuildInitialIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutStaticData,
+ DynamicIA2Data* aOutDynamicData);
+ bool IsTargetInterfaceCacheable();
+
+ /**
+ * Build the payload for later marshaling.
+ * This is intended to be used during a bulk fetch operation and must only be
+ * called from the main thread.
+ */
+ void PrebuildPayload(NotNull<mscom::IInterceptor*> aInterceptor);
+
+ // Replace a raw object from the main thread with a wrapped, intercepted
+ // object suitable for calling from the MTA.
+ // The reference to the original object is adopted; i.e. you should not
+ // separately release it.
+ // This is intended for objects returned from method calls on the main thread.
+ template <typename Interface>
+ HRESULT ToWrappedObject(Interface** aObj);
+ void GetAllTextInfoMainThread(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns, HRESULT* result);
+ void GetRelationsInfoMainThread(IARelationData** aRelations,
+ long* aNRelations, HRESULT* result);
+ void GetAllChildrenMainThread(AccChildData** aChildren, ULONG* aNChildren,
+ HRESULT* result);
+
+ Atomic<uint32_t> mRefCnt;
+ Mutex mMutex MOZ_UNANNOTATED; // Protects mSerializer
+ const IID mTargetUnkIid;
+ mscom::InterceptorTargetPtr<IUnknown>
+ mTargetUnk; // Constant, main thread only
+ UniquePtr<mscom::StructToStream> mSerializer;
+ RefPtr<IUnknown> mFastMarshalUnk;
+
+ struct IA2PayloadDeleter {
+ void operator()(IA2Payload* aPayload) {
+ // When CoMarshalInterface writes interfaces out to a stream, it AddRefs.
+ // Therefore, we must release our references after this.
+ ReleaseStaticIA2DataInterfaces(aPayload->mStaticData);
+ CleanupDynamicIA2Data(aPayload->mDynamicData);
+ delete aPayload;
+ }
+ };
+ using IA2PayloadPtr = UniquePtr<IA2Payload, IA2PayloadDeleter>;
+
+ // Used when the payload is built prior to marshaling the object by a bulk
+ // fetch operation. See prebuildPayload().
+ IA2PayloadPtr mPayload;
+ Mutex mPayloadMutex MOZ_UNANNOTATED; // Protects mPayload
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerProvider_h
diff --git a/accessible/ipc/win/PDocAccessible.ipdl b/accessible/ipc/win/PDocAccessible.ipdl
new file mode 100644
index 0000000000..15a25a6000
--- /dev/null
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -0,0 +1,155 @@
+/* -*- 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 PBrowser;
+
+include DocAccessibleTypes;
+
+include "mozilla/GfxMessageUtils.h";
+
+using mozilla::a11y::role from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::IDispatchHolder from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::AccType from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::AccGenericType from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
+[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
+using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+
+namespace mozilla {
+namespace a11y {
+
+struct AccessibleData
+{
+ uint64_t ID;
+ int32_t MsaaID;
+ role Role;
+ uint32_t ChildrenCount;
+ AccType Type;
+ AccGenericType GenericTypes;
+ uint8_t RoleMapEntryIndex;
+};
+
+struct ShowEventData
+{
+ uint64_t ID;
+ uint32_t Idx;
+ AccessibleData[] NewTree;
+ bool EventSuppressed;
+};
+
+struct TextRangeData
+{
+ uint64_t StartID;
+ uint64_t EndID;
+ int32_t StartOffset;
+ int32_t EndOffset;
+};
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+sync protocol PDocAccessible
+{
+ manager PBrowser;
+
+parent:
+ async Shutdown();
+
+ /*
+ * Notify the parent process the document in the child process is firing an
+ * event.
+ */
+ async Event(uint64_t aID, uint32_t type);
+ async ShowEvent(ShowEventData data, bool aFromUser);
+ async HideEvent(uint64_t aRootID, bool aFromUser);
+ async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
+ async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect,
+ int32_t aOffset, bool aIsAtEndOfLine,
+ bool aIsSelectionCollapsed, int32_t aGranularity);
+ async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
+ bool aIsInsert, bool aFromUser);
+ sync SyncTextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart,
+ uint32_t aLen, bool aIsInsert, bool aFromUser);
+ async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);
+ async RoleChangedEvent(role aRole, uint8_t aRoleMapEntryIndex);
+ async FocusEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect);
+ async VirtualCursorChangeEvent(uint64_t aID,
+ uint64_t aOldPosition,
+ int32_t aOldStartOffset, int32_t aOldEndOffset,
+ uint64_t aPosition,
+ int32_t aStartOffset, int32_t aEndOffset,
+ int16_t aReason, int16_t aBoundaryType,
+ bool aFromUservcEvent);
+ async ScrollingEvent(uint64_t aID, uint64_t aType,
+ uint32_t aScrollX, uint32_t aScrollY,
+ uint32_t aMaxScrollX, uint32_t aMaxScrollY);
+ async TextSelectionChangeEvent(uint64_t aID, TextRangeData[] aSelection);
+
+ /*
+ * Tell the parent document to bind the existing document as a new child
+ * document.
+ */
+ async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
+
+ /*
+ * Cache The World
+ * aDispatchShowEvent is true when a show event with the first accessible in
+ * the cache list as the target should be dispatched after the cache is
+ * populated. The show event will have a from-user flag value of false.
+ */
+ async Cache(CacheUpdateType aUpdateType, CacheData[] aData, bool aDispatchShowEvent);
+
+ /*
+ * Lists of accessibles that either gained or lost a selected state.
+ */
+ async SelectedAccessiblesChanged(uint64_t[] aSelectedIDs, uint64_t[] aUnselectedIDs);
+
+ /*
+ * Tell the parent process that the given Accessibles are about to be moved
+ * via subsequent hide and show events.
+ */
+ async AccessiblesWillMove(uint64_t[] aIDs);
+
+child:
+ /**
+ * We use IDispatchHolder instead of IAccessibleHolder for the following two
+ * methods because of sandboxing. IAccessible::get_accParent returns the parent
+ * as an IDispatch. COM is not smart enough to understand that IAccessible is
+ * derived from IDispatch, so during marshaling it attempts to QueryInterface
+ * on the parent's proxy for IDispatch. This will fail with E_ACCESSDENIED
+ * thanks to the sandbox. We can avoid this entirely by just giving content
+ * the correct interface to begin with: IDispatch.
+ */
+ async ParentCOMProxy(IDispatchHolder aParentCOMProxy);
+ async EmulatedWindow(WindowsHandle aEmulatedWindowHandle,
+ IDispatchHolder aEmulatedWindowCOMProxy);
+ async TopLevelDocCOMProxy(IAccessibleHolder aCOMProxy);
+ /*
+ * Called as a result of focus shifting from chrome to content
+ * elements through keyboard navigation.
+ */
+ async RestoreFocus();
+
+ async TakeFocus(uint64_t aID);
+ async TakeSelection(uint64_t aID);
+ async SetSelected(uint64_t aID, bool aSelected);
+ async ScrollTo(uint64_t aID, uint32_t aScrollType);
+
+ /*
+ * Verify the cache. Used for testing purposes.
+ */
+ async VerifyCache(uint64_t aID, uint64_t aCacheDomain, AccAttributes aFields);
+
+ async DoActionAsync(uint64_t aID, uint8_t aIndex);
+
+ async SetCaretOffset(uint64_t aID, int32_t aOffset);
+
+ async __delete__();
+};
+
+}
+}
diff --git a/accessible/ipc/win/PlatformChild.cpp b/accessible/ipc/win/PlatformChild.cpp
new file mode 100644
index 0000000000..31026bcf2d
--- /dev/null
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -0,0 +1,87 @@
+/* -*- 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/AccessibleHandler.h"
+#include "mozilla/a11y/PlatformChild.h"
+#include "mozilla/mscom/ActCtxResource.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#include "mozilla/mscom/InterceptorLog.h"
+
+#include "Accessible2.h"
+#include "Accessible2_2.h"
+#include "AccessibleHypertext2.h"
+#include "AccessibleTable2.h"
+#include "AccessibleTableCell.h"
+
+#include "AccessibleDocument_i.c"
+#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, NEWEST_IA2_IID, 2},
+ {IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 3,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
+ {IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 1,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
+ {IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 1,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
+ {IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF,
+ IID_IAccessibleHyperlink, 1,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
+ {IID_IAccessibleTable2, 12, 0, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 1,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer}};
+
+// 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()
+ : mIA2Proxy(mozilla::mscom::RegisterProxy(L"ia2marshal.dll")),
+ mAccTypelib(mozilla::mscom::RegisterTypelib(
+ L"oleacc.dll",
+ mozilla::mscom::RegistrationFlags::eUseSystemDirectory)),
+ mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb")),
+ mSdnTypelib(mozilla::mscom::RegisterTypelib(L"AccessibleMarshal.dll")) {
+ auto actCtxResource = mscom::ActCtxResource::GetAccessibilityResource();
+
+ mozilla::mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion>
+ tmpActCtxMTA;
+ mozilla::mscom::EnsureMTA([actCtxResource, &tmpActCtxMTA]() -> void {
+ tmpActCtxMTA.reset(
+ new mozilla::mscom::ActivationContextRegion(actCtxResource));
+ });
+ mActCtxMTA = std::move(tmpActCtxMTA);
+
+ mozilla::mscom::InterceptorLog::Init();
+ mozilla::mscom::RegisterArrayData(sPlatformChildArrayData);
+
+ UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
+ mozilla::mscom::EnsureMTA([&customProxy]() -> void {
+ customProxy = mozilla::mscom::RegisterProxy();
+ });
+ mCustomProxy = std::move(customProxy);
+
+ // IA2 needs to be registered in both the main thread's STA as well as the MTA
+ UniquePtr<mozilla::mscom::RegisteredProxy> ia2ProxyMTA;
+ mozilla::mscom::EnsureMTA([&ia2ProxyMTA]() -> void {
+ ia2ProxyMTA = mozilla::mscom::RegisterProxy(L"ia2marshal.dll");
+ });
+ mIA2ProxyMTA = std::move(ia2ProxyMTA);
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/PlatformChild.h b/accessible/ipc/win/PlatformChild.h
new file mode 100644
index 0000000000..a175d6f453
--- /dev/null
+++ b/accessible/ipc/win/PlatformChild.h
@@ -0,0 +1,39 @@
+/* -*- 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/ActivationContext.h"
+#include "mozilla/mscom/Ptr.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:
+ mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> mActCtxMTA;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mIA2ProxyMTA;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_PlatformChild_h
diff --git a/accessible/ipc/win/RemoteAccessible.cpp b/accessible/ipc/win/RemoteAccessible.cpp
new file mode 100644
index 0000000000..f63dca49af
--- /dev/null
+++ b/accessible/ipc/win/RemoteAccessible.cpp
@@ -0,0 +1,943 @@
+/* -*- 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 "RemoteAccessible.h"
+#include "ia2AccessibleRelation.h"
+#include "ia2AccessibleValue.h"
+#include "IGeckoCustom.h"
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "DocAccessible.h"
+#include "mozilla/a11y/DocManager.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/Unused.h"
+#include "mozilla/a11y/Platform.h"
+#include "Relation.h"
+#include "RelationType.h"
+#include "mozilla/a11y/Role.h"
+#include "mozilla/StaticPrefs_accessibility.h"
+
+#include <comutil.h>
+
+static const VARIANT kChildIdSelf = {{{VT_I4}}};
+
+namespace mozilla {
+namespace a11y {
+
+bool RemoteAccessible::GetCOMInterface(void** aOutAccessible) const {
+ if (!aOutAccessible) {
+ return false;
+ }
+
+ // This should never be called if the cache is enabled. We can't get a COM
+ // proxy from the content process in that case. Instead, the code below would
+ // return an MsaaAccessible from our process which would end up calling
+ // methods here in RemoteAccessible, causing infinite recursion.
+ MOZ_ASSERT(!StaticPrefs::accessibility_cache_enabled_AtStartup());
+ if (!mCOMProxy && mSafeToRecurse) {
+ RemoteAccessible* thisPtr = const_cast<RemoteAccessible*>(this);
+ // See if we can lazily obtain a COM proxy
+ MsaaAccessible* msaa = MsaaAccessible::GetFrom(thisPtr);
+ bool isDefunct = false;
+ // NB: Don't pass CHILDID_SELF here, use the absolute MSAA ID. Otherwise
+ // GetIAccessibleFor will recurse into this function and we will just
+ // overflow the stack.
+ VARIANT realId = {{{VT_I4}}};
+ realId.ulVal = msaa->GetExistingID();
+ MOZ_DIAGNOSTIC_ASSERT(realId.ulVal != CHILDID_SELF);
+ thisPtr->mCOMProxy = msaa->GetIAccessibleFor(realId, &isDefunct);
+ }
+
+ RefPtr<IAccessible> addRefed = mCOMProxy;
+ addRefed.forget(aOutAccessible);
+ return !!mCOMProxy;
+}
+
+/**
+ * Specializations of this template map an IAccessible type to its IID
+ */
+template <typename Interface>
+struct InterfaceIID {};
+
+template <>
+struct InterfaceIID<IAccessibleValue> {
+ static REFIID Value() { return IID_IAccessibleValue; }
+};
+
+template <>
+struct InterfaceIID<IAccessibleText> {
+ static REFIID Value() { return IID_IAccessibleText; }
+};
+
+template <>
+struct InterfaceIID<IAccessibleHyperlink> {
+ static REFIID Value() { return IID_IAccessibleHyperlink; }
+};
+
+template <>
+struct InterfaceIID<IGeckoCustom> {
+ static REFIID Value() { return IID_IGeckoCustom; }
+};
+
+template <>
+struct InterfaceIID<IAccessible2_2> {
+ static REFIID Value() { return IID_IAccessible2_2; }
+};
+
+/**
+ * Get the COM proxy for this proxy accessible and QueryInterface it with the
+ * correct IID
+ */
+template <typename Interface>
+static already_AddRefed<Interface> QueryInterface(
+ const RemoteAccessible* aProxy) {
+ RefPtr<IAccessible> acc;
+ if (!aProxy->GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return nullptr;
+ }
+
+ RefPtr<Interface> acc2;
+ if (FAILED(acc->QueryInterface(InterfaceIID<Interface>::Value(),
+ (void**)getter_AddRefs(acc2)))) {
+ return nullptr;
+ }
+
+ return acc2.forget();
+}
+
+static Maybe<uint64_t> GetIdFor(DocAccessibleParent* aDoc,
+ IUnknown* aCOMProxy) {
+ RefPtr<IGeckoCustom> custom;
+ if (FAILED(aCOMProxy->QueryInterface(IID_IGeckoCustom,
+ (void**)getter_AddRefs(custom)))) {
+ return Nothing();
+ }
+
+ uint64_t id;
+ if (FAILED(custom->get_ID(&id))) {
+ return Nothing();
+ }
+
+ return Some(id);
+}
+
+static RemoteAccessible* GetProxyFor(DocAccessibleParent* aDoc,
+ IUnknown* aCOMProxy) {
+ if (auto id = GetIdFor(aDoc, aCOMProxy)) {
+ return aDoc->GetAccessible(*id);
+ }
+ return nullptr;
+}
+
+ENameValueFlag RemoteAccessible::Name(nsString& aName) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Name(aName);
+ }
+
+ /* The return values here exist only to match behvaiour required
+ * by the header declaration of this function. On Mac, we'd like
+ * to return the associated ENameValueFlag, but we don't have
+ * access to that here, so we return a dummy eNameOK value instead.
+ */
+ aName.Truncate();
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return eNameOK;
+ }
+
+ BSTR result;
+ HRESULT hr = acc->get_accName(kChildIdSelf, &result);
+ _bstr_t resultWrap(result, false);
+ if (FAILED(hr)) {
+ return eNameOK;
+ }
+ aName = (wchar_t*)resultWrap;
+ if (!resultWrap) {
+ aName.SetIsVoid(true);
+ }
+ return eNameOK;
+}
+
+void RemoteAccessible::Value(nsString& aValue) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ RemoteAccessibleBase<RemoteAccessible>::Value(aValue);
+ return;
+ }
+
+ aValue.Truncate();
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return;
+ }
+
+ BSTR result;
+ HRESULT hr = acc->get_accValue(kChildIdSelf, &result);
+ _bstr_t resultWrap(result, false);
+ if (FAILED(hr)) {
+ return;
+ }
+ aValue = (wchar_t*)resultWrap;
+}
+
+double RemoteAccessible::Step() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Step();
+ }
+
+ RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+ if (!custom) {
+ return 0;
+ }
+
+ double increment;
+ HRESULT hr = custom->get_minimumIncrement(&increment);
+ if (FAILED(hr)) {
+ return 0;
+ }
+
+ return increment;
+}
+
+void RemoteAccessible::Description(nsString& aDesc) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Description(aDesc);
+ }
+
+ aDesc.Truncate();
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return;
+ }
+
+ BSTR result;
+ HRESULT hr = acc->get_accDescription(kChildIdSelf, &result);
+ _bstr_t resultWrap(result, false);
+ if (FAILED(hr)) {
+ return;
+ }
+ aDesc = (wchar_t*)resultWrap;
+}
+
+uint64_t RemoteAccessible::State() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::State();
+ }
+
+ RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+ if (!custom) {
+ return 0;
+ }
+
+ uint64_t state;
+ HRESULT hr = custom->get_mozState(&state);
+ if (FAILED(hr)) {
+ return 0;
+ }
+ return state;
+}
+
+LayoutDeviceIntRect RemoteAccessible::Bounds() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Bounds();
+ }
+
+ LayoutDeviceIntRect rect;
+
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return rect;
+ }
+
+ long left;
+ long top;
+ long width;
+ long height;
+ HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
+ if (FAILED(hr)) {
+ return rect;
+ }
+ rect.SetRect(left, top, width, height);
+ return rect;
+}
+
+nsIntRect RemoteAccessible::BoundsInCSSPixels() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::BoundsInCSSPixels();
+ }
+
+ RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+ if (!custom) {
+ return nsIntRect();
+ }
+
+ nsIntRect rect;
+ Unused << custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width,
+ &rect.height);
+ return rect;
+}
+
+void RemoteAccessible::Language(nsString& aLocale) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ aLocale.Truncate();
+ return;
+ }
+ 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;
+ }
+ }
+}
+
+static bool IsEscapedChar(const wchar_t c) {
+ return c == L'\\' || c == L':' || c == ',' || c == '=' || c == ';';
+}
+
+// XXX: This creates an all-strings AccAttributes, this isn't ideal
+// but an OK temporary stop-gap before IPC goes full IPDL.
+static bool ConvertBSTRAttributesToAccAttributes(
+ const nsAString& aStr, RefPtr<AccAttributes>& aAttrs) {
+ enum { eName = 0, eValue = 1, eNumStates } state;
+ nsString tokens[eNumStates];
+ auto itr = aStr.BeginReading(), end = aStr.EndReading();
+
+ 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;
+ RefPtr<nsAtom> nameAtom = NS_Atomize(tokens[eName]);
+ aAttrs->SetAttribute(nameAtom, std::move(tokens[eValue]));
+ tokens[eName].Truncate();
+ ++itr;
+ continue;
+ }
+ default:
+ break;
+ }
+ tokens[state] += *itr;
+ ++itr;
+ }
+ return true;
+}
+
+already_AddRefed<AccAttributes> RemoteAccessible::Attributes() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::Attributes();
+ }
+ RefPtr<AccAttributes> attrsObj = new AccAttributes();
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return attrsObj.forget();
+ }
+
+ RefPtr<IAccessible2> acc2;
+ if (FAILED(acc->QueryInterface(IID_IAccessible2,
+ (void**)getter_AddRefs(acc2)))) {
+ return attrsObj.forget();
+ }
+
+ BSTR attrs;
+ HRESULT hr = acc2->get_attributes(&attrs);
+ _bstr_t attrsWrap(attrs, false);
+ if (FAILED(hr)) {
+ return attrsObj.forget();
+ }
+
+ ConvertBSTRAttributesToAccAttributes(
+ nsDependentString((wchar_t*)attrs, attrsWrap.length()), attrsObj);
+ return attrsObj.forget();
+}
+
+Relation RemoteAccessible::RelationByType(RelationType aType) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::RelationByType(aType);
+ }
+
+ RefPtr<IAccessible2_2> acc = QueryInterface<IAccessible2_2>(this);
+ if (!acc) {
+ return Relation();
+ }
+
+ _bstr_t relationType;
+ for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
+ if (aType == sRelationTypePairs[idx].first) {
+ relationType = sRelationTypePairs[idx].second;
+ break;
+ }
+ }
+
+ if (!relationType) {
+ return Relation();
+ }
+
+ IUnknown** targets;
+ long nTargets = 0;
+ HRESULT hr =
+ acc->get_relationTargetsOfType(relationType, 0, &targets, &nTargets);
+ if (FAILED(hr)) {
+ return Relation();
+ }
+
+ nsTArray<uint64_t> ids;
+ for (long idx = 0; idx < nTargets; idx++) {
+ IUnknown* target = targets[idx];
+ if (auto id = GetIdFor(Document(), target)) {
+ ids.AppendElement(*id);
+ }
+ target->Release();
+ }
+ CoTaskMemFree(targets);
+
+ return Relation(new RemoteAccIterator(std::move(ids), Document()));
+}
+
+double RemoteAccessible::CurValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::CurValue();
+ }
+
+ RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+ if (!acc) {
+ return UnspecifiedNaN<double>();
+ }
+
+ VARIANT currentValue;
+ HRESULT hr = acc->get_currentValue(&currentValue);
+ if (FAILED(hr) || currentValue.vt != VT_R8) {
+ return UnspecifiedNaN<double>();
+ }
+
+ return currentValue.dblVal;
+}
+
+bool RemoteAccessible::SetCurValue(double aValue) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return false;
+ }
+ RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+ if (!acc) {
+ return false;
+ }
+
+ VARIANT currentValue;
+ VariantInit(&currentValue);
+ currentValue.vt = VT_R8;
+ currentValue.dblVal = aValue;
+ HRESULT hr = acc->setCurrentValue(currentValue);
+ return SUCCEEDED(hr);
+}
+
+double RemoteAccessible::MinValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::MinValue();
+ }
+
+ RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+ if (!acc) {
+ return UnspecifiedNaN<double>();
+ }
+
+ VARIANT minimumValue;
+ HRESULT hr = acc->get_minimumValue(&minimumValue);
+ if (FAILED(hr) || minimumValue.vt != VT_R8) {
+ return UnspecifiedNaN<double>();
+ }
+
+ return minimumValue.dblVal;
+}
+
+double RemoteAccessible::MaxValue() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::MaxValue();
+ }
+
+ RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+ if (!acc) {
+ return UnspecifiedNaN<double>();
+ }
+
+ VARIANT maximumValue;
+ HRESULT hr = acc->get_maximumValue(&maximumValue);
+ if (FAILED(hr) || maximumValue.vt != VT_R8) {
+ return UnspecifiedNaN<double>();
+ }
+
+ return maximumValue.dblVal;
+}
+
+static IA2TextBoundaryType GetIA2TextBoundary(
+ AccessibleTextBoundary aGeckoBoundaryType) {
+ switch (aGeckoBoundaryType) {
+ case nsIAccessibleText::BOUNDARY_CHAR:
+ return IA2_TEXT_BOUNDARY_CHAR;
+ case nsIAccessibleText::BOUNDARY_WORD_START:
+ return IA2_TEXT_BOUNDARY_WORD;
+ case nsIAccessibleText::BOUNDARY_LINE_START:
+ return IA2_TEXT_BOUNDARY_LINE;
+ case nsIAccessibleText::BOUNDARY_PARAGRAPH:
+ return IA2_TEXT_BOUNDARY_PARAGRAPH;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+int32_t RemoteAccessible::OffsetAtPoint(int32_t aX, int32_t aY,
+ uint32_t aCoordinateType) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::OffsetAtPoint(
+ aX, aY, aCoordinateType);
+ }
+
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return -1;
+ }
+
+ IA2CoordinateType coordType;
+ if (aCoordinateType ==
+ nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
+ coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
+ } else if (aCoordinateType ==
+ nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
+ coordType = IA2_COORDTYPE_PARENT_RELATIVE;
+ } else {
+ MOZ_CRASH("unsupported coord type");
+ }
+
+ long offset;
+ HRESULT hr = acc->get_offsetAtPoint(
+ static_cast<long>(aX), static_cast<long>(aY), coordType, &offset);
+ if (FAILED(hr)) {
+ return -1;
+ }
+
+ return static_cast<int32_t>(offset);
+}
+
+void RemoteAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
+ nsAString& aText) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextSubstring(
+ aStartOffset, aEndOffset, aText);
+ }
+
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ BSTR result;
+ HRESULT hr = acc->get_text(static_cast<long>(aStartOffset),
+ static_cast<long>(aEndOffset), &result);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ _bstr_t resultWrap(result, false);
+ aText = (wchar_t*)result;
+}
+
+void RemoteAccessible::TextBeforeOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset,
+ int32_t* aEndOffset, nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextBeforeOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ BSTR result;
+ long start, end;
+ HRESULT hr = acc->get_textBeforeOffset(
+ aOffset, GetIA2TextBoundary(aBoundaryType), &start, &end, &result);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ _bstr_t resultWrap(result, false);
+ *aStartOffset = start;
+ *aEndOffset = end;
+ aText = (wchar_t*)result;
+}
+
+void RemoteAccessible::TextAfterOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset,
+ int32_t* aEndOffset, nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextAfterOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ BSTR result;
+ long start, end;
+ HRESULT hr = acc->get_textAfterOffset(
+ aOffset, GetIA2TextBoundary(aBoundaryType), &start, &end, &result);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ _bstr_t resultWrap(result, false);
+ aText = (wchar_t*)result;
+ *aStartOffset = start;
+ *aEndOffset = end;
+}
+
+void RemoteAccessible::TextAtOffset(int32_t aOffset,
+ AccessibleTextBoundary aBoundaryType,
+ int32_t* aStartOffset, int32_t* aEndOffset,
+ nsAString& aText) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TextAtOffset(
+ aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ BSTR result;
+ long start, end;
+ HRESULT hr = acc->get_textAtOffset(aOffset, GetIA2TextBoundary(aBoundaryType),
+ &start, &end, &result);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ _bstr_t resultWrap(result, false);
+ aText = (wchar_t*)result;
+ *aStartOffset = start;
+ *aEndOffset = end;
+}
+
+bool RemoteAccessible::AddToSelection(int32_t aStartOffset,
+ int32_t aEndOffset) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return false;
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return false;
+ }
+
+ return SUCCEEDED(acc->addSelection(static_cast<long>(aStartOffset),
+ static_cast<long>(aEndOffset)));
+}
+
+bool RemoteAccessible::RemoveFromSelection(int32_t aSelectionNum) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return false;
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return false;
+ }
+
+ return SUCCEEDED(acc->removeSelection(static_cast<long>(aSelectionNum)));
+}
+
+int32_t RemoteAccessible::CaretOffset() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::CaretOffset();
+ }
+
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return -1;
+ }
+
+ long offset;
+ HRESULT hr = acc->get_caretOffset(&offset);
+ if (FAILED(hr)) {
+ return -1;
+ }
+
+ return static_cast<int32_t>(offset);
+}
+
+void RemoteAccessible::SetCaretOffset(int32_t aOffset) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::SetCaretOffset(aOffset);
+ }
+
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ acc->setCaretOffset(static_cast<long>(aOffset));
+}
+
+/**
+ * aScrollType should be one of the nsIAccessiblescrollType constants.
+ */
+void RemoteAccessible::ScrollSubstringTo(int32_t aStartOffset,
+ int32_t aEndOffset,
+ uint32_t aScrollType) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return;
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ acc->scrollSubstringTo(static_cast<long>(aStartOffset),
+ static_cast<long>(aEndOffset),
+ static_cast<IA2ScrollType>(aScrollType));
+}
+
+/**
+ * aCoordinateType is one of the nsIAccessibleCoordinateType constants.
+ */
+void RemoteAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
+ int32_t aEndOffset,
+ uint32_t aCoordinateType,
+ int32_t aX, int32_t aY) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return;
+ }
+ RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+ if (!acc) {
+ return;
+ }
+
+ IA2CoordinateType coordType;
+ if (aCoordinateType ==
+ nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
+ coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
+ } else if (aCoordinateType ==
+ nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
+ coordType = IA2_COORDTYPE_PARENT_RELATIVE;
+ } else {
+ MOZ_CRASH("unsupported coord type");
+ }
+
+ acc->scrollSubstringToPoint(static_cast<long>(aStartOffset),
+ static_cast<long>(aEndOffset), coordType,
+ static_cast<long>(aX), static_cast<long>(aY));
+}
+
+bool RemoteAccessible::IsLinkValid() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return false;
+ }
+ RefPtr<IAccessibleHyperlink> acc = QueryInterface<IAccessibleHyperlink>(this);
+ if (!acc) {
+ return false;
+ }
+
+ boolean valid;
+ if (FAILED(acc->get_valid(&valid))) {
+ return false;
+ }
+
+ return valid;
+}
+
+uint32_t RemoteAccessible::AnchorCount(bool* aOk) {
+ *aOk = false;
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return 0;
+ }
+ RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+ if (!custom) {
+ return 0;
+ }
+
+ long count;
+ if (FAILED(custom->get_anchorCount(&count))) {
+ return 0;
+ }
+
+ *aOk = true;
+ return count;
+}
+
+RemoteAccessible* RemoteAccessible::AnchorAt(uint32_t aIdx) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ // Not yet supported by the cache.
+ return nullptr;
+ }
+ RefPtr<IAccessibleHyperlink> link =
+ QueryInterface<IAccessibleHyperlink>(this);
+ if (!link) {
+ return nullptr;
+ }
+
+ VARIANT anchor;
+ if (FAILED(link->get_anchor(aIdx, &anchor))) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(anchor.vt == VT_UNKNOWN);
+ RemoteAccessible* proxyAnchor = GetProxyFor(Document(), anchor.punkVal);
+ anchor.punkVal->Release();
+ return proxyAnchor;
+}
+
+void RemoteAccessible::DOMNodeID(nsString& aID) const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::DOMNodeID(aID);
+ }
+
+ aID.Truncate();
+ RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+ if (!custom) {
+ return;
+ }
+
+ BSTR result;
+ HRESULT hr = custom->get_DOMNodeID(&result);
+ _bstr_t resultWrap(result, false);
+ if (FAILED(hr)) {
+ return;
+ }
+ aID = (wchar_t*)resultWrap;
+}
+
+void RemoteAccessible::TakeFocus() const {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::TakeFocus();
+ }
+ RefPtr<IAccessible> acc;
+ if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
+ return;
+ }
+ acc->accSelect(SELFLAG_TAKEFOCUS, kChildIdSelf);
+}
+
+Accessible* RemoteAccessible::ChildAtPoint(
+ int32_t aX, int32_t aY, Accessible::EWhichChildAtPoint aWhichChild) {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::ChildAtPoint(aX, aY,
+ aWhichChild);
+ }
+
+ RefPtr<IAccessible2_2> target = QueryInterface<IAccessible2_2>(this);
+ if (!target) {
+ return nullptr;
+ }
+ DocAccessibleParent* doc = Document();
+ RemoteAccessible* proxy = this;
+ // accHitTest only does direct children, but we might want the deepest child.
+ for (;;) {
+ VARIANT childVar;
+ if (FAILED(target->accHitTest(aX, aY, &childVar)) ||
+ childVar.vt == VT_EMPTY) {
+ return nullptr;
+ }
+ if (childVar.vt == VT_I4 && childVar.lVal == CHILDID_SELF) {
+ break;
+ }
+ MOZ_ASSERT(childVar.vt == VT_DISPATCH && childVar.pdispVal);
+ target = nullptr;
+ childVar.pdispVal->QueryInterface(IID_IAccessible2_2,
+ getter_AddRefs(target));
+ childVar.pdispVal->Release();
+ if (!target) {
+ return nullptr;
+ }
+ // We can't always use GetProxyFor because it can't cross document
+ // boundaries.
+ if (proxy->ChildCount() == 1) {
+ proxy = proxy->RemoteChildAt(0);
+ if (proxy->IsDoc()) {
+ // We're crossing into a child document.
+ doc = proxy->AsDoc();
+ }
+ } else {
+ proxy = GetProxyFor(doc, target);
+ }
+ if (aWhichChild == Accessible::EWhichChildAtPoint::DirectChild) {
+ break;
+ }
+ }
+ return proxy;
+}
+
+GroupPos RemoteAccessible::GroupPosition() {
+ if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+ return RemoteAccessibleBase<RemoteAccessible>::GroupPosition();
+ }
+
+ // This is only supported when cache is enabled.
+ return GroupPos();
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/RemoteAccessible.h b/accessible/ipc/win/RemoteAccessible.h
new file mode 100644
index 0000000000..d3aace3607
--- /dev/null
+++ b/accessible/ipc/win/RemoteAccessible.h
@@ -0,0 +1,76 @@
+/* -*- 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_RemoteAccessible_h
+#define mozilla_a11y_RemoteAccessible_h
+
+#include "LocalAccessible.h"
+#include "mozilla/a11y/RemoteAccessibleBase.h"
+#include "mozilla/a11y/Role.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsRect.h"
+
+#include <oleacc.h>
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Windows specific functionality for an accessibility tree node that originated
+ * in the parent process.
+ */
+class RemoteAccessible : public RemoteAccessibleBase<RemoteAccessible> {
+ public:
+ RemoteAccessible(uint64_t aID, RemoteAccessible* aParent,
+ DocAccessibleParent* aDoc, role aRole, AccType aType,
+ AccGenericType aGenericTypes, uint8_t aRoleMapEntryIndex)
+ : RemoteAccessibleBase(aID, aParent, aDoc, aRole, aType, aGenericTypes,
+ aRoleMapEntryIndex) {
+ MOZ_COUNT_CTOR(RemoteAccessible);
+ }
+
+ MOZ_COUNTED_DTOR(RemoteAccessible)
+
+#include "mozilla/a11y/RemoteAccessibleShared.h"
+
+ virtual void TakeFocus() const override;
+ virtual void SetCaretOffset(int32_t aOffset) override;
+
+ bool GetCOMInterface(void** aOutAccessible) const;
+ void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible) {
+ if (aIAccessible) {
+ mCOMProxy = aIAccessible;
+ } else {
+ // If we were supposed to be receiving an interface (hence the call to
+ // this function), but the interface turns out to be null, then we're
+ // broken for some reason.
+ mSafeToRecurse = false;
+ }
+ }
+
+ protected:
+ explicit RemoteAccessible(DocAccessibleParent* aThisAsDoc)
+ : RemoteAccessibleBase(aThisAsDoc) {
+ MOZ_COUNT_CTOR(RemoteAccessible);
+ }
+
+ private:
+ RefPtr<IAccessible> mCOMProxy;
+ bool mSafeToRecurse = true;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// RemoteAccessible downcasting method
+
+inline RemoteAccessible* Accessible::AsRemote() {
+ return IsRemote() ? static_cast<RemoteAccessible*>(this) : nullptr;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/ipc/win/handler/AccessibleHandler.cpp b/accessible/ipc/win/handler/AccessibleHandler.cpp
new file mode 100644
index 0000000000..bf70af4f35
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -0,0 +1,2189 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include "AccessibleHandler.h"
+#include "AccessibleHandlerControl.h"
+#include "HandlerChildEnumerator.h"
+#include "HandlerRelation.h"
+
+#include "Factory.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/a11y/HandlerDataCleanup.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/UniquePtr.h"
+
+#include <objbase.h>
+#include <uiautomation.h>
+#include <winreg.h>
+
+#include "AccessibleHypertext.h"
+#include "AccessibleHypertext2.h"
+#include "AccessibleRole.h"
+#include "Accessible2_i.c"
+#include "Accessible2_2_i.c"
+#include "Accessible2_3_i.c"
+#include "AccessibleAction_i.c"
+#include "AccessibleHyperlink_i.c"
+#include "AccessibleHypertext_i.c"
+#include "AccessibleHypertext2_i.c"
+#include "AccessibleTable_i.c"
+#include "AccessibleTable2_i.c"
+#include "AccessibleTableCell_i.c"
+#include "AccessibleText_i.c"
+
+namespace mozilla {
+namespace a11y {
+
+// Must be kept in sync with kClassNameTabContent in
+// accessible/windows/msaa/nsWinUtils.h.
+const WCHAR kEmulatedWindowClassName[] = L"MozillaContentWindowClass";
+const uint32_t kEmulatedWindowClassNameNChars =
+ sizeof(kEmulatedWindowClassName) / sizeof(WCHAR);
+// Mask to get the content process portion of a Windows accessible unique id.
+// This is bits 23 through 30 (LSB 0) of the id. This must be kept in sync
+// with kNumContentProcessIDBits in accessible/windows/msaa/MsaaIdGenerator.cpp.
+const uint32_t kIdContentProcessMask = 0x7F800000;
+
+static mscom::Factory<AccessibleHandler> sHandlerFactory;
+
+HRESULT
+AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface) {
+ if (!aOutInterface || !aOuter || aIid != IID_IUnknown) {
+ return E_INVALIDARG;
+ }
+
+ *aOutInterface = nullptr;
+
+ HRESULT hr;
+ RefPtr<AccessibleHandler> handler(new AccessibleHandler(aOuter, &hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return handler->InternalQueryInterface(aIid, aOutInterface);
+}
+
+AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
+ : mscom::Handler(aOuter, aResult),
+ mDispatch(nullptr),
+ mIA2PassThru(nullptr),
+ mServProvPassThru(nullptr),
+ mIAHyperlinkPassThru(nullptr),
+ mIATableCellPassThru(nullptr),
+ mIAHypertextPassThru(nullptr),
+ mCachedData(),
+ mCachedDynamicDataMarshaledByCom(false),
+ mCacheGen(0),
+ mCachedHyperlinks(nullptr),
+ mCachedNHyperlinks(-1),
+ mCachedTextAttribRuns(nullptr),
+ mCachedNTextAttribRuns(-1),
+ mCachedRelations(nullptr),
+ mCachedNRelations(-1),
+ mIsEmulatedWindow(false) {
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+ MOZ_ASSERT(ctl);
+ if (!ctl) {
+ if (aResult) {
+ *aResult = E_UNEXPECTED;
+ }
+ return;
+ }
+
+ mCacheGen = ctl->GetCacheGen();
+}
+
+AccessibleHandler::~AccessibleHandler() {
+ CleanupDynamicIA2Data(mCachedData.mDynamicData,
+ mCachedDynamicDataMarshaledByCom);
+ if (mCachedData.mGeckoBackChannel) {
+ mCachedData.mGeckoBackChannel->Release();
+ }
+ ClearTextCache();
+ ClearRelationCache();
+}
+
+HRESULT
+AccessibleHandler::ResolveIA2() {
+ if (mIA2PassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = proxy->QueryInterface(NEWEST_IA2_IID,
+ reinterpret_cast<void**>(&mIA2PassThru));
+ if (SUCCEEDED(hr)) {
+ // mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
+ mIA2PassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::ResolveIAHyperlink() {
+ if (mIAHyperlinkPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr =
+ proxy->QueryInterface(IID_IAccessibleHyperlink,
+ reinterpret_cast<void**>(&mIAHyperlinkPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIAHyperlinkPassThru is a weak reference
+ // (see comments in AccesssibleHandler.h)
+ mIAHyperlinkPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::ResolveIATableCell() {
+ if (mIATableCellPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr =
+ proxy->QueryInterface(IID_IAccessibleTableCell,
+ reinterpret_cast<void**>(&mIATableCellPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIATableCellPassThru is a weak reference
+ // (see comments in AccesssibleHandler.h)
+ mIATableCellPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::ResolveIAHypertext() {
+ if (mIAHypertextPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr =
+ proxy->QueryInterface(IID_IAccessibleHypertext2,
+ reinterpret_cast<void**>(&mIAHypertextPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIAHypertextPassThru is a weak reference
+ // (see comments in AccessibleHandler.h)
+ mIAHypertextPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::MaybeUpdateCachedData() {
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+
+ uint32_t gen = ctl->GetCacheGen();
+ if (gen == mCacheGen) {
+ // Cache is already up to date.
+ return S_OK;
+ }
+
+ if (!mCachedData.mGeckoBackChannel) {
+ return E_POINTER;
+ }
+
+ // While we're making the outgoing COM call below, an incoming COM call can
+ // be handled which calls ReadHandlerPayload or re-enters this function.
+ // Therefore, we mustn't update the cached data directly lest it be mutated
+ // elsewhere before the outgoing COM call returns and cause corruption or
+ // memory leaks. Instead, pass a temporary struct and update the cached data
+ // only after this call completes.
+ DynamicIA2Data newData;
+ HRESULT hr = mCachedData.mGeckoBackChannel->Refresh(&newData);
+ if (SUCCEEDED(hr)) {
+ // Clean up the old data.
+ CleanupDynamicIA2Data(mCachedData.mDynamicData,
+ mCachedDynamicDataMarshaledByCom);
+ mCachedData.mDynamicData = newData;
+ mCachedDynamicDataMarshaledByCom = true;
+ // We just updated the cache, so update this object's cache generation
+ // so we only update the cache again after the next change.
+ mCacheGen = gen;
+ }
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::GetAllTextInfo(BSTR* aText) {
+ MOZ_ASSERT(mCachedData.mGeckoBackChannel);
+
+ ClearTextCache();
+
+ return mCachedData.mGeckoBackChannel->get_AllTextInfo(
+ aText, &mCachedHyperlinks, &mCachedNHyperlinks, &mCachedTextAttribRuns,
+ &mCachedNTextAttribRuns);
+}
+
+void AccessibleHandler::ClearTextCache() {
+ if (mCachedNHyperlinks >= 0) {
+ // We cached hyperlinks, but the caller never retrieved them.
+ for (long index = 0; index < mCachedNHyperlinks; ++index) {
+ mCachedHyperlinks[index]->Release();
+ }
+ // mCachedHyperlinks might already be null if there are no hyperlinks.
+ if (mCachedHyperlinks) {
+ ::CoTaskMemFree(mCachedHyperlinks);
+ mCachedHyperlinks = nullptr;
+ }
+ mCachedNHyperlinks = -1;
+ }
+
+ if (mCachedTextAttribRuns) {
+ for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
+ if (mCachedTextAttribRuns[index].text) {
+ // The caller never requested this attribute run.
+ ::SysFreeString(mCachedTextAttribRuns[index].text);
+ }
+ }
+ // This array is internal to us, so we must always free it.
+ ::CoTaskMemFree(mCachedTextAttribRuns);
+ mCachedTextAttribRuns = nullptr;
+ mCachedNTextAttribRuns = -1;
+ }
+}
+
+HRESULT
+AccessibleHandler::GetRelationsInfo() {
+ MOZ_ASSERT(mCachedData.mGeckoBackChannel);
+
+ ClearRelationCache();
+
+ return mCachedData.mGeckoBackChannel->get_RelationsInfo(&mCachedRelations,
+ &mCachedNRelations);
+}
+
+void AccessibleHandler::ClearRelationCache() {
+ if (mCachedNRelations == -1) {
+ // No cache; nothing to do.
+ return;
+ }
+
+ // We cached relations, but the client never retrieved them.
+ if (mCachedRelations) {
+ for (long index = 0; index < mCachedNRelations; ++index) {
+ IARelationData& relData = mCachedRelations[index];
+ if (relData.mType) {
+ ::SysFreeString(relData.mType);
+ }
+ }
+ // This array is internal to us, so we must always free it.
+ ::CoTaskMemFree(mCachedRelations);
+ mCachedRelations = nullptr;
+ }
+ mCachedNRelations = -1;
+}
+
+HRESULT
+AccessibleHandler::ResolveIDispatch() {
+ if (mDispatch) {
+ return S_OK;
+ }
+
+ HRESULT hr;
+
+ if (!mDispatchUnk) {
+ RefPtr<AccessibleHandlerControl> ctl(
+ gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+
+ RefPtr<ITypeInfo> typeinfo;
+ hr = ctl->GetHandlerTypeInfo(getter_AddRefs(typeinfo));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = ::CreateStdDispatch(GetOuter(),
+ static_cast<NEWEST_IA2_INTERFACE*>(this), typeinfo,
+ getter_AddRefs(mDispatchUnk));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ hr = mDispatchUnk->QueryInterface(IID_IDispatch,
+ reinterpret_cast<void**>(&mDispatch));
+ if (SUCCEEDED(hr)) {
+ // mDispatch is weak (see comments in AccessibleHandler.h)
+ mDispatch->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
+ void** aOutInterface) {
+ MOZ_ASSERT(aProxyUnknown);
+
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if (aIid == IID_IDispatch || aIid == IID_IAccessible2_3 ||
+ aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2 ||
+ aIid == IID_IAccessible) {
+ RefPtr<NEWEST_IA2_INTERFACE> ia2(static_cast<NEWEST_IA2_INTERFACE*>(this));
+ ia2.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IServiceProvider) {
+ RefPtr<IServiceProvider> svcProv(static_cast<IServiceProvider*>(this));
+ svcProv.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (HasPayload()) {
+ // The proxy manager caches interfaces marshaled in the payload
+ // and returns them on QI without a cross-process call.
+ // However, it doesn't know about interfaces which don't exist.
+ // We can determine this from the payload.
+ if (((aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
+ aIid == IID_IAccessibleHypertext2) &&
+ !mCachedData.mStaticData.mIAHypertext) ||
+ ((aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) &&
+ !mCachedData.mStaticData.mIAHyperlink) ||
+ (aIid == IID_IAccessibleTable && !mCachedData.mStaticData.mIATable) ||
+ (aIid == IID_IAccessibleTable2 && !mCachedData.mStaticData.mIATable2) ||
+ (aIid == IID_IAccessibleTableCell &&
+ !mCachedData.mStaticData.mIATableCell)) {
+ // We already know this interface is not available, so don't query
+ // the proxy, thus avoiding a pointless cross-process call.
+ // If we return E_NOINTERFACE here, mscom::Handler will try the COM
+ // proxy. S_FALSE signals that the proxy should not be tried.
+ return S_FALSE;
+ }
+ }
+
+ if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) {
+ RefPtr<IAccessibleHyperlink> iaLink(
+ static_cast<IAccessibleHyperlink*>(this));
+ iaLink.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IAccessibleTableCell) {
+ RefPtr<IAccessibleTableCell> iaCell(
+ static_cast<IAccessibleTableCell*>(this));
+ iaCell.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
+ aIid == IID_IAccessibleHypertext2) {
+ RefPtr<IAccessibleHypertext2> iaHt(
+ static_cast<IAccessibleHypertext2*>(this));
+ iaHt.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IProvideClassInfo) {
+ RefPtr<IProvideClassInfo> clsInfo(this);
+ clsInfo.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IEnumVARIANT && mCachedData.mGeckoBackChannel) {
+ if (mCachedData.mDynamicData.mChildCount == 0) {
+ return E_NOINTERFACE;
+ }
+ if (!mCachedData.mStaticData.mIAHypertext &&
+ mCachedData.mDynamicData.mChildCount == 1) {
+ // This might be an OOP iframe. (We can't check the role because it might
+ // be overridden by ARIA.) HandlerChildEnumerator works fine for iframes
+ // rendered in the same content process. However, for out-of-process
+ // iframes, HandlerProvider::get_AllChildren (called by
+ // HandlerChildEnumerator) will fail. This is because we only send down
+ // an IDispatch COM proxy for the embedded document, but get_AllChildren
+ // will try to QueryInterface this to IAccessible2 to reduce QI calls
+ // from the parent process. Because the content process is sandboxed,
+ // it can't make the outgoing COM call to QI the proxy from IDispatch to
+ // IAccessible2 and so it fails.
+ // Since this Accessible only has one child anyway, we don't need the bulk
+ // fetch optimization offered by HandlerChildEnumerator or even
+ // IEnumVARIANT. Therefore, we explicitly tell the client this interface
+ // is not supported, which will cause the oleacc AccessibleChildren
+ // function to fall back to accChild. If we return E_NOINTERFACE here,
+ // mscom::Handler will try the COM proxy. S_FALSE signals that the proxy
+ // should not be tried.
+ return S_FALSE;
+ }
+ RefPtr<IEnumVARIANT> childEnum(
+ new HandlerChildEnumerator(this, mCachedData.mGeckoBackChannel));
+ childEnum.forget(aOutInterface);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+HRESULT
+AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid) {
+ if (!aStream) {
+ return E_INVALIDARG;
+ }
+
+ mscom::StructFromStream deserializer(aStream);
+ if (!deserializer) {
+ return E_FAIL;
+ }
+ if (deserializer.IsEmpty()) {
+ return S_FALSE;
+ }
+
+ // QueryHandlerInterface might get called while we deserialize the payload,
+ // but that checks the interface pointers in the payload to determine what
+ // interfaces are available. Therefore, deserialize into a temporary struct
+ // and update mCachedData only after deserialization completes.
+ // The decoding functions can misbehave if their target memory is not zeroed
+ // beforehand, so ensure we do that.
+ IA2Payload newData{};
+ if (!deserializer.Read(&newData, &IA2Payload_Decode)) {
+ return E_FAIL;
+ }
+ // Clean up the old data.
+ CleanupDynamicIA2Data(mCachedData.mDynamicData,
+ mCachedDynamicDataMarshaledByCom);
+ mCachedData = newData;
+ mCachedDynamicDataMarshaledByCom = false;
+
+ // These interfaces have been aggregated into the proxy manager.
+ // The proxy manager will resolve these interfaces now on QI,
+ // so we can release these pointers.
+ // However, we don't null them out because we use their presence
+ // to determine whether the interface is available
+ // so as to avoid pointless cross-proc QI calls returning E_NOINTERFACE.
+ // Note that if pointers to other objects (in contrast to
+ // interfaces of *this* object) are added in future, we should not release
+ // those pointers.
+ ReleaseStaticIA2DataInterfaces(mCachedData.mStaticData);
+
+ WCHAR className[kEmulatedWindowClassNameNChars];
+ if (mCachedData.mDynamicData.mHwnd &&
+ ::GetClassName(
+ reinterpret_cast<HWND>(uintptr_t(mCachedData.mDynamicData.mHwnd)),
+ className, kEmulatedWindowClassNameNChars) > 0 &&
+ wcscmp(className, kEmulatedWindowClassName) == 0) {
+ mIsEmulatedWindow = true;
+ }
+
+ if (!mCachedData.mGeckoBackChannel) {
+ return S_OK;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+
+ if (mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_COLUMNHEADER ||
+ mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_ROWHEADER) {
+ // Because the same headers can apply to many cells, handler payloads
+ // include the ids of header cells, rather than potentially marshaling the
+ // same objects many times. We need to cache header cells here so we can
+ // get them by id later.
+ ctl->CacheAccessible(mCachedData.mDynamicData.mUniqueId, this);
+ }
+
+ return ctl->Register(WrapNotNull(mCachedData.mGeckoBackChannel));
+}
+
+REFIID
+AccessibleHandler::MarshalAs(REFIID aIid) {
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
+ aIid == IID_IAccessible2 || aIid == IID_IAccessible ||
+ aIid == IID_IDispatch) {
+ return NEWEST_IA2_IID;
+ }
+
+ return aIid;
+}
+
+HRESULT
+AccessibleHandler::GetMarshalInterface(REFIID aMarshalAsIid,
+ NotNull<IUnknown*> aProxy,
+ NotNull<IID*> aOutIid,
+ NotNull<IUnknown**> aOutUnk) {
+ if (aMarshalAsIid == NEWEST_IA2_IID) {
+ *aOutIid = IID_IAccessible;
+ } else {
+ *aOutIid = aMarshalAsIid;
+ }
+
+ return aProxy->QueryInterface(
+ aMarshalAsIid,
+ reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk)));
+}
+
+HRESULT
+AccessibleHandler::GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) {
+ if (!aOutPayloadSize) {
+ return E_INVALIDARG;
+ }
+
+ // If we're sending the payload to somebody else, we'd better make sure that
+ // it is up to date. If the cache update fails then we'll return a 0 payload
+ // size so that we don't transfer obsolete data.
+ if (FAILED(MaybeUpdateCachedData())) {
+ *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
+ return S_OK;
+ }
+
+ mSerializer =
+ MakeUnique<mscom::StructToStream>(mCachedData, &IA2Payload_Encode);
+ if (!mSerializer) {
+ return E_FAIL;
+ }
+
+ *aOutPayloadSize = mSerializer->GetSize();
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::WriteHandlerPayload(IStream* aStream, REFIID aIid) {
+ if (!aStream) {
+ return E_INVALIDARG;
+ }
+
+ if (!mSerializer) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = mSerializer->Write(aStream);
+ mSerializer.reset();
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::QueryInterface(REFIID riid, void** ppv) {
+ return Handler::QueryInterface(riid, ppv);
+}
+
+ULONG
+AccessibleHandler::AddRef() { return Handler::AddRef(); }
+
+ULONG
+AccessibleHandler::Release() { return Handler::Release(); }
+
+HRESULT
+AccessibleHandler::GetTypeInfoCount(UINT* pctinfo) {
+ HRESULT hr = ResolveIDispatch();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mDispatch->GetTypeInfoCount(pctinfo);
+}
+
+HRESULT
+AccessibleHandler::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
+ HRESULT hr = ResolveIDispatch();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
+}
+
+HRESULT
+AccessibleHandler::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+ LCID lcid, DISPID* rgDispId) {
+ HRESULT hr = ResolveIDispatch();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+HRESULT
+AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
+ WORD wFlags, DISPPARAMS* pDispParams,
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
+ UINT* puArgErr) {
+ HRESULT hr = ResolveIDispatch();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
+ pVarResult, pExcepInfo, puArgErr);
+}
+
+#define BEGIN_CACHE_ACCESS \
+ { \
+ HRESULT hr; \
+ if (FAILED(hr = MaybeUpdateCachedData())) { \
+ return hr; \
+ } \
+ }
+
+#define GET_FIELD(member, assignTo) \
+ { assignTo = mCachedData.mDynamicData.member; }
+
+#define GET_BSTR(member, assignTo) \
+ { assignTo = CopyBSTR(mCachedData.mDynamicData.member); }
+
+/*** IAccessible ***/
+
+HRESULT
+AccessibleHandler::get_accParent(IDispatch** ppdispParent) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accParent(ppdispParent);
+}
+
+HRESULT
+AccessibleHandler::get_accChildCount(long* pcountChildren) {
+ if (!pcountChildren) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accChildCount(pcountChildren);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ if (mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_DOCUMENT) {
+ RefPtr<AccessibleHandlerControl> ctl(
+ gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+ if (ctl->IsA11ySuppressedForClipboardCopy()) {
+ // Bug 1798098: Windows Suggested Actions (introduced in Windows 11
+ // 22H2) might walk the document a11y tree using UIA whenever anything
+ // is copied to the clipboard. This causes an unacceptable hang,
+ // particularly when the cache is disabled. Even though we lie about the
+ // selection in nSelections, it falls back to a normal tree walk on the
+ // document if it doesn't get a proper text selection. Prevent that by
+ // returning a 0 child count on the document.
+ *pcountChildren = 0;
+ return S_OK;
+ }
+ }
+
+ GET_FIELD(mChildCount, *pcountChildren);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accChild(VARIANT varChild, IDispatch** ppdispChild) {
+ if (!ppdispChild) {
+ return E_INVALIDARG;
+ }
+ // Unlikely, but we might as well optimize for it
+ if (varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF) {
+ RefPtr<IDispatch> disp(this);
+ disp.forget(ppdispChild);
+ return S_OK;
+ }
+
+ if (mIsEmulatedWindow && varChild.vt == VT_I4 && varChild.lVal < 0 &&
+ (varChild.lVal & kIdContentProcessMask) !=
+ (mCachedData.mDynamicData.mUniqueId & kIdContentProcessMask)) {
+ // Window emulation is enabled and the target id is in a different
+ // process to this accessible.
+ // When window emulation is enabled, each tab document gets its own HWND.
+ // OOP iframes get the same HWND as their tab document and fire events with
+ // that HWND. However, the root accessible for the HWND (the tab document)
+ // can't return accessibles for OOP iframes. Therefore, we must get the root
+ // accessible from the main HWND and call accChild on that instead.
+ // We don't need an oleacc proxy, so send WM_GETOBJECT directly instead of
+ // calling AccessibleObjectFromEvent.
+ HWND rootHwnd = GetParent(
+ reinterpret_cast<HWND>(uintptr_t(mCachedData.mDynamicData.mHwnd)));
+ MOZ_ASSERT(rootHwnd);
+ LRESULT lresult = ::SendMessage(rootHwnd, WM_GETOBJECT, 0, OBJID_CLIENT);
+ if (lresult > 0) {
+ RefPtr<IAccessible2_3> rootAcc;
+ HRESULT hr = ::ObjectFromLresult(lresult, IID_IAccessible2_3, 0,
+ getter_AddRefs(rootAcc));
+ if (hr == S_OK) {
+ return rootAcc->get_accChild(varChild, ppdispChild);
+ }
+ }
+ }
+
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accChild(varChild, ppdispChild);
+}
+
+HRESULT
+AccessibleHandler::get_accName(VARIANT varChild, BSTR* pszName) {
+ if (!pszName) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accName(varChild, pszName);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mName, *pszName);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accValue(VARIANT varChild, BSTR* pszValue) {
+ if (!pszValue) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accValue(varChild, pszValue);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mValue, *pszValue);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accDescription(VARIANT varChild, BSTR* pszDescription) {
+ if (!pszDescription) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accDescription(varChild, pszDescription);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mDescription, *pszDescription);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
+ if (!pvarRole) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accRole(varChild, pvarRole);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ return ::VariantCopy(pvarRole, &mCachedData.mDynamicData.mRole);
+}
+
+HRESULT
+AccessibleHandler::get_accState(VARIANT varChild, VARIANT* pvarState) {
+ if (!pvarState) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accState(varChild, pvarState);
+ }
+
+ pvarState->vt = VT_I4;
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mState, pvarState->lVal);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
+ // This matches what AccessibleWrap does
+ if (!pszHelp) {
+ return E_INVALIDARG;
+ }
+ *pszHelp = nullptr;
+ return S_FALSE;
+}
+
+HRESULT
+AccessibleHandler::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
+ long* pidTopic) {
+ // This matches what AccessibleWrap does
+ if (!pszHelpFile || !pidTopic) {
+ return E_INVALIDARG;
+ }
+ *pszHelpFile = nullptr;
+ *pidTopic = 0;
+ return S_FALSE;
+}
+
+HRESULT
+AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
+ BSTR* pszKeyboardShortcut) {
+ if (!pszKeyboardShortcut) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_accFocus(VARIANT* pvarChild) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accFocus(pvarChild);
+}
+
+HRESULT
+AccessibleHandler::get_accSelection(VARIANT* pvarChildren) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accSelection(pvarChildren);
+}
+
+HRESULT
+AccessibleHandler::get_accDefaultAction(VARIANT varChild,
+ BSTR* pszDefaultAction) {
+ if (!pszDefaultAction) {
+ return E_INVALIDARG;
+ }
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mDefaultAction, *pszDefaultAction);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accSelect(flagsSelect, varChild);
+}
+
+HRESULT
+AccessibleHandler::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
+ long* pcyHeight, VARIANT varChild) {
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
+ varChild);
+ }
+
+ if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
+ return E_INVALIDARG;
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mLeft, *pxLeft);
+ GET_FIELD(mTop, *pyTop);
+ GET_FIELD(mWidth, *pcxWidth);
+ GET_FIELD(mHeight, *pcyHeight);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
+ VARIANT* pvarEndUpAt) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accNavigate(navDir, varStart, pvarEndUpAt);
+}
+
+HRESULT
+AccessibleHandler::accHitTest(long xLeft, long yTop, VARIANT* pvarChild) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accHitTest(xLeft, yTop, pvarChild);
+}
+
+HRESULT
+AccessibleHandler::accDoDefaultAction(VARIANT varChild) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accDoDefaultAction(varChild);
+}
+
+HRESULT
+AccessibleHandler::put_accName(VARIANT varChild, BSTR szName) {
+ // This matches AccessibleWrap
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->put_accValue(varChild, szValue);
+}
+
+/*** IAccessible2 ***/
+
+HRESULT
+AccessibleHandler::get_nRelations(long* nRelations) {
+ if (!nRelations) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ if (mCachedData.mGeckoBackChannel) {
+ // If the caller wants nRelations, they will almost certainly want the
+ // actual relations too.
+ hr = GetRelationsInfo();
+ if (SUCCEEDED(hr)) {
+ *nRelations = mCachedNRelations;
+ return S_OK;
+ }
+ // We fall back to a normal call if this fails.
+ }
+
+ hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_nRelations(nRelations);
+}
+
+HRESULT
+AccessibleHandler::get_relation(long relationIndex,
+ IAccessibleRelation** relation) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_relation(relationIndex, relation);
+}
+
+HRESULT
+AccessibleHandler::get_relations(long maxRelations,
+ IAccessibleRelation** relations,
+ long* nRelations) {
+ if (maxRelations == 0 || !relations || !nRelations) {
+ return E_INVALIDARG;
+ }
+
+ // We currently only support retrieval of *all* cached relations at once.
+ if (mCachedNRelations != -1 && maxRelations >= mCachedNRelations) {
+ for (long index = 0; index < mCachedNRelations; ++index) {
+ IARelationData& relData = mCachedRelations[index];
+ RefPtr<IAccessibleRelation> hrel(new HandlerRelation(this, relData));
+ hrel.forget(&relations[index]);
+ }
+ *nRelations = mCachedNRelations;
+ // Clean up the cache, since we only cache for one call.
+ // We don't use ClearRelationCache here because that scans for data to free
+ // in the array and we don't we need that. The HandlerRelation instances
+ // will handle freeing of the data.
+ ::CoTaskMemFree(mCachedRelations);
+ mCachedRelations = nullptr;
+ mCachedNRelations = -1;
+ return S_OK;
+ }
+
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_relations(maxRelations, relations, nRelations);
+}
+
+HRESULT
+AccessibleHandler::role(long* role) {
+ if (!role) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->role(role);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mIA2Role, *role);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::scrollTo(IA2ScrollType scrollType) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->scrollTo(scrollType);
+}
+
+HRESULT
+AccessibleHandler::scrollToPoint(IA2CoordinateType coordinateType, long x,
+ long y) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->scrollToPoint(coordinateType, x, y);
+}
+
+HRESULT
+AccessibleHandler::get_groupPosition(long* groupLevel,
+ long* similarItemsInGroup,
+ long* positionInGroup) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_groupPosition(groupLevel, similarItemsInGroup,
+ positionInGroup);
+}
+
+HRESULT
+AccessibleHandler::get_states(AccessibleStates* states) {
+ if (!states) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_states(states);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mIA2States, *states);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_extendedRole(BSTR* extendedRole) {
+ // This matches ia2Accessible
+ if (!extendedRole) {
+ return E_INVALIDARG;
+ }
+ *extendedRole = nullptr;
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_localizedExtendedRole(BSTR* localizedExtendedRole) {
+ // This matches ia2Accessible
+ if (!localizedExtendedRole) {
+ return E_INVALIDARG;
+ }
+ *localizedExtendedRole = nullptr;
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_nExtendedStates(long* nExtendedStates) {
+ // This matches ia2Accessible
+ if (!nExtendedStates) {
+ return E_INVALIDARG;
+ }
+ *nExtendedStates = 0;
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_extendedStates(long maxExtendedStates,
+ BSTR** extendedStates,
+ long* nExtendedStates) {
+ // This matches ia2Accessible
+ if (!extendedStates || !nExtendedStates) {
+ return E_INVALIDARG;
+ }
+ *extendedStates = nullptr;
+ *nExtendedStates = 0;
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_localizedExtendedStates(long maxLocalizedExtendedStates,
+ BSTR** localizedExtendedStates,
+ long* nLocalizedExtendedStates) {
+ // This matches ia2Accessible
+ if (!localizedExtendedStates || !nLocalizedExtendedStates) {
+ return E_INVALIDARG;
+ }
+ *localizedExtendedStates = nullptr;
+ *nLocalizedExtendedStates = 0;
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_uniqueID(long* uniqueID) {
+ if (!uniqueID) {
+ return E_INVALIDARG;
+ }
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_uniqueID(uniqueID);
+ }
+ *uniqueID = mCachedData.mDynamicData.mUniqueId;
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_windowHandle(HWND* windowHandle) {
+ if (!windowHandle) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_windowHandle(windowHandle);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ long hwnd = 0;
+ GET_FIELD(mHwnd, hwnd);
+ *windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_indexInParent(long* indexInParent) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_indexInParent(indexInParent);
+}
+
+HRESULT
+AccessibleHandler::get_locale(IA2Locale* locale) {
+ if (!locale) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_locale(locale);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mIA2Locale.language, locale->language);
+ GET_BSTR(mIA2Locale.country, locale->country);
+ GET_BSTR(mIA2Locale.variant, locale->variant);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_attributes(BSTR* attributes) {
+ if (!attributes) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_attributes(attributes);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mAttributes, *attributes);
+ return S_OK;
+}
+
+/*** IAccessible2_2 ***/
+
+HRESULT
+AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute) {
+ // Not yet implemented by ia2Accessible.
+ // Once ia2Accessible implements this, we could either pass it through
+ // or we could extract these individually from cached mAttributes.
+ // The latter should be considered if traffic warrants it.
+ return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible,
+ long* caretOffset) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accessibleWithCaret(accessible, caretOffset);
+}
+
+HRESULT
+AccessibleHandler::get_relationTargetsOfType(BSTR type, long maxTargets,
+ IUnknown*** targets,
+ long* nTargets) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
+ nTargets);
+}
+
+/*** IAccessible2_3 ***/
+
+HRESULT
+AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_selectionRanges(ranges, nRanges);
+}
+
+/*** IServiceProvider ***/
+
+HRESULT
+AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid,
+ void** aOutInterface) {
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ /* We're taking advantage of the fact that we are implementing IA2 as part
+ of our own object to implement this just like a QI. */
+ if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
+ aIid == IID_IAccessible2) {
+ RefPtr<NEWEST_IA2_INTERFACE> ia2(this);
+ ia2.forget(aOutInterface);
+ return S_OK;
+ }
+
+ // JAWS uses QueryService for these, but QI will work just fine and we can
+ // thus avoid a cross-process call. More importantly, if QS is used, the
+ // handler won't get used for that object, so our caching won't be used.
+ if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleText) {
+ return InternalQueryInterface(aIid, aOutInterface);
+ }
+
+ for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) {
+ if (aServiceId == kUnsupportedServices[i]) {
+ return E_NOINTERFACE;
+ }
+ }
+
+ if (!mServProvPassThru) {
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = proxy->QueryInterface(
+ IID_IServiceProvider, reinterpret_cast<void**>(&mServProvPassThru));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // mServProvPassThru is a weak reference (see comments in
+ // AccessibleHandler.h)
+ mServProvPassThru->Release();
+ }
+
+ return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
+}
+
+/*** IProvideClassInfo ***/
+
+HRESULT
+AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo) {
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+
+ return ctl->GetHandlerTypeInfo(aOutTypeInfo);
+}
+
+/*** IAccessibleAction ***/
+
+HRESULT
+AccessibleHandler::nActions(long* nActions) {
+ if (!nActions) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->nActions(nActions);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mNActions, *nActions);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::doAction(long actionIndex) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->doAction(actionIndex);
+}
+
+HRESULT
+AccessibleHandler::get_description(long actionIndex, BSTR* description) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_description(actionIndex, description);
+}
+
+HRESULT
+AccessibleHandler::get_keyBinding(long actionIndex, long nMaxBindings,
+ BSTR** keyBindings, long* nBindings) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_keyBinding(actionIndex, nMaxBindings,
+ keyBindings, nBindings);
+}
+
+HRESULT
+AccessibleHandler::get_name(long actionIndex, BSTR* name) {
+ if (!name) {
+ return E_INVALIDARG;
+ }
+
+ if (HasPayload()) {
+ if (actionIndex >= mCachedData.mDynamicData.mNActions) {
+ // Action does not exist.
+ return E_INVALIDARG;
+ }
+
+ if (actionIndex == 0) {
+ // same as accDefaultAction.
+ GET_BSTR(mDefaultAction, *name);
+ return S_OK;
+ }
+ }
+
+ // At this point, there's either no payload or actionIndex is > 0.
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_name(actionIndex, name);
+}
+
+HRESULT
+AccessibleHandler::get_localizedName(long actionIndex, BSTR* localizedName) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_localizedName(actionIndex, localizedName);
+}
+
+/*** IAccessibleHyperlink ***/
+
+HRESULT
+AccessibleHandler::get_anchor(long index, VARIANT* anchor) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_anchor(index, anchor);
+}
+
+HRESULT
+AccessibleHandler::get_anchorTarget(long index, VARIANT* anchorTarget) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_anchorTarget(index, anchorTarget);
+}
+
+HRESULT
+AccessibleHandler::get_startIndex(long* index) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_startIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_endIndex(long* index) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_endIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_valid(boolean* valid) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_valid(valid);
+}
+
+/*** IAccessibleTableCell ***/
+
+HRESULT
+AccessibleHandler::get_columnExtent(long* nColumnsSpanned) {
+ if (!nColumnsSpanned) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_columnExtent(nColumnsSpanned);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mColumnExtent, *nColumnsSpanned);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_columnHeaderCells(IUnknown*** cellAccessibles,
+ long* nColumnHeaderCells) {
+ if (!cellAccessibles || !nColumnHeaderCells) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = S_OK;
+ if (HasPayload()) {
+ RefPtr<AccessibleHandlerControl> ctl(
+ gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+ *nColumnHeaderCells = mCachedData.mDynamicData.mNColumnHeaderCells;
+ *cellAccessibles = static_cast<IUnknown**>(
+ ::CoTaskMemAlloc(sizeof(IUnknown*) * *nColumnHeaderCells));
+ long i;
+ for (i = 0; i < *nColumnHeaderCells; ++i) {
+ RefPtr<AccessibleHandler> headerAcc;
+ hr = ctl->GetCachedAccessible(
+ mCachedData.mDynamicData.mColumnHeaderCellIds[i],
+ getter_AddRefs(headerAcc));
+ if (FAILED(hr)) {
+ break;
+ }
+ hr = headerAcc->QueryInterface(IID_IUnknown,
+ (void**)&(*cellAccessibles)[i]);
+ if (FAILED(hr)) {
+ break;
+ }
+ }
+ if (SUCCEEDED(hr)) {
+ return S_OK;
+ }
+ // If we failed to get any of the headers from the cache, don't use the
+ // cache at all. We need to clean up anything we did so far.
+ long failedHeader = i;
+ for (i = 0; i < failedHeader; ++i) {
+ (*cellAccessibles)[i]->Release();
+ }
+ ::CoTaskMemFree(*cellAccessibles);
+ *cellAccessibles = nullptr;
+ }
+
+ hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_columnHeaderCells(cellAccessibles,
+ nColumnHeaderCells);
+}
+
+HRESULT
+AccessibleHandler::get_columnIndex(long* columnIndex) {
+ if (!columnIndex) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_columnIndex(columnIndex);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mColumnIndex, *columnIndex);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowExtent(long* nRowsSpanned) {
+ if (!nRowsSpanned) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowExtent(nRowsSpanned);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowExtent, *nRowsSpanned);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowHeaderCells(IUnknown*** cellAccessibles,
+ long* nRowHeaderCells) {
+ if (!cellAccessibles || !nRowHeaderCells) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = S_OK;
+ if (HasPayload()) {
+ RefPtr<AccessibleHandlerControl> ctl(
+ gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+ *nRowHeaderCells = mCachedData.mDynamicData.mNRowHeaderCells;
+ *cellAccessibles = static_cast<IUnknown**>(
+ ::CoTaskMemAlloc(sizeof(IUnknown*) * *nRowHeaderCells));
+ long i;
+ for (i = 0; i < *nRowHeaderCells; ++i) {
+ RefPtr<AccessibleHandler> headerAcc;
+ hr = ctl->GetCachedAccessible(
+ mCachedData.mDynamicData.mRowHeaderCellIds[i],
+ getter_AddRefs(headerAcc));
+ if (FAILED(hr)) {
+ break;
+ }
+ hr = headerAcc->QueryInterface(IID_IUnknown,
+ (void**)&(*cellAccessibles)[i]);
+ if (FAILED(hr)) {
+ break;
+ }
+ }
+ if (SUCCEEDED(hr)) {
+ return S_OK;
+ }
+ // If we failed to get any of the headers from the cache, don't use the
+ // cache at all. We need to clean up anything we did so far.
+ long failedHeader = i;
+ for (i = 0; i < failedHeader; ++i) {
+ (*cellAccessibles)[i]->Release();
+ }
+ ::CoTaskMemFree(*cellAccessibles);
+ *cellAccessibles = nullptr;
+ }
+
+ hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_rowHeaderCells(cellAccessibles,
+ nRowHeaderCells);
+}
+
+HRESULT
+AccessibleHandler::get_rowIndex(long* rowIndex) {
+ if (!rowIndex) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowIndex(rowIndex);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowIndex, *rowIndex);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_isSelected(boolean* isSelected) {
+ if (!isSelected) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_isSelected(isSelected);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mCellIsSelected, *isSelected);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowColumnExtents(long* row, long* column,
+ long* rowExtents, long* columnExtents,
+ boolean* isSelected) {
+ if (!row || !column || !rowExtents || !columnExtents || !isSelected) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowColumnExtents(
+ row, column, rowExtents, columnExtents, isSelected);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowIndex, *row);
+ GET_FIELD(mColumnIndex, *column);
+ GET_FIELD(mRowExtent, *rowExtents);
+ GET_FIELD(mColumnExtent, *columnExtents);
+ GET_FIELD(mCellIsSelected, *isSelected);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_table(IUnknown** table) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_table(table);
+}
+
+/*** IAccessibleText ***/
+
+HRESULT
+AccessibleHandler::addSelection(long startOffset, long endOffset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->addSelection(startOffset, endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_attributes(long offset, long* startOffset,
+ long* endOffset, BSTR* textAttributes) {
+ if (!startOffset || !endOffset || !textAttributes) {
+ return E_INVALIDARG;
+ }
+
+ if (mCachedNTextAttribRuns >= 0) {
+ // We have cached attributes.
+ for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
+ auto& attribRun = mCachedTextAttribRuns[index];
+ if (attribRun.start <= offset && offset < attribRun.end) {
+ *startOffset = attribRun.start;
+ *endOffset = attribRun.end;
+ *textAttributes = attribRun.text;
+ // The caller will clean this up.
+ // (We only keep each cached attribute run for one call.)
+ attribRun.text = nullptr;
+ // The cache for this run is now invalid, so don't visit it again.
+ attribRun.end = 0;
+ return S_OK;
+ }
+ }
+ }
+
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_attributes(offset, startOffset, endOffset,
+ textAttributes);
+}
+
+HRESULT
+AccessibleHandler::get_caretOffset(long* offset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_caretOffset(offset);
+}
+
+HRESULT
+AccessibleHandler::get_characterExtents(long offset,
+ enum IA2CoordinateType coordType,
+ long* x, long* y, long* width,
+ long* height) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_characterExtents(offset, coordType, x, y,
+ width, height);
+}
+
+HRESULT
+AccessibleHandler::get_nSelections(long* nSelections) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = mIAHypertextPassThru->get_nSelections(nSelections);
+ if (SUCCEEDED(hr) && *nSelections == 0 && HasPayload()) {
+ BEGIN_CACHE_ACCESS;
+ if (mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_DOCUMENT) {
+ RefPtr<AccessibleHandlerControl> ctl(
+ gControlFactory.GetOrCreateSingleton());
+ if (!ctl) {
+ return E_OUTOFMEMORY;
+ }
+ if (ctl->IsA11ySuppressedForClipboardCopy()) {
+ // Bug 1798098: Windows Suggested Actions (introduced in Windows 11
+ // 22H2) might walk the document a11y tree using UIA whenever anything
+ // is copied to the clipboard. This causes an unacceptable hang,
+ // particularly when the cache is disabled. It walks using
+ // IAccessibleText/IAccessibleHyperText if the document reports no
+ // selection, so we lie here and say that there is a selection even
+ // though there isn't. It will subsequently call get_selection, which
+ // will fail, but this hack here seems to be enough to avoid further
+ // text calls.
+ *nSelections = 1;
+ }
+ }
+ }
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::get_offsetAtPoint(long x, long y,
+ enum IA2CoordinateType coordType,
+ long* offset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_offsetAtPoint(x, y, coordType, offset);
+}
+
+HRESULT
+AccessibleHandler::get_selection(long selectionIndex, long* startOffset,
+ long* endOffset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_selection(selectionIndex, startOffset,
+ endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_text(long startOffset, long endOffset, BSTR* text) {
+ if (!text) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ if (mCachedData.mGeckoBackChannel && startOffset == 0 &&
+ endOffset == IA2_TEXT_OFFSET_LENGTH) {
+ // If the caller is retrieving all text, they will probably want all
+ // hyperlinks and attributes as well.
+ hr = GetAllTextInfo(text);
+ if (SUCCEEDED(hr)) {
+ return hr;
+ }
+ // We fall back to a normal call if this fails.
+ }
+
+ hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_text(startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::get_textBeforeOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textBeforeOffset(
+ offset, boundaryType, startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::get_textAfterOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textAfterOffset(
+ offset, boundaryType, startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::get_textAtOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textAtOffset(offset, boundaryType,
+ startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::removeSelection(long selectionIndex) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->removeSelection(selectionIndex);
+}
+
+HRESULT
+AccessibleHandler::setCaretOffset(long offset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->setCaretOffset(offset);
+}
+
+HRESULT
+AccessibleHandler::setSelection(long selectionIndex, long startOffset,
+ long endOffset) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->setSelection(selectionIndex, startOffset,
+ endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_nCharacters(long* nCharacters) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_nCharacters(nCharacters);
+}
+
+HRESULT
+AccessibleHandler::scrollSubstringTo(long startIndex, long endIndex,
+ enum IA2ScrollType scrollType) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->scrollSubstringTo(startIndex, endIndex,
+ scrollType);
+}
+
+HRESULT
+AccessibleHandler::scrollSubstringToPoint(long startIndex, long endIndex,
+ enum IA2CoordinateType coordinateType,
+ long x, long y) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->scrollSubstringToPoint(startIndex, endIndex,
+ coordinateType, x, y);
+}
+
+HRESULT
+AccessibleHandler::get_newText(IA2TextSegment* newText) {
+ if (!newText) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
+ MOZ_ASSERT(ctl);
+ if (!ctl) {
+ return S_OK;
+ }
+
+ long id;
+ HRESULT hr = this->get_uniqueID(&id);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ctl->GetNewText(id, WrapNotNull(newText));
+}
+
+HRESULT
+AccessibleHandler::get_oldText(IA2TextSegment* oldText) {
+ if (!oldText) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
+ MOZ_ASSERT(ctl);
+ if (!ctl) {
+ return S_OK;
+ }
+
+ long id;
+ HRESULT hr = this->get_uniqueID(&id);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ctl->GetOldText(id, WrapNotNull(oldText));
+}
+
+/*** IAccessibleHypertext ***/
+
+HRESULT
+AccessibleHandler::get_nHyperlinks(long* hyperlinkCount) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_nHyperlinks(hyperlinkCount);
+}
+
+HRESULT
+AccessibleHandler::get_hyperlink(long index, IAccessibleHyperlink** hyperlink) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlink(index, hyperlink);
+}
+
+HRESULT
+AccessibleHandler::get_hyperlinkIndex(long charIndex, long* hyperlinkIndex) {
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlinkIndex(charIndex, hyperlinkIndex);
+}
+
+/*** IAccessibleHypertext2 ***/
+
+HRESULT
+AccessibleHandler::get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+ long* nHyperlinks) {
+ if (!hyperlinks || !nHyperlinks) {
+ return E_INVALIDARG;
+ }
+
+ if (mCachedNHyperlinks >= 0) {
+ // We have cached hyperlinks.
+ *hyperlinks = mCachedHyperlinks;
+ *nHyperlinks = mCachedNHyperlinks;
+ // The client will clean these up. (We only keep the cache for one call.)
+ mCachedHyperlinks = nullptr;
+ mCachedNHyperlinks = -1;
+ return mCachedNHyperlinks == 0 ? S_FALSE : S_OK;
+ }
+
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlinks(hyperlinks, nHyperlinks);
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+extern "C" HRESULT __stdcall ProxyDllCanUnloadNow();
+
+extern "C" HRESULT __stdcall DllCanUnloadNow() {
+ return mozilla::mscom::Module::CanUnload() && ProxyDllCanUnloadNow();
+}
+
+extern "C" HRESULT __stdcall ProxyDllGetClassObject(REFCLSID aClsid,
+ REFIID aIid,
+ LPVOID* aOutInterface);
+
+extern "C" HRESULT __stdcall DllGetClassObject(REFCLSID aClsid, REFIID aIid,
+ LPVOID* aOutInterface) {
+ if (aClsid == CLSID_AccessibleHandler) {
+ return mozilla::a11y::sHandlerFactory.QueryInterface(aIid, aOutInterface);
+ }
+ return ProxyDllGetClassObject(aClsid, aIid, aOutInterface);
+}
+
+extern "C" BOOL WINAPI ProxyDllMain(HINSTANCE aInstDll, DWORD aReason,
+ LPVOID aReserved);
+
+BOOL WINAPI DllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved) {
+ if (aReason == DLL_PROCESS_ATTACH) {
+ DisableThreadLibraryCalls((HMODULE)aInstDll);
+ }
+ // This is required for ProxyDllRegisterServer to work correctly
+ return ProxyDllMain(aInstDll, aReason, aReserved);
+}
+
+extern "C" HRESULT __stdcall ProxyDllRegisterServer();
+
+extern "C" HRESULT __stdcall DllRegisterServer() {
+ HRESULT hr = mozilla::mscom::Handler::Register(CLSID_AccessibleHandler);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ProxyDllRegisterServer();
+}
+
+extern "C" HRESULT __stdcall ProxyDllUnregisterServer();
+
+extern "C" HRESULT __stdcall DllUnregisterServer() {
+ HRESULT hr = mozilla::mscom::Handler::Unregister(CLSID_AccessibleHandler);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ProxyDllUnregisterServer();
+}
+
+extern "C" HRESULT __stdcall RegisterMsix() {
+ return mozilla::mscom::Handler::Register(CLSID_AccessibleHandler,
+ /* aMsixContainer */ true);
+}
diff --git a/accessible/ipc/win/handler/AccessibleHandler.def b/accessible/ipc/win/handler/AccessibleHandler.def
new file mode 100644
index 0000000000..f1f3513b0e
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.def
@@ -0,0 +1,12 @@
+;+# 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/.
+
+LIBRARY AccessibleHandler.dll
+
+EXPORTS DllGetClassObject PRIVATE
+ DllCanUnloadNow PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+ GetProxyDllInfo PRIVATE
+ RegisterMsix PRIVATE
diff --git a/accessible/ipc/win/handler/AccessibleHandler.h b/accessible/ipc/win/handler/AccessibleHandler.h
new file mode 100644
index 0000000000..f99091d6c3
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.h
@@ -0,0 +1,336 @@
+/* -*- 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_AccessibleHandler_h
+#define mozilla_a11y_AccessibleHandler_h
+
+#define NEWEST_IA2_BASENAME Accessible2_3
+
+#define __GENIFACE(base) I##base
+#define INTERFACEFOR(base) __GENIFACE(base)
+#define NEWEST_IA2_INTERFACE INTERFACEFOR(NEWEST_IA2_BASENAME)
+
+#define __GENIID(iface) IID_##iface
+#define IIDFOR(iface) __GENIID(iface)
+#define NEWEST_IA2_IID IIDFOR(NEWEST_IA2_INTERFACE)
+
+#if defined(__midl) || defined(__WIDL__)
+
+import "Accessible2_3.idl";
+
+#else
+
+# include "HandlerData.h"
+
+# include <windows.h>
+
+namespace mozilla {
+namespace a11y {
+
+static const GUID kUnsupportedServices[] = {
+ // clang-format off
+ // Unknown, queried by Windows on devices with touch screens or similar devices
+ // connected.
+ {0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}},
+ // Unknown, queried by Windows
+ {0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }},
+ // Unknown, queried by Windows
+ {0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }},
+ // Unknown, queried by Windows
+ {0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }},
+ // SID_IsUIAutomationObject (undocumented), queried by Windows
+ {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }},
+ // IIS_IsOleaccProxy (undocumented), queried by Windows
+ {0x902697FA, 0x80E4, 0x4560, {0x80, 0x2A, 0xA1, 0x3F, 0x22, 0xA6, 0x47, 0x09}},
+ // IID_IHTMLElement, queried by JAWS
+ {0x3050F1FF, 0x98B5, 0x11CF, {0xBB, 0x82, 0x00, 0xAA, 0x00, 0xBD, 0xCE, 0x0B}}
+ // clang-format on
+};
+
+}
+} // namespace mozilla
+
+# if !defined(MOZILLA_INTERNAL_API)
+
+# include "Accessible2_3.h"
+# include "AccessibleHyperlink.h"
+# include "AccessibleHypertext2.h"
+# include "AccessibleTableCell.h"
+# include "Handler.h"
+# include "mozilla/mscom/StructStream.h"
+# include "mozilla/UniquePtr.h"
+
+# include <ocidl.h>
+# include <servprov.h>
+
+namespace mozilla {
+namespace a11y {
+
+class AccessibleHandler final : public mscom::Handler,
+ public NEWEST_IA2_INTERFACE,
+ public IServiceProvider,
+ public IProvideClassInfo,
+ public IAccessibleHyperlink,
+ public IAccessibleTableCell,
+ public IAccessibleHypertext2 {
+ public:
+ static HRESULT Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface);
+
+ // mscom::Handler
+ HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
+ void** aOutInterface) override;
+ HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) override;
+
+ REFIID MarshalAs(REFIID aRequestedIid) override;
+ HRESULT GetMarshalInterface(REFIID aMarshalAsIid, NotNull<IUnknown*> aProxy,
+ NotNull<IID*> aOutIid,
+ NotNull<IUnknown**> aOutUnk) override;
+ HRESULT GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) override;
+ HRESULT WriteHandlerPayload(IStream* aStream, REFIID aIId) override;
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IDispatch
+ STDMETHODIMP GetTypeInfoCount(UINT* pctinfo) override;
+ STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid,
+ ITypeInfo** ppTInfo) override;
+ STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+ LCID lcid, DISPID* rgDispId) override;
+ STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS* pDispParams, VARIANT* pVarResult,
+ EXCEPINFO* pExcepInfo, UINT* puArgErr) override;
+
+ // IAccessible
+ STDMETHODIMP get_accParent(IDispatch** ppdispParent) override;
+ STDMETHODIMP get_accChildCount(long* pcountChildren) override;
+ STDMETHODIMP get_accChild(VARIANT varChild, IDispatch** ppdispChild) override;
+ STDMETHODIMP get_accName(VARIANT varChild, BSTR* pszName) override;
+ STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pszValue) override;
+ STDMETHODIMP get_accDescription(VARIANT varChild,
+ BSTR* pszDescription) override;
+ STDMETHODIMP get_accRole(VARIANT varChild, VARIANT* pvarRole) override;
+ STDMETHODIMP get_accState(VARIANT varChild, VARIANT* pvarState) override;
+ STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pszHelp) override;
+ STDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
+ long* pidTopic) override;
+ STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild,
+ BSTR* pszKeyboardShortcut) override;
+ STDMETHODIMP get_accFocus(VARIANT* pvarChild) override;
+ STDMETHODIMP get_accSelection(VARIANT* pvarChildren) override;
+ STDMETHODIMP get_accDefaultAction(VARIANT varChild,
+ BSTR* pszDefaultAction) override;
+ STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) override;
+ STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
+ long* pcyHeight, VARIANT varChild) override;
+ STDMETHODIMP accNavigate(long navDir, VARIANT varStart,
+ VARIANT* pvarEndUpAt) override;
+ STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT* pvarChild) override;
+ STDMETHODIMP accDoDefaultAction(VARIANT varChild) override;
+ STDMETHODIMP put_accName(VARIANT varChild, BSTR szName) override;
+ STDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) override;
+
+ // IAccessible2
+ STDMETHODIMP get_nRelations(long* nRelations) override;
+ STDMETHODIMP get_relation(long relationIndex,
+ IAccessibleRelation** relation) override;
+ STDMETHODIMP get_relations(long maxRelations, IAccessibleRelation** relations,
+ long* nRelations) override;
+ STDMETHODIMP role(long* role) override;
+ STDMETHODIMP scrollTo(IA2ScrollType scrollType) override;
+ STDMETHODIMP scrollToPoint(IA2CoordinateType coordinateType, long x,
+ long y) override;
+ STDMETHODIMP get_groupPosition(long* groupLevel, long* similarItemsInGroup,
+ long* positionInGroup) override;
+ STDMETHODIMP get_states(AccessibleStates* states) override;
+ STDMETHODIMP get_extendedRole(BSTR* extendedRole) override;
+ STDMETHODIMP get_localizedExtendedRole(BSTR* localizedExtendedRole) override;
+ STDMETHODIMP get_nExtendedStates(long* nExtendedStates) override;
+ STDMETHODIMP get_extendedStates(long maxExtendedStates, BSTR** extendedStates,
+ long* nExtendedStates) override;
+ STDMETHODIMP get_localizedExtendedStates(
+ long maxLocalizedExtendedStates, BSTR** localizedExtendedStates,
+ long* nLocalizedExtendedStates) override;
+ STDMETHODIMP get_uniqueID(long* uniqueID) override;
+ STDMETHODIMP get_windowHandle(HWND* windowHandle) override;
+ STDMETHODIMP get_indexInParent(long* indexInParent) override;
+ STDMETHODIMP get_locale(IA2Locale* locale) override;
+ STDMETHODIMP get_attributes(BSTR* attributes) override;
+
+ // IAccessible2_2
+ STDMETHODIMP get_attribute(BSTR name, VARIANT* attribute) override;
+ STDMETHODIMP get_accessibleWithCaret(IUnknown** accessible,
+ long* caretOffset) override;
+ STDMETHODIMP get_relationTargetsOfType(BSTR type, long maxTargets,
+ IUnknown*** targets,
+ long* nTargets) override;
+
+ // IAccessible2_3
+ STDMETHODIMP get_selectionRanges(IA2Range** ranges, long* nRanges) override;
+
+ // IServiceProvider
+ STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid,
+ void** aOutInterface) override;
+
+ // IProvideClassInfo
+ STDMETHODIMP GetClassInfo(ITypeInfo** aOutTypeInfo) override;
+
+ // IAccessibleAction
+ STDMETHODIMP nActions(long* nActions) override;
+ STDMETHODIMP doAction(long actionIndex) override;
+ STDMETHODIMP get_description(long actionIndex, BSTR* description) override;
+ STDMETHODIMP get_keyBinding(long actionIndex, long nMaxBindings,
+ BSTR** keyBindings, long* nBindings) override;
+ STDMETHODIMP get_name(long actionIndex, BSTR* name) override;
+ STDMETHODIMP get_localizedName(long actionIndex,
+ BSTR* localizedName) override;
+
+ // IAccessibleHyperlink
+ STDMETHODIMP get_anchor(long index, VARIANT* anchor) override;
+ STDMETHODIMP get_anchorTarget(long index, VARIANT* anchorTarget) override;
+ STDMETHODIMP get_startIndex(long* index) override;
+ STDMETHODIMP get_endIndex(long* index) override;
+ STDMETHODIMP get_valid(boolean* valid) override;
+
+ // IAccessibleTableCell
+ STDMETHODIMP get_columnExtent(long* nColumnsSpanned) override;
+ STDMETHODIMP get_columnHeaderCells(IUnknown*** cellAccessibles,
+ long* nColumnHeaderCells) override;
+ STDMETHODIMP get_columnIndex(long* columnIndex) override;
+ STDMETHODIMP get_rowExtent(long* nRowsSpanned) override;
+ STDMETHODIMP get_rowHeaderCells(IUnknown*** cellAccessibles,
+ long* nRowHeaderCells) override;
+ STDMETHODIMP get_rowIndex(long* rowIndex) override;
+ STDMETHODIMP get_isSelected(boolean* isSelected) override;
+ STDMETHODIMP get_rowColumnExtents(long* row, long* column, long* rowExtents,
+ long* columnExtents,
+ boolean* isSelected) override;
+ STDMETHODIMP get_table(IUnknown** table) override;
+
+ // IAccessibleText
+ STDMETHODIMP addSelection(long startOffset, long endOffset) override;
+ STDMETHODIMP get_attributes(long offset, long* startOffset, long* endOffset,
+ BSTR* textAttributes) override;
+ STDMETHODIMP get_caretOffset(long* offset) override;
+ STDMETHODIMP get_characterExtents(long offset,
+ enum IA2CoordinateType coordType, long* x,
+ long* y, long* width,
+ long* height) override;
+ STDMETHODIMP get_nSelections(long* nSelections) override;
+ STDMETHODIMP get_offsetAtPoint(long x, long y,
+ enum IA2CoordinateType coordType,
+ long* offset) override;
+ STDMETHODIMP get_selection(long selectionIndex, long* startOffset,
+ long* endOffset) override;
+ STDMETHODIMP get_text(long startOffset, long endOffset, BSTR* text) override;
+ STDMETHODIMP get_textBeforeOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) override;
+ STDMETHODIMP get_textAfterOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) override;
+ STDMETHODIMP get_textAtOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long* startOffset, long* endOffset,
+ BSTR* text) override;
+ STDMETHODIMP removeSelection(long selectionIndex) override;
+ STDMETHODIMP setCaretOffset(long offset) override;
+ STDMETHODIMP setSelection(long selectionIndex, long startOffset,
+ long endOffset) override;
+ STDMETHODIMP get_nCharacters(long* nCharacters) override;
+ STDMETHODIMP scrollSubstringTo(long startIndex, long endIndex,
+ enum IA2ScrollType scrollType) override;
+ STDMETHODIMP scrollSubstringToPoint(long startIndex, long endIndex,
+ enum IA2CoordinateType coordinateType,
+ long x, long y) override;
+ STDMETHODIMP get_newText(IA2TextSegment* newText) override;
+ STDMETHODIMP get_oldText(IA2TextSegment* oldText) override;
+
+ // IAccessibleHypertext
+ STDMETHODIMP get_nHyperlinks(long* hyperlinkCount) override;
+ STDMETHODIMP get_hyperlink(long index,
+ IAccessibleHyperlink** hyperlink) override;
+ STDMETHODIMP get_hyperlinkIndex(long charIndex,
+ long* hyperlinkIndex) override;
+
+ // IAccessibleHypertext2
+ STDMETHODIMP get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+ long* nHyperlinks) override;
+
+ private:
+ AccessibleHandler(IUnknown* aOuter, HRESULT* aResult);
+ virtual ~AccessibleHandler();
+
+ HRESULT ResolveIA2();
+ HRESULT ResolveIDispatch();
+ HRESULT ResolveIAHyperlink();
+ HRESULT ResolveIAHypertext();
+ HRESULT ResolveIATableCell();
+ HRESULT MaybeUpdateCachedData();
+ HRESULT GetAllTextInfo(BSTR* aText);
+ void ClearTextCache();
+ HRESULT GetRelationsInfo();
+ void ClearRelationCache();
+
+ RefPtr<IUnknown> mDispatchUnk;
+ /**
+ * Handlers aggregate their proxies. This means that their proxies delegate
+ * their IUnknown implementation to us.
+ *
+ * mDispatchUnk and the result of Handler::GetProxy() are both strong
+ * references to the aggregated objects. OTOH, any interfaces that are QI'd
+ * from those aggregated objects have delegated unknowns.
+ *
+ * AddRef'ing an interface with a delegated unknown ends up incrementing the
+ * refcount of the *aggregator*. Since we are the aggregator of mDispatchUnk
+ * and of the wrapped proxy, holding a strong reference to any interfaces
+ * QI'd off of those objects would create a reference cycle.
+ *
+ * We may hold onto pointers to those references, but when we query them we
+ * must immediately Release() them to prevent these cycles.
+ *
+ * It is safe for us to use these raw pointers because the aggregated
+ * objects's lifetimes are proper subsets of our own lifetime.
+ */
+ IDispatch* mDispatch; // weak
+ NEWEST_IA2_INTERFACE* mIA2PassThru; // weak
+ IServiceProvider* mServProvPassThru; // weak
+ IAccessibleHyperlink* mIAHyperlinkPassThru; // weak
+ IAccessibleTableCell* mIATableCellPassThru; // weak
+ IAccessibleHypertext2* mIAHypertextPassThru; // weak
+ IA2Payload mCachedData;
+ bool mCachedDynamicDataMarshaledByCom;
+ UniquePtr<mscom::StructToStream> mSerializer;
+ uint32_t mCacheGen;
+ IAccessibleHyperlink** mCachedHyperlinks;
+ long mCachedNHyperlinks;
+ IA2TextSegment* mCachedTextAttribRuns;
+ long mCachedNTextAttribRuns;
+ IARelationData* mCachedRelations;
+ long mCachedNRelations;
+ bool mIsEmulatedWindow;
+};
+
+inline static BSTR CopyBSTR(BSTR aSrc) {
+ if (!aSrc) {
+ return nullptr;
+ }
+
+ return ::SysAllocStringLen(aSrc, ::SysStringLen(aSrc));
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+# endif // !defined(MOZILLA_INTERNAL_API)
+
+#endif // defined(__midl)
+
+#endif // mozilla_a11y_AccessibleHandler_h
diff --git a/accessible/ipc/win/handler/AccessibleHandler.rc b/accessible/ipc/win/handler/AccessibleHandler.rc
new file mode 100644
index 0000000000..3dac2f89d5
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.rc
@@ -0,0 +1,5 @@
+/* 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/. */
+
+1 typelib HandlerData.tlb
diff --git a/accessible/ipc/win/handler/AccessibleHandlerControl.cpp b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp
new file mode 100644
index 0000000000..0d0bd4d71d
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp
@@ -0,0 +1,221 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include "AccessibleHandlerControl.h"
+
+#include <utility>
+
+#include "AccessibleEventId.h"
+#include "AccessibleHandler.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace a11y {
+
+mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory;
+
+namespace detail {
+
+TextChange::TextChange() : mIA2UniqueId(0), mIsInsert(false), mText() {}
+
+TextChange::TextChange(long aIA2UniqueId, bool aIsInsert,
+ NotNull<IA2TextSegment*> aText)
+ : mIA2UniqueId(aIA2UniqueId),
+ mIsInsert(aIsInsert),
+ mText{BSTRCopy(aText->text), aText->start, aText->end} {}
+
+TextChange::TextChange(TextChange&& aOther) : mText() {
+ *this = std::move(aOther);
+}
+
+TextChange::TextChange(const TextChange& aOther) : mText() { *this = aOther; }
+
+TextChange& TextChange::operator=(TextChange&& aOther) {
+ mIA2UniqueId = aOther.mIA2UniqueId;
+ mIsInsert = aOther.mIsInsert;
+ aOther.mIA2UniqueId = 0;
+ ::SysFreeString(mText.text);
+ mText = aOther.mText;
+ aOther.mText.text = nullptr;
+ return *this;
+}
+
+TextChange& TextChange::operator=(const TextChange& aOther) {
+ mIA2UniqueId = aOther.mIA2UniqueId;
+ mIsInsert = aOther.mIsInsert;
+ ::SysFreeString(mText.text);
+ mText = {BSTRCopy(aOther.mText.text), aOther.mText.start, aOther.mText.end};
+ return *this;
+}
+
+TextChange::~TextChange() { ::SysFreeString(mText.text); }
+
+HRESULT
+TextChange::GetOld(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldSegment) {
+ if (mIsInsert || aIA2UniqueId != mIA2UniqueId) {
+ return S_OK;
+ }
+
+ return SegCopy(*aOutOldSegment, mText);
+}
+
+HRESULT
+TextChange::GetNew(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewSegment) {
+ if (!mIsInsert || aIA2UniqueId != mIA2UniqueId) {
+ return S_OK;
+ }
+
+ return SegCopy(*aOutNewSegment, mText);
+}
+
+/* static */
+BSTR TextChange::BSTRCopy(const BSTR& aIn) {
+ return ::SysAllocStringLen(aIn, ::SysStringLen(aIn));
+}
+
+/* static */
+HRESULT TextChange::SegCopy(IA2TextSegment& aDest, const IA2TextSegment& aSrc) {
+ aDest = {BSTRCopy(aSrc.text), aSrc.start, aSrc.end};
+ if (aSrc.text && !aDest.text) {
+ return E_OUTOFMEMORY;
+ }
+ if (!::SysStringLen(aDest.text)) {
+ return S_FALSE;
+ }
+ return S_OK;
+}
+
+} // namespace detail
+
+HRESULT
+AccessibleHandlerControl::Create(AccessibleHandlerControl** aOutObject) {
+ if (!aOutObject) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(new AccessibleHandlerControl());
+ ctl.forget(aOutObject);
+ return S_OK;
+}
+
+AccessibleHandlerControl::AccessibleHandlerControl()
+ : mIsRegistered(false),
+ mCacheGen(0),
+ mIA2Proxy(mscom::RegisterProxy(L"ia2marshal.dll")),
+ mHandlerProxy(mscom::RegisterProxy()) {
+ MOZ_ASSERT(mIA2Proxy);
+}
+
+IMPL_IUNKNOWN1(AccessibleHandlerControl, IHandlerControl)
+
+HRESULT
+AccessibleHandlerControl::Invalidate() {
+ ++mCacheGen;
+ // We can't just call mAccessibleCache.clear() because doing so would release
+ // remote objects, making remote COM calls. Since this is an STA, an incoming
+ // COM call might be handled which might marshal an AccessibleHandler,
+ // which in turn might add itself to mAccessibleCache. Since we'd be in the
+ // middle of mutating mAccessibleCache, that might cause a crash. Instead,
+ // swap mAccessibleCache into a temporary map first, which will empty
+ // mAccessibleCache without releasing remote objects. Once mAccessibleCache
+ // is empty, it's safe to let the temporary map be destroyed when it goes
+ // out of scope. Remote calls will be made, but nothing will re-enter
+ // the temporary map while it's being destroyed.
+ AccessibleCache oldCache;
+ mAccessibleCache.swap(oldCache);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandlerControl::OnTextChange(long aHwnd, long aIA2UniqueId,
+ VARIANT_BOOL aIsInsert,
+ IA2TextSegment* aText) {
+ if (!aText) {
+ return E_INVALIDARG;
+ }
+
+ mTextChange = detail::TextChange(aIA2UniqueId, aIsInsert, WrapNotNull(aText));
+ NotifyWinEvent(aIsInsert ? IA2_EVENT_TEXT_INSERTED : IA2_EVENT_TEXT_REMOVED,
+ reinterpret_cast<HWND>(static_cast<uintptr_t>(aHwnd)),
+ OBJID_CLIENT, aIA2UniqueId);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandlerControl::GetNewText(long aIA2UniqueId,
+ NotNull<IA2TextSegment*> aOutNewText) {
+ return mTextChange.GetNew(aIA2UniqueId, aOutNewText);
+}
+
+HRESULT
+AccessibleHandlerControl::GetOldText(long aIA2UniqueId,
+ NotNull<IA2TextSegment*> aOutOldText) {
+ return mTextChange.GetOld(aIA2UniqueId, aOutOldText);
+}
+
+HRESULT
+AccessibleHandlerControl::GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo) {
+ if (!mHandlerProxy) {
+ return E_UNEXPECTED;
+ }
+
+ return mHandlerProxy->GetTypeInfoForGuid(CLSID_AccessibleHandler,
+ aOutTypeInfo);
+}
+
+HRESULT
+AccessibleHandlerControl::Register(NotNull<IGeckoBackChannel*> aGecko) {
+ if (mIsRegistered) {
+ return S_OK;
+ }
+
+ long pid = static_cast<long>(::GetCurrentProcessId());
+ HRESULT hr = aGecko->put_HandlerControl(pid, this);
+ mIsRegistered = SUCCEEDED(hr);
+ MOZ_ASSERT(mIsRegistered);
+ return hr;
+}
+
+void AccessibleHandlerControl::CacheAccessible(long aUniqueId,
+ AccessibleHandler* aAccessible) {
+ MOZ_ASSERT(aUniqueId && aAccessible);
+ mAccessibleCache[aUniqueId] = aAccessible;
+}
+
+HRESULT AccessibleHandlerControl::GetCachedAccessible(
+ long aUniqueId, AccessibleHandler** aAccessible) {
+ MOZ_ASSERT(aUniqueId && aAccessible);
+ auto it = mAccessibleCache.find(aUniqueId);
+ if (it == mAccessibleCache.end()) {
+ return E_INVALIDARG;
+ }
+ RefPtr<AccessibleHandler> ref = it->second;
+ ref.forget(aAccessible);
+ return S_OK;
+}
+
+HRESULT AccessibleHandlerControl::SuppressA11yForClipboardCopy() {
+ mA11yClipboardCopySuppressionStartTime = ::GetTickCount();
+ return S_OK;
+}
+
+bool AccessibleHandlerControl::IsA11ySuppressedForClipboardCopy() {
+ // Must be kept in sync with kSuppressTimeout in
+ // accessible/windows/msaa/Compatibility.cpp.
+ constexpr DWORD kSuppressTimeout = 1500; // ms
+ if (!mA11yClipboardCopySuppressionStartTime) {
+ return false;
+ }
+ return ::GetTickCount() - mA11yClipboardCopySuppressionStartTime <
+ kSuppressTimeout;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/handler/AccessibleHandlerControl.h b/accessible/ipc/win/handler/AccessibleHandlerControl.h
new file mode 100644
index 0000000000..9a94ff9fe8
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.h
@@ -0,0 +1,103 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#ifndef mozilla_a11y_AccessibleHandlerControl_h
+# define mozilla_a11y_AccessibleHandlerControl_h
+
+# include <unordered_map>
+# include "Factory.h"
+# include "HandlerData.h"
+# include "IUnknownImpl.h"
+# include "mozilla/mscom/Registration.h"
+# include "mozilla/NotNull.h"
+
+namespace mozilla {
+namespace a11y {
+
+namespace detail {
+
+class TextChange final {
+ public:
+ TextChange();
+ TextChange(long aIA2UniqueId, bool aIsInsert, NotNull<IA2TextSegment*> aText);
+ TextChange(TextChange&& aOther);
+ TextChange(const TextChange& aOther);
+
+ TextChange& operator=(TextChange&& aOther);
+ TextChange& operator=(const TextChange& aOther);
+
+ ~TextChange();
+
+ HRESULT GetOld(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldSegment);
+ HRESULT GetNew(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewSegment);
+
+ private:
+ static BSTR BSTRCopy(const BSTR& aIn);
+ static HRESULT SegCopy(IA2TextSegment& aDest, const IA2TextSegment& aSrc);
+
+ long mIA2UniqueId;
+ bool mIsInsert;
+ IA2TextSegment mText;
+};
+
+} // namespace detail
+
+class AccessibleHandler;
+
+class AccessibleHandlerControl final : public IHandlerControl {
+ public:
+ static HRESULT Create(AccessibleHandlerControl** aOutObject);
+
+ DECL_IUNKNOWN
+
+ // IHandlerControl
+ STDMETHODIMP Invalidate() override;
+ STDMETHODIMP OnTextChange(long aHwnd, long aIA2UniqueId,
+ VARIANT_BOOL aIsInsert,
+ IA2TextSegment* aText) override;
+ STDMETHODIMP SuppressA11yForClipboardCopy() override;
+
+ uint32_t GetCacheGen() const { return mCacheGen; }
+
+ HRESULT GetNewText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewText);
+ HRESULT GetOldText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldText);
+
+ HRESULT GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo);
+
+ HRESULT Register(NotNull<IGeckoBackChannel*> aGecko);
+
+ void CacheAccessible(long aUniqueId, AccessibleHandler* aAccessible);
+ HRESULT GetCachedAccessible(long aUniqueId, AccessibleHandler** aAccessible);
+
+ bool IsA11ySuppressedForClipboardCopy();
+
+ private:
+ AccessibleHandlerControl();
+ ~AccessibleHandlerControl() = default;
+
+ bool mIsRegistered;
+ uint32_t mCacheGen;
+ detail::TextChange mTextChange;
+ UniquePtr<mscom::RegisteredProxy> mIA2Proxy;
+ UniquePtr<mscom::RegisteredProxy> mHandlerProxy;
+ // We can't use Gecko APIs in this dll, hence the use of std::unordered_map.
+ typedef std::unordered_map<long, RefPtr<AccessibleHandler>> AccessibleCache;
+ AccessibleCache mAccessibleCache;
+ // Time when SuppressA11yForClipboardCopy() was called, as returned by
+ // ::GetTickCount().
+ DWORD mA11yClipboardCopySuppressionStartTime = 0;
+};
+
+extern mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_AccessibleHandlerControl_h
diff --git a/accessible/ipc/win/handler/HandlerChildEnumerator.cpp b/accessible/ipc/win/handler/HandlerChildEnumerator.cpp
new file mode 100644
index 0000000000..2062387bf6
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerChildEnumerator.cpp
@@ -0,0 +1,170 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include "HandlerChildEnumerator.h"
+#include "HandlerTextLeaf.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace a11y {
+
+HandlerChildEnumerator::HandlerChildEnumerator(
+ AccessibleHandler* aHandler, IGeckoBackChannel* aGeckoBackChannel)
+ : mHandler(aHandler),
+ mGeckoBackChannel(aGeckoBackChannel),
+ mChildCount(0),
+ mNextChild(0) {
+ MOZ_ASSERT(aHandler);
+ MOZ_ASSERT(aGeckoBackChannel);
+}
+
+HandlerChildEnumerator::HandlerChildEnumerator(
+ const HandlerChildEnumerator& aEnumerator)
+ : mHandler(aEnumerator.mHandler),
+ mGeckoBackChannel(aEnumerator.mGeckoBackChannel),
+ mChildCount(aEnumerator.mChildCount),
+ mNextChild(aEnumerator.mNextChild) {
+ if (mChildCount == 0) {
+ return;
+ }
+ mChildren = MakeUnique<VARIANT[]>(mChildCount);
+ CopyMemory(mChildren.get(), aEnumerator.mChildren.get(),
+ sizeof(VARIANT) * mChildCount);
+ for (ULONG index = 0; index < mChildCount; ++index) {
+ mChildren[index].pdispVal->AddRef();
+ }
+}
+
+HandlerChildEnumerator::~HandlerChildEnumerator() { ClearCache(); }
+
+void HandlerChildEnumerator::ClearCache() {
+ if (!mChildren) {
+ return;
+ }
+
+ for (ULONG index = 0; index < mChildCount; ++index) {
+ mChildren[index].pdispVal->Release();
+ }
+
+ mChildren = nullptr;
+ mChildCount = 0;
+}
+
+HRESULT
+HandlerChildEnumerator::MaybeCacheChildren() {
+ if (mChildren) {
+ // Already cached.
+ return S_OK;
+ }
+
+ AccChildData* children;
+ HRESULT hr = mGeckoBackChannel->get_AllChildren(&children, &mChildCount);
+ if (FAILED(hr)) {
+ mChildCount = 0;
+ ClearCache();
+ return hr;
+ }
+
+ HWND hwnd = nullptr;
+ hr = mHandler->get_windowHandle(&hwnd);
+ MOZ_ASSERT(SUCCEEDED(hr));
+
+ RefPtr<IDispatch> parent;
+ hr = mHandler->QueryInterface(IID_IDispatch, getter_AddRefs(parent));
+ MOZ_ASSERT(SUCCEEDED(hr));
+
+ mChildren = MakeUnique<VARIANT[]>(mChildCount);
+ for (ULONG index = 0; index < mChildCount; ++index) {
+ AccChildData& data = children[index];
+ VARIANT& child = mChildren[index];
+ if (data.mAccessible) {
+ RefPtr<IDispatch> disp;
+ hr =
+ data.mAccessible->QueryInterface(IID_IDispatch, getter_AddRefs(disp));
+ data.mAccessible->Release();
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ child.vt = VT_EMPTY;
+ continue;
+ }
+ child.vt = VT_DISPATCH;
+ disp.forget(&child.pdispVal);
+ } else {
+ // Text leaf.
+ RefPtr<IDispatch> leaf(new HandlerTextLeaf(parent, index, hwnd, data));
+ child.vt = VT_DISPATCH;
+ leaf.forget(&child.pdispVal);
+ }
+ }
+
+ ::CoTaskMemFree(children);
+ return S_OK;
+}
+
+IMPL_IUNKNOWN_QUERY_HEAD(HandlerChildEnumerator)
+IMPL_IUNKNOWN_QUERY_IFACE(IEnumVARIANT)
+IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mHandler)
+
+/*** IEnumVARIANT ***/
+
+HRESULT
+HandlerChildEnumerator::Clone(IEnumVARIANT** aPpEnum) {
+ RefPtr<HandlerChildEnumerator> newEnum(new HandlerChildEnumerator(*this));
+ newEnum.forget(aPpEnum);
+ return S_OK;
+}
+
+HRESULT
+HandlerChildEnumerator::Next(ULONG aCelt, VARIANT* aRgVar,
+ ULONG* aPCeltFetched) {
+ if (!aRgVar || aCelt == 0) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = MaybeCacheChildren();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ for (ULONG index = 0; index < aCelt; ++index) {
+ if (mNextChild >= mChildCount) {
+ // Less elements remaining than were requested.
+ if (aPCeltFetched) {
+ *aPCeltFetched = index;
+ }
+ return S_FALSE;
+ }
+ aRgVar[index] = mChildren[mNextChild];
+ aRgVar[index].pdispVal->AddRef();
+ ++mNextChild;
+ }
+ *aPCeltFetched = aCelt;
+ return S_OK;
+}
+
+HRESULT
+HandlerChildEnumerator::Reset() {
+ mNextChild = 0;
+ ClearCache();
+ return S_OK;
+}
+
+HRESULT
+HandlerChildEnumerator::Skip(ULONG aCelt) {
+ mNextChild += aCelt;
+ if (mNextChild > mChildCount) {
+ // Less elements remaining than the client requested to skip.
+ return S_FALSE;
+ }
+ return S_OK;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/handler/HandlerChildEnumerator.h b/accessible/ipc/win/handler/HandlerChildEnumerator.h
new file mode 100644
index 0000000000..df0218c6ed
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerChildEnumerator.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#ifndef mozilla_a11y_HandlerChildEnumerator_h
+# define mozilla_a11y_HandlerChildEnumerator_h
+
+# include "AccessibleHandler.h"
+# include "IUnknownImpl.h"
+# include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HandlerChildEnumerator final : public IEnumVARIANT {
+ public:
+ explicit HandlerChildEnumerator(AccessibleHandler* aHandler,
+ IGeckoBackChannel* aGeckoBackChannel);
+
+ DECL_IUNKNOWN
+
+ // IEnumVARIANT
+ STDMETHODIMP Clone(IEnumVARIANT** aPpEnum) override;
+ STDMETHODIMP Next(ULONG aCelt, VARIANT* aRgVar,
+ ULONG* aPCeltFetched) override;
+ STDMETHODIMP Reset() override;
+ STDMETHODIMP Skip(ULONG aCelt) override;
+
+ private:
+ explicit HandlerChildEnumerator(const HandlerChildEnumerator& aEnumerator);
+ ~HandlerChildEnumerator();
+ void ClearCache();
+ HRESULT MaybeCacheChildren();
+
+ RefPtr<AccessibleHandler> mHandler;
+ RefPtr<IGeckoBackChannel> mGeckoBackChannel;
+ UniquePtr<VARIANT[]> mChildren;
+ ULONG mChildCount;
+ ULONG mNextChild;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerChildEnumerator_h
diff --git a/accessible/ipc/win/handler/HandlerData.acf b/accessible/ipc/win/handler/HandlerData.acf
new file mode 100644
index 0000000000..993240b063
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerData.acf
@@ -0,0 +1,11 @@
+/* -*- 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/. */
+
+[explicit_handle]
+interface HandlerData
+{
+ typedef [encode,decode] IA2Payload;
+}
diff --git a/accessible/ipc/win/handler/HandlerData.idl b/accessible/ipc/win/handler/HandlerData.idl
new file mode 100644
index 0000000000..e449a88263
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerData.idl
@@ -0,0 +1,155 @@
+/* -*- 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 "mozilla-config.h"
+#include "AccessibleHandler.h"
+#include "HandlerDataUUID.h"
+
+import "ocidl.idl";
+import "servprov.idl";
+
+import "Accessible2_3.idl";
+import "AccessibleHypertext2.idl";
+import "AccessibleHyperlink.idl";
+import "AccessibleTable.idl";
+import "AccessibleTable2.idl";
+import "AccessibleTableCell.idl";
+
+typedef struct _StaticIA2Data
+{
+ NEWEST_IA2_INTERFACE* mIA2;
+ IAccessibleHypertext2* mIAHypertext;
+ IAccessibleHyperlink* mIAHyperlink;
+ IAccessibleTable* mIATable;
+ IAccessibleTable2* mIATable2;
+ IAccessibleTableCell* mIATableCell;
+} StaticIA2Data;
+
+typedef struct _DynamicIA2Data
+{
+ // From IAccessible/IAccessible2
+ VARIANT mRole;
+ long mState;
+ long mChildCount;
+ long mIA2Role;
+ AccessibleStates mIA2States;
+ long mLeft;
+ long mTop;
+ long mWidth;
+ long mHeight;
+ long mHwnd;
+ BSTR mKeyboardShortcut;
+ BSTR mName;
+ BSTR mDescription;
+ BSTR mDefaultAction;
+ BSTR mValue;
+ BSTR mAttributes;
+ IA2Locale mIA2Locale;
+ // From IAccessibleAction
+ long mNActions;
+ // From IAccessibleTableCell
+ long mRowIndex;
+ long mColumnIndex;
+ long mRowExtent;
+ long mColumnExtent;
+ boolean mCellIsSelected;
+ long mNRowHeaderCells;
+ [size_is(mNRowHeaderCells)] long* mRowHeaderCellIds;
+ long mNColumnHeaderCells;
+ [size_is(mNColumnHeaderCells)] long* mColumnHeaderCellIds;
+ // From IAccessible2
+ long mUniqueId;
+} DynamicIA2Data;
+
+interface IGeckoBackChannel;
+
+[uuid(2b0e83b3-fd1a-443f-9ed6-c00d39055b58)]
+interface HandlerData
+{
+ typedef struct _IA2Payload
+ {
+ StaticIA2Data mStaticData;
+ DynamicIA2Data mDynamicData;
+ IGeckoBackChannel* mGeckoBackChannel;
+ } IA2Payload;
+}
+
+[object,
+ uuid(IHANDLERCONTROL_IID),
+ async_uuid(ASYNCIHANDLERCONTROL_IID),
+ pointer_default(unique)]
+interface IHandlerControl : IUnknown
+{
+ HRESULT Invalidate();
+ HRESULT OnTextChange([in] long aHwnd, [in] long aIA2UniqueId,
+ [in] VARIANT_BOOL aIsInsert,
+ [in] IA2TextSegment* aText);
+ HRESULT SuppressA11yForClipboardCopy();
+}
+
+typedef struct _IARelationData
+{
+ BSTR mType;
+ long mNTargets;
+} IARelationData;
+
+typedef struct _AccChildData
+{
+ NEWEST_IA2_INTERFACE* mAccessible;
+ BSTR mText;
+ long mTextRole;
+ long mTextId;
+ long mTextState;
+ long mTextLeft;
+ long mTextTop;
+ long mTextWidth;
+ long mTextHeight;
+} AccChildData;
+
+[object,
+ uuid(IGECKOBACKCHANNEL_IID),
+ pointer_default(unique)]
+interface IGeckoBackChannel : IUnknown
+{
+ [propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl);
+ HRESULT Refresh([out] DynamicIA2Data* aOutData);
+ [propget] HRESULT AllTextInfo([out] BSTR* aText,
+ [out, size_is(,*aNHyperlinks)] IAccessibleHyperlink*** aHyperlinks,
+ [out] long* aNHyperlinks,
+ [out, size_is(,*aNAttribRuns)] IA2TextSegment** aAttribRuns,
+ [out] long* aNAttribRuns);
+ [propget] HRESULT RelationsInfo(
+ [out, size_is(,*aNRelations)] IARelationData** aRelations,
+ [out] long* aNRelations);
+ [propget] HRESULT AllChildren(
+ [out, size_is(,*aNChildren)] AccChildData** aChildren,
+ [out] ULONG* aNChildren);
+}
+
+[uuid(1e545f07-f108-4912-9471-546827a80983)]
+library AccessibleHandlerTypeLib
+{
+ importlib("stdole2.tlb");
+
+ /**
+ * This definition is required in order for the handler implementation to
+ * support IDispatch (aka Automation). This is used by interpreted language
+ * FFIs to discover which interfaces may be controlled via IDispatch.
+ * (In particular, the python FFI used by NVDA needs this).
+ *
+ * In reality, the only a11y interface that is Automation compliant is
+ * IAccessible; our remaining interfaces are not.
+ *
+ * Once the FFI knows that IAccessible is supported, the FFI queries for
+ * IAccessible and is then able to resolve non-automation interfaces from
+ * there.
+ */
+ [uuid(HANDLER_CLSID)]
+ coclass AccessibleHandler
+ {
+ [default] interface IAccessible;
+ };
+};
diff --git a/accessible/ipc/win/handler/HandlerDataCleanup.h b/accessible/ipc/win/handler/HandlerDataCleanup.h
new file mode 100644
index 0000000000..14d9995c19
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerDataCleanup.h
@@ -0,0 +1,95 @@
+/* -*- 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_HandlerDataCleanup_h
+#define mozilla_a11y_HandlerDataCleanup_h
+
+#include <oleauto.h>
+
+namespace mozilla {
+namespace a11y {
+
+inline void ReleaseStaticIA2DataInterfaces(StaticIA2Data& aData) {
+ // Only interfaces of the proxied object wrapped by this handler should be
+ // released here, never other objects!
+ // For example, if StaticIA2Data were to include accParent in future,
+ // that must not be released here.
+ if (aData.mIA2) {
+ aData.mIA2->Release();
+ }
+ if (aData.mIAHypertext) {
+ aData.mIAHypertext->Release();
+ }
+ if (aData.mIAHyperlink) {
+ aData.mIAHyperlink->Release();
+ }
+ if (aData.mIATable) {
+ aData.mIATable->Release();
+ }
+ if (aData.mIATable2) {
+ aData.mIATable2->Release();
+ }
+ if (aData.mIATableCell) {
+ aData.mIATableCell->Release();
+ }
+}
+
+/**
+ * Pass true for aMarshaledByCom if this struct was directly marshaled as an
+ * out parameter of a COM method, currently only IGeckoBackChannel::Refresh.
+ */
+inline void CleanupDynamicIA2Data(DynamicIA2Data& aData,
+ bool aMarshaledByCom = false) {
+ // If freeing generic memory returned to the client, you *must* use freeMem,
+ // not CoTaskMemFree!
+ auto freeMem = [aMarshaledByCom](void* aMem) {
+ if (aMarshaledByCom) {
+ ::CoTaskMemFree(aMem);
+ } else {
+ ::midl_user_free(aMem);
+ }
+ };
+
+ ::VariantClear(&aData.mRole);
+ if (aData.mKeyboardShortcut) {
+ ::SysFreeString(aData.mKeyboardShortcut);
+ }
+ if (aData.mName) {
+ ::SysFreeString(aData.mName);
+ }
+ if (aData.mDescription) {
+ ::SysFreeString(aData.mDescription);
+ }
+ if (aData.mDefaultAction) {
+ ::SysFreeString(aData.mDefaultAction);
+ }
+ if (aData.mValue) {
+ ::SysFreeString(aData.mValue);
+ }
+ if (aData.mAttributes) {
+ ::SysFreeString(aData.mAttributes);
+ }
+ if (aData.mIA2Locale.language) {
+ ::SysFreeString(aData.mIA2Locale.language);
+ }
+ if (aData.mIA2Locale.country) {
+ ::SysFreeString(aData.mIA2Locale.country);
+ }
+ if (aData.mIA2Locale.variant) {
+ ::SysFreeString(aData.mIA2Locale.variant);
+ }
+ if (aData.mRowHeaderCellIds) {
+ freeMem(aData.mRowHeaderCellIds);
+ }
+ if (aData.mColumnHeaderCellIds) {
+ freeMem(aData.mColumnHeaderCellIds);
+ }
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerDataCleanup_h
diff --git a/accessible/ipc/win/handler/HandlerDataUUID.h.in b/accessible/ipc/win/handler/HandlerDataUUID.h.in
new file mode 100644
index 0000000000..234c3d6114
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerDataUUID.h.in
@@ -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/. */
+
+// We use different CLSIDs and IIDs depending on channel and officiality.
+// This prevents handlers from installing overtop one another when multiple
+// channels are present. Note that we only do this for the UUIDs that are
+// written to the registry.
+// The specific UUIDs are defined in branding configuration.
+
+#define HANDLER_CLSID @MOZ_HANDLER_CLSID@
+#define IHANDLERCONTROL_IID @MOZ_IHANDLERCONTROL_IID@
+#define ASYNCIHANDLERCONTROL_IID @MOZ_ASYNCIHANDLERCONTROL_IID@
+#define IGECKOBACKCHANNEL_IID @MOZ_IGECKOBACKCHANNEL_IID@
diff --git a/accessible/ipc/win/handler/HandlerRelation.cpp b/accessible/ipc/win/handler/HandlerRelation.cpp
new file mode 100644
index 0000000000..8e03da5360
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerRelation.cpp
@@ -0,0 +1,138 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include "HandlerRelation.h"
+#include "mozilla/Assertions.h"
+
+#include "AccessibleRelation_i.c"
+
+namespace mozilla {
+namespace a11y {
+
+HandlerRelation::HandlerRelation(AccessibleHandler* aHandler,
+ IARelationData& aData)
+ : mHandler(aHandler), mData(aData), mTargets(nullptr) {
+ // This instance now owns any pointers, so ensure no one else can
+ // manipulate them.
+ aData.mType = nullptr;
+}
+
+HandlerRelation::~HandlerRelation() {
+ if (mData.mType) {
+ ::SysFreeString(mData.mType);
+ }
+
+ if (mTargets) {
+ for (long index = 0; index < mData.mNTargets; ++index) {
+ mTargets[index]->Release();
+ }
+ ::CoTaskMemFree(mTargets);
+ mTargets = nullptr;
+ }
+}
+
+HRESULT
+HandlerRelation::GetTargets() {
+ if (mTargets) {
+ // Already cached.
+ return S_OK;
+ }
+
+ // Marshaling all of the IAccessibleRelation objects across processes is
+ // slow, and the client probably only wants targets for a few of them.
+ // Therefore, we just use IAccessible2_2::relationTargetsOfType, passing
+ // the type we have cached. This is a bit inefficient because Gecko has
+ // to look up the relation twice, but it's significantly faster than
+ // marshaling the relation objects regardless.
+ return mHandler->get_relationTargetsOfType(mData.mType, 0, &mTargets,
+ &mData.mNTargets);
+}
+
+IMPL_IUNKNOWN1(HandlerRelation, IAccessibleRelation)
+
+/*** IAccessibleRelation ***/
+
+HRESULT
+HandlerRelation::get_relationType(BSTR* aType) {
+ if (!aType) {
+ return E_INVALIDARG;
+ }
+ if (!mData.mType) {
+ return E_FAIL;
+ }
+ *aType = CopyBSTR(mData.mType);
+ return S_OK;
+}
+
+HRESULT
+HandlerRelation::get_localizedRelationType(BSTR* aLocalizedType) {
+ // This is not implemented as per ia2AccessibleRelation.
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerRelation::get_nTargets(long* aNTargets) {
+ if (!aNTargets) {
+ return E_INVALIDARG;
+ }
+ if (mData.mNTargets == -1) {
+ return E_FAIL;
+ }
+ *aNTargets = mData.mNTargets;
+ return S_OK;
+}
+
+HRESULT
+HandlerRelation::get_target(long aIndex, IUnknown** aTarget) {
+ if (!aTarget) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = GetTargets();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ if (aIndex >= mData.mNTargets) {
+ return E_INVALIDARG;
+ }
+
+ *aTarget = mTargets[aIndex];
+ (*aTarget)->AddRef();
+ return S_OK;
+}
+
+HRESULT
+HandlerRelation::get_targets(long aMaxTargets, IUnknown** aTargets,
+ long* aNTargets) {
+ if (aMaxTargets == 0 || !aTargets || !aNTargets) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = GetTargets();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (mData.mNTargets > aMaxTargets) {
+ // Don't give back more targets than were requested.
+ *aNTargets = aMaxTargets;
+ } else {
+ *aNTargets = mData.mNTargets;
+ }
+
+ for (long index = 0; index < *aNTargets; ++index) {
+ aTargets[index] = mTargets[index];
+ aTargets[index]->AddRef();
+ }
+ return S_OK;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/handler/HandlerRelation.h b/accessible/ipc/win/handler/HandlerRelation.h
new file mode 100644
index 0000000000..e25b4ee189
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerRelation.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#ifndef mozilla_a11y_HandlerRelation_h
+# define mozilla_a11y_HandlerRelation_h
+
+# include "AccessibleHandler.h"
+# include "IUnknownImpl.h"
+# include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HandlerRelation final : public IAccessibleRelation {
+ public:
+ explicit HandlerRelation(AccessibleHandler* aHandler, IARelationData& aData);
+
+ DECL_IUNKNOWN
+
+ // IAccessibleRelation
+ STDMETHODIMP get_relationType(BSTR* aType) override;
+ STDMETHODIMP get_localizedRelationType(BSTR* aLocalizedType) override;
+ STDMETHODIMP get_nTargets(long* aNTargets) override;
+ STDMETHODIMP get_target(long aIndex, IUnknown** aTarget) override;
+ STDMETHODIMP get_targets(long aMaxTargets, IUnknown** aTargets,
+ long* aNTargets) override;
+
+ private:
+ virtual ~HandlerRelation();
+ HRESULT GetTargets();
+ RefPtr<AccessibleHandler> mHandler;
+ IARelationData mData;
+ IUnknown** mTargets;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerRelation_h
diff --git a/accessible/ipc/win/handler/HandlerTextLeaf.cpp b/accessible/ipc/win/handler/HandlerTextLeaf.cpp
new file mode 100644
index 0000000000..730aa88df9
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerTextLeaf.cpp
@@ -0,0 +1,337 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include "HandlerTextLeaf.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace a11y {
+
+HandlerTextLeaf::HandlerTextLeaf(IDispatch* aParent, long aIndexInParent,
+ HWND aHwnd, AccChildData& aData)
+ : mParent(aParent),
+ mIndexInParent(aIndexInParent),
+ mHwnd(aHwnd),
+ mData(aData) {
+ MOZ_ASSERT(aParent);
+}
+
+HandlerTextLeaf::~HandlerTextLeaf() {
+ if (mData.mText) {
+ ::SysFreeString(mData.mText);
+ }
+}
+
+IMPL_IUNKNOWN_QUERY_HEAD(HandlerTextLeaf)
+IMPL_IUNKNOWN_QUERY_IFACE(IDispatch)
+IMPL_IUNKNOWN_QUERY_IFACE(IAccessible)
+IMPL_IUNKNOWN_QUERY_IFACE(IAccessible2)
+IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider)
+IMPL_IUNKNOWN_QUERY_TAIL
+
+/*** IDispatch ***/
+
+HRESULT
+HandlerTextLeaf::GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+ LCID lcid, DISPID* rgDispId) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
+ WORD wFlags, DISPPARAMS* pDispParams,
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
+ UINT* puArgErr) {
+ return E_NOTIMPL;
+}
+
+/*** IAccessible ***/
+
+HRESULT
+HandlerTextLeaf::get_accParent(IDispatch** ppdispParent) {
+ if (!ppdispParent) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<IDispatch> parent(mParent);
+ parent.forget(ppdispParent);
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_accChildCount(long* pcountChildren) {
+ if (!pcountChildren) {
+ return E_INVALIDARG;
+ }
+
+ *pcountChildren = 0;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_accChild(VARIANT varChild, IDispatch** ppdispChild) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accName(VARIANT varChild, BSTR* pszName) {
+ if (varChild.lVal != CHILDID_SELF || !pszName) {
+ return E_INVALIDARG;
+ }
+
+ *pszName = CopyBSTR(mData.mText);
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_accValue(VARIANT varChild, BSTR* pszValue) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accDescription(VARIANT varChild, BSTR* pszDescription) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
+ if (varChild.lVal != CHILDID_SELF || !pvarRole) {
+ return E_INVALIDARG;
+ }
+
+ pvarRole->vt = VT_I4;
+ pvarRole->lVal = mData.mTextRole;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_accState(VARIANT varChild, VARIANT* pvarState) {
+ if (varChild.lVal != CHILDID_SELF || !pvarState) {
+ return E_INVALIDARG;
+ }
+
+ pvarState->vt = VT_I4;
+ pvarState->lVal = mData.mTextState;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
+ long* pidTopic) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accKeyboardShortcut(VARIANT varChild,
+ BSTR* pszKeyboardShortcut) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_accFocus(VARIANT* pvarChild) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::get_accSelection(VARIANT* pvarChildren) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::get_accDefaultAction(VARIANT varChild,
+ BSTR* pszDefaultAction) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::accSelect(long flagsSelect, VARIANT varChild) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
+ long* pcyHeight, VARIANT varChild) {
+ if (varChild.lVal != CHILDID_SELF || !pxLeft || !pyTop || !pcxWidth ||
+ !pcyHeight) {
+ return E_INVALIDARG;
+ }
+
+ *pxLeft = mData.mTextLeft;
+ *pyTop = mData.mTextTop;
+ *pcxWidth = mData.mTextWidth;
+ *pcyHeight = mData.mTextHeight;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::accNavigate(long navDir, VARIANT varStart,
+ VARIANT* pvarEndUpAt) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::accHitTest(long xLeft, long yTop, VARIANT* pvarChild) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::accDoDefaultAction(VARIANT varChild) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::put_accName(VARIANT varChild, BSTR szName) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::put_accValue(VARIANT varChild, BSTR szValue) {
+ return E_NOTIMPL;
+}
+
+/*** IAccessible2 ***/
+
+HRESULT
+HandlerTextLeaf::get_nRelations(long* nRelations) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::get_relation(long relationIndex,
+ IAccessibleRelation** relation) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_relations(long maxRelations,
+ IAccessibleRelation** relations,
+ long* nRelations) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::role(long* role) {
+ if (!role) {
+ return E_INVALIDARG;
+ }
+
+ *role = mData.mTextRole;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::scrollTo(IA2ScrollType scrollType) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::scrollToPoint(IA2CoordinateType coordinateType, long x,
+ long y) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_groupPosition(long* groupLevel, long* similarItemsInGroup,
+ long* positionInGroup) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_states(AccessibleStates* states) {
+ if (!states) {
+ return E_INVALIDARG;
+ }
+
+ *states = 0;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_extendedRole(BSTR* extendedRole) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::get_localizedExtendedRole(BSTR* localizedExtendedRole) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_nExtendedStates(long* nExtendedStates) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_extendedStates(long maxExtendedStates,
+ BSTR** extendedStates,
+ long* nExtendedStates) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_localizedExtendedStates(long maxLocalizedExtendedStates,
+ BSTR** localizedExtendedStates,
+ long* nLocalizedExtendedStates) {
+ return E_NOTIMPL;
+}
+
+HRESULT
+HandlerTextLeaf::get_uniqueID(long* uniqueID) {
+ if (!uniqueID) {
+ return E_INVALIDARG;
+ }
+
+ *uniqueID = mData.mTextId;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_windowHandle(HWND* windowHandle) {
+ if (!windowHandle) {
+ return E_INVALIDARG;
+ }
+
+ *windowHandle = mHwnd;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_indexInParent(long* indexInParent) {
+ if (!indexInParent) {
+ return E_INVALIDARG;
+ }
+
+ *indexInParent = mIndexInParent;
+ return S_OK;
+}
+
+HRESULT
+HandlerTextLeaf::get_locale(IA2Locale* locale) { return E_NOTIMPL; }
+
+HRESULT
+HandlerTextLeaf::get_attributes(BSTR* attributes) { return E_NOTIMPL; }
+
+/*** IServiceProvider ***/
+
+HRESULT
+HandlerTextLeaf::QueryService(REFGUID aServiceId, REFIID aIid,
+ void** aOutInterface) {
+ if (aIid == IID_IAccessible2) {
+ RefPtr<IAccessible2> ia2(this);
+ ia2.forget(aOutInterface);
+ return S_OK;
+ }
+
+ return E_INVALIDARG;
+}
+
+} // namespace a11y
+} // namespace mozilla
diff --git a/accessible/ipc/win/handler/HandlerTextLeaf.h b/accessible/ipc/win/handler/HandlerTextLeaf.h
new file mode 100644
index 0000000000..f1a31b9219
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerTextLeaf.h
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+#if defined(MOZILLA_INTERNAL_API)
+# error This code is NOT for internal Gecko use!
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#ifndef mozilla_a11y_HandlerTextLeaf_h
+# define mozilla_a11y_HandlerTextLeaf_h
+
+# include "AccessibleHandler.h"
+# include "IUnknownImpl.h"
+# include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HandlerTextLeaf final : public IAccessible2, public IServiceProvider {
+ public:
+ explicit HandlerTextLeaf(IDispatch* aParent, long aIndexInParent, HWND aHwnd,
+ AccChildData& aData);
+
+ DECL_IUNKNOWN
+
+ // IDispatch
+ STDMETHODIMP GetTypeInfoCount(UINT* pctinfo) override;
+ STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid,
+ ITypeInfo** ppTInfo) override;
+ STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
+ LCID lcid, DISPID* rgDispId) override;
+ STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS* pDispParams, VARIANT* pVarResult,
+ EXCEPINFO* pExcepInfo, UINT* puArgErr) override;
+
+ // IAccessible
+ STDMETHODIMP get_accParent(IDispatch** ppdispParent) override;
+ STDMETHODIMP get_accChildCount(long* pcountChildren) override;
+ STDMETHODIMP get_accChild(VARIANT varChild, IDispatch** ppdispChild) override;
+ STDMETHODIMP get_accName(VARIANT varChild, BSTR* pszName) override;
+ STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pszValue) override;
+ STDMETHODIMP get_accDescription(VARIANT varChild,
+ BSTR* pszDescription) override;
+ STDMETHODIMP get_accRole(VARIANT varChild, VARIANT* pvarRole) override;
+ STDMETHODIMP get_accState(VARIANT varChild, VARIANT* pvarState) override;
+ STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pszHelp) override;
+ STDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
+ long* pidTopic) override;
+ STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild,
+ BSTR* pszKeyboardShortcut) override;
+ STDMETHODIMP get_accFocus(VARIANT* pvarChild) override;
+ STDMETHODIMP get_accSelection(VARIANT* pvarChildren) override;
+ STDMETHODIMP get_accDefaultAction(VARIANT varChild,
+ BSTR* pszDefaultAction) override;
+ STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) override;
+ STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
+ long* pcyHeight, VARIANT varChild) override;
+ STDMETHODIMP accNavigate(long navDir, VARIANT varStart,
+ VARIANT* pvarEndUpAt) override;
+ STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT* pvarChild) override;
+ STDMETHODIMP accDoDefaultAction(VARIANT varChild) override;
+ STDMETHODIMP put_accName(VARIANT varChild, BSTR szName) override;
+ STDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) override;
+
+ // IAccessible2
+ STDMETHODIMP get_nRelations(long* nRelations) override;
+ STDMETHODIMP get_relation(long relationIndex,
+ IAccessibleRelation** relation) override;
+ STDMETHODIMP get_relations(long maxRelations, IAccessibleRelation** relations,
+ long* nRelations) override;
+ STDMETHODIMP role(long* role) override;
+ STDMETHODIMP scrollTo(IA2ScrollType scrollType) override;
+ STDMETHODIMP scrollToPoint(IA2CoordinateType coordinateType, long x,
+ long y) override;
+ STDMETHODIMP get_groupPosition(long* groupLevel, long* similarItemsInGroup,
+ long* positionInGroup) override;
+ STDMETHODIMP get_states(AccessibleStates* states) override;
+ STDMETHODIMP get_extendedRole(BSTR* extendedRole) override;
+ STDMETHODIMP get_localizedExtendedRole(BSTR* localizedExtendedRole) override;
+ STDMETHODIMP get_nExtendedStates(long* nExtendedStates) override;
+ STDMETHODIMP get_extendedStates(long maxExtendedStates, BSTR** extendedStates,
+ long* nExtendedStates) override;
+ STDMETHODIMP get_localizedExtendedStates(
+ long maxLocalizedExtendedStates, BSTR** localizedExtendedStates,
+ long* nLocalizedExtendedStates) override;
+ STDMETHODIMP get_uniqueID(long* uniqueID) override;
+ STDMETHODIMP get_windowHandle(HWND* windowHandle) override;
+ STDMETHODIMP get_indexInParent(long* indexInParent) override;
+ STDMETHODIMP get_locale(IA2Locale* locale) override;
+ STDMETHODIMP get_attributes(BSTR* attributes) override;
+
+ // IServiceProvider
+ STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid,
+ void** aOutInterface) override;
+
+ private:
+ ~HandlerTextLeaf();
+
+ RefPtr<IDispatch> mParent;
+ long mIndexInParent;
+ HWND mHwnd;
+ AccChildData mData;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerTextLeaf_h
diff --git a/accessible/ipc/win/handler/moz.build b/accessible/ipc/win/handler/moz.build
new file mode 100644
index 0000000000..1242bb6b8c
--- /dev/null
+++ b/accessible/ipc/win/handler/moz.build
@@ -0,0 +1,132 @@
+# -*- 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/.
+
+SharedLibrary("AccessibleHandler")
+
+EXPORTS.mozilla.a11y += [
+ "AccessibleHandler.h",
+ "HandlerDataCleanup.h",
+]
+
+LOCAL_INCLUDES += [
+ "/accessible/interfaces/ia2",
+ "/ipc/mscom/oop",
+]
+
+# We want to generate distinct UUIDs on a per-channel basis, so we need
+# finer granularity than the standard preprocessor definitions offer.
+# This generated include allow us to separate local builds from automated
+# builds, separate beta from release, and use different UUIDs in downstream
+# projects such as Thunderbird.
+CONFIGURE_SUBST_FILES += ["HandlerDataUUID.h"]
+flags = []
+
+GeneratedFile(
+ "HandlerData.h",
+ "HandlerData_p.c",
+ "HandlerData_i.c",
+ "HandlerData_c.c",
+ "HandlerData_dlldata.c",
+ "HandlerData.tlb",
+ inputs=["HandlerData.idl"],
+ script="/build/midl.py",
+ entry_point="midl",
+ flags=flags
+ + [
+ "-I",
+ TOPOBJDIR,
+ "-I",
+ TOPOBJDIR + "/dist/include",
+ "-I",
+ TOPSRCDIR + "/other-licenses/ia2",
+ "-I",
+ SRCDIR,
+ "-I",
+ OBJDIR,
+ "-acf",
+ SRCDIR + "/HandlerData.acf",
+ "-dlldata",
+ OBJDIR + "/HandlerData_dlldata.c",
+ ],
+)
+
+SOURCES += [
+ "!HandlerData_c.c",
+ "!HandlerData_dlldata.c",
+ "!HandlerData_i.c",
+ "!HandlerData_p.c",
+ "AccessibleHandler.cpp",
+ "AccessibleHandlerControl.cpp",
+ "HandlerChildEnumerator.cpp",
+ "HandlerRelation.cpp",
+ "HandlerTextLeaf.cpp",
+]
+
+EXPORTS += [
+ "!HandlerData.h",
+ "!HandlerData_i.c",
+ "!HandlerDataUUID.h",
+]
+
+# Give some symbols a unique name in each translation unit, to avoid
+# collisions caused by https://llvm.org/pr41817.
+if CONFIG["CC_TYPE"] == "clang-cl":
+ SOURCES["!HandlerData_p.c"].flags += [
+ "-DHandlerData__MIDL_ProcFormatString=HandlerData__MIDL_ProcFormatString__HandlerData_p"
+ ]
+ SOURCES["!HandlerData_p.c"].flags += [
+ "-DHandlerData__MIDL_TypeFormatString=HandlerData__MIDL_TypeFormatString__HandlerData_p"
+ ]
+ for p in ("dlldata", "c", "i", "p"):
+ SOURCES["!HandlerData_%s.c" % p].flags += [
+ "-DUserMarshalRoutines=UserMarshalRoutines__HandlerData_%s" % p
+ ]
+
+DEFFILE = "AccessibleHandler.def"
+
+USE_LIBS += [
+ "mscom_oop",
+]
+
+OS_LIBS += [
+ "advapi32",
+ "uuid",
+ "rpcrt4",
+ "oleacc",
+ "user32",
+]
+
+RCINCLUDE = "AccessibleHandler.rc"
+
+# Suppress warnings from the MIDL generated code.
+if CONFIG["CC_TYPE"] == "clang-cl":
+ CFLAGS += [
+ "-Wno-extern-initializer",
+ "-Wno-incompatible-pointer-types",
+ "-Wno-missing-braces",
+ "-Wno-unused-const-variable",
+ ]
+
+# Since we are defining our own COM entry points (DllRegisterServer et al),
+# but we still want to be able to delegate some work to the generated code,
+# we add the prefix "Proxy" to all of the generated counterparts.
+DEFINES["ENTRY_PREFIX"] = "Proxy"
+DEFINES["REGISTER_PROXY_DLL"] = True
+LIBRARY_DEFINES["MOZ_MSCOM_REMARSHAL_NO_HANDLER"] = True
+
+# This DLL may be loaded into other processes, so we need static libs for
+# Windows 7 and Windows 8.
+USE_STATIC_LIBS = True
+
+LIBRARY_DEFINES["UNICODE"] = True
+LIBRARY_DEFINES["_UNICODE"] = True
+LIBRARY_DEFINES["MOZ_NO_MOZALLOC"] = True
+DisableStlWrapping()
+
+DELAYLOAD_DLLS += [
+ "advapi32.dll",
+ "ktmw32.dll",
+]
diff --git a/accessible/ipc/win/moz.build b/accessible/ipc/win/moz.build
new file mode 100644
index 0000000000..597f358f0b
--- /dev/null
+++ b/accessible/ipc/win/moz.build
@@ -0,0 +1,63 @@
+# -*- 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/.
+
+if CONFIG["COMPILE_ENVIRONMENT"] and CONFIG["ACCESSIBILITY"]:
+ DIRS += [
+ "handler",
+ "typelib",
+ ]
+
+if CONFIG["ACCESSIBILITY"]:
+ IPDL_SOURCES += ["PDocAccessible.ipdl"]
+
+ EXPORTS.mozilla.a11y += [
+ "COMPtrTypes.h",
+ "DocAccessibleChild.h",
+ "HandlerProvider.h",
+ "PlatformChild.h",
+ "RemoteAccessible.h",
+ ]
+
+ SOURCES += [
+ "!./handler/HandlerData_c.c",
+ "COMPtrTypes.cpp",
+ "DocAccessibleChild.cpp",
+ "HandlerProvider.cpp",
+ "PlatformChild.cpp",
+ "RemoteAccessible.cpp",
+ ]
+
+ # Give some symbols a unique name in each translation unit, to avoid
+ # collisions caused by https://llvm.org/pr41817.
+ if CONFIG["CC_TYPE"] == "clang-cl":
+ SOURCES["!./handler/HandlerData_c.c"].flags += [
+ "-DUserMarshalRoutines=UserMarshalRoutines__HandlerData_c"
+ ]
+
+ LOCAL_INCLUDES += [
+ "/accessible/base",
+ "/accessible/generic",
+ "/accessible/windows/ia2",
+ "/accessible/windows/msaa",
+ "/accessible/xpcom",
+ ]
+
+ # Suppress warnings from the MIDL generated code.
+ if CONFIG["CC_TYPE"] == "clang-cl":
+ CFLAGS += [
+ "-Wno-extern-initializer",
+ "-Wno-incompatible-pointer-types",
+ "-Wno-missing-braces",
+ "-Wno-unused-const-variable",
+ ]
+
+ CXXFLAGS += [
+ "-Wno-missing-braces",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/accessible/ipc/win/typelib/Accessible.idl b/accessible/ipc/win/typelib/Accessible.idl
new file mode 100644
index 0000000000..10d310a316
--- /dev/null
+++ b/accessible/ipc/win/typelib/Accessible.idl
@@ -0,0 +1,15 @@
+/* -*- 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;
+};
diff --git a/accessible/ipc/win/typelib/moz.build b/accessible/ipc/win/typelib/moz.build
new file mode 100644
index 0000000000..2992deda1a
--- /dev/null
+++ b/accessible/ipc/win/typelib/moz.build
@@ -0,0 +1,16 @@
+# -*- 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",
+]
+
+GeneratedFile(
+ "Accessible.tlb",
+ inputs=["Accessible.idl"],
+ script="/build/midl.py",
+ entry_point="midl",
+)