summaryrefslogtreecommitdiffstats
path: root/accessible/ipc/win/RemoteAccessible.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/ipc/win/RemoteAccessible.cpp')
-rw-r--r--accessible/ipc/win/RemoteAccessible.cpp943
1 files changed, 943 insertions, 0 deletions
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