ProxyAccessible.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "Accessible2.h"
  6. #include "ProxyAccessible.h"
  7. #include "ia2AccessibleValue.h"
  8. #include "mozilla/a11y/DocAccessibleParent.h"
  9. #include "DocAccessible.h"
  10. #include "mozilla/a11y/DocManager.h"
  11. #include "mozilla/dom/Element.h"
  12. #include "mozilla/dom/TabParent.h"
  13. #include "mozilla/Unused.h"
  14. #include "mozilla/a11y/Platform.h"
  15. #include "RelationType.h"
  16. #include "mozilla/a11y/Role.h"
  17. #include "xpcAccessibleDocument.h"
  18. #include <comutil.h>
  19. static const VARIANT kChildIdSelf = {VT_I4};
  20. namespace mozilla {
  21. namespace a11y {
  22. bool
  23. ProxyAccessible::GetCOMInterface(void** aOutAccessible) const
  24. {
  25. if (!aOutAccessible) {
  26. return false;
  27. }
  28. if (!mCOMProxy) {
  29. // See if we can lazily obtain a COM proxy
  30. AccessibleWrap* wrap = WrapperFor(this);
  31. bool isDefunct = false;
  32. ProxyAccessible* thisPtr = const_cast<ProxyAccessible*>(this);
  33. // NB: Don't pass CHILDID_SELF here, use the absolute MSAA ID. Otherwise
  34. // GetIAccessibleFor will recurse into this function and we will just
  35. // overflow the stack.
  36. VARIANT realId = {VT_I4};
  37. realId.ulVal = wrap->GetExistingID();
  38. thisPtr->mCOMProxy = wrap->GetIAccessibleFor(realId, &isDefunct);
  39. }
  40. RefPtr<IAccessible> addRefed = mCOMProxy;
  41. addRefed.forget(aOutAccessible);
  42. return !!mCOMProxy;
  43. }
  44. /**
  45. * Specializations of this template map an IAccessible type to its IID
  46. */
  47. template<typename Interface> struct InterfaceIID {};
  48. template<>
  49. struct InterfaceIID<IAccessibleValue>
  50. {
  51. static REFIID Value() { return IID_IAccessibleValue; }
  52. };
  53. template<>
  54. struct InterfaceIID<IAccessibleText>
  55. {
  56. static REFIID Value() { return IID_IAccessibleText; }
  57. };
  58. /**
  59. * Get the COM proxy for this proxy accessible and QueryInterface it with the
  60. * correct IID
  61. */
  62. template<typename Interface>
  63. static already_AddRefed<Interface>
  64. QueryInterface(const ProxyAccessible* aProxy)
  65. {
  66. RefPtr<IAccessible> acc;
  67. if (!aProxy->GetCOMInterface((void**)getter_AddRefs(acc))) {
  68. return nullptr;
  69. }
  70. RefPtr<Interface> acc2;
  71. if (FAILED(acc->QueryInterface(InterfaceIID<Interface>::Value(),
  72. (void**)getter_AddRefs(acc2)))) {
  73. return nullptr;
  74. }
  75. return acc2.forget();
  76. }
  77. void
  78. ProxyAccessible::Name(nsString& aName) const
  79. {
  80. aName.Truncate();
  81. RefPtr<IAccessible> acc;
  82. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  83. return;
  84. }
  85. BSTR result;
  86. HRESULT hr = acc->get_accName(kChildIdSelf, &result);
  87. _bstr_t resultWrap(result, false);
  88. if (FAILED(hr)) {
  89. return;
  90. }
  91. aName = (wchar_t*)resultWrap;
  92. }
  93. void
  94. ProxyAccessible::Value(nsString& aValue) const
  95. {
  96. aValue.Truncate();
  97. RefPtr<IAccessible> acc;
  98. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  99. return;
  100. }
  101. BSTR result;
  102. HRESULT hr = acc->get_accValue(kChildIdSelf, &result);
  103. _bstr_t resultWrap(result, false);
  104. if (FAILED(hr)) {
  105. return;
  106. }
  107. aValue = (wchar_t*)resultWrap;
  108. }
  109. void
  110. ProxyAccessible::Description(nsString& aDesc) const
  111. {
  112. aDesc.Truncate();
  113. RefPtr<IAccessible> acc;
  114. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  115. return;
  116. }
  117. BSTR result;
  118. HRESULT hr = acc->get_accDescription(kChildIdSelf, &result);
  119. _bstr_t resultWrap(result, false);
  120. if (FAILED(hr)) {
  121. return;
  122. }
  123. aDesc = (wchar_t*)resultWrap;
  124. }
  125. uint64_t
  126. ProxyAccessible::State() const
  127. {
  128. uint64_t state = 0;
  129. RefPtr<IAccessible> acc;
  130. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  131. return state;
  132. }
  133. VARIANT varState;
  134. HRESULT hr = acc->get_accState(kChildIdSelf, &varState);
  135. if (FAILED(hr)) {
  136. return state;
  137. }
  138. return uint64_t(varState.lVal);
  139. }
  140. nsIntRect
  141. ProxyAccessible::Bounds()
  142. {
  143. nsIntRect rect;
  144. RefPtr<IAccessible> acc;
  145. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  146. return rect;
  147. }
  148. long left;
  149. long top;
  150. long width;
  151. long height;
  152. HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
  153. if (FAILED(hr)) {
  154. return rect;
  155. }
  156. rect.x = left;
  157. rect.y = top;
  158. rect.width = width;
  159. rect.height = height;
  160. return rect;
  161. }
  162. void
  163. ProxyAccessible::Language(nsString& aLocale)
  164. {
  165. aLocale.Truncate();
  166. RefPtr<IAccessible> acc;
  167. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  168. return;
  169. }
  170. RefPtr<IAccessible2> acc2;
  171. if (FAILED(acc->QueryInterface(IID_IAccessible2, (void**)getter_AddRefs(acc2)))) {
  172. return;
  173. }
  174. IA2Locale locale;
  175. HRESULT hr = acc2->get_locale(&locale);
  176. _bstr_t langWrap(locale.language, false);
  177. _bstr_t countryWrap(locale.country, false);
  178. _bstr_t variantWrap(locale.variant, false);
  179. if (FAILED(hr)) {
  180. return;
  181. }
  182. // The remaining code should essentially be the inverse of the
  183. // ia2Accessible::get_locale conversion to IA2Locale.
  184. if (!!variantWrap) {
  185. aLocale = (wchar_t*)variantWrap;
  186. return;
  187. }
  188. if (!!langWrap) {
  189. aLocale = (wchar_t*)langWrap;
  190. if (!!countryWrap) {
  191. aLocale += L"-";
  192. aLocale += (wchar_t*)countryWrap;
  193. }
  194. }
  195. }
  196. static bool
  197. IsEscapedChar(const wchar_t c)
  198. {
  199. return c == L'\\' || c == L':' || c == ',' || c == '=' || c == ';';
  200. }
  201. static bool
  202. ConvertBSTRAttributesToArray(const nsAString& aStr,
  203. nsTArray<Attribute>* aAttrs)
  204. {
  205. if (!aAttrs) {
  206. return false;
  207. }
  208. enum
  209. {
  210. eName = 0,
  211. eValue = 1,
  212. eNumStates
  213. } state;
  214. nsAutoString tokens[eNumStates];
  215. auto itr = aStr.BeginReading(), end = aStr.EndReading();
  216. state = eName;
  217. while (itr != end) {
  218. switch (*itr) {
  219. case L'\\':
  220. // Skip the backslash so that we're looking at the escaped char
  221. ++itr;
  222. if (itr == end || !IsEscapedChar(*itr)) {
  223. // Invalid state
  224. return false;
  225. }
  226. break;
  227. case L':':
  228. if (state != eName) {
  229. // Bad, should be looking at name
  230. return false;
  231. }
  232. state = eValue;
  233. ++itr;
  234. continue;
  235. case L';':
  236. if (state != eValue) {
  237. // Bad, should be looking at value
  238. return false;
  239. }
  240. state = eName;
  241. aAttrs->AppendElement(Attribute(NS_ConvertUTF16toUTF8(tokens[eName]),
  242. tokens[eValue]));
  243. tokens[eName].Truncate();
  244. tokens[eValue].Truncate();
  245. ++itr;
  246. continue;
  247. default:
  248. break;
  249. }
  250. tokens[state] += *itr;
  251. }
  252. return true;
  253. }
  254. void
  255. ProxyAccessible::Attributes(nsTArray<Attribute>* aAttrs) const
  256. {
  257. aAttrs->Clear();
  258. RefPtr<IAccessible> acc;
  259. if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
  260. return;
  261. }
  262. RefPtr<IAccessible2> acc2;
  263. if (FAILED(acc->QueryInterface(IID_IAccessible2, (void**)getter_AddRefs(acc2)))) {
  264. return;
  265. }
  266. BSTR attrs;
  267. HRESULT hr = acc2->get_attributes(&attrs);
  268. _bstr_t attrsWrap(attrs, false);
  269. if (FAILED(hr)) {
  270. return;
  271. }
  272. ConvertBSTRAttributesToArray(nsDependentString((wchar_t*)attrs,
  273. attrsWrap.length()),
  274. aAttrs);
  275. }
  276. double
  277. ProxyAccessible::CurValue()
  278. {
  279. RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
  280. if (!acc) {
  281. return UnspecifiedNaN<double>();
  282. }
  283. VARIANT currentValue;
  284. HRESULT hr = acc->get_currentValue(&currentValue);
  285. if (FAILED(hr) || currentValue.vt != VT_R8) {
  286. return UnspecifiedNaN<double>();
  287. }
  288. return currentValue.dblVal;
  289. }
  290. bool
  291. ProxyAccessible::SetCurValue(double aValue)
  292. {
  293. RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
  294. if (!acc) {
  295. return false;
  296. }
  297. VARIANT currentValue;
  298. VariantInit(&currentValue);
  299. currentValue.vt = VT_R8;
  300. currentValue.dblVal = aValue;
  301. HRESULT hr = acc->setCurrentValue(currentValue);
  302. return SUCCEEDED(hr);
  303. }
  304. double
  305. ProxyAccessible::MinValue()
  306. {
  307. RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
  308. if (!acc) {
  309. return UnspecifiedNaN<double>();
  310. }
  311. VARIANT minimumValue;
  312. HRESULT hr = acc->get_minimumValue(&minimumValue);
  313. if (FAILED(hr) || minimumValue.vt != VT_R8) {
  314. return UnspecifiedNaN<double>();
  315. }
  316. return minimumValue.dblVal;
  317. }
  318. double
  319. ProxyAccessible::MaxValue()
  320. {
  321. RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
  322. if (!acc) {
  323. return UnspecifiedNaN<double>();
  324. }
  325. VARIANT maximumValue;
  326. HRESULT hr = acc->get_maximumValue(&maximumValue);
  327. if (FAILED(hr) || maximumValue.vt != VT_R8) {
  328. return UnspecifiedNaN<double>();
  329. }
  330. return maximumValue.dblVal;
  331. }
  332. static IA2TextBoundaryType
  333. GetIA2TextBoundary(AccessibleTextBoundary aGeckoBoundaryType)
  334. {
  335. switch (aGeckoBoundaryType) {
  336. case nsIAccessibleText::BOUNDARY_CHAR:
  337. return IA2_TEXT_BOUNDARY_CHAR;
  338. case nsIAccessibleText::BOUNDARY_WORD_START:
  339. return IA2_TEXT_BOUNDARY_WORD;
  340. case nsIAccessibleText::BOUNDARY_LINE_START:
  341. return IA2_TEXT_BOUNDARY_LINE;
  342. default:
  343. MOZ_RELEASE_ASSERT(false);
  344. }
  345. }
  346. bool
  347. ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
  348. nsString& aText) const
  349. {
  350. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  351. if (!acc) {
  352. return false;
  353. }
  354. BSTR result;
  355. HRESULT hr = acc->get_text(static_cast<long>(aStartOffset),
  356. static_cast<long>(aEndOffset), &result);
  357. if (FAILED(hr)) {
  358. return false;
  359. }
  360. _bstr_t resultWrap(result, false);
  361. aText = (wchar_t*)result;
  362. return true;
  363. }
  364. void
  365. ProxyAccessible::GetTextBeforeOffset(int32_t aOffset,
  366. AccessibleTextBoundary aBoundaryType,
  367. nsString& aText, int32_t* aStartOffset,
  368. int32_t* aEndOffset)
  369. {
  370. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  371. if (!acc) {
  372. return;
  373. }
  374. BSTR result;
  375. long start, end;
  376. HRESULT hr = acc->get_textBeforeOffset(aOffset,
  377. GetIA2TextBoundary(aBoundaryType),
  378. &start, &end, &result);
  379. if (FAILED(hr)) {
  380. return;
  381. }
  382. _bstr_t resultWrap(result, false);
  383. *aStartOffset = start;
  384. *aEndOffset = end;
  385. aText = (wchar_t*)result;
  386. }
  387. void
  388. ProxyAccessible::GetTextAfterOffset(int32_t aOffset,
  389. AccessibleTextBoundary aBoundaryType,
  390. nsString& aText, int32_t* aStartOffset,
  391. int32_t* aEndOffset)
  392. {
  393. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  394. if (!acc) {
  395. return;
  396. }
  397. BSTR result;
  398. long start, end;
  399. HRESULT hr = acc->get_textAfterOffset(aOffset,
  400. GetIA2TextBoundary(aBoundaryType),
  401. &start, &end, &result);
  402. if (FAILED(hr)) {
  403. return;
  404. }
  405. _bstr_t resultWrap(result, false);
  406. aText = (wchar_t*)result;
  407. *aStartOffset = start;
  408. *aEndOffset = end;
  409. }
  410. void
  411. ProxyAccessible::GetTextAtOffset(int32_t aOffset,
  412. AccessibleTextBoundary aBoundaryType,
  413. nsString& aText, int32_t* aStartOffset,
  414. int32_t* aEndOffset)
  415. {
  416. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  417. if (!acc) {
  418. return;
  419. }
  420. BSTR result;
  421. long start, end;
  422. HRESULT hr = acc->get_textAtOffset(aOffset, GetIA2TextBoundary(aBoundaryType),
  423. &start, &end, &result);
  424. if (FAILED(hr)) {
  425. return;
  426. }
  427. _bstr_t resultWrap(result, false);
  428. aText = (wchar_t*)result;
  429. *aStartOffset = start;
  430. *aEndOffset = end;
  431. }
  432. bool
  433. ProxyAccessible::AddToSelection(int32_t aStartOffset, int32_t aEndOffset)
  434. {
  435. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  436. if (!acc) {
  437. return false;
  438. }
  439. return SUCCEEDED(acc->addSelection(static_cast<long>(aStartOffset),
  440. static_cast<long>(aEndOffset)));
  441. }
  442. bool
  443. ProxyAccessible::RemoveFromSelection(int32_t aSelectionNum)
  444. {
  445. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  446. if (!acc) {
  447. return false;
  448. }
  449. return SUCCEEDED(acc->removeSelection(static_cast<long>(aSelectionNum)));
  450. }
  451. int32_t
  452. ProxyAccessible::CaretOffset()
  453. {
  454. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  455. if (!acc) {
  456. return -1;
  457. }
  458. long offset;
  459. HRESULT hr = acc->get_caretOffset(&offset);
  460. if (FAILED(hr)) {
  461. return -1;
  462. }
  463. return static_cast<int32_t>(offset);
  464. }
  465. void
  466. ProxyAccessible::SetCaretOffset(int32_t aOffset)
  467. {
  468. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  469. if (!acc) {
  470. return;
  471. }
  472. acc->setCaretOffset(static_cast<long>(aOffset));
  473. }
  474. /**
  475. * aScrollType should be one of the nsIAccessiblescrollType constants.
  476. */
  477. void
  478. ProxyAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
  479. uint32_t aScrollType)
  480. {
  481. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  482. if (!acc) {
  483. return;
  484. }
  485. acc->scrollSubstringTo(static_cast<long>(aStartOffset),
  486. static_cast<long>(aEndOffset),
  487. static_cast<IA2ScrollType>(aScrollType));
  488. }
  489. /**
  490. * aCoordinateType is one of the nsIAccessibleCoordinateType constants.
  491. */
  492. void
  493. ProxyAccessible::ScrollSubstringToPoint(int32_t aStartOffset, int32_t aEndOffset,
  494. uint32_t aCoordinateType, int32_t aX,
  495. int32_t aY)
  496. {
  497. RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
  498. if (!acc) {
  499. return;
  500. }
  501. IA2CoordinateType coordType;
  502. if (aCoordinateType == nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
  503. coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
  504. } else if (aCoordinateType == nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
  505. coordType = IA2_COORDTYPE_PARENT_RELATIVE;
  506. } else {
  507. MOZ_RELEASE_ASSERT(false, "unsupported coord type");
  508. }
  509. acc->scrollSubstringToPoint(static_cast<long>(aStartOffset),
  510. static_cast<long>(aEndOffset),
  511. coordType,
  512. static_cast<long>(aX),
  513. static_cast<long>(aY));
  514. }
  515. } // namespace a11y
  516. } // namespace mozilla