1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DocManager.h"
8 #include "ApplicationAccessible.h"
10 #include "DocAccessible-inl.h"
11 #include "nsAccessibilityService.h"
12 #include "RootAccessibleWrap.h"
18 #include "mozilla/EventListenerManager.h"
19 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
20 #include "nsCURILoader.h"
21 #include "nsDocShellLoadTypes.h"
22 #include "nsIChannel.h"
23 #include "nsIDOMDocument.h"
24 #include "nsIDOMWindow.h"
25 #include "nsIInterfaceRequestorUtils.h"
26 #include "nsIWebNavigation.h"
27 #include "nsServiceManagerUtils.h"
28 #include "nsIWebProgress.h"
29 #include "nsCoreUtils.h"
31 using namespace mozilla
;
32 using namespace mozilla::a11y
;
33 using namespace mozilla::dom
;
35 ////////////////////////////////////////////////////////////////////////////////
37 ////////////////////////////////////////////////////////////////////////////////
39 DocManager::DocManager()
40 : mDocAccessibleCache(2)
44 ////////////////////////////////////////////////////////////////////////////////
48 DocManager::GetDocAccessible(nsIDocument
* aDocument
)
53 // Ensure CacheChildren is called before we query cache.
54 ApplicationAcc()->EnsureChildren();
56 DocAccessible
* docAcc
= GetExistingDocAccessible(aDocument
);
60 return CreateDocOrRootAccessible(aDocument
);
64 DocManager::FindAccessibleInCache(nsINode
* aNode
) const
66 nsSearchAccessibleInCacheArg arg
;
69 mDocAccessibleCache
.EnumerateRead(SearchAccessibleInDocCache
,
70 static_cast<void*>(&arg
));
72 return arg
.mAccessible
;
77 DocManager::IsProcessingRefreshDriverNotification() const
79 bool isDocRefreshing
= false;
80 mDocAccessibleCache
.EnumerateRead(SearchIfDocIsRefreshing
,
81 static_cast<void*>(&isDocRefreshing
));
83 return isDocRefreshing
;
88 ////////////////////////////////////////////////////////////////////////////////
89 // DocManager protected
94 nsCOMPtr
<nsIWebProgress
> progress
=
95 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID
);
100 progress
->AddProgressListener(static_cast<nsIWebProgressListener
*>(this),
101 nsIWebProgress::NOTIFY_STATE_DOCUMENT
);
107 DocManager::Shutdown()
109 nsCOMPtr
<nsIWebProgress
> progress
=
110 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID
);
113 progress
->RemoveProgressListener(static_cast<nsIWebProgressListener
*>(this));
118 ////////////////////////////////////////////////////////////////////////////////
121 NS_IMPL_ISUPPORTS(DocManager
,
122 nsIWebProgressListener
,
124 nsISupportsWeakReference
)
126 ////////////////////////////////////////////////////////////////////////////////
127 // nsIWebProgressListener
130 DocManager::OnStateChange(nsIWebProgress
* aWebProgress
,
131 nsIRequest
* aRequest
, uint32_t aStateFlags
,
134 NS_ASSERTION(aStateFlags
& STATE_IS_DOCUMENT
, "Other notifications excluded");
136 if (nsAccessibilityService::IsShutdown() || !aWebProgress
||
137 (aStateFlags
& (STATE_START
| STATE_STOP
)) == 0)
140 nsCOMPtr
<nsIDOMWindow
> DOMWindow
;
141 aWebProgress
->GetDOMWindow(getter_AddRefs(DOMWindow
));
142 NS_ENSURE_STATE(DOMWindow
);
144 nsCOMPtr
<nsIDOMDocument
> DOMDocument
;
145 DOMWindow
->GetDocument(getter_AddRefs(DOMDocument
));
146 NS_ENSURE_STATE(DOMDocument
);
148 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(DOMDocument
));
150 // Document was loaded.
151 if (aStateFlags
& STATE_STOP
) {
153 if (logging::IsEnabled(logging::eDocLoad
))
154 logging::DocLoad("document loaded", aWebProgress
, aRequest
, aStateFlags
);
157 // Figure out an event type to notify the document has been loaded.
158 uint32_t eventType
= nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
;
160 // Some XUL documents get start state and then stop state with failure
161 // status when everything is ok. Fire document load complete event in this
163 if (NS_SUCCEEDED(aStatus
) || !nsCoreUtils::IsContentDocument(document
))
164 eventType
= nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
;
166 // If end consumer has been retargeted for loaded content then do not fire
167 // any event because it means no new document has been loaded, for example,
168 // it happens when user clicks on file link.
170 uint32_t loadFlags
= 0;
171 aRequest
->GetLoadFlags(&loadFlags
);
172 if (loadFlags
& nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
)
176 HandleDOMDocumentLoad(document
, eventType
);
180 // Document loading was started.
182 if (logging::IsEnabled(logging::eDocLoad
))
183 logging::DocLoad("start document loading", aWebProgress
, aRequest
, aStateFlags
);
186 DocAccessible
* docAcc
= GetExistingDocAccessible(document
);
190 nsCOMPtr
<nsIWebNavigation
> webNav(do_GetInterface(DOMWindow
));
191 nsCOMPtr
<nsIDocShell
> docShell(do_QueryInterface(webNav
));
192 NS_ENSURE_STATE(docShell
);
194 bool isReloading
= false;
196 docShell
->GetLoadType(&loadType
);
197 if (loadType
== LOAD_RELOAD_NORMAL
||
198 loadType
== LOAD_RELOAD_BYPASS_CACHE
||
199 loadType
== LOAD_RELOAD_BYPASS_PROXY
||
200 loadType
== LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
||
201 loadType
== LOAD_RELOAD_ALLOW_MIXED_CONTENT
) {
205 docAcc
->NotifyOfLoading(isReloading
);
210 DocManager::OnProgressChange(nsIWebProgress
* aWebProgress
,
211 nsIRequest
* aRequest
,
212 int32_t aCurSelfProgress
,
213 int32_t aMaxSelfProgress
,
214 int32_t aCurTotalProgress
,
215 int32_t aMaxTotalProgress
)
217 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
222 DocManager::OnLocationChange(nsIWebProgress
* aWebProgress
,
223 nsIRequest
* aRequest
, nsIURI
* aLocation
,
226 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
231 DocManager::OnStatusChange(nsIWebProgress
* aWebProgress
,
232 nsIRequest
* aRequest
, nsresult aStatus
,
233 const char16_t
* aMessage
)
235 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
240 DocManager::OnSecurityChange(nsIWebProgress
* aWebProgress
,
241 nsIRequest
* aRequest
,
244 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
248 ////////////////////////////////////////////////////////////////////////////////
249 // nsIDOMEventListener
252 DocManager::HandleEvent(nsIDOMEvent
* aEvent
)
255 aEvent
->GetType(type
);
257 nsCOMPtr
<nsIDocument
> document
=
258 do_QueryInterface(aEvent
->InternalDOMEvent()->GetTarget());
259 NS_ASSERTION(document
, "pagehide or DOMContentLoaded for non document!");
263 if (type
.EqualsLiteral("pagehide")) {
264 // 'pagehide' event is registered on every DOM document we create an
265 // accessible for, process the event for the target. This document
266 // accessible and all its sub document accessible are shutdown as result of
270 if (logging::IsEnabled(logging::eDocDestroy
))
271 logging::DocDestroy("received 'pagehide' event", document
);
274 // Shutdown this one and sub document accessibles.
276 // We're allowed to not remove listeners when accessible document is
277 // shutdown since we don't keep strong reference on chrome event target and
278 // listeners are removed automatically when chrome event target goes away.
279 DocAccessible
* docAccessible
= GetExistingDocAccessible(document
);
281 docAccessible
->Shutdown();
286 // XXX: handle error pages loading separately since they get neither
287 // webprogress notifications nor 'pageshow' event.
288 if (type
.EqualsLiteral("DOMContentLoaded") &&
289 nsCoreUtils::IsErrorPage(document
)) {
291 if (logging::IsEnabled(logging::eDocLoad
))
292 logging::DocLoad("handled 'DOMContentLoaded' event", document
);
295 HandleDOMDocumentLoad(document
,
296 nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
);
302 ////////////////////////////////////////////////////////////////////////////////
303 // DocManager private
306 DocManager::HandleDOMDocumentLoad(nsIDocument
* aDocument
,
307 uint32_t aLoadEventType
)
309 // Document accessible can be created before we were notified the DOM document
310 // was loaded completely. However if it's not created yet then create it.
311 DocAccessible
* docAcc
= GetExistingDocAccessible(aDocument
);
313 docAcc
= CreateDocOrRootAccessible(aDocument
);
318 docAcc
->NotifyOfLoad(aLoadEventType
);
322 DocManager::AddListeners(nsIDocument
* aDocument
,
323 bool aAddDOMContentLoadedListener
)
325 nsPIDOMWindow
* window
= aDocument
->GetWindow();
326 EventTarget
* target
= window
->GetChromeEventHandler();
327 EventListenerManager
* elm
= target
->GetOrCreateListenerManager();
328 elm
->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
329 TrustedEventsAtCapture());
332 if (logging::IsEnabled(logging::eDocCreate
))
333 logging::Text("added 'pagehide' listener");
336 if (aAddDOMContentLoadedListener
) {
337 elm
->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
338 TrustedEventsAtCapture());
340 if (logging::IsEnabled(logging::eDocCreate
))
341 logging::Text("added 'DOMContentLoaded' listener");
347 DocManager::RemoveListeners(nsIDocument
* aDocument
)
349 nsPIDOMWindow
* window
= aDocument
->GetWindow();
353 EventTarget
* target
= window
->GetChromeEventHandler();
357 EventListenerManager
* elm
= target
->GetOrCreateListenerManager();
358 elm
->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
359 TrustedEventsAtCapture());
361 elm
->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
362 TrustedEventsAtCapture());
366 DocManager::CreateDocOrRootAccessible(nsIDocument
* aDocument
)
368 // Ignore hiding, resource documents and documents without docshell.
369 if (!aDocument
->IsVisibleConsideringAncestors() ||
370 aDocument
->IsResourceDoc() || !aDocument
->IsActive())
373 // Ignore documents without presshell and not having root frame.
374 nsIPresShell
* presShell
= aDocument
->GetShell();
375 if (!presShell
|| presShell
->IsDestroying())
378 bool isRootDoc
= nsCoreUtils::IsRootDocument(aDocument
);
380 DocAccessible
* parentDocAcc
= nullptr;
382 // XXXaaronl: ideally we would traverse the presshell chain. Since there's
383 // no easy way to do that, we cheat and use the document hierarchy.
384 parentDocAcc
= GetDocAccessible(aDocument
->GetParentDocument());
385 NS_ASSERTION(parentDocAcc
,
386 "Can't create an accessible for the document!");
391 // We only create root accessibles for the true root, otherwise create a
393 nsIContent
*rootElm
= nsCoreUtils::GetRoleContent(aDocument
);
394 nsRefPtr
<DocAccessible
> docAcc
= isRootDoc
?
395 new RootAccessibleWrap(aDocument
, rootElm
, presShell
) :
396 new DocAccessibleWrap(aDocument
, rootElm
, presShell
);
398 // Cache the document accessible into document cache.
399 mDocAccessibleCache
.Put(aDocument
, docAcc
);
401 // Initialize the document accessible.
403 docAcc
->SetRoleMapEntry(aria::GetRoleMap(aDocument
));
405 // Bind the document to the tree.
407 if (!ApplicationAcc()->AppendChild(docAcc
)) {
412 // Fire reorder event to notify new accessible document has been attached to
413 // the tree. The reorder event is delivered after the document tree is
414 // constructed because event processing and tree construction are done by
415 // the same document.
416 // Note: don't use AccReorderEvent to avoid coalsecense and special reorder
417 // events processing.
418 docAcc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER
,
422 parentDocAcc
->BindChildDocument(docAcc
);
426 if (logging::IsEnabled(logging::eDocCreate
)) {
427 logging::DocCreate("document creation finished", aDocument
);
432 AddListeners(aDocument
, isRootDoc
);
436 ////////////////////////////////////////////////////////////////////////////////
440 DocManager::GetFirstEntryInDocCache(const nsIDocument
* aKey
,
441 DocAccessible
* aDocAccessible
,
444 NS_ASSERTION(aDocAccessible
,
445 "No doc accessible for the object in doc accessible cache!");
446 *reinterpret_cast<DocAccessible
**>(aUserArg
) = aDocAccessible
;
448 return PL_DHASH_STOP
;
452 DocManager::ClearDocCache()
454 DocAccessible
* docAcc
= nullptr;
455 while (mDocAccessibleCache
.EnumerateRead(GetFirstEntryInDocCache
, static_cast<void*>(&docAcc
))) {
462 DocManager::SearchAccessibleInDocCache(const nsIDocument
* aKey
,
463 DocAccessible
* aDocAccessible
,
466 NS_ASSERTION(aDocAccessible
,
467 "No doc accessible for the object in doc accessible cache!");
469 if (aDocAccessible
) {
470 nsSearchAccessibleInCacheArg
* arg
=
471 static_cast<nsSearchAccessibleInCacheArg
*>(aUserArg
);
472 arg
->mAccessible
= aDocAccessible
->GetAccessible(arg
->mNode
);
473 if (arg
->mAccessible
)
474 return PL_DHASH_STOP
;
477 return PL_DHASH_NEXT
;
482 DocManager::SearchIfDocIsRefreshing(const nsIDocument
* aKey
,
483 DocAccessible
* aDocAccessible
,
486 NS_ASSERTION(aDocAccessible
,
487 "No doc accessible for the object in doc accessible cache!");
489 if (aDocAccessible
&& aDocAccessible
->mNotificationController
&&
490 aDocAccessible
->mNotificationController
->IsUpdating()) {
491 *(static_cast<bool*>(aUserArg
)) = true;
492 return PL_DHASH_STOP
;
495 return PL_DHASH_NEXT
;