/src/mozilla-central/accessible/base/DocManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DocManager.h" |
8 | | |
9 | | #include "ApplicationAccessible.h" |
10 | | #include "ARIAMap.h" |
11 | | #include "DocAccessible-inl.h" |
12 | | #include "DocAccessibleChild.h" |
13 | | #include "DocAccessibleParent.h" |
14 | | #include "nsAccessibilityService.h" |
15 | | #include "Platform.h" |
16 | | #include "RootAccessibleWrap.h" |
17 | | #include "xpcAccessibleDocument.h" |
18 | | |
19 | | #ifdef A11Y_LOG |
20 | | #include "Logging.h" |
21 | | #endif |
22 | | |
23 | | #include "mozilla/EventListenerManager.h" |
24 | | #include "mozilla/dom/Event.h" // for Event |
25 | | #include "nsContentUtils.h" |
26 | | #include "nsCURILoader.h" |
27 | | #include "nsDocShellLoadTypes.h" |
28 | | #include "nsIChannel.h" |
29 | | #include "nsIInterfaceRequestorUtils.h" |
30 | | #include "nsIWebNavigation.h" |
31 | | #include "nsServiceManagerUtils.h" |
32 | | #include "nsIWebProgress.h" |
33 | | #include "nsCoreUtils.h" |
34 | | #include "nsXULAppAPI.h" |
35 | | #include "mozilla/dom/TabChild.h" |
36 | | |
37 | | using namespace mozilla; |
38 | | using namespace mozilla::a11y; |
39 | | using namespace mozilla::dom; |
40 | | |
41 | | StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments; |
42 | | nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>* |
43 | | DocManager::sRemoteXPCDocumentCache = nullptr; |
44 | | |
45 | | //////////////////////////////////////////////////////////////////////////////// |
46 | | // DocManager |
47 | | //////////////////////////////////////////////////////////////////////////////// |
48 | | |
49 | | DocManager::DocManager() |
50 | | : mDocAccessibleCache(2), mXPCDocumentCache(0) |
51 | 0 | { |
52 | 0 | } |
53 | | |
54 | | //////////////////////////////////////////////////////////////////////////////// |
55 | | // DocManager public |
56 | | |
57 | | DocAccessible* |
58 | | DocManager::GetDocAccessible(nsIDocument* aDocument) |
59 | 0 | { |
60 | 0 | if (!aDocument) |
61 | 0 | return nullptr; |
62 | 0 | |
63 | 0 | DocAccessible* docAcc = GetExistingDocAccessible(aDocument); |
64 | 0 | if (docAcc) |
65 | 0 | return docAcc; |
66 | 0 | |
67 | 0 | return CreateDocOrRootAccessible(aDocument); |
68 | 0 | } |
69 | | |
70 | | Accessible* |
71 | | DocManager::FindAccessibleInCache(nsINode* aNode) const |
72 | 0 | { |
73 | 0 | for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) { |
74 | 0 | DocAccessible* docAccessible = iter.UserData(); |
75 | 0 | NS_ASSERTION(docAccessible, |
76 | 0 | "No doc accessible for the object in doc accessible cache!"); |
77 | 0 |
|
78 | 0 | if (docAccessible) { |
79 | 0 | Accessible* accessible = docAccessible->GetAccessible(aNode); |
80 | 0 | if (accessible) { |
81 | 0 | return accessible; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | } |
85 | 0 | return nullptr; |
86 | 0 | } |
87 | | |
88 | | void |
89 | | DocManager::RemoveFromXPCDocumentCache(DocAccessible* aDocument) |
90 | 0 | { |
91 | 0 | xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument); |
92 | 0 | if (xpcDoc) { |
93 | 0 | xpcDoc->Shutdown(); |
94 | 0 | mXPCDocumentCache.Remove(aDocument); |
95 | 0 |
|
96 | 0 | if (!HasXPCDocuments()) { |
97 | 0 | MaybeShutdownAccService(nsAccessibilityService::eXPCOM); |
98 | 0 | } |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | void |
103 | | DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument, |
104 | | nsIDocument* aDOMDocument) |
105 | 0 | { |
106 | 0 | // We need to remove listeners in both cases, when document is being shutdown |
107 | 0 | // or when accessibility service is being shut down as well. |
108 | 0 | RemoveListeners(aDOMDocument); |
109 | 0 |
|
110 | 0 | // Document will already be removed when accessibility service is shutting |
111 | 0 | // down so we do not need to remove it twice. |
112 | 0 | if (nsAccessibilityService::IsShutdown()) { |
113 | 0 | return; |
114 | 0 | } |
115 | 0 | |
116 | 0 | RemoveFromXPCDocumentCache(aDocument); |
117 | 0 | mDocAccessibleCache.Remove(aDOMDocument); |
118 | 0 | } |
119 | | |
120 | | void |
121 | | DocManager::RemoveFromRemoteXPCDocumentCache(DocAccessibleParent* aDoc) |
122 | 0 | { |
123 | 0 | xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc); |
124 | 0 | if (doc) { |
125 | 0 | doc->Shutdown(); |
126 | 0 | sRemoteXPCDocumentCache->Remove(aDoc); |
127 | 0 | } |
128 | 0 |
|
129 | 0 | if (sRemoteXPCDocumentCache && sRemoteXPCDocumentCache->Count() == 0) { |
130 | 0 | MaybeShutdownAccService(nsAccessibilityService::eXPCOM); |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | void |
135 | | DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc) |
136 | 0 | { |
137 | 0 | RemoveFromRemoteXPCDocumentCache(aDoc); |
138 | 0 | } |
139 | | |
140 | | xpcAccessibleDocument* |
141 | | DocManager::GetXPCDocument(DocAccessible* aDocument) |
142 | 0 | { |
143 | 0 | if (!aDocument) |
144 | 0 | return nullptr; |
145 | 0 | |
146 | 0 | xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument); |
147 | 0 | if (!xpcDoc) { |
148 | 0 | xpcDoc = new xpcAccessibleDocument(aDocument); |
149 | 0 | mXPCDocumentCache.Put(aDocument, xpcDoc); |
150 | 0 | } |
151 | 0 | return xpcDoc; |
152 | 0 | } |
153 | | |
154 | | xpcAccessibleDocument* |
155 | | DocManager::GetXPCDocument(DocAccessibleParent* aDoc) |
156 | 0 | { |
157 | 0 | xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc); |
158 | 0 | if (doc) { |
159 | 0 | return doc; |
160 | 0 | } |
161 | 0 | |
162 | 0 | if (!sRemoteXPCDocumentCache) { |
163 | 0 | sRemoteXPCDocumentCache = |
164 | 0 | new nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>; |
165 | 0 | } |
166 | 0 |
|
167 | 0 | doc = |
168 | 0 | new xpcAccessibleDocument(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT); |
169 | 0 | sRemoteXPCDocumentCache->Put(aDoc, doc); |
170 | 0 |
|
171 | 0 | return doc; |
172 | 0 | } |
173 | | |
174 | | #ifdef DEBUG |
175 | | bool |
176 | | DocManager::IsProcessingRefreshDriverNotification() const |
177 | | { |
178 | | for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) { |
179 | | DocAccessible* docAccessible = iter.UserData(); |
180 | | NS_ASSERTION(docAccessible, |
181 | | "No doc accessible for the object in doc accessible cache!"); |
182 | | |
183 | | if (docAccessible && docAccessible->mNotificationController && |
184 | | docAccessible->mNotificationController->IsUpdating()) { |
185 | | return true; |
186 | | } |
187 | | } |
188 | | return false; |
189 | | } |
190 | | #endif |
191 | | |
192 | | |
193 | | //////////////////////////////////////////////////////////////////////////////// |
194 | | // DocManager protected |
195 | | |
196 | | bool |
197 | | DocManager::Init() |
198 | 0 | { |
199 | 0 | nsCOMPtr<nsIWebProgress> progress = |
200 | 0 | do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); |
201 | 0 |
|
202 | 0 | if (!progress) |
203 | 0 | return false; |
204 | 0 | |
205 | 0 | progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this), |
206 | 0 | nsIWebProgress::NOTIFY_STATE_DOCUMENT); |
207 | 0 |
|
208 | 0 | return true; |
209 | 0 | } |
210 | | |
211 | | void |
212 | | DocManager::Shutdown() |
213 | 0 | { |
214 | 0 | nsCOMPtr<nsIWebProgress> progress = |
215 | 0 | do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); |
216 | 0 |
|
217 | 0 | if (progress) |
218 | 0 | progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this)); |
219 | 0 |
|
220 | 0 | ClearDocCache(); |
221 | 0 | } |
222 | | |
223 | | //////////////////////////////////////////////////////////////////////////////// |
224 | | // nsISupports |
225 | | |
226 | | NS_IMPL_ISUPPORTS(DocManager, |
227 | | nsIWebProgressListener, |
228 | | nsIDOMEventListener, |
229 | | nsISupportsWeakReference) |
230 | | |
231 | | //////////////////////////////////////////////////////////////////////////////// |
232 | | // nsIWebProgressListener |
233 | | |
234 | | NS_IMETHODIMP |
235 | | DocManager::OnStateChange(nsIWebProgress* aWebProgress, |
236 | | nsIRequest* aRequest, uint32_t aStateFlags, |
237 | | nsresult aStatus) |
238 | 0 | { |
239 | 0 | NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded"); |
240 | 0 |
|
241 | 0 | if (nsAccessibilityService::IsShutdown() || !aWebProgress || |
242 | 0 | (aStateFlags & (STATE_START | STATE_STOP)) == 0) |
243 | 0 | return NS_OK; |
244 | 0 | |
245 | 0 | nsCOMPtr<mozIDOMWindowProxy> DOMWindow; |
246 | 0 | aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow)); |
247 | 0 | NS_ENSURE_STATE(DOMWindow); |
248 | 0 |
|
249 | 0 | nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(DOMWindow); |
250 | 0 | MOZ_ASSERT(piWindow); |
251 | 0 |
|
252 | 0 | nsCOMPtr<nsIDocument> document = piWindow->GetDoc(); |
253 | 0 | NS_ENSURE_STATE(document); |
254 | 0 |
|
255 | 0 | // Document was loaded. |
256 | 0 | if (aStateFlags & STATE_STOP) { |
257 | 0 | #ifdef A11Y_LOG |
258 | 0 | if (logging::IsEnabled(logging::eDocLoad)) |
259 | 0 | logging::DocLoad("document loaded", aWebProgress, aRequest, aStateFlags); |
260 | 0 | #endif |
261 | 0 |
|
262 | 0 | // Figure out an event type to notify the document has been loaded. |
263 | 0 | uint32_t eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED; |
264 | 0 |
|
265 | 0 | // Some XUL documents get start state and then stop state with failure |
266 | 0 | // status when everything is ok. Fire document load complete event in this |
267 | 0 | // case. |
268 | 0 | if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document)) |
269 | 0 | eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE; |
270 | 0 |
|
271 | 0 | // If end consumer has been retargeted for loaded content then do not fire |
272 | 0 | // any event because it means no new document has been loaded, for example, |
273 | 0 | // it happens when user clicks on file link. |
274 | 0 | if (aRequest) { |
275 | 0 | uint32_t loadFlags = 0; |
276 | 0 | aRequest->GetLoadFlags(&loadFlags); |
277 | 0 | if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) |
278 | 0 | eventType = 0; |
279 | 0 | } |
280 | 0 |
|
281 | 0 | HandleDOMDocumentLoad(document, eventType); |
282 | 0 | return NS_OK; |
283 | 0 | } |
284 | 0 |
|
285 | 0 | // Document loading was started. |
286 | 0 | #ifdef A11Y_LOG |
287 | 0 | if (logging::IsEnabled(logging::eDocLoad)) |
288 | 0 | logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags); |
289 | 0 | #endif |
290 | 0 |
|
291 | 0 | DocAccessible* docAcc = GetExistingDocAccessible(document); |
292 | 0 | if (!docAcc) |
293 | 0 | return NS_OK; |
294 | 0 | |
295 | 0 | nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow)); |
296 | 0 | nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav)); |
297 | 0 | NS_ENSURE_STATE(docShell); |
298 | 0 |
|
299 | 0 | bool isReloading = false; |
300 | 0 | uint32_t loadType; |
301 | 0 | docShell->GetLoadType(&loadType); |
302 | 0 | if (loadType == LOAD_RELOAD_NORMAL || |
303 | 0 | loadType == LOAD_RELOAD_BYPASS_CACHE || |
304 | 0 | loadType == LOAD_RELOAD_BYPASS_PROXY || |
305 | 0 | loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE || |
306 | 0 | loadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) { |
307 | 0 | isReloading = true; |
308 | 0 | } |
309 | 0 |
|
310 | 0 | docAcc->NotifyOfLoading(isReloading); |
311 | 0 | return NS_OK; |
312 | 0 | } |
313 | | |
314 | | NS_IMETHODIMP |
315 | | DocManager::OnProgressChange(nsIWebProgress* aWebProgress, |
316 | | nsIRequest* aRequest, |
317 | | int32_t aCurSelfProgress, |
318 | | int32_t aMaxSelfProgress, |
319 | | int32_t aCurTotalProgress, |
320 | | int32_t aMaxTotalProgress) |
321 | 0 | { |
322 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
323 | 0 | return NS_OK; |
324 | 0 | } |
325 | | |
326 | | NS_IMETHODIMP |
327 | | DocManager::OnLocationChange(nsIWebProgress* aWebProgress, |
328 | | nsIRequest* aRequest, nsIURI* aLocation, |
329 | | uint32_t aFlags) |
330 | 0 | { |
331 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
332 | 0 | return NS_OK; |
333 | 0 | } |
334 | | |
335 | | NS_IMETHODIMP |
336 | | DocManager::OnStatusChange(nsIWebProgress* aWebProgress, |
337 | | nsIRequest* aRequest, nsresult aStatus, |
338 | | const char16_t* aMessage) |
339 | 0 | { |
340 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
341 | 0 | return NS_OK; |
342 | 0 | } |
343 | | |
344 | | NS_IMETHODIMP |
345 | | DocManager::OnSecurityChange(nsIWebProgress* aWebProgress, |
346 | | nsIRequest* aRequest, |
347 | | uint32_t aState) |
348 | 0 | { |
349 | 0 | MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); |
350 | 0 | return NS_OK; |
351 | 0 | } |
352 | | |
353 | | //////////////////////////////////////////////////////////////////////////////// |
354 | | // nsIDOMEventListener |
355 | | |
356 | | NS_IMETHODIMP |
357 | | DocManager::HandleEvent(Event* aEvent) |
358 | 0 | { |
359 | 0 | nsAutoString type; |
360 | 0 | aEvent->GetType(type); |
361 | 0 |
|
362 | 0 | nsCOMPtr<nsIDocument> document = do_QueryInterface(aEvent->GetTarget()); |
363 | 0 | NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!"); |
364 | 0 | if (!document) |
365 | 0 | return NS_OK; |
366 | 0 | |
367 | 0 | if (type.EqualsLiteral("pagehide")) { |
368 | 0 | // 'pagehide' event is registered on every DOM document we create an |
369 | 0 | // accessible for, process the event for the target. This document |
370 | 0 | // accessible and all its sub document accessible are shutdown as result of |
371 | 0 | // processing. |
372 | 0 |
|
373 | 0 | #ifdef A11Y_LOG |
374 | 0 | if (logging::IsEnabled(logging::eDocDestroy)) |
375 | 0 | logging::DocDestroy("received 'pagehide' event", document); |
376 | 0 | #endif |
377 | 0 |
|
378 | 0 | // Shutdown this one and sub document accessibles. |
379 | 0 |
|
380 | 0 | // We're allowed to not remove listeners when accessible document is |
381 | 0 | // shutdown since we don't keep strong reference on chrome event target and |
382 | 0 | // listeners are removed automatically when chrome event target goes away. |
383 | 0 | DocAccessible* docAccessible = GetExistingDocAccessible(document); |
384 | 0 | if (docAccessible) |
385 | 0 | docAccessible->Shutdown(); |
386 | 0 |
|
387 | 0 | return NS_OK; |
388 | 0 | } |
389 | 0 |
|
390 | 0 | // XXX: handle error pages loading separately since they get neither |
391 | 0 | // webprogress notifications nor 'pageshow' event. |
392 | 0 | if (type.EqualsLiteral("DOMContentLoaded") && |
393 | 0 | nsCoreUtils::IsErrorPage(document)) { |
394 | 0 | #ifdef A11Y_LOG |
395 | 0 | if (logging::IsEnabled(logging::eDocLoad)) |
396 | 0 | logging::DocLoad("handled 'DOMContentLoaded' event", document); |
397 | 0 | #endif |
398 | 0 |
|
399 | 0 | HandleDOMDocumentLoad(document, |
400 | 0 | nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE); |
401 | 0 | } |
402 | 0 |
|
403 | 0 | return NS_OK; |
404 | 0 | } |
405 | | |
406 | | //////////////////////////////////////////////////////////////////////////////// |
407 | | // DocManager private |
408 | | |
409 | | void |
410 | | DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument, |
411 | | uint32_t aLoadEventType) |
412 | 0 | { |
413 | 0 | // Document accessible can be created before we were notified the DOM document |
414 | 0 | // was loaded completely. However if it's not created yet then create it. |
415 | 0 | DocAccessible* docAcc = GetExistingDocAccessible(aDocument); |
416 | 0 | if (!docAcc) { |
417 | 0 | docAcc = CreateDocOrRootAccessible(aDocument); |
418 | 0 | if (!docAcc) |
419 | 0 | return; |
420 | 0 | } |
421 | 0 | |
422 | 0 | docAcc->NotifyOfLoad(aLoadEventType); |
423 | 0 | } |
424 | | |
425 | | void |
426 | | DocManager::AddListeners(nsIDocument* aDocument, |
427 | | bool aAddDOMContentLoadedListener) |
428 | 0 | { |
429 | 0 | nsPIDOMWindowOuter* window = aDocument->GetWindow(); |
430 | 0 | EventTarget* target = window->GetChromeEventHandler(); |
431 | 0 | EventListenerManager* elm = target->GetOrCreateListenerManager(); |
432 | 0 | elm->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"), |
433 | 0 | TrustedEventsAtCapture()); |
434 | 0 |
|
435 | 0 | #ifdef A11Y_LOG |
436 | 0 | if (logging::IsEnabled(logging::eDocCreate)) |
437 | 0 | logging::Text("added 'pagehide' listener"); |
438 | 0 | #endif |
439 | 0 |
|
440 | 0 | if (aAddDOMContentLoadedListener) { |
441 | 0 | elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"), |
442 | 0 | TrustedEventsAtCapture()); |
443 | 0 | #ifdef A11Y_LOG |
444 | 0 | if (logging::IsEnabled(logging::eDocCreate)) |
445 | 0 | logging::Text("added 'DOMContentLoaded' listener"); |
446 | 0 | #endif |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | | void |
451 | | DocManager::RemoveListeners(nsIDocument* aDocument) |
452 | 0 | { |
453 | 0 | nsPIDOMWindowOuter* window = aDocument->GetWindow(); |
454 | 0 | if (!window) |
455 | 0 | return; |
456 | 0 | |
457 | 0 | EventTarget* target = window->GetChromeEventHandler(); |
458 | 0 | if (!target) |
459 | 0 | return; |
460 | 0 | |
461 | 0 | EventListenerManager* elm = target->GetOrCreateListenerManager(); |
462 | 0 | elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"), |
463 | 0 | TrustedEventsAtCapture()); |
464 | 0 |
|
465 | 0 | elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"), |
466 | 0 | TrustedEventsAtCapture()); |
467 | 0 | } |
468 | | |
469 | | DocAccessible* |
470 | | DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument) |
471 | 0 | { |
472 | 0 | // Ignore hidden documents, resource documents, static clone |
473 | 0 | // (printing) documents and documents without a docshell. |
474 | 0 | if (!aDocument->IsVisibleConsideringAncestors() || |
475 | 0 | aDocument->IsResourceDoc() || aDocument->IsStaticDocument() || |
476 | 0 | !aDocument->IsActive()) { |
477 | 0 | return nullptr; |
478 | 0 | } |
479 | 0 | |
480 | 0 | nsIDocShell* docShell = aDocument->GetDocShell(); |
481 | 0 | if (!docShell || docShell->IsInvisible()) { |
482 | 0 | return nullptr; |
483 | 0 | } |
484 | 0 | |
485 | 0 | nsIWidget* widget = nsContentUtils::WidgetForDocument(aDocument); |
486 | 0 | if (!widget || widget->WindowType() == eWindowType_invisible) { |
487 | 0 | return nullptr; |
488 | 0 | } |
489 | 0 | |
490 | 0 | // Ignore documents without presshell and not having root frame. |
491 | 0 | nsIPresShell* presShell = aDocument->GetShell(); |
492 | 0 | if (!presShell || presShell->IsDestroying()) |
493 | 0 | return nullptr; |
494 | 0 | |
495 | 0 | bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument); |
496 | 0 |
|
497 | 0 | DocAccessible* parentDocAcc = nullptr; |
498 | 0 | if (!isRootDoc) { |
499 | 0 | // XXXaaronl: ideally we would traverse the presshell chain. Since there's |
500 | 0 | // no easy way to do that, we cheat and use the document hierarchy. |
501 | 0 | parentDocAcc = GetDocAccessible(aDocument->GetParentDocument()); |
502 | 0 | NS_ASSERTION(parentDocAcc, |
503 | 0 | "Can't create an accessible for the document!"); |
504 | 0 | if (!parentDocAcc) |
505 | 0 | return nullptr; |
506 | 0 | } |
507 | 0 | |
508 | 0 | // We only create root accessibles for the true root, otherwise create a |
509 | 0 | // doc accessible. |
510 | 0 | RefPtr<DocAccessible> docAcc = isRootDoc ? |
511 | 0 | new RootAccessibleWrap(aDocument, presShell) : |
512 | 0 | new DocAccessibleWrap(aDocument, presShell); |
513 | 0 |
|
514 | 0 | // Cache the document accessible into document cache. |
515 | 0 | mDocAccessibleCache.Put(aDocument, docAcc); |
516 | 0 |
|
517 | 0 | // Initialize the document accessible. |
518 | 0 | docAcc->Init(); |
519 | 0 |
|
520 | 0 | // Bind the document to the tree. |
521 | 0 | if (isRootDoc) { |
522 | 0 | if (!ApplicationAcc()->AppendChild(docAcc)) { |
523 | 0 | docAcc->Shutdown(); |
524 | 0 | return nullptr; |
525 | 0 | } |
526 | 0 | |
527 | 0 | // Fire reorder event to notify new accessible document has been attached to |
528 | 0 | // the tree. The reorder event is delivered after the document tree is |
529 | 0 | // constructed because event processing and tree construction are done by |
530 | 0 | // the same document. |
531 | 0 | // Note: don't use AccReorderEvent to avoid coalsecense and special reorder |
532 | 0 | // events processing. |
533 | 0 | docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER, |
534 | 0 | ApplicationAcc()); |
535 | 0 |
|
536 | 0 | } else { |
537 | 0 | parentDocAcc->BindChildDocument(docAcc); |
538 | 0 | } |
539 | 0 |
|
540 | 0 | #ifdef A11Y_LOG |
541 | 0 | if (logging::IsEnabled(logging::eDocCreate)) { |
542 | 0 | logging::DocCreate("document creation finished", aDocument); |
543 | 0 | logging::Stack(); |
544 | 0 | } |
545 | 0 | #endif |
546 | 0 |
|
547 | 0 | AddListeners(aDocument, isRootDoc); |
548 | 0 | return docAcc; |
549 | 0 | } |
550 | | |
551 | | //////////////////////////////////////////////////////////////////////////////// |
552 | | // DocManager static |
553 | | |
554 | | void |
555 | | DocManager::ClearDocCache() |
556 | 0 | { |
557 | 0 | while (mDocAccessibleCache.Count() > 0) { |
558 | 0 | auto iter = mDocAccessibleCache.Iter(); |
559 | 0 | MOZ_ASSERT(!iter.Done()); |
560 | 0 | DocAccessible* docAcc = iter.UserData(); |
561 | 0 | NS_ASSERTION(docAcc, |
562 | 0 | "No doc accessible for the object in doc accessible cache!"); |
563 | 0 | if (docAcc) { |
564 | 0 | docAcc->Shutdown(); |
565 | 0 | } |
566 | 0 |
|
567 | 0 | iter.Remove(); |
568 | 0 | } |
569 | 0 |
|
570 | 0 | // Ensure that all xpcom accessible documents are shut down as well. |
571 | 0 | while (mXPCDocumentCache.Count() > 0) { |
572 | 0 | auto iter = mXPCDocumentCache.Iter(); |
573 | 0 | MOZ_ASSERT(!iter.Done()); |
574 | 0 | xpcAccessibleDocument* xpcDoc = iter.UserData(); |
575 | 0 | NS_ASSERTION(xpcDoc, "No xpc doc for the object in xpc doc cache!"); |
576 | 0 |
|
577 | 0 | if (xpcDoc) { |
578 | 0 | xpcDoc->Shutdown(); |
579 | 0 | } |
580 | 0 |
|
581 | 0 | iter.Remove(); |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | | void |
586 | | DocManager::RemoteDocAdded(DocAccessibleParent* aDoc) |
587 | 0 | { |
588 | 0 | if (!sRemoteDocuments) { |
589 | 0 | sRemoteDocuments = new nsTArray<DocAccessibleParent*>; |
590 | 0 | ClearOnShutdown(&sRemoteDocuments); |
591 | 0 | } |
592 | 0 |
|
593 | 0 | MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc), |
594 | 0 | "How did we already have the doc!"); |
595 | 0 | sRemoteDocuments->AppendElement(aDoc); |
596 | 0 | ProxyCreated(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT); |
597 | 0 | } |