DocAccessible.cpp 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387
  1. /* -*- Mode: C++; tab-width: 8; 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 "Accessible-inl.h"
  6. #include "AccIterator.h"
  7. #include "DocAccessible-inl.h"
  8. #include "DocAccessibleChild.h"
  9. #include "HTMLImageMapAccessible.h"
  10. #include "nsAccCache.h"
  11. #include "nsAccessiblePivot.h"
  12. #include "nsAccUtils.h"
  13. #include "nsEventShell.h"
  14. #include "nsTextEquivUtils.h"
  15. #include "Role.h"
  16. #include "RootAccessible.h"
  17. #include "TreeWalker.h"
  18. #include "xpcAccessibleDocument.h"
  19. #include "nsIMutableArray.h"
  20. #include "nsICommandManager.h"
  21. #include "nsIDocShell.h"
  22. #include "nsIDocument.h"
  23. #include "nsIDOMAttr.h"
  24. #include "nsIDOMCharacterData.h"
  25. #include "nsIDOMDocument.h"
  26. #include "nsIDOMXULDocument.h"
  27. #include "nsIDOMMutationEvent.h"
  28. #include "nsPIDOMWindow.h"
  29. #include "nsIDOMXULPopupElement.h"
  30. #include "nsIEditingSession.h"
  31. #include "nsIFrame.h"
  32. #include "nsIInterfaceRequestorUtils.h"
  33. #include "nsImageFrame.h"
  34. #include "nsIPersistentProperties2.h"
  35. #include "nsIPresShell.h"
  36. #include "nsIServiceManager.h"
  37. #include "nsViewManager.h"
  38. #include "nsIScrollableFrame.h"
  39. #include "nsUnicharUtils.h"
  40. #include "nsIURI.h"
  41. #include "nsIWebNavigation.h"
  42. #include "nsFocusManager.h"
  43. #include "mozilla/ArrayUtils.h"
  44. #include "mozilla/Assertions.h"
  45. #include "mozilla/EventStates.h"
  46. #include "mozilla/dom/DocumentType.h"
  47. #include "mozilla/dom/Element.h"
  48. #ifdef MOZ_XUL
  49. #include "nsIXULDocument.h"
  50. #endif
  51. using namespace mozilla;
  52. using namespace mozilla::a11y;
  53. ////////////////////////////////////////////////////////////////////////////////
  54. // Static member initialization
  55. static nsIAtom** kRelationAttrs[] =
  56. {
  57. &nsGkAtoms::aria_labelledby,
  58. &nsGkAtoms::aria_describedby,
  59. &nsGkAtoms::aria_details,
  60. &nsGkAtoms::aria_owns,
  61. &nsGkAtoms::aria_controls,
  62. &nsGkAtoms::aria_flowto,
  63. &nsGkAtoms::aria_errormessage,
  64. &nsGkAtoms::_for,
  65. &nsGkAtoms::control
  66. };
  67. static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs);
  68. ////////////////////////////////////////////////////////////////////////////////
  69. // Constructor/desctructor
  70. DocAccessible::
  71. DocAccessible(nsIDocument* aDocument, nsIPresShell* aPresShell) :
  72. // XXX don't pass a document to the Accessible constructor so that we don't
  73. // set mDoc until our vtable is fully setup. If we set mDoc before setting
  74. // up the vtable we will call Accessible::AddRef() but not the overrides of
  75. // it for subclasses. It is important to call those overrides to avoid
  76. // confusing leak checking machinary.
  77. HyperTextAccessibleWrap(nullptr, nullptr),
  78. // XXX aaronl should we use an algorithm for the initial cache size?
  79. mAccessibleCache(kDefaultCacheLength),
  80. mNodeToAccessibleMap(kDefaultCacheLength),
  81. mDocumentNode(aDocument),
  82. mScrollPositionChangedTicks(0),
  83. mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
  84. mVirtualCursor(nullptr),
  85. mPresShell(aPresShell), mIPCDoc(nullptr)
  86. {
  87. mGenericTypes |= eDocument;
  88. mStateFlags |= eNotNodeMapEntry;
  89. mDoc = this;
  90. MOZ_ASSERT(mPresShell, "should have been given a pres shell");
  91. mPresShell->SetDocAccessible(this);
  92. // If this is a XUL Document, it should not implement nsHyperText
  93. if (mDocumentNode && mDocumentNode->IsXULDocument())
  94. mGenericTypes &= ~eHyperText;
  95. }
  96. DocAccessible::~DocAccessible()
  97. {
  98. NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
  99. }
  100. ////////////////////////////////////////////////////////////////////////////////
  101. // nsISupports
  102. NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
  103. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
  104. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
  105. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
  106. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
  107. for (auto iter = tmp->mDependentIDsHash.Iter(); !iter.Done(); iter.Next()) {
  108. AttrRelProviderArray* providers = iter.UserData();
  109. for (int32_t jdx = providers->Length() - 1; jdx >= 0; jdx--) {
  110. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
  111. cb, "content of dependent ids hash entry of document accessible");
  112. AttrRelProvider* provider = (*providers)[jdx];
  113. cb.NoteXPCOMChild(provider->mContent);
  114. NS_ASSERTION(provider->mContent->IsInUncomposedDoc(),
  115. "Referred content is not in document!");
  116. }
  117. }
  118. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
  119. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
  120. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInvalidationList)
  121. for (auto it = tmp->mARIAOwnsHash.ConstIter(); !it.Done(); it.Next()) {
  122. nsTArray<RefPtr<Accessible> >* ar = it.UserData();
  123. for (uint32_t i = 0; i < ar->Length(); i++) {
  124. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
  125. "mARIAOwnsHash entry item");
  126. cb.NoteXPCOMChild(ar->ElementAt(i));
  127. }
  128. }
  129. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  130. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
  131. NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
  132. NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
  133. NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
  134. tmp->mDependentIDsHash.Clear();
  135. tmp->mNodeToAccessibleMap.Clear();
  136. NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
  137. NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
  138. NS_IMPL_CYCLE_COLLECTION_UNLINK(mInvalidationList)
  139. tmp->mARIAOwnsHash.Clear();
  140. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  141. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
  142. NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
  143. NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
  144. NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  145. NS_INTERFACE_MAP_ENTRY(nsIObserver)
  146. NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
  147. NS_INTERFACE_MAP_END_INHERITING(HyperTextAccessible)
  148. NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
  149. NS_IMPL_RELEASE_INHERITED(DocAccessible, HyperTextAccessible)
  150. ////////////////////////////////////////////////////////////////////////////////
  151. // nsIAccessible
  152. ENameValueFlag
  153. DocAccessible::Name(nsString& aName)
  154. {
  155. aName.Truncate();
  156. if (mParent) {
  157. mParent->Name(aName); // Allow owning iframe to override the name
  158. }
  159. if (aName.IsEmpty()) {
  160. // Allow name via aria-labelledby or title attribute
  161. Accessible::Name(aName);
  162. }
  163. if (aName.IsEmpty()) {
  164. Title(aName); // Try title element
  165. }
  166. if (aName.IsEmpty()) { // Last resort: use URL
  167. URL(aName);
  168. }
  169. return eNameOK;
  170. }
  171. // Accessible public method
  172. role
  173. DocAccessible::NativeRole()
  174. {
  175. nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
  176. if (docShell) {
  177. nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
  178. docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
  179. int32_t itemType = docShell->ItemType();
  180. if (sameTypeRoot == docShell) {
  181. // Root of content or chrome tree
  182. if (itemType == nsIDocShellTreeItem::typeChrome)
  183. return roles::CHROME_WINDOW;
  184. if (itemType == nsIDocShellTreeItem::typeContent) {
  185. #ifdef MOZ_XUL
  186. nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
  187. if (xulDoc)
  188. return roles::APPLICATION;
  189. #endif
  190. return roles::DOCUMENT;
  191. }
  192. }
  193. else if (itemType == nsIDocShellTreeItem::typeContent) {
  194. return roles::DOCUMENT;
  195. }
  196. }
  197. return roles::PANE; // Fall back;
  198. }
  199. void
  200. DocAccessible::Description(nsString& aDescription)
  201. {
  202. if (mParent)
  203. mParent->Description(aDescription);
  204. if (HasOwnContent() && aDescription.IsEmpty()) {
  205. nsTextEquivUtils::
  206. GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
  207. aDescription);
  208. }
  209. }
  210. // Accessible public method
  211. uint64_t
  212. DocAccessible::NativeState()
  213. {
  214. // Document is always focusable.
  215. uint64_t state = states::FOCUSABLE; // keep in sync with NativeInteractiveState() impl
  216. if (FocusMgr()->IsFocused(this))
  217. state |= states::FOCUSED;
  218. // Expose stale state until the document is ready (DOM is loaded and tree is
  219. // constructed).
  220. if (!HasLoadState(eReady))
  221. state |= states::STALE;
  222. // Expose state busy until the document and all its subdocuments is completely
  223. // loaded.
  224. if (!HasLoadState(eCompletelyLoaded))
  225. state |= states::BUSY;
  226. nsIFrame* frame = GetFrame();
  227. if (!frame ||
  228. !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
  229. state |= states::INVISIBLE | states::OFFSCREEN;
  230. }
  231. nsCOMPtr<nsIEditor> editor = GetEditor();
  232. state |= editor ? states::EDITABLE : states::READONLY;
  233. return state;
  234. }
  235. uint64_t
  236. DocAccessible::NativeInteractiveState() const
  237. {
  238. // Document is always focusable.
  239. return states::FOCUSABLE;
  240. }
  241. bool
  242. DocAccessible::NativelyUnavailable() const
  243. {
  244. return false;
  245. }
  246. // Accessible public method
  247. void
  248. DocAccessible::ApplyARIAState(uint64_t* aState) const
  249. {
  250. // Grab states from content element.
  251. if (mContent)
  252. Accessible::ApplyARIAState(aState);
  253. // Allow iframe/frame etc. to have final state override via ARIA.
  254. if (mParent)
  255. mParent->ApplyARIAState(aState);
  256. }
  257. already_AddRefed<nsIPersistentProperties>
  258. DocAccessible::Attributes()
  259. {
  260. nsCOMPtr<nsIPersistentProperties> attributes =
  261. HyperTextAccessibleWrap::Attributes();
  262. // No attributes if document is not attached to the tree or if it's a root
  263. // document.
  264. if (!mParent || IsRoot())
  265. return attributes.forget();
  266. // Override ARIA object attributes from outerdoc.
  267. aria::AttrIterator attribIter(mParent->GetContent());
  268. nsAutoString name, value, unused;
  269. while(attribIter.Next(name, value))
  270. attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
  271. return attributes.forget();
  272. }
  273. Accessible*
  274. DocAccessible::FocusedChild()
  275. {
  276. // Return an accessible for the current global focus, which does not have to
  277. // be contained within the current document.
  278. return FocusMgr()->FocusedAccessible();
  279. }
  280. void
  281. DocAccessible::TakeFocus()
  282. {
  283. // Focus the document.
  284. nsFocusManager* fm = nsFocusManager::GetFocusManager();
  285. nsCOMPtr<nsIDOMElement> newFocus;
  286. fm->MoveFocus(mDocumentNode->GetWindow(), nullptr,
  287. nsFocusManager::MOVEFOCUS_ROOT, 0, getter_AddRefs(newFocus));
  288. }
  289. // HyperTextAccessible method
  290. already_AddRefed<nsIEditor>
  291. DocAccessible::GetEditor() const
  292. {
  293. // Check if document is editable (designMode="on" case). Otherwise check if
  294. // the html:body (for HTML document case) or document element is editable.
  295. if (!mDocumentNode->HasFlag(NODE_IS_EDITABLE) &&
  296. (!mContent || !mContent->HasFlag(NODE_IS_EDITABLE)))
  297. return nullptr;
  298. nsCOMPtr<nsIDocShell> docShell = mDocumentNode->GetDocShell();
  299. if (!docShell) {
  300. return nullptr;
  301. }
  302. nsCOMPtr<nsIEditingSession> editingSession;
  303. docShell->GetEditingSession(getter_AddRefs(editingSession));
  304. if (!editingSession)
  305. return nullptr; // No editing session interface
  306. nsCOMPtr<nsIEditor> editor;
  307. editingSession->GetEditorForWindow(mDocumentNode->GetWindow(), getter_AddRefs(editor));
  308. if (!editor)
  309. return nullptr;
  310. bool isEditable = false;
  311. editor->GetIsDocumentEditable(&isEditable);
  312. if (isEditable)
  313. return editor.forget();
  314. return nullptr;
  315. }
  316. // DocAccessible public method
  317. void
  318. DocAccessible::URL(nsAString& aURL) const
  319. {
  320. nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
  321. nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
  322. nsAutoCString theURL;
  323. if (webNav) {
  324. nsCOMPtr<nsIURI> pURI;
  325. webNav->GetCurrentURI(getter_AddRefs(pURI));
  326. if (pURI)
  327. pURI->GetSpec(theURL);
  328. }
  329. CopyUTF8toUTF16(theURL, aURL);
  330. }
  331. void
  332. DocAccessible::DocType(nsAString& aType) const
  333. {
  334. #ifdef MOZ_XUL
  335. nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
  336. if (xulDoc) {
  337. aType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
  338. return;
  339. }
  340. #endif
  341. dom::DocumentType* docType = mDocumentNode->GetDoctype();
  342. if (docType)
  343. docType->GetPublicId(aType);
  344. }
  345. ////////////////////////////////////////////////////////////////////////////////
  346. // Accessible
  347. void
  348. DocAccessible::Init()
  349. {
  350. #ifdef A11Y_LOG
  351. if (logging::IsEnabled(logging::eDocCreate))
  352. logging::DocCreate("document initialize", mDocumentNode, this);
  353. #endif
  354. // Initialize notification controller.
  355. mNotificationController = new NotificationController(this, mPresShell);
  356. // Mark the document accessible as loaded if its DOM document was loaded at
  357. // this point (this can happen because a11y is started late or DOM document
  358. // having no container was loaded.
  359. if (mDocumentNode->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
  360. mLoadState |= eDOMLoaded;
  361. AddEventListeners();
  362. }
  363. void
  364. DocAccessible::Shutdown()
  365. {
  366. if (!mPresShell) // already shutdown
  367. return;
  368. #ifdef A11Y_LOG
  369. if (logging::IsEnabled(logging::eDocDestroy))
  370. logging::DocDestroy("document shutdown", mDocumentNode, this);
  371. #endif
  372. // Mark the document as shutdown before AT is notified about the document
  373. // removal from its container (valid for root documents on ATK and due to
  374. // some reason for MSAA, refer to bug 757392 for details).
  375. mStateFlags |= eIsDefunct;
  376. if (mNotificationController) {
  377. mNotificationController->Shutdown();
  378. mNotificationController = nullptr;
  379. }
  380. RemoveEventListeners();
  381. nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocumentNode;
  382. mDocumentNode = nullptr;
  383. if (mParent) {
  384. DocAccessible* parentDocument = mParent->Document();
  385. if (parentDocument)
  386. parentDocument->RemoveChildDocument(this);
  387. mParent->RemoveChild(this);
  388. }
  389. // Walk the array backwards because child documents remove themselves from the
  390. // array as they are shutdown.
  391. int32_t childDocCount = mChildDocuments.Length();
  392. for (int32_t idx = childDocCount - 1; idx >= 0; idx--)
  393. mChildDocuments[idx]->Shutdown();
  394. mChildDocuments.Clear();
  395. // XXX thinking about ordering?
  396. if (mIPCDoc) {
  397. MOZ_ASSERT(IPCAccessibilityActive());
  398. mIPCDoc->Shutdown();
  399. MOZ_ASSERT(!mIPCDoc);
  400. }
  401. if (mVirtualCursor) {
  402. mVirtualCursor->RemoveObserver(this);
  403. mVirtualCursor = nullptr;
  404. }
  405. mPresShell->SetDocAccessible(nullptr);
  406. mPresShell = nullptr; // Avoid reentrancy
  407. mDependentIDsHash.Clear();
  408. mNodeToAccessibleMap.Clear();
  409. for (auto iter = mAccessibleCache.Iter(); !iter.Done(); iter.Next()) {
  410. Accessible* accessible = iter.Data();
  411. MOZ_ASSERT(accessible);
  412. if (accessible && !accessible->IsDefunct()) {
  413. // Unlink parent to avoid its cleaning overhead in shutdown.
  414. accessible->mParent = nullptr;
  415. accessible->Shutdown();
  416. }
  417. iter.Remove();
  418. }
  419. HyperTextAccessibleWrap::Shutdown();
  420. GetAccService()->NotifyOfDocumentShutdown(this, kungFuDeathGripDoc);
  421. }
  422. nsIFrame*
  423. DocAccessible::GetFrame() const
  424. {
  425. nsIFrame* root = nullptr;
  426. if (mPresShell)
  427. root = mPresShell->GetRootFrame();
  428. return root;
  429. }
  430. // DocAccessible protected member
  431. nsRect
  432. DocAccessible::RelativeBounds(nsIFrame** aRelativeFrame) const
  433. {
  434. *aRelativeFrame = GetFrame();
  435. nsIDocument *document = mDocumentNode;
  436. nsIDocument *parentDoc = nullptr;
  437. nsRect bounds;
  438. while (document) {
  439. nsIPresShell *presShell = document->GetShell();
  440. if (!presShell)
  441. return nsRect();
  442. nsRect scrollPort;
  443. nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
  444. if (sf) {
  445. scrollPort = sf->GetScrollPortRect();
  446. } else {
  447. nsIFrame* rootFrame = presShell->GetRootFrame();
  448. if (!rootFrame)
  449. return nsRect();
  450. scrollPort = rootFrame->GetRect();
  451. }
  452. if (parentDoc) { // After first time thru loop
  453. // XXXroc bogus code! scrollPort is relative to the viewport of
  454. // this document, but we're intersecting rectangles derived from
  455. // multiple documents and assuming they're all in the same coordinate
  456. // system. See bug 514117.
  457. bounds.IntersectRect(scrollPort, bounds);
  458. }
  459. else { // First time through loop
  460. bounds = scrollPort;
  461. }
  462. document = parentDoc = document->GetParentDocument();
  463. }
  464. return bounds;
  465. }
  466. // DocAccessible protected member
  467. nsresult
  468. DocAccessible::AddEventListeners()
  469. {
  470. nsCOMPtr<nsIDocShell> docShell(mDocumentNode->GetDocShell());
  471. // We want to add a command observer only if the document is content and has
  472. // an editor.
  473. if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
  474. nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
  475. if (commandManager)
  476. commandManager->AddCommandObserver(this, "obs_documentCreated");
  477. }
  478. SelectionMgr()->AddDocSelectionListener(mPresShell);
  479. // Add document observer.
  480. mDocumentNode->AddObserver(this);
  481. return NS_OK;
  482. }
  483. // DocAccessible protected member
  484. nsresult
  485. DocAccessible::RemoveEventListeners()
  486. {
  487. // Remove listeners associated with content documents
  488. // Remove scroll position listener
  489. RemoveScrollListener();
  490. NS_ASSERTION(mDocumentNode, "No document during removal of listeners.");
  491. if (mDocumentNode) {
  492. mDocumentNode->RemoveObserver(this);
  493. nsCOMPtr<nsIDocShell> docShell(mDocumentNode->GetDocShell());
  494. NS_ASSERTION(docShell, "doc should support nsIDocShellTreeItem.");
  495. if (docShell) {
  496. if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
  497. nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
  498. if (commandManager) {
  499. commandManager->RemoveCommandObserver(this, "obs_documentCreated");
  500. }
  501. }
  502. }
  503. }
  504. if (mScrollWatchTimer) {
  505. mScrollWatchTimer->Cancel();
  506. mScrollWatchTimer = nullptr;
  507. NS_RELEASE_THIS(); // Kung fu death grip
  508. }
  509. SelectionMgr()->RemoveDocSelectionListener(mPresShell);
  510. return NS_OK;
  511. }
  512. void
  513. DocAccessible::ScrollTimerCallback(nsITimer* aTimer, void* aClosure)
  514. {
  515. DocAccessible* docAcc = reinterpret_cast<DocAccessible*>(aClosure);
  516. if (docAcc && docAcc->mScrollPositionChangedTicks &&
  517. ++docAcc->mScrollPositionChangedTicks > 2) {
  518. // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
  519. // We only want to fire accessibilty scroll event when scrolling stops or pauses
  520. // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
  521. // That indicates a pause in scrolling, so we fire the accessibilty scroll event
  522. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
  523. docAcc->mScrollPositionChangedTicks = 0;
  524. if (docAcc->mScrollWatchTimer) {
  525. docAcc->mScrollWatchTimer->Cancel();
  526. docAcc->mScrollWatchTimer = nullptr;
  527. NS_RELEASE(docAcc); // Release kung fu death grip
  528. }
  529. }
  530. }
  531. ////////////////////////////////////////////////////////////////////////////////
  532. // nsIScrollPositionListener
  533. void
  534. DocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
  535. {
  536. // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
  537. // then the ::Notify() method will fire the accessibility event for scroll position changes
  538. const uint32_t kScrollPosCheckWait = 50;
  539. if (mScrollWatchTimer) {
  540. mScrollWatchTimer->SetDelay(kScrollPosCheckWait); // Create new timer, to avoid leaks
  541. }
  542. else {
  543. mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
  544. if (mScrollWatchTimer) {
  545. NS_ADDREF_THIS(); // Kung fu death grip
  546. mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
  547. kScrollPosCheckWait,
  548. nsITimer::TYPE_REPEATING_SLACK);
  549. }
  550. }
  551. mScrollPositionChangedTicks = 1;
  552. }
  553. ////////////////////////////////////////////////////////////////////////////////
  554. // nsIObserver
  555. NS_IMETHODIMP
  556. DocAccessible::Observe(nsISupports* aSubject, const char* aTopic,
  557. const char16_t* aData)
  558. {
  559. if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
  560. // State editable will now be set, readonly is now clear
  561. // Normally we only fire delayed events created from the node, not an
  562. // accessible object. See the AccStateChangeEvent constructor for details
  563. // about this exceptional case.
  564. RefPtr<AccEvent> event =
  565. new AccStateChangeEvent(this, states::EDITABLE, true);
  566. FireDelayedEvent(event);
  567. }
  568. return NS_OK;
  569. }
  570. ////////////////////////////////////////////////////////////////////////////////
  571. // nsIAccessiblePivotObserver
  572. NS_IMETHODIMP
  573. DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
  574. nsIAccessible* aOldAccessible,
  575. int32_t aOldStart, int32_t aOldEnd,
  576. PivotMoveReason aReason,
  577. bool aIsFromUserInput)
  578. {
  579. RefPtr<AccEvent> event =
  580. new AccVCChangeEvent(
  581. this, (aOldAccessible ? aOldAccessible->ToInternalAccessible() : nullptr),
  582. aOldStart, aOldEnd, aReason,
  583. aIsFromUserInput ? eFromUserInput : eNoUserInput);
  584. nsEventShell::FireEvent(event);
  585. return NS_OK;
  586. }
  587. ////////////////////////////////////////////////////////////////////////////////
  588. // nsIDocumentObserver
  589. NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
  590. NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
  591. NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(DocAccessible)
  592. void
  593. DocAccessible::AttributeWillChange(nsIDocument* aDocument,
  594. dom::Element* aElement,
  595. int32_t aNameSpaceID,
  596. nsIAtom* aAttribute, int32_t aModType,
  597. const nsAttrValue* aNewValue)
  598. {
  599. Accessible* accessible = GetAccessible(aElement);
  600. if (!accessible) {
  601. if (aElement != mContent)
  602. return;
  603. accessible = this;
  604. }
  605. // Update dependent IDs cache. Take care of elements that are accessible
  606. // because dependent IDs cache doesn't contain IDs from non accessible
  607. // elements.
  608. if (aModType != nsIDOMMutationEvent::ADDITION)
  609. RemoveDependentIDsFor(accessible, aAttribute);
  610. if (aAttribute == nsGkAtoms::id) {
  611. RelocateARIAOwnedIfNeeded(aElement);
  612. }
  613. // Store the ARIA attribute old value so that it can be used after
  614. // attribute change. Note, we assume there's no nested ARIA attribute
  615. // changes. If this happens then we should end up with keeping a stack of
  616. // old values.
  617. // XXX TODO: bugs 472142, 472143.
  618. // Here we will want to cache whatever attribute values we are interested
  619. // in, such as the existence of aria-pressed for button (so we know if we
  620. // need to newly expose it as a toggle button) etc.
  621. if (aAttribute == nsGkAtoms::aria_checked ||
  622. aAttribute == nsGkAtoms::aria_pressed) {
  623. mARIAAttrOldValue = (aModType != nsIDOMMutationEvent::ADDITION) ?
  624. nsAccUtils::GetARIAToken(aElement, aAttribute) : nullptr;
  625. return;
  626. }
  627. if (aAttribute == nsGkAtoms::aria_disabled ||
  628. aAttribute == nsGkAtoms::disabled)
  629. mStateBitWasOn = accessible->Unavailable();
  630. }
  631. void
  632. DocAccessible::NativeAnonymousChildListChange(nsIDocument* aDocument,
  633. nsIContent* aContent,
  634. bool aIsRemove)
  635. {
  636. }
  637. void
  638. DocAccessible::AttributeChanged(nsIDocument* aDocument,
  639. dom::Element* aElement,
  640. int32_t aNameSpaceID, nsIAtom* aAttribute,
  641. int32_t aModType,
  642. const nsAttrValue* aOldValue)
  643. {
  644. NS_ASSERTION(!IsDefunct(),
  645. "Attribute changed called on defunct document accessible!");
  646. // Proceed even if the element is not accessible because element may become
  647. // accessible if it gets certain attribute.
  648. if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
  649. return;
  650. // Ignore attribute change if the element doesn't have an accessible (at all
  651. // or still) iff the element is not a root content of this document accessible
  652. // (which is treated as attribute change on this document accessible).
  653. // Note: we don't bail if all the content hasn't finished loading because
  654. // these attributes are changing for a loaded part of the content.
  655. Accessible* accessible = GetAccessible(aElement);
  656. if (!accessible) {
  657. if (mContent != aElement)
  658. return;
  659. accessible = this;
  660. }
  661. MOZ_ASSERT(accessible->IsBoundToParent() || accessible->IsDoc(),
  662. "DOM attribute change on an accessible detached from the tree");
  663. // Fire accessible events iff there's an accessible, otherwise we consider
  664. // the accessible state wasn't changed, i.e. its state is initial state.
  665. AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
  666. // Update dependent IDs cache. Take care of accessible elements because no
  667. // accessible element means either the element is not accessible at all or
  668. // its accessible will be created later. It doesn't make sense to keep
  669. // dependent IDs for non accessible elements. For the second case we'll update
  670. // dependent IDs cache when its accessible is created.
  671. if (aModType == nsIDOMMutationEvent::MODIFICATION ||
  672. aModType == nsIDOMMutationEvent::ADDITION) {
  673. AddDependentIDsFor(accessible, aAttribute);
  674. }
  675. }
  676. // DocAccessible protected member
  677. void
  678. DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
  679. int32_t aNameSpaceID, nsIAtom* aAttribute)
  680. {
  681. // Fire accessible event after short timer, because we need to wait for
  682. // DOM attribute & resulting layout to actually change. Otherwise,
  683. // assistive technology will retrieve the wrong state/value/selection info.
  684. // XXX todo
  685. // We still need to handle special HTML cases here
  686. // For example, if an <img>'s usemap attribute is modified
  687. // Otherwise it may just be a state change, for example an object changing
  688. // its visibility
  689. //
  690. // XXX todo: report aria state changes for "undefined" literal value changes
  691. // filed as bug 472142
  692. //
  693. // XXX todo: invalidate accessible when aria state changes affect exposed role
  694. // filed as bug 472143
  695. // Universal boolean properties that don't require a role. Fire the state
  696. // change when disabled or aria-disabled attribute is set.
  697. // Note. Checking the XUL or HTML namespace would not seem to gain us
  698. // anything, because disabled attribute really is going to mean the same
  699. // thing in any namespace.
  700. // Note. We use the attribute instead of the disabled state bit because
  701. // ARIA's aria-disabled does not affect the disabled state bit.
  702. if (aAttribute == nsGkAtoms::disabled ||
  703. aAttribute == nsGkAtoms::aria_disabled) {
  704. // Do nothing if state wasn't changed (like @aria-disabled was removed but
  705. // @disabled is still presented).
  706. if (aAccessible->Unavailable() == mStateBitWasOn)
  707. return;
  708. RefPtr<AccEvent> enabledChangeEvent =
  709. new AccStateChangeEvent(aAccessible, states::ENABLED, mStateBitWasOn);
  710. FireDelayedEvent(enabledChangeEvent);
  711. RefPtr<AccEvent> sensitiveChangeEvent =
  712. new AccStateChangeEvent(aAccessible, states::SENSITIVE, mStateBitWasOn);
  713. FireDelayedEvent(sensitiveChangeEvent);
  714. return;
  715. }
  716. // Check for namespaced ARIA attribute
  717. if (aNameSpaceID == kNameSpaceID_None) {
  718. // Check for hyphenated aria-foo property?
  719. if (StringBeginsWith(nsDependentAtomString(aAttribute),
  720. NS_LITERAL_STRING("aria-"))) {
  721. ARIAAttributeChanged(aAccessible, aAttribute);
  722. }
  723. }
  724. // Fire name change and description change events. XXX: it's not complete and
  725. // dupes the code logic of accessible name and description calculation, we do
  726. // that for performance reasons.
  727. if (aAttribute == nsGkAtoms::aria_label) {
  728. FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  729. return;
  730. }
  731. if (aAttribute == nsGkAtoms::aria_describedby) {
  732. FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
  733. return;
  734. }
  735. nsIContent* elm = aAccessible->GetContent();
  736. if (aAttribute == nsGkAtoms::aria_labelledby &&
  737. !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
  738. FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  739. return;
  740. }
  741. if (aAttribute == nsGkAtoms::alt &&
  742. !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
  743. !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby)) {
  744. FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  745. return;
  746. }
  747. if (aAttribute == nsGkAtoms::title) {
  748. if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
  749. !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby) &&
  750. !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
  751. FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  752. return;
  753. }
  754. if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_describedby))
  755. FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
  756. return;
  757. }
  758. if (aAttribute == nsGkAtoms::aria_busy) {
  759. bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
  760. eCaseMatters);
  761. RefPtr<AccEvent> event =
  762. new AccStateChangeEvent(aAccessible, states::BUSY, isOn);
  763. FireDelayedEvent(event);
  764. return;
  765. }
  766. if (aAttribute == nsGkAtoms::id) {
  767. RelocateARIAOwnedIfNeeded(elm);
  768. }
  769. // ARIA or XUL selection
  770. if ((aAccessible->GetContent()->IsXULElement() &&
  771. aAttribute == nsGkAtoms::selected) ||
  772. aAttribute == nsGkAtoms::aria_selected) {
  773. Accessible* widget =
  774. nsAccUtils::GetSelectableContainer(aAccessible, aAccessible->State());
  775. if (widget) {
  776. AccSelChangeEvent::SelChangeType selChangeType =
  777. elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters) ?
  778. AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
  779. RefPtr<AccEvent> event =
  780. new AccSelChangeEvent(widget, aAccessible, selChangeType);
  781. FireDelayedEvent(event);
  782. }
  783. return;
  784. }
  785. if (aAttribute == nsGkAtoms::contenteditable) {
  786. RefPtr<AccEvent> editableChangeEvent =
  787. new AccStateChangeEvent(aAccessible, states::EDITABLE);
  788. FireDelayedEvent(editableChangeEvent);
  789. return;
  790. }
  791. if (aAttribute == nsGkAtoms::value) {
  792. if (aAccessible->IsProgress())
  793. FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
  794. }
  795. }
  796. // DocAccessible protected member
  797. void
  798. DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute)
  799. {
  800. // Note: For universal/global ARIA states and properties we don't care if
  801. // there is an ARIA role present or not.
  802. if (aAttribute == nsGkAtoms::aria_required) {
  803. RefPtr<AccEvent> event =
  804. new AccStateChangeEvent(aAccessible, states::REQUIRED);
  805. FireDelayedEvent(event);
  806. return;
  807. }
  808. if (aAttribute == nsGkAtoms::aria_invalid) {
  809. RefPtr<AccEvent> event =
  810. new AccStateChangeEvent(aAccessible, states::INVALID);
  811. FireDelayedEvent(event);
  812. return;
  813. }
  814. // The activedescendant universal property redirects accessible focus events
  815. // to the element with the id that activedescendant points to. Make sure
  816. // the tree up to date before processing.
  817. if (aAttribute == nsGkAtoms::aria_activedescendant) {
  818. mNotificationController->HandleNotification<DocAccessible, Accessible>
  819. (this, &DocAccessible::ARIAActiveDescendantChanged, aAccessible);
  820. return;
  821. }
  822. // We treat aria-expanded as a global ARIA state for historical reasons
  823. if (aAttribute == nsGkAtoms::aria_expanded) {
  824. RefPtr<AccEvent> event =
  825. new AccStateChangeEvent(aAccessible, states::EXPANDED);
  826. FireDelayedEvent(event);
  827. return;
  828. }
  829. // For aria attributes like drag and drop changes we fire a generic attribute
  830. // change event; at least until native API comes up with a more meaningful event.
  831. uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
  832. if (!(attrFlags & ATTR_BYPASSOBJ)) {
  833. RefPtr<AccEvent> event =
  834. new AccObjectAttrChangedEvent(aAccessible, aAttribute);
  835. FireDelayedEvent(event);
  836. }
  837. nsIContent* elm = aAccessible->GetContent();
  838. // Update aria-hidden flag for the whole subtree iff aria-hidden is changed
  839. // on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
  840. // of top aria-hidden.
  841. if (aAttribute == nsGkAtoms::aria_hidden) {
  842. bool isDefined = aria::HasDefinedARIAHidden(elm);
  843. if (isDefined != aAccessible->IsARIAHidden() &&
  844. (!aAccessible->Parent() || !aAccessible->Parent()->IsARIAHidden())) {
  845. aAccessible->SetARIAHidden(isDefined);
  846. RefPtr<AccEvent> event =
  847. new AccObjectAttrChangedEvent(aAccessible, aAttribute);
  848. FireDelayedEvent(event);
  849. }
  850. return;
  851. }
  852. if (aAttribute == nsGkAtoms::aria_checked ||
  853. (aAccessible->IsButton() &&
  854. aAttribute == nsGkAtoms::aria_pressed)) {
  855. const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
  856. states::CHECKED : states::PRESSED;
  857. RefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
  858. FireDelayedEvent(event);
  859. bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
  860. bool isMixed = elm->AttrValueIs(kNameSpaceID_None, aAttribute,
  861. nsGkAtoms::mixed, eCaseMatters);
  862. if (isMixed != wasMixed) {
  863. RefPtr<AccEvent> event =
  864. new AccStateChangeEvent(aAccessible, states::MIXED, isMixed);
  865. FireDelayedEvent(event);
  866. }
  867. return;
  868. }
  869. if (aAttribute == nsGkAtoms::aria_readonly) {
  870. RefPtr<AccEvent> event =
  871. new AccStateChangeEvent(aAccessible, states::READONLY);
  872. FireDelayedEvent(event);
  873. return;
  874. }
  875. // Fire text value change event whenever aria-valuetext is changed.
  876. if (aAttribute == nsGkAtoms::aria_valuetext) {
  877. FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE, aAccessible);
  878. return;
  879. }
  880. // Fire numeric value change event when aria-valuenow is changed and
  881. // aria-valuetext is empty
  882. if (aAttribute == nsGkAtoms::aria_valuenow &&
  883. (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
  884. elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
  885. nsGkAtoms::_empty, eCaseMatters))) {
  886. FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
  887. return;
  888. }
  889. if (aAttribute == nsGkAtoms::aria_owns) {
  890. mNotificationController->ScheduleRelocation(aAccessible);
  891. }
  892. }
  893. void
  894. DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
  895. {
  896. nsIContent* elm = aAccessible->GetContent();
  897. if (elm && aAccessible->IsActiveWidget()) {
  898. nsAutoString id;
  899. if (elm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
  900. dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
  901. if (activeDescendantElm) {
  902. Accessible* activeDescendant = GetAccessible(activeDescendantElm);
  903. if (activeDescendant) {
  904. FocusMgr()->ActiveItemChanged(activeDescendant, false);
  905. #ifdef A11Y_LOG
  906. if (logging::IsEnabled(logging::eFocus))
  907. logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
  908. activeDescendant);
  909. #endif
  910. }
  911. }
  912. }
  913. }
  914. }
  915. void
  916. DocAccessible::ContentAppended(nsIDocument* aDocument,
  917. nsIContent* aContainer,
  918. nsIContent* aFirstNewContent,
  919. int32_t /* unused */)
  920. {
  921. }
  922. void
  923. DocAccessible::ContentStateChanged(nsIDocument* aDocument,
  924. nsIContent* aContent,
  925. EventStates aStateMask)
  926. {
  927. Accessible* accessible = GetAccessible(aContent);
  928. if (!accessible)
  929. return;
  930. if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
  931. Accessible* widget = accessible->ContainerWidget();
  932. if (widget && widget->IsSelect()) {
  933. AccSelChangeEvent::SelChangeType selChangeType =
  934. aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
  935. AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
  936. RefPtr<AccEvent> event =
  937. new AccSelChangeEvent(widget, accessible, selChangeType);
  938. FireDelayedEvent(event);
  939. return;
  940. }
  941. RefPtr<AccEvent> event =
  942. new AccStateChangeEvent(accessible, states::CHECKED,
  943. aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED));
  944. FireDelayedEvent(event);
  945. }
  946. if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
  947. RefPtr<AccEvent> event =
  948. new AccStateChangeEvent(accessible, states::INVALID, true);
  949. FireDelayedEvent(event);
  950. }
  951. if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
  952. RefPtr<AccEvent> event =
  953. new AccStateChangeEvent(accessible, states::TRAVERSED, true);
  954. FireDelayedEvent(event);
  955. }
  956. }
  957. void
  958. DocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
  959. EventStates aStateMask)
  960. {
  961. }
  962. void
  963. DocAccessible::CharacterDataWillChange(nsIDocument* aDocument,
  964. nsIContent* aContent,
  965. CharacterDataChangeInfo* aInfo)
  966. {
  967. }
  968. void
  969. DocAccessible::CharacterDataChanged(nsIDocument* aDocument,
  970. nsIContent* aContent,
  971. CharacterDataChangeInfo* aInfo)
  972. {
  973. }
  974. void
  975. DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
  976. nsIContent* aChild, int32_t /* unused */)
  977. {
  978. }
  979. void
  980. DocAccessible::ContentRemoved(nsIDocument* aDocument,
  981. nsIContent* aContainerNode,
  982. nsIContent* aChildNode, int32_t /* unused */,
  983. nsIContent* aPreviousSiblingNode)
  984. {
  985. #ifdef A11Y_LOG
  986. if (logging::IsEnabled(logging::eTree)) {
  987. logging::MsgBegin("TREE", "DOM content removed; doc: %p", this);
  988. logging::Node("container node", aContainerNode);
  989. logging::Node("content node", aChildNode);
  990. logging::MsgEnd();
  991. }
  992. #endif
  993. // This one and content removal notification from layout may result in
  994. // double processing of same subtrees. If it pops up in profiling, then
  995. // consider reusing a document node cache to reject these notifications early.
  996. Accessible* container = GetAccessibleOrContainer(aContainerNode);
  997. if (container) {
  998. UpdateTreeOnRemoval(container, aChildNode);
  999. }
  1000. }
  1001. void
  1002. DocAccessible::ParentChainChanged(nsIContent* aContent)
  1003. {
  1004. }
  1005. ////////////////////////////////////////////////////////////////////////////////
  1006. // Accessible
  1007. #ifdef A11Y_LOG
  1008. nsresult
  1009. DocAccessible::HandleAccEvent(AccEvent* aEvent)
  1010. {
  1011. if (logging::IsEnabled(logging::eDocLoad))
  1012. logging::DocLoadEventHandled(aEvent);
  1013. return HyperTextAccessible::HandleAccEvent(aEvent);
  1014. }
  1015. #endif
  1016. ////////////////////////////////////////////////////////////////////////////////
  1017. // Public members
  1018. void*
  1019. DocAccessible::GetNativeWindow() const
  1020. {
  1021. if (!mPresShell)
  1022. return nullptr;
  1023. nsViewManager* vm = mPresShell->GetViewManager();
  1024. if (!vm)
  1025. return nullptr;
  1026. nsCOMPtr<nsIWidget> widget;
  1027. vm->GetRootWidget(getter_AddRefs(widget));
  1028. if (widget)
  1029. return widget->GetNativeData(NS_NATIVE_WINDOW);
  1030. return nullptr;
  1031. }
  1032. Accessible*
  1033. DocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
  1034. {
  1035. Accessible* child = GetAccessibleByUniqueID(aUniqueID);
  1036. if (child)
  1037. return child;
  1038. uint32_t childDocCount = mChildDocuments.Length();
  1039. for (uint32_t childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
  1040. DocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
  1041. child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
  1042. if (child)
  1043. return child;
  1044. }
  1045. return nullptr;
  1046. }
  1047. Accessible*
  1048. DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const
  1049. {
  1050. if (!aNode || !aNode->GetComposedDoc())
  1051. return nullptr;
  1052. nsINode* currNode = aNode;
  1053. Accessible* accessible = nullptr;
  1054. while (!(accessible = GetAccessible(currNode))) {
  1055. nsINode* parent = nullptr;
  1056. // If this is a content node, try to get a flattened parent content node.
  1057. // This will smartly skip from the shadow root to the host element,
  1058. // over parentless document fragment
  1059. if (currNode->IsContent())
  1060. parent = currNode->AsContent()->GetFlattenedTreeParent();
  1061. // Fallback to just get parent node, in case there is no parent content
  1062. // node. Or current node is not a content node.
  1063. if (!parent)
  1064. parent = currNode->GetParentNode();
  1065. if (!(currNode = parent)) break;
  1066. }
  1067. return accessible;
  1068. }
  1069. Accessible*
  1070. DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const
  1071. {
  1072. Accessible* acc = GetAccessible(aNode);
  1073. if (acc)
  1074. return acc;
  1075. acc = GetContainerAccessible(aNode);
  1076. if (acc) {
  1077. uint32_t childCnt = acc->ChildCount();
  1078. for (uint32_t idx = 0; idx < childCnt; idx++) {
  1079. Accessible* child = acc->GetChildAt(idx);
  1080. for (nsIContent* elm = child->GetContent();
  1081. elm && elm != acc->GetContent();
  1082. elm = elm->GetFlattenedTreeParent()) {
  1083. if (elm == aNode)
  1084. return child;
  1085. }
  1086. }
  1087. }
  1088. return nullptr;
  1089. }
  1090. void
  1091. DocAccessible::BindToDocument(Accessible* aAccessible,
  1092. const nsRoleMapEntry* aRoleMapEntry)
  1093. {
  1094. // Put into DOM node cache.
  1095. if (aAccessible->IsNodeMapEntry())
  1096. mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible);
  1097. // Put into unique ID cache.
  1098. mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
  1099. aAccessible->SetRoleMapEntry(aRoleMapEntry);
  1100. AddDependentIDsFor(aAccessible);
  1101. if (aAccessible->HasOwnContent()) {
  1102. nsIContent* el = aAccessible->GetContent();
  1103. if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
  1104. mNotificationController->ScheduleRelocation(aAccessible);
  1105. }
  1106. }
  1107. }
  1108. void
  1109. DocAccessible::UnbindFromDocument(Accessible* aAccessible)
  1110. {
  1111. NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
  1112. "Unbinding the unbound accessible!");
  1113. // Fire focus event on accessible having DOM focus if active item was removed
  1114. // from the tree.
  1115. if (FocusMgr()->IsActiveItem(aAccessible)) {
  1116. FocusMgr()->ActiveItemChanged(nullptr);
  1117. #ifdef A11Y_LOG
  1118. if (logging::IsEnabled(logging::eFocus))
  1119. logging::ActiveItemChangeCausedBy("tree shutdown", aAccessible);
  1120. #endif
  1121. }
  1122. // Remove an accessible from node-to-accessible map if it exists there.
  1123. if (aAccessible->IsNodeMapEntry() &&
  1124. mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
  1125. mNodeToAccessibleMap.Remove(aAccessible->GetNode());
  1126. aAccessible->mStateFlags |= eIsNotInDocument;
  1127. // Update XPCOM part.
  1128. xpcAccessibleDocument* xpcDoc = GetAccService()->GetCachedXPCDocument(this);
  1129. if (xpcDoc)
  1130. xpcDoc->NotifyOfShutdown(aAccessible);
  1131. void* uniqueID = aAccessible->UniqueID();
  1132. NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
  1133. aAccessible->Shutdown();
  1134. mAccessibleCache.Remove(uniqueID);
  1135. }
  1136. void
  1137. DocAccessible::ContentInserted(nsIContent* aContainerNode,
  1138. nsIContent* aStartChildNode,
  1139. nsIContent* aEndChildNode)
  1140. {
  1141. // Ignore content insertions until we constructed accessible tree. Otherwise
  1142. // schedule tree update on content insertion after layout.
  1143. if (mNotificationController && HasLoadState(eTreeConstructed)) {
  1144. // Update the whole tree of this document accessible when the container is
  1145. // null (document element is inserted or removed).
  1146. Accessible* container = aContainerNode ?
  1147. AccessibleOrTrueContainer(aContainerNode) : this;
  1148. if (container) {
  1149. // Ignore notification if the container node is no longer in the DOM tree.
  1150. mNotificationController->ScheduleContentInsertion(container,
  1151. aStartChildNode,
  1152. aEndChildNode);
  1153. }
  1154. }
  1155. }
  1156. void
  1157. DocAccessible::RecreateAccessible(nsIContent* aContent)
  1158. {
  1159. #ifdef A11Y_LOG
  1160. if (logging::IsEnabled(logging::eTree)) {
  1161. logging::MsgBegin("TREE", "accessible recreated");
  1162. logging::Node("content", aContent);
  1163. logging::MsgEnd();
  1164. }
  1165. #endif
  1166. // XXX: we shouldn't recreate whole accessible subtree, instead we should
  1167. // subclass hide and show events to handle them separately and implement their
  1168. // coalescence with normal hide and show events. Note, in this case they
  1169. // should be coalesced with normal show/hide events.
  1170. nsIContent* parent = aContent->GetFlattenedTreeParent();
  1171. ContentRemoved(parent, aContent);
  1172. ContentInserted(parent, aContent, aContent->GetNextSibling());
  1173. }
  1174. void
  1175. DocAccessible::ProcessInvalidationList()
  1176. {
  1177. // Invalidate children of container accessible for each element in
  1178. // invalidation list. Allow invalidation list insertions while container
  1179. // children are recached.
  1180. for (uint32_t idx = 0; idx < mInvalidationList.Length(); idx++) {
  1181. nsIContent* content = mInvalidationList[idx];
  1182. if (!HasAccessible(content) && content->HasID()) {
  1183. Accessible* container = GetContainerAccessible(content);
  1184. if (container) {
  1185. // Check if the node is a target of aria-owns, and if so, don't process
  1186. // it here and let DoARIAOwnsRelocation process it.
  1187. AttrRelProviderArray* list =
  1188. mDependentIDsHash.Get(nsDependentAtomString(content->GetID()));
  1189. bool shouldProcess = !!list;
  1190. if (shouldProcess) {
  1191. for (uint32_t idx = 0; idx < list->Length(); idx++) {
  1192. if (list->ElementAt(idx)->mRelAttr == nsGkAtoms::aria_owns) {
  1193. shouldProcess = false;
  1194. break;
  1195. }
  1196. }
  1197. if (shouldProcess) {
  1198. ProcessContentInserted(container, content);
  1199. }
  1200. }
  1201. }
  1202. }
  1203. }
  1204. mInvalidationList.Clear();
  1205. }
  1206. Accessible*
  1207. DocAccessible::GetAccessibleEvenIfNotInMap(nsINode* aNode) const
  1208. {
  1209. if (!aNode->IsContent() || !aNode->AsContent()->IsHTMLElement(nsGkAtoms::area))
  1210. return GetAccessible(aNode);
  1211. // XXX Bug 135040, incorrect when multiple images use the same map.
  1212. nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
  1213. nsImageFrame* imageFrame = do_QueryFrame(frame);
  1214. if (imageFrame) {
  1215. Accessible* parent = GetAccessible(imageFrame->GetContent());
  1216. if (parent) {
  1217. Accessible* area =
  1218. parent->AsImageMap()->GetChildAccessibleFor(aNode);
  1219. if (area)
  1220. return area;
  1221. return nullptr;
  1222. }
  1223. }
  1224. return GetAccessible(aNode);
  1225. }
  1226. ////////////////////////////////////////////////////////////////////////////////
  1227. // Protected members
  1228. void
  1229. DocAccessible::NotifyOfLoading(bool aIsReloading)
  1230. {
  1231. // Mark the document accessible as loading, if it stays alive then we'll mark
  1232. // it as loaded when we receive proper notification.
  1233. mLoadState &= ~eDOMLoaded;
  1234. if (!IsLoadEventTarget())
  1235. return;
  1236. if (aIsReloading) {
  1237. // Fire reload and state busy events on existing document accessible while
  1238. // event from user input flag can be calculated properly and accessible
  1239. // is alive. When new document gets loaded then this one is destroyed.
  1240. RefPtr<AccEvent> reloadEvent =
  1241. new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
  1242. nsEventShell::FireEvent(reloadEvent);
  1243. }
  1244. // Fire state busy change event. Use delayed event since we don't care
  1245. // actually if event isn't delivered when the document goes away like a shot.
  1246. RefPtr<AccEvent> stateEvent =
  1247. new AccStateChangeEvent(this, states::BUSY, true);
  1248. FireDelayedEvent(stateEvent);
  1249. }
  1250. void
  1251. DocAccessible::DoInitialUpdate()
  1252. {
  1253. if (nsCoreUtils::IsTabDocument(mDocumentNode))
  1254. mDocFlags |= eTabDocument;
  1255. mLoadState |= eTreeConstructed;
  1256. // Set up a root element and ARIA role mapping.
  1257. UpdateRootElIfNeeded();
  1258. // Build initial tree.
  1259. CacheChildrenInSubtree(this);
  1260. #ifdef A11Y_LOG
  1261. if (logging::IsEnabled(logging::eVerbose)) {
  1262. logging::Tree("TREE", "Initial subtree", this);
  1263. }
  1264. #endif
  1265. // Fire reorder event after the document tree is constructed. Note, since
  1266. // this reorder event is processed by parent document then events targeted to
  1267. // this document may be fired prior to this reorder event. If this is
  1268. // a problem then consider to keep event processing per tab document.
  1269. if (!IsRoot()) {
  1270. RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
  1271. ParentDocument()->FireDelayedEvent(reorderEvent);
  1272. }
  1273. TreeMutation mt(this);
  1274. uint32_t childCount = ChildCount();
  1275. for (uint32_t i = 0; i < childCount; i++) {
  1276. Accessible* child = GetChildAt(i);
  1277. mt.AfterInsertion(child);
  1278. }
  1279. mt.Done();
  1280. }
  1281. void
  1282. DocAccessible::ProcessLoad()
  1283. {
  1284. mLoadState |= eCompletelyLoaded;
  1285. #ifdef A11Y_LOG
  1286. if (logging::IsEnabled(logging::eDocLoad))
  1287. logging::DocCompleteLoad(this, IsLoadEventTarget());
  1288. #endif
  1289. // Do not fire document complete/stop events for root chrome document
  1290. // accessibles and for frame/iframe documents because
  1291. // a) screen readers start working on focus event in the case of root chrome
  1292. // documents
  1293. // b) document load event on sub documents causes screen readers to act is if
  1294. // entire page is reloaded.
  1295. if (!IsLoadEventTarget())
  1296. return;
  1297. // Fire complete/load stopped if the load event type is given.
  1298. if (mLoadEventType) {
  1299. RefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
  1300. FireDelayedEvent(loadEvent);
  1301. mLoadEventType = 0;
  1302. }
  1303. // Fire busy state change event.
  1304. RefPtr<AccEvent> stateEvent =
  1305. new AccStateChangeEvent(this, states::BUSY, false);
  1306. FireDelayedEvent(stateEvent);
  1307. }
  1308. void
  1309. DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
  1310. {
  1311. dom::Element* relProviderEl = aRelProvider->Elm();
  1312. if (!relProviderEl)
  1313. return;
  1314. for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
  1315. nsIAtom* relAttr = *kRelationAttrs[idx];
  1316. if (aRelAttr && aRelAttr != relAttr)
  1317. continue;
  1318. if (relAttr == nsGkAtoms::_for) {
  1319. if (!relProviderEl->IsAnyOfHTMLElements(nsGkAtoms::label,
  1320. nsGkAtoms::output))
  1321. continue;
  1322. } else if (relAttr == nsGkAtoms::control) {
  1323. if (!relProviderEl->IsAnyOfXULElements(nsGkAtoms::label,
  1324. nsGkAtoms::description))
  1325. continue;
  1326. }
  1327. IDRefsIterator iter(this, relProviderEl, relAttr);
  1328. while (true) {
  1329. const nsDependentSubstring id = iter.NextID();
  1330. if (id.IsEmpty())
  1331. break;
  1332. AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
  1333. if (!providers) {
  1334. providers = new AttrRelProviderArray();
  1335. if (providers) {
  1336. mDependentIDsHash.Put(id, providers);
  1337. }
  1338. }
  1339. if (providers) {
  1340. AttrRelProvider* provider =
  1341. new AttrRelProvider(relAttr, relProviderEl);
  1342. if (provider) {
  1343. providers->AppendElement(provider);
  1344. // We've got here during the children caching. If the referenced
  1345. // content is not accessible then store it to pend its container
  1346. // children invalidation (this happens immediately after the caching
  1347. // is finished).
  1348. nsIContent* dependentContent = iter.GetElem(id);
  1349. if (dependentContent) {
  1350. if (!HasAccessible(dependentContent)) {
  1351. mInvalidationList.AppendElement(dependentContent);
  1352. }
  1353. }
  1354. }
  1355. }
  1356. }
  1357. // If the relation attribute is given then we don't have anything else to
  1358. // check.
  1359. if (aRelAttr)
  1360. break;
  1361. }
  1362. // Make sure to schedule the tree update if needed.
  1363. mNotificationController->ScheduleProcessing();
  1364. }
  1365. void
  1366. DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
  1367. nsIAtom* aRelAttr)
  1368. {
  1369. dom::Element* relProviderElm = aRelProvider->Elm();
  1370. if (!relProviderElm)
  1371. return;
  1372. for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
  1373. nsIAtom* relAttr = *kRelationAttrs[idx];
  1374. if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
  1375. continue;
  1376. IDRefsIterator iter(this, relProviderElm, relAttr);
  1377. while (true) {
  1378. const nsDependentSubstring id = iter.NextID();
  1379. if (id.IsEmpty())
  1380. break;
  1381. AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
  1382. if (providers) {
  1383. for (uint32_t jdx = 0; jdx < providers->Length(); ) {
  1384. AttrRelProvider* provider = (*providers)[jdx];
  1385. if (provider->mRelAttr == relAttr &&
  1386. provider->mContent == relProviderElm)
  1387. providers->RemoveElement(provider);
  1388. else
  1389. jdx++;
  1390. }
  1391. if (providers->Length() == 0)
  1392. mDependentIDsHash.Remove(id);
  1393. }
  1394. }
  1395. // If the relation attribute is given then we don't have anything else to
  1396. // check.
  1397. if (aRelAttr)
  1398. break;
  1399. }
  1400. }
  1401. bool
  1402. DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
  1403. nsIAtom* aAttribute)
  1404. {
  1405. if (aAttribute == nsGkAtoms::role) {
  1406. // It is common for js libraries to set the role on the body element after
  1407. // the document has loaded. In this case we just update the role map entry.
  1408. if (mContent == aElement) {
  1409. SetRoleMapEntry(aria::GetRoleMap(aElement));
  1410. if (mIPCDoc) {
  1411. mIPCDoc->SendRoleChangedEvent(Role());
  1412. }
  1413. return true;
  1414. }
  1415. // Recreate the accessible when role is changed because we might require a
  1416. // different accessible class for the new role or the accessible may expose
  1417. // a different sets of interfaces (COM restriction).
  1418. RecreateAccessible(aElement);
  1419. return true;
  1420. }
  1421. if (aAttribute == nsGkAtoms::href) {
  1422. // Not worth the expense to ensure which namespace these are in. It doesn't
  1423. // kill use to recreate the accessible even if the attribute was used in
  1424. // the wrong namespace or an element that doesn't support it.
  1425. // Make sure the accessible is recreated asynchronously to allow the content
  1426. // to handle the attribute change.
  1427. RecreateAccessible(aElement);
  1428. return true;
  1429. }
  1430. if (aAttribute == nsGkAtoms::aria_multiselectable &&
  1431. aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
  1432. // This affects whether the accessible supports SelectAccessible.
  1433. // COM says we cannot change what interfaces are supported on-the-fly,
  1434. // so invalidate this object. A new one will be created on demand.
  1435. RecreateAccessible(aElement);
  1436. return true;
  1437. }
  1438. return false;
  1439. }
  1440. void
  1441. DocAccessible::UpdateRootElIfNeeded()
  1442. {
  1443. dom::Element* rootEl = mDocumentNode->GetBodyElement();
  1444. if (!rootEl) {
  1445. rootEl = mDocumentNode->GetRootElement();
  1446. }
  1447. if (rootEl != mContent) {
  1448. mContent = rootEl;
  1449. SetRoleMapEntry(aria::GetRoleMap(rootEl));
  1450. if (mIPCDoc) {
  1451. mIPCDoc->SendRoleChangedEvent(Role());
  1452. }
  1453. }
  1454. }
  1455. /**
  1456. * Content insertion helper.
  1457. */
  1458. class InsertIterator final
  1459. {
  1460. public:
  1461. InsertIterator(Accessible* aContext,
  1462. const nsTArray<nsCOMPtr<nsIContent> >* aNodes) :
  1463. mChild(nullptr), mChildBefore(nullptr), mWalker(aContext),
  1464. mNodes(aNodes), mNodesIdx(0)
  1465. {
  1466. MOZ_ASSERT(aContext, "No context");
  1467. MOZ_ASSERT(aNodes, "No nodes to search for accessible elements");
  1468. MOZ_COUNT_CTOR(InsertIterator);
  1469. }
  1470. ~InsertIterator() { MOZ_COUNT_DTOR(InsertIterator); }
  1471. Accessible* Context() const { return mWalker.Context(); }
  1472. Accessible* Child() const { return mChild; }
  1473. Accessible* ChildBefore() const { return mChildBefore; }
  1474. DocAccessible* Document() const { return mWalker.Document(); }
  1475. /**
  1476. * Iterates to a next accessible within the inserted content.
  1477. */
  1478. bool Next();
  1479. void Rejected()
  1480. {
  1481. mChild = nullptr;
  1482. mChildBefore = nullptr;
  1483. }
  1484. private:
  1485. Accessible* mChild;
  1486. Accessible* mChildBefore;
  1487. TreeWalker mWalker;
  1488. const nsTArray<nsCOMPtr<nsIContent> >* mNodes;
  1489. uint32_t mNodesIdx;
  1490. };
  1491. bool
  1492. InsertIterator::Next()
  1493. {
  1494. if (mNodesIdx > 0) {
  1495. Accessible* nextChild = mWalker.Next();
  1496. if (nextChild) {
  1497. mChildBefore = mChild;
  1498. mChild = nextChild;
  1499. return true;
  1500. }
  1501. }
  1502. while (mNodesIdx < mNodes->Length()) {
  1503. // Ignore nodes that are not contained by the container anymore.
  1504. // The container might be changed, for example, because of the subsequent
  1505. // overlapping content insertion (i.e. other content was inserted between
  1506. // this inserted content and its container or the content was reinserted
  1507. // into different container of unrelated part of tree). To avoid a double
  1508. // processing of the content insertion ignore this insertion notification.
  1509. // Note, the inserted content might be not in tree at all at this point
  1510. // what means there's no container. Ignore the insertion too.
  1511. nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1);
  1512. nsIContent* node = mNodes->ElementAt(mNodesIdx++);
  1513. Accessible* container = Document()->AccessibleOrTrueContainer(node);
  1514. if (container != Context()) {
  1515. continue;
  1516. }
  1517. // HTML comboboxes have no-content list accessible as an intermediate
  1518. // containing all options.
  1519. if (container->IsHTMLCombobox()) {
  1520. container = container->FirstChild();
  1521. }
  1522. if (!container->IsAcceptableChild(node)) {
  1523. continue;
  1524. }
  1525. #ifdef A11Y_LOG
  1526. logging::TreeInfo("traversing an inserted node", logging::eVerbose,
  1527. "container", container, "node", node);
  1528. #endif
  1529. // If inserted nodes are siblings then just move the walker next.
  1530. if (mChild && prevNode && prevNode->GetNextSibling() == node) {
  1531. Accessible* nextChild = mWalker.Scope(node);
  1532. if (nextChild) {
  1533. mChildBefore = mChild;
  1534. mChild = nextChild;
  1535. return true;
  1536. }
  1537. }
  1538. else {
  1539. TreeWalker finder(container);
  1540. if (finder.Seek(node)) {
  1541. mChild = mWalker.Scope(node);
  1542. if (mChild) {
  1543. mChildBefore = finder.Prev();
  1544. return true;
  1545. }
  1546. }
  1547. }
  1548. }
  1549. return false;
  1550. }
  1551. void
  1552. DocAccessible::ProcessContentInserted(Accessible* aContainer,
  1553. const nsTArray<nsCOMPtr<nsIContent> >* aNodes)
  1554. {
  1555. // Process insertions if the container accessible is still in tree.
  1556. if (!aContainer->IsInDocument()) {
  1557. return;
  1558. }
  1559. // If new root content has been inserted then update it.
  1560. if (aContainer == this) {
  1561. UpdateRootElIfNeeded();
  1562. }
  1563. InsertIterator iter(aContainer, aNodes);
  1564. if (!iter.Next()) {
  1565. return;
  1566. }
  1567. #ifdef A11Y_LOG
  1568. logging::TreeInfo("children before insertion", logging::eVerbose,
  1569. aContainer);
  1570. #endif
  1571. TreeMutation mt(aContainer);
  1572. do {
  1573. Accessible* parent = iter.Child()->Parent();
  1574. if (parent) {
  1575. if (parent != aContainer) {
  1576. #ifdef A11Y_LOG
  1577. logging::TreeInfo("stealing accessible", 0,
  1578. "old parent", parent, "new parent",
  1579. aContainer, "child", iter.Child(), nullptr);
  1580. #endif
  1581. MOZ_ASSERT_UNREACHABLE("stealing accessible");
  1582. continue;
  1583. }
  1584. #ifdef A11Y_LOG
  1585. logging::TreeInfo("binding to same parent", logging::eVerbose,
  1586. "parent", aContainer, "child", iter.Child(), nullptr);
  1587. #endif
  1588. continue;
  1589. }
  1590. if (aContainer->InsertAfter(iter.Child(), iter.ChildBefore())) {
  1591. #ifdef A11Y_LOG
  1592. logging::TreeInfo("accessible was inserted", 0,
  1593. "container", aContainer, "child", iter.Child(), nullptr);
  1594. #endif
  1595. CreateSubtree(iter.Child());
  1596. mt.AfterInsertion(iter.Child());
  1597. continue;
  1598. }
  1599. MOZ_ASSERT_UNREACHABLE("accessible was rejected");
  1600. iter.Rejected();
  1601. } while (iter.Next());
  1602. mt.Done();
  1603. #ifdef A11Y_LOG
  1604. logging::TreeInfo("children after insertion", logging::eVerbose,
  1605. aContainer);
  1606. #endif
  1607. FireEventsOnInsertion(aContainer);
  1608. }
  1609. void
  1610. DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
  1611. {
  1612. if (!aContainer->IsInDocument()) {
  1613. return;
  1614. }
  1615. #ifdef A11Y_LOG
  1616. logging::TreeInfo("children before insertion", logging::eVerbose, aContainer);
  1617. #endif
  1618. #ifdef A11Y_LOG
  1619. logging::TreeInfo("traversing an inserted node", logging::eVerbose,
  1620. "container", aContainer, "node", aNode);
  1621. #endif
  1622. TreeWalker walker(aContainer);
  1623. if (aContainer->IsAcceptableChild(aNode) && walker.Seek(aNode)) {
  1624. Accessible* child = GetAccessible(aNode);
  1625. if (!child) {
  1626. child = GetAccService()->CreateAccessible(aNode, aContainer);
  1627. }
  1628. if (child) {
  1629. TreeMutation mt(aContainer);
  1630. if (!aContainer->InsertAfter(child, walker.Prev())) {
  1631. return;
  1632. }
  1633. CreateSubtree(child);
  1634. mt.AfterInsertion(child);
  1635. mt.Done();
  1636. FireEventsOnInsertion(aContainer);
  1637. }
  1638. }
  1639. #ifdef A11Y_LOG
  1640. logging::TreeInfo("children after insertion", logging::eVerbose, aContainer);
  1641. #endif
  1642. }
  1643. void
  1644. DocAccessible::FireEventsOnInsertion(Accessible* aContainer)
  1645. {
  1646. // Check to see if change occurred inside an alert, and fire an EVENT_ALERT
  1647. // if it did.
  1648. if (aContainer->IsAlert() || aContainer->IsInsideAlert()) {
  1649. Accessible* ancestor = aContainer;
  1650. do {
  1651. if (ancestor->IsAlert()) {
  1652. FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
  1653. break;
  1654. }
  1655. }
  1656. while ((ancestor = ancestor->Parent()));
  1657. }
  1658. }
  1659. void
  1660. DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode)
  1661. {
  1662. // If child node is not accessible then look for its accessible children.
  1663. Accessible* child = GetAccessible(aChildNode);
  1664. #ifdef A11Y_LOG
  1665. logging::TreeInfo("process content removal", 0,
  1666. "container", aContainer, "child", aChildNode);
  1667. #endif
  1668. TreeMutation mt(aContainer);
  1669. if (child) {
  1670. RefPtr<Accessible> kungFuDeathGripChild(child);
  1671. mt.BeforeRemoval(child);
  1672. if (child->IsDefunct()) {
  1673. return; // event coalescence may kill us
  1674. }
  1675. MOZ_ASSERT(aContainer == child->Parent(), "Wrong parent");
  1676. aContainer->RemoveChild(child);
  1677. UncacheChildrenInSubtree(child);
  1678. mt.Done();
  1679. return;
  1680. }
  1681. TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
  1682. while (Accessible* child = walker.Next()) {
  1683. RefPtr<Accessible> kungFuDeathGripChild(child);
  1684. mt.BeforeRemoval(child);
  1685. if (child->IsDefunct()) {
  1686. return; // event coalescence may kill us
  1687. }
  1688. MOZ_ASSERT(aContainer == child->Parent(), "Wrong parent");
  1689. aContainer->RemoveChild(child);
  1690. UncacheChildrenInSubtree(child);
  1691. }
  1692. mt.Done();
  1693. }
  1694. bool
  1695. DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
  1696. {
  1697. if (!aElement->HasID())
  1698. return false;
  1699. AttrRelProviderArray* list =
  1700. mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
  1701. if (list) {
  1702. for (uint32_t idx = 0; idx < list->Length(); idx++) {
  1703. if (list->ElementAt(idx)->mRelAttr == nsGkAtoms::aria_owns) {
  1704. Accessible* owner = GetAccessible(list->ElementAt(idx)->mContent);
  1705. if (owner) {
  1706. mNotificationController->ScheduleRelocation(owner);
  1707. return true;
  1708. }
  1709. }
  1710. }
  1711. }
  1712. return false;
  1713. }
  1714. void
  1715. DocAccessible::ValidateARIAOwned()
  1716. {
  1717. for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
  1718. Accessible* owner = it.Key();
  1719. nsTArray<RefPtr<Accessible> >* children = it.UserData();
  1720. // Owner is about to die, put children back if applicable.
  1721. if (!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
  1722. !owner->IsInDocument()) {
  1723. PutChildrenBack(children, 0);
  1724. it.Remove();
  1725. continue;
  1726. }
  1727. for (uint32_t idx = 0; idx < children->Length(); idx++) {
  1728. Accessible* child = children->ElementAt(idx);
  1729. if (!child->IsInDocument()) {
  1730. children->RemoveElementAt(idx);
  1731. idx--;
  1732. continue;
  1733. }
  1734. NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
  1735. // If DOM node doesn't have a frame anymore then shutdown its accessible.
  1736. if (child->Parent() && !child->GetFrame()) {
  1737. UpdateTreeOnRemoval(child->Parent(), child->GetContent());
  1738. children->RemoveElementAt(idx);
  1739. idx--;
  1740. continue;
  1741. }
  1742. NS_ASSERTION(child->Parent() == owner,
  1743. "Illigally stolen ARIA owned child!");
  1744. }
  1745. if (children->Length() == 0) {
  1746. it.Remove();
  1747. }
  1748. }
  1749. }
  1750. void
  1751. DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
  1752. {
  1753. MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
  1754. MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
  1755. #ifdef A11Y_LOG
  1756. logging::TreeInfo("aria owns relocation", logging::eVerbose, aOwner);
  1757. #endif
  1758. nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.LookupOrAdd(aOwner);
  1759. IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
  1760. uint32_t idx = 0;
  1761. while (nsIContent* childEl = iter.NextElem()) {
  1762. Accessible* child = GetAccessible(childEl);
  1763. auto insertIdx = aOwner->ChildCount() - owned->Length() + idx;
  1764. // Make an attempt to create an accessible if it wasn't created yet.
  1765. if (!child) {
  1766. if (aOwner->IsAcceptableChild(childEl)) {
  1767. child = GetAccService()->CreateAccessible(childEl, aOwner);
  1768. if (child) {
  1769. TreeMutation imut(aOwner);
  1770. aOwner->InsertChildAt(insertIdx, child);
  1771. imut.AfterInsertion(child);
  1772. imut.Done();
  1773. child->SetRelocated(true);
  1774. owned->InsertElementAt(idx, child);
  1775. idx++;
  1776. // Create subtree before adjusting the insertion index, since subtree
  1777. // creation may alter children in the container.
  1778. CreateSubtree(child);
  1779. FireEventsOnInsertion(aOwner);
  1780. }
  1781. }
  1782. continue;
  1783. }
  1784. #ifdef A11Y_LOG
  1785. logging::TreeInfo("aria owns traversal", logging::eVerbose,
  1786. "candidate", child, nullptr);
  1787. #endif
  1788. // Same child on same position, no change.
  1789. if (child->Parent() == aOwner &&
  1790. child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
  1791. MOZ_ASSERT(owned->ElementAt(idx) == child, "Not in sync!");
  1792. idx++;
  1793. continue;
  1794. }
  1795. MOZ_ASSERT(owned->SafeElementAt(idx) != child, "Already in place!");
  1796. if (owned->IndexOf(child) < idx) {
  1797. continue; // ignore second entry of same ID
  1798. }
  1799. // A new child is found, check for loops.
  1800. if (child->Parent() != aOwner) {
  1801. Accessible* parent = aOwner;
  1802. while (parent && parent != child && !parent->IsDoc()) {
  1803. parent = parent->Parent();
  1804. }
  1805. // A referred child cannot be a parent of the owner.
  1806. if (parent == child) {
  1807. continue;
  1808. }
  1809. }
  1810. if (MoveChild(child, aOwner, insertIdx)) {
  1811. child->SetRelocated(true);
  1812. MOZ_ASSERT(owned == mARIAOwnsHash.Get(aOwner));
  1813. owned = mARIAOwnsHash.LookupOrAdd(aOwner);
  1814. owned->InsertElementAt(idx, child);
  1815. idx++;
  1816. }
  1817. }
  1818. // Put back children that are not seized anymore.
  1819. PutChildrenBack(owned, idx);
  1820. if (owned->Length() == 0) {
  1821. mARIAOwnsHash.Remove(aOwner);
  1822. }
  1823. }
  1824. void
  1825. DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
  1826. uint32_t aStartIdx)
  1827. {
  1828. MOZ_ASSERT(aStartIdx <= aChildren->Length(), "Wrong removal index");
  1829. nsTArray<RefPtr<Accessible> > containers;
  1830. for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
  1831. Accessible* child = aChildren->ElementAt(idx);
  1832. if (!child->IsInDocument()) {
  1833. continue;
  1834. }
  1835. // Remove the child from the owner
  1836. Accessible* owner = child->Parent();
  1837. if (!owner) {
  1838. NS_ERROR("Cannot put the child back. No parent, a broken tree.");
  1839. continue;
  1840. }
  1841. #ifdef A11Y_LOG
  1842. logging::TreeInfo("aria owns put child back", 0,
  1843. "old parent", owner, "child", child, nullptr);
  1844. #endif
  1845. // Unset relocated flag to find an insertion point for the child.
  1846. child->SetRelocated(false);
  1847. int32_t idxInParent = -1;
  1848. Accessible* origContainer = GetContainerAccessible(child->GetContent());
  1849. if (origContainer) {
  1850. TreeWalker walker(origContainer);
  1851. if (walker.Seek(child->GetContent())) {
  1852. Accessible* prevChild = walker.Prev();
  1853. if (prevChild) {
  1854. idxInParent = prevChild->IndexInParent() + 1;
  1855. MOZ_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
  1856. origContainer = prevChild->Parent();
  1857. }
  1858. else {
  1859. idxInParent = 0;
  1860. }
  1861. }
  1862. }
  1863. MoveChild(child, origContainer, idxInParent);
  1864. }
  1865. aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
  1866. }
  1867. bool
  1868. DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
  1869. int32_t aIdxInParent)
  1870. {
  1871. MOZ_ASSERT(aChild, "No child");
  1872. MOZ_ASSERT(aChild->Parent(), "No parent");
  1873. MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
  1874. "Wrong insertion point for a moving child");
  1875. Accessible* curParent = aChild->Parent();
  1876. #ifdef A11Y_LOG
  1877. logging::TreeInfo("move child", 0,
  1878. "old parent", curParent, "new parent", aNewParent,
  1879. "child", aChild, nullptr);
  1880. #endif
  1881. // Forget aria-owns info in case of ARIA owned element. The caller is expected
  1882. // to update it if needed.
  1883. if (aChild->IsRelocated()) {
  1884. aChild->SetRelocated(false);
  1885. nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
  1886. children->RemoveElement(aChild);
  1887. }
  1888. NotificationController::MoveGuard mguard(mNotificationController);
  1889. if (curParent == aNewParent) {
  1890. MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
  1891. curParent->MoveChild(aIdxInParent, aChild);
  1892. #ifdef A11Y_LOG
  1893. logging::TreeInfo("move child: parent tree after",
  1894. logging::eVerbose, curParent);
  1895. #endif
  1896. return true;
  1897. }
  1898. if (!aNewParent->IsAcceptableChild(aChild->GetContent())) {
  1899. return false;
  1900. }
  1901. TreeMutation rmut(curParent);
  1902. rmut.BeforeRemoval(aChild, TreeMutation::kNoShutdown);
  1903. curParent->RemoveChild(aChild);
  1904. rmut.Done();
  1905. // No insertion point for the child.
  1906. if (aIdxInParent == -1) {
  1907. return true;
  1908. }
  1909. if (aIdxInParent > static_cast<int32_t>(aNewParent->ChildCount())) {
  1910. MOZ_ASSERT_UNREACHABLE("Wrong insertion point for a moving child");
  1911. return true;
  1912. }
  1913. TreeMutation imut(aNewParent);
  1914. aNewParent->InsertChildAt(aIdxInParent, aChild);
  1915. imut.AfterInsertion(aChild);
  1916. imut.Done();
  1917. #ifdef A11Y_LOG
  1918. logging::TreeInfo("move child: old parent tree after",
  1919. logging::eVerbose, curParent);
  1920. logging::TreeInfo("move child: new parent tree after",
  1921. logging::eVerbose, aNewParent);
  1922. #endif
  1923. return true;
  1924. }
  1925. void
  1926. DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
  1927. Accessible** aFocusedAcc)
  1928. {
  1929. // If the accessible is focused then report a focus event after all related
  1930. // mutation events.
  1931. if (aFocusedAcc && !*aFocusedAcc &&
  1932. FocusMgr()->HasDOMFocus(aRoot->GetContent()))
  1933. *aFocusedAcc = aRoot;
  1934. Accessible* root = aRoot->IsHTMLCombobox() ? aRoot->FirstChild() : aRoot;
  1935. if (root->KidsFromDOM()) {
  1936. TreeMutation mt(root, TreeMutation::kNoEvents);
  1937. TreeWalker walker(root);
  1938. while (Accessible* child = walker.Next()) {
  1939. if (child->IsBoundToParent()) {
  1940. MoveChild(child, root, root->ChildCount());
  1941. continue;
  1942. }
  1943. root->AppendChild(child);
  1944. mt.AfterInsertion(child);
  1945. CacheChildrenInSubtree(child, aFocusedAcc);
  1946. }
  1947. mt.Done();
  1948. }
  1949. // Fire events for ARIA elements.
  1950. if (!aRoot->HasARIARole()) {
  1951. return;
  1952. }
  1953. // XXX: we should delay document load complete event if the ARIA document
  1954. // has aria-busy.
  1955. roles::Role role = aRoot->ARIARole();
  1956. if (!aRoot->IsDoc() && (role == roles::DIALOG || role == roles::DOCUMENT)) {
  1957. FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
  1958. }
  1959. }
  1960. void
  1961. DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
  1962. {
  1963. aRoot->mStateFlags |= eIsNotInDocument;
  1964. RemoveDependentIDsFor(aRoot);
  1965. uint32_t count = aRoot->ContentChildCount();
  1966. for (uint32_t idx = 0; idx < count; idx++) {
  1967. Accessible* child = aRoot->ContentChildAt(idx);
  1968. // Removing this accessible from the document doesn't mean anything about
  1969. // accessibles for subdocuments, so skip removing those from the tree.
  1970. if (!child->IsDoc()) {
  1971. UncacheChildrenInSubtree(child);
  1972. }
  1973. }
  1974. if (aRoot->IsNodeMapEntry() &&
  1975. mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
  1976. mNodeToAccessibleMap.Remove(aRoot->GetNode());
  1977. }
  1978. void
  1979. DocAccessible::ShutdownChildrenInSubtree(Accessible* aAccessible)
  1980. {
  1981. // Traverse through children and shutdown them before this accessible. When
  1982. // child gets shutdown then it removes itself from children array of its
  1983. //parent. Use jdx index to process the cases if child is not attached to the
  1984. // parent and as result doesn't remove itself from its children.
  1985. uint32_t count = aAccessible->ContentChildCount();
  1986. for (uint32_t idx = 0, jdx = 0; idx < count; idx++) {
  1987. Accessible* child = aAccessible->ContentChildAt(jdx);
  1988. if (!child->IsBoundToParent()) {
  1989. NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
  1990. jdx++;
  1991. }
  1992. // Don't cross document boundaries. The outerdoc shutdown takes care about
  1993. // its subdocument.
  1994. if (!child->IsDoc())
  1995. ShutdownChildrenInSubtree(child);
  1996. }
  1997. UnbindFromDocument(aAccessible);
  1998. }
  1999. bool
  2000. DocAccessible::IsLoadEventTarget() const
  2001. {
  2002. nsCOMPtr<nsIDocShellTreeItem> treeItem = mDocumentNode->GetDocShell();
  2003. NS_ASSERTION(treeItem, "No document shell for document!");
  2004. nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
  2005. treeItem->GetParent(getter_AddRefs(parentTreeItem));
  2006. // Not a root document.
  2007. if (parentTreeItem) {
  2008. // Return true if it's either:
  2009. // a) tab document;
  2010. nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
  2011. treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
  2012. if (parentTreeItem == rootTreeItem)
  2013. return true;
  2014. // b) frame/iframe document and its parent document is not in loading state
  2015. // Note: we can get notifications while document is loading (and thus
  2016. // while there's no parent document yet).
  2017. DocAccessible* parentDoc = ParentDocument();
  2018. return parentDoc && parentDoc->HasLoadState(eCompletelyLoaded);
  2019. }
  2020. // It's content (not chrome) root document.
  2021. return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
  2022. }