LCOV - code coverage report
Current view: directory - accessible/src/base - nsDocAccessible.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 861 2 0.2 %
Date: 2012-06-02 Functions: 97 2 2.1 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2003
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Original Author: Aaron Leventhal (aaronl@netscape.com)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "AccIterator.h"
      40                 : #include "nsAccCache.h"
      41                 : #include "nsAccessibilityService.h"
      42                 : #include "nsAccessiblePivot.h"
      43                 : #include "nsAccTreeWalker.h"
      44                 : #include "nsAccUtils.h"
      45                 : #include "nsRootAccessible.h"
      46                 : #include "nsTextEquivUtils.h"
      47                 : #include "Role.h"
      48                 : #include "States.h"
      49                 : 
      50                 : #include "nsIMutableArray.h"
      51                 : #include "nsICommandManager.h"
      52                 : #include "nsIDocShell.h"
      53                 : #include "nsIDocShellTreeItem.h"
      54                 : #include "nsIDocument.h"
      55                 : #include "nsIDOMAttr.h"
      56                 : #include "nsIDOMCharacterData.h"
      57                 : #include "nsIDOMDocument.h"
      58                 : #include "nsIDOMDocumentType.h"
      59                 : #include "nsIDOMXULDocument.h"
      60                 : #include "nsIDOMMutationEvent.h"
      61                 : #include "nsPIDOMWindow.h"
      62                 : #include "nsIDOMXULPopupElement.h"
      63                 : #include "nsIEditingSession.h"
      64                 : #include "nsEventStateManager.h"
      65                 : #include "nsIFrame.h"
      66                 : #include "nsHTMLSelectAccessible.h"
      67                 : #include "nsIInterfaceRequestorUtils.h"
      68                 : #include "nsINameSpaceManager.h"
      69                 : #include "nsIPresShell.h"
      70                 : #include "nsIServiceManager.h"
      71                 : #include "nsIViewManager.h"
      72                 : #include "nsIScrollableFrame.h"
      73                 : #include "nsUnicharUtils.h"
      74                 : #include "nsIURI.h"
      75                 : #include "nsIWebNavigation.h"
      76                 : #include "nsFocusManager.h"
      77                 : #include "mozilla/dom/Element.h"
      78                 : #ifdef MOZ_XUL
      79                 : #include "nsIXULDocument.h"
      80                 : #endif
      81                 : 
      82                 : using namespace mozilla;
      83                 : using namespace mozilla::a11y;
      84                 : 
      85                 : ////////////////////////////////////////////////////////////////////////////////
      86                 : // Static member initialization
      87                 : 
      88                 : static nsIAtom** kRelationAttrs[] =
      89                 : {
      90                 :   &nsGkAtoms::aria_labelledby,
      91                 :   &nsGkAtoms::aria_describedby,
      92                 :   &nsGkAtoms::aria_owns,
      93                 :   &nsGkAtoms::aria_controls,
      94                 :   &nsGkAtoms::aria_flowto,
      95                 :   &nsGkAtoms::_for,
      96                 :   &nsGkAtoms::control
      97                 : };
      98                 : 
      99                 : static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
     100                 : 
     101                 : ////////////////////////////////////////////////////////////////////////////////
     102                 : // Constructor/desctructor
     103                 : 
     104               0 : nsDocAccessible::
     105                 :   nsDocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
     106                 :                   nsIPresShell* aPresShell) :
     107                 :   nsHyperTextAccessibleWrap(aRootContent, this),
     108                 :   mDocument(aDocument), mScrollPositionChangedTicks(0),
     109                 :   mLoadState(eTreeConstructionPending), mLoadEventType(0),
     110                 :   mVirtualCursor(nsnull),
     111               0 :   mPresShell(aPresShell)
     112                 : {
     113               0 :   mFlags |= eDocAccessible;
     114                 : 
     115               0 :   mDependentIDsHash.Init();
     116                 :   // XXX aaronl should we use an algorithm for the initial cache size?
     117               0 :   mAccessibleCache.Init(kDefaultCacheSize);
     118               0 :   mNodeToAccessibleMap.Init(kDefaultCacheSize);
     119                 : 
     120                 :   // If this is a XUL Document, it should not implement nsHyperText
     121               0 :   if (mDocument && mDocument->IsXUL())
     122               0 :     mFlags &= ~eHyperTextAccessible;
     123                 : 
     124                 :   // For GTK+ native window, we do nothing here.
     125               0 :   if (!mDocument)
     126               0 :     return;
     127                 : 
     128                 :   // nsAccDocManager creates document accessible when scrollable frame is
     129                 :   // available already, it should be safe time to add scroll listener.
     130               0 :   AddScrollListener();
     131                 : 
     132                 :   // We provide a virtual cursor if this is a root doc or if it's a tab doc.
     133               0 :   mIsCursorable = (!(mDocument->GetParentDocument()) ||
     134               0 :                    nsCoreUtils::IsTabDocument(mDocument));
     135                 : }
     136                 : 
     137               0 : nsDocAccessible::~nsDocAccessible()
     138                 : {
     139               0 :   NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
     140               0 : }
     141                 : 
     142                 : 
     143                 : ////////////////////////////////////////////////////////////////////////////////
     144                 : // nsISupports
     145                 : 
     146            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
     147                 : 
     148               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
     149               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
     150               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
     151                 :                                                   NotificationController)
     152                 : 
     153               0 :   if (tmp->mVirtualCursor) {
     154               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
     155                 :                                                     nsAccessiblePivot)
     156                 :   }
     157                 : 
     158               0 :   PRUint32 i, length = tmp->mChildDocuments.Length();
     159               0 :   for (i = 0; i < length; ++i) {
     160               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
     161                 :                                                          nsIAccessible)
     162                 :   }
     163                 : 
     164               0 :   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
     165               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     166                 : 
     167               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
     168               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
     169               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
     170               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
     171               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
     172               0 :   tmp->mDependentIDsHash.Clear();
     173               0 :   tmp->mNodeToAccessibleMap.Clear();
     174               0 :   ClearCache(tmp->mAccessibleCache);
     175               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     176                 : 
     177               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
     178               0 :   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
     179               0 :   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
     180               0 :   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
     181               0 :   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
     182               0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     183               0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     184               0 :   NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
     185               0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
     186               0 :   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
     187                 :                                      mIsCursorable)
     188               0 :     foundInterface = 0;
     189                 : 
     190                 :   nsresult status;
     191               0 :   if (!foundInterface) {
     192                 :     // HTML document accessible must inherit from nsHyperTextAccessible to get
     193                 :     // support text interfaces. XUL document accessible doesn't need this.
     194                 :     // However at some point we may push <body> to implement the interfaces and
     195                 :     // return nsDocAccessible to inherit from nsAccessibleWrap.
     196                 : 
     197               0 :     status = IsHyperText() ? 
     198                 :       nsHyperTextAccessible::QueryInterface(aIID,
     199               0 :                                             (void**)&foundInterface) :
     200               0 :       nsAccessible::QueryInterface(aIID, (void**)&foundInterface);
     201                 :   } else {
     202               0 :     NS_ADDREF(foundInterface);
     203               0 :     status = NS_OK;
     204                 :   }
     205                 : 
     206               0 :   *aInstancePtr = foundInterface;
     207               0 :   return status;
     208                 : }
     209                 : 
     210               0 : NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsHyperTextAccessible)
     211               0 : NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsHyperTextAccessible)
     212                 : 
     213                 : ////////////////////////////////////////////////////////////////////////////////
     214                 : // nsIAccessible
     215                 : 
     216                 : NS_IMETHODIMP
     217               0 : nsDocAccessible::GetName(nsAString& aName)
     218                 : {
     219               0 :   nsresult rv = NS_OK;
     220               0 :   aName.Truncate();
     221               0 :   if (mParent) {
     222               0 :     rv = mParent->GetName(aName); // Allow owning iframe to override the name
     223                 :   }
     224               0 :   if (aName.IsEmpty()) {
     225                 :     // Allow name via aria-labelledby or title attribute
     226               0 :     rv = nsAccessible::GetName(aName);
     227                 :   }
     228               0 :   if (aName.IsEmpty()) {
     229               0 :     rv = GetTitle(aName);   // Try title element
     230                 :   }
     231               0 :   if (aName.IsEmpty()) {   // Last resort: use URL
     232               0 :     rv = GetURL(aName);
     233                 :   }
     234                 : 
     235               0 :   return rv;
     236                 : }
     237                 : 
     238                 : // nsAccessible public method
     239                 : role
     240               0 : nsDocAccessible::NativeRole()
     241                 : {
     242                 :   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     243               0 :     nsCoreUtils::GetDocShellTreeItemFor(mDocument);
     244               0 :   if (docShellTreeItem) {
     245               0 :     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     246               0 :     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
     247                 :     PRInt32 itemType;
     248               0 :     docShellTreeItem->GetItemType(&itemType);
     249               0 :     if (sameTypeRoot == docShellTreeItem) {
     250                 :       // Root of content or chrome tree
     251               0 :       if (itemType == nsIDocShellTreeItem::typeChrome)
     252               0 :         return roles::CHROME_WINDOW;
     253                 : 
     254               0 :       if (itemType == nsIDocShellTreeItem::typeContent) {
     255                 : #ifdef MOZ_XUL
     256               0 :         nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
     257               0 :         if (xulDoc)
     258               0 :           return roles::APPLICATION;
     259                 : #endif
     260               0 :         return roles::DOCUMENT;
     261                 :       }
     262                 :     }
     263               0 :     else if (itemType == nsIDocShellTreeItem::typeContent) {
     264               0 :       return roles::DOCUMENT;
     265                 :     }
     266                 :   }
     267                 : 
     268               0 :   return roles::PANE; // Fall back;
     269                 : }
     270                 : 
     271                 : // nsAccessible public method
     272                 : void
     273               0 : nsDocAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
     274                 : {
     275               0 :   NS_ASSERTION(mDocument, "No document during initialization!");
     276               0 :   if (!mDocument)
     277               0 :     return;
     278                 : 
     279               0 :   mRoleMapEntry = aRoleMapEntry;
     280                 : 
     281               0 :   nsIDocument *parentDoc = mDocument->GetParentDocument();
     282               0 :   if (!parentDoc)
     283               0 :     return; // No parent document for the root document
     284                 : 
     285                 :   // Allow use of ARIA role from outer to override
     286               0 :   nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
     287               0 :   if (ownerContent) {
     288               0 :     nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(ownerContent);
     289               0 :     if (roleMapEntry)
     290               0 :       mRoleMapEntry = roleMapEntry; // Override
     291                 :   }
     292                 : }
     293                 : 
     294                 : void
     295               0 : nsDocAccessible::Description(nsString& aDescription)
     296                 : {
     297               0 :   if (mParent)
     298               0 :     mParent->Description(aDescription);
     299                 : 
     300               0 :   if (aDescription.IsEmpty())
     301                 :     nsTextEquivUtils::
     302                 :       GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
     303               0 :                              aDescription);
     304               0 : }
     305                 : 
     306                 : // nsAccessible public method
     307                 : PRUint64
     308               0 : nsDocAccessible::NativeState()
     309                 : {
     310                 :   // The root content of the document might be removed so that mContent is
     311                 :   // out of date.
     312               0 :   PRUint64 state = (mContent->GetCurrentDoc() == mDocument) ?
     313               0 :     0 : states::STALE;
     314                 : 
     315                 :   // Document is always focusable.
     316               0 :   state |= states::FOCUSABLE;
     317               0 :   if (FocusMgr()->IsFocused(this))
     318               0 :     state |= states::FOCUSED;
     319                 : 
     320                 :   // Expose stale state until the document is ready (DOM is loaded and tree is
     321                 :   // constructed).
     322               0 :   if (!HasLoadState(eReady))
     323               0 :     state |= states::STALE;
     324                 : 
     325                 :   // Expose state busy until the document and all its subdocuments is completely
     326                 :   // loaded.
     327               0 :   if (!HasLoadState(eCompletelyLoaded))
     328               0 :     state |= states::BUSY;
     329                 : 
     330               0 :   nsIFrame* frame = GetFrame();
     331               0 :   if (!frame ||
     332               0 :       !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
     333               0 :     state |= states::INVISIBLE | states::OFFSCREEN;
     334                 :   }
     335                 : 
     336               0 :   nsCOMPtr<nsIEditor> editor = GetEditor();
     337               0 :   state |= editor ? states::EDITABLE : states::READONLY;
     338                 : 
     339               0 :   return state;
     340                 : }
     341                 : 
     342                 : // nsAccessible public method
     343                 : void
     344               0 : nsDocAccessible::ApplyARIAState(PRUint64* aState)
     345                 : {
     346                 :   // Combine with states from outer doc
     347                 :   // 
     348               0 :   nsAccessible::ApplyARIAState(aState);
     349                 : 
     350                 :   // Allow iframe/frame etc. to have final state override via ARIA
     351               0 :   if (mParent)
     352               0 :     mParent->ApplyARIAState(aState);
     353                 : 
     354               0 : }
     355                 : 
     356                 : NS_IMETHODIMP
     357               0 : nsDocAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
     358                 : {
     359               0 :   nsAccessible::GetAttributes(aAttributes);
     360               0 :   if (mParent) {
     361               0 :     mParent->GetAttributes(aAttributes); // Add parent attributes (override inner)
     362                 :   }
     363               0 :   return NS_OK;
     364                 : }
     365                 : 
     366                 : nsAccessible*
     367               0 : nsDocAccessible::FocusedChild()
     368                 : {
     369                 :   // Return an accessible for the current global focus, which does not have to
     370                 :   // be contained within the current document.
     371               0 :   return FocusMgr()->FocusedAccessible();
     372                 : }
     373                 : 
     374               0 : NS_IMETHODIMP nsDocAccessible::TakeFocus()
     375                 : {
     376               0 :   if (IsDefunct())
     377               0 :     return NS_ERROR_FAILURE;
     378                 : 
     379                 :   // Focus the document.
     380               0 :   nsFocusManager* fm = nsFocusManager::GetFocusManager();
     381               0 :   NS_ENSURE_STATE(fm);
     382                 : 
     383               0 :   nsCOMPtr<nsIDOMElement> newFocus;
     384               0 :   return fm->MoveFocus(mDocument->GetWindow(), nsnull,
     385                 :                        nsIFocusManager::MOVEFOCUS_ROOT, 0,
     386               0 :                        getter_AddRefs(newFocus));
     387                 : }
     388                 : 
     389                 : 
     390                 : ////////////////////////////////////////////////////////////////////////////////
     391                 : // nsIAccessibleDocument
     392                 : 
     393               0 : NS_IMETHODIMP nsDocAccessible::GetURL(nsAString& aURL)
     394                 : {
     395               0 :   if (IsDefunct())
     396               0 :     return NS_ERROR_FAILURE;
     397                 : 
     398               0 :   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
     399               0 :   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
     400               0 :   nsCAutoString theURL;
     401               0 :   if (webNav) {
     402               0 :     nsCOMPtr<nsIURI> pURI;
     403               0 :     webNav->GetCurrentURI(getter_AddRefs(pURI));
     404               0 :     if (pURI)
     405               0 :       pURI->GetSpec(theURL);
     406                 :   }
     407               0 :   CopyUTF8toUTF16(theURL, aURL);
     408               0 :   return NS_OK;
     409                 : }
     410                 : 
     411                 : NS_IMETHODIMP
     412               0 : nsDocAccessible::GetTitle(nsAString& aTitle)
     413                 : {
     414               0 :   nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
     415               0 :   if (!domDocument) {
     416               0 :     return NS_ERROR_FAILURE;
     417                 :   }
     418               0 :   return domDocument->GetTitle(aTitle);
     419                 : }
     420                 : 
     421                 : NS_IMETHODIMP
     422               0 : nsDocAccessible::GetMimeType(nsAString& aMimeType)
     423                 : {
     424               0 :   nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
     425               0 :   if (!domDocument) {
     426               0 :     return NS_ERROR_FAILURE;
     427                 :   }
     428               0 :   return domDocument->GetContentType(aMimeType);
     429                 : }
     430                 : 
     431               0 : NS_IMETHODIMP nsDocAccessible::GetDocType(nsAString& aDocType)
     432                 : {
     433               0 :   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
     434               0 :   nsCOMPtr<nsIDOMDocumentType> docType;
     435                 : 
     436                 : #ifdef MOZ_XUL
     437               0 :   nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
     438               0 :   if (xulDoc) {
     439               0 :     aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
     440               0 :     return NS_OK;
     441                 :   } else
     442                 : #endif
     443               0 :   if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
     444               0 :     return docType->GetPublicId(aDocType);
     445                 :   }
     446                 : 
     447               0 :   return NS_ERROR_FAILURE;
     448                 : }
     449                 : 
     450               0 : NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAString& aNameSpaceURI)
     451                 : {
     452               0 :   if (mDocument) {
     453                 :     nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
     454               0 :         do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
     455               0 :     if (nameSpaceManager)
     456               0 :       return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
     457                 :   }
     458               0 :   return NS_ERROR_FAILURE;
     459                 : }
     460                 : 
     461               0 : NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
     462                 : {
     463               0 :   NS_ENSURE_ARG_POINTER(aWindow);
     464               0 :   *aWindow = GetNativeWindow();
     465               0 :   return NS_OK;
     466                 : }
     467                 : 
     468               0 : NS_IMETHODIMP nsDocAccessible::GetWindow(nsIDOMWindow **aDOMWin)
     469                 : {
     470               0 :   *aDOMWin = nsnull;
     471               0 :   if (!mDocument) {
     472               0 :     return NS_ERROR_FAILURE;  // Accessible is Shutdown()
     473                 :   }
     474               0 :   *aDOMWin = mDocument->GetWindow();
     475                 : 
     476               0 :   if (!*aDOMWin)
     477               0 :     return NS_ERROR_FAILURE;  // No DOM Window
     478                 : 
     479               0 :   NS_ADDREF(*aDOMWin);
     480                 : 
     481               0 :   return NS_OK;
     482                 : }
     483                 : 
     484                 : NS_IMETHODIMP
     485               0 : nsDocAccessible::GetDOMDocument(nsIDOMDocument **aDOMDocument)
     486                 : {
     487               0 :   NS_ENSURE_ARG_POINTER(aDOMDocument);
     488               0 :   *aDOMDocument = nsnull;
     489                 : 
     490               0 :   if (mDocument)
     491               0 :     CallQueryInterface(mDocument, aDOMDocument);
     492                 : 
     493               0 :   return NS_OK;
     494                 : }
     495                 : 
     496                 : NS_IMETHODIMP
     497               0 : nsDocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument)
     498                 : {
     499               0 :   NS_ENSURE_ARG_POINTER(aDocument);
     500               0 :   *aDocument = nsnull;
     501                 : 
     502               0 :   if (!IsDefunct())
     503               0 :     NS_IF_ADDREF(*aDocument = ParentDocument());
     504                 : 
     505               0 :   return NS_OK;
     506                 : }
     507                 : 
     508                 : NS_IMETHODIMP
     509               0 : nsDocAccessible::GetChildDocumentCount(PRUint32* aCount)
     510                 : {
     511               0 :   NS_ENSURE_ARG_POINTER(aCount);
     512               0 :   *aCount = 0;
     513                 : 
     514               0 :   if (!IsDefunct())
     515               0 :     *aCount = ChildDocumentCount();
     516                 : 
     517               0 :   return NS_OK;
     518                 : }
     519                 : 
     520                 : NS_IMETHODIMP
     521               0 : nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex,
     522                 :                                     nsIAccessibleDocument** aDocument)
     523                 : {
     524               0 :   NS_ENSURE_ARG_POINTER(aDocument);
     525               0 :   *aDocument = nsnull;
     526                 : 
     527               0 :   if (IsDefunct())
     528               0 :     return NS_OK;
     529                 : 
     530               0 :   NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex));
     531               0 :   return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
     532                 : }
     533                 : 
     534                 : // nsIAccessibleVirtualCursor method
     535                 : NS_IMETHODIMP
     536               0 : nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
     537                 : {
     538               0 :   NS_ENSURE_ARG_POINTER(aVirtualCursor);
     539               0 :   *aVirtualCursor = nsnull;
     540                 : 
     541               0 :   if (IsDefunct())
     542               0 :     return NS_ERROR_FAILURE;
     543                 : 
     544               0 :   NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
     545                 : 
     546               0 :   if (!mVirtualCursor) {
     547               0 :     mVirtualCursor = new nsAccessiblePivot(this);
     548               0 :     mVirtualCursor->AddObserver(this);
     549                 :   }
     550                 : 
     551               0 :   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
     552               0 :   return NS_OK;
     553                 : }
     554                 : 
     555                 : // nsHyperTextAccessible method
     556                 : already_AddRefed<nsIEditor>
     557               0 : nsDocAccessible::GetEditor() const
     558                 : {
     559                 :   // Check if document is editable (designMode="on" case). Otherwise check if
     560                 :   // the html:body (for HTML document case) or document element is editable.
     561               0 :   if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
     562               0 :       !mContent->HasFlag(NODE_IS_EDITABLE))
     563               0 :     return nsnull;
     564                 : 
     565               0 :   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
     566               0 :   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
     567               0 :   if (!editingSession)
     568               0 :     return nsnull; // No editing session interface
     569                 : 
     570               0 :   nsCOMPtr<nsIEditor> editor;
     571               0 :   editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
     572               0 :   if (!editor)
     573               0 :     return nsnull;
     574                 : 
     575               0 :   bool isEditable = false;
     576               0 :   editor->GetIsDocumentEditable(&isEditable);
     577               0 :   if (isEditable)
     578               0 :     return editor.forget();
     579                 : 
     580               0 :   return nsnull;
     581                 : }
     582                 : 
     583                 : // nsDocAccessible public method
     584                 : nsAccessible*
     585               0 : nsDocAccessible::GetAccessible(nsINode* aNode) const
     586                 : {
     587               0 :   nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
     588                 : 
     589                 :   // No accessible in the cache, check if the given ID is unique ID of this
     590                 :   // document accessible.
     591               0 :   if (!accessible) {
     592               0 :     if (GetNode() != aNode)
     593               0 :       return nsnull;
     594                 : 
     595               0 :     accessible = const_cast<nsDocAccessible*>(this);
     596                 :   }
     597                 : 
     598                 : #ifdef DEBUG
     599                 :   // All cached accessible nodes should be in the parent
     600                 :   // It will assert if not all the children were created
     601                 :   // when they were first cached, and no invalidation
     602                 :   // ever corrected parent accessible's child cache.
     603               0 :   nsAccessible* parent = accessible->Parent();
     604               0 :   if (parent)
     605               0 :     parent->TestChildCache(accessible);
     606                 : #endif
     607                 : 
     608               0 :   return accessible;
     609                 : }
     610                 : 
     611                 : ////////////////////////////////////////////////////////////////////////////////
     612                 : // nsAccessNode
     613                 : 
     614                 : bool
     615               0 : nsDocAccessible::Init()
     616                 : {
     617                 :   NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
     618                 : 
     619                 :   // Initialize notification controller.
     620               0 :   mNotificationController = new NotificationController(this, mPresShell);
     621               0 :   if (!mNotificationController)
     622               0 :     return false;
     623                 : 
     624                 :   // Mark the document accessible as loaded if its DOM document was loaded at
     625                 :   // this point (this can happen because a11y is started late or DOM document
     626                 :   // having no container was loaded.
     627               0 :   if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
     628               0 :     mLoadState |= eDOMLoaded;
     629                 : 
     630               0 :   AddEventListeners();
     631               0 :   return true;
     632                 : }
     633                 : 
     634                 : void
     635               0 : nsDocAccessible::Shutdown()
     636                 : {
     637               0 :   if (!mPresShell) // already shutdown
     638               0 :     return;
     639                 : 
     640                 :   NS_LOG_ACCDOCDESTROY_FOR("document shutdown", mDocument, this)
     641                 : 
     642               0 :   if (mNotificationController) {
     643               0 :     mNotificationController->Shutdown();
     644               0 :     mNotificationController = nsnull;
     645                 :   }
     646                 : 
     647               0 :   RemoveEventListeners();
     648                 : 
     649                 :   // Mark the document as shutdown before AT is notified about the document
     650                 :   // removal from its container (valid for root documents on ATK).
     651               0 :   nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
     652               0 :   mDocument = nsnull;
     653                 : 
     654               0 :   if (mParent) {
     655               0 :     nsDocAccessible* parentDocument = mParent->Document();
     656               0 :     if (parentDocument)
     657               0 :       parentDocument->RemoveChildDocument(this);
     658                 : 
     659               0 :     mParent->RemoveChild(this);
     660                 :   }
     661                 : 
     662                 :   // Walk the array backwards because child documents remove themselves from the
     663                 :   // array as they are shutdown.
     664               0 :   PRInt32 childDocCount = mChildDocuments.Length();
     665               0 :   for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
     666               0 :     mChildDocuments[idx]->Shutdown();
     667                 : 
     668               0 :   mChildDocuments.Clear();
     669                 : 
     670               0 :   if (mVirtualCursor) {
     671               0 :     mVirtualCursor->RemoveObserver(this);
     672               0 :     mVirtualCursor = nsnull;
     673                 :   }
     674                 : 
     675               0 :   mPresShell = nsnull;  // Avoid reentrancy
     676                 : 
     677               0 :   mDependentIDsHash.Clear();
     678               0 :   mNodeToAccessibleMap.Clear();
     679               0 :   ClearCache(mAccessibleCache);
     680                 : 
     681               0 :   nsHyperTextAccessibleWrap::Shutdown();
     682                 : 
     683               0 :   GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc);
     684                 : }
     685                 : 
     686                 : nsIFrame*
     687               0 : nsDocAccessible::GetFrame() const
     688                 : {
     689               0 :   nsIFrame* root = nsnull;
     690               0 :   if (mPresShell)
     691               0 :     root = mPresShell->GetRootFrame();
     692                 : 
     693               0 :   return root;
     694                 : }
     695                 : 
     696                 : bool
     697               0 : nsDocAccessible::IsDefunct() const
     698                 : {
     699               0 :   return nsHyperTextAccessibleWrap::IsDefunct() || !mDocument;
     700                 : }
     701                 : 
     702                 : // nsDocAccessible protected member
     703               0 : void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
     704                 : {
     705               0 :   *aRelativeFrame = GetFrame();
     706                 : 
     707               0 :   nsIDocument *document = mDocument;
     708               0 :   nsIDocument *parentDoc = nsnull;
     709                 : 
     710               0 :   while (document) {
     711               0 :     nsIPresShell *presShell = document->GetShell();
     712               0 :     if (!presShell) {
     713               0 :       return;
     714                 :     }
     715                 : 
     716               0 :     nsRect scrollPort;
     717               0 :     nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
     718               0 :     if (sf) {
     719               0 :       scrollPort = sf->GetScrollPortRect();
     720                 :     } else {
     721               0 :       nsIFrame* rootFrame = presShell->GetRootFrame();
     722               0 :       if (!rootFrame) {
     723                 :         return;
     724                 :       }
     725               0 :       scrollPort = rootFrame->GetRect();
     726                 :     }
     727                 : 
     728               0 :     if (parentDoc) {  // After first time thru loop
     729                 :       // XXXroc bogus code! scrollPort is relative to the viewport of
     730                 :       // this document, but we're intersecting rectangles derived from
     731                 :       // multiple documents and assuming they're all in the same coordinate
     732                 :       // system. See bug 514117.
     733               0 :       aBounds.IntersectRect(scrollPort, aBounds);
     734                 :     }
     735                 :     else {  // First time through loop
     736               0 :       aBounds = scrollPort;
     737                 :     }
     738                 : 
     739               0 :     document = parentDoc = document->GetParentDocument();
     740                 :   }
     741                 : }
     742                 : 
     743                 : // nsDocAccessible protected member
     744               0 : nsresult nsDocAccessible::AddEventListeners()
     745                 : {
     746                 :   // 1) Set up scroll position listener
     747                 :   // 2) Check for editor and listen for changes to editor
     748                 : 
     749               0 :   NS_ENSURE_TRUE(mPresShell, NS_ERROR_FAILURE);
     750                 : 
     751               0 :   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
     752               0 :   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
     753               0 :   NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
     754                 : 
     755                 :   // Make sure we're a content docshell
     756                 :   // We don't want to listen to chrome progress
     757                 :   PRInt32 itemType;
     758               0 :   docShellTreeItem->GetItemType(&itemType);
     759                 : 
     760               0 :   bool isContent = (itemType == nsIDocShellTreeItem::typeContent);
     761                 : 
     762               0 :   if (isContent) {
     763                 :     // We're not an editor yet, but we might become one
     764               0 :     nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
     765               0 :     if (commandManager) {
     766               0 :       commandManager->AddCommandObserver(this, "obs_documentCreated");
     767                 :     }
     768                 :   }
     769                 : 
     770               0 :   nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
     771               0 :   docShellTreeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
     772               0 :   if (rootTreeItem) {
     773               0 :     nsRootAccessible* rootAccessible = RootAccessible();
     774               0 :     NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
     775               0 :     nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
     776               0 :     if (caretAccessible) {
     777               0 :       caretAccessible->AddDocSelectionListener(mPresShell);
     778                 :     }
     779                 :   }
     780                 : 
     781                 :   // add document observer
     782               0 :   mDocument->AddObserver(this);
     783               0 :   return NS_OK;
     784                 : }
     785                 : 
     786                 : // nsDocAccessible protected member
     787               0 : nsresult nsDocAccessible::RemoveEventListeners()
     788                 : {
     789                 :   // Remove listeners associated with content documents
     790                 :   // Remove scroll position listener
     791               0 :   RemoveScrollListener();
     792                 : 
     793               0 :   NS_ASSERTION(mDocument, "No document during removal of listeners.");
     794                 : 
     795               0 :   if (mDocument) {
     796               0 :     mDocument->RemoveObserver(this);
     797                 : 
     798               0 :     nsCOMPtr<nsISupports> container = mDocument->GetContainer();
     799               0 :     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
     800               0 :     NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
     801                 : 
     802               0 :     if (docShellTreeItem) {
     803                 :       PRInt32 itemType;
     804               0 :       docShellTreeItem->GetItemType(&itemType);
     805               0 :       if (itemType == nsIDocShellTreeItem::typeContent) {
     806               0 :         nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
     807               0 :         if (commandManager) {
     808               0 :           commandManager->RemoveCommandObserver(this, "obs_documentCreated");
     809                 :         }
     810                 :       }
     811                 :     }
     812                 :   }
     813                 : 
     814               0 :   if (mScrollWatchTimer) {
     815               0 :     mScrollWatchTimer->Cancel();
     816               0 :     mScrollWatchTimer = nsnull;
     817               0 :     NS_RELEASE_THIS(); // Kung fu death grip
     818                 :   }
     819                 : 
     820               0 :   nsRootAccessible* rootAccessible = RootAccessible();
     821               0 :   if (rootAccessible) {
     822               0 :     nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
     823               0 :     if (caretAccessible)
     824               0 :       caretAccessible->RemoveDocSelectionListener(mPresShell);
     825                 :   }
     826                 : 
     827               0 :   return NS_OK;
     828                 : }
     829                 : 
     830               0 : void nsDocAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
     831                 : {
     832               0 :   nsDocAccessible *docAcc = reinterpret_cast<nsDocAccessible*>(aClosure);
     833                 : 
     834               0 :   if (docAcc && docAcc->mScrollPositionChangedTicks &&
     835                 :       ++docAcc->mScrollPositionChangedTicks > 2) {
     836                 :     // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
     837                 :     // We only want to fire accessibilty scroll event when scrolling stops or pauses
     838                 :     // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
     839                 :     // That indicates a pause in scrolling, so we fire the accessibilty scroll event
     840               0 :     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
     841                 : 
     842               0 :     docAcc->mScrollPositionChangedTicks = 0;
     843               0 :     if (docAcc->mScrollWatchTimer) {
     844               0 :       docAcc->mScrollWatchTimer->Cancel();
     845               0 :       docAcc->mScrollWatchTimer = nsnull;
     846               0 :       NS_RELEASE(docAcc); // Release kung fu death grip
     847                 :     }
     848                 :   }
     849               0 : }
     850                 : 
     851                 : // nsDocAccessible protected member
     852               0 : void nsDocAccessible::AddScrollListener()
     853                 : {
     854               0 :   if (!mPresShell)
     855               0 :     return;
     856                 : 
     857               0 :   nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
     858               0 :   if (sf) {
     859               0 :     sf->AddScrollPositionListener(this);
     860                 :     NS_LOG_ACCDOCCREATE_TEXT("add scroll listener")
     861                 :   }
     862                 : }
     863                 : 
     864                 : // nsDocAccessible protected member
     865               0 : void nsDocAccessible::RemoveScrollListener()
     866                 : {
     867               0 :   if (!mPresShell)
     868               0 :     return;
     869                 :  
     870               0 :   nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
     871               0 :   if (sf) {
     872               0 :     sf->RemoveScrollPositionListener(this);
     873                 :   }
     874                 : }
     875                 : 
     876                 : ////////////////////////////////////////////////////////////////////////////////
     877                 : // nsIScrollPositionListener
     878                 : 
     879               0 : void nsDocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
     880                 : {
     881                 :   // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
     882                 :   // then the ::Notify() method will fire the accessibility event for scroll position changes
     883               0 :   const PRUint32 kScrollPosCheckWait = 50;
     884               0 :   if (mScrollWatchTimer) {
     885               0 :     mScrollWatchTimer->SetDelay(kScrollPosCheckWait);  // Create new timer, to avoid leaks
     886                 :   }
     887                 :   else {
     888               0 :     mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
     889               0 :     if (mScrollWatchTimer) {
     890               0 :       NS_ADDREF_THIS(); // Kung fu death grip
     891               0 :       mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
     892                 :                                               kScrollPosCheckWait,
     893               0 :                                               nsITimer::TYPE_REPEATING_SLACK);
     894                 :     }
     895                 :   }
     896               0 :   mScrollPositionChangedTicks = 1;
     897               0 : }
     898                 : 
     899                 : ////////////////////////////////////////////////////////////////////////////////
     900                 : // nsIObserver
     901                 : 
     902               0 : NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
     903                 :                                        const PRUnichar *aData)
     904                 : {
     905               0 :   if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {    
     906                 :     // State editable will now be set, readonly is now clear
     907                 :     // Normally we only fire delayed events created from the node, not an
     908                 :     // accessible object. See the AccStateChangeEvent constructor for details
     909                 :     // about this exceptional case.
     910                 :     nsRefPtr<AccEvent> event =
     911               0 :       new AccStateChangeEvent(this, states::EDITABLE, true);
     912               0 :     FireDelayedAccessibleEvent(event);
     913                 :   }
     914                 : 
     915               0 :   return NS_OK;
     916                 : }
     917                 : 
     918                 : ////////////////////////////////////////////////////////////////////////////////
     919                 : // nsIAccessiblePivotObserver
     920                 : 
     921                 : NS_IMETHODIMP
     922               0 : nsDocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
     923                 :                                 nsIAccessible* aOldAccessible,
     924                 :                                 PRInt32 aOldStart, PRInt32 aOldEnd)
     925                 : {
     926                 :   nsRefPtr<AccEvent> event = new AccVCChangeEvent(this, aOldAccessible,
     927               0 :                                                   aOldStart, aOldEnd);
     928               0 :   nsEventShell::FireEvent(event);
     929                 : 
     930               0 :   return NS_OK;
     931                 : }
     932                 : 
     933                 : ////////////////////////////////////////////////////////////////////////////////
     934                 : // nsIDocumentObserver
     935                 : 
     936               0 : NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
     937               0 : NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
     938               0 : NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
     939                 : 
     940                 : void
     941               0 : nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
     942                 :                                      dom::Element* aElement,
     943                 :                                      PRInt32 aNameSpaceID,
     944                 :                                      nsIAtom* aAttribute, PRInt32 aModType)
     945                 : {
     946               0 :   nsAccessible* accessible = GetAccessible(aElement);
     947               0 :   if (!accessible) {
     948               0 :     if (aElement != mContent)
     949               0 :       return;
     950                 : 
     951               0 :     accessible = this;
     952                 :   }
     953                 : 
     954                 :   // Update dependent IDs cache. Take care of elements that are accessible
     955                 :   // because dependent IDs cache doesn't contain IDs from non accessible
     956                 :   // elements.
     957               0 :   if (aModType != nsIDOMMutationEvent::ADDITION)
     958               0 :     RemoveDependentIDsFor(accessible, aAttribute);
     959                 : 
     960                 :   // Store the ARIA attribute old value so that it can be used after
     961                 :   // attribute change. Note, we assume there's no nested ARIA attribute
     962                 :   // changes. If this happens then we should end up with keeping a stack of
     963                 :   // old values.
     964                 : 
     965                 :   // XXX TODO: bugs 472142, 472143.
     966                 :   // Here we will want to cache whatever attribute values we are interested
     967                 :   // in, such as the existence of aria-pressed for button (so we know if we
     968                 :   // need to newly expose it as a toggle button) etc.
     969               0 :   if (aAttribute == nsGkAtoms::aria_checked ||
     970                 :       aAttribute == nsGkAtoms::aria_pressed) {
     971                 :     mARIAAttrOldValue = (aModType != nsIDOMMutationEvent::ADDITION) ?
     972               0 :       nsAccUtils::GetARIAToken(aElement, aAttribute) : nsnull;
     973                 :   }
     974                 : }
     975                 : 
     976                 : void
     977               0 : nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
     978                 :                                   dom::Element* aElement,
     979                 :                                   PRInt32 aNameSpaceID, nsIAtom* aAttribute,
     980                 :                                   PRInt32 aModType)
     981                 : {
     982               0 :   NS_ASSERTION(!IsDefunct(),
     983                 :                "Attribute changed called on defunct document accessible!");
     984                 : 
     985                 :   // Proceed even if the element is not accessible because element may become
     986                 :   // accessible if it gets certain attribute.
     987               0 :   if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
     988               0 :     return;
     989                 : 
     990                 :   // Ignore attribute change if the element doesn't have an accessible (at all
     991                 :   // or still) iff the element is not a root content of this document accessible
     992                 :   // (which is treated as attribute change on this document accessible).
     993                 :   // Note: we don't bail if all the content hasn't finished loading because
     994                 :   // these attributes are changing for a loaded part of the content.
     995               0 :   nsAccessible* accessible = GetAccessible(aElement);
     996               0 :   if (!accessible) {
     997               0 :     if (mContent != aElement)
     998               0 :       return;
     999                 : 
    1000               0 :     accessible = this;
    1001                 :   }
    1002                 : 
    1003                 :   // Fire accessible events iff there's an accessible, otherwise we consider
    1004                 :   // the accessible state wasn't changed, i.e. its state is initial state.
    1005               0 :   AttributeChangedImpl(aElement, aNameSpaceID, aAttribute);
    1006                 : 
    1007                 :   // Update dependent IDs cache. Take care of accessible elements because no
    1008                 :   // accessible element means either the element is not accessible at all or
    1009                 :   // its accessible will be created later. It doesn't make sense to keep
    1010                 :   // dependent IDs for non accessible elements. For the second case we'll update
    1011                 :   // dependent IDs cache when its accessible is created.
    1012               0 :   if (aModType == nsIDOMMutationEvent::MODIFICATION ||
    1013                 :       aModType == nsIDOMMutationEvent::ADDITION) {
    1014               0 :     AddDependentIDsFor(accessible, aAttribute);
    1015                 :   }
    1016                 : }
    1017                 : 
    1018                 : // nsDocAccessible protected member
    1019                 : void
    1020               0 : nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute)
    1021                 : {
    1022                 :   // Fire accessible event after short timer, because we need to wait for
    1023                 :   // DOM attribute & resulting layout to actually change. Otherwise,
    1024                 :   // assistive technology will retrieve the wrong state/value/selection info.
    1025                 : 
    1026                 :   // XXX todo
    1027                 :   // We still need to handle special HTML cases here
    1028                 :   // For example, if an <img>'s usemap attribute is modified
    1029                 :   // Otherwise it may just be a state change, for example an object changing
    1030                 :   // its visibility
    1031                 :   // 
    1032                 :   // XXX todo: report aria state changes for "undefined" literal value changes
    1033                 :   // filed as bug 472142
    1034                 :   //
    1035                 :   // XXX todo:  invalidate accessible when aria state changes affect exposed role
    1036                 :   // filed as bug 472143
    1037                 : 
    1038                 :   // Universal boolean properties that don't require a role. Fire the state
    1039                 :   // change when disabled or aria-disabled attribute is set.
    1040               0 :   if (aAttribute == nsGkAtoms::disabled ||
    1041                 :       aAttribute == nsGkAtoms::aria_disabled) {
    1042                 : 
    1043                 :     // Note. Checking the XUL or HTML namespace would not seem to gain us
    1044                 :     // anything, because disabled attribute really is going to mean the same
    1045                 :     // thing in any namespace.
    1046                 : 
    1047                 :     // Note. We use the attribute instead of the disabled state bit because
    1048                 :     // ARIA's aria-disabled does not affect the disabled state bit.
    1049                 : 
    1050                 :     nsRefPtr<AccEvent> enabledChangeEvent =
    1051               0 :       new AccStateChangeEvent(aContent, states::ENABLED);
    1052                 : 
    1053               0 :     FireDelayedAccessibleEvent(enabledChangeEvent);
    1054                 : 
    1055                 :     nsRefPtr<AccEvent> sensitiveChangeEvent =
    1056               0 :       new AccStateChangeEvent(aContent, states::SENSITIVE);
    1057                 : 
    1058               0 :     FireDelayedAccessibleEvent(sensitiveChangeEvent);
    1059                 :     return;
    1060                 :   }
    1061                 : 
    1062                 :   // Check for namespaced ARIA attribute
    1063               0 :   if (aNameSpaceID == kNameSpaceID_None) {
    1064                 :     // Check for hyphenated aria-foo property?
    1065               0 :     if (StringBeginsWith(nsDependentAtomString(aAttribute),
    1066               0 :                          NS_LITERAL_STRING("aria-"))) {
    1067               0 :       ARIAAttributeChanged(aContent, aAttribute);
    1068                 :     }
    1069                 :   }
    1070                 : 
    1071               0 :   if (aAttribute == nsGkAtoms::alt ||
    1072                 :       aAttribute == nsGkAtoms::title ||
    1073                 :       aAttribute == nsGkAtoms::aria_label ||
    1074                 :       aAttribute == nsGkAtoms::aria_labelledby) {
    1075                 :     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
    1076               0 :                                aContent);
    1077               0 :     return;
    1078                 :   }
    1079                 : 
    1080               0 :   if (aAttribute == nsGkAtoms::aria_busy) {
    1081                 :     bool isOn = aContent->AttrValueIs(aNameSpaceID, aAttribute,
    1082               0 :                                         nsGkAtoms::_true, eCaseMatters);
    1083               0 :     nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, states::BUSY, isOn);
    1084               0 :     FireDelayedAccessibleEvent(event);
    1085                 :     return;
    1086                 :   }
    1087                 : 
    1088                 :   // ARIA or XUL selection
    1089               0 :   if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
    1090                 :       aAttribute == nsGkAtoms::aria_selected) {
    1091               0 :     nsAccessible* item = GetAccessible(aContent);
    1092                 :     nsAccessible* widget =
    1093               0 :       nsAccUtils::GetSelectableContainer(item, item->State());
    1094               0 :     if (widget) {
    1095                 :       AccSelChangeEvent::SelChangeType selChangeType =
    1096                 :         aContent->AttrValueIs(aNameSpaceID, aAttribute,
    1097               0 :                               nsGkAtoms::_true, eCaseMatters) ?
    1098               0 :           AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
    1099                 : 
    1100                 :       nsRefPtr<AccEvent> event =
    1101               0 :         new AccSelChangeEvent(widget, item, selChangeType);
    1102               0 :       FireDelayedAccessibleEvent(event);
    1103                 :     }
    1104               0 :     return;
    1105                 :   }
    1106                 : 
    1107               0 :   if (aAttribute == nsGkAtoms::contenteditable) {
    1108                 :     nsRefPtr<AccEvent> editableChangeEvent =
    1109               0 :       new AccStateChangeEvent(aContent, states::EDITABLE);
    1110               0 :     FireDelayedAccessibleEvent(editableChangeEvent);
    1111                 :     return;
    1112                 :   }
    1113                 : }
    1114                 : 
    1115                 : // nsDocAccessible protected member
    1116                 : void
    1117               0 : nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
    1118                 : {
    1119                 :   // Note: For universal/global ARIA states and properties we don't care if
    1120                 :   // there is an ARIA role present or not.
    1121                 : 
    1122               0 :   if (aAttribute == nsGkAtoms::aria_required) {
    1123                 :     nsRefPtr<AccEvent> event =
    1124               0 :       new AccStateChangeEvent(aContent, states::REQUIRED);
    1125               0 :     FireDelayedAccessibleEvent(event);
    1126                 :     return;
    1127                 :   }
    1128                 : 
    1129               0 :   if (aAttribute == nsGkAtoms::aria_invalid) {
    1130                 :     nsRefPtr<AccEvent> event =
    1131               0 :       new AccStateChangeEvent(aContent, states::INVALID);
    1132               0 :     FireDelayedAccessibleEvent(event);
    1133                 :     return;
    1134                 :   }
    1135                 : 
    1136                 :   // The activedescendant universal property redirects accessible focus events
    1137                 :   // to the element with the id that activedescendant points to. Make sure
    1138                 :   // the tree up to date before processing.
    1139               0 :   if (aAttribute == nsGkAtoms::aria_activedescendant) {
    1140                 :     mNotificationController->HandleNotification<nsDocAccessible, nsIContent>
    1141               0 :       (this, &nsDocAccessible::ARIAActiveDescendantChanged, aContent);
    1142                 : 
    1143               0 :     return;
    1144                 :   }
    1145                 : 
    1146                 :   // For aria drag and drop changes we fire a generic attribute change event;
    1147                 :   // at least until native API comes up with a more meaningful event.
    1148               0 :   if (aAttribute == nsGkAtoms::aria_grabbed ||
    1149                 :       aAttribute == nsGkAtoms::aria_dropeffect ||
    1150                 :       aAttribute == nsGkAtoms::aria_hidden ||
    1151                 :       aAttribute == nsGkAtoms::aria_sort) {
    1152                 :     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
    1153               0 :                                aContent);
    1154                 :   }
    1155                 : 
    1156                 :   // We treat aria-expanded as a global ARIA state for historical reasons
    1157               0 :   if (aAttribute == nsGkAtoms::aria_expanded) {
    1158                 :     nsRefPtr<AccEvent> event =
    1159               0 :       new AccStateChangeEvent(aContent, states::EXPANDED);
    1160               0 :     FireDelayedAccessibleEvent(event);
    1161                 :     return;
    1162                 :   }
    1163                 : 
    1164               0 :   if (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
    1165                 :     // We don't care about these other ARIA attribute changes unless there is
    1166                 :     // an ARIA role set for the element
    1167                 :     // XXX: we should check the role map to see if the changed property is
    1168                 :     // relevant for that particular role.
    1169               0 :     return;
    1170                 :   }
    1171                 : 
    1172                 :   // The following ARIA attributes only take affect when dynamic content role is present
    1173               0 :   if (aAttribute == nsGkAtoms::aria_checked ||
    1174                 :       aAttribute == nsGkAtoms::aria_pressed) {
    1175                 :     const PRUint32 kState = (aAttribute == nsGkAtoms::aria_checked) ?
    1176               0 :                             states::CHECKED : states::PRESSED;
    1177               0 :     nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, kState);
    1178               0 :     FireDelayedAccessibleEvent(event);
    1179                 : 
    1180               0 :     nsAccessible* accessible = event->GetAccessible();
    1181               0 :     if (accessible) {
    1182               0 :       bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
    1183                 :       bool isMixed = aContent->AttrValueIs(kNameSpaceID_None, aAttribute,
    1184               0 :                                            nsGkAtoms::mixed, eCaseMatters);
    1185               0 :       if (isMixed != wasMixed) {
    1186                 :         nsRefPtr<AccEvent> event =
    1187               0 :           new AccStateChangeEvent(aContent, states::MIXED, isMixed);
    1188               0 :         FireDelayedAccessibleEvent(event);
    1189                 :       }
    1190                 :     }
    1191                 :     return;
    1192                 :   }
    1193                 : 
    1194               0 :   if (aAttribute == nsGkAtoms::aria_readonly) {
    1195                 :     nsRefPtr<AccEvent> event =
    1196               0 :       new AccStateChangeEvent(aContent, states::READONLY);
    1197               0 :     FireDelayedAccessibleEvent(event);
    1198                 :     return;
    1199                 :   }
    1200                 : 
    1201                 :   // Fire value change event whenever aria-valuetext is changed, or
    1202                 :   // when aria-valuenow is changed and aria-valuetext is empty
    1203               0 :   if (aAttribute == nsGkAtoms::aria_valuetext ||
    1204                 :       (aAttribute == nsGkAtoms::aria_valuenow &&
    1205               0 :        (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
    1206                 :         aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
    1207               0 :                               nsGkAtoms::_empty, eCaseMatters)))) {
    1208                 :     FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
    1209               0 :                                aContent);
    1210               0 :     return;
    1211                 :   }
    1212                 : }
    1213                 : 
    1214                 : void
    1215               0 : nsDocAccessible::ARIAActiveDescendantChanged(nsIContent* aElm)
    1216                 : {
    1217               0 :   if (FocusMgr()->HasDOMFocus(aElm)) {
    1218               0 :     nsAutoString id;
    1219               0 :     if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
    1220               0 :       nsIDocument* DOMDoc = aElm->OwnerDoc();
    1221               0 :       dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
    1222               0 :       if (activeDescendantElm) {
    1223               0 :         nsAccessible* activeDescendant = GetAccessible(activeDescendantElm);
    1224               0 :         if (activeDescendant) {
    1225               0 :           FocusMgr()->ActiveItemChanged(activeDescendant, false);
    1226                 :           A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("ARIA activedescedant changed",
    1227                 :                                                  activeDescendant)
    1228                 :         }
    1229                 :       }
    1230                 :     }
    1231                 :   }
    1232               0 : }
    1233                 : 
    1234               0 : void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
    1235                 :                                       nsIContent* aContainer,
    1236                 :                                       nsIContent* aFirstNewContent,
    1237                 :                                       PRInt32 /* unused */)
    1238                 : {
    1239               0 : }
    1240                 : 
    1241               0 : void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
    1242                 :                                           nsIContent* aContent,
    1243                 :                                           nsEventStates aStateMask)
    1244                 : {
    1245               0 :   if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
    1246               0 :     nsAccessible* item = GetAccessible(aContent);
    1247               0 :     if (item) {
    1248               0 :       nsAccessible* widget = item->ContainerWidget();
    1249               0 :       if (widget && widget->IsSelect()) {
    1250                 :         AccSelChangeEvent::SelChangeType selChangeType =
    1251               0 :           aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
    1252               0 :             AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
    1253                 :         nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
    1254               0 :                                                          selChangeType);
    1255               0 :         FireDelayedAccessibleEvent(event);
    1256                 :       }
    1257                 :     }
    1258                 :   }
    1259                 : 
    1260               0 :   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
    1261                 :     nsRefPtr<AccEvent> event =
    1262               0 :       new AccStateChangeEvent(aContent, states::INVALID, true);
    1263               0 :     FireDelayedAccessibleEvent(event);
    1264                 :    }
    1265               0 : }
    1266                 : 
    1267               0 : void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
    1268                 :                                             nsEventStates aStateMask)
    1269                 : {
    1270               0 : }
    1271                 : 
    1272               0 : void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
    1273                 :                                               nsIContent* aContent,
    1274                 :                                               CharacterDataChangeInfo* aInfo)
    1275                 : {
    1276               0 : }
    1277                 : 
    1278               0 : void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
    1279                 :                                            nsIContent* aContent,
    1280                 :                                            CharacterDataChangeInfo* aInfo)
    1281                 : {
    1282               0 : }
    1283                 : 
    1284                 : void
    1285               0 : nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
    1286                 :                                  nsIContent* aChild, PRInt32 /* unused */)
    1287                 : {
    1288               0 : }
    1289                 : 
    1290                 : void
    1291               0 : nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
    1292                 :                                 nsIContent* aChild, PRInt32 /* unused */,
    1293                 :                                 nsIContent* aPreviousSibling)
    1294                 : {
    1295               0 : }
    1296                 : 
    1297                 : void
    1298               0 : nsDocAccessible::ParentChainChanged(nsIContent *aContent)
    1299                 : {
    1300               0 : }
    1301                 : 
    1302                 : 
    1303                 : ////////////////////////////////////////////////////////////////////////////////
    1304                 : // nsAccessible
    1305                 : 
    1306                 : #ifdef DEBUG_ACCDOCMGR
    1307                 : nsresult
    1308                 : nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent)
    1309                 : {
    1310                 :   NS_LOG_ACCDOCLOAD_HANDLEEVENT(aAccEvent)
    1311                 : 
    1312                 :   return nsHyperTextAccessible::HandleAccEvent(aAccEvent);
    1313                 : 
    1314                 : }
    1315                 : #endif
    1316                 : 
    1317                 : ////////////////////////////////////////////////////////////////////////////////
    1318                 : // Public members
    1319                 : 
    1320                 : void*
    1321               0 : nsDocAccessible::GetNativeWindow() const
    1322                 : {
    1323               0 :   if (!mPresShell)
    1324               0 :     return nsnull;
    1325                 : 
    1326               0 :   nsIViewManager* vm = mPresShell->GetViewManager();
    1327               0 :   if (!vm)
    1328               0 :     return nsnull;
    1329                 : 
    1330               0 :   nsCOMPtr<nsIWidget> widget;
    1331               0 :   vm->GetRootWidget(getter_AddRefs(widget));
    1332               0 :   if (widget)
    1333               0 :     return widget->GetNativeData(NS_NATIVE_WINDOW);
    1334                 : 
    1335               0 :   return nsnull;
    1336                 : }
    1337                 : 
    1338                 : nsAccessible*
    1339               0 : nsDocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
    1340                 : {
    1341               0 :   nsAccessible* child = GetAccessibleByUniqueID(aUniqueID);
    1342               0 :   if (child)
    1343               0 :     return child;
    1344                 : 
    1345               0 :   PRUint32 childDocCount = mChildDocuments.Length();
    1346               0 :   for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
    1347               0 :     nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
    1348               0 :     child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
    1349               0 :     if (child)
    1350               0 :       return child;
    1351                 :   }
    1352                 : 
    1353               0 :   return nsnull;
    1354                 : }
    1355                 : 
    1356                 : nsAccessible*
    1357               0 : nsDocAccessible::GetAccessibleOrContainer(nsINode* aNode)
    1358                 : {
    1359               0 :   if (!aNode || !aNode->IsInDoc())
    1360               0 :     return nsnull;
    1361                 : 
    1362               0 :   nsINode* currNode = aNode;
    1363               0 :   nsAccessible* accessible = nsnull;
    1364               0 :   while (!(accessible = GetAccessible(currNode)) &&
    1365                 :          (currNode = currNode->GetNodeParent()));
    1366                 : 
    1367               0 :   return accessible;
    1368                 : }
    1369                 : 
    1370                 : bool
    1371               0 : nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
    1372                 :                                 nsRoleMapEntry* aRoleMapEntry)
    1373                 : {
    1374               0 :   if (!aAccessible)
    1375               0 :     return false;
    1376                 : 
    1377                 :   // Put into DOM node cache.
    1378               0 :   if (aAccessible->IsPrimaryForNode() &&
    1379               0 :       !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
    1380               0 :     return false;
    1381                 : 
    1382                 :   // Put into unique ID cache.
    1383               0 :   if (!mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible)) {
    1384               0 :     if (aAccessible->IsPrimaryForNode())
    1385               0 :       mNodeToAccessibleMap.Remove(aAccessible->GetNode());
    1386                 : 
    1387               0 :     return false;
    1388                 :   }
    1389                 : 
    1390                 :   // Initialize the accessible.
    1391               0 :   if (!aAccessible->Init()) {
    1392               0 :     NS_ERROR("Failed to initialize an accessible!");
    1393                 : 
    1394               0 :     UnbindFromDocument(aAccessible);
    1395               0 :     return false;
    1396                 :   }
    1397                 : 
    1398               0 :   aAccessible->SetRoleMapEntry(aRoleMapEntry);
    1399               0 :   if (aAccessible->IsElement())
    1400               0 :     AddDependentIDsFor(aAccessible);
    1401                 : 
    1402               0 :   return true;
    1403                 : }
    1404                 : 
    1405                 : void
    1406               0 : nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
    1407                 : {
    1408               0 :   NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
    1409                 :                "Unbinding the unbound accessible!");
    1410                 : 
    1411                 :   // Fire focus event on accessible having DOM focus if active item was removed
    1412                 :   // from the tree.
    1413               0 :   if (FocusMgr()->IsActiveItem(aAccessible)) {
    1414               0 :     FocusMgr()->ActiveItemChanged(nsnull);
    1415                 :     A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("tree shutdown", aAccessible)
    1416                 :   }
    1417                 : 
    1418                 :   // Remove an accessible from node-to-accessible map if it exists there.
    1419               0 :   if (aAccessible->IsPrimaryForNode() &&
    1420               0 :       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
    1421               0 :     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
    1422                 : 
    1423               0 :   void* uniqueID = aAccessible->UniqueID();
    1424                 : 
    1425               0 :   NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
    1426               0 :   aAccessible->Shutdown();
    1427                 : 
    1428               0 :   mAccessibleCache.Remove(uniqueID);
    1429               0 : }
    1430                 : 
    1431                 : void
    1432               0 : nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
    1433                 :                                  nsIContent* aStartChildNode,
    1434                 :                                  nsIContent* aEndChildNode)
    1435                 : {
    1436                 :   // Ignore content insertions until we constructed accessible tree. Otherwise
    1437                 :   // schedule tree update on content insertion after layout.
    1438               0 :   if (mNotificationController && HasLoadState(eTreeConstructed)) {
    1439                 :     // Update the whole tree of this document accessible when the container is
    1440                 :     // null (document element is inserted or removed).
    1441                 :     nsAccessible* container = aContainerNode ?
    1442               0 :       GetAccessibleOrContainer(aContainerNode) : this;
    1443                 : 
    1444                 :     mNotificationController->ScheduleContentInsertion(container,
    1445                 :                                                       aStartChildNode,
    1446               0 :                                                       aEndChildNode);
    1447                 :   }
    1448               0 : }
    1449                 : 
    1450                 : void
    1451               0 : nsDocAccessible::ContentRemoved(nsIContent* aContainerNode,
    1452                 :                                 nsIContent* aChildNode)
    1453                 : {
    1454                 :   // Update the whole tree of this document accessible when the container is
    1455                 :   // null (document element is removed).
    1456                 :   nsAccessible* container = aContainerNode ?
    1457               0 :     GetAccessibleOrContainer(aContainerNode) : this;
    1458                 : 
    1459               0 :   UpdateTree(container, aChildNode, false);
    1460               0 : }
    1461                 : 
    1462                 : void
    1463               0 : nsDocAccessible::RecreateAccessible(nsIContent* aContent)
    1464                 : {
    1465                 :   // XXX: we shouldn't recreate whole accessible subtree, instead we should
    1466                 :   // subclass hide and show events to handle them separately and implement their
    1467                 :   // coalescence with normal hide and show events. Note, in this case they
    1468                 :   // should be coalesced with normal show/hide events.
    1469                 : 
    1470                 :   // Check if the node is in accessible document.
    1471               0 :   nsAccessible* container = GetContainerAccessible(aContent);
    1472               0 :   if (container) {
    1473                 :     // Remove and reinsert.
    1474               0 :     UpdateTree(container, aContent, false);
    1475               0 :     container->UpdateChildren();
    1476               0 :     UpdateTree(container, aContent, true);
    1477                 :   }
    1478               0 : }
    1479                 : 
    1480                 : void
    1481               0 : nsDocAccessible::ProcessInvalidationList()
    1482                 : {
    1483                 :   // Invalidate children of container accessible for each element in
    1484                 :   // invalidation list. Allow invalidation list insertions while container
    1485                 :   // children are recached.
    1486               0 :   for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
    1487               0 :     nsIContent* content = mInvalidationList[idx];
    1488               0 :     nsAccessible* accessible = GetAccessible(content);
    1489               0 :     if (!accessible) {
    1490               0 :       nsAccessible* container = GetContainerAccessible(content);
    1491               0 :       if (container) {
    1492               0 :         container->UpdateChildren();
    1493               0 :         accessible = GetAccessible(content);
    1494                 :       }
    1495                 :     }
    1496                 : 
    1497                 :     // Make sure the subtree is created.
    1498               0 :     if (accessible)
    1499               0 :       CacheChildrenInSubtree(accessible);
    1500                 :   }
    1501                 : 
    1502               0 :   mInvalidationList.Clear();
    1503               0 : }
    1504                 : 
    1505                 : ////////////////////////////////////////////////////////////////////////////////
    1506                 : // nsAccessible protected
    1507                 : 
    1508                 : void
    1509               0 : nsDocAccessible::CacheChildren()
    1510                 : {
    1511                 :   // Search for accessible children starting from the document element since
    1512                 :   // some web pages tend to insert elements under it rather than document body.
    1513               0 :   nsAccTreeWalker walker(this, mDocument->GetRootElement(),
    1514               0 :                          CanHaveAnonChildren());
    1515                 : 
    1516               0 :   nsAccessible* child = nsnull;
    1517               0 :   while ((child = walker.NextChild()) && AppendChild(child));
    1518               0 : }
    1519                 : 
    1520                 : ////////////////////////////////////////////////////////////////////////////////
    1521                 : // Protected members
    1522                 : 
    1523                 : void
    1524               0 : nsDocAccessible::NotifyOfLoading(bool aIsReloading)
    1525                 : {
    1526                 :   // Mark the document accessible as loading, if it stays alive then we'll mark
    1527                 :   // it as loaded when we receive proper notification.
    1528               0 :   mLoadState &= ~eDOMLoaded;
    1529                 : 
    1530               0 :   if (!IsLoadEventTarget())
    1531               0 :     return;
    1532                 : 
    1533               0 :   if (aIsReloading) {
    1534                 :     // Fire reload and state busy events on existing document accessible while
    1535                 :     // event from user input flag can be calculated properly and accessible
    1536                 :     // is alive. When new document gets loaded then this one is destroyed.
    1537                 :     nsRefPtr<AccEvent> reloadEvent =
    1538               0 :       new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
    1539               0 :     nsEventShell::FireEvent(reloadEvent);
    1540                 :   }
    1541                 : 
    1542                 :   // Fire state busy change event. Use delayed event since we don't care
    1543                 :   // actually if event isn't delivered when the document goes away like a shot.
    1544                 :   nsRefPtr<AccEvent> stateEvent =
    1545               0 :     new AccStateChangeEvent(mDocument, states::BUSY, true);
    1546               0 :   FireDelayedAccessibleEvent(stateEvent);
    1547                 : }
    1548                 : 
    1549                 : void
    1550               0 : nsDocAccessible::DoInitialUpdate()
    1551                 : {
    1552               0 :   mLoadState |= eTreeConstructed;
    1553                 : 
    1554                 :   // The content element may be changed before the initial update and then we
    1555                 :   // miss the notification (since content tree change notifications are ignored
    1556                 :   // prior to initial update). Make sure the content element is valid.
    1557               0 :   nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocument);
    1558               0 :   if (contentElm && mContent != contentElm)
    1559               0 :     mContent = contentElm;
    1560                 : 
    1561                 :   // Build initial tree.
    1562               0 :   CacheChildrenInSubtree(this);
    1563                 : 
    1564                 :   // Fire reorder event after the document tree is constructed. Note, since
    1565                 :   // this reorder event is processed by parent document then events targeted to
    1566                 :   // this document may be fired prior to this reorder event. If this is
    1567                 :   // a problem then consider to keep event processing per tab document.
    1568               0 :   if (!IsRoot()) {
    1569                 :     nsRefPtr<AccEvent> reorderEvent =
    1570               0 :       new AccEvent(nsIAccessibleEvent::EVENT_REORDER, Parent(), eAutoDetect,
    1571               0 :                    AccEvent::eCoalesceFromSameSubtree);
    1572               0 :     ParentDocument()->FireDelayedAccessibleEvent(reorderEvent);
    1573                 :   }
    1574               0 : }
    1575                 : 
    1576                 : void
    1577               0 : nsDocAccessible::ProcessLoad()
    1578                 : {
    1579               0 :   mLoadState |= eCompletelyLoaded;
    1580                 : 
    1581                 :   // Do not fire document complete/stop events for root chrome document
    1582                 :   // accessibles and for frame/iframe documents because
    1583                 :   // a) screen readers start working on focus event in the case of root chrome
    1584                 :   // documents
    1585                 :   // b) document load event on sub documents causes screen readers to act is if
    1586                 :   // entire page is reloaded.
    1587               0 :   if (!IsLoadEventTarget())
    1588               0 :     return;
    1589                 : 
    1590                 :   // Fire complete/load stopped if the load event type is given.
    1591               0 :   if (mLoadEventType) {
    1592               0 :     nsRefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
    1593               0 :     nsEventShell::FireEvent(loadEvent);
    1594                 : 
    1595               0 :     mLoadEventType = 0;
    1596                 :   }
    1597                 : 
    1598                 :   // Fire busy state change event.
    1599                 :   nsRefPtr<AccEvent> stateEvent =
    1600               0 :     new AccStateChangeEvent(this, states::BUSY, false);
    1601               0 :   nsEventShell::FireEvent(stateEvent);
    1602                 : }
    1603                 : 
    1604                 : void
    1605               0 : nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
    1606                 :                                     nsIAtom* aRelAttr)
    1607                 : {
    1608               0 :   for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
    1609               0 :     nsIAtom* relAttr = *kRelationAttrs[idx];
    1610               0 :     if (aRelAttr && aRelAttr != relAttr)
    1611               0 :       continue;
    1612                 : 
    1613               0 :     if (relAttr == nsGkAtoms::_for) {
    1614               0 :       if (!aRelProvider->GetContent()->IsHTML() ||
    1615               0 :           (aRelProvider->GetContent()->Tag() != nsGkAtoms::label &&
    1616               0 :            aRelProvider->GetContent()->Tag() != nsGkAtoms::output))
    1617               0 :         continue;
    1618                 : 
    1619               0 :     } else if (relAttr == nsGkAtoms::control) {
    1620               0 :       if (!aRelProvider->GetContent()->IsXUL() ||
    1621               0 :           (aRelProvider->GetContent()->Tag() != nsGkAtoms::label &&
    1622               0 :            aRelProvider->GetContent()->Tag() != nsGkAtoms::description))
    1623               0 :         continue;
    1624                 :     }
    1625                 : 
    1626               0 :     IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
    1627               0 :     while (true) {
    1628               0 :       const nsDependentSubstring id = iter.NextID();
    1629               0 :       if (id.IsEmpty())
    1630                 :         break;
    1631                 : 
    1632               0 :       AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
    1633               0 :       if (!providers) {
    1634               0 :         providers = new AttrRelProviderArray();
    1635               0 :         if (providers) {
    1636               0 :           if (!mDependentIDsHash.Put(id, providers)) {
    1637               0 :             delete providers;
    1638               0 :             providers = nsnull;
    1639                 :           }
    1640                 :         }
    1641                 :       }
    1642                 : 
    1643               0 :       if (providers) {
    1644                 :         AttrRelProvider* provider =
    1645               0 :           new AttrRelProvider(relAttr, aRelProvider->GetContent());
    1646               0 :         if (provider) {
    1647               0 :           providers->AppendElement(provider);
    1648                 : 
    1649                 :           // We've got here during the children caching. If the referenced
    1650                 :           // content is not accessible then store it to pend its container
    1651                 :           // children invalidation (this happens immediately after the caching
    1652                 :           // is finished).
    1653               0 :           nsIContent* dependentContent = iter.GetElem(id);
    1654               0 :           if (dependentContent && !HasAccessible(dependentContent)) {
    1655               0 :             mInvalidationList.AppendElement(dependentContent);
    1656                 :           }
    1657                 :         }
    1658                 :       }
    1659                 :     }
    1660                 : 
    1661                 :     // If the relation attribute is given then we don't have anything else to
    1662                 :     // check.
    1663               0 :     if (aRelAttr)
    1664                 :       break;
    1665                 :   }
    1666               0 : }
    1667                 : 
    1668                 : void
    1669               0 : nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
    1670                 :                                        nsIAtom* aRelAttr)
    1671                 : {
    1672               0 :   for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
    1673               0 :     nsIAtom* relAttr = *kRelationAttrs[idx];
    1674               0 :     if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
    1675               0 :       continue;
    1676                 : 
    1677               0 :     IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
    1678               0 :     while (true) {
    1679               0 :       const nsDependentSubstring id = iter.NextID();
    1680               0 :       if (id.IsEmpty())
    1681                 :         break;
    1682                 : 
    1683               0 :       AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
    1684               0 :       if (providers) {
    1685               0 :         for (PRUint32 jdx = 0; jdx < providers->Length(); ) {
    1686               0 :           AttrRelProvider* provider = (*providers)[jdx];
    1687               0 :           if (provider->mRelAttr == relAttr &&
    1688               0 :               provider->mContent == aRelProvider->GetContent())
    1689               0 :             providers->RemoveElement(provider);
    1690                 :           else
    1691               0 :             jdx++;
    1692                 :         }
    1693               0 :         if (providers->Length() == 0)
    1694               0 :           mDependentIDsHash.Remove(id);
    1695                 :       }
    1696                 :     }
    1697                 : 
    1698                 :     // If the relation attribute is given then we don't have anything else to
    1699                 :     // check.
    1700               0 :     if (aRelAttr)
    1701                 :       break;
    1702                 :   }
    1703               0 : }
    1704                 : 
    1705                 : bool
    1706               0 : nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
    1707                 :                                               nsIAtom* aAttribute)
    1708                 : {
    1709               0 :   if (aAttribute == nsGkAtoms::role) {
    1710                 :     // It is common for js libraries to set the role on the body element after
    1711                 :     // the document has loaded. In this case we just update the role map entry.
    1712               0 :     if (mContent == aElement) {
    1713               0 :       SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aElement));
    1714               0 :       return true;
    1715                 :     }
    1716                 : 
    1717                 :     // Recreate the accessible when role is changed because we might require a
    1718                 :     // different accessible class for the new role or the accessible may expose
    1719                 :     // a different sets of interfaces (COM restriction).
    1720                 :     HandleNotification<nsDocAccessible, nsIContent>
    1721               0 :       (this, &nsDocAccessible::RecreateAccessible, aElement);
    1722                 : 
    1723               0 :     return true;
    1724                 :   }
    1725                 : 
    1726               0 :   if (aAttribute == nsGkAtoms::href ||
    1727                 :       aAttribute == nsGkAtoms::onclick) {
    1728                 :     // Not worth the expense to ensure which namespace these are in. It doesn't
    1729                 :     // kill use to recreate the accessible even if the attribute was used in
    1730                 :     // the wrong namespace or an element that doesn't support it.
    1731                 : 
    1732                 :     // Recreate accessible asynchronously to allow the content to handle
    1733                 :     // the attribute change.
    1734                 :     mNotificationController->ScheduleNotification<nsDocAccessible, nsIContent>
    1735               0 :       (this, &nsDocAccessible::RecreateAccessible, aElement);
    1736                 : 
    1737               0 :     return true;
    1738                 :   }
    1739                 : 
    1740               0 :   if (aAttribute == nsGkAtoms::aria_multiselectable &&
    1741               0 :       aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
    1742                 :     // This affects whether the accessible supports SelectAccessible.
    1743                 :     // COM says we cannot change what interfaces are supported on-the-fly,
    1744                 :     // so invalidate this object. A new one will be created on demand.
    1745                 :     HandleNotification<nsDocAccessible, nsIContent>
    1746               0 :       (this, &nsDocAccessible::RecreateAccessible, aElement);
    1747                 : 
    1748               0 :     return true;
    1749                 :   }
    1750                 : 
    1751               0 :   return false;
    1752                 : }
    1753                 : 
    1754                 : // nsDocAccessible public member
    1755                 : nsresult
    1756               0 : nsDocAccessible::FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
    1757                 :                                             AccEvent::EEventRule aAllowDupes,
    1758                 :                                             EIsFromUserInput aIsFromUserInput)
    1759                 : {
    1760                 :   nsRefPtr<AccEvent> event =
    1761               0 :     new AccEvent(aEventType, aNode, aIsFromUserInput, aAllowDupes);
    1762               0 :   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
    1763                 : 
    1764               0 :   return FireDelayedAccessibleEvent(event);
    1765                 : }
    1766                 : 
    1767                 : // nsDocAccessible public member
    1768                 : nsresult
    1769               0 : nsDocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent)
    1770                 : {
    1771               0 :   NS_ENSURE_ARG(aEvent);
    1772                 :   NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)
    1773                 : 
    1774               0 :   if (mNotificationController)
    1775               0 :     mNotificationController->QueueEvent(aEvent);
    1776                 : 
    1777               0 :   return NS_OK;
    1778                 : }
    1779                 : 
    1780                 : void
    1781               0 : nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
    1782                 : {
    1783               0 :   PRUint32 eventType = aEvent->GetEventType();
    1784               0 :   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
    1785               0 :     nsHyperTextAccessible* hyperText = aEvent->GetAccessible()->AsHyperText();
    1786                 :     PRInt32 caretOffset;
    1787               0 :     if (hyperText &&
    1788               0 :         NS_SUCCEEDED(hyperText->GetCaretOffset(&caretOffset))) {
    1789                 : #ifdef DEBUG_A11Y
    1790                 :       PRUnichar chAtOffset;
    1791                 :       hyperText->GetCharacterAtOffset(caretOffset, &chAtOffset);
    1792                 :       printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
    1793                 : #endif
    1794                 :       nsRefPtr<AccEvent> caretMoveEvent =
    1795               0 :         new AccCaretMoveEvent(hyperText, caretOffset);
    1796               0 :       nsEventShell::FireEvent(caretMoveEvent);
    1797                 : 
    1798                 :       PRInt32 selectionCount;
    1799               0 :       hyperText->GetSelectionCount(&selectionCount);
    1800               0 :       if (selectionCount) {  // There's a selection so fire selection change as well
    1801                 :         nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
    1802               0 :                                 hyperText);
    1803                 :       }
    1804                 :     }
    1805                 :   }
    1806                 :   else {
    1807               0 :     nsEventShell::FireEvent(aEvent);
    1808                 : 
    1809                 :     // Post event processing
    1810               0 :     if (eventType == nsIAccessibleEvent::EVENT_HIDE)
    1811               0 :       ShutdownChildrenInSubtree(aEvent->GetAccessible());
    1812                 :   }
    1813               0 : }
    1814                 : 
    1815                 : void
    1816               0 : nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
    1817                 :                                         const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
    1818                 : {
    1819                 :   // Process the notification if the container accessible is still in tree.
    1820               0 :   if (!HasAccessible(aContainer->GetNode()))
    1821               0 :     return;
    1822                 : 
    1823               0 :   if (aContainer == this) {
    1824                 :     // If new root content has been inserted then update it.
    1825               0 :     nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
    1826               0 :     if (rootContent && rootContent != mContent)
    1827               0 :       mContent = rootContent;
    1828                 : 
    1829                 :     // Continue to update the tree even if we don't have root content.
    1830                 :     // For example, elements may be inserted under the document element while
    1831                 :     // there is no HTML body element.
    1832                 :   }
    1833                 : 
    1834                 :   // XXX: Invalidate parent-child relations for container accessible and its
    1835                 :   // children because there's no good way to find insertion point of new child
    1836                 :   // accessibles into accessible tree. We need to invalidate children even
    1837                 :   // there's no inserted accessibles in the end because accessible children
    1838                 :   // are created while parent recaches child accessibles.
    1839               0 :   aContainer->UpdateChildren();
    1840                 : 
    1841                 :   // The container might be changed, for example, because of the subsequent
    1842                 :   // overlapping content insertion (i.e. other content was inserted between this
    1843                 :   // inserted content and its container or the content was reinserted into
    1844                 :   // different container of unrelated part of tree). These cases result in
    1845                 :   // double processing, however generated events are coalesced and we don't
    1846                 :   // harm an AT.
    1847                 :   // Theoretically the element might be not in tree at all at this point what
    1848                 :   // means there's no container.
    1849               0 :   for (PRUint32 idx = 0; idx < aInsertedContent->Length(); idx++) {
    1850                 :     nsAccessible* directContainer =
    1851               0 :       GetContainerAccessible(aInsertedContent->ElementAt(idx));
    1852               0 :     if (directContainer)
    1853               0 :       UpdateTree(directContainer, aInsertedContent->ElementAt(idx), true);
    1854                 :   }
    1855                 : }
    1856                 : 
    1857                 : void
    1858               0 : nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
    1859                 :                             bool aIsInsert)
    1860                 : {
    1861               0 :   PRUint32 updateFlags = eNoAccessible;
    1862                 : 
    1863                 :   // If child node is not accessible then look for its accessible children.
    1864               0 :   nsAccessible* child = GetAccessible(aChildNode);
    1865               0 :   if (child) {
    1866               0 :     updateFlags |= UpdateTreeInternal(child, aIsInsert);
    1867                 : 
    1868                 :   } else {
    1869                 :     nsAccTreeWalker walker(this, aChildNode,
    1870               0 :                            aContainer->CanHaveAnonChildren(), true);
    1871                 : 
    1872               0 :     while ((child = walker.NextChild()))
    1873               0 :       updateFlags |= UpdateTreeInternal(child, aIsInsert);
    1874                 :   }
    1875                 : 
    1876                 :   // Content insertion/removal is not cause of accessible tree change.
    1877               0 :   if (updateFlags == eNoAccessible)
    1878               0 :     return;
    1879                 : 
    1880                 :   // Check to see if change occurred inside an alert, and fire an EVENT_ALERT
    1881                 :   // if it did.
    1882               0 :   if (aIsInsert && !(updateFlags & eAlertAccessible)) {
    1883                 :     // XXX: tree traversal is perf issue, accessible should know if they are
    1884                 :     // children of alert accessible to avoid this.
    1885               0 :     nsAccessible* ancestor = aContainer;
    1886               0 :     while (ancestor) {
    1887               0 :       if (ancestor->ARIARole() == roles::ALERT) {
    1888                 :         FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
    1889               0 :                                    ancestor->GetNode());
    1890               0 :         break;
    1891                 :       }
    1892                 : 
    1893                 :       // Don't climb above this document.
    1894               0 :       if (ancestor == this)
    1895               0 :         break;
    1896                 : 
    1897               0 :       ancestor = ancestor->Parent();
    1898                 :     }
    1899                 :   }
    1900                 : 
    1901               0 :   MaybeNotifyOfValueChange(aContainer);
    1902                 : 
    1903                 :   // Fire reorder event so the MSAA clients know the children have changed. Also
    1904                 :   // the event is used internally by MSAA layer.
    1905                 :   nsRefPtr<AccEvent> reorderEvent =
    1906               0 :     new AccEvent(nsIAccessibleEvent::EVENT_REORDER, aContainer->GetNode(),
    1907               0 :                  eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
    1908               0 :   if (reorderEvent)
    1909               0 :     FireDelayedAccessibleEvent(reorderEvent);
    1910                 : }
    1911                 : 
    1912                 : PRUint32
    1913               0 : nsDocAccessible::UpdateTreeInternal(nsAccessible* aChild, bool aIsInsert)
    1914                 : {
    1915               0 :   PRUint32 updateFlags = eAccessible;
    1916                 : 
    1917               0 :   nsINode* node = aChild->GetNode();
    1918               0 :   if (aIsInsert) {
    1919                 :     // Create accessible tree for shown accessible.
    1920               0 :     CacheChildrenInSubtree(aChild);
    1921                 : 
    1922                 :   } else {
    1923                 :     // Fire menupopup end event before hide event if a menu goes away.
    1924                 : 
    1925                 :     // XXX: We don't look into children of hidden subtree to find hiding
    1926                 :     // menupopup (as we did prior bug 570275) because we don't do that when
    1927                 :     // menu is showing (and that's impossible until bug 606924 is fixed).
    1928                 :     // Nevertheless we should do this at least because layout coalesces
    1929                 :     // the changes before our processing and we may miss some menupopup
    1930                 :     // events. Now we just want to be consistent in content insertion/removal
    1931                 :     // handling.
    1932               0 :     if (aChild->ARIARole() == roles::MENUPOPUP) {
    1933                 :       nsRefPtr<AccEvent> event =
    1934               0 :         new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
    1935                 : 
    1936               0 :       if (event)
    1937               0 :         FireDelayedAccessibleEvent(event);
    1938                 :     }
    1939                 :   }
    1940                 : 
    1941                 :   // Fire show/hide event.
    1942               0 :   nsRefPtr<AccEvent> event;
    1943               0 :   if (aIsInsert)
    1944               0 :     event = new AccShowEvent(aChild, node);
    1945                 :   else
    1946               0 :     event = new AccHideEvent(aChild, node);
    1947                 : 
    1948               0 :   if (event)
    1949               0 :     FireDelayedAccessibleEvent(event);
    1950                 : 
    1951               0 :   if (aIsInsert) {
    1952               0 :     roles::Role ariaRole = aChild->ARIARole();
    1953               0 :     if (ariaRole == roles::MENUPOPUP) {
    1954                 :       // Fire EVENT_MENUPOPUP_START if ARIA menu appears.
    1955                 :       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
    1956               0 :                                  node, AccEvent::eRemoveDupes);
    1957                 : 
    1958               0 :     } else if (ariaRole == roles::ALERT) {
    1959                 :       // Fire EVENT_ALERT if ARIA alert appears.
    1960               0 :       updateFlags = eAlertAccessible;
    1961                 :       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
    1962               0 :                                  AccEvent::eRemoveDupes);
    1963                 :     }
    1964                 : 
    1965                 :     // If focused node has been shown then it means its frame was recreated
    1966                 :     // while it's focused. Fire focus event on new focused accessible. If
    1967                 :     // the queue contains focus event for this node then it's suppressed by
    1968                 :     // this one.
    1969                 :     // XXX: do we really want to send focus to focused DOM node not taking into
    1970                 :     // account active item?
    1971               0 :     if (FocusMgr()->IsFocused(aChild))
    1972               0 :       FocusMgr()->DispatchFocusEvent(this, aChild);
    1973                 : 
    1974                 :   } else {
    1975                 :     // Update the tree for content removal.
    1976                 :     // The accessible parent may differ from container accessible if
    1977                 :     // the parent doesn't have own DOM node like list accessible for HTML
    1978                 :     // selects.
    1979               0 :     nsAccessible* parent = aChild->Parent();
    1980               0 :     NS_ASSERTION(parent, "No accessible parent?!");
    1981               0 :     if (parent)
    1982               0 :       parent->RemoveChild(aChild);
    1983                 : 
    1984               0 :     UncacheChildrenInSubtree(aChild);
    1985                 :   }
    1986                 : 
    1987               0 :   return updateFlags;
    1988                 : }
    1989                 : 
    1990                 : void
    1991               0 : nsDocAccessible::CacheChildrenInSubtree(nsAccessible* aRoot)
    1992                 : {
    1993               0 :   aRoot->EnsureChildren();
    1994                 : 
    1995                 :   // Make sure we create accessible tree defined in DOM only, i.e. if accessible
    1996                 :   // provides specific tree (like XUL trees) then tree creation is handled by
    1997                 :   // this accessible.
    1998               0 :   PRUint32 count = aRoot->ContentChildCount();
    1999               0 :   for (PRUint32 idx = 0; idx < count; idx++) {
    2000               0 :     nsAccessible* child = aRoot->ContentChildAt(idx);
    2001               0 :     NS_ASSERTION(child, "Illicit tree change while tree is created!");
    2002                 :     // Don't cross document boundaries.
    2003               0 :     if (child && child->IsContent())
    2004               0 :       CacheChildrenInSubtree(child);
    2005                 :   }
    2006               0 : }
    2007                 : 
    2008                 : void
    2009               0 : nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot)
    2010                 : {
    2011               0 :   if (aRoot->IsElement())
    2012               0 :     RemoveDependentIDsFor(aRoot);
    2013                 : 
    2014               0 :   PRUint32 count = aRoot->ContentChildCount();
    2015               0 :   for (PRUint32 idx = 0; idx < count; idx++)
    2016               0 :     UncacheChildrenInSubtree(aRoot->ContentChildAt(idx));
    2017                 : 
    2018               0 :   if (aRoot->IsPrimaryForNode() &&
    2019               0 :       mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
    2020               0 :     mNodeToAccessibleMap.Remove(aRoot->GetNode());
    2021               0 : }
    2022                 : 
    2023                 : void
    2024               0 : nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
    2025                 : {
    2026                 :   // Traverse through children and shutdown them before this accessible. When
    2027                 :   // child gets shutdown then it removes itself from children array of its
    2028                 :   //parent. Use jdx index to process the cases if child is not attached to the
    2029                 :   // parent and as result doesn't remove itself from its children.
    2030               0 :   PRUint32 count = aAccessible->ContentChildCount();
    2031               0 :   for (PRUint32 idx = 0, jdx = 0; idx < count; idx++) {
    2032               0 :     nsAccessible* child = aAccessible->ContentChildAt(jdx);
    2033               0 :     if (!child->IsBoundToParent()) {
    2034               0 :       NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
    2035               0 :       jdx++;
    2036                 :     }
    2037                 : 
    2038               0 :     ShutdownChildrenInSubtree(child);
    2039                 :   }
    2040                 : 
    2041               0 :   UnbindFromDocument(aAccessible);
    2042               0 : }
    2043                 : 
    2044                 : bool
    2045               0 : nsDocAccessible::IsLoadEventTarget() const
    2046                 : {
    2047               0 :   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
    2048                 :   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
    2049               0 :     do_QueryInterface(container);
    2050               0 :   NS_ASSERTION(docShellTreeItem, "No document shell for document!");
    2051                 : 
    2052               0 :   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
    2053               0 :   docShellTreeItem->GetParent(getter_AddRefs(parentTreeItem));
    2054                 : 
    2055                 :   // It's not a root document.
    2056               0 :   if (parentTreeItem) {
    2057               0 :     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
    2058               0 :     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
    2059                 : 
    2060                 :     // It's not a sub document, i.e. a frame or iframe.
    2061               0 :     return (sameTypeRoot == docShellTreeItem);
    2062                 :   }
    2063                 : 
    2064                 :   // It's not chrome root document.
    2065                 :   PRInt32 contentType;
    2066               0 :   docShellTreeItem->GetItemType(&contentType);
    2067               0 :   return (contentType == nsIDocShellTreeItem::typeContent);
    2068            4392 : }
    2069                 : 

Generated by: LCOV version 1.7