accessible/src/generic/RootAccessible.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/src/generic/RootAccessible.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,720 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "RootAccessible.h"
    1.10 +
    1.11 +#include "mozilla/ArrayUtils.h"
    1.12 +
    1.13 +#define CreateEvent CreateEventA
    1.14 +#include "nsIDOMDocument.h"
    1.15 +
    1.16 +#include "Accessible-inl.h"
    1.17 +#include "DocAccessible-inl.h"
    1.18 +#include "nsAccessibilityService.h"
    1.19 +#include "nsAccUtils.h"
    1.20 +#include "nsCoreUtils.h"
    1.21 +#include "nsEventShell.h"
    1.22 +#include "Relation.h"
    1.23 +#include "Role.h"
    1.24 +#include "States.h"
    1.25 +#ifdef MOZ_XUL
    1.26 +#include "XULTreeAccessible.h"
    1.27 +#endif
    1.28 +
    1.29 +#include "mozilla/dom/Element.h"
    1.30 +
    1.31 +#include "nsIAccessibleRelation.h"
    1.32 +#include "nsIDocShellTreeItem.h"
    1.33 +#include "nsIDocShellTreeOwner.h"
    1.34 +#include "mozilla/dom/Event.h"
    1.35 +#include "mozilla/dom/EventTarget.h"
    1.36 +#include "nsIDOMCustomEvent.h"
    1.37 +#include "nsIDOMXULMultSelectCntrlEl.h"
    1.38 +#include "nsIDocument.h"
    1.39 +#include "nsIInterfaceRequestorUtils.h"
    1.40 +#include "nsIPropertyBag2.h"
    1.41 +#include "nsIServiceManager.h"
    1.42 +#include "nsPIDOMWindow.h"
    1.43 +#include "nsIWebBrowserChrome.h"
    1.44 +#include "nsReadableUtils.h"
    1.45 +#include "nsFocusManager.h"
    1.46 +
    1.47 +#ifdef MOZ_XUL
    1.48 +#include "nsIXULDocument.h"
    1.49 +#include "nsIXULWindow.h"
    1.50 +#endif
    1.51 +
    1.52 +using namespace mozilla;
    1.53 +using namespace mozilla::a11y;
    1.54 +using namespace mozilla::dom;
    1.55 +
    1.56 +////////////////////////////////////////////////////////////////////////////////
    1.57 +// nsISupports
    1.58 +
    1.59 +NS_IMPL_ISUPPORTS_INHERITED(RootAccessible, DocAccessible, nsIAccessibleDocument)
    1.60 +
    1.61 +////////////////////////////////////////////////////////////////////////////////
    1.62 +// Constructor/destructor
    1.63 +
    1.64 +RootAccessible::
    1.65 +  RootAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
    1.66 +                 nsIPresShell* aPresShell) :
    1.67 +  DocAccessibleWrap(aDocument, aRootContent, aPresShell)
    1.68 +{
    1.69 +  mType = eRootType;
    1.70 +}
    1.71 +
    1.72 +RootAccessible::~RootAccessible()
    1.73 +{
    1.74 +}
    1.75 +
    1.76 +////////////////////////////////////////////////////////////////////////////////
    1.77 +// Accessible
    1.78 +
    1.79 +ENameValueFlag
    1.80 +RootAccessible::Name(nsString& aName)
    1.81 +{
    1.82 +  aName.Truncate();
    1.83 +
    1.84 +  if (mRoleMapEntry) {
    1.85 +    Accessible::Name(aName);
    1.86 +    if (!aName.IsEmpty())
    1.87 +      return eNameOK;
    1.88 +  }
    1.89 +
    1.90 +  mDocumentNode->GetTitle(aName);
    1.91 +  return eNameOK;
    1.92 +}
    1.93 +
    1.94 +role
    1.95 +RootAccessible::NativeRole()
    1.96 +{
    1.97 +  // If it's a <dialog> or <wizard>, use roles::DIALOG instead
    1.98 +  dom::Element* rootElm = mDocumentNode->GetRootElement();
    1.99 +  if (rootElm && (rootElm->Tag() == nsGkAtoms::dialog ||
   1.100 +                  rootElm->Tag() == nsGkAtoms::wizard))
   1.101 +    return roles::DIALOG;
   1.102 +
   1.103 +  return DocAccessibleWrap::NativeRole();
   1.104 +}
   1.105 +
   1.106 +// RootAccessible protected member
   1.107 +#ifdef MOZ_XUL
   1.108 +uint32_t
   1.109 +RootAccessible::GetChromeFlags()
   1.110 +{
   1.111 +  // Return the flag set for the top level window as defined 
   1.112 +  // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
   1.113 +  // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
   1.114 +  nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
   1.115 +  NS_ENSURE_TRUE(docShell, 0);
   1.116 +  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   1.117 +  docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   1.118 +  NS_ENSURE_TRUE(treeOwner, 0);
   1.119 +  nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
   1.120 +  if (!xulWin) {
   1.121 +    return 0;
   1.122 +  }
   1.123 +  uint32_t chromeFlags;
   1.124 +  xulWin->GetChromeFlags(&chromeFlags);
   1.125 +  return chromeFlags;
   1.126 +}
   1.127 +#endif
   1.128 +
   1.129 +uint64_t
   1.130 +RootAccessible::NativeState()
   1.131 +{
   1.132 +  uint64_t state = DocAccessibleWrap::NativeState();
   1.133 +  if (state & states::DEFUNCT)
   1.134 +    return state;
   1.135 +
   1.136 +#ifdef MOZ_XUL
   1.137 +  uint32_t chromeFlags = GetChromeFlags();
   1.138 +  if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
   1.139 +    state |= states::SIZEABLE;
   1.140 +    // If it has a titlebar it's movable
   1.141 +    // XXX unless it's minimized or maximized, but not sure
   1.142 +    //     how to detect that
   1.143 +  if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
   1.144 +    state |= states::MOVEABLE;
   1.145 +  if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
   1.146 +    state |= states::MODAL;
   1.147 +#endif
   1.148 +
   1.149 +  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   1.150 +  if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
   1.151 +    state |= states::ACTIVE;
   1.152 +
   1.153 +  return state;
   1.154 +}
   1.155 +
   1.156 +const char* const kEventTypes[] = {
   1.157 +#ifdef DEBUG_DRAGDROPSTART
   1.158 +  // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
   1.159 +  // debugging a11y objects with event viewers.
   1.160 +  "mouseover",
   1.161 +#endif
   1.162 +  // Fired when list or tree selection changes.
   1.163 +  "select",
   1.164 +  // Fired when value changes immediately, wether or not focused changed.
   1.165 +  "ValueChange",
   1.166 +  "AlertActive",
   1.167 +  "TreeRowCountChanged",
   1.168 +  "TreeInvalidated",
   1.169 +  // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
   1.170 +  "OpenStateChange",
   1.171 +  // add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp)
   1.172 +  "CheckboxStateChange",
   1.173 +  // add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp  & radio.xml)
   1.174 +  "RadioStateChange",
   1.175 +  "popupshown",
   1.176 +  "popuphiding",
   1.177 +  "DOMMenuInactive",
   1.178 +  "DOMMenuItemActive",
   1.179 +  "DOMMenuItemInactive",
   1.180 +  "DOMMenuBarActive",
   1.181 +  "DOMMenuBarInactive"
   1.182 +};
   1.183 +
   1.184 +nsresult
   1.185 +RootAccessible::AddEventListeners()
   1.186 +{
   1.187 +  // EventTarget interface allows to register event listeners to
   1.188 +  // receive untrusted events (synthetic events generated by untrusted code).
   1.189 +  // For example, XBL bindings implementations for elements that are hosted in
   1.190 +  // non chrome document fire untrusted events.
   1.191 +  nsCOMPtr<EventTarget> nstarget = mDocumentNode;
   1.192 +
   1.193 +  if (nstarget) {
   1.194 +    for (const char* const* e = kEventTypes,
   1.195 +                   * const* e_end = ArrayEnd(kEventTypes);
   1.196 +         e < e_end; ++e) {
   1.197 +      nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
   1.198 +                                               this, true, true, 2);
   1.199 +      NS_ENSURE_SUCCESS(rv, rv);
   1.200 +    }
   1.201 +  }
   1.202 +
   1.203 +  return DocAccessible::AddEventListeners();
   1.204 +}
   1.205 +
   1.206 +nsresult
   1.207 +RootAccessible::RemoveEventListeners()
   1.208 +{
   1.209 +  nsCOMPtr<EventTarget> target = mDocumentNode;
   1.210 +  if (target) { 
   1.211 +    for (const char* const* e = kEventTypes,
   1.212 +                   * const* e_end = ArrayEnd(kEventTypes);
   1.213 +         e < e_end; ++e) {
   1.214 +      nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
   1.215 +      NS_ENSURE_SUCCESS(rv, rv);
   1.216 +    }
   1.217 +  }
   1.218 +
   1.219 +  // Do this before removing clearing caret accessible, so that it can use
   1.220 +  // shutdown the caret accessible's selection listener
   1.221 +  DocAccessible::RemoveEventListeners();
   1.222 +  return NS_OK;
   1.223 +}
   1.224 +
   1.225 +////////////////////////////////////////////////////////////////////////////////
   1.226 +// public
   1.227 +
   1.228 +void
   1.229 +RootAccessible::DocumentActivated(DocAccessible* aDocument)
   1.230 +{
   1.231 +}
   1.232 +
   1.233 +////////////////////////////////////////////////////////////////////////////////
   1.234 +// nsIDOMEventListener
   1.235 +
   1.236 +NS_IMETHODIMP
   1.237 +RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
   1.238 +{
   1.239 +  MOZ_ASSERT(aDOMEvent);
   1.240 +  Event* event = aDOMEvent->InternalDOMEvent();
   1.241 +  nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
   1.242 +  if (!origTargetNode)
   1.243 +    return NS_OK;
   1.244 +
   1.245 +#ifdef A11Y_LOG
   1.246 +  if (logging::IsEnabled(logging::eDOMEvents)) {
   1.247 +    nsAutoString eventType;
   1.248 +    aDOMEvent->GetType(eventType);
   1.249 +    logging::DOMEvent("handled", origTargetNode, eventType);
   1.250 +  }
   1.251 +#endif
   1.252 +
   1.253 +  DocAccessible* document =
   1.254 +    GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
   1.255 +
   1.256 +  if (document) {
   1.257 +    // Root accessible exists longer than any of its descendant documents so
   1.258 +    // that we are guaranteed notification is processed before root accessible
   1.259 +    // is destroyed.
   1.260 +    document->HandleNotification<RootAccessible, nsIDOMEvent>
   1.261 +      (this, &RootAccessible::ProcessDOMEvent, aDOMEvent);
   1.262 +  }
   1.263 +
   1.264 +  return NS_OK;
   1.265 +}
   1.266 +
   1.267 +// RootAccessible protected
   1.268 +void
   1.269 +RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
   1.270 +{
   1.271 +  MOZ_ASSERT(aDOMEvent);
   1.272 +  Event* event = aDOMEvent->InternalDOMEvent();
   1.273 +  nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
   1.274 +
   1.275 +  nsAutoString eventType;
   1.276 +  aDOMEvent->GetType(eventType);
   1.277 +
   1.278 +#ifdef A11Y_LOG
   1.279 +  if (logging::IsEnabled(logging::eDOMEvents))
   1.280 +    logging::DOMEvent("processed", origTargetNode, eventType);
   1.281 +#endif
   1.282 +
   1.283 +  if (eventType.EqualsLiteral("popuphiding")) {
   1.284 +    HandlePopupHidingEvent(origTargetNode);
   1.285 +    return;
   1.286 +  }
   1.287 +
   1.288 +  DocAccessible* targetDocument = GetAccService()->
   1.289 +    GetDocAccessible(origTargetNode->OwnerDoc());
   1.290 +  NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
   1.291 +
   1.292 +  Accessible* accessible = 
   1.293 +    targetDocument->GetAccessibleOrContainer(origTargetNode);
   1.294 +  if (!accessible)
   1.295 +    return;
   1.296 +
   1.297 +#ifdef MOZ_XUL
   1.298 +  XULTreeAccessible* treeAcc = accessible->AsXULTree();
   1.299 +  if (treeAcc) {
   1.300 +    if (eventType.EqualsLiteral("TreeRowCountChanged")) {
   1.301 +      HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
   1.302 +      return;
   1.303 +    }
   1.304 +
   1.305 +    if (eventType.EqualsLiteral("TreeInvalidated")) {
   1.306 +      HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
   1.307 +      return;
   1.308 +    }
   1.309 +  }
   1.310 +#endif
   1.311 +
   1.312 +  if (eventType.EqualsLiteral("RadioStateChange")) {
   1.313 +    uint64_t state = accessible->State();
   1.314 +    bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
   1.315 +
   1.316 +    if (accessible->NeedsDOMUIEvent()) {
   1.317 +      nsRefPtr<AccEvent> accEvent =
   1.318 +        new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
   1.319 +      nsEventShell::FireEvent(accEvent);
   1.320 +    }
   1.321 +
   1.322 +    if (isEnabled) {
   1.323 +      FocusMgr()->ActiveItemChanged(accessible);
   1.324 +#ifdef A11Y_LOG
   1.325 +      if (logging::IsEnabled(logging::eFocus))
   1.326 +        logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
   1.327 +#endif
   1.328 +    }
   1.329 +
   1.330 +    return;
   1.331 +  }
   1.332 +
   1.333 +  if (eventType.EqualsLiteral("CheckboxStateChange")) {
   1.334 +    if (accessible->NeedsDOMUIEvent()) {
   1.335 +      uint64_t state = accessible->State();
   1.336 +      bool isEnabled = !!(state & states::CHECKED);
   1.337 +
   1.338 +      nsRefPtr<AccEvent> accEvent =
   1.339 +        new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
   1.340 +      nsEventShell::FireEvent(accEvent);
   1.341 +    }
   1.342 +    return;
   1.343 +  }
   1.344 +
   1.345 +  Accessible* treeItemAcc = nullptr;
   1.346 +#ifdef MOZ_XUL
   1.347 +  // If it's a tree element, need the currently selected item.
   1.348 +  if (treeAcc) {
   1.349 +    treeItemAcc = accessible->CurrentItem();
   1.350 +    if (treeItemAcc)
   1.351 +      accessible = treeItemAcc;
   1.352 +  }
   1.353 +
   1.354 +  if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
   1.355 +    uint64_t state = accessible->State();
   1.356 +    bool isEnabled = (state & states::EXPANDED) != 0;
   1.357 +
   1.358 +    nsRefPtr<AccEvent> accEvent =
   1.359 +      new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
   1.360 +    nsEventShell::FireEvent(accEvent);
   1.361 +    return;
   1.362 +  }
   1.363 +
   1.364 +  nsINode* targetNode = accessible->GetNode();
   1.365 +  if (treeItemAcc && eventType.EqualsLiteral("select")) {
   1.366 +    // XXX: We shouldn't be based on DOM select event which doesn't provide us
   1.367 +    // any context info. We should integrate into nsTreeSelection instead.
   1.368 +    // If multiselect tree, we should fire selectionadd or selection removed
   1.369 +    if (FocusMgr()->HasDOMFocus(targetNode)) {
   1.370 +      nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
   1.371 +        do_QueryInterface(targetNode);
   1.372 +      nsAutoString selType;
   1.373 +      multiSel->GetSelType(selType);
   1.374 +      if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
   1.375 +        // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
   1.376 +        // for each tree item. Perhaps each tree item will need to cache its
   1.377 +        // selection state and fire an event after a DOM "select" event when
   1.378 +        // that state changes. XULTreeAccessible::UpdateTreeSelection();
   1.379 +        nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
   1.380 +                                accessible);
   1.381 +        return;
   1.382 +      }
   1.383 +
   1.384 +      nsRefPtr<AccSelChangeEvent> selChangeEvent =
   1.385 +        new AccSelChangeEvent(treeAcc, treeItemAcc,
   1.386 +                              AccSelChangeEvent::eSelectionAdd);
   1.387 +      nsEventShell::FireEvent(selChangeEvent);
   1.388 +      return;
   1.389 +    }
   1.390 +  }
   1.391 +  else
   1.392 +#endif
   1.393 +  if (eventType.EqualsLiteral("AlertActive")) {
   1.394 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
   1.395 +  }
   1.396 +  else if (eventType.EqualsLiteral("popupshown")) {
   1.397 +    HandlePopupShownEvent(accessible);
   1.398 +  }
   1.399 +  else if (eventType.EqualsLiteral("DOMMenuInactive")) {
   1.400 +    if (accessible->Role() == roles::MENUPOPUP) {
   1.401 +      nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
   1.402 +                              accessible);
   1.403 +    }
   1.404 +  }
   1.405 +  else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
   1.406 +    FocusMgr()->ActiveItemChanged(accessible);
   1.407 +#ifdef A11Y_LOG
   1.408 +    if (logging::IsEnabled(logging::eFocus))
   1.409 +      logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
   1.410 +#endif
   1.411 +  }
   1.412 +  else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
   1.413 +    // Process DOMMenuItemInactive event for autocomplete only because this is
   1.414 +    // unique widget that may acquire focus from autocomplete popup while popup
   1.415 +    // stays open and has no active item. In case of XUL tree autocomplete
   1.416 +    // popup this event is fired for tree accessible.
   1.417 +    Accessible* widget =
   1.418 +      accessible->IsWidget() ? accessible : accessible->ContainerWidget();
   1.419 +    if (widget && widget->IsAutoCompletePopup()) {
   1.420 +      FocusMgr()->ActiveItemChanged(nullptr);
   1.421 +#ifdef A11Y_LOG
   1.422 +      if (logging::IsEnabled(logging::eFocus))
   1.423 +        logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
   1.424 +#endif
   1.425 +    }
   1.426 +  }
   1.427 +  else if (eventType.EqualsLiteral("DOMMenuBarActive")) {  // Always from user input
   1.428 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
   1.429 +                            accessible, eFromUserInput);
   1.430 +
   1.431 +    // Notify of active item change when menubar gets active and if it has
   1.432 +    // current item. This is a case of mouseover (set current menuitem) and
   1.433 +    // mouse click (activate the menubar). If menubar doesn't have current item
   1.434 +    // (can be a case of menubar activation from keyboard) then ignore this
   1.435 +    // notification because later we'll receive DOMMenuItemActive event after
   1.436 +    // current menuitem is set.
   1.437 +    Accessible* activeItem = accessible->CurrentItem();
   1.438 +    if (activeItem) {
   1.439 +      FocusMgr()->ActiveItemChanged(activeItem);
   1.440 +#ifdef A11Y_LOG
   1.441 +      if (logging::IsEnabled(logging::eFocus))
   1.442 +        logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
   1.443 +#endif
   1.444 +    }
   1.445 +  }
   1.446 +  else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {  // Always from user input
   1.447 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
   1.448 +                            accessible, eFromUserInput);
   1.449 +
   1.450 +    FocusMgr()->ActiveItemChanged(nullptr);
   1.451 +#ifdef A11Y_LOG
   1.452 +    if (logging::IsEnabled(logging::eFocus))
   1.453 +      logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
   1.454 +#endif
   1.455 +  }
   1.456 +  else if (accessible->NeedsDOMUIEvent() &&
   1.457 +           eventType.EqualsLiteral("ValueChange")) {
   1.458 +     targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
   1.459 +                                      accessible);
   1.460 +  }
   1.461 +#ifdef DEBUG_DRAGDROPSTART
   1.462 +  else if (eventType.EqualsLiteral("mouseover")) {
   1.463 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
   1.464 +                            accessible);
   1.465 +  }
   1.466 +#endif
   1.467 +}
   1.468 +
   1.469 +
   1.470 +////////////////////////////////////////////////////////////////////////////////
   1.471 +// Accessible
   1.472 +
   1.473 +void
   1.474 +RootAccessible::Shutdown()
   1.475 +{
   1.476 +  // Called manually or by Accessible::LastRelease()
   1.477 +  if (!PresShell())
   1.478 +    return;  // Already shutdown
   1.479 +
   1.480 +  DocAccessibleWrap::Shutdown();
   1.481 +}
   1.482 +
   1.483 +// nsIAccessible method
   1.484 +Relation
   1.485 +RootAccessible::RelationByType(RelationType aType)
   1.486 +{
   1.487 +  if (!mDocumentNode || aType != RelationType::EMBEDS)
   1.488 +    return DocAccessibleWrap::RelationByType(aType);
   1.489 +
   1.490 +  nsIDOMWindow* rootWindow = mDocumentNode->GetWindow();
   1.491 +  if (rootWindow) {
   1.492 +    nsCOMPtr<nsIDOMWindow> contentWindow;
   1.493 +    rootWindow->GetContent(getter_AddRefs(contentWindow));
   1.494 +    if (contentWindow) {
   1.495 +      nsCOMPtr<nsIDOMDocument> contentDOMDocument;
   1.496 +      contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
   1.497 +      nsCOMPtr<nsIDocument> contentDocumentNode =
   1.498 +        do_QueryInterface(contentDOMDocument);
   1.499 +      if (contentDocumentNode) {
   1.500 +        DocAccessible* contentDocument =
   1.501 +          GetAccService()->GetDocAccessible(contentDocumentNode);
   1.502 +        if (contentDocument)
   1.503 +          return Relation(contentDocument);
   1.504 +      }
   1.505 +    }
   1.506 +  }
   1.507 +
   1.508 +  return Relation();
   1.509 +}
   1.510 +
   1.511 +////////////////////////////////////////////////////////////////////////////////
   1.512 +// Protected members
   1.513 +
   1.514 +void
   1.515 +RootAccessible::HandlePopupShownEvent(Accessible* aAccessible)
   1.516 +{
   1.517 +  roles::Role role = aAccessible->Role();
   1.518 +
   1.519 +  if (role == roles::MENUPOPUP) {
   1.520 +    // Don't fire menupopup events for combobox and autocomplete lists.
   1.521 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
   1.522 +                            aAccessible);
   1.523 +    return;
   1.524 +  }
   1.525 +
   1.526 +  if (role == roles::TOOLTIP) {
   1.527 +    // There is a single <xul:tooltip> node which Mozilla moves around.
   1.528 +    // The accessible for it stays the same no matter where it moves. 
   1.529 +    // AT's expect to get an EVENT_SHOW for the tooltip. 
   1.530 +    // In event callback the tooltip's accessible will be ready.
   1.531 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
   1.532 +    return;
   1.533 +  }
   1.534 +
   1.535 +  if (role == roles::COMBOBOX_LIST) {
   1.536 +    // Fire expanded state change event for comboboxes and autocompeletes.
   1.537 +    Accessible* combobox = aAccessible->Parent();
   1.538 +    if (!combobox)
   1.539 +      return;
   1.540 +
   1.541 +    roles::Role comboboxRole = combobox->Role();
   1.542 +    if (comboboxRole == roles::COMBOBOX || 
   1.543 +	comboboxRole == roles::AUTOCOMPLETE) {
   1.544 +      nsRefPtr<AccEvent> event =
   1.545 +        new AccStateChangeEvent(combobox, states::EXPANDED, true);
   1.546 +      if (event)
   1.547 +        nsEventShell::FireEvent(event);
   1.548 +    }
   1.549 +  }
   1.550 +}
   1.551 +
   1.552 +void
   1.553 +RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
   1.554 +{
   1.555 +  // Get popup accessible. There are cases when popup element isn't accessible
   1.556 +  // but an underlying widget is and behaves like popup, an example is
   1.557 +  // autocomplete popups.
   1.558 +  DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
   1.559 +  if (!document)
   1.560 +    return;
   1.561 +
   1.562 +  Accessible* popup = document->GetAccessible(aPopupNode);
   1.563 +  if (!popup) {
   1.564 +    Accessible* popupContainer = document->GetContainerAccessible(aPopupNode);
   1.565 +    if (!popupContainer)
   1.566 +      return;
   1.567 +
   1.568 +    uint32_t childCount = popupContainer->ChildCount();
   1.569 +    for (uint32_t idx = 0; idx < childCount; idx++) {
   1.570 +      Accessible* child = popupContainer->GetChildAt(idx);
   1.571 +      if (child->IsAutoCompletePopup()) {
   1.572 +        popup = child;
   1.573 +        break;
   1.574 +      }
   1.575 +    }
   1.576 +
   1.577 +    // No popup no events. Focus is managed by DOM. This is a case for
   1.578 +    // menupopups of menus on Linux since there are no accessible for popups.
   1.579 +    if (!popup)
   1.580 +      return;
   1.581 +  }
   1.582 +
   1.583 +  // In case of autocompletes and comboboxes fire state change event for
   1.584 +  // expanded state. Note, HTML form autocomplete isn't a subject of state
   1.585 +  // change event because they aren't autocompletes strictly speaking.
   1.586 +  // When popup closes (except nested popups and menus) then fire focus event to
   1.587 +  // where it was. The focus event is expected even if popup didn't take a focus.
   1.588 +
   1.589 +  static const uint32_t kNotifyOfFocus = 1;
   1.590 +  static const uint32_t kNotifyOfState = 2;
   1.591 +  uint32_t notifyOf = 0;
   1.592 +
   1.593 +  // HTML select is target of popuphidding event. Otherwise get container
   1.594 +  // widget. No container widget means this is either tooltip or menupopup.
   1.595 +  // No events in the former case.
   1.596 +  Accessible* widget = nullptr;
   1.597 +  if (popup->IsCombobox()) {
   1.598 +    widget = popup;
   1.599 +  } else {
   1.600 +    widget = popup->ContainerWidget();
   1.601 +    if (!widget) {
   1.602 +      if (!popup->IsMenuPopup())
   1.603 +        return;
   1.604 +
   1.605 +      widget = popup;
   1.606 +    }
   1.607 +  }
   1.608 +
   1.609 +  if (popup->IsAutoCompletePopup()) {
   1.610 +    // No focus event for autocomplete because it's managed by
   1.611 +    // DOMMenuItemInactive events.
   1.612 +    if (widget->IsAutoComplete())
   1.613 +      notifyOf = kNotifyOfState;
   1.614 +
   1.615 +  } else if (widget->IsCombobox()) {
   1.616 +    // Fire focus for active combobox, otherwise the focus is managed by DOM
   1.617 +    // focus notifications. Always fire state change event.
   1.618 +    if (widget->IsActiveWidget())
   1.619 +      notifyOf = kNotifyOfFocus;
   1.620 +    notifyOf |= kNotifyOfState;
   1.621 +
   1.622 +  } else if (widget->IsMenuButton()) {
   1.623 +    // Can be a part of autocomplete.
   1.624 +    Accessible* compositeWidget = widget->ContainerWidget();
   1.625 +    if (compositeWidget && compositeWidget->IsAutoComplete()) {
   1.626 +      widget = compositeWidget;
   1.627 +      notifyOf = kNotifyOfState;
   1.628 +    }
   1.629 +
   1.630 +    // Autocomplete (like searchbar) can be inactive when popup hiddens
   1.631 +    notifyOf |= kNotifyOfFocus;
   1.632 +
   1.633 +  } else if (widget == popup) {
   1.634 +    // Top level context menus and alerts.
   1.635 +    // Ignore submenus and menubar. When submenu is closed then sumbenu
   1.636 +    // container menuitem takes a focus via DOMMenuItemActive notification.
   1.637 +    // For menubars processing we listen DOMMenubarActive/Inactive
   1.638 +    // notifications.
   1.639 +    notifyOf = kNotifyOfFocus;
   1.640 +  }
   1.641 +
   1.642 +  // Restore focus to where it was.
   1.643 +  if (notifyOf & kNotifyOfFocus) {
   1.644 +    FocusMgr()->ActiveItemChanged(nullptr);
   1.645 +#ifdef A11Y_LOG
   1.646 +    if (logging::IsEnabled(logging::eFocus))
   1.647 +      logging::ActiveItemChangeCausedBy("popuphiding", popup);
   1.648 +#endif
   1.649 +  }
   1.650 +
   1.651 +  // Fire expanded state change event.
   1.652 +  if (notifyOf & kNotifyOfState) {
   1.653 +    nsRefPtr<AccEvent> event =
   1.654 +      new AccStateChangeEvent(widget, states::EXPANDED, false);
   1.655 +    document->FireDelayedEvent(event);
   1.656 +  }
   1.657 +}
   1.658 +
   1.659 +#ifdef MOZ_XUL
   1.660 +void
   1.661 +RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
   1.662 +                                               XULTreeAccessible* aAccessible)
   1.663 +{
   1.664 +  nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
   1.665 +  if (!customEvent)
   1.666 +    return;
   1.667 +
   1.668 +  nsCOMPtr<nsIVariant> detailVariant;
   1.669 +  customEvent->GetDetail(getter_AddRefs(detailVariant));
   1.670 +  if (!detailVariant)
   1.671 +    return;
   1.672 +
   1.673 +  nsCOMPtr<nsISupports> supports;
   1.674 +  detailVariant->GetAsISupports(getter_AddRefs(supports));
   1.675 +  nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
   1.676 +  if (!propBag)
   1.677 +    return;
   1.678 +
   1.679 +  nsresult rv;
   1.680 +  int32_t index, count;
   1.681 +  rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("index"), &index);
   1.682 +  if (NS_FAILED(rv))
   1.683 +    return;
   1.684 +
   1.685 +  rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("count"), &count);
   1.686 +  if (NS_FAILED(rv))
   1.687 +    return;
   1.688 +
   1.689 +  aAccessible->InvalidateCache(index, count);
   1.690 +}
   1.691 +
   1.692 +void
   1.693 +RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
   1.694 +                                           XULTreeAccessible* aAccessible)
   1.695 +{
   1.696 +  nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
   1.697 +  if (!customEvent)
   1.698 +    return;
   1.699 +
   1.700 +  nsCOMPtr<nsIVariant> detailVariant;
   1.701 +  customEvent->GetDetail(getter_AddRefs(detailVariant));
   1.702 +  if (!detailVariant)
   1.703 +    return;
   1.704 +
   1.705 +  nsCOMPtr<nsISupports> supports;
   1.706 +  detailVariant->GetAsISupports(getter_AddRefs(supports));
   1.707 +  nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
   1.708 +  if (!propBag)
   1.709 +    return;
   1.710 +
   1.711 +  int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1;
   1.712 +  propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
   1.713 +                              &startRow);
   1.714 +  propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
   1.715 +                              &endRow);
   1.716 +  propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
   1.717 +                              &startCol);
   1.718 +  propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
   1.719 +                              &endCol);
   1.720 +
   1.721 +  aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
   1.722 +}
   1.723 +#endif

mercurial