Back to index

lightning-sunbird  0.9+nobinonly
nsDocAccessible.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: Aaron Leventhal (aaronl@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsDocAccessible.h"
00040 #include "nsAccessibilityAtoms.h"
00041 #include "nsAccessibleEventData.h"
00042 #include "nsIAccessibilityService.h"
00043 #include "nsArray.h"
00044 #include "nsICommandManager.h"
00045 #include "nsIDocShell.h"
00046 #include "nsIDocShellTreeItem.h"
00047 #include "nsIDocument.h"
00048 #include "nsIDOMAttr.h"
00049 #include "nsIDOMCharacterData.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMEventTarget.h"
00052 #include "nsIDOMEvent.h"
00053 #include "nsIDOMDocument.h"
00054 #include "nsIDOMDocumentType.h"
00055 #include "nsIDOMNSDocument.h"
00056 #include "nsIDOMNSHTMLDocument.h"
00057 #include "nsIDOMMutationEvent.h"
00058 #include "nsIDOMWindow.h"
00059 #include "nsIDOMXULPopupElement.h"
00060 #include "nsIEditingSession.h"
00061 #include "nsIEventStateManager.h"
00062 #include "nsIFrame.h"
00063 #include "nsHTMLSelectAccessible.h"
00064 #include "nsIInterfaceRequestorUtils.h"
00065 #include "nsINameSpaceManager.h"
00066 #include "nsIObserverService.h"
00067 #include "nsIPlaintextEditor.h"
00068 #include "nsIPresShell.h"
00069 #include "nsIScriptGlobalObject.h"
00070 #include "nsIServiceManager.h"
00071 #include "nsIScrollableView.h"
00072 #include "nsUnicharUtils.h"
00073 #include "nsIURI.h"
00074 #include "nsIWebNavigation.h"
00075 #ifdef MOZ_XUL
00076 #include "nsIXULDocument.h"
00077 #endif
00078 
00079 //=============================//
00080 // nsDocAccessible  //
00081 //=============================//
00082 
00083 //-----------------------------------------------------
00084 // construction 
00085 //-----------------------------------------------------
00086 nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
00087   nsBlockAccessible(aDOMNode, aShell), mWnd(nsnull),
00088   mEditor(nsnull), mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE)
00089 {
00090   // Because of the way document loading happens, the new nsIWidget is created before
00091   // the old one is removed. Since it creates the nsDocAccessible, for a brief moment 
00092   // there can be 2 nsDocAccessible's for the content area, although for 2 different
00093   // pres shells.
00094 
00095   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
00096   if (shell) {
00097     mDocument = shell->GetDocument();
00098     nsIViewManager* vm = shell->GetViewManager();
00099     if (vm) {
00100       nsCOMPtr<nsIWidget> widget;
00101       vm->GetWidget(getter_AddRefs(widget));
00102       if (widget) {
00103         mWnd = widget->GetNativeData(NS_NATIVE_WINDOW);
00104       }
00105     }
00106   }
00107   
00108   // XXX aaronl should we use an algorithm for the initial cache size?
00109   mAccessNodeCache.Init(kDefaultCacheSize);
00110 
00111   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
00112     GetDocShellTreeItemFor(mDOMNode);
00113   if (docShellTreeItem) {
00114     PRInt32 itemType;
00115     docShellTreeItem->GetItemType(&itemType);
00116     if (itemType == nsIDocShellTreeItem::typeChrome) {
00117       mIsContentLoaded = PR_TRUE;
00118     }
00119   }
00120 }
00121 
00122 //-----------------------------------------------------
00123 // destruction
00124 //-----------------------------------------------------
00125 nsDocAccessible::~nsDocAccessible()
00126 {
00127 }
00128 
00129 NS_INTERFACE_MAP_BEGIN(nsDocAccessible)
00130   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
00131   NS_INTERFACE_MAP_ENTRY(nsPIAccessibleDocument)
00132   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
00133   NS_INTERFACE_MAP_ENTRY(nsIScrollPositionListener)
00134   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00135   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
00136   NS_INTERFACE_MAP_ENTRY(nsIObserver)
00137 NS_INTERFACE_MAP_END_INHERITING(nsBlockAccessible)
00138 
00139 NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsBlockAccessible)
00140 NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsBlockAccessible)
00141 
00142 NS_IMETHODIMP nsDocAccessible::GetName(nsAString& aName) 
00143 { 
00144   nsresult rv = NS_OK;
00145   aName.Truncate();
00146   if (mRoleMapEntry) {
00147     rv = nsAccessible::GetName(aName);
00148   }
00149   if (aName.IsEmpty()) {
00150     rv = GetTitle(aName);
00151   }
00152   if (aName.IsEmpty() && mParent) {
00153     rv = mParent->GetName(aName);
00154   }
00155 
00156   return rv;
00157 }
00158 
00159 NS_IMETHODIMP nsDocAccessible::GetRole(PRUint32 *aRole)
00160 {
00161   *aRole = ROLE_PANE; // Fall back
00162 
00163   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
00164     GetDocShellTreeItemFor(mDOMNode);
00165   if (docShellTreeItem) {
00166     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
00167     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
00168     if (sameTypeRoot == docShellTreeItem) {
00169       // Root of content or chrome tree
00170       PRInt32 itemType;
00171       docShellTreeItem->GetItemType(&itemType);
00172       if (itemType == nsIDocShellTreeItem::typeChrome) {
00173         *aRole = ROLE_APPLICATION;
00174       }
00175       else if (itemType == nsIDocShellTreeItem::typeContent) {
00176 #ifdef MOZ_XUL
00177         nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
00178         *aRole = xulDoc ? ROLE_APPLICATION : ROLE_DOCUMENT;
00179 #else
00180         *aRole = ROLE_DOCUMENT;
00181 #endif
00182       }
00183     }
00184   }
00185   
00186   return NS_OK;
00187 }
00188 
00189 NS_IMETHODIMP nsDocAccessible::GetValue(nsAString& aValue)
00190 {
00191   return GetURL(aValue);
00192 }
00193 
00194 NS_IMETHODIMP nsDocAccessible::GetState(PRUint32 *aState)
00195 {
00196   if (!mDOMNode) {
00197     return NS_ERROR_FAILURE;
00198   }
00199   nsAccessible::GetState(aState);
00200   *aState |= STATE_FOCUSABLE;
00201   
00202   if (!mIsContentLoaded) {
00203     *aState |= STATE_BUSY;
00204   }
00205 
00206   // Is it visible?
00207   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
00208   nsCOMPtr<nsIWidget> widget;
00209   if (shell) {
00210     nsIViewManager* vm = shell->GetViewManager();
00211     if (vm) {
00212       vm->GetWidget(getter_AddRefs(widget));
00213     }
00214   }
00215   PRBool isVisible = (widget != nsnull);
00216   while (widget && isVisible) {
00217     widget->IsVisible(isVisible);
00218     widget = widget->GetParent();
00219   }
00220   if (!isVisible) {
00221     *aState |= STATE_INVISIBLE;
00222   }
00223 
00224   PRBool isEditable;
00225   GetIsEditable(&isEditable);
00226 
00227   if (!isEditable) {
00228     // Use STATE_READONLY when we're not in an editor pane
00229     *aState |= STATE_READONLY; 
00230   }
00231   return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP nsDocAccessible::GetFocusedChild(nsIAccessible **aFocusedChild) 
00235 {
00236   if (!gLastFocusedNode) {
00237     *aFocusedChild = nsnull;
00238     return NS_OK;
00239   }
00240 
00241   // Return an accessible for the current global focus, which does not have to
00242   // be contained within the current document.
00243   nsCOMPtr<nsIAccessibilityService> accService =
00244     do_GetService("@mozilla.org/accessibilityService;1");
00245   return accService->GetAccessibleFor(gLastFocusedNode, aFocusedChild);
00246 }
00247 
00248 // ------- nsIAccessibleDocument Methods (5) ---------------
00249 
00250 NS_IMETHODIMP nsDocAccessible::GetURL(nsAString& aURL)
00251 { 
00252   if (!mDocument) {
00253     return NS_ERROR_FAILURE; // Document has been shut down
00254   }
00255   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
00256   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
00257   nsCAutoString theURL;
00258   if (webNav) {
00259     nsCOMPtr<nsIURI> pURI;
00260     webNav->GetCurrentURI(getter_AddRefs(pURI));
00261     if (pURI) 
00262       pURI->GetSpec(theURL);
00263   }
00264   CopyUTF8toUTF16(theURL, aURL);
00265   return NS_OK;
00266 }
00267 
00268 NS_IMETHODIMP nsDocAccessible::GetTitle(nsAString& aTitle)
00269 {
00270   if (mDocument) {
00271     aTitle = mDocument->GetDocumentTitle();
00272     return NS_OK;
00273   }
00274 
00275   return NS_ERROR_FAILURE;
00276 }
00277 
00278 NS_IMETHODIMP nsDocAccessible::GetMimeType(nsAString& aMimeType)
00279 {
00280   nsCOMPtr<nsIDOMNSDocument> domnsDocument(do_QueryInterface(mDocument));
00281   if (domnsDocument) {
00282     return domnsDocument->GetContentType(aMimeType);
00283   }
00284   return NS_ERROR_FAILURE;
00285 }
00286 
00287 NS_IMETHODIMP nsDocAccessible::GetDocType(nsAString& aDocType)
00288 {
00289   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
00290   nsCOMPtr<nsIDOMDocumentType> docType;
00291 
00292 #ifdef MOZ_XUL
00293   nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
00294   if (xulDoc) {
00295     aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
00296     return NS_OK;
00297   } else
00298 #endif
00299   if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
00300     return docType->GetPublicId(aDocType);
00301   }
00302 
00303   return NS_ERROR_FAILURE;
00304 }
00305 
00306 NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAString& aNameSpaceURI)
00307 {
00308   if (mDocument) {
00309     nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
00310         do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
00311     if (nameSpaceManager) 
00312       return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
00313   }
00314   return NS_ERROR_FAILURE;
00315 }
00316 
00317 NS_IMETHODIMP nsDocAccessible::GetCaretAccessible(nsIAccessible **aCaretAccessible)
00318 {
00319   // We only have a caret accessible on the root document
00320   *aCaretAccessible = nsnull;
00321   return NS_OK;
00322 }
00323 
00324 NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
00325 {
00326   *aWindow = mWnd;
00327   return NS_OK;
00328 }
00329 
00330 NS_IMETHODIMP nsDocAccessible::GetWindow(nsIDOMWindow **aDOMWin)
00331 {
00332   *aDOMWin = nsnull;
00333   if (!mDocument) {
00334     return NS_ERROR_FAILURE;  // Accessible is Shutdown()
00335   }
00336   nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(mDocument->GetScriptGlobalObject()));
00337 
00338   if (!domWindow)
00339     return NS_ERROR_FAILURE;  // No DOM Window
00340 
00341   NS_ADDREF(*aDOMWin = domWindow);
00342 
00343   return NS_OK;
00344 }
00345 
00346 NS_IMETHODIMP nsDocAccessible::GetDocument(nsIDOMDocument **aDOMDoc)
00347 {
00348   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
00349   *aDOMDoc = domDoc;
00350 
00351   if (domDoc) {
00352     NS_ADDREF(*aDOMDoc);
00353     return NS_OK;
00354   }
00355 
00356   return NS_ERROR_FAILURE;
00357 }
00358 
00359 void nsDocAccessible::CheckForEditor()
00360 {
00361   if (mEditor) {
00362     return;  // Already have editor, don't need to check
00363   }
00364   if (!mDocument) {
00365     return;  // No document -- we've been shut down
00366   }
00367   nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(mDocument->GetScriptGlobalObject()));
00368   if (!domWindow)
00369     return;  // No DOM Window
00370 
00371   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
00372   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
00373   if (!editingSession)
00374     return; // No editing session interface
00375 
00376   editingSession->GetEditorForWindow(domWindow, getter_AddRefs(mEditor));
00377   if (mEditor) {
00378     // State readonly is now clear
00379 #ifdef MOZ_ACCESSIBILITY_ATK
00380     AtkStateChange stateData;
00381     stateData.enable = PR_TRUE;
00382     stateData.state = STATE_READONLY; // Will be translated to ATK_STATE_EDITABLE
00383     FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, &stateData);
00384 #else
00385     FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, nsnull);
00386 #endif
00387   }
00388 }
00389 
00390 NS_IMETHODIMP nsDocAccessible::GetIsEditable(PRBool *aIsEditable)
00391 {
00392   *aIsEditable = PR_FALSE;
00393   if (mEditor) {
00394     PRUint32 flags;
00395     mEditor->GetFlags(&flags);
00396     *aIsEditable = (flags & nsIPlaintextEditor::eEditorReadonlyMask) == 0;
00397   }
00398   return NS_OK;
00399 }
00400 
00401 NS_IMETHODIMP nsDocAccessible::GetCachedAccessNode(void *aUniqueID, nsIAccessNode **aAccessNode)
00402 {
00403   GetCacheEntry(mAccessNodeCache, aUniqueID, aAccessNode); // Addrefs for us
00404   return NS_OK;
00405 }
00406 
00407 NS_IMETHODIMP nsDocAccessible::CacheAccessNode(void *aUniqueID, nsIAccessNode *aAccessNode)
00408 {
00409   PutCacheEntry(mAccessNodeCache, aUniqueID, aAccessNode);
00410   return NS_OK;
00411 }
00412 
00413 NS_IMETHODIMP nsDocAccessible::GetParent(nsIAccessible **aParent)
00414 {
00415   // Hook up our new accessible with our parent
00416   if (!mParent) {
00417     nsIDocument *parentDoc = mDocument->GetParentDocument();
00418     if (parentDoc) {
00419       nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
00420       nsCOMPtr<nsIDOMNode> ownerNode(do_QueryInterface(ownerContent));
00421       if (ownerNode) {
00422         nsCOMPtr<nsIAccessibilityService> accService = 
00423           do_GetService("@mozilla.org/accessibilityService;1");
00424         if (accService) {
00425           // XXX aaronl: ideally we would traverse the presshell chain
00426           // Since there's no easy way to do that, we cheat and use
00427           // the document hierarchy. GetAccessibleFor() is bad because
00428           // it doesn't support our concept of multiple presshells per doc.
00429           // It should be changed to use GetAccessibleInWeakShell()
00430           accService->GetAccessibleFor(ownerNode, getter_AddRefs(mParent));
00431         }
00432       }
00433     }
00434   }
00435   return mParent ? nsAccessible::GetParent(aParent) : NS_ERROR_FAILURE;
00436 }
00437 
00438 NS_IMETHODIMP nsDocAccessible::Init()
00439 {
00440   PutCacheEntry(gGlobalDocAccessibleCache, mWeakShell, this);
00441 
00442   AddEventListeners();
00443 
00444   nsresult rv = nsBlockAccessible::Init();
00445 
00446   if (mRoleMapEntry && mRoleMapEntry->role != ROLE_DIALOG &&
00447       mRoleMapEntry->role != ROLE_APPLICATION &&
00448       mRoleMapEntry->role != ROLE_ALERT &&
00449       mRoleMapEntry->role != ROLE_DOCUMENT) {
00450     // Document accessible can only have certain roles
00451     // This was set in nsAccessible::Init() based on dynamic role attribute
00452     mRoleMapEntry = nsnull; // role attribute is not valid for a document
00453   }
00454 
00455   return rv;
00456 }  
00457 
00458 
00459 NS_IMETHODIMP nsDocAccessible::Destroy()
00460 {
00461   gGlobalDocAccessibleCache.Remove(NS_STATIC_CAST(void*, mWeakShell));
00462   return Shutdown();
00463 }
00464 
00465 NS_IMETHODIMP nsDocAccessible::Shutdown()
00466 {
00467   if (!mWeakShell) {
00468     return NS_OK;  // Already shutdown
00469   }
00470 
00471   RemoveEventListeners();
00472 
00473   mWeakShell = nsnull;  // Avoid reentrancy
00474 
00475   mEditor = nsnull;
00476 
00477   if (mFireEventTimer) {
00478     mFireEventTimer->Cancel();
00479     mFireEventTimer = nsnull;
00480   }
00481   mEventsToFire.Clear();
00482 
00483   ClearCache(mAccessNodeCache);
00484 
00485   mDocument = nsnull;
00486 
00487   return nsBlockAccessible::Shutdown();
00488 }
00489 
00490 nsIFrame* nsDocAccessible::GetFrame()
00491 {
00492   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
00493 
00494   nsIFrame* root = nsnull;
00495   if (shell) 
00496     root = shell->GetRootFrame();
00497 
00498   return root;
00499 }
00500 
00501 void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
00502 {
00503   *aRelativeFrame = GetFrame();
00504 
00505   nsIDocument *document = mDocument;
00506   nsIDocument *parentDoc = nsnull;
00507 
00508   while (document) {
00509     nsIPresShell *presShell = document->GetShellAt(0);
00510     if (!presShell) {
00511       return;
00512     }
00513     nsIViewManager* vm = presShell->GetViewManager();
00514     if (!vm) {
00515       return;
00516     }
00517 
00518     nsIScrollableView* scrollableView = nsnull;
00519     vm->GetRootScrollableView(&scrollableView);
00520 
00521     nsRect viewBounds(0, 0, 0, 0);
00522     if (scrollableView) {
00523       viewBounds = scrollableView->View()->GetBounds();
00524     }
00525     else {
00526       nsIView *view;
00527       vm->GetRootView(view);
00528       if (view) {
00529         viewBounds = view->GetBounds();
00530       }
00531     }
00532 
00533     if (parentDoc) {  // After first time thru loop
00534       aBounds.IntersectRect(viewBounds, aBounds);
00535     }
00536     else {  // First time through loop
00537       aBounds = viewBounds;
00538     }
00539 
00540     document = parentDoc = document->GetParentDocument();
00541   }
00542 }
00543 
00544 
00545 nsresult nsDocAccessible::AddEventListeners()
00546 {
00547   // 1) Set up scroll position listener
00548   // 2) Check for editor and listen for changes to editor
00549 
00550   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00551   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
00552 
00553   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
00554   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
00555   NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
00556 
00557   // Make sure we're a content docshell
00558   // We don't want to listen to chrome progress
00559   PRInt32 itemType;
00560   docShellTreeItem->GetItemType(&itemType);
00561 
00562   PRBool isContent = (itemType == nsIDocShellTreeItem::typeContent);
00563 
00564   if (isContent) {
00565     CheckForEditor();
00566   
00567     if (!mEditor) {
00568       // We're not an editor yet, but we might become one
00569       nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
00570       if (commandManager) {
00571         commandManager->AddCommandObserver(this, "obs_documentCreated");
00572       }
00573     }
00574   }
00575 
00576   // add document observer
00577   mDocument->AddObserver(this);
00578   return NS_OK;
00579 }
00580 
00581 nsresult nsDocAccessible::RemoveEventListeners()
00582 {
00583   // Remove listeners associated with content documents
00584   // Remove scroll position listener
00585   RemoveScrollListener();
00586 
00587   // Remove document observer
00588   mDocument->RemoveObserver(this);
00589 
00590   if (mScrollWatchTimer) {
00591     mScrollWatchTimer->Cancel();
00592     mScrollWatchTimer = nsnull;
00593   }
00594 
00595   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
00596   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
00597   NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
00598 
00599   PRInt32 itemType;
00600   docShellTreeItem->GetItemType(&itemType);
00601   if (itemType == nsIDocShellTreeItem::typeContent) {
00602     nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
00603     if (commandManager) {
00604       commandManager->RemoveCommandObserver(this, "obs_documentCreated");
00605     }
00606   }
00607 
00608   return NS_OK;
00609 }
00610 
00611 NS_IMETHODIMP nsDocAccessible::FireAnchorJumpEvent()
00612 {
00613   return NS_OK;
00614 }
00615 
00616 NS_IMETHODIMP nsDocAccessible::FireDocLoadingEvent(PRBool aIsFinished)
00617 {
00618   if (!mDocument || !mWeakShell) {
00619     return NS_OK;  // Document has been shut down
00620   }
00621 
00622   if (mIsContentLoaded == aIsFinished) {
00623     return NS_OK;
00624   }
00625   mIsContentLoaded = aIsFinished;
00626 
00627   if (aIsFinished) {
00628     // Need to wait until scrollable view is available
00629     AddScrollListener();
00630     nsCOMPtr<nsIAccessible> parent;
00631     GetParent(getter_AddRefs(parent));
00632     nsCOMPtr<nsPIAccessible> privateAccessible(do_QueryInterface(parent));
00633     if (privateAccessible) {
00634       // Make the parent forget about the old document as a child
00635       privateAccessible->InvalidateChildren();
00636     }
00637   }
00638 
00639   return NS_OK;
00640 }
00641 
00642 void nsDocAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
00643 {
00644   nsDocAccessible *docAcc = NS_REINTERPRET_CAST(nsDocAccessible*, aClosure);
00645 
00646   if (docAcc && docAcc->mScrollPositionChangedTicks && 
00647       ++docAcc->mScrollPositionChangedTicks > 2) {
00648     // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
00649     // We only want to fire accessibilty scroll event when scrolling stops or pauses
00650     // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
00651     // That indicates a pause in scrolling, so we fire the accessibilty scroll event
00652     docAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SCROLLINGEND, docAcc, nsnull);
00653     docAcc->mScrollPositionChangedTicks = 0;
00654     if (docAcc->mScrollWatchTimer) {
00655       docAcc->mScrollWatchTimer->Cancel();
00656       docAcc->mScrollWatchTimer = nsnull;
00657     }
00658   }
00659 }
00660 
00661 void nsDocAccessible::AddScrollListener()
00662 {
00663   nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
00664 
00665   nsIViewManager* vm = nsnull;
00666   if (presShell)
00667     vm = presShell->GetViewManager();
00668 
00669   nsIScrollableView* scrollableView = nsnull;
00670   if (vm)
00671     vm->GetRootScrollableView(&scrollableView);
00672 
00673   if (scrollableView)
00674     scrollableView->AddScrollPositionListener(this);
00675 }
00676 
00677 void nsDocAccessible::RemoveScrollListener()
00678 {
00679   nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
00680 
00681   nsIViewManager* vm = nsnull;
00682   if (presShell)
00683     vm = presShell->GetViewManager();
00684 
00685   nsIScrollableView* scrollableView = nsnull;
00686   if (vm)
00687     vm->GetRootScrollableView(&scrollableView);
00688 
00689   if (scrollableView)
00690     scrollableView->RemoveScrollPositionListener(this);
00691 }
00692 
00693 NS_IMETHODIMP nsDocAccessible::ScrollPositionWillChange(nsIScrollableView *aView, nscoord aX, nscoord aY)
00694 {
00695   return NS_OK;
00696 }
00697 
00698 NS_IMETHODIMP nsDocAccessible::ScrollPositionDidChange(nsIScrollableView *aScrollableView, nscoord aX, nscoord aY)
00699 {
00700   // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes, 
00701   // then the ::Notify() method will fire the accessibility event for scroll position changes
00702   const PRUint32 kScrollPosCheckWait = 50;
00703   if (mScrollWatchTimer) {
00704     mScrollWatchTimer->SetDelay(kScrollPosCheckWait);  // Create new timer, to avoid leaks
00705   }
00706   else {
00707     mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
00708     if (mScrollWatchTimer) {
00709       mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
00710                                               kScrollPosCheckWait, 
00711                                               nsITimer::TYPE_REPEATING_SLACK);
00712     }
00713   }
00714   mScrollPositionChangedTicks = 1;
00715   return NS_OK;
00716 }
00717 
00718 NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
00719                                        const PRUnichar *aData)
00720 {
00721   if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
00722     CheckForEditor();
00723     NS_ASSERTION(mEditor, "Should have editor if we see obs_documentCreated");
00724   }
00725 
00726   return NS_OK;
00727 }
00728 
00730 // nsIDocumentObserver
00731 
00732 NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
00733 NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
00734 NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsDocAccessible)
00735 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
00736 
00737 void
00738 nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
00739                                   PRInt32 aNameSpaceID, nsIAtom* aAttribute,
00740                                   PRInt32 aModType)
00741 {
00742   // XXX todo
00743   // We still need to handle special HTML cases here
00744   // For example, if an <img>'s usemap attribute is modified
00745   // Otherwise it may just be a state change, for example an object changing
00746   // its visibility
00747 
00748   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
00749   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
00750   if (!docShell) {
00751     return;
00752   }
00753   PRUint32 busyFlags;
00754   docShell->GetBusyFlags(&busyFlags);
00755   if (busyFlags) {
00756     return; // Still loading, ignore setting of initial attributes
00757   }
00758 
00759   nsCOMPtr<nsIPresShell> shell = GetPresShell();
00760   if (!shell) {
00761     return; // Document has been shut down
00762   }
00763 
00764   nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(aContent));
00765   NS_ASSERTION(targetNode, "No node for attr modified");
00766   if (!targetNode) {
00767     return;
00768   }
00769 
00770   if (aNameSpaceID == kNameSpaceID_XHTML2_Unofficial ||
00771       aNameSpaceID == kNameSpaceID_XHTML) {
00772     if (aAttribute == nsAccessibilityAtoms::role) {
00773       InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER);
00774     }
00775     return;
00776   }
00777 
00778   if (aAttribute == nsAccessibilityAtoms::href || aAttribute == nsAccessibilityAtoms::onclick) {
00779     InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER);
00780     return;
00781   }
00782 
00783   PRUint32 eventType = 0;
00784   if (aAttribute == nsAccessibilityAtoms::selected) {
00785     // DHTML or XUL selection
00786     nsCOMPtr<nsIAccessible> multiSelect = GetMultiSelectFor(targetNode);
00787     // Multi selects use selection_add and selection_remove
00788     // Single select widgets just mirror event_selection for
00789     // whatever gets event_focus, which is done in
00790     // nsRootAccessible::FireAccessibleFocusEvent()
00791     // So right here we make sure only to deal with multi selects
00792     if (multiSelect) {
00793       // Need to find the right event to use here, SELECTION_WITHIN would
00794       // seem right but we had started using it for something else
00795       nsCOMPtr<nsIAccessNode> multiSelectAccessNode =
00796         do_QueryInterface(multiSelect);
00797       nsCOMPtr<nsIDOMNode> multiSelectDOMNode;
00798       multiSelectAccessNode->GetDOMNode(getter_AddRefs(multiSelectDOMNode));
00799       NS_ASSERTION(multiSelectDOMNode, "A new accessible without a DOM node!");
00800       FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
00801                               multiSelectDOMNode, nsnull, PR_TRUE);
00802       nsAutoString attrValue;
00803       aContent->GetAttr(aNameSpaceID,
00804                         nsAccessibilityAtoms::selected, attrValue);
00805       if (attrValue.IsEmpty() || attrValue.EqualsLiteral("false")) {
00806         eventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
00807       }
00808       else {
00809         eventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
00810       }
00811     }
00812   }
00813   else if (aNameSpaceID == kNameSpaceID_WAIProperties) {
00814     // DHTML accessibility attributes
00815     if (!HasRoleAttribute(aContent)) {
00816       // We don't care about DHTML state changes unless there is
00817       // a DHTML role set for the element
00818       return;
00819     }
00820     if (aAttribute == nsAccessibilityAtoms::checked ||
00821         aAttribute == nsAccessibilityAtoms::expanded) {
00822       eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
00823     }
00824     else if (aAttribute == nsAccessibilityAtoms::readonly ||
00825              aAttribute == nsAccessibilityAtoms::disabled ||
00826              aAttribute == nsAccessibilityAtoms::required ||
00827              aAttribute == nsAccessibilityAtoms::invalid) {
00828       eventType = nsIAccessibleEvent::EVENT_STATE_CHANGE;
00829     }
00830     else if (aAttribute == nsAccessibilityAtoms::valuenow) {
00831       eventType = nsIAccessibleEvent::EVENT_VALUE_CHANGE;
00832     }
00833     else if (aAttribute == nsAccessibilityAtoms::multiselect) {
00834       // This affects whether the accessible supports nsIAccessibleSelectable.
00835       // COM says we cannot change what interfaces are supported on-the-fly,
00836       // so invalidate this object. A new one will be created on demand.
00837       if (HasRoleAttribute(aContent)) {
00838         // The multiselect and other waistate attributes only take affect
00839         // when dynamic content role is present
00840         InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER);
00841       }
00842     }
00843   }
00844 
00845   // Fire after short timer, because we need to wait for
00846   // DOM attribute & resulting layout to actually change.
00847   // Otherwise, assistive technology 
00848   // will retrieve the wrong state/value/selection info.
00849   if (eventType) {
00850     // The attribute change occured on an accessible node
00851     FireDelayedToolkitEvent(eventType, targetNode, nsnull);
00852   }
00853 }
00854 
00855 void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
00856                                       nsIContent* aContainer,
00857                                       PRInt32 aNewIndexInContainer)
00858 {
00859   // InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
00860   // unless an accessible can be created for the passed in node, which it
00861   // can't do unless the node is visible. The right thing happens there so
00862   // no need for an extra visibility check here.
00863   PRUint32 childCount = aContainer->GetChildCount();
00864   for (PRUint32 index = aNewIndexInContainer; index < childCount; index ++) {
00865     InvalidateCacheSubtree(aContainer->GetChildAt(index),
00866                            nsIAccessibleEvent::EVENT_SHOW);
00867   }
00868 }
00869 
00870 void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
00871                                            nsIContent* aContent1,
00872                                            nsIContent* aContent2,
00873                                            PRInt32 aStateMask)
00874 {
00875   if (0 == (aStateMask & NS_EVENT_STATE_CHECKED)) {
00876     return;
00877   }
00878 
00879   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
00880   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
00881 }
00882 
00883 void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
00884                                            nsIContent* aContent,
00885                                            PRBool aAppend)
00886 {
00887   InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER);
00888 }
00889 
00890 void
00891 nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
00892                                  nsIContent* aChild, PRInt32 aIndexInContainer)
00893 {
00894   // InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
00895   // unless an accessible can be created for the passed in node, which it
00896   // can't do unless the node is visible. The right thing happens there so
00897   // no need for an extra visibility check here.
00898   InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_SHOW);
00899 }
00900 
00901 void
00902 nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
00903                                 nsIContent* aChild, PRInt32 aIndexInContainer)
00904 {
00905   InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_HIDE);
00906 }
00907 
00908 nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent,
00909                                                   nsIDOMNode *aDOMNode,
00910                                                   void *aData,
00911                                                   PRBool aAllowDupes)
00912 {
00913   PRBool isTimerStarted = PR_TRUE;
00914   PRInt32 numQueuedEvents = mEventsToFire.Count();
00915     if (!mFireEventTimer) {
00916       // Do not yet have a timer going for firing another event.
00917       mFireEventTimer = do_CreateInstance("@mozilla.org/timer;1");
00918       NS_ENSURE_TRUE(mFireEventTimer, NS_ERROR_OUT_OF_MEMORY);
00919     }
00920   if (numQueuedEvents == 0) {
00921     isTimerStarted = PR_FALSE;
00922   }
00923   else if (!aAllowDupes) {
00924     // Check for repeat events. If a redundant event exists remove
00925     // original and put the new event at the end of the queue
00926     // so it is fired after the others
00927     for (PRInt32 index = 0; index < numQueuedEvents; index ++) {
00928       nsIAccessibleEvent *accessibleEvent = mEventsToFire[index];
00929       NS_ASSERTION(accessibleEvent, "Array item is not an accessible event");
00930       if (!accessibleEvent) {
00931         continue;
00932       }
00933       PRUint32 eventType;
00934       accessibleEvent->GetEventType(&eventType);
00935       if (eventType == aEvent) {
00936         nsCOMPtr<nsIDOMNode> domNode;
00937         accessibleEvent->GetDOMNode(getter_AddRefs(domNode));
00938         if (domNode == aDOMNode) {
00939           mEventsToFire.RemoveObjectAt(index);
00940           -- index;
00941           -- numQueuedEvents;
00942         }
00943       }
00944     }
00945   }
00946 
00947   // XXX Add related data for ATK support.
00948   // For example, state change event should provide what state has changed,
00949   // as well as the old and new value.
00950   nsCOMPtr<nsIAccessibleEvent> event =
00951     new nsAccessibleEventData(aEvent, aDOMNode, this, aData);
00952   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
00953   mEventsToFire.AppendObject(event);
00954   if (!isTimerStarted) {
00955     // This is be the first delayed event in queue, start timer
00956     // so that event gets fired via FlushEventsCallback
00957     mFireEventTimer->InitWithFuncCallback(FlushEventsCallback,
00958                                           NS_STATIC_CAST(nsPIAccessibleDocument*, this),
00959                                           0, nsITimer::TYPE_ONE_SHOT);
00960   }
00961   return NS_OK;
00962 }
00963 
00964 NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
00965 {
00966   PRUint32 length = mEventsToFire.Count();
00967   NS_ASSERTION(length, "How did we get here without events to fire?");
00968   PRUint32 index;
00969   for (index = 0; index < length; index ++) {
00970     nsIAccessibleEvent *accessibleEvent = mEventsToFire[index];
00971     NS_ASSERTION(accessibleEvent, "Array item is not an accessible event");
00972     nsCOMPtr<nsIAccessible> accessible;
00973     accessibleEvent->GetAccessible(getter_AddRefs(accessible));
00974     if (accessible) {
00975       PRUint32 eventType;
00976       accessibleEvent->GetEventType(&eventType);
00977       FireToolkitEvent(eventType, accessible, nsnull);
00978     }
00979   }
00980   mEventsToFire.Clear(); // Clear out array
00981   return NS_OK;
00982 }
00983 
00984 void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
00985 {
00986   nsPIAccessibleDocument *accessibleDoc = NS_STATIC_CAST(nsPIAccessibleDocument*, aClosure);
00987   NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
00988   accessibleDoc->FlushPendingEvents();
00989 }
00990 
00991 void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent)
00992 {
00993   nsCOMPtr<nsIDOMNode> iterNode(aStartNode), nextNode;
00994   nsCOMPtr<nsIAccessNode> accessNode;
00995 
00996   do {
00997     GetCachedAccessNode(iterNode, getter_AddRefs(accessNode));
00998     if (accessNode) {
00999       // Accessibles that implement their own subtrees,
01000       // like html combo boxes and xul trees must shutdown all of their own
01001       // children when they override Shutdown()
01002 
01003       // Don't shutdown our doc object!
01004       if (accessNode != NS_STATIC_CAST(nsIAccessNode*, this)) { 
01005         if (aChangeEvent != nsIAccessibleEvent::EVENT_SHOW) {
01006           nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
01007           if (accessible) {
01008             // Fire menupopupend events for menu popups that go away
01009             PRUint32 role, event = 0;
01010             accessible->GetFinalRole(&role);
01011             if (role == ROLE_MENUPOPUP) {
01012               nsCOMPtr<nsIDOMNode> domNode;
01013               accessNode->GetDOMNode(getter_AddRefs(domNode));
01014               nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
01015               if (!popup) {
01016                 // Popup elements already fire these via DOMMenuInactive
01017                 // handling in nsRootAccessible::HandleEvent
01018                 event = nsIAccessibleEvent::EVENT_MENUPOPUPEND;
01019               }
01020             }
01021             else if (role == ROLE_PROGRESSBAR && iterNode != aStartNode) {
01022               // Make sure EVENT_HIDE gets fired for progress meters
01023               event = nsIAccessibleEvent::EVENT_HIDE;
01024             }
01025             if (event) {
01026               FireToolkitEvent(event, accessible, nsnull);
01027             }
01028           }
01029         }
01030         void *uniqueID;
01031         accessNode->GetUniqueID(&uniqueID);
01032         nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
01033         privateAccessNode->Shutdown();
01034         // Remove from hash table as well
01035         mAccessNodeCache.Remove(uniqueID);
01036       }
01037     }
01038 
01039     iterNode->GetFirstChild(getter_AddRefs(nextNode));
01040     if (nextNode) {
01041       iterNode = nextNode;
01042       continue;
01043     }
01044 
01045     if (iterNode == aStartNode)
01046       break;
01047     iterNode->GetNextSibling(getter_AddRefs(nextNode));
01048     if (nextNode) {
01049       iterNode = nextNode;
01050       continue;
01051     }
01052 
01053     do {
01054       iterNode->GetParentNode(getter_AddRefs(nextNode));
01055       if (!nextNode || nextNode == aStartNode) {
01056         return;
01057       }
01058       nextNode->GetNextSibling(getter_AddRefs(iterNode));
01059       if (iterNode)
01060         break;
01061       iterNode = nextNode;
01062     } while (PR_TRUE);
01063   }
01064   while (iterNode && iterNode != aStartNode);
01065 }
01066 
01067 NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
01068                                                       PRUint32 aChangeEventType)
01069 {
01070   NS_ASSERTION(aChangeEventType == nsIAccessibleEvent::EVENT_REORDER ||
01071                aChangeEventType == nsIAccessibleEvent::EVENT_SHOW ||
01072                aChangeEventType == nsIAccessibleEvent::EVENT_HIDE,
01073                "Incorrect aChangeEventType passed in");
01074 
01075   // Invalidate cache subtree
01076   // We have to check for accessibles for each dom node by traversing DOM tree
01077   // instead of just the accessible tree, although that would be faster
01078   // Otherwise we might miss the nsAccessNode's that are not nsAccessible's.
01079 
01080   nsCOMPtr<nsIDOMNode> childNode = aChild ? do_QueryInterface(aChild) : mDOMNode;
01081 
01082   if (!mIsContentLoaded && mAccessNodeCache.Count() <= 1) {
01083     return NS_OK; // Still loading and nothing to invalidate yet
01084   }
01085 
01086   else if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE) {
01087     // Fire EVENT_HIDE or EVENT_MENUPOPUPEND if previous accessible existed
01088     // for node being hidden. Fire this before the accessible goes away
01089     nsCOMPtr<nsIAccessNode> childAccessNode;
01090     GetCachedAccessNode(childNode, getter_AddRefs(childAccessNode));
01091     nsCOMPtr<nsIAccessible> childAccessible = do_QueryInterface(childAccessNode);
01092     if (childAccessible) {
01093       nsCOMPtr<nsPIAccessible> privateChildAccessible =
01094         do_QueryInterface(childAccessible);
01095       NS_ASSERTION(privateChildAccessible, "No nsPIAccessible for an nsIAccessible");
01096       privateChildAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_HIDE,
01097                                                childAccessible, nsnull);
01098     }
01099   }
01100 
01101   // Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
01102   if (aChangeEventType != nsIAccessibleEvent::EVENT_SHOW) {
01103     RefreshNodes(childNode, aChangeEventType);
01104   }
01105 
01106   // We need to get an accessible for the mutation event's container node
01107   // If there is no accessible for that node, we need to keep moving up the parent
01108   // chain so there is some accessible.
01109   // We will use this accessible to fire the accessible mutation event.
01110   // We're guaranteed success, because we will eventually end up at the doc accessible,
01111   // and there is always one of those.
01112 
01113   nsCOMPtr<nsIAccessible> containerAccessible;
01114   if (childNode == mDOMNode) {
01115     // Don't get parent accessible if already at the root of a docshell chain like UI or content
01116     // Don't fire any other events if doc is still loading
01117     nsCOMPtr<nsISupports> container = mDocument->GetContainer();
01118     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
01119     NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
01120     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
01121     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
01122     if (sameTypeRoot == docShellTreeItem) {
01123       containerAccessible = this;  // At the root of UI or content
01124     }
01125   }
01126   if (!containerAccessible) {
01127     GetAccessibleInParentChain(childNode, getter_AddRefs(containerAccessible));
01128   }
01129   nsCOMPtr<nsPIAccessible> privateContainerAccessible =
01130     do_QueryInterface(containerAccessible);
01131   if (privateContainerAccessible) {
01132     privateContainerAccessible->InvalidateChildren();
01133   }
01134 
01135   if (!mIsContentLoaded) {
01136     return NS_OK;
01137   }
01138 
01139   // Fire an event so the assistive technology knows the objects it is holding onto
01140   // in this part of the subtree are no longer useful and should be released.
01141   // However, they still won't crash if the AT tries to use them, because a stub of the
01142   // object still exists as long as it is refcounted, even from outside of Gecko.
01143   nsCOMPtr<nsIAccessNode> containerAccessNode =
01144     do_QueryInterface(containerAccessible);
01145   if (containerAccessNode) {
01146     nsCOMPtr<nsIDOMNode> containerNode;
01147     containerAccessNode->GetDOMNode(getter_AddRefs(containerNode));
01148     if (containerNode) {
01149       FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_REORDER,
01150                               containerNode, nsnull);
01151     }
01152   }
01153 
01154   if (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW && aChild) {
01155     // Fire EVENT_SHOW, EVENT_MENUPOPUPSTART for newly visible content.
01156     // Fire after a short timer, because we want to make sure the view has been
01157     // updated to make this accessible content visible. If we don't wait,
01158     // the assistive technology may receive the event and then retrieve
01159     // STATE_INVISIBLE for the event's accessible object.
01160     FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SHOW, childNode, nsnull);
01161     nsAutoString role;
01162     if (GetRoleAttribute(aChild, role) &&
01163         StringEndsWith(role, NS_LITERAL_STRING(":menu"), nsCaseInsensitiveStringComparator())) {
01164       FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUPSTART, childNode, nsnull);
01165     }
01166   }
01167 
01168   // Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did
01169   if (aChangeEventType != nsIAccessibleEvent::EVENT_HIDE) {
01170     nsIContent *ancestor = aChild;
01171     nsAutoString role;
01172     while (ancestor) {
01173       if (GetRoleAttribute(ancestor, role) &&
01174           StringEndsWith(role, NS_LITERAL_STRING(":alert"), nsCaseInsensitiveStringComparator())) {
01175         nsCOMPtr<nsIDOMNode> alertNode(do_QueryInterface(ancestor));
01176         FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull);
01177         break;
01178       }
01179       ancestor = ancestor->GetParent();
01180     }
01181   }
01182 
01183   return NS_OK;
01184 }
01185 
01186 NS_IMETHODIMP 
01187 nsDocAccessible::GetAccessibleInParentChain(nsIDOMNode *aNode, 
01188                                             nsIAccessible **aAccessible)
01189 {
01190   // Find a pre-existing accessible in parent chain of DOM nodes, or return null
01191   *aAccessible = nsnull;
01192   nsCOMPtr<nsIDOMNode> currentNode(aNode), parentNode;
01193   nsCOMPtr<nsIAccessNode> accessNode;
01194 
01195   do  {
01196     GetCachedAccessNode(currentNode, getter_AddRefs(accessNode));
01197     nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
01198     if (accessible) {
01199       if (currentNode == aNode) {
01200         // We don't want an accessible for the passed-innode --
01201         // it must be from an ancestor
01202         return accessible->GetParent(aAccessible);
01203       }
01204       NS_ADDREF(*aAccessible = accessible);
01205       break;
01206     }
01207     currentNode->GetParentNode(getter_AddRefs(parentNode));
01208     currentNode = parentNode;
01209   }
01210   while (currentNode);
01211 
01212   return NS_OK;
01213 }
01214 
01215 NS_IMETHODIMP nsDocAccessible::FireToolkitEvent(PRUint32 aEvent, nsIAccessible* aAccessible, void* aData)
01216 {
01217   nsCOMPtr<nsIObserverService> obsService =
01218     do_GetService("@mozilla.org/observer-service;1");
01219   if (!obsService) {
01220     return NS_ERROR_FAILURE;
01221   }
01222 
01223   nsCOMPtr<nsIAccessibleEvent> accEvent = new nsAccessibleEventData(aEvent, aAccessible, this, aData);
01224   NS_ENSURE_TRUE(accEvent, NS_ERROR_OUT_OF_MEMORY);
01225 
01226   return obsService->NotifyObservers(accEvent, NS_ACCESSIBLE_EVENT_TOPIC, nsnull);
01227 }
01228