DocAccessibleParent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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 "DocAccessibleParent.h"
  6. #include "mozilla/a11y/Platform.h"
  7. #include "mozilla/dom/TabParent.h"
  8. #include "xpcAccessibleDocument.h"
  9. #include "xpcAccEvents.h"
  10. #include "nsAccUtils.h"
  11. #include "nsCoreUtils.h"
  12. namespace mozilla {
  13. namespace a11y {
  14. bool
  15. DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
  16. const bool& aFromUser)
  17. {
  18. if (mShutdown)
  19. return true;
  20. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  21. if (aData.NewTree().IsEmpty()) {
  22. NS_ERROR("no children being added");
  23. return false;
  24. }
  25. ProxyAccessible* parent = GetAccessible(aData.ID());
  26. // XXX This should really never happen, but sometimes we fail to fire the
  27. // required show events.
  28. if (!parent) {
  29. NS_ERROR("adding child to unknown accessible");
  30. return true;
  31. }
  32. uint32_t newChildIdx = aData.Idx();
  33. if (newChildIdx > parent->ChildrenCount()) {
  34. NS_ERROR("invalid index to add child at");
  35. return true;
  36. }
  37. uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
  38. MOZ_ASSERT(consumed == aData.NewTree().Length());
  39. // XXX This shouldn't happen, but if we failed to add children then the below
  40. // is pointless and can crash.
  41. if (!consumed) {
  42. return true;
  43. }
  44. #ifdef DEBUG
  45. for (uint32_t i = 0; i < consumed; i++) {
  46. uint64_t id = aData.NewTree()[i].ID();
  47. MOZ_ASSERT(mAccessibles.GetEntry(id));
  48. }
  49. #endif
  50. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  51. ProxyAccessible* target = parent->ChildAt(newChildIdx);
  52. ProxyShowHideEvent(target, parent, true, aFromUser);
  53. if (!nsCoreUtils::AccEventObserversExist()) {
  54. return true;
  55. }
  56. uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
  57. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
  58. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  59. nsIDOMNode* node = nullptr;
  60. RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node,
  61. aFromUser);
  62. nsCoreUtils::DispatchAccEvent(Move(event));
  63. return true;
  64. }
  65. uint32_t
  66. DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
  67. const nsTArray<a11y::AccessibleData>& aNewTree,
  68. uint32_t aIdx, uint32_t aIdxInParent)
  69. {
  70. if (aNewTree.Length() <= aIdx) {
  71. NS_ERROR("bad index in serialized tree!");
  72. return 0;
  73. }
  74. const AccessibleData& newChild = aNewTree[aIdx];
  75. if (newChild.Role() > roles::LAST_ROLE) {
  76. NS_ERROR("invalid role");
  77. return 0;
  78. }
  79. if (mAccessibles.Contains(newChild.ID())) {
  80. NS_ERROR("ID already in use");
  81. return 0;
  82. }
  83. auto role = static_cast<a11y::role>(newChild.Role());
  84. ProxyAccessible* newProxy =
  85. new ProxyAccessible(newChild.ID(), aParent, this, role,
  86. newChild.Interfaces());
  87. aParent->AddChildAt(aIdxInParent, newProxy);
  88. mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
  89. ProxyCreated(newProxy, newChild.Interfaces());
  90. #if defined(XP_WIN)
  91. WrapperFor(newProxy)->SetID(newChild.MsaaID());
  92. #endif
  93. uint32_t accessibles = 1;
  94. uint32_t kids = newChild.ChildrenCount();
  95. for (uint32_t i = 0; i < kids; i++) {
  96. uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
  97. if (!consumed)
  98. return 0;
  99. accessibles += consumed;
  100. }
  101. MOZ_ASSERT(newProxy->ChildrenCount() == kids);
  102. return accessibles;
  103. }
  104. bool
  105. DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID,
  106. const bool& aFromUser)
  107. {
  108. if (mShutdown)
  109. return true;
  110. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  111. // We shouldn't actually need this because mAccessibles shouldn't have an
  112. // entry for the document itself, but it doesn't hurt to be explicit.
  113. if (!aRootID) {
  114. NS_ERROR("trying to hide entire document?");
  115. return false;
  116. }
  117. ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
  118. if (!rootEntry) {
  119. NS_ERROR("invalid root being removed!");
  120. return true;
  121. }
  122. ProxyAccessible* root = rootEntry->mProxy;
  123. if (!root) {
  124. NS_ERROR("invalid root being removed!");
  125. return true;
  126. }
  127. ProxyAccessible* parent = root->Parent();
  128. ProxyShowHideEvent(root, parent, false, aFromUser);
  129. RefPtr<xpcAccHideEvent> event = nullptr;
  130. if (nsCoreUtils::AccEventObserversExist()) {
  131. uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
  132. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
  133. xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
  134. ProxyAccessible* next = root->NextSibling();
  135. xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
  136. ProxyAccessible* prev = root->PrevSibling();
  137. xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
  138. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  139. nsIDOMNode* node = nullptr;
  140. event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
  141. xpcNext, xpcPrev);
  142. }
  143. parent->RemoveChild(root);
  144. root->Shutdown();
  145. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  146. if (event) {
  147. nsCoreUtils::DispatchAccEvent(Move(event));
  148. }
  149. return true;
  150. }
  151. bool
  152. DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
  153. {
  154. ProxyAccessible* proxy = GetAccessible(aID);
  155. if (!proxy) {
  156. NS_ERROR("no proxy for event!");
  157. return true;
  158. }
  159. ProxyEvent(proxy, aEventType);
  160. if (!nsCoreUtils::AccEventObserversExist()) {
  161. return true;
  162. }
  163. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
  164. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  165. nsIDOMNode* node = nullptr;
  166. bool fromUser = true; // XXX fix me
  167. RefPtr<xpcAccEvent> event = new xpcAccEvent(aEventType, xpcAcc, doc, node,
  168. fromUser);
  169. nsCoreUtils::DispatchAccEvent(Move(event));
  170. return true;
  171. }
  172. bool
  173. DocAccessibleParent::RecvStateChangeEvent(const uint64_t& aID,
  174. const uint64_t& aState,
  175. const bool& aEnabled)
  176. {
  177. ProxyAccessible* target = GetAccessible(aID);
  178. if (!target) {
  179. NS_ERROR("we don't know about the target of a state change event!");
  180. return true;
  181. }
  182. ProxyStateChangeEvent(target, aState, aEnabled);
  183. if (!nsCoreUtils::AccEventObserversExist()) {
  184. return true;
  185. }
  186. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
  187. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  188. uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
  189. bool extra;
  190. uint32_t state = nsAccUtils::To32States(aState, &extra);
  191. bool fromUser = true; // XXX fix this
  192. nsIDOMNode* node = nullptr; // XXX can we do better?
  193. RefPtr<xpcAccStateChangeEvent> event =
  194. new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra,
  195. aEnabled);
  196. nsCoreUtils::DispatchAccEvent(Move(event));
  197. return true;
  198. }
  199. bool
  200. DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
  201. {
  202. ProxyAccessible* proxy = GetAccessible(aID);
  203. if (!proxy) {
  204. NS_ERROR("unknown caret move event target!");
  205. return true;
  206. }
  207. ProxyCaretMoveEvent(proxy, aOffset);
  208. if (!nsCoreUtils::AccEventObserversExist()) {
  209. return true;
  210. }
  211. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
  212. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  213. nsIDOMNode* node = nullptr;
  214. bool fromUser = true; // XXX fix me
  215. uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
  216. RefPtr<xpcAccCaretMoveEvent> event =
  217. new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset);
  218. nsCoreUtils::DispatchAccEvent(Move(event));
  219. return true;
  220. }
  221. bool
  222. DocAccessibleParent::RecvTextChangeEvent(const uint64_t& aID,
  223. const nsString& aStr,
  224. const int32_t& aStart,
  225. const uint32_t& aLen,
  226. const bool& aIsInsert,
  227. const bool& aFromUser)
  228. {
  229. ProxyAccessible* target = GetAccessible(aID);
  230. if (!target) {
  231. NS_ERROR("text change event target is unknown!");
  232. return true;
  233. }
  234. ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
  235. if (!nsCoreUtils::AccEventObserversExist()) {
  236. return true;
  237. }
  238. xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
  239. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  240. uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED :
  241. nsIAccessibleEvent::EVENT_TEXT_REMOVED;
  242. nsIDOMNode* node = nullptr;
  243. RefPtr<xpcAccTextChangeEvent> event =
  244. new xpcAccTextChangeEvent(type, xpcAcc, doc, node, aFromUser, aStart, aLen,
  245. aIsInsert, aStr);
  246. nsCoreUtils::DispatchAccEvent(Move(event));
  247. return true;
  248. }
  249. bool
  250. DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID,
  251. const uint64_t& aWidgetID,
  252. const uint32_t& aType)
  253. {
  254. ProxyAccessible* target = GetAccessible(aID);
  255. ProxyAccessible* widget = GetAccessible(aWidgetID);
  256. if (!target || !widget) {
  257. NS_ERROR("invalid id in selection event");
  258. return true;
  259. }
  260. ProxySelectionEvent(target, widget, aType);
  261. if (!nsCoreUtils::AccEventObserversExist()) {
  262. return true;
  263. }
  264. xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
  265. xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
  266. RefPtr<xpcAccEvent> event = new xpcAccEvent(aType, xpcTarget, xpcDoc,
  267. nullptr, false);
  268. nsCoreUtils::DispatchAccEvent(Move(event));
  269. return true;
  270. }
  271. bool
  272. DocAccessibleParent::RecvRoleChangedEvent(const uint32_t& aRole)
  273. {
  274. if (aRole >= roles::LAST_ROLE) {
  275. NS_ERROR("child sent bad role in RoleChangedEvent");
  276. return false;
  277. }
  278. mRole = static_cast<a11y::role>(aRole);
  279. return true;
  280. }
  281. bool
  282. DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
  283. {
  284. // One document should never directly be the child of another.
  285. // We should always have at least an outer doc accessible in between.
  286. MOZ_ASSERT(aID);
  287. if (!aID)
  288. return false;
  289. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  290. auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
  291. childDoc->Unbind();
  292. bool result = AddChildDoc(childDoc, aID, false);
  293. MOZ_ASSERT(result);
  294. MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
  295. return result;
  296. }
  297. bool
  298. DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
  299. uint64_t aParentID, bool aCreating)
  300. {
  301. // We do not use GetAccessible here because we want to be sure to not get the
  302. // document it self.
  303. ProxyEntry* e = mAccessibles.GetEntry(aParentID);
  304. if (!e)
  305. return false;
  306. ProxyAccessible* outerDoc = e->mProxy;
  307. MOZ_ASSERT(outerDoc);
  308. // OuterDocAccessibles are expected to only have a document as a child.
  309. // However for compatibility we tolerate replacing one document with another
  310. // here.
  311. if (outerDoc->ChildrenCount() > 1 ||
  312. (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
  313. return false;
  314. }
  315. aChildDoc->mParent = outerDoc;
  316. outerDoc->SetChildDoc(aChildDoc);
  317. mChildDocs.AppendElement(aChildDoc);
  318. aChildDoc->mParentDoc = this;
  319. if (aCreating) {
  320. ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
  321. }
  322. return true;
  323. }
  324. bool
  325. DocAccessibleParent::RecvShutdown()
  326. {
  327. Destroy();
  328. if (!static_cast<dom::TabParent*>(Manager())->IsDestroyed()) {
  329. return PDocAccessibleParent::Send__delete__(this);
  330. }
  331. return true;
  332. }
  333. void
  334. DocAccessibleParent::Destroy()
  335. {
  336. NS_ASSERTION(mChildDocs.IsEmpty(),
  337. "why weren't the child docs destroyed already?");
  338. MOZ_ASSERT(!mShutdown);
  339. mShutdown = true;
  340. uint32_t childDocCount = mChildDocs.Length();
  341. for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
  342. mChildDocs[i]->Destroy();
  343. for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
  344. MOZ_ASSERT(iter.Get()->mProxy != this);
  345. ProxyDestroyed(iter.Get()->mProxy);
  346. iter.Remove();
  347. }
  348. DocManager::NotifyOfRemoteDocShutdown(this);
  349. ProxyDestroyed(this);
  350. if (mParentDoc)
  351. mParentDoc->RemoveChildDoc(this);
  352. else if (IsTopLevel())
  353. GetAccService()->RemoteDocShutdown(this);
  354. }
  355. bool
  356. DocAccessibleParent::CheckDocTree() const
  357. {
  358. size_t childDocs = mChildDocs.Length();
  359. for (size_t i = 0; i < childDocs; i++) {
  360. if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
  361. return false;
  362. if (!mChildDocs[i]->CheckDocTree()) {
  363. return false;
  364. }
  365. }
  366. return true;
  367. }
  368. xpcAccessibleGeneric*
  369. DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
  370. {
  371. xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
  372. MOZ_ASSERT(doc);
  373. return doc->GetXPCAccessible(aProxy);
  374. }
  375. #if defined(XP_WIN)
  376. /**
  377. * @param aCOMProxy COM Proxy to the document in the content process.
  378. */
  379. void
  380. DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
  381. {
  382. SetCOMInterface(aCOMProxy);
  383. // Make sure that we're not racing with a tab shutdown
  384. auto tab = static_cast<dom::TabParent*>(Manager());
  385. MOZ_ASSERT(tab);
  386. if (tab->IsDestroyed()) {
  387. return;
  388. }
  389. Accessible* outerDoc = OuterDocOfRemoteBrowser();
  390. MOZ_ASSERT(outerDoc);
  391. IAccessible* rawNative = nullptr;
  392. if (outerDoc) {
  393. outerDoc->GetNativeInterface((void**) &rawNative);
  394. MOZ_ASSERT(rawNative);
  395. }
  396. IAccessibleHolder::COMPtrType ptr(rawNative);
  397. IAccessibleHolder holder(Move(ptr));
  398. Unused << SendParentCOMProxy(holder);
  399. }
  400. bool
  401. DocAccessibleParent::RecvGetWindowedPluginIAccessible(
  402. const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy)
  403. {
  404. return false;
  405. }
  406. #endif // defined(XP_WIN)
  407. } // a11y
  408. } // mozilla