tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DocManager.cpp (19191B)


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