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 : #include "mozilla/Util.h"
39 :
40 : #define CreateEvent CreateEventA
41 : #include "nsIDOMDocument.h"
42 :
43 : #include "nsAccessibilityService.h"
44 : #include "nsApplicationAccessibleWrap.h"
45 : #include "nsAccUtils.h"
46 : #include "nsCoreUtils.h"
47 : #include "Relation.h"
48 : #include "Role.h"
49 : #include "States.h"
50 :
51 : #include "mozilla/dom/Element.h"
52 : #include "nsHTMLSelectAccessible.h"
53 : #include "nsIAccessibleRelation.h"
54 : #include "nsIDocShell.h"
55 : #include "nsIDocShellTreeItem.h"
56 : #include "nsIDocShellTreeNode.h"
57 : #include "nsIDocShellTreeOwner.h"
58 : #include "nsIDOMElement.h"
59 : #include "nsIDOMEventListener.h"
60 : #include "nsIDOMEventTarget.h"
61 : #include "nsIDOMHTMLAnchorElement.h"
62 : #include "nsIDOMHTMLImageElement.h"
63 : #include "nsIDOMHTMLInputElement.h"
64 : #include "nsIDOMHTMLSelectElement.h"
65 : #include "nsIDOMDataContainerEvent.h"
66 : #include "nsIDOMNSEvent.h"
67 : #include "nsIDOMXULMultSelectCntrlEl.h"
68 : #include "nsIDOMXULPopupElement.h"
69 : #include "nsIDocument.h"
70 : #include "nsEventListenerManager.h"
71 : #include "nsIFrame.h"
72 : #include "nsIHTMLDocument.h"
73 : #include "nsIInterfaceRequestorUtils.h"
74 : #include "nsISelectionPrivate.h"
75 : #include "nsIServiceManager.h"
76 : #include "nsPIDOMWindow.h"
77 : #include "nsIWebBrowserChrome.h"
78 : #include "nsReadableUtils.h"
79 : #include "nsRootAccessible.h"
80 : #include "nsIPrivateDOMEvent.h"
81 : #include "nsFocusManager.h"
82 :
83 : #ifdef MOZ_XUL
84 : #include "nsXULTreeAccessible.h"
85 : #include "nsIXULDocument.h"
86 : #include "nsIXULWindow.h"
87 : #endif
88 :
89 : using namespace mozilla;
90 : using namespace mozilla::a11y;
91 :
92 : ////////////////////////////////////////////////////////////////////////////////
93 : // nsISupports
94 :
95 : // Expanded version of NS_IMPL_ISUPPORTS_INHERITED2
96 : // so we can QI directly to concrete nsRootAccessible
97 0 : NS_IMPL_QUERY_HEAD(nsRootAccessible)
98 0 : NS_IMPL_QUERY_BODY(nsIDOMEventListener)
99 0 : if (aIID.Equals(NS_GET_IID(nsRootAccessible)))
100 0 : foundInterface = reinterpret_cast<nsISupports*>(this);
101 : else
102 0 : NS_IMPL_QUERY_TAIL_INHERITING(nsDocAccessible)
103 :
104 0 : NS_IMPL_ADDREF_INHERITED(nsRootAccessible, nsDocAccessible)
105 0 : NS_IMPL_RELEASE_INHERITED(nsRootAccessible, nsDocAccessible)
106 :
107 : ////////////////////////////////////////////////////////////////////////////////
108 : // Constructor/desctructor
109 :
110 0 : nsRootAccessible::
111 : nsRootAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
112 : nsIPresShell* aPresShell) :
113 0 : nsDocAccessibleWrap(aDocument, aRootContent, aPresShell)
114 : {
115 0 : mFlags |= eRootAccessible;
116 0 : }
117 :
118 0 : nsRootAccessible::~nsRootAccessible()
119 : {
120 0 : }
121 :
122 : ////////////////////////////////////////////////////////////////////////////////
123 : // nsIAccessible
124 :
125 : /* readonly attribute AString name; */
126 : NS_IMETHODIMP
127 0 : nsRootAccessible::GetName(nsAString& aName)
128 : {
129 0 : aName.Truncate();
130 :
131 0 : if (!mDocument) {
132 0 : return NS_ERROR_FAILURE;
133 : }
134 :
135 0 : if (mRoleMapEntry) {
136 0 : nsAccessible::GetName(aName);
137 0 : if (!aName.IsEmpty()) {
138 0 : return NS_OK;
139 : }
140 : }
141 :
142 0 : nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocument);
143 0 : return document->GetTitle(aName);
144 : }
145 :
146 : role
147 0 : nsRootAccessible::NativeRole()
148 : {
149 : // If it's a <dialog> or <wizard>, use roles::DIALOG instead
150 0 : dom::Element *root = mDocument->GetRootElement();
151 0 : if (root) {
152 0 : nsCOMPtr<nsIDOMElement> rootElement(do_QueryInterface(root));
153 0 : if (rootElement) {
154 0 : nsAutoString name;
155 0 : rootElement->GetLocalName(name);
156 0 : if (name.EqualsLiteral("dialog") || name.EqualsLiteral("wizard")) {
157 0 : return roles::DIALOG; // Always at the root
158 : }
159 : }
160 : }
161 :
162 0 : return nsDocAccessibleWrap::NativeRole();
163 : }
164 :
165 : // nsRootAccessible protected member
166 : #ifdef MOZ_XUL
167 0 : PRUint32 nsRootAccessible::GetChromeFlags()
168 : {
169 : // Return the flag set for the top level window as defined
170 : // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
171 : // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
172 : nsCOMPtr<nsIDocShellTreeItem> treeItem =
173 0 : nsCoreUtils::GetDocShellTreeItemFor(mDocument);
174 0 : NS_ENSURE_TRUE(treeItem, 0);
175 0 : nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
176 0 : treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
177 0 : NS_ENSURE_TRUE(treeOwner, 0);
178 0 : nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
179 0 : if (!xulWin) {
180 0 : return 0;
181 : }
182 : PRUint32 chromeFlags;
183 0 : xulWin->GetChromeFlags(&chromeFlags);
184 0 : return chromeFlags;
185 : }
186 : #endif
187 :
188 : PRUint64
189 0 : nsRootAccessible::NativeState()
190 : {
191 0 : PRUint64 states = nsDocAccessibleWrap::NativeState();
192 :
193 : #ifdef MOZ_XUL
194 0 : PRUint32 chromeFlags = GetChromeFlags();
195 0 : if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
196 0 : states |= states::SIZEABLE;
197 : // If it has a titlebar it's movable
198 : // XXX unless it's minimized or maximized, but not sure
199 : // how to detect that
200 0 : if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
201 0 : states |= states::MOVEABLE;
202 0 : if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
203 0 : states |= states::MODAL;
204 : #endif
205 :
206 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
207 0 : if (fm) {
208 0 : nsCOMPtr<nsIDOMWindow> rootWindow;
209 0 : GetWindow(getter_AddRefs(rootWindow));
210 :
211 0 : nsCOMPtr<nsIDOMWindow> activeWindow;
212 0 : fm->GetActiveWindow(getter_AddRefs(activeWindow));
213 0 : if (activeWindow == rootWindow)
214 0 : states |= states::ACTIVE;
215 : }
216 :
217 0 : return states;
218 : }
219 :
220 : const char* const docEvents[] = {
221 : #ifdef DEBUG_DRAGDROPSTART
222 : // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
223 : // debugging a11y objects with event viewers
224 : "mouseover",
225 : #endif
226 : // capture Form change events
227 : "select",
228 : // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
229 : "ValueChange",
230 : // capture AlertActive events (fired whenever alert pops up)
231 : "AlertActive",
232 : // add ourself as a TreeViewChanged listener (custom event fired in nsTreeBodyFrame.cpp)
233 : "TreeViewChanged",
234 : "TreeRowCountChanged",
235 : "TreeInvalidated",
236 : // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
237 : "OpenStateChange",
238 : // add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
239 : "CheckboxStateChange",
240 : // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp & radio.xml)
241 : "RadioStateChange",
242 : "popupshown",
243 : "popuphiding",
244 : "DOMMenuInactive",
245 : "DOMMenuItemActive",
246 : "DOMMenuItemInactive",
247 : "DOMMenuBarActive",
248 : "DOMMenuBarInactive"
249 : };
250 :
251 0 : nsresult nsRootAccessible::AddEventListeners()
252 : {
253 : // nsIDOMEventTarget interface allows to register event listeners to
254 : // receive untrusted events (synthetic events generated by untrusted code).
255 : // For example, XBL bindings implementations for elements that are hosted in
256 : // non chrome document fire untrusted events.
257 0 : nsCOMPtr<nsIDOMEventTarget> nstarget(do_QueryInterface(mDocument));
258 :
259 0 : if (nstarget) {
260 0 : for (const char* const* e = docEvents,
261 0 : * const* e_end = ArrayEnd(docEvents);
262 : e < e_end; ++e) {
263 0 : nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
264 0 : this, true, true, 2);
265 0 : NS_ENSURE_SUCCESS(rv, rv);
266 : }
267 : }
268 :
269 0 : if (!mCaretAccessible) {
270 0 : mCaretAccessible = new nsCaretAccessible(this);
271 : }
272 :
273 0 : return nsDocAccessible::AddEventListeners();
274 : }
275 :
276 0 : nsresult nsRootAccessible::RemoveEventListeners()
277 : {
278 0 : nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
279 0 : if (target) {
280 0 : for (const char* const* e = docEvents,
281 0 : * const* e_end = ArrayEnd(docEvents);
282 : e < e_end; ++e) {
283 0 : nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
284 0 : NS_ENSURE_SUCCESS(rv, rv);
285 : }
286 : }
287 :
288 : // Do this before removing clearing caret accessible, so that it can use
289 : // shutdown the caret accessible's selection listener
290 0 : nsDocAccessible::RemoveEventListeners();
291 :
292 0 : if (mCaretAccessible) {
293 0 : mCaretAccessible->Shutdown();
294 0 : mCaretAccessible = nsnull;
295 : }
296 :
297 0 : return NS_OK;
298 : }
299 :
300 : ////////////////////////////////////////////////////////////////////////////////
301 : // public
302 :
303 : nsCaretAccessible*
304 0 : nsRootAccessible::GetCaretAccessible()
305 : {
306 0 : return mCaretAccessible;
307 : }
308 :
309 : void
310 0 : nsRootAccessible::DocumentActivated(nsDocAccessible* aDocument)
311 : {
312 0 : }
313 :
314 : ////////////////////////////////////////////////////////////////////////////////
315 : // nsIDOMEventListener
316 :
317 : NS_IMETHODIMP
318 0 : nsRootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
319 : {
320 0 : nsCOMPtr<nsIDOMNSEvent> DOMNSEvent(do_QueryInterface(aDOMEvent));
321 0 : nsCOMPtr<nsIDOMEventTarget> DOMEventTarget;
322 0 : DOMNSEvent->GetOriginalTarget(getter_AddRefs(DOMEventTarget));
323 0 : nsCOMPtr<nsINode> origTargetNode(do_QueryInterface(DOMEventTarget));
324 0 : if (!origTargetNode)
325 0 : return NS_OK;
326 :
327 : nsDocAccessible* document =
328 0 : GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
329 :
330 0 : if (document) {
331 : #ifdef DEBUG_NOTIFICATIONS
332 : if (origTargetNode->IsElement()) {
333 : nsIContent* elm = origTargetNode->AsElement();
334 :
335 : nsAutoString tag;
336 : elm->Tag()->ToString(tag);
337 :
338 : nsIAtom* atomid = elm->GetID();
339 : nsCAutoString id;
340 : if (atomid)
341 : atomid->ToUTF8String(id);
342 :
343 : nsAutoString eventType;
344 : aDOMEvent->GetType(eventType);
345 :
346 : printf("\nPend DOM event processing for %s@id='%s', type: %s\n\n",
347 : NS_ConvertUTF16toUTF8(tag).get(), id.get(),
348 : NS_ConvertUTF16toUTF8(eventType).get());
349 : }
350 : #endif
351 :
352 : // Root accessible exists longer than any of its descendant documents so
353 : // that we are guaranteed notification is processed before root accessible
354 : // is destroyed.
355 : document->HandleNotification<nsRootAccessible, nsIDOMEvent>
356 0 : (this, &nsRootAccessible::ProcessDOMEvent, aDOMEvent);
357 : }
358 :
359 0 : return NS_OK;
360 : }
361 :
362 : // nsRootAccessible protected
363 : void
364 0 : nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
365 : {
366 0 : nsCOMPtr<nsIDOMNSEvent> DOMNSEvent(do_QueryInterface(aDOMEvent));
367 0 : nsCOMPtr<nsIDOMEventTarget> DOMEventTarget;
368 0 : DOMNSEvent->GetOriginalTarget(getter_AddRefs(DOMEventTarget));
369 0 : nsCOMPtr<nsINode> origTargetNode(do_QueryInterface(DOMEventTarget));
370 :
371 0 : nsAutoString eventType;
372 0 : aDOMEvent->GetType(eventType);
373 :
374 0 : if (eventType.EqualsLiteral("popuphiding")) {
375 0 : HandlePopupHidingEvent(origTargetNode);
376 : return;
377 : }
378 :
379 0 : nsDocAccessible* targetDocument = GetAccService()->
380 0 : GetDocAccessible(origTargetNode->OwnerDoc());
381 0 : NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
382 :
383 : nsAccessible* accessible =
384 0 : targetDocument->GetAccessibleOrContainer(origTargetNode);
385 0 : if (!accessible)
386 : return;
387 :
388 0 : nsINode* targetNode = accessible->GetNode();
389 :
390 : #ifdef MOZ_XUL
391 0 : nsRefPtr<nsXULTreeAccessible> treeAcc;
392 0 : if (targetNode->IsElement() &&
393 0 : targetNode->AsElement()->NodeInfo()->Equals(nsGkAtoms::tree,
394 0 : kNameSpaceID_XUL)) {
395 0 : treeAcc = do_QueryObject(accessible);
396 0 : if (treeAcc) {
397 0 : if (eventType.EqualsLiteral("TreeViewChanged")) {
398 0 : treeAcc->TreeViewChanged();
399 : return;
400 : }
401 :
402 0 : if (eventType.EqualsLiteral("TreeRowCountChanged")) {
403 0 : HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
404 : return;
405 : }
406 :
407 0 : if (eventType.EqualsLiteral("TreeInvalidated")) {
408 0 : HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
409 : return;
410 : }
411 : }
412 : }
413 : #endif
414 :
415 0 : if (eventType.EqualsLiteral("RadioStateChange")) {
416 0 : PRUint64 state = accessible->State();
417 :
418 : // radiogroup in prefWindow is exposed as a list,
419 : // and panebutton is exposed as XULListitem in A11y.
420 : // nsXULListitemAccessible::GetStateInternal uses STATE_SELECTED in this case,
421 : // so we need to check states::SELECTED also.
422 0 : bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
423 :
424 : nsRefPtr<AccEvent> accEvent =
425 0 : new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
426 0 : nsEventShell::FireEvent(accEvent);
427 :
428 0 : if (isEnabled) {
429 0 : FocusMgr()->ActiveItemChanged(accessible);
430 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("RadioStateChange", accessible)
431 : }
432 :
433 : return;
434 : }
435 :
436 0 : if (eventType.EqualsLiteral("CheckboxStateChange")) {
437 0 : PRUint64 state = accessible->State();
438 :
439 0 : bool isEnabled = !!(state & states::CHECKED);
440 :
441 : nsRefPtr<AccEvent> accEvent =
442 0 : new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
443 :
444 0 : nsEventShell::FireEvent(accEvent);
445 : return;
446 : }
447 :
448 0 : nsAccessible* treeItemAcc = nsnull;
449 : #ifdef MOZ_XUL
450 : // If it's a tree element, need the currently selected item.
451 0 : if (treeAcc) {
452 0 : treeItemAcc = accessible->CurrentItem();
453 0 : if (treeItemAcc)
454 0 : accessible = treeItemAcc;
455 : }
456 :
457 0 : if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
458 0 : PRUint64 state = accessible->State();
459 0 : bool isEnabled = (state & states::EXPANDED) != 0;
460 :
461 : nsRefPtr<AccEvent> accEvent =
462 0 : new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
463 0 : nsEventShell::FireEvent(accEvent);
464 : return;
465 : }
466 :
467 0 : if (treeItemAcc && eventType.EqualsLiteral("select")) {
468 : // XXX: We shouldn't be based on DOM select event which doesn't provide us
469 : // any context info. We should integrate into nsTreeSelection instead.
470 : // If multiselect tree, we should fire selectionadd or selection removed
471 0 : if (FocusMgr()->HasDOMFocus(targetNode)) {
472 : nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
473 0 : do_QueryInterface(targetNode);
474 0 : nsAutoString selType;
475 0 : multiSel->GetSelType(selType);
476 0 : if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
477 : // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
478 : // for each tree item. Perhaps each tree item will need to cache its
479 : // selection state and fire an event after a DOM "select" event when
480 : // that state changes. nsXULTreeAccessible::UpdateTreeSelection();
481 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
482 0 : accessible);
483 : return;
484 : }
485 :
486 : nsRefPtr<AccSelChangeEvent> selChangeEvent =
487 : new AccSelChangeEvent(treeAcc, treeItemAcc,
488 0 : AccSelChangeEvent::eSelectionAdd);
489 0 : nsEventShell::FireEvent(selChangeEvent);
490 : return;
491 : }
492 : }
493 : else
494 : #endif
495 0 : if (eventType.EqualsLiteral("AlertActive")) {
496 0 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
497 : }
498 0 : else if (eventType.EqualsLiteral("popupshown")) {
499 0 : HandlePopupShownEvent(accessible);
500 : }
501 0 : else if (eventType.EqualsLiteral("DOMMenuInactive")) {
502 0 : if (accessible->Role() == roles::MENUPOPUP) {
503 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
504 0 : accessible);
505 : }
506 : }
507 0 : else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
508 0 : FocusMgr()->ActiveItemChanged(accessible);
509 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemActive", accessible)
510 : }
511 0 : else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
512 : // Process DOMMenuItemInactive event for autocomplete only because this is
513 : // unique widget that may acquire focus from autocomplete popup while popup
514 : // stays open and has no active item. In case of XUL tree autocomplete
515 : // popup this event is fired for tree accessible.
516 : nsAccessible* widget =
517 0 : accessible->IsWidget() ? accessible : accessible->ContainerWidget();
518 0 : if (widget && widget->IsAutoCompletePopup()) {
519 0 : FocusMgr()->ActiveItemChanged(nsnull);
520 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemInactive", accessible)
521 : }
522 : }
523 0 : else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
524 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
525 0 : accessible, eFromUserInput);
526 :
527 : // Notify of active item change when menubar gets active and if it has
528 : // current item. This is a case of mouseover (set current menuitem) and
529 : // mouse click (activate the menubar). If menubar doesn't have current item
530 : // (can be a case of menubar activation from keyboard) then ignore this
531 : // notification because later we'll receive DOMMenuItemActive event after
532 : // current menuitem is set.
533 0 : nsAccessible* activeItem = accessible->CurrentItem();
534 0 : if (activeItem) {
535 0 : FocusMgr()->ActiveItemChanged(activeItem);
536 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarActive", accessible)
537 : }
538 : }
539 0 : else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
540 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
541 0 : accessible, eFromUserInput);
542 :
543 0 : FocusMgr()->ActiveItemChanged(nsnull);
544 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarInactive", accessible)
545 : }
546 0 : else if (eventType.EqualsLiteral("ValueChange")) {
547 : targetDocument->
548 : FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
549 0 : targetNode, AccEvent::eRemoveDupes);
550 : }
551 : #ifdef DEBUG_DRAGDROPSTART
552 : else if (eventType.EqualsLiteral("mouseover")) {
553 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
554 : accessible);
555 : }
556 : #endif
557 : }
558 :
559 :
560 : ////////////////////////////////////////////////////////////////////////////////
561 : // nsAccessNode
562 :
563 : void
564 0 : nsRootAccessible::Shutdown()
565 : {
566 : // Called manually or by nsAccessNode::LastRelease()
567 0 : if (!PresShell())
568 0 : return; // Already shutdown
569 :
570 0 : nsDocAccessibleWrap::Shutdown();
571 : }
572 :
573 : // nsIAccessible method
574 : Relation
575 0 : nsRootAccessible::RelationByType(PRUint32 aType)
576 : {
577 0 : if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
578 0 : return nsDocAccessibleWrap::RelationByType(aType);
579 :
580 0 : nsIDOMWindow* rootWindow = mDocument->GetWindow();
581 0 : if (rootWindow) {
582 0 : nsCOMPtr<nsIDOMWindow> contentWindow;
583 0 : rootWindow->GetContent(getter_AddRefs(contentWindow));
584 0 : if (contentWindow) {
585 0 : nsCOMPtr<nsIDOMDocument> contentDOMDocument;
586 0 : contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
587 : nsCOMPtr<nsIDocument> contentDocumentNode =
588 0 : do_QueryInterface(contentDOMDocument);
589 0 : if (contentDocumentNode) {
590 : nsDocAccessible* contentDocument =
591 0 : GetAccService()->GetDocAccessible(contentDocumentNode);
592 0 : if (contentDocument)
593 0 : return Relation(contentDocument);
594 : }
595 : }
596 : }
597 :
598 0 : return Relation();
599 : }
600 :
601 : ////////////////////////////////////////////////////////////////////////////////
602 : // Protected members
603 :
604 : void
605 0 : nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
606 : {
607 0 : roles::Role role = aAccessible->Role();
608 :
609 0 : if (role == roles::MENUPOPUP) {
610 : // Don't fire menupopup events for combobox and autocomplete lists.
611 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
612 0 : aAccessible);
613 0 : return;
614 : }
615 :
616 0 : if (role == roles::TOOLTIP) {
617 : // There is a single <xul:tooltip> node which Mozilla moves around.
618 : // The accessible for it stays the same no matter where it moves.
619 : // AT's expect to get an EVENT_SHOW for the tooltip.
620 : // In event callback the tooltip's accessible will be ready.
621 0 : nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
622 0 : return;
623 : }
624 :
625 0 : if (role == roles::COMBOBOX_LIST) {
626 : // Fire expanded state change event for comboboxes and autocompeletes.
627 0 : nsAccessible* combobox = aAccessible->Parent();
628 0 : if (!combobox)
629 0 : return;
630 :
631 0 : roles::Role comboboxRole = combobox->Role();
632 0 : if (comboboxRole == roles::COMBOBOX ||
633 : comboboxRole == roles::AUTOCOMPLETE) {
634 : nsRefPtr<AccEvent> event =
635 0 : new AccStateChangeEvent(combobox, states::EXPANDED, true);
636 0 : if (event)
637 0 : nsEventShell::FireEvent(event);
638 : }
639 : }
640 : }
641 :
642 : void
643 0 : nsRootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
644 : {
645 : // Get popup accessible. There are cases when popup element isn't accessible
646 : // but an underlying widget is and behaves like popup, an example is
647 : // autocomplete popups.
648 0 : nsDocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
649 0 : if (!document)
650 0 : return;
651 :
652 0 : nsAccessible* popup = document->GetAccessible(aPopupNode);
653 0 : if (!popup) {
654 0 : nsAccessible* popupContainer = document->GetContainerAccessible(aPopupNode);
655 0 : if (!popupContainer)
656 0 : return;
657 :
658 0 : PRInt32 childCount = popupContainer->GetChildCount();
659 0 : for (PRInt32 idx = 0; idx < childCount; idx++) {
660 0 : nsAccessible* child = popupContainer->GetChildAt(idx);
661 0 : if (child->IsAutoCompletePopup()) {
662 0 : popup = child;
663 0 : break;
664 : }
665 : }
666 :
667 : // No popup no events. Focus is managed by DOM. This is a case for
668 : // menupopups of menus on Linux since there are no accessible for popups.
669 0 : if (!popup)
670 0 : return;
671 : }
672 :
673 : // In case of autocompletes and comboboxes fire state change event for
674 : // expanded state. Note, HTML form autocomplete isn't a subject of state
675 : // change event because they aren't autocompletes strictly speaking.
676 : // When popup closes (except nested popups and menus) then fire focus event to
677 : // where it was. The focus event is expected even if popup didn't take a focus.
678 :
679 : static const PRUint32 kNotifyOfFocus = 1;
680 : static const PRUint32 kNotifyOfState = 2;
681 0 : PRUint32 notifyOf = 0;
682 :
683 : // HTML select is target of popuphidding event. Otherwise get container
684 : // widget. No container widget means this is either tooltip or menupopup.
685 : // No events in the former case.
686 0 : nsAccessible* widget = nsnull;
687 0 : if (popup->IsCombobox()) {
688 0 : widget = popup;
689 : } else {
690 0 : widget = popup->ContainerWidget();
691 0 : if (!widget) {
692 0 : if (!popup->IsMenuPopup())
693 0 : return;
694 :
695 0 : widget = popup;
696 : }
697 : }
698 :
699 0 : if (popup->IsAutoCompletePopup()) {
700 : // No focus event for autocomplete because it's managed by
701 : // DOMMenuItemInactive events.
702 0 : if (widget->IsAutoComplete())
703 0 : notifyOf = kNotifyOfState;
704 :
705 0 : } else if (widget->IsCombobox()) {
706 : // Fire focus for active combobox, otherwise the focus is managed by DOM
707 : // focus notifications. Always fire state change event.
708 0 : if (widget->IsActiveWidget())
709 0 : notifyOf = kNotifyOfFocus;
710 0 : notifyOf |= kNotifyOfState;
711 :
712 0 : } else if (widget->IsMenuButton()) {
713 : // Can be a part of autocomplete.
714 0 : nsAccessible* compositeWidget = widget->ContainerWidget();
715 0 : if (compositeWidget && compositeWidget->IsAutoComplete()) {
716 0 : widget = compositeWidget;
717 0 : notifyOf = kNotifyOfState;
718 : }
719 :
720 : // Autocomplete (like searchbar) can be inactive when popup hiddens
721 0 : notifyOf |= kNotifyOfFocus;
722 :
723 0 : } else if (widget == popup) {
724 : // Top level context menus and alerts.
725 : // Ignore submenus and menubar. When submenu is closed then sumbenu
726 : // container menuitem takes a focus via DOMMenuItemActive notification.
727 : // For menubars processing we listen DOMMenubarActive/Inactive
728 : // notifications.
729 0 : notifyOf = kNotifyOfFocus;
730 : }
731 :
732 : // Restore focus to where it was.
733 0 : if (notifyOf & kNotifyOfFocus) {
734 0 : FocusMgr()->ActiveItemChanged(nsnull);
735 : A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("popuphiding", popup)
736 : }
737 :
738 : // Fire expanded state change event.
739 0 : if (notifyOf & kNotifyOfState) {
740 : nsRefPtr<AccEvent> event =
741 0 : new AccStateChangeEvent(widget, states::EXPANDED, false);
742 0 : document->FireDelayedAccessibleEvent(event);
743 : }
744 : }
745 :
746 : #ifdef MOZ_XUL
747 : void
748 0 : nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
749 : nsXULTreeAccessible* aAccessible)
750 : {
751 0 : nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
752 0 : if (!dataEvent)
753 : return;
754 :
755 0 : nsCOMPtr<nsIVariant> indexVariant;
756 0 : dataEvent->GetData(NS_LITERAL_STRING("index"),
757 0 : getter_AddRefs(indexVariant));
758 0 : if (!indexVariant)
759 : return;
760 :
761 0 : nsCOMPtr<nsIVariant> countVariant;
762 0 : dataEvent->GetData(NS_LITERAL_STRING("count"),
763 0 : getter_AddRefs(countVariant));
764 0 : if (!countVariant)
765 : return;
766 :
767 : PRInt32 index, count;
768 0 : indexVariant->GetAsInt32(&index);
769 0 : countVariant->GetAsInt32(&count);
770 :
771 0 : aAccessible->InvalidateCache(index, count);
772 : }
773 :
774 : void
775 0 : nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
776 : nsXULTreeAccessible* aAccessible)
777 : {
778 0 : nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
779 0 : if (!dataEvent)
780 : return;
781 :
782 0 : PRInt32 startRow = 0, endRow = -1, startCol = 0, endCol = -1;
783 :
784 0 : nsCOMPtr<nsIVariant> startRowVariant;
785 0 : dataEvent->GetData(NS_LITERAL_STRING("startrow"),
786 0 : getter_AddRefs(startRowVariant));
787 0 : if (startRowVariant)
788 0 : startRowVariant->GetAsInt32(&startRow);
789 :
790 0 : nsCOMPtr<nsIVariant> endRowVariant;
791 0 : dataEvent->GetData(NS_LITERAL_STRING("endrow"),
792 0 : getter_AddRefs(endRowVariant));
793 0 : if (endRowVariant)
794 0 : endRowVariant->GetAsInt32(&endRow);
795 :
796 0 : nsCOMPtr<nsIVariant> startColVariant;
797 0 : dataEvent->GetData(NS_LITERAL_STRING("startcolumn"),
798 0 : getter_AddRefs(startColVariant));
799 0 : if (startColVariant)
800 0 : startColVariant->GetAsInt32(&startCol);
801 :
802 0 : nsCOMPtr<nsIVariant> endColVariant;
803 0 : dataEvent->GetData(NS_LITERAL_STRING("endcolumn"),
804 0 : getter_AddRefs(endColVariant));
805 0 : if (endColVariant)
806 0 : endColVariant->GetAsInt32(&endCol);
807 :
808 0 : aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
809 : }
810 : #endif
|