Back to index

lightning-sunbird  0.9+nobinonly
nsRootAccessible.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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 // NOTE: alphabetically ordered
00039 #include "nsAccessibilityService.h"
00040 #include "nsAccessibleEventData.h"
00041 #include "nsCaretAccessible.h"
00042 #include "nsHTMLSelectAccessible.h"
00043 #include "nsIAccessibleCaret.h"
00044 #include "nsIChromeEventHandler.h"
00045 #include "nsIDOMElement.h"
00046 #include "nsIDOMEventListener.h"
00047 #include "nsIDOMEventTarget.h"
00048 #include "nsIDOMHTMLAnchorElement.h"
00049 #include "nsIDOMHTMLImageElement.h"
00050 #include "nsIDOMHTMLInputElement.h"
00051 #include "nsIDOMHTMLSelectElement.h"
00052 #include "nsIDOMNSEvent.h"
00053 #include "nsIDOMWindow.h"
00054 #include "nsIDOMXULMenuListElement.h"
00055 #include "nsIDOMXULMultSelectCntrlEl.h"
00056 #include "nsIDOMXULSelectCntrlItemEl.h"
00057 #include "nsIDOMXULPopupElement.h"
00058 #include "nsIDocument.h"
00059 #include "nsIEventListenerManager.h"
00060 #include "nsIHTMLDocument.h"
00061 #include "nsIFocusController.h"
00062 #include "nsIFrame.h"
00063 #include "nsIInterfaceRequestorUtils.h"
00064 #include "nsIScriptGlobalObject.h"
00065 #include "nsIScrollableView.h"
00066 #include "nsIServiceManager.h"
00067 #include "nsIViewManager.h"
00068 #include "nsLayoutAtoms.h"
00069 #include "nsPIDOMWindow.h"
00070 #include "nsReadableUtils.h"
00071 #include "nsRootAccessible.h"
00072 #include "nsIDocShell.h"
00073 #include "nsIDocShellTreeItem.h"
00074 #include "nsIDocShellTreeNode.h"
00075 #include "nsIDocShellTreeOwner.h"
00076 #include "nsIBaseWindow.h"
00077 
00078 #ifdef MOZ_XUL
00079 #include "nsXULTreeAccessible.h"
00080 #include "nsIXULDocument.h"
00081 #endif
00082 #include "nsAccessibilityService.h"
00083 #include "nsISelectionPrivate.h"
00084 #include "nsICaret.h"
00085 #include "nsIDOMHTMLInputElement.h"
00086 #include "nsAccessibleEventData.h"
00087 #include "nsIDOMDocument.h"
00088 
00089 #ifdef MOZ_ACCESSIBILITY_ATK
00090 #include "nsIAccessibleHyperText.h"
00091 #endif
00092 
00093 NS_INTERFACE_MAP_BEGIN(nsRootAccessible)
00094   NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
00095   NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
00096   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
00097   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFormListener)
00098 NS_INTERFACE_MAP_END_INHERITING(nsDocAccessible)
00099 
00100 NS_IMPL_ADDREF_INHERITED(nsRootAccessible, nsDocAccessible)
00101 NS_IMPL_RELEASE_INHERITED(nsRootAccessible, nsDocAccessible)
00102 
00103 
00104 //-----------------------------------------------------
00105 // construction 
00106 //-----------------------------------------------------
00107 nsRootAccessible::nsRootAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
00108   nsDocAccessibleWrap(aDOMNode, aShell), 
00109   mAccService(do_GetService("@mozilla.org/accessibilityService;1")),
00110   mIsInDHTMLMenu(PR_FALSE)
00111 {
00112 }
00113 
00114 //-----------------------------------------------------
00115 // destruction
00116 //-----------------------------------------------------
00117 nsRootAccessible::~nsRootAccessible()
00118 {
00119 }
00120 
00121 // helpers
00122 /* readonly attribute AString name; */
00123 NS_IMETHODIMP nsRootAccessible::GetName(nsAString& aName)
00124 {
00125   if (!mDocument) {
00126     return NS_ERROR_FAILURE;
00127   }
00128 
00129   if (mRoleMapEntry) {
00130     nsAccessible::GetName(aName);
00131     if (!aName.IsEmpty()) {
00132       return NS_OK;
00133     }
00134   }
00135 
00136   nsIScriptGlobalObject *globalScript = mDocument->GetScriptGlobalObject();
00137   nsIDocShell *docShell = nsnull;
00138   if (globalScript) {
00139     docShell = globalScript->GetDocShell();
00140   }
00141 
00142   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
00143   if(!docShellAsItem)
00144      return NS_ERROR_FAILURE;
00145 
00146   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
00147   docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
00148 
00149   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
00150   if (baseWindow) {
00151     nsXPIDLString title;
00152     baseWindow->GetTitle(getter_Copies(title));
00153     aName.Assign(title);
00154     return NS_OK;
00155   }
00156 
00157   return NS_ERROR_FAILURE;
00158 }
00159 
00160 /* readonly attribute nsIAccessible accParent; */
00161 NS_IMETHODIMP nsRootAccessible::GetParent(nsIAccessible * *aParent) 
00162 { 
00163   *aParent = nsnull;
00164   return NS_OK;
00165 }
00166 
00167 /* readonly attribute unsigned long accRole; */
00168 NS_IMETHODIMP nsRootAccessible::GetRole(PRUint32 *aRole) 
00169 { 
00170   if (!mDocument) {
00171     return NS_ERROR_FAILURE;
00172   }
00173 
00174   // If it's a <dialog> or <wizard>, use ROLE_DIALOG instead
00175   nsIContent *rootContent = mDocument->GetRootContent();
00176   if (rootContent) {
00177     nsCOMPtr<nsIDOMElement> rootElement(do_QueryInterface(rootContent));
00178     if (rootElement) {
00179       nsAutoString name;
00180       rootElement->GetLocalName(name);
00181       if (name.EqualsLiteral("dialog") || name.EqualsLiteral("wizard")) {
00182         *aRole = ROLE_DIALOG; // Always at the root
00183         return NS_OK;
00184       }
00185     }
00186   }
00187 
00188   return nsDocAccessibleWrap::GetRole(aRole);
00189 }
00190 
00191 NS_IMETHODIMP nsRootAccessible::GetState(PRUint32 *aState) 
00192 {
00193   nsresult rv = NS_ERROR_FAILURE;
00194   if (mDOMNode) {
00195     rv = nsDocAccessibleWrap::GetState(aState);
00196   }
00197   if (NS_FAILED(rv)) {
00198     return rv;
00199   }
00200 
00201   NS_ASSERTION(mDocument, "mDocument should not be null unless mDOMNode is");
00202   if (gLastFocusedNode) {
00203     nsCOMPtr<nsIDOMDocument> rootAccessibleDoc(do_QueryInterface(mDocument));
00204     nsCOMPtr<nsIDOMDocument> focusedDoc;
00205     gLastFocusedNode->GetOwnerDocument(getter_AddRefs(focusedDoc));
00206     if (rootAccessibleDoc == focusedDoc) {
00207       *aState |= STATE_FOCUSED;
00208     }
00209   }
00210   return NS_OK;
00211 }
00212 
00213 void
00214 nsRootAccessible::GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget)
00215 {
00216   nsCOMPtr<nsIDOMWindow> domWin;
00217   GetWindow(getter_AddRefs(domWin));
00218   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(domWin));
00219   nsCOMPtr<nsIChromeEventHandler> chromeEventHandler;
00220   if (privateDOMWindow) {
00221     chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
00222   }
00223 
00224   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
00225 
00226   *aChromeTarget = target;
00227   NS_IF_ADDREF(*aChromeTarget);
00228 }
00229 
00230 nsresult nsRootAccessible::AddEventListeners()
00231 {
00232   // use AddEventListener from the nsIDOMEventTarget interface
00233   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
00234   if (target) { 
00235     // capture DOM focus events 
00236     nsresult rv = target->AddEventListener(NS_LITERAL_STRING("focus"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
00237     NS_ENSURE_SUCCESS(rv, rv);
00238 
00239     // capture Form change events 
00240     rv = target->AddEventListener(NS_LITERAL_STRING("select"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
00241     NS_ENSURE_SUCCESS(rv, rv);
00242 
00243     // capture NameChange events (fired whenever name changes, immediately after, whether focus moves or not)
00244     rv = target->AddEventListener(NS_LITERAL_STRING("NameChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00245     NS_ENSURE_SUCCESS(rv, rv);
00246 
00247     // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
00248     rv = target->AddEventListener(NS_LITERAL_STRING("ValueChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00249     NS_ENSURE_SUCCESS(rv, rv);
00250 
00251     // capture AlertActive events (fired whenever alert pops up)
00252     rv = target->AddEventListener(NS_LITERAL_STRING("AlertActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00253     NS_ENSURE_SUCCESS(rv, rv);
00254 
00255     // add ourself as a TreeViewChanged listener (custom event fired in tree.xml)
00256     rv = target->AddEventListener(NS_LITERAL_STRING("TreeViewChanged"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00257     NS_ENSURE_SUCCESS(rv, rv);
00258 
00259     // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
00260     rv = target->AddEventListener(NS_LITERAL_STRING("OpenStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00261     NS_ENSURE_SUCCESS(rv, rv);
00262 
00263     // add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
00264     rv = target->AddEventListener(NS_LITERAL_STRING("CheckboxStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00265     NS_ENSURE_SUCCESS(rv, rv);
00266 
00267     // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp  & radio.xml)
00268     rv = target->AddEventListener(NS_LITERAL_STRING("RadioStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00269     NS_ENSURE_SUCCESS(rv, rv);
00270 
00271     rv = target->AddEventListener(NS_LITERAL_STRING("popupshown"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00272     NS_ENSURE_SUCCESS(rv, rv);
00273 
00274     rv = target->AddEventListener(NS_LITERAL_STRING("popuphiding"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00275     NS_ENSURE_SUCCESS(rv, rv);
00276 
00277     rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00278     NS_ENSURE_SUCCESS(rv, rv);
00279 
00280     rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuItemActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00281     NS_ENSURE_SUCCESS(rv, rv);
00282     
00283     rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00284     NS_ENSURE_SUCCESS(rv, rv);
00285     
00286     rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00287     NS_ENSURE_SUCCESS(rv, rv);
00288 
00289     rv = target->AddEventListener(NS_LITERAL_STRING("DOMContentLoaded"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00290     NS_ENSURE_SUCCESS(rv, rv);
00291   }
00292 
00293   GetChromeEventHandler(getter_AddRefs(target));
00294   NS_ASSERTION(target, "No chrome event handler for document");
00295   if (target) {
00296     nsresult rv = target->AddEventListener(NS_LITERAL_STRING("pagehide"), 
00297                                            NS_STATIC_CAST(nsIDOMXULListener*, this), 
00298                                            PR_TRUE);
00299     NS_ENSURE_SUCCESS(rv, rv);
00300   }
00301 
00302   if (!mCaretAccessible)
00303     mCaretAccessible = new nsCaretAccessible(mDOMNode, mWeakShell, this);
00304 
00305   // Fire accessible focus event for pre-existing focus, but wait until all internal
00306   // focus events are finished for window initialization.
00307   mFireFocusTimer = do_CreateInstance("@mozilla.org/timer;1");
00308   if (mFireFocusTimer) {
00309     mFireFocusTimer->InitWithFuncCallback(FireFocusCallback, this,
00310                                           0, nsITimer::TYPE_ONE_SHOT);
00311   }
00312 
00313   return nsDocAccessible::AddEventListeners();
00314 }
00315 
00316 nsresult nsRootAccessible::RemoveEventListeners()
00317 {
00318   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
00319   if (target) { 
00320     target->RemoveEventListener(NS_LITERAL_STRING("focus"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
00321     target->RemoveEventListener(NS_LITERAL_STRING("select"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
00322     target->RemoveEventListener(NS_LITERAL_STRING("NameChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00323     target->RemoveEventListener(NS_LITERAL_STRING("ValueChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00324     target->RemoveEventListener(NS_LITERAL_STRING("AlertActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00325     target->RemoveEventListener(NS_LITERAL_STRING("TreeViewChanged"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00326     target->RemoveEventListener(NS_LITERAL_STRING("OpenStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00327     target->RemoveEventListener(NS_LITERAL_STRING("CheckboxStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00328     target->RemoveEventListener(NS_LITERAL_STRING("RadioStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00329     target->RemoveEventListener(NS_LITERAL_STRING("popupshown"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00330     target->RemoveEventListener(NS_LITERAL_STRING("popuphiding"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00331     target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00332     target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuItemActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00333     target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00334     target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00335     target->RemoveEventListener(NS_LITERAL_STRING("DOMContentLoaded"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
00336   }
00337 
00338   GetChromeEventHandler(getter_AddRefs(target));
00339   if (target) {
00340     target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), 
00341                                 NS_STATIC_CAST(nsIDOMXULListener*, this), 
00342                                 PR_TRUE);
00343   }
00344 
00345   if (mCaretAccessible) {
00346     mCaretAccessible->RemoveSelectionListener();
00347     mCaretAccessible = nsnull;
00348   }
00349 
00350   mAccService = nsnull;
00351 
00352   return nsDocAccessible::RemoveEventListeners();
00353 }
00354 
00355 NS_IMETHODIMP nsRootAccessible::GetCaretAccessible(nsIAccessible **aCaretAccessible)
00356 {
00357   *aCaretAccessible = nsnull;
00358   if (mCaretAccessible) {
00359     CallQueryInterface(mCaretAccessible, aCaretAccessible);
00360   }
00361 
00362   return NS_OK;
00363 }
00364 
00365 void nsRootAccessible::TryFireEarlyLoadEvent(nsIAccessible *aAccessible, nsIDOMNode *aDocNode)
00366 {
00367   // We can fire an early load event based on DOMContentLoaded unless we 
00368   // have subdocuments. For that we wait until WebProgressListener
00369   // STATE_STOP handling in nsAccessibilityService.
00370 
00371   // Note, we don't fire any page load finished events for chrome or for
00372   // frame/iframe page loads during the initial complete page load -- that page
00373   // load event for the entire content pane needs to stand alone.
00374 
00375   // This also works for firing events for error pages
00376 
00377   nsCOMPtr<nsIDocShellTreeItem> treeItem =
00378     GetDocShellTreeItemFor(aDocNode);
00379   NS_ASSERTION(treeItem, "No docshelltreeitem for aDocNode");
00380   if (!treeItem) {
00381     return;
00382   }
00383   PRInt32 itemType;
00384   treeItem->GetItemType(&itemType);
00385   if (itemType != nsIDocShellTreeItem::typeContent) {
00386     return;
00387   }
00388   nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(treeItem));
00389   if (treeNode) {
00390     PRInt32 subDocuments;
00391     treeNode->GetChildCount(&subDocuments);
00392     if (subDocuments) {
00393       return;
00394     }
00395   }
00396   nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
00397   treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
00398   NS_ASSERTION(rootContentTreeItem, "No root content tree item");
00399   if (!rootContentTreeItem) { // Not at root of content
00400     return;
00401   }
00402   if (rootContentTreeItem != treeItem) {
00403     nsCOMPtr<nsIAccessibleDocument> rootContentDocAccessible =
00404       GetDocAccessibleFor(rootContentTreeItem);
00405     nsCOMPtr<nsIAccessible> rootContentAccessible =
00406       do_QueryInterface(rootContentDocAccessible);
00407     if (!rootContentAccessible) {
00408       return;
00409     }
00410     PRUint32 state;
00411     rootContentAccessible->GetFinalState(&state);
00412     if (state & STATE_BUSY) {
00413       // Don't fire page load events on subdocuments for initial page load of entire page
00414       return;
00415     }
00416   }
00417 
00418   // No frames or iframes, so we can fire the doc load finished event early
00419   nsCOMPtr<nsPIAccessibleDocument> docAccessible =
00420     do_QueryInterface(aAccessible);
00421   NS_ASSERTION(docAccessible, "No doc aAccessible for DOMContentLoaded");
00422   if (docAccessible) {
00423     docAccessible->FireDocLoadingEvent(PR_TRUE);
00424   }
00425 }
00426 
00427 void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
00428                                                 nsIDOMNode *aNode,
00429                                                 nsIDOMEvent *aFocusEvent,
00430                                                 PRBool aForceEvent)
00431 {
00432   NS_ASSERTION(aAccessible, "Attempted to fire focus event for no accessible");
00433 
00434   if (mCaretAccessible) {
00435     nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aFocusEvent));
00436     if (nsevent) {
00437       // Use the originally focused node where the selection lives.
00438       // For example, use the anonymous HTML:input instead of the containing
00439       // XUL:textbox. In this case, sometimes it is a later focus event
00440       // which points to the actual anonymous child with focus, so to be safe 
00441       // we need to reset the selection listener every time.
00442       // This happens because when some bindings handle focus, they retarget
00443       // focus to the appropriate child inside of themselves, but DOM focus
00444       // stays outside on that binding parent.
00445       nsCOMPtr<nsIDOMEventTarget> domEventTarget;
00446       nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
00447       nsCOMPtr<nsIDOMNode> realFocusedNode = do_QueryInterface(domEventTarget);
00448       mCaretAccessible->AttachNewSelectionListener(realFocusedNode);
00449     }
00450   }
00451 
00452   // Fire focus only if it changes, but always fire focus events when aForceEvent == PR_TRUE
00453   if (gLastFocusedNode == aNode && !aForceEvent) {
00454     return;
00455   }
00456 
00457   nsCOMPtr<nsPIAccessible> privateAccessible =
00458     do_QueryInterface(aAccessible);
00459   NS_ASSERTION(privateAccessible , "No nsPIAccessible for nsIAccessible");
00460 
00461   // Use focus events on DHTML menuitems to indicate when to fire menustart and menuend
00462   // Special dynamic content handling
00463   PRUint32 role = ROLE_NOTHING;
00464   aAccessible->GetFinalRole(&role);
00465   if (role == ROLE_MENUITEM) {
00466     if (!mIsInDHTMLMenu) {  // Entering menus
00467       PRUint32 naturalRole; // The natural role is the role that this type of element normally has
00468       aAccessible->GetRole(&naturalRole);
00469       if (role != naturalRole) { // Must be a DHTML menuitem
00470          FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUSTART, this, nsnull);
00471          mIsInDHTMLMenu = ROLE_MENUITEM;
00472       }
00473     }
00474   }
00475   else if (mIsInDHTMLMenu) {
00476     FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUEND, this, nsnull);
00477     mIsInDHTMLMenu = PR_FALSE;
00478   }
00479 
00480   NS_IF_RELEASE(gLastFocusedNode);
00481   gLastFocusedNode = aNode;
00482   NS_IF_ADDREF(gLastFocusedNode);
00483 
00484   privateAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS,
00485                                       aAccessible, nsnull);
00486 }
00487 
00488 void nsRootAccessible::FireCurrentFocusEvent()
00489 {
00490   nsCOMPtr<nsIDOMWindow> domWin;
00491   GetWindow(getter_AddRefs(domWin));
00492   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(domWin));
00493   if (!privateDOMWindow) {
00494     return;
00495   }
00496   nsIFocusController *focusController = privateDOMWindow->GetRootFocusController();
00497   if (!focusController) {
00498     return;
00499   }
00500   nsCOMPtr<nsIDOMElement> focusedElement;
00501   focusController->GetFocusedElement(getter_AddRefs(focusedElement));
00502   nsCOMPtr<nsIDOMNode> focusedNode(do_QueryInterface(focusedElement));
00503   if (!focusedNode) {
00504     // Document itself may have focus
00505     nsCOMPtr<nsIDOMWindowInternal> focusedWinInternal;
00506     focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal));
00507     if (focusedWinInternal) {
00508       nsCOMPtr<nsIDOMDocument> focusedDOMDocument;
00509       focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument));
00510       focusedNode = do_QueryInterface(focusedDOMDocument);
00511     }
00512     if (!focusedNode) {
00513       return;  // Could not get a focused document either
00514     }
00515   }
00516 
00517   // Simulate a focus event so that we can reuse code that fires focus for container children like treeitems
00518   nsIContent *rootContent = mDocument->GetRootContent();
00519   nsPresContext *presContext = GetPresContext();
00520   if (rootContent && presContext) {
00521     nsCOMPtr<nsIDOMEvent> event;
00522     nsCOMPtr<nsIEventListenerManager> manager;
00523     rootContent->GetListenerManager(getter_AddRefs(manager));
00524     if (manager && NS_SUCCEEDED(manager->CreateEvent(presContext, nsnull,
00525                                                      NS_LITERAL_STRING("Events"),
00526                                                      getter_AddRefs(event))) &&
00527         NS_SUCCEEDED(event->InitEvent(NS_LITERAL_STRING("focus"), PR_TRUE, PR_TRUE))) {
00528       HandleEvent(event);
00529     }
00530   }
00531 }
00532 
00533 // --------------- nsIDOMEventListener Methods (3) ------------------------
00534 
00535 NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
00536 {
00537   // Turn DOM events in accessibility events
00538 
00539   // Get info about event and target
00540   nsCOMPtr<nsIDOMNode> targetNode; 
00541   GetTargetNode(aEvent, getter_AddRefs(targetNode));
00542   if (!targetNode)
00543     return NS_ERROR_FAILURE;
00544 
00545   nsAutoString eventType;
00546   aEvent->GetType(eventType);
00547   nsAutoString localName;
00548   targetNode->GetLocalName(localName);
00549 #ifdef DEBUG_aleventhal
00550   // Very useful for debugging, please leave this here.
00551   if (eventType.LowerCaseEqualsLiteral("popupshown")) {
00552     printf("\ndebugging dommenuitemactive events for %s", NS_ConvertUCS2toUTF8(localName).get());
00553   }
00554   if (localName.EqualsIgnoreCase("popup")) {
00555     printf("\ndebugging events in popup, event is %s", NS_ConvertUCS2toUTF8(eventType).get());
00556   }
00557   if (localName.EqualsIgnoreCase("select")) {
00558     printf("\ndebugging events in select, event is %s", NS_ConvertUCS2toUTF8(eventType).get());
00559   }
00560 #endif
00561 
00562   nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(targetNode);
00563 
00564 #ifdef MOZ_ACCESSIBILITY_ATK
00565   nsCOMPtr<nsIDOMHTMLAnchorElement> anchorElement(do_QueryInterface(targetNode));
00566   // For ATK, check whether this link is for an image.
00567   // If so, fire event for the image.
00568   if (anchorElement) {
00569     nsCOMPtr<nsIDOMNode> childNode;
00570     if (NS_SUCCEEDED(targetNode->GetFirstChild(getter_AddRefs(childNode)))) {
00571       while (childNode) {
00572         nsCOMPtr<nsIDOMHTMLImageElement> imgNode(do_QueryInterface(childNode));
00573         if (imgNode) {
00574           anchorElement = nsnull; // ignore the link
00575           targetNode = childNode; // only fire event for image
00576           break;
00577         }
00578         nsCOMPtr<nsIDOMNode> tmpNode;
00579         tmpNode.swap(childNode);
00580         tmpNode->GetNextSibling(getter_AddRefs(childNode));
00581       }
00582     }
00583   }
00584 
00585   if (anchorElement) {
00586     nsCOMPtr<nsIDOMNode> blockNode;
00587     // For ATK, we don't create any individual object for hyperlink, use its parent who has block frame instead
00588     if (NS_SUCCEEDED(nsAccessible::GetParentBlockNode(eventShell, targetNode, getter_AddRefs(blockNode))))
00589       targetNode = blockNode;
00590   }
00591 #endif
00592 
00593   if (!eventShell) {
00594     return NS_OK;
00595   }
00596       
00597   if (eventType.LowerCaseEqualsLiteral("pagehide")) {
00598     // Only get cached accessible for pagehide -- so that we don't create it
00599     // just to destroy it.
00600     nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(eventShell));
00601     nsCOMPtr<nsIAccessibleDocument> accessibleDoc =
00602       nsAccessNode::GetDocAccessibleFor(weakShell);
00603     nsCOMPtr<nsPIAccessibleDocument> privateAccDoc = do_QueryInterface(accessibleDoc);
00604     if (privateAccDoc) {
00605       privateAccDoc->Destroy();
00606     }
00607     return NS_OK;
00608   }
00609 
00610   if (eventType.LowerCaseEqualsLiteral("popupshown")) {
00611     // Fire menupopupstart events after a delay so that ancestor views
00612     // are visible, otherwise an accessible cannot be created for the
00613     // popup and the accessibility toolkit event can't be fired.
00614     nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(targetNode));
00615     if (popup) {
00616       return FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUPSTART,
00617                                     targetNode, nsnull);
00618     }
00619   }
00620 
00621   if (eventType.EqualsLiteral("TreeViewChanged")) {
00622     NS_ENSURE_TRUE(localName.EqualsLiteral("tree"), NS_OK);
00623     nsCOMPtr<nsIContent> treeContent = do_QueryInterface(targetNode);
00624     return mAccService->InvalidateSubtreeFor(eventShell, treeContent,
00625                                              nsIAccessibleEvent::EVENT_REORDER);
00626   }
00627 
00628   nsCOMPtr<nsIAccessible> accessible;
00629   if (NS_FAILED(mAccService->GetAccessibleInShell(targetNode, eventShell,
00630                                                   getter_AddRefs(accessible))))
00631     return NS_OK;
00632   
00633 #ifdef MOZ_XUL
00634   // If it's a tree element, need the currently selected item
00635   nsCOMPtr<nsIAccessible> treeItemAccessible;
00636   if (localName.EqualsLiteral("tree")) {
00637     nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
00638       do_QueryInterface(targetNode);
00639     if (multiSelect) {
00640       PRInt32 treeIndex = -1;
00641       multiSelect->GetCurrentIndex(&treeIndex);
00642       if (treeIndex >= 0) {
00643         nsCOMPtr<nsIAccessibleTreeCache> treeCache(do_QueryInterface(accessible));
00644         if (!treeCache ||
00645             NS_FAILED(treeCache->GetCachedTreeitemAccessible(
00646                       treeIndex,
00647                       nsnull,
00648                       getter_AddRefs(treeItemAccessible))) ||
00649                       !treeItemAccessible) {
00650           return NS_ERROR_OUT_OF_MEMORY;
00651         }
00652         accessible = treeItemAccessible;
00653       }
00654     }
00655   }
00656 #endif
00657 
00658   nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
00659 
00660 #ifndef MOZ_ACCESSIBILITY_ATK
00661 #ifdef MOZ_XUL
00662   // tree event
00663   if (eventType.LowerCaseEqualsLiteral("checkboxstatechange") ||
00664            eventType.LowerCaseEqualsLiteral("openstatechange")) {
00665       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, 
00666                                 accessible, nsnull);
00667       return NS_OK;
00668     }
00669   else if (treeItemAccessible) {
00670     if (eventType.LowerCaseEqualsLiteral("focus")) {
00671       FireAccessibleFocusEvent(accessible, targetNode, aEvent); // Tree has focus
00672     }
00673     else if (eventType.LowerCaseEqualsLiteral("dommenuitemactive")) {
00674       FireAccessibleFocusEvent(treeItemAccessible, targetNode, aEvent, PR_TRUE);
00675     }
00676     else if (eventType.LowerCaseEqualsLiteral("namechange")) {
00677       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, 
00678                                 accessible, nsnull);
00679     }
00680     else if (eventType.LowerCaseEqualsLiteral("select")) {
00681       // If multiselect tree, we should fire selectionadd or selection removed
00682       if (gLastFocusedNode == targetNode) {
00683         nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
00684           do_QueryInterface(targetNode);
00685         nsAutoString selType;
00686         multiSel->GetSelType(selType);
00687         if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
00688           privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, 
00689                                   accessible, nsnull);
00690           // XXX We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
00691           //     for each tree item. Perhaps each tree item will need to
00692           //     cache its selection state and fire an event after a DOM "select"
00693           //     event when that state changes.
00694           // nsXULTreeAccessible::UpdateTreeSelection();
00695         }
00696         else {
00697           privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION, 
00698                                   treeItemAccessible, nsnull);
00699         }
00700       }
00701     }
00702     return NS_OK;
00703   }
00704   else 
00705 #endif
00706   if (eventType.LowerCaseEqualsLiteral("dommenuitemactive")) {
00707     nsCOMPtr<nsIAccessible> containerAccessible = accessible;
00708     PRUint32 containerState = 0;
00709     do {
00710       nsIAccessible *tempAccessible = containerAccessible;
00711       tempAccessible->GetParent(getter_AddRefs(containerAccessible));
00712       if (!containerAccessible) {
00713         break;
00714       }
00715       containerAccessible->GetFinalState(&containerState);
00716     }
00717     while ((containerState & STATE_HASPOPUP) == 0);
00718 
00719     // Only fire focus event for DOMMenuItemActive is not inside collapsed popup
00720     if (0 == (containerState & STATE_COLLAPSED)) {
00721       FireAccessibleFocusEvent(accessible, targetNode, aEvent, PR_TRUE);
00722     }
00723   }
00724   else if (eventType.LowerCaseEqualsLiteral("focus")) {
00725     nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
00726       do_QueryInterface(targetNode);
00727     // Send focus to individual radio button or selected item
00728     if (selectControl) {
00729       nsCOMPtr<nsIDOMXULMenuListElement> menuList =
00730         do_QueryInterface(targetNode);
00731       if (!menuList) {
00732         // Don't do this for menu lists, the items only get focused
00733         // when the list is open, based on DOMMenuitemActive events
00734         nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
00735         selectControl->GetSelectedItem(getter_AddRefs(selectedItem));
00736         if (selectedItem) {
00737           targetNode = do_QueryInterface(selectedItem);
00738         }
00739 
00740         if (!targetNode ||
00741             NS_FAILED(mAccService->GetAccessibleInShell(targetNode, eventShell,
00742                       getter_AddRefs(accessible)))) {
00743           return NS_OK;
00744         }
00745       }
00746     }
00747     if (accessible == this) {
00748       // Top level window focus events already automatically fired by MSAA
00749       // based on HWND activities. Don't fire the extra focus event.
00750       NS_IF_RELEASE(gLastFocusedNode);
00751       gLastFocusedNode = mDOMNode;
00752       NS_IF_ADDREF(gLastFocusedNode);
00753       return NS_OK;
00754     }
00755     FireAccessibleFocusEvent(accessible, targetNode, aEvent);
00756   }
00757   else if (eventType.LowerCaseEqualsLiteral("namechange")) {
00758     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, 
00759                               accessible, nsnull);
00760   }
00761   else if (eventType.EqualsLiteral("ValueChange")) {
00762     PRUint32 role;
00763     accessible->GetFinalRole(&role);
00764     if (role == ROLE_PROGRESSBAR) {
00765       // For progressmeter, fire EVENT_SHOW on 1st value change
00766       nsAutoString value;
00767       accessible->GetFinalValue(value);
00768       if (value.EqualsLiteral("0%")) {
00769         privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_SHOW, 
00770                                   accessible, nsnull);
00771       }
00772     }
00773     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, 
00774                               accessible, nsnull);
00775   }
00776   else if (eventType.EqualsLiteral("AlertActive")) { 
00777     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, 
00778                               accessible, nsnull);
00779   }
00780   else if (eventType.LowerCaseEqualsLiteral("radiostatechange") ) {
00781     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, 
00782                               accessible, nsnull);
00783     PRUint32 finalState;
00784     accessible->GetFinalState(&finalState);
00785     if (finalState & (STATE_CHECKED | STATE_SELECTED)) {
00786       FireAccessibleFocusEvent(accessible, targetNode, aEvent);
00787     }
00788   }
00789   else if (eventType.LowerCaseEqualsLiteral("dommenubaractive"))
00790     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUSTART, accessible, nsnull);
00791   else if (eventType.LowerCaseEqualsLiteral("dommenubarinactive")) {
00792     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUEND, accessible, nsnull);
00793     FireCurrentFocusEvent();
00794   }
00795   else if (eventType.LowerCaseEqualsLiteral("popuphiding")) {
00796     // If accessible focus was inside popup that closes,
00797     // then restore it to true current focus.
00798     // This is the case when we've been getting DOMMenuItemActive events
00799     // inside of a combo box that closes. The real focus is on the combo box.
00800     if (!gLastFocusedNode) {
00801       return NS_OK;
00802     }
00803     nsCOMPtr<nsIDOMNode> parentOfFocus;
00804     gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus));
00805     if (parentOfFocus != targetNode) {
00806       return NS_OK;
00807     }
00808     // Focus was inside of popup that's being hidden
00809     FireCurrentFocusEvent();
00810   }
00811   else if (eventType.LowerCaseEqualsLiteral("domcontentloaded")) {
00812     // Don't create the doc accessible until load scripts have a chance to set
00813     // role attribute for <body> or <html> element, because the value of 
00814     // role attribute will be cached when the doc accessible is Init()'d
00815     TryFireEarlyLoadEvent(accessible, targetNode);
00816   }
00817   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
00818     nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(targetNode));
00819     if (popup) {
00820       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUPEND,
00821                                 accessible, nsnull);
00822     }
00823   }
00824 #else
00825   AtkStateChange stateData;
00826   if (eventType.LowerCaseEqualsLiteral("focus") || 
00827       eventType.LowerCaseEqualsLiteral("dommenuitemactive")) {
00828     if (treeItemAccessible) { // use focused treeitem
00829       privAcc = do_QueryInterface(treeItemAccessible);
00830       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS, 
00831                                 treeItemAccessible, nsnull);
00832     }
00833     else if (anchorElement) {
00834       nsCOMPtr<nsIAccessibleHyperText> hyperText(do_QueryInterface(accessible));
00835       if (hyperText) {
00836        nsCOMPtr<nsIDOMNode> focusedNode(do_QueryInterface(anchorElement));
00837         NS_IF_RELEASE(gLastFocusedNode);
00838         gLastFocusedNode = focusedNode;
00839         NS_IF_ADDREF(gLastFocusedNode);
00840 
00841         PRInt32 selectedLink;
00842         hyperText->GetSelectedLinkIndex(&selectedLink);
00843         privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_ATK_LINK_SELECTED, accessible, &selectedLink);
00844       }
00845     }
00846     else if (localName.EqualsIgnoreCase("radiogroup")) {
00847       // fire focus event for checked radio instead of radiogroup
00848       PRInt32 childCount = 0;
00849       accessible->GetChildCount(&childCount);
00850       nsCOMPtr<nsIAccessible> radioAcc;
00851       for (PRInt32 index = 0; index < childCount; index++) {
00852         accessible->GetChildAt(index, getter_AddRefs(radioAcc));
00853         if (radioAcc) {
00854           radioAcc->GetFinalState(&stateData.state);
00855           if (stateData.state & (STATE_CHECKED | STATE_SELECTED)) {
00856             break;
00857           }
00858         }
00859       }
00860       if (radioAcc) {
00861         privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS, radioAcc, nsnull);
00862       }
00863     }
00864     else
00865       FireAccessibleFocusEvent(accessible, targetNode, aEvent);
00866   }
00867   else if (eventType.LowerCaseEqualsLiteral("select")) {
00868     if (treeItemAccessible) { // it's a XUL <tree>
00869       // use EVENT_FOCUS instead of EVENT_ATK_SELECTION_CHANGE
00870       privAcc = do_QueryInterface(treeItemAccessible);
00871       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS, 
00872                                 treeItemAccessible, nsnull);
00873     }
00874     else if (localName.LowerCaseEqualsLiteral("tabpanels")) {
00875       // make GOK refresh "UI-Grab" window
00876       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_REORDER, accessible, nsnull);
00877     }
00878   }
00879 #if 0
00880   // XXX todo: value change events for ATK are done with 
00881   // AtkPropertyChange, PROP_VALUE. Need the old and new value.
00882   // Not sure how we'll get the old value.
00883   // Aaron: I think this is a problem with the ATK API -- its much harder to
00884   // grab the old value for all the application developers than it is for
00885   // AT's to cache old values when they need to (when would that be!?)
00886   else if (eventType.LowerCaseEqualsLiteral("valuechange")) { 
00887     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, 
00888                               accessible, nsnull);
00889   }
00890 #endif
00891   else if (eventType.LowerCaseEqualsLiteral("checkboxstatechange") || // it's a XUL <checkbox>
00892            eventType.LowerCaseEqualsLiteral("radiostatechange")) { // it's a XUL <radio>
00893     accessible->GetFinalState(&stateData.state);
00894     // prefPane tab is implemented as list items in A11y, so we need to check STATE_SELECTED also
00895     stateData.enable = (stateData.state & (STATE_CHECKED | STATE_SELECTED)) != 0;
00896     stateData.state = STATE_CHECKED;
00897     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, accessible, &stateData);
00898     // only fire focus event for checked radio
00899     if (eventType.LowerCaseEqualsLiteral("radiostatechange") &&
00900         stateData.enable) {
00901       FireAccessibleFocusEvent(accessible, targetNode, aEvent);
00902     }
00903   }
00904   else if (eventType.LowerCaseEqualsLiteral("openstatechange")) { // collapsed/expanded changed
00905     accessible->GetFinalState(&stateData.state);
00906     stateData.enable = (stateData.state & STATE_EXPANDED) != 0;
00907     stateData.state = STATE_EXPANDED;
00908     privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, accessible, &stateData);
00909   }
00910   else if (eventType.LowerCaseEqualsLiteral("popuphiding")) {
00911     // If accessible focus was inside popup that closes,
00912     // then restore it to true current focus.
00913     // This is the case when we've been getting DOMMenuItemActive events
00914     // inside of a combo box that closes. The real focus is on the combo box.
00915     if (!gLastFocusedNode) {
00916       return NS_OK;
00917     }
00918     nsCOMPtr<nsIDOMNode> parentOfFocus;
00919     gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus));
00920     if (parentOfFocus != targetNode) {
00921       return NS_OK;
00922     }
00923     // Focus was inside of popup that's being hidden
00924     FireCurrentFocusEvent();
00925   }
00926   else if (eventType.LowerCaseEqualsLiteral("popupshown")) {
00927     FireAccessibleFocusEvent(accessible, targetNode, aEvent);
00928   }
00929   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
00930     nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(targetNode));
00931     if (popup) {
00932       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUPEND,
00933                                 accessible, nsnull);
00934     }
00935 
00936   }
00937 #endif
00938   return NS_OK;
00939 }
00940 
00941 void nsRootAccessible::GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode)
00942 {
00943   *aTargetNode = nsnull;
00944 
00945   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aEvent));
00946 
00947   if (nsevent) {
00948     nsCOMPtr<nsIDOMEventTarget> domEventTarget;
00949     nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
00950     nsCOMPtr<nsIContent> content(do_QueryInterface(domEventTarget));
00951     nsIContent *bindingParent;
00952     if (content && content->IsContentOfType(nsIContent::eHTML) &&
00953       (bindingParent = content->GetBindingParent()) != nsnull) {
00954       // Use binding parent when the event occurs in 
00955       // anonymous HTML content.
00956       // This gets the following important cases correct:
00957       // 1. Inserted <dialog> buttons like OK, Cancel, Help.
00958       // 2. XUL menulists and comboboxes.
00959       // 3. The focused radio button in a group.
00960       CallQueryInterface(bindingParent, aTargetNode);
00961       NS_ASSERTION(*aTargetNode, "No target node for binding parent of anonymous event target");
00962       return;
00963     }
00964     if (domEventTarget) {
00965       CallQueryInterface(domEventTarget, aTargetNode);
00966     }
00967   }
00968 }
00969 
00970 void nsRootAccessible::FireFocusCallback(nsITimer *aTimer, void *aClosure)
00971 {
00972   nsRootAccessible *rootAccessible = NS_STATIC_CAST(nsRootAccessible*, aClosure);
00973   NS_ASSERTION(rootAccessible, "How did we get here without a root accessible?");
00974   rootAccessible->FireCurrentFocusEvent();
00975 }
00976 
00977 // ------- nsIDOMFocusListener Methods (1) -------------
00978 
00979 NS_IMETHODIMP nsRootAccessible::Focus(nsIDOMEvent* aEvent) 
00980 { 
00981   return HandleEvent(aEvent);
00982 }
00983 
00984 NS_IMETHODIMP nsRootAccessible::Blur(nsIDOMEvent* aEvent) 
00985 { 
00986   return NS_OK; 
00987 }
00988 
00989 // ------- nsIDOMFormListener Methods (5) -------------
00990 
00991 NS_IMETHODIMP nsRootAccessible::Submit(nsIDOMEvent* aEvent) 
00992 { 
00993   return NS_OK; 
00994 }
00995 
00996 NS_IMETHODIMP nsRootAccessible::Reset(nsIDOMEvent* aEvent) 
00997 { 
00998   return NS_OK; 
00999 }
01000 
01001 NS_IMETHODIMP nsRootAccessible::Change(nsIDOMEvent* aEvent)
01002 {
01003   // get change events when the form elements changes its state, checked->not,
01004   // deleted text, new text, change in selection for list/combo boxes
01005   // this may be the event that we have the individual Accessible objects
01006   // handle themselves -- have list/combos figure out the change in selection
01007   // have textareas and inputs fire a change of state etc...
01008   return NS_OK;   // Ignore form change events in MSAA
01009 }
01010 
01011 // gets Select events when text is selected in a textarea or input
01012 NS_IMETHODIMP nsRootAccessible::Select(nsIDOMEvent* aEvent) 
01013 {
01014   return HandleEvent(aEvent);
01015 }
01016 
01017 // gets Input events when text is entered or deleted in a textarea or input
01018 NS_IMETHODIMP nsRootAccessible::Input(nsIDOMEvent* aEvent) 
01019 { 
01020   return NS_OK; 
01021 }
01022 
01023 // ------- nsIDOMXULListener Methods (8) ---------------
01024 
01025 NS_IMETHODIMP nsRootAccessible::PopupShowing(nsIDOMEvent* aEvent) { return NS_OK; }
01026 
01027 NS_IMETHODIMP nsRootAccessible::PopupShown(nsIDOMEvent* aEvent) { return NS_OK; }
01028 
01029 NS_IMETHODIMP nsRootAccessible::PopupHiding(nsIDOMEvent* aEvent) { return NS_OK; }
01030 
01031 NS_IMETHODIMP nsRootAccessible::PopupHidden(nsIDOMEvent* aEvent) { return NS_OK; }
01032 
01033 NS_IMETHODIMP nsRootAccessible::Close(nsIDOMEvent* aEvent) { return NS_OK; }
01034 
01035 NS_IMETHODIMP nsRootAccessible::Command(nsIDOMEvent* aEvent) { return NS_OK; }
01036 
01037 NS_IMETHODIMP nsRootAccessible::Broadcast(nsIDOMEvent* aEvent) { return NS_OK; }
01038 
01039 NS_IMETHODIMP nsRootAccessible::CommandUpdate(nsIDOMEvent* aEvent) { return NS_OK; }
01040 
01041 NS_IMETHODIMP nsRootAccessible::Shutdown()
01042 {
01043   // Called manually or by nsAccessNode::~nsAccessNode()
01044   if (!mWeakShell) {
01045     return NS_OK;  // Already shutdown
01046   }
01047   mCaretAccessible = nsnull;
01048   mAccService = nsnull;
01049   if (mFireFocusTimer) {
01050     mFireFocusTimer->Cancel();
01051     mFireFocusTimer = nsnull;
01052   }
01053 
01054   return nsDocAccessibleWrap::Shutdown();
01055 }
01056 
01057 already_AddRefed<nsIDocShellTreeItem>
01058 nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
01059 {
01060   PRInt32 itemType;
01061   aStart->GetItemType(&itemType);
01062   if (itemType == nsIDocShellTreeItem::typeContent) {
01063     nsCOMPtr<nsIAccessibleDocument> accDoc = GetDocAccessibleFor(aStart);
01064     nsCOMPtr<nsIAccessible> accessible = do_QueryInterface(accDoc);
01065     // If ancestor chain of accessibles is not completely visible,
01066     // don't use this one. This happens for example if it's inside
01067     // a background tab (tabbed browsing)
01068     while (accessible) {
01069       PRUint32 state;
01070       accessible->GetFinalState(&state);
01071       if (state & STATE_INVISIBLE) {
01072         return nsnull;
01073       }
01074       nsCOMPtr<nsIAccessible> ancestor;
01075       accessible->GetParent(getter_AddRefs(ancestor));
01076       accessible.swap(ancestor);
01077     }
01078 
01079     NS_ADDREF(aStart);
01080     return aStart;
01081   }
01082   nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
01083   if (treeNode) {
01084     PRInt32 subDocuments;
01085     treeNode->GetChildCount(&subDocuments);
01086     for (PRInt32 count = 0; count < subDocuments; count ++) {
01087       nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
01088       treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
01089       NS_ENSURE_TRUE(treeItemChild, nsnull);
01090       contentTreeItem = GetContentDocShell(treeItemChild);
01091       if (contentTreeItem) {
01092         NS_ADDREF(aStart = contentTreeItem);
01093         return aStart;
01094       }
01095     }
01096   }
01097   return nsnull;
01098 }
01099 
01100 NS_IMETHODIMP nsRootAccessible::GetAccessibleRelated(PRUint32 aRelationType,
01101                                                      nsIAccessible **aRelated)
01102 {
01103   *aRelated = nsnull;
01104 
01105   if (!mDOMNode || aRelationType != RELATION_EMBEDS) {
01106     return nsDocAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
01107   }
01108 
01109   nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShellTreeItemFor(mDOMNode);   
01110   nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
01111   nsCOMPtr<nsIAccessibleDocument> accDoc = GetDocAccessibleFor(contentTreeItem);
01112   return accDoc->QueryInterface(NS_GET_IID(nsIAccessible), (void**)aRelated);
01113 }
01114