LXR Roots - seamonkey Cross Reference: seamonkey
seamonkey/ accessible/ src/ base/ nsRootAccessible.cpp
CVS Log
CVS Blame
CVS Graph
Raw output
changes to
this file in
the last:
day
week
month
  1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2 /* ***** BEGIN LICENSE BLOCK *****
  3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4  *
  5  * The contents of this file are subject to the Mozilla Public License Version
  6  * 1.1 (the "License"); you may not use this file except in compliance with
  7  * the License. You may obtain a copy of the License at
  8  * http://www.mozilla.org/MPL/
  9  *
 10  * Software distributed under the License is distributed on an "AS IS" basis,
 11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 12  * for the specific language governing rights and limitations under the
 13  * License.
 14  *
 15  * The Original Code is mozilla.org code.
 16  *
 17  * The Initial Developer of the Original Code is
 18  * Netscape Communications Corporation.
 19  * Portions created by the Initial Developer are Copyright (C) 1998
 20  * the Initial Developer. All Rights Reserved.
 21  *
 22  * Contributor(s):
 23  *
 24  * Alternatively, the contents of this file may be used under the terms of
 25  * either of the GNU General Public License Version 2 or later (the "GPL"),
 26  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27  * in which case the provisions of the GPL or the LGPL are applicable instead
 28  * of those above. If you wish to allow use of your version of this file only
 29  * under the terms of either the GPL or the LGPL, and not to allow others to
 30  * use your version of this file under the terms of the MPL, indicate your
 31  * decision by deleting the provisions above and replace them with the notice
 32  * and other provisions required by the GPL or the LGPL. If you do not delete
 33  * the provisions above, a recipient may use your version of this file under
 34  * the terms of any one of the MPL, the GPL or the LGPL.
 35  *
 36  * ***** END LICENSE BLOCK ***** */
 37 
 38 // NOTE: alphabetically ordered
 39 #include "nsAccessibilityService.h"
 40 #include "nsAccessibleEventData.h"
 41 #include "nsHTMLSelectAccessible.h"
 42 #include "nsIBaseWindow.h"
 43 #include "nsICaret.h"
 44 #include "nsIDocShell.h"
 45 #include "nsIDocShellTreeItem.h"
 46 #include "nsIDocShellTreeNode.h"
 47 #include "nsIDocShellTreeOwner.h"
 48 #include "nsIDOMDocument.h"
 49 #include "nsIDOMElement.h"
 50 #include "nsIDOMEventListener.h"
 51 #include "nsIDOMEventTarget.h"
 52 #include "nsIDOMHTMLAnchorElement.h"
 53 #include "nsIDOMHTMLImageElement.h"
 54 #include "nsIDOMHTMLInputElement.h"
 55 #include "nsIDOMHTMLSelectElement.h"
 56 #include "nsIDOMNSEvent.h"
 57 #include "nsIDOMXULMenuListElement.h"
 58 #include "nsIDOMXULMultSelectCntrlEl.h"
 59 #include "nsIDOMXULSelectCntrlItemEl.h"
 60 #include "nsIDOMXULPopupElement.h"
 61 #include "nsIDocument.h"
 62 #include "nsIEventListenerManager.h"
 63 #include "nsIFocusController.h"
 64 #include "nsIFrame.h"
 65 #include "nsIMenuFrame.h"
 66 #include "nsIHTMLDocument.h"
 67 #include "nsIInterfaceRequestorUtils.h"
 68 #include "nsIMenuParent.h"
 69 #include "nsIScrollableView.h"
 70 #include "nsISelectionPrivate.h"
 71 #include "nsIServiceManager.h"
 72 #include "nsIViewManager.h"
 73 #include "nsPIDOMWindow.h"
 74 #include "nsIWebBrowserChrome.h"
 75 #include "nsReadableUtils.h"
 76 #include "nsRootAccessible.h"
 77 #include "nsIDOMNSEventTarget.h"
 78 #include "nsIDOMDocumentEvent.h"
 79 
 80 #ifdef MOZ_XUL
 81 #include "nsXULTreeAccessible.h"
 82 #include "nsIXULDocument.h"
 83 #include "nsIXULWindow.h"
 84 #endif
 85 
 86 #ifdef MOZ_ACCESSIBILITY_ATK
 87 #include "nsAppRootAccessible.h"
 88 #else
 89 #include "nsApplicationAccessibleWrap.h"
 90 #endif
 91 
 92 // Expanded version of NS_IMPL_ISUPPORTS_INHERITED2 
 93 // so we can QI directly to concrete nsRootAccessible
 94 NS_IMPL_QUERY_HEAD(nsRootAccessible)
 95 NS_IMPL_QUERY_BODY(nsIDOMEventListener)
 96 if (aIID.Equals(NS_GET_IID(nsRootAccessible)))
 97   foundInterface = reinterpret_cast<nsISupports*>(this);
 98 else
 99 NS_IMPL_QUERY_TAIL_INHERITING(nsDocAccessible)
100 
101 NS_IMPL_ADDREF_INHERITED(nsRootAccessible, nsDocAccessible) 
102 NS_IMPL_RELEASE_INHERITED(nsRootAccessible, nsDocAccessible)
103 
104 //-----------------------------------------------------
105 // construction 
106 //-----------------------------------------------------
107 nsRootAccessible::nsRootAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
108   nsDocAccessibleWrap(aDOMNode, aShell)
109 {
110 }
111 
112 //-----------------------------------------------------
113 // destruction
114 //-----------------------------------------------------
115 nsRootAccessible::~nsRootAccessible()
116 {
117 }
118 
119 // helpers
120 /* readonly attribute AString name; */
121 NS_IMETHODIMP nsRootAccessible::GetName(nsAString& aName)
122 {
123   if (!mDocument) {
124     return NS_ERROR_FAILURE;
125   }
126 
127   if (mRoleMapEntry) {
128     nsAccessible::GetName(aName);
129     if (!aName.IsEmpty()) {
130       return NS_OK;
131     }
132   }
133 
134   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
135     nsAccUtils::GetDocShellTreeItemFor(mDOMNode);
136   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
137 
138   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
139   docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
140 
141   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
142   if (baseWindow) {
143     nsXPIDLString title;
144     baseWindow->GetTitle(getter_Copies(title));
145     aName.Assign(title);
146     return NS_OK;
147   }
148 
149   return NS_ERROR_FAILURE;
150 }
151 
152 /* readonly attribute nsIAccessible accParent; */
153 NS_IMETHODIMP nsRootAccessible::GetParent(nsIAccessible * *aParent) 
154 {
155   NS_ENSURE_ARG_POINTER(aParent);
156   *aParent = nsnull;
157 
158   nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
159   NS_IF_ADDREF(*aParent = root);
160 
161   return NS_OK;
162 }
163 
164 /* readonly attribute unsigned long accRole; */
165 NS_IMETHODIMP nsRootAccessible::GetRole(PRUint32 *aRole) 
166 { 
167   if (!mDocument) {
168     return NS_ERROR_FAILURE;
169   }
170 
171   // If it's a <dialog> or <wizard>, use nsIAccessibleRole::ROLE_DIALOG instead
172   nsIContent *rootContent = mDocument->GetRootContent();
173   if (rootContent) {
174     nsCOMPtr<nsIDOMElement> rootElement(do_QueryInterface(rootContent));
175     if (rootElement) {
176       nsAutoString name;
177       rootElement->GetLocalName(name);
178       if (name.EqualsLiteral("dialog") || name.EqualsLiteral("wizard")) {
179         *aRole = nsIAccessibleRole::ROLE_DIALOG; // Always at the root
180         return NS_OK;
181       }
182     }
183   }
184 
185   return nsDocAccessibleWrap::GetRole(aRole);
186 }
187 
188 #ifdef MOZ_XUL
189 PRUint32 nsRootAccessible::GetChromeFlags()
190 {
191   // Return the flag set for the top level window as defined 
192   // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
193   // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
194   nsCOMPtr<nsIDocShellTreeItem> treeItem =
195     nsAccUtils::GetDocShellTreeItemFor(mDOMNode);
196   NS_ENSURE_TRUE(treeItem, 0);
197   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
198   treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
199   NS_ENSURE_TRUE(treeOwner, 0);
200   nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
201   if (!xulWin) {
202     return 0;
203   }
204   PRUint32 chromeFlags;
205   xulWin->GetChromeFlags(&chromeFlags);
206   return chromeFlags;
207 }
208 #endif
209 
210 NS_IMETHODIMP
211 nsRootAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
212 {
213   nsresult rv = nsDocAccessibleWrap::GetState(aState, aExtraState);
214   NS_ENSURE_SUCCESS(rv, rv);
215 
216 #ifdef MOZ_XUL
217   PRUint32 chromeFlags = GetChromeFlags();
218   if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
219     *aState |= nsIAccessibleStates::STATE_SIZEABLE;
220   }
221   if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR) {
222     // If it has a titlebar it's movable
223     // XXX unless it's minimized or maximized, but not sure
224     //     how to detect that
225     *aState |= nsIAccessibleStates::STATE_MOVEABLE;
226   }
227 #endif
228 
229   if (!aExtraState)
230     return NS_OK;
231 
232   nsCOMPtr<nsIDOMWindow> domWin;
233   GetWindow(getter_AddRefs(domWin));
234   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(domWin));
235   if (privateDOMWindow) {
236     nsIFocusController *focusController =
237       privateDOMWindow->GetRootFocusController();
238     PRBool isActive = PR_FALSE;
239     focusController->GetActive(&isActive);
240     if (isActive) {
241       *aExtraState |= nsIAccessibleStates::EXT_STATE_ACTIVE;
242     }
243   }
244 #ifdef MOZ_XUL
245   if (GetChromeFlags() & nsIWebBrowserChrome::CHROME_MODAL) {
246     *aExtraState |= nsIAccessibleStates::EXT_STATE_MODAL;
247   }
248 #endif
249 
250   return NS_OK;
251 }
252 
253 void
254 nsRootAccessible::GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget)
255 {
256   nsCOMPtr<nsIDOMWindow> domWin;
257   GetWindow(getter_AddRefs(domWin));
258   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(domWin));
259   nsCOMPtr<nsPIDOMEventTarget> chromeEventHandler;
260   if (privateDOMWindow) {
261     chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
262   }
263 
264   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
265 
266   *aChromeTarget = target;
267   NS_IF_ADDREF(*aChromeTarget);
268 }
269 
270 const char* const docEvents[] = {
271   // capture DOM focus events 
272   "focus",
273   // capture Form change events 
274   "select",
275   // capture NameChange events (fired whenever name changes, immediately after, whether focus moves or not)
276   "NameChange",
277   // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
278   "ValueChange",
279   // capture AlertActive events (fired whenever alert pops up)
280   "AlertActive",
281   // add ourself as a TreeViewChanged listener (custom event fired in nsTreeBodyFrame.cpp)
282   "TreeViewChanged",
283   // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
284   "OpenStateChange",
285   // add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
286   "CheckboxStateChange",
287   // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp  & radio.xml)
288   "RadioStateChange",
289   "popupshown",
290   "popuphiding",
291   "DOMMenuInactive",
292   "DOMMenuItemActive",
293   "DOMMenuBarActive",
294   "DOMMenuBarInactive",
295   "DOMContentLoaded"
296 };
297 
298 nsresult nsRootAccessible::AddEventListeners()
299 {
300   // nsIDOMNSEventTarget interface allows to register event listeners to
301   // receive untrusted events (synthetic events generated by untrusted code).
302   // For example, XBL bindings implementations for elements that are hosted in
303   // non chrome document fire untrusted events.
304   nsCOMPtr<nsIDOMNSEventTarget> nstarget(do_QueryInterface(mDocument));
305 
306   if (nstarget) {
307     for (const char* const* e = docEvents,
308                    * const* e_end = docEvents + NS_ARRAY_LENGTH(docEvents);
309          e < e_end; ++e) {
310       nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
311                                                this, PR_TRUE, PR_TRUE);
312       NS_ENSURE_SUCCESS(rv, rv);
313     }
314   }
315 
316   nsCOMPtr<nsIDOMEventTarget> target;
317   GetChromeEventHandler(getter_AddRefs(target));
318   if (target) {
319     target->AddEventListener(NS_LITERAL_STRING("pagehide"), this, PR_TRUE);
320   }
321 
322   if (!mCaretAccessible) {
323     mCaretAccessible = new nsCaretAccessible(this);
324   }
325 
326   // Fire accessible focus event for pre-existing focus, but wait until all internal
327   // focus events are finished for window initialization.
328   mFireFocusTimer = do_CreateInstance("@mozilla.org/timer;1");
329   if (mFireFocusTimer) {
330     mFireFocusTimer->InitWithFuncCallback(FireFocusCallback, this,
331                                           0, nsITimer::TYPE_ONE_SHOT);
332   }
333 
334   return nsDocAccessible::AddEventListeners();
335 }
336 
337 nsresult nsRootAccessible::RemoveEventListeners()
338 {
339   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
340   if (target) { 
341     for (const char* const* e = docEvents,
342                    * const* e_end = docEvents + NS_ARRAY_LENGTH(docEvents);
343          e < e_end; ++e) {
344       nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, PR_TRUE);
345       NS_ENSURE_SUCCESS(rv, rv);
346     }
347   }
348 
349   GetChromeEventHandler(getter_AddRefs(target));
350   if (target) {
351     target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), this, PR_TRUE);
352   }
353 
354   if (mCaretAccessible) {
355     mCaretAccessible->Shutdown();
356     mCaretAccessible = nsnull;
357   }
358 
359   return nsDocAccessible::RemoveEventListeners();
360 }
361 
362 nsCaretAccessible*
363 nsRootAccessible::GetCaretAccessible()
364 {
365   return mCaretAccessible;
366 }
367 
368 void nsRootAccessible::TryFireEarlyLoadEvent(nsIDOMNode *aDocNode)
369 {
370   // We can fire an early load event based on DOMContentLoaded unless we 
371   // have subdocuments. For that we wait until WebProgressListener
372   // STATE_STOP handling in nsAccessibilityService.
373 
374   // Note, we don't fire any page load finished events for chrome or for
375   // frame/iframe page loads during the initial complete page load -- that page
376   // load event for the entire content pane needs to stand alone.
377 
378   // This also works for firing events for error pages
379 
380   nsCOMPtr<nsIDocShellTreeItem> treeItem =
381     nsAccUtils::GetDocShellTreeItemFor(aDocNode);
382   NS_ASSERTION(treeItem, "No docshelltreeitem for aDocNode");
383   if (!treeItem) {
384     return;
385   }
386   PRInt32 itemType;
387   treeItem->GetItemType(&itemType);
388   if (itemType != nsIDocShellTreeItem::typeContent) {
389     return;
390   }
391   nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(treeItem));
392   if (treeNode) {
393     PRInt32 subDocuments;
394     treeNode->GetChildCount(&subDocuments);
395     if (subDocuments) {
396       return;
397     }
398   }
399   nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
400   treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
401   NS_ASSERTION(rootContentTreeItem, "No root content tree item");
402   if (!rootContentTreeItem) { // Not at root of content
403     return;
404   }
405   if (rootContentTreeItem != treeItem) {
406     nsCOMPtr<nsIAccessibleDocument> rootContentDocAccessible =
407       GetDocAccessibleFor(rootContentTreeItem);
408     nsCOMPtr<nsIAccessible> rootContentAccessible =
409       do_QueryInterface(rootContentDocAccessible);
410     if (!rootContentAccessible) {
411       return;
412     }
413     PRUint32 state = State(rootContentAccessible);
414     if (state & nsIAccessibleStates::STATE_BUSY) {
415       // Don't fire page load events on subdocuments for initial page load of entire page
416       return;
417     }
418   }
419 
420   // No frames or iframes, so we can fire the doc load finished event early
421   FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_INTERNAL_LOAD, aDocNode,
422                           nsnull, eRemoveDupes);
423 }
424 
425 PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
426                                                   nsIDOMNode *aNode,
427                                                   nsIDOMEvent *aFocusEvent,
428                                                   PRBool aForceEvent,
429                                                   PRBool aIsAsynch)
430 {
431   if (mCaretAccessible) {
432     nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aFocusEvent));
433     if (nsevent) {
434       // Use the originally focused node where the selection lives.
435       // For example, use the anonymous HTML:input instead of the containing
436       // XUL:textbox. In this case, sometimes it is a later focus event
437       // which points to the actual anonymous child with focus, so to be safe 
438       // we need to reset the selection listener every time.
439       // This happens because when some bindings handle focus, they retarget
440       // focus to the appropriate child inside of themselves, but DOM focus
441       // stays outside on that binding parent.
442       nsCOMPtr<nsIDOMEventTarget> domEventTarget;
443       nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
444       nsCOMPtr<nsIDOMNode> realFocusedNode(do_QueryInterface(domEventTarget));
445       if (realFocusedNode) {
446         mCaretAccessible->SetControlSelectionListener(realFocusedNode);
447       }
448     }
449   }
450 
451   // Check for aaa:activedescendant, which changes which element has focus
452   nsCOMPtr<nsIDOMNode> finalFocusNode = aNode;
453   nsCOMPtr<nsIAccessible> finalFocusAccessible = aAccessible;
454   nsCOMPtr<nsIContent> finalFocusContent  = do_QueryInterface(aNode);
455   if (finalFocusContent) {
456     nsAutoString id;
457     if (finalFocusContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::activedescendant, id)) {
458       nsCOMPtr<nsIDOMDocument> domDoc;
459       aNode->GetOwnerDocument(getter_AddRefs(domDoc));
460       if (!domDoc) {
461         return PR_FALSE;
462       }
463       nsCOMPtr<nsIDOMElement> relatedEl;
464       domDoc->GetElementById(id, getter_AddRefs(relatedEl));
465       finalFocusNode = do_QueryInterface(relatedEl);
466       if (!finalFocusNode) {
467         return PR_FALSE;
468       }
469       GetAccService()->GetAccessibleFor(finalFocusNode, getter_AddRefs(finalFocusAccessible));      
470       // For activedescendant, the ARIA spec does not require that the user agent
471       // checks whether finalFocusNode is actually a descendant of the element with
472       // the activedescendant attribute.
473       if (!finalFocusAccessible) {
474         return PR_FALSE;
475       }
476     }
477   }
478 
479   // Fire focus only if it changes, but always fire focus events when aForceEvent == PR_TRUE
480   if (gLastFocusedNode == finalFocusNode && !aForceEvent) {
481     return PR_FALSE;
482   }
483 
484   PRUint32 role = Role(finalFocusAccessible);
485   if (role == nsIAccessibleRole::ROLE_MENUITEM) {
486     if (!mCurrentARIAMenubar) {  // Entering menus
487       PRUint32 naturalRole; // The natural role is the role that this type of element normally has
488       finalFocusAccessible->GetRole(&naturalRole);
489       if (role != naturalRole) { // Must be a DHTML menuitem
490         mCurrentARIAMenubar =
491           nsAccUtils::GetAncestorWithRole(finalFocusAccessible, nsIAccessibleRole::ROLE_MENUBAR);
492         if (mCurrentARIAMenubar) {
493           nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_START, mCurrentARIAMenubar);
494         }
495       }
496     }
497   }
498   else if (mCurrentARIAMenubar) {
499     nsCOMPtr<nsIAccessibleEvent> menuEndEvent =
500       new nsAccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar, nsnull, PR_FALSE);
501     if (menuEndEvent) {
502       FireDelayedAccessibleEvent(menuEndEvent, eAllowDupes, PR_FALSE);
503     }
504     mCurrentARIAMenubar = nsnull;
505   }
506 
507   NS_IF_RELEASE(gLastFocusedNode);
508   gLastFocusedNode = finalFocusNode;
509   NS_IF_ADDREF(gLastFocusedNode);
510 
511   nsCOMPtr<nsIAccessibleDocument> docAccessible = do_QueryInterface(finalFocusAccessible);
512   if (docAccessible) {
513     // Doc is gaining focus, but actual focus may be on an element within document
514     nsCOMPtr<nsIDOMNode> realFocusedNode = GetCurrentFocus();
515     if (realFocusedNode != aNode || realFocusedNode == mDOMNode) {
516       // Suppress document focus, because real DOM focus will be fired next,
517       // and that's what we care about
518       // Make sure we never fire focus for the nsRootAccessible (mDOMNode)
519       return PR_FALSE;
520     }
521   }
522 
523   FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS,
524                           finalFocusNode, nsnull, eRemoveDupes, aIsAsynch);
525 
526   return PR_TRUE;
527 }
528 
529 void nsRootAccessible::FireCurrentFocusEvent()
530 {
531   nsCOMPtr<nsIDOMNode> focusedNode = GetCurrentFocus();
532   if (!focusedNode) {
533     return; // No current focus
534   }
535 
536   // Simulate a focus event so that we can reuse code that fires focus for container children like treeitems
537   nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(mDocument);
538   if (docEvent) {
539     nsCOMPtr<nsIDOMEvent> event;
540     if (NS_SUCCEEDED(docEvent->CreateEvent(NS_LITERAL_STRING("Events"),
541                                            getter_AddRefs(event))) &&
542         NS_SUCCEEDED(event->InitEvent(NS_LITERAL_STRING("focus"), PR_TRUE, PR_TRUE))) {
543       // Get the target node we really want for the event.
544       nsIAccessibilityService* accService = GetAccService();
545       if (accService) {
546         nsCOMPtr<nsIDOMNode> targetNode;
547         accService->GetRelevantContentNodeFor(focusedNode,
548                                             getter_AddRefs(targetNode));
549         if (targetNode) {
550           HandleEventWithTarget(event, targetNode);
551         }
552       }
553     }
554   }
555 }
556 
557 // --------------- nsIDOMEventListener Methods (3) ------------------------
558 
559 NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
560 {
561   // Turn DOM events in accessibility events
562   // Get info about event and target
563   nsCOMPtr<nsIDOMNode> targetNode;
564   GetTargetNode(aEvent, getter_AddRefs(targetNode));
565   if (!targetNode)
566     return NS_ERROR_FAILURE;
567   
568   return HandleEventWithTarget(aEvent, targetNode);
569 }
570 
571 nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
572                                                  nsIDOMNode* aTargetNode)
573 {
574   nsAutoString eventType;
575   aEvent->GetType(eventType);
576   nsAutoString localName;
577   aTargetNode->GetLocalName(localName);
578 #ifdef DEBUG_A11Y
579   // Very useful for debugging, please leave this here.
580   if (eventType.EqualsLiteral("AlertActive")) {
581     printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get());
582   }
583   if (localName.LowerCaseEqualsLiteral("textbox")) {
584     printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get());
585   }
586 #endif
587 
588   nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(aTargetNode);
589   if (!eventShell) {
590     return NS_OK;
591   }
592       
593   nsIAccessibilityService *accService = GetAccService();
594   NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
595 
596   if (eventType.EqualsLiteral("pagehide")) {
597     // pagehide event can be fired under several conditions, such as HTML
598     // document going away, closing a window/dialog, and wizard page changing.
599     // We only destroy the accessible object when it's a document accessible,
600     // so that we don't destroy something still in use, like wizard page. 
601     // And we only get cached document accessible to destroy, so that we don't
602     // create it just to destroy it.
603     nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(eventShell));
604     nsCOMPtr<nsIAccessible> accessible;
605     accService->GetCachedAccessible(aTargetNode, weakShell, getter_AddRefs(accessible));
606     nsCOMPtr<nsPIAccessibleDocument> privateAccDoc = do_QueryInterface(accessible);
607     if (privateAccDoc) {
608       privateAccDoc->Destroy();
609     }
610     return NS_OK;
611   }
612 
613   if (eventType.EqualsLiteral("DOMContentLoaded")) {
614     // Don't create the doc accessible until load scripts have a chance to set
615     // role attribute for <body> or <html> element, because the value of 
616     // role attribute will be cached when the doc accessible is Init()'d
617     TryFireEarlyLoadEvent(aTargetNode);
618     return NS_OK;
619   }
620 
621   if (eventType.EqualsLiteral("TreeViewChanged")) { // Always asynch, always from user input
622     NS_ENSURE_TRUE(localName.EqualsLiteral("tree"), NS_OK);
623     nsCOMPtr<nsIContent> treeContent = do_QueryInterface(aTargetNode);
624     nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);
625     return accService->InvalidateSubtreeFor(eventShell, treeContent,
626                                             nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE);
627   }
628 
629   nsCOMPtr<nsIAccessible> accessible;
630   accService->GetAccessibleInShell(aTargetNode, eventShell,
631                                    getter_AddRefs(accessible));
632   nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
633   if (!privAcc)
634     return NS_OK;
635 
636   if (eventType.EqualsLiteral("RadioStateChange")) {
637     PRUint32 state = State(accessible);
638 
639     // radiogroup in prefWindow is exposed as a list,
640     // and panebutton is exposed as XULListitem in A11y.
641     // nsXULListitemAccessible::GetState uses STATE_SELECTED in this case,
642     // so we need to check nsIAccessibleStates::STATE_SELECTED also.
643     PRBool isEnabled = (state & (nsIAccessibleStates::STATE_CHECKED |
644                         nsIAccessibleStates::STATE_SELECTED)) != 0;
645 
646     nsCOMPtr<nsIAccessibleStateChangeEvent> accEvent =
647       new nsAccStateChangeEvent(accessible, nsIAccessibleStates::STATE_CHECKED,
648                                 PR_FALSE, isEnabled);
649     privAcc->FireAccessibleEvent(accEvent);
650 
651     if (isEnabled)
652       FireAccessibleFocusEvent(accessible, aTargetNode, aEvent);
653 
654     return NS_OK;
655   }
656 
657   if (eventType.EqualsLiteral("CheckboxStateChange")) {
658     PRUint32 state = State(accessible);
659 
660     PRBool isEnabled = state & nsIAccessibleStates::STATE_CHECKED;
661 
662     nsCOMPtr<nsIAccessibleStateChangeEvent> accEvent =
663       new nsAccStateChangeEvent(accessible,
664                                 nsIAccessibleStates::STATE_CHECKED,
665                                 PR_FALSE, isEnabled);
666 
667     return privAcc->FireAccessibleEvent(accEvent);
668   }
669 
670   nsCOMPtr<nsIAccessible> treeItemAccessible;
671 #ifdef MOZ_XUL
672   // If it's a tree element, need the currently selected item
673   if (localName.EqualsLiteral("tree")) {
674     nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
675       do_QueryInterface(aTargetNode);
676     if (multiSelect) {
677       PRInt32 treeIndex = -1;
678       multiSelect->GetCurrentIndex(&treeIndex);
679       if (treeIndex >= 0) {
680         nsCOMPtr<nsIAccessibleTreeCache> treeCache(do_QueryInterface(accessible));
681         if (!treeCache ||
682             NS_FAILED(treeCache->GetCachedTreeitemAccessible(
683                       treeIndex,
684                       nsnull,
685                       getter_AddRefs(treeItemAccessible))) ||
686                       !treeItemAccessible) {
687           return NS_ERROR_OUT_OF_MEMORY;
688         }
689         accessible = treeItemAccessible;
690       }
691     }
692   }
693 #endif
694 
695 #ifdef MOZ_XUL
696   if (treeItemAccessible && eventType.EqualsLiteral("OpenStateChange")) {
697     PRUint32 state = State(accessible); // collapsed/expanded changed
698     PRBool isEnabled = (state & nsIAccessibleStates::STATE_EXPANDED) != 0;
699 
700     nsCOMPtr<nsIAccessibleStateChangeEvent> accEvent =
701       new nsAccStateChangeEvent(accessible, nsIAccessibleStates::STATE_EXPANDED,
702                                 PR_FALSE, isEnabled);
703     return FireAccessibleEvent(accEvent);
704   }
705 
706   if (treeItemAccessible && eventType.EqualsLiteral("select")) {
707     // If multiselect tree, we should fire selectionadd or selection removed
708     if (gLastFocusedNode == aTargetNode) {
709       nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
710         do_QueryInterface(aTargetNode);
711       nsAutoString selType;
712       multiSel->GetSelType(selType);
713       if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
714         // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
715         // for each tree item. Perhaps each tree item will need to cache its
716         // selection state and fire an event after a DOM "select" event when
717         // that state changes. nsXULTreeAccessible::UpdateTreeSelection();
718         return nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
719                                         accessible);
720       }
721 
722       return nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_SELECTION,
723                                       treeItemAccessible);
724     }
725   }
726   else
727 #endif
728   if (eventType.EqualsLiteral("focus")) {
729     // Keep a reference to the target node. We might want to change
730     // it to the individual radio button or selected item, and send
731     // the focus event to that.
732     nsCOMPtr<nsIDOMNode> focusedItem(aTargetNode);
733 
734     if (!treeItemAccessible) {
735       nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
736         do_QueryInterface(aTargetNode);
737       if (selectControl) {
738         nsCOMPtr<nsIDOMXULMenuListElement> menuList =
739           do_QueryInterface(aTargetNode);
740         if (!menuList) {
741           // Don't do this for menu lists, the items only get focused
742           // when the list is open, based on DOMMenuitemActive events
743           nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
744           selectControl->GetSelectedItem(getter_AddRefs(selectedItem));
745           if (selectedItem)
746             focusedItem = do_QueryInterface(selectedItem);
747 
748           if (!focusedItem)
749             return NS_OK;
750 
751           accService->GetAccessibleInShell(focusedItem, eventShell,
752                                            getter_AddRefs(accessible));
753           if (!accessible)
754             return NS_OK;
755         }
756       }
757     }
758     FireAccessibleFocusEvent(accessible, focusedItem, aEvent);
759   }
760   else if (eventType.EqualsLiteral("NameChange")) {
761     nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, accessible);
762   }
763   else if (eventType.EqualsLiteral("AlertActive")) { 
764     nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
765   }
766   else if (eventType.EqualsLiteral("popupshown")) {
767     // Don't fire menupopup events for combobox and autocomplete lists
768     PRUint32 role = Role(accessible);
769     PRInt32 event = 0;
770     if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
771       event = nsIAccessibleEvent::EVENT_MENUPOPUP_START;
772     }
773     else if (role == nsIAccessibleRole::ROLE_TOOLTIP) {
774       // There is a single <xul:tooltip> node which Mozilla moves around.
775       // The accessible for it stays the same no matter where it moves. 
776       // AT's expect to get an EVENT_SHOW for the tooltip. 
777       // In event callback the tooltip's accessible will be ready.
778       event = nsIAccessibleEvent::EVENT_ASYNCH_SHOW;
779     }
780     if (event) {
781       nsAccUtils::FireAccEvent(event, accessible);
782     }
783   }
784 
785   else if (eventType.EqualsLiteral("popuphiding")) {
786     // If accessible focus was on or inside popup that closes,
787     // then restore it to true current focus.
788     // This is the case when we've been getting DOMMenuItemActive events
789     // inside of a combo box that closes. The real focus is on the combo box.
790     // It's also the case when a popup gets focus in ATK -- when it closes
791     // we need to fire an event to restore focus to where it was
792     if (!gLastFocusedNode) {
793       return NS_OK;
794     }
795     if (gLastFocusedNode != aTargetNode) {
796       // Was not focused on popup
797       nsCOMPtr<nsIDOMNode> parentOfFocus;
798       gLastFocusedNode->GetParentNode(getter_AddRefs(parentOfFocus));
799       if (parentOfFocus != aTargetNode) {
800         return NS_OK;  // And was not focused on an item inside the popup
801       }
802     }
803     // Focus was on or inside of a popup that's being hidden
804     FireCurrentFocusEvent();
805   }
806   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
807     if (Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
808       nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
809                                accessible);
810     }
811   }
812   else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
813     if (!treeItemAccessible) {
814       nsCOMPtr<nsPIAccessNode> menuAccessNode = do_QueryInterface(accessible);
815       NS_ENSURE_TRUE(menuAccessNode, NS_ERROR_FAILURE);
816       nsIFrame* menuFrame = menuAccessNode->GetFrame();
817       NS_ENSURE_TRUE(menuFrame, NS_ERROR_FAILURE);
818       nsIMenuFrame* imenuFrame;
819       CallQueryInterface(menuFrame, &imenuFrame);
820       // QI failed for nsIMenuFrame means it's not on menu bar
821       if (imenuFrame && imenuFrame->IsOnMenuBar() &&
822                        !imenuFrame->IsOnActiveMenuBar()) {
823         // It is a top level menuitem. Only fire a focus event when the menu bar
824         // is active.
825         return NS_OK;
826       } else {
827         nsCOMPtr<nsIAccessible> containerAccessible;
828         accessible->GetParent(getter_AddRefs(containerAccessible));
829         NS_ENSURE_TRUE(containerAccessible, NS_ERROR_FAILURE);
830         // It is not top level menuitem
831         // Only fire focus event if it is not inside collapsed popup
832         if (State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED)
833           return NS_OK;
834       }
835     }
836     nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);  // Always asynch, always from user input
837     FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE, PR_TRUE);
838   }
839   else if (eventType.EqualsLiteral("DOMMenuBarActive")) {  // Always asynch, always from user input
840     nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);
841     nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_START, accessible, PR_TRUE);
842   }
843   else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {  // Always asynch, always from user input
844     nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);
845     nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_END, accessible, PR_TRUE);
846     FireCurrentFocusEvent();
847   }
848   else if (eventType.EqualsLiteral("ValueChange")) {
849     nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, accessible);
850   }
851   return NS_OK;
852 }
853 
854 void nsRootAccessible::GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode)
855 {
856   *aTargetNode = nsnull;
857 
858   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aEvent));
859 
860   if (!nsevent)
861     return;
862 
863   nsCOMPtr<nsIDOMEventTarget> domEventTarget;
864   nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
865   nsCOMPtr<nsIDOMNode> eventTarget(do_QueryInterface(domEventTarget));
866   if (!eventTarget)
867     return;
868 
869   nsIAccessibilityService* accService = GetAccService();
870   if (accService) {
871     nsresult rv = accService->GetRelevantContentNodeFor(eventTarget,
872                                                         aTargetNode);
873     if (NS_SUCCEEDED(rv) && *aTargetNode)
874       return;
875   }
876 
877   NS_ADDREF(*aTargetNode = eventTarget);
878 }
879 
880 void nsRootAccessible::FireFocusCallback(nsITimer *aTimer, void *aClosure)
881 {
882   nsRootAccessible *rootAccessible = static_cast<nsRootAccessible*>(aClosure);
883   NS_ASSERTION(rootAccessible, "How did we get here without a root accessible?");
884   rootAccessible->FireCurrentFocusEvent();
885 }
886 
887 NS_IMETHODIMP
888 nsRootAccessible::Init()
889 {
890   nsresult rv = nsDocAccessibleWrap::Init();
891   NS_ENSURE_SUCCESS(rv, rv);
892 
893   nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
894   NS_ENSURE_STATE(root);
895 
896   root->AddRootAccessible(this);
897   return NS_OK;
898 }
899 
900 NS_IMETHODIMP nsRootAccessible::Shutdown()
901 {
902   nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
903   NS_ENSURE_STATE(root);
904 
905   root->RemoveRootAccessible(this);
906 
907   // Called manually or by nsAccessNode::LastRelease()
908   if (!mWeakShell) {
909     return NS_OK;  // Already shutdown
910   }
911   mCurrentARIAMenubar = nsnull;
912 
913   if (mFireFocusTimer) {
914     mFireFocusTimer->Cancel();
915     mFireFocusTimer = nsnull;
916   }
917 
918   return nsDocAccessibleWrap::Shutdown();
919 }
920 
921 already_AddRefed<nsIDocShellTreeItem>
922 nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
923 {
924   if (!aStart) {
925     return nsnull;
926   }
927 
928   PRInt32 itemType;
929   aStart->GetItemType(&itemType);
930   if (itemType == nsIDocShellTreeItem::typeContent) {
931     nsCOMPtr<nsIAccessibleDocument> accDoc =
932       GetDocAccessibleFor(aStart, PR_TRUE);
933     nsCOMPtr<nsIAccessible> accessible = do_QueryInterface(accDoc);
934     // If ancestor chain of accessibles is not completely visible,
935     // don't use this one. This happens for example if it's inside
936     // a background tab (tabbed browsing)
937     while (accessible) {
938       if (State(accessible) & nsIAccessibleStates::STATE_INVISIBLE) {
939         return nsnull;
940       }
941       nsCOMPtr<nsIAccessible> ancestor;
942       accessible->GetParent(getter_AddRefs(ancestor));
943       if (ancestor == this) {
944         break; // Don't check past original root accessible we started with
945       }
946       accessible.swap(ancestor);
947     }
948 
949     NS_ADDREF(aStart);
950     return aStart;
951   }
952   nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
953   if (treeNode) {
954     PRInt32 subDocuments;
955     treeNode->GetChildCount(&subDocuments);
956     for (PRInt32 count = 0; count < subDocuments; count ++) {
957       nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
958       treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
959       NS_ENSURE_TRUE(treeItemChild, nsnull);
960       contentTreeItem = GetContentDocShell(treeItemChild);
961       if (contentTreeItem) {
962         NS_ADDREF(aStart = contentTreeItem);
963         return aStart;
964       }
965     }
966   }
967   return nsnull;
968 }
969 
970 NS_IMETHODIMP nsRootAccessible::GetAccessibleRelated(PRUint32 aRelationType,
971                                                      nsIAccessible **aRelated)
972 {
973   *aRelated = nsnull;
974 
975   if (!mDOMNode || aRelationType != nsIAccessibleRelation::RELATION_EMBEDS) {
976     return nsDocAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
977   }
978 
979   nsCOMPtr<nsIDocShellTreeItem> treeItem =
980     nsAccUtils::GetDocShellTreeItemFor(mDOMNode);
981   nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
982   // there may be no content area, so we need a null check
983   if (contentTreeItem) {
984     nsCOMPtr<nsIAccessibleDocument> accDoc =
985       GetDocAccessibleFor(contentTreeItem, PR_TRUE);
986     NS_ASSERTION(accDoc, "No EMBEDS document");
987     if (accDoc) {
988       accDoc->QueryInterface(NS_GET_IID(nsIAccessible), (void**)aRelated);
989     }
990   }
991   return NS_OK;
992 }
993 
994 NS_IMETHODIMP nsRootAccessible::FireDocLoadEvents(PRUint32 aEventType)
995 {
996   if (!mDocument || !mWeakShell) {
997     return NS_OK;  // Document has been shut down
998   }
999 
1000   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
1001     nsAccUtils::GetDocShellTreeItemFor(mDOMNode);
1002   NS_ASSERTION(docShellTreeItem, "No doc shell tree item for document");
1003   NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
1004   PRInt32 contentType;
1005   docShellTreeItem->GetItemType(&contentType);
1006   if (contentType == nsIDocShellTreeItem::typeContent) {
1007     return nsDocAccessibleWrap::FireDocLoadEvents(aEventType); // Content might need to fire event
1008   }
1009 
1010   // Root chrome: don't fire event
1011   mIsContentLoaded = (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE ||
1012                       aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED);
1013 
1014   return NS_OK;
1015 }
1016 

This page was automatically generated by LXR.