LCOV - code coverage report
Current view: top level - accessible/generic - DocAccessible.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1100 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 94 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13