accessible/src/base/DocManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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"
     9 #include "ARIAMap.h"
    10 #include "DocAccessible-inl.h"
    11 #include "nsAccessibilityService.h"
    12 #include "RootAccessibleWrap.h"
    14 #ifdef A11Y_LOG
    15 #include "Logging.h"
    16 #endif
    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 ////////////////////////////////////////////////////////////////////////////////
    36 // DocManager
    37 ////////////////////////////////////////////////////////////////////////////////
    39 DocManager::DocManager()
    40   : mDocAccessibleCache(4)
    41 {
    42 }
    44 ////////////////////////////////////////////////////////////////////////////////
    45 // DocManager public
    47 DocAccessible*
    48 DocManager::GetDocAccessible(nsIDocument* aDocument)
    49 {
    50   if (!aDocument)
    51     return nullptr;
    53   // Ensure CacheChildren is called before we query cache.
    54   ApplicationAcc()->EnsureChildren();
    56   DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
    57   if (docAcc)
    58     return docAcc;
    60   return CreateDocOrRootAccessible(aDocument);
    61 }
    63 Accessible*
    64 DocManager::FindAccessibleInCache(nsINode* aNode) const
    65 {
    66   nsSearchAccessibleInCacheArg arg;
    67   arg.mNode = aNode;
    69   mDocAccessibleCache.EnumerateRead(SearchAccessibleInDocCache,
    70                                     static_cast<void*>(&arg));
    72   return arg.mAccessible;
    73 }
    75 #ifdef DEBUG
    76 bool
    77 DocManager::IsProcessingRefreshDriverNotification() const
    78 {
    79   bool isDocRefreshing = false;
    80   mDocAccessibleCache.EnumerateRead(SearchIfDocIsRefreshing,
    81                                     static_cast<void*>(&isDocRefreshing));
    83   return isDocRefreshing;
    84 }
    85 #endif
    88 ////////////////////////////////////////////////////////////////////////////////
    89 // DocManager protected
    91 bool
    92 DocManager::Init()
    93 {
    94   nsCOMPtr<nsIWebProgress> progress =
    95     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
    97   if (!progress)
    98     return false;
   100   progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
   101                                 nsIWebProgress::NOTIFY_STATE_DOCUMENT);
   103   return true;
   104 }
   106 void
   107 DocManager::Shutdown()
   108 {
   109   nsCOMPtr<nsIWebProgress> progress =
   110     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
   112   if (progress)
   113     progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
   115   ClearDocCache();
   116 }
   118 ////////////////////////////////////////////////////////////////////////////////
   119 // nsISupports
   121 NS_IMPL_ISUPPORTS(DocManager,
   122                   nsIWebProgressListener,
   123                   nsIDOMEventListener,
   124                   nsISupportsWeakReference)
   126 ////////////////////////////////////////////////////////////////////////////////
   127 // nsIWebProgressListener
   129 NS_IMETHODIMP
   130 DocManager::OnStateChange(nsIWebProgress* aWebProgress,
   131                           nsIRequest* aRequest, uint32_t aStateFlags,
   132                           nsresult aStatus)
   133 {
   134   NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
   136   if (nsAccessibilityService::IsShutdown() || !aWebProgress ||
   137       (aStateFlags & (STATE_START | STATE_STOP)) == 0)
   138     return NS_OK;
   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) {
   152 #ifdef A11Y_LOG
   153     if (logging::IsEnabled(logging::eDocLoad))
   154       logging::DocLoad("document loaded", aWebProgress, aRequest, aStateFlags);
   155 #endif
   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
   162     // case.
   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.
   169     if (aRequest) {
   170       uint32_t loadFlags = 0;
   171       aRequest->GetLoadFlags(&loadFlags);
   172       if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
   173         eventType = 0;
   174     }
   176     HandleDOMDocumentLoad(document, eventType);
   177     return NS_OK;
   178   }
   180   // Document loading was started.
   181 #ifdef A11Y_LOG
   182   if (logging::IsEnabled(logging::eDocLoad))
   183     logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags);
   184 #endif
   186   DocAccessible* docAcc = GetExistingDocAccessible(document);
   187   if (!docAcc)
   188     return NS_OK;
   190   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
   191   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
   192   NS_ENSURE_STATE(docShell);
   194   bool isReloading = false;
   195   uint32_t loadType;
   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) {
   202     isReloading = true;
   203   }
   205   docAcc->NotifyOfLoading(isReloading);
   206   return NS_OK;
   207 }
   209 NS_IMETHODIMP
   210 DocManager::OnProgressChange(nsIWebProgress* aWebProgress,
   211                              nsIRequest* aRequest,
   212                              int32_t aCurSelfProgress,
   213                              int32_t aMaxSelfProgress,
   214                              int32_t aCurTotalProgress,
   215                              int32_t aMaxTotalProgress)
   216 {
   217   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   218   return NS_OK;
   219 }
   221 NS_IMETHODIMP
   222 DocManager::OnLocationChange(nsIWebProgress* aWebProgress,
   223                              nsIRequest* aRequest, nsIURI* aLocation,
   224                              uint32_t aFlags)
   225 {
   226   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   227   return NS_OK;
   228 }
   230 NS_IMETHODIMP
   231 DocManager::OnStatusChange(nsIWebProgress* aWebProgress,
   232                            nsIRequest* aRequest, nsresult aStatus,
   233                            const char16_t* aMessage)
   234 {
   235   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   236   return NS_OK;
   237 }
   239 NS_IMETHODIMP
   240 DocManager::OnSecurityChange(nsIWebProgress* aWebProgress,
   241                              nsIRequest* aRequest,
   242                              uint32_t aState)
   243 {
   244   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   245   return NS_OK;
   246 }
   248 ////////////////////////////////////////////////////////////////////////////////
   249 // nsIDOMEventListener
   251 NS_IMETHODIMP
   252 DocManager::HandleEvent(nsIDOMEvent* aEvent)
   253 {
   254   nsAutoString type;
   255   aEvent->GetType(type);
   257   nsCOMPtr<nsIDocument> document =
   258     do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
   259   NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!");
   260   if (!document)
   261     return NS_OK;
   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
   267     // processing.
   269 #ifdef A11Y_LOG
   270     if (logging::IsEnabled(logging::eDocDestroy))
   271       logging::DocDestroy("received 'pagehide' event", document);
   272 #endif
   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);
   280     if (docAccessible)
   281       docAccessible->Shutdown();
   283     return NS_OK;
   284   }
   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)) {
   290 #ifdef A11Y_LOG
   291     if (logging::IsEnabled(logging::eDocLoad))
   292       logging::DocLoad("handled 'DOMContentLoaded' event", document);
   293 #endif
   295     HandleDOMDocumentLoad(document,
   296                           nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
   297   }
   299   return NS_OK;
   300 }
   302 ////////////////////////////////////////////////////////////////////////////////
   303 // DocManager private
   305 void
   306 DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument,
   307                                   uint32_t aLoadEventType)
   308 {
   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);
   312   if (!docAcc) {
   313     docAcc = CreateDocOrRootAccessible(aDocument);
   314     if (!docAcc)
   315       return;
   316   }
   318   docAcc->NotifyOfLoad(aLoadEventType);
   319 }
   321 void
   322 DocManager::AddListeners(nsIDocument* aDocument,
   323                          bool aAddDOMContentLoadedListener)
   324 {
   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());
   331 #ifdef A11Y_LOG
   332   if (logging::IsEnabled(logging::eDocCreate))
   333     logging::Text("added 'pagehide' listener");
   334 #endif
   336   if (aAddDOMContentLoadedListener) {
   337     elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
   338                                 TrustedEventsAtCapture());
   339 #ifdef A11Y_LOG
   340     if (logging::IsEnabled(logging::eDocCreate))
   341       logging::Text("added 'DOMContentLoaded' listener");
   342 #endif
   343   }
   344 }
   346 void
   347 DocManager::RemoveListeners(nsIDocument* aDocument)
   348 {
   349   nsPIDOMWindow* window = aDocument->GetWindow();
   350   if (!window)
   351     return;
   353   EventTarget* target = window->GetChromeEventHandler();
   354   if (!target)
   355     return;
   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());
   363 }
   365 DocAccessible*
   366 DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
   367 {
   368   // Ignore hiding, resource documents and documents without docshell.
   369   if (!aDocument->IsVisibleConsideringAncestors() ||
   370       aDocument->IsResourceDoc() || !aDocument->IsActive())
   371     return nullptr;
   373   // Ignore documents without presshell and not having root frame.
   374   nsIPresShell* presShell = aDocument->GetShell();
   375   if (!presShell || presShell->IsDestroying())
   376     return nullptr;
   378   bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
   380   DocAccessible* parentDocAcc = nullptr;
   381   if (!isRootDoc) {
   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!");
   387     if (!parentDocAcc)
   388       return nullptr;
   389   }
   391   // We only create root accessibles for the true root, otherwise create a
   392   // doc accessible.
   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.
   402   docAcc->Init();
   403   docAcc->SetRoleMapEntry(aria::GetRoleMap(aDocument));
   405   // Bind the document to the tree.
   406   if (isRootDoc) {
   407     if (!ApplicationAcc()->AppendChild(docAcc)) {
   408       docAcc->Shutdown();
   409       return nullptr;
   410     }
   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,
   419                              ApplicationAcc());
   421   } else {
   422     parentDocAcc->BindChildDocument(docAcc);
   423   }
   425 #ifdef A11Y_LOG
   426   if (logging::IsEnabled(logging::eDocCreate)) {
   427     logging::DocCreate("document creation finished", aDocument);
   428     logging::Stack();
   429   }
   430 #endif
   432   AddListeners(aDocument, isRootDoc);
   433   return docAcc;
   434 }
   436 ////////////////////////////////////////////////////////////////////////////////
   437 // DocManager static
   439 PLDHashOperator
   440 DocManager::GetFirstEntryInDocCache(const nsIDocument* aKey,
   441                                     DocAccessible* aDocAccessible,
   442                                     void* aUserArg)
   443 {
   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;
   449 }
   451 void
   452 DocManager::ClearDocCache()
   453 {
   454   DocAccessible* docAcc = nullptr;
   455   while (mDocAccessibleCache.EnumerateRead(GetFirstEntryInDocCache, static_cast<void*>(&docAcc))) {
   456     if (docAcc)
   457       docAcc->Shutdown();
   458   }
   459 }
   461 PLDHashOperator
   462 DocManager::SearchAccessibleInDocCache(const nsIDocument* aKey,
   463                                        DocAccessible* aDocAccessible,
   464                                        void* aUserArg)
   465 {
   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;
   475   }
   477   return PL_DHASH_NEXT;
   478 }
   480 #ifdef DEBUG
   481 PLDHashOperator
   482 DocManager::SearchIfDocIsRefreshing(const nsIDocument* aKey,
   483                                     DocAccessible* aDocAccessible,
   484                                     void* aUserArg)
   485 {
   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;
   493   }
   495   return PL_DHASH_NEXT;
   496 }
   497 #endif

mercurial