accessible/src/generic/DocAccessible.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/src/generic/DocAccessible.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2104 @@
     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 "Accessible-inl.h"
    1.10 +#include "AccIterator.h"
    1.11 +#include "DocAccessible-inl.h"
    1.12 +#include "HTMLImageMapAccessible.h"
    1.13 +#include "nsAccCache.h"
    1.14 +#include "nsAccessiblePivot.h"
    1.15 +#include "nsAccUtils.h"
    1.16 +#include "nsEventShell.h"
    1.17 +#include "nsTextEquivUtils.h"
    1.18 +#include "Role.h"
    1.19 +#include "RootAccessible.h"
    1.20 +#include "TreeWalker.h"
    1.21 +
    1.22 +#include "nsIMutableArray.h"
    1.23 +#include "nsICommandManager.h"
    1.24 +#include "nsIDocShell.h"
    1.25 +#include "nsIDocument.h"
    1.26 +#include "nsIDOMAttr.h"
    1.27 +#include "nsIDOMCharacterData.h"
    1.28 +#include "nsIDOMDocument.h"
    1.29 +#include "nsIDOMXULDocument.h"
    1.30 +#include "nsIDOMMutationEvent.h"
    1.31 +#include "nsPIDOMWindow.h"
    1.32 +#include "nsIDOMXULPopupElement.h"
    1.33 +#include "nsIEditingSession.h"
    1.34 +#include "nsIFrame.h"
    1.35 +#include "nsIInterfaceRequestorUtils.h"
    1.36 +#include "nsImageFrame.h"
    1.37 +#include "nsIPersistentProperties2.h"
    1.38 +#include "nsIPresShell.h"
    1.39 +#include "nsIServiceManager.h"
    1.40 +#include "nsViewManager.h"
    1.41 +#include "nsIScrollableFrame.h"
    1.42 +#include "nsUnicharUtils.h"
    1.43 +#include "nsIURI.h"
    1.44 +#include "nsIWebNavigation.h"
    1.45 +#include "nsFocusManager.h"
    1.46 +#include "nsNameSpaceManager.h"
    1.47 +#include "mozilla/ArrayUtils.h"
    1.48 +#include "mozilla/Assertions.h"
    1.49 +#include "mozilla/EventStates.h"
    1.50 +#include "mozilla/dom/DocumentType.h"
    1.51 +#include "mozilla/dom/Element.h"
    1.52 +
    1.53 +#ifdef MOZ_XUL
    1.54 +#include "nsIXULDocument.h"
    1.55 +#endif
    1.56 +
    1.57 +using namespace mozilla;
    1.58 +using namespace mozilla::a11y;
    1.59 +
    1.60 +////////////////////////////////////////////////////////////////////////////////
    1.61 +// Static member initialization
    1.62 +
    1.63 +static nsIAtom** kRelationAttrs[] =
    1.64 +{
    1.65 +  &nsGkAtoms::aria_labelledby,
    1.66 +  &nsGkAtoms::aria_describedby,
    1.67 +  &nsGkAtoms::aria_owns,
    1.68 +  &nsGkAtoms::aria_controls,
    1.69 +  &nsGkAtoms::aria_flowto,
    1.70 +  &nsGkAtoms::_for,
    1.71 +  &nsGkAtoms::control
    1.72 +};
    1.73 +
    1.74 +static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs);
    1.75 +
    1.76 +////////////////////////////////////////////////////////////////////////////////
    1.77 +// Constructor/desctructor
    1.78 +
    1.79 +DocAccessible::
    1.80 +  DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
    1.81 +                  nsIPresShell* aPresShell) :
    1.82 +  HyperTextAccessibleWrap(aRootContent, this),
    1.83 +  // XXX aaronl should we use an algorithm for the initial cache size?
    1.84 +  mAccessibleCache(kDefaultCacheSize),
    1.85 +  mNodeToAccessibleMap(kDefaultCacheSize),
    1.86 +  mDocumentNode(aDocument),
    1.87 +  mScrollPositionChangedTicks(0),
    1.88 +  mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
    1.89 +  mVirtualCursor(nullptr),
    1.90 +  mPresShell(aPresShell)
    1.91 +{
    1.92 +  mGenericTypes |= eDocument;
    1.93 +  mStateFlags |= eNotNodeMapEntry;
    1.94 +
    1.95 +  MOZ_ASSERT(mPresShell, "should have been given a pres shell");
    1.96 +  mPresShell->SetDocAccessible(this);
    1.97 +
    1.98 +  // If this is a XUL Document, it should not implement nsHyperText
    1.99 +  if (mDocumentNode && mDocumentNode->IsXUL())
   1.100 +    mGenericTypes &= ~eHyperText;
   1.101 +}
   1.102 +
   1.103 +DocAccessible::~DocAccessible()
   1.104 +{
   1.105 +  NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
   1.106 +}
   1.107 +
   1.108 +
   1.109 +////////////////////////////////////////////////////////////////////////////////
   1.110 +// nsISupports
   1.111 +
   1.112 +NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
   1.113 +
   1.114 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
   1.115 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
   1.116 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
   1.117 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
   1.118 +  tmp->mDependentIDsHash.EnumerateRead(CycleCollectorTraverseDepIDsEntry, &cb);
   1.119 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
   1.120 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
   1.121 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.122 +
   1.123 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
   1.124 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
   1.125 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
   1.126 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
   1.127 +  tmp->mDependentIDsHash.Clear();
   1.128 +  tmp->mNodeToAccessibleMap.Clear();
   1.129 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
   1.130 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
   1.131 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.132 +
   1.133 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
   1.134 +  NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   1.135 +  NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   1.136 +  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   1.137 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1.138 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   1.139 +  NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
   1.140 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
   1.141 +    foundInterface = 0;
   1.142 +
   1.143 +  nsresult status;
   1.144 +  if (!foundInterface) {
   1.145 +    // HTML document accessible must inherit from HyperTextAccessible to get
   1.146 +    // support text interfaces. XUL document accessible doesn't need this.
   1.147 +    // However at some point we may push <body> to implement the interfaces and
   1.148 +    // return DocAccessible to inherit from AccessibleWrap.
   1.149 +
   1.150 +    status = IsHyperText() ? 
   1.151 +      HyperTextAccessible::QueryInterface(aIID, (void**)&foundInterface) :
   1.152 +      Accessible::QueryInterface(aIID, (void**)&foundInterface);
   1.153 +  } else {
   1.154 +    NS_ADDREF(foundInterface);
   1.155 +    status = NS_OK;
   1.156 +  }
   1.157 +
   1.158 +  *aInstancePtr = foundInterface;
   1.159 +  return status;
   1.160 +}
   1.161 +
   1.162 +NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
   1.163 +NS_IMPL_RELEASE_INHERITED(DocAccessible, HyperTextAccessible)
   1.164 +
   1.165 +////////////////////////////////////////////////////////////////////////////////
   1.166 +// nsIAccessible
   1.167 +
   1.168 +ENameValueFlag
   1.169 +DocAccessible::Name(nsString& aName)
   1.170 +{
   1.171 +  aName.Truncate();
   1.172 +
   1.173 +  if (mParent) {
   1.174 +    mParent->Name(aName); // Allow owning iframe to override the name
   1.175 +  }
   1.176 +  if (aName.IsEmpty()) {
   1.177 +    // Allow name via aria-labelledby or title attribute
   1.178 +    Accessible::Name(aName);
   1.179 +  }
   1.180 +  if (aName.IsEmpty()) {
   1.181 +    GetTitle(aName);   // Try title element
   1.182 +  }
   1.183 +  if (aName.IsEmpty()) {   // Last resort: use URL
   1.184 +    GetURL(aName);
   1.185 +  }
   1.186 + 
   1.187 +  return eNameOK;
   1.188 +}
   1.189 +
   1.190 +// Accessible public method
   1.191 +role
   1.192 +DocAccessible::NativeRole()
   1.193 +{
   1.194 +  nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
   1.195 +  if (docShell) {
   1.196 +    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
   1.197 +    docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
   1.198 +    int32_t itemType = docShell->ItemType();
   1.199 +    if (sameTypeRoot == docShell) {
   1.200 +      // Root of content or chrome tree
   1.201 +      if (itemType == nsIDocShellTreeItem::typeChrome)
   1.202 +        return roles::CHROME_WINDOW;
   1.203 +
   1.204 +      if (itemType == nsIDocShellTreeItem::typeContent) {
   1.205 +#ifdef MOZ_XUL
   1.206 +        nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
   1.207 +        if (xulDoc)
   1.208 +          return roles::APPLICATION;
   1.209 +#endif
   1.210 +        return roles::DOCUMENT;
   1.211 +      }
   1.212 +    }
   1.213 +    else if (itemType == nsIDocShellTreeItem::typeContent) {
   1.214 +      return roles::DOCUMENT;
   1.215 +    }
   1.216 +  }
   1.217 +
   1.218 +  return roles::PANE; // Fall back;
   1.219 +}
   1.220 +
   1.221 +void
   1.222 +DocAccessible::Description(nsString& aDescription)
   1.223 +{
   1.224 +  if (mParent)
   1.225 +    mParent->Description(aDescription);
   1.226 +
   1.227 +  if (HasOwnContent() && aDescription.IsEmpty()) {
   1.228 +    nsTextEquivUtils::
   1.229 +      GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
   1.230 +                             aDescription);
   1.231 +  }
   1.232 +}
   1.233 +
   1.234 +// Accessible public method
   1.235 +uint64_t
   1.236 +DocAccessible::NativeState()
   1.237 +{
   1.238 +  // Document is always focusable.
   1.239 +  uint64_t state = states::FOCUSABLE; // keep in sync with NativeInteractiveState() impl
   1.240 +  if (FocusMgr()->IsFocused(this))
   1.241 +    state |= states::FOCUSED;
   1.242 +
   1.243 +  // Expose stale state until the document is ready (DOM is loaded and tree is
   1.244 +  // constructed).
   1.245 +  if (!HasLoadState(eReady))
   1.246 +    state |= states::STALE;
   1.247 +
   1.248 +  // Expose state busy until the document and all its subdocuments is completely
   1.249 +  // loaded.
   1.250 +  if (!HasLoadState(eCompletelyLoaded))
   1.251 +    state |= states::BUSY;
   1.252 +
   1.253 +  nsIFrame* frame = GetFrame();
   1.254 +  if (!frame ||
   1.255 +      !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
   1.256 +    state |= states::INVISIBLE | states::OFFSCREEN;
   1.257 +  }
   1.258 +
   1.259 +  nsCOMPtr<nsIEditor> editor = GetEditor();
   1.260 +  state |= editor ? states::EDITABLE : states::READONLY;
   1.261 +
   1.262 +  return state;
   1.263 +}
   1.264 +
   1.265 +uint64_t
   1.266 +DocAccessible::NativeInteractiveState() const
   1.267 +{
   1.268 +  // Document is always focusable.
   1.269 +  return states::FOCUSABLE;
   1.270 +}
   1.271 +
   1.272 +bool
   1.273 +DocAccessible::NativelyUnavailable() const
   1.274 +{
   1.275 +  return false;
   1.276 +}
   1.277 +
   1.278 +// Accessible public method
   1.279 +void
   1.280 +DocAccessible::ApplyARIAState(uint64_t* aState) const
   1.281 +{
   1.282 +  // Grab states from content element.
   1.283 +  if (mContent)
   1.284 +    Accessible::ApplyARIAState(aState);
   1.285 +
   1.286 +  // Allow iframe/frame etc. to have final state override via ARIA.
   1.287 +  if (mParent)
   1.288 +    mParent->ApplyARIAState(aState);
   1.289 +}
   1.290 +
   1.291 +already_AddRefed<nsIPersistentProperties>
   1.292 +DocAccessible::Attributes()
   1.293 +{
   1.294 +  nsCOMPtr<nsIPersistentProperties> attributes =
   1.295 +    HyperTextAccessibleWrap::Attributes();
   1.296 +
   1.297 +  // No attributes if document is not attached to the tree or if it's a root
   1.298 +  // document.
   1.299 +  if (!mParent || IsRoot())
   1.300 +    return attributes.forget();
   1.301 +
   1.302 +  // Override ARIA object attributes from outerdoc.
   1.303 +  aria::AttrIterator attribIter(mParent->GetContent());
   1.304 +  nsAutoString name, value, unused;
   1.305 +  while(attribIter.Next(name, value))
   1.306 +    attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
   1.307 +
   1.308 +  return attributes.forget();
   1.309 +}
   1.310 +
   1.311 +Accessible*
   1.312 +DocAccessible::FocusedChild()
   1.313 +{
   1.314 +  // Return an accessible for the current global focus, which does not have to
   1.315 +  // be contained within the current document.
   1.316 +  return FocusMgr()->FocusedAccessible();
   1.317 +}
   1.318 +
   1.319 +NS_IMETHODIMP
   1.320 +DocAccessible::TakeFocus()
   1.321 +{
   1.322 +  if (IsDefunct())
   1.323 +    return NS_ERROR_FAILURE;
   1.324 +
   1.325 +  // Focus the document.
   1.326 +  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   1.327 +  NS_ENSURE_STATE(fm);
   1.328 +
   1.329 +  nsCOMPtr<nsIDOMElement> newFocus;
   1.330 +  return fm->MoveFocus(mDocumentNode->GetWindow(), nullptr,
   1.331 +                       nsIFocusManager::MOVEFOCUS_ROOT, 0,
   1.332 +                       getter_AddRefs(newFocus));
   1.333 +}
   1.334 +
   1.335 +
   1.336 +////////////////////////////////////////////////////////////////////////////////
   1.337 +// nsIAccessibleDocument
   1.338 +
   1.339 +NS_IMETHODIMP
   1.340 +DocAccessible::GetURL(nsAString& aURL)
   1.341 +{
   1.342 +  if (IsDefunct())
   1.343 +    return NS_ERROR_FAILURE;
   1.344 +
   1.345 +  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   1.346 +  nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
   1.347 +  nsAutoCString theURL;
   1.348 +  if (webNav) {
   1.349 +    nsCOMPtr<nsIURI> pURI;
   1.350 +    webNav->GetCurrentURI(getter_AddRefs(pURI));
   1.351 +    if (pURI)
   1.352 +      pURI->GetSpec(theURL);
   1.353 +  }
   1.354 +  CopyUTF8toUTF16(theURL, aURL);
   1.355 +  return NS_OK;
   1.356 +}
   1.357 +
   1.358 +NS_IMETHODIMP
   1.359 +DocAccessible::GetTitle(nsAString& aTitle)
   1.360 +{
   1.361 +  if (!mDocumentNode) {
   1.362 +    return NS_ERROR_FAILURE;
   1.363 +  }
   1.364 +  nsString title;
   1.365 +  mDocumentNode->GetTitle(title);
   1.366 +  aTitle = title;
   1.367 +  return NS_OK;
   1.368 +}
   1.369 +
   1.370 +NS_IMETHODIMP
   1.371 +DocAccessible::GetMimeType(nsAString& aMimeType)
   1.372 +{
   1.373 +  if (!mDocumentNode) {
   1.374 +    return NS_ERROR_FAILURE;
   1.375 +  }
   1.376 +  return mDocumentNode->GetContentType(aMimeType);
   1.377 +}
   1.378 +
   1.379 +NS_IMETHODIMP
   1.380 +DocAccessible::GetDocType(nsAString& aDocType)
   1.381 +{
   1.382 +#ifdef MOZ_XUL
   1.383 +  nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
   1.384 +  if (xulDoc) {
   1.385 +    aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
   1.386 +    return NS_OK;
   1.387 +  } else
   1.388 +#endif
   1.389 +  if (mDocumentNode) {
   1.390 +    dom::DocumentType* docType = mDocumentNode->GetDoctype();
   1.391 +    if (docType) {
   1.392 +      return docType->GetPublicId(aDocType);
   1.393 +    }
   1.394 +  }
   1.395 +
   1.396 +  return NS_ERROR_FAILURE;
   1.397 +}
   1.398 +
   1.399 +NS_IMETHODIMP
   1.400 +DocAccessible::GetNameSpaceURIForID(int16_t aNameSpaceID, nsAString& aNameSpaceURI)
   1.401 +{
   1.402 +  if (mDocumentNode) {
   1.403 +    nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance();
   1.404 +    if (nameSpaceManager)
   1.405 +      return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
   1.406 +  }
   1.407 +  return NS_ERROR_FAILURE;
   1.408 +}
   1.409 +
   1.410 +NS_IMETHODIMP
   1.411 +DocAccessible::GetWindowHandle(void** aWindow)
   1.412 +{
   1.413 +  NS_ENSURE_ARG_POINTER(aWindow);
   1.414 +  *aWindow = GetNativeWindow();
   1.415 +  return NS_OK;
   1.416 +}
   1.417 +
   1.418 +NS_IMETHODIMP
   1.419 +DocAccessible::GetWindow(nsIDOMWindow** aDOMWin)
   1.420 +{
   1.421 +  *aDOMWin = nullptr;
   1.422 +  if (!mDocumentNode) {
   1.423 +    return NS_ERROR_FAILURE;  // Accessible is Shutdown()
   1.424 +  }
   1.425 +  *aDOMWin = mDocumentNode->GetWindow();
   1.426 +
   1.427 +  if (!*aDOMWin)
   1.428 +    return NS_ERROR_FAILURE;  // No DOM Window
   1.429 +
   1.430 +  NS_ADDREF(*aDOMWin);
   1.431 +
   1.432 +  return NS_OK;
   1.433 +}
   1.434 +
   1.435 +NS_IMETHODIMP
   1.436 +DocAccessible::GetDOMDocument(nsIDOMDocument** aDOMDocument)
   1.437 +{
   1.438 +  NS_ENSURE_ARG_POINTER(aDOMDocument);
   1.439 +  *aDOMDocument = nullptr;
   1.440 +
   1.441 +  if (mDocumentNode)
   1.442 +    CallQueryInterface(mDocumentNode, aDOMDocument);
   1.443 +
   1.444 +  return NS_OK;
   1.445 +}
   1.446 +
   1.447 +NS_IMETHODIMP
   1.448 +DocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument)
   1.449 +{
   1.450 +  NS_ENSURE_ARG_POINTER(aDocument);
   1.451 +  *aDocument = nullptr;
   1.452 +
   1.453 +  if (!IsDefunct())
   1.454 +    NS_IF_ADDREF(*aDocument = ParentDocument());
   1.455 +
   1.456 +  return NS_OK;
   1.457 +}
   1.458 +
   1.459 +NS_IMETHODIMP
   1.460 +DocAccessible::GetChildDocumentCount(uint32_t* aCount)
   1.461 +{
   1.462 +  NS_ENSURE_ARG_POINTER(aCount);
   1.463 +  *aCount = 0;
   1.464 +
   1.465 +  if (!IsDefunct())
   1.466 +    *aCount = ChildDocumentCount();
   1.467 +
   1.468 +  return NS_OK;
   1.469 +}
   1.470 +
   1.471 +NS_IMETHODIMP
   1.472 +DocAccessible::GetChildDocumentAt(uint32_t aIndex,
   1.473 +                                  nsIAccessibleDocument** aDocument)
   1.474 +{
   1.475 +  NS_ENSURE_ARG_POINTER(aDocument);
   1.476 +  *aDocument = nullptr;
   1.477 +
   1.478 +  if (IsDefunct())
   1.479 +    return NS_OK;
   1.480 +
   1.481 +  NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex));
   1.482 +  return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
   1.483 +}
   1.484 +
   1.485 +NS_IMETHODIMP
   1.486 +DocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
   1.487 +{
   1.488 +  NS_ENSURE_ARG_POINTER(aVirtualCursor);
   1.489 +  *aVirtualCursor = nullptr;
   1.490 +
   1.491 +  if (IsDefunct())
   1.492 +    return NS_ERROR_FAILURE;
   1.493 +
   1.494 +  if (!mVirtualCursor) {
   1.495 +    mVirtualCursor = new nsAccessiblePivot(this);
   1.496 +    mVirtualCursor->AddObserver(this);
   1.497 +  }
   1.498 +
   1.499 +  NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   1.500 +  return NS_OK;
   1.501 +}
   1.502 +
   1.503 +// HyperTextAccessible method
   1.504 +already_AddRefed<nsIEditor>
   1.505 +DocAccessible::GetEditor() const
   1.506 +{
   1.507 +  // Check if document is editable (designMode="on" case). Otherwise check if
   1.508 +  // the html:body (for HTML document case) or document element is editable.
   1.509 +  if (!mDocumentNode->HasFlag(NODE_IS_EDITABLE) &&
   1.510 +      (!mContent || !mContent->HasFlag(NODE_IS_EDITABLE)))
   1.511 +    return nullptr;
   1.512 +
   1.513 +  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   1.514 +  nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
   1.515 +  if (!editingSession)
   1.516 +    return nullptr; // No editing session interface
   1.517 +
   1.518 +  nsCOMPtr<nsIEditor> editor;
   1.519 +  editingSession->GetEditorForWindow(mDocumentNode->GetWindow(), getter_AddRefs(editor));
   1.520 +  if (!editor)
   1.521 +    return nullptr;
   1.522 +
   1.523 +  bool isEditable = false;
   1.524 +  editor->GetIsDocumentEditable(&isEditable);
   1.525 +  if (isEditable)
   1.526 +    return editor.forget();
   1.527 +
   1.528 +  return nullptr;
   1.529 +}
   1.530 +
   1.531 +// DocAccessible public method
   1.532 +Accessible*
   1.533 +DocAccessible::GetAccessible(nsINode* aNode) const
   1.534 +{
   1.535 +  Accessible* accessible = mNodeToAccessibleMap.Get(aNode);
   1.536 +
   1.537 +  // No accessible in the cache, check if the given ID is unique ID of this
   1.538 +  // document accessible.
   1.539 +  if (!accessible) {
   1.540 +    if (GetNode() != aNode)
   1.541 +      return nullptr;
   1.542 +
   1.543 +    accessible = const_cast<DocAccessible*>(this);
   1.544 +  }
   1.545 +
   1.546 +#ifdef DEBUG
   1.547 +  // All cached accessible nodes should be in the parent
   1.548 +  // It will assert if not all the children were created
   1.549 +  // when they were first cached, and no invalidation
   1.550 +  // ever corrected parent accessible's child cache.
   1.551 +  Accessible* parent = accessible->Parent();
   1.552 +  if (parent)
   1.553 +    parent->TestChildCache(accessible);
   1.554 +#endif
   1.555 +
   1.556 +  return accessible;
   1.557 +}
   1.558 +
   1.559 +////////////////////////////////////////////////////////////////////////////////
   1.560 +// Accessible
   1.561 +
   1.562 +void
   1.563 +DocAccessible::Init()
   1.564 +{
   1.565 +#ifdef A11Y_LOG
   1.566 +  if (logging::IsEnabled(logging::eDocCreate))
   1.567 +    logging::DocCreate("document initialize", mDocumentNode, this);
   1.568 +#endif
   1.569 +
   1.570 +  // Initialize notification controller.
   1.571 +  mNotificationController = new NotificationController(this, mPresShell);
   1.572 +
   1.573 +  // Mark the document accessible as loaded if its DOM document was loaded at
   1.574 +  // this point (this can happen because a11y is started late or DOM document
   1.575 +  // having no container was loaded.
   1.576 +  if (mDocumentNode->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
   1.577 +    mLoadState |= eDOMLoaded;
   1.578 +
   1.579 +  AddEventListeners();
   1.580 +}
   1.581 +
   1.582 +void
   1.583 +DocAccessible::Shutdown()
   1.584 +{
   1.585 +  if (!mPresShell) // already shutdown
   1.586 +    return;
   1.587 +
   1.588 +#ifdef A11Y_LOG
   1.589 +  if (logging::IsEnabled(logging::eDocDestroy))
   1.590 +    logging::DocDestroy("document shutdown", mDocumentNode, this);
   1.591 +#endif
   1.592 +
   1.593 +  if (mNotificationController) {
   1.594 +    mNotificationController->Shutdown();
   1.595 +    mNotificationController = nullptr;
   1.596 +  }
   1.597 +
   1.598 +  RemoveEventListeners();
   1.599 +
   1.600 +  // Mark the document as shutdown before AT is notified about the document
   1.601 +  // removal from its container (valid for root documents on ATK and due to
   1.602 +  // some reason for MSAA, refer to bug 757392 for details).
   1.603 +  mStateFlags |= eIsDefunct;
   1.604 +  nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocumentNode;
   1.605 +  mDocumentNode = nullptr;
   1.606 +
   1.607 +  if (mParent) {
   1.608 +    DocAccessible* parentDocument = mParent->Document();
   1.609 +    if (parentDocument)
   1.610 +      parentDocument->RemoveChildDocument(this);
   1.611 +
   1.612 +    mParent->RemoveChild(this);
   1.613 +  }
   1.614 +
   1.615 +  // Walk the array backwards because child documents remove themselves from the
   1.616 +  // array as they are shutdown.
   1.617 +  int32_t childDocCount = mChildDocuments.Length();
   1.618 +  for (int32_t idx = childDocCount - 1; idx >= 0; idx--)
   1.619 +    mChildDocuments[idx]->Shutdown();
   1.620 +
   1.621 +  mChildDocuments.Clear();
   1.622 +
   1.623 +  if (mVirtualCursor) {
   1.624 +    mVirtualCursor->RemoveObserver(this);
   1.625 +    mVirtualCursor = nullptr;
   1.626 +  }
   1.627 +
   1.628 +  mPresShell->SetDocAccessible(nullptr);
   1.629 +  mPresShell = nullptr;  // Avoid reentrancy
   1.630 +
   1.631 +  mDependentIDsHash.Clear();
   1.632 +  mNodeToAccessibleMap.Clear();
   1.633 +  ClearCache(mAccessibleCache);
   1.634 +
   1.635 +  HyperTextAccessibleWrap::Shutdown();
   1.636 +
   1.637 +  GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc);
   1.638 +}
   1.639 +
   1.640 +nsIFrame*
   1.641 +DocAccessible::GetFrame() const
   1.642 +{
   1.643 +  nsIFrame* root = nullptr;
   1.644 +  if (mPresShell)
   1.645 +    root = mPresShell->GetRootFrame();
   1.646 +
   1.647 +  return root;
   1.648 +}
   1.649 +
   1.650 +// DocAccessible protected member
   1.651 +void
   1.652 +DocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
   1.653 +{
   1.654 +  *aRelativeFrame = GetFrame();
   1.655 +
   1.656 +  nsIDocument *document = mDocumentNode;
   1.657 +  nsIDocument *parentDoc = nullptr;
   1.658 +
   1.659 +  while (document) {
   1.660 +    nsIPresShell *presShell = document->GetShell();
   1.661 +    if (!presShell) {
   1.662 +      return;
   1.663 +    }
   1.664 +
   1.665 +    nsRect scrollPort;
   1.666 +    nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
   1.667 +    if (sf) {
   1.668 +      scrollPort = sf->GetScrollPortRect();
   1.669 +    } else {
   1.670 +      nsIFrame* rootFrame = presShell->GetRootFrame();
   1.671 +      if (!rootFrame) {
   1.672 +        return;
   1.673 +      }
   1.674 +      scrollPort = rootFrame->GetRect();
   1.675 +    }
   1.676 +
   1.677 +    if (parentDoc) {  // After first time thru loop
   1.678 +      // XXXroc bogus code! scrollPort is relative to the viewport of
   1.679 +      // this document, but we're intersecting rectangles derived from
   1.680 +      // multiple documents and assuming they're all in the same coordinate
   1.681 +      // system. See bug 514117.
   1.682 +      aBounds.IntersectRect(scrollPort, aBounds);
   1.683 +    }
   1.684 +    else {  // First time through loop
   1.685 +      aBounds = scrollPort;
   1.686 +    }
   1.687 +
   1.688 +    document = parentDoc = document->GetParentDocument();
   1.689 +  }
   1.690 +}
   1.691 +
   1.692 +// DocAccessible protected member
   1.693 +nsresult
   1.694 +DocAccessible::AddEventListeners()
   1.695 +{
   1.696 +  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
   1.697 +
   1.698 +  // We want to add a command observer only if the document is content and has
   1.699 +  // an editor.
   1.700 +  if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
   1.701 +    nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
   1.702 +    if (commandManager)
   1.703 +      commandManager->AddCommandObserver(this, "obs_documentCreated");
   1.704 +  }
   1.705 +
   1.706 +  SelectionMgr()->AddDocSelectionListener(mPresShell);
   1.707 +
   1.708 +  // Add document observer.
   1.709 +  mDocumentNode->AddObserver(this);
   1.710 +  return NS_OK;
   1.711 +}
   1.712 +
   1.713 +// DocAccessible protected member
   1.714 +nsresult
   1.715 +DocAccessible::RemoveEventListeners()
   1.716 +{
   1.717 +  // Remove listeners associated with content documents
   1.718 +  // Remove scroll position listener
   1.719 +  RemoveScrollListener();
   1.720 +
   1.721 +  NS_ASSERTION(mDocumentNode, "No document during removal of listeners.");
   1.722 +
   1.723 +  if (mDocumentNode) {
   1.724 +    mDocumentNode->RemoveObserver(this);
   1.725 +
   1.726 +    nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
   1.727 +    NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
   1.728 +
   1.729 +    if (docShellTreeItem) {
   1.730 +      if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
   1.731 +        nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
   1.732 +        if (commandManager) {
   1.733 +          commandManager->RemoveCommandObserver(this, "obs_documentCreated");
   1.734 +        }
   1.735 +      }
   1.736 +    }
   1.737 +  }
   1.738 +
   1.739 +  if (mScrollWatchTimer) {
   1.740 +    mScrollWatchTimer->Cancel();
   1.741 +    mScrollWatchTimer = nullptr;
   1.742 +    NS_RELEASE_THIS(); // Kung fu death grip
   1.743 +  }
   1.744 +
   1.745 +  SelectionMgr()->RemoveDocSelectionListener(mPresShell);
   1.746 +  return NS_OK;
   1.747 +}
   1.748 +
   1.749 +void
   1.750 +DocAccessible::ScrollTimerCallback(nsITimer* aTimer, void* aClosure)
   1.751 +{
   1.752 +  DocAccessible* docAcc = reinterpret_cast<DocAccessible*>(aClosure);
   1.753 +
   1.754 +  if (docAcc && docAcc->mScrollPositionChangedTicks &&
   1.755 +      ++docAcc->mScrollPositionChangedTicks > 2) {
   1.756 +    // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
   1.757 +    // We only want to fire accessibilty scroll event when scrolling stops or pauses
   1.758 +    // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
   1.759 +    // That indicates a pause in scrolling, so we fire the accessibilty scroll event
   1.760 +    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
   1.761 +
   1.762 +    docAcc->mScrollPositionChangedTicks = 0;
   1.763 +    if (docAcc->mScrollWatchTimer) {
   1.764 +      docAcc->mScrollWatchTimer->Cancel();
   1.765 +      docAcc->mScrollWatchTimer = nullptr;
   1.766 +      NS_RELEASE(docAcc); // Release kung fu death grip
   1.767 +    }
   1.768 +  }
   1.769 +}
   1.770 +
   1.771 +////////////////////////////////////////////////////////////////////////////////
   1.772 +// nsIScrollPositionListener
   1.773 +
   1.774 +void
   1.775 +DocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
   1.776 +{
   1.777 +  // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
   1.778 +  // then the ::Notify() method will fire the accessibility event for scroll position changes
   1.779 +  const uint32_t kScrollPosCheckWait = 50;
   1.780 +  if (mScrollWatchTimer) {
   1.781 +    mScrollWatchTimer->SetDelay(kScrollPosCheckWait);  // Create new timer, to avoid leaks
   1.782 +  }
   1.783 +  else {
   1.784 +    mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.785 +    if (mScrollWatchTimer) {
   1.786 +      NS_ADDREF_THIS(); // Kung fu death grip
   1.787 +      mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
   1.788 +                                              kScrollPosCheckWait,
   1.789 +                                              nsITimer::TYPE_REPEATING_SLACK);
   1.790 +    }
   1.791 +  }
   1.792 +  mScrollPositionChangedTicks = 1;
   1.793 +}
   1.794 +
   1.795 +////////////////////////////////////////////////////////////////////////////////
   1.796 +// nsIObserver
   1.797 +
   1.798 +NS_IMETHODIMP
   1.799 +DocAccessible::Observe(nsISupports* aSubject, const char* aTopic,
   1.800 +                       const char16_t* aData)
   1.801 +{
   1.802 +  if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {    
   1.803 +    // State editable will now be set, readonly is now clear
   1.804 +    // Normally we only fire delayed events created from the node, not an
   1.805 +    // accessible object. See the AccStateChangeEvent constructor for details
   1.806 +    // about this exceptional case.
   1.807 +    nsRefPtr<AccEvent> event =
   1.808 +      new AccStateChangeEvent(this, states::EDITABLE, true);
   1.809 +    FireDelayedEvent(event);
   1.810 +  }
   1.811 +
   1.812 +  return NS_OK;
   1.813 +}
   1.814 +
   1.815 +////////////////////////////////////////////////////////////////////////////////
   1.816 +// nsIAccessiblePivotObserver
   1.817 +
   1.818 +NS_IMETHODIMP
   1.819 +DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
   1.820 +                              nsIAccessible* aOldAccessible,
   1.821 +                              int32_t aOldStart, int32_t aOldEnd,
   1.822 +                              PivotMoveReason aReason)
   1.823 +{
   1.824 +  nsRefPtr<AccEvent> event = new AccVCChangeEvent(this, aOldAccessible,
   1.825 +                                                  aOldStart, aOldEnd,
   1.826 +                                                  aReason);
   1.827 +  nsEventShell::FireEvent(event);
   1.828 +
   1.829 +  return NS_OK;
   1.830 +}
   1.831 +
   1.832 +////////////////////////////////////////////////////////////////////////////////
   1.833 +// nsIDocumentObserver
   1.834 +
   1.835 +NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
   1.836 +NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
   1.837 +NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(DocAccessible)
   1.838 +
   1.839 +void
   1.840 +DocAccessible::AttributeWillChange(nsIDocument* aDocument,
   1.841 +                                   dom::Element* aElement,
   1.842 +                                   int32_t aNameSpaceID,
   1.843 +                                   nsIAtom* aAttribute, int32_t aModType)
   1.844 +{
   1.845 +  Accessible* accessible = GetAccessible(aElement);
   1.846 +  if (!accessible) {
   1.847 +    if (aElement != mContent)
   1.848 +      return;
   1.849 +
   1.850 +    accessible = this;
   1.851 +  }
   1.852 +
   1.853 +  // Update dependent IDs cache. Take care of elements that are accessible
   1.854 +  // because dependent IDs cache doesn't contain IDs from non accessible
   1.855 +  // elements.
   1.856 +  if (aModType != nsIDOMMutationEvent::ADDITION)
   1.857 +    RemoveDependentIDsFor(aElement, aAttribute);
   1.858 +
   1.859 +  // Store the ARIA attribute old value so that it can be used after
   1.860 +  // attribute change. Note, we assume there's no nested ARIA attribute
   1.861 +  // changes. If this happens then we should end up with keeping a stack of
   1.862 +  // old values.
   1.863 +
   1.864 +  // XXX TODO: bugs 472142, 472143.
   1.865 +  // Here we will want to cache whatever attribute values we are interested
   1.866 +  // in, such as the existence of aria-pressed for button (so we know if we
   1.867 +  // need to newly expose it as a toggle button) etc.
   1.868 +  if (aAttribute == nsGkAtoms::aria_checked ||
   1.869 +      aAttribute == nsGkAtoms::aria_pressed) {
   1.870 +    mARIAAttrOldValue = (aModType != nsIDOMMutationEvent::ADDITION) ?
   1.871 +      nsAccUtils::GetARIAToken(aElement, aAttribute) : nullptr;
   1.872 +    return;
   1.873 +  }
   1.874 +
   1.875 +  if (aAttribute == nsGkAtoms::aria_disabled ||
   1.876 +      aAttribute == nsGkAtoms::disabled)
   1.877 +    mStateBitWasOn = accessible->Unavailable();
   1.878 +}
   1.879 +
   1.880 +void
   1.881 +DocAccessible::AttributeChanged(nsIDocument* aDocument,
   1.882 +                                dom::Element* aElement,
   1.883 +                                int32_t aNameSpaceID, nsIAtom* aAttribute,
   1.884 +                                int32_t aModType)
   1.885 +{
   1.886 +  NS_ASSERTION(!IsDefunct(),
   1.887 +               "Attribute changed called on defunct document accessible!");
   1.888 +
   1.889 +  // Proceed even if the element is not accessible because element may become
   1.890 +  // accessible if it gets certain attribute.
   1.891 +  if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
   1.892 +    return;
   1.893 +
   1.894 +  // Ignore attribute change if the element doesn't have an accessible (at all
   1.895 +  // or still) iff the element is not a root content of this document accessible
   1.896 +  // (which is treated as attribute change on this document accessible).
   1.897 +  // Note: we don't bail if all the content hasn't finished loading because
   1.898 +  // these attributes are changing for a loaded part of the content.
   1.899 +  Accessible* accessible = GetAccessible(aElement);
   1.900 +  if (!accessible) {
   1.901 +    if (mContent != aElement)
   1.902 +      return;
   1.903 +
   1.904 +    accessible = this;
   1.905 +  }
   1.906 +
   1.907 +  // Fire accessible events iff there's an accessible, otherwise we consider
   1.908 +  // the accessible state wasn't changed, i.e. its state is initial state.
   1.909 +  AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
   1.910 +
   1.911 +  // Update dependent IDs cache. Take care of accessible elements because no
   1.912 +  // accessible element means either the element is not accessible at all or
   1.913 +  // its accessible will be created later. It doesn't make sense to keep
   1.914 +  // dependent IDs for non accessible elements. For the second case we'll update
   1.915 +  // dependent IDs cache when its accessible is created.
   1.916 +  if (aModType == nsIDOMMutationEvent::MODIFICATION ||
   1.917 +      aModType == nsIDOMMutationEvent::ADDITION) {
   1.918 +    AddDependentIDsFor(aElement, aAttribute);
   1.919 +  }
   1.920 +}
   1.921 +
   1.922 +// DocAccessible protected member
   1.923 +void
   1.924 +DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
   1.925 +                                    int32_t aNameSpaceID, nsIAtom* aAttribute)
   1.926 +{
   1.927 +  // Fire accessible event after short timer, because we need to wait for
   1.928 +  // DOM attribute & resulting layout to actually change. Otherwise,
   1.929 +  // assistive technology will retrieve the wrong state/value/selection info.
   1.930 +
   1.931 +  // XXX todo
   1.932 +  // We still need to handle special HTML cases here
   1.933 +  // For example, if an <img>'s usemap attribute is modified
   1.934 +  // Otherwise it may just be a state change, for example an object changing
   1.935 +  // its visibility
   1.936 +  // 
   1.937 +  // XXX todo: report aria state changes for "undefined" literal value changes
   1.938 +  // filed as bug 472142
   1.939 +  //
   1.940 +  // XXX todo:  invalidate accessible when aria state changes affect exposed role
   1.941 +  // filed as bug 472143
   1.942 +
   1.943 +  // Universal boolean properties that don't require a role. Fire the state
   1.944 +  // change when disabled or aria-disabled attribute is set.
   1.945 +  // Note. Checking the XUL or HTML namespace would not seem to gain us
   1.946 +  // anything, because disabled attribute really is going to mean the same
   1.947 +  // thing in any namespace.
   1.948 +  // Note. We use the attribute instead of the disabled state bit because
   1.949 +  // ARIA's aria-disabled does not affect the disabled state bit.
   1.950 +  if (aAttribute == nsGkAtoms::disabled ||
   1.951 +      aAttribute == nsGkAtoms::aria_disabled) {
   1.952 +    // Do nothing if state wasn't changed (like @aria-disabled was removed but
   1.953 +    // @disabled is still presented).
   1.954 +    if (aAccessible->Unavailable() == mStateBitWasOn)
   1.955 +      return;
   1.956 +
   1.957 +    nsRefPtr<AccEvent> enabledChangeEvent =
   1.958 +      new AccStateChangeEvent(aAccessible, states::ENABLED, mStateBitWasOn);
   1.959 +    FireDelayedEvent(enabledChangeEvent);
   1.960 +
   1.961 +    nsRefPtr<AccEvent> sensitiveChangeEvent =
   1.962 +      new AccStateChangeEvent(aAccessible, states::SENSITIVE, mStateBitWasOn);
   1.963 +    FireDelayedEvent(sensitiveChangeEvent);
   1.964 +    return;
   1.965 +  }
   1.966 +
   1.967 +  // Check for namespaced ARIA attribute
   1.968 +  if (aNameSpaceID == kNameSpaceID_None) {
   1.969 +    // Check for hyphenated aria-foo property?
   1.970 +    if (StringBeginsWith(nsDependentAtomString(aAttribute),
   1.971 +                         NS_LITERAL_STRING("aria-"))) {
   1.972 +      ARIAAttributeChanged(aAccessible, aAttribute);
   1.973 +    }
   1.974 +  }
   1.975 +
   1.976 +  // Fire name change and description change events. XXX: it's not complete and
   1.977 +  // dupes the code logic of accessible name and description calculation, we do
   1.978 +  // that for performance reasons.
   1.979 +  if (aAttribute == nsGkAtoms::aria_label) {
   1.980 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
   1.981 +    return;
   1.982 +  }
   1.983 +
   1.984 +  if (aAttribute == nsGkAtoms::aria_describedby) {
   1.985 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
   1.986 +    return;
   1.987 +  }
   1.988 +
   1.989 +  nsIContent* elm = aAccessible->GetContent();
   1.990 +  if (aAttribute == nsGkAtoms::aria_labelledby &&
   1.991 +      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
   1.992 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
   1.993 +    return;
   1.994 +  }
   1.995 +
   1.996 +  if (aAttribute == nsGkAtoms::alt &&
   1.997 +      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
   1.998 +      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby)) {
   1.999 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  1.1000 +    return;
  1.1001 +  }
  1.1002 +
  1.1003 +  if (aAttribute == nsGkAtoms::title) {
  1.1004 +    if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
  1.1005 +        !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby) &&
  1.1006 +        !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
  1.1007 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
  1.1008 +      return;
  1.1009 +    }
  1.1010 +
  1.1011 +    if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_describedby))
  1.1012 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
  1.1013 +
  1.1014 +    return;
  1.1015 +  }
  1.1016 +
  1.1017 +  if (aAttribute == nsGkAtoms::aria_busy) {
  1.1018 +    bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
  1.1019 +                                 eCaseMatters);
  1.1020 +    nsRefPtr<AccEvent> event =
  1.1021 +      new AccStateChangeEvent(aAccessible, states::BUSY, isOn);
  1.1022 +    FireDelayedEvent(event);
  1.1023 +    return;
  1.1024 +  }
  1.1025 +
  1.1026 +  // ARIA or XUL selection
  1.1027 +  if ((aAccessible->GetContent()->IsXUL() && aAttribute == nsGkAtoms::selected) ||
  1.1028 +      aAttribute == nsGkAtoms::aria_selected) {
  1.1029 +    Accessible* widget =
  1.1030 +      nsAccUtils::GetSelectableContainer(aAccessible, aAccessible->State());
  1.1031 +    if (widget) {
  1.1032 +      AccSelChangeEvent::SelChangeType selChangeType =
  1.1033 +        elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters) ?
  1.1034 +          AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
  1.1035 +
  1.1036 +      nsRefPtr<AccEvent> event =
  1.1037 +        new AccSelChangeEvent(widget, aAccessible, selChangeType);
  1.1038 +      FireDelayedEvent(event);
  1.1039 +    }
  1.1040 +
  1.1041 +    return;
  1.1042 +  }
  1.1043 +
  1.1044 +  if (aAttribute == nsGkAtoms::contenteditable) {
  1.1045 +    nsRefPtr<AccEvent> editableChangeEvent =
  1.1046 +      new AccStateChangeEvent(aAccessible, states::EDITABLE);
  1.1047 +    FireDelayedEvent(editableChangeEvent);
  1.1048 +    return;
  1.1049 +  }
  1.1050 +
  1.1051 +  if (aAttribute == nsGkAtoms::value) {
  1.1052 +    if (aAccessible->IsProgress())
  1.1053 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
  1.1054 +  }
  1.1055 +}
  1.1056 +
  1.1057 +// DocAccessible protected member
  1.1058 +void
  1.1059 +DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute)
  1.1060 +{
  1.1061 +  // Note: For universal/global ARIA states and properties we don't care if
  1.1062 +  // there is an ARIA role present or not.
  1.1063 +
  1.1064 +  if (aAttribute == nsGkAtoms::aria_required) {
  1.1065 +    nsRefPtr<AccEvent> event =
  1.1066 +      new AccStateChangeEvent(aAccessible, states::REQUIRED);
  1.1067 +    FireDelayedEvent(event);
  1.1068 +    return;
  1.1069 +  }
  1.1070 +
  1.1071 +  if (aAttribute == nsGkAtoms::aria_invalid) {
  1.1072 +    nsRefPtr<AccEvent> event =
  1.1073 +      new AccStateChangeEvent(aAccessible, states::INVALID);
  1.1074 +    FireDelayedEvent(event);
  1.1075 +    return;
  1.1076 +  }
  1.1077 +
  1.1078 +  // The activedescendant universal property redirects accessible focus events
  1.1079 +  // to the element with the id that activedescendant points to. Make sure
  1.1080 +  // the tree up to date before processing.
  1.1081 +  if (aAttribute == nsGkAtoms::aria_activedescendant) {
  1.1082 +    mNotificationController->HandleNotification<DocAccessible, Accessible>
  1.1083 +      (this, &DocAccessible::ARIAActiveDescendantChanged, aAccessible);
  1.1084 +
  1.1085 +    return;
  1.1086 +  }
  1.1087 +
  1.1088 +  // We treat aria-expanded as a global ARIA state for historical reasons
  1.1089 +  if (aAttribute == nsGkAtoms::aria_expanded) {
  1.1090 +    nsRefPtr<AccEvent> event =
  1.1091 +      new AccStateChangeEvent(aAccessible, states::EXPANDED);
  1.1092 +    FireDelayedEvent(event);
  1.1093 +    return;
  1.1094 +  }
  1.1095 +
  1.1096 +  // For aria attributes like drag and drop changes we fire a generic attribute
  1.1097 +  // change event; at least until native API comes up with a more meaningful event.
  1.1098 +  uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
  1.1099 +  if (!(attrFlags & ATTR_BYPASSOBJ))
  1.1100 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
  1.1101 +                     aAccessible);
  1.1102 +
  1.1103 +  nsIContent* elm = aAccessible->GetContent();
  1.1104 +
  1.1105 +  if (aAttribute == nsGkAtoms::aria_checked ||
  1.1106 +      (aAccessible->IsButton() &&
  1.1107 +       aAttribute == nsGkAtoms::aria_pressed)) {
  1.1108 +    const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
  1.1109 +                            states::CHECKED : states::PRESSED;
  1.1110 +    nsRefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
  1.1111 +    FireDelayedEvent(event);
  1.1112 +
  1.1113 +    bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
  1.1114 +    bool isMixed = elm->AttrValueIs(kNameSpaceID_None, aAttribute,
  1.1115 +                                    nsGkAtoms::mixed, eCaseMatters);
  1.1116 +    if (isMixed != wasMixed) {
  1.1117 +      nsRefPtr<AccEvent> event =
  1.1118 +        new AccStateChangeEvent(aAccessible, states::MIXED, isMixed);
  1.1119 +      FireDelayedEvent(event);
  1.1120 +    }
  1.1121 +    return;
  1.1122 +  }
  1.1123 +
  1.1124 +  if (aAttribute == nsGkAtoms::aria_readonly) {
  1.1125 +    nsRefPtr<AccEvent> event =
  1.1126 +      new AccStateChangeEvent(aAccessible, states::READONLY);
  1.1127 +    FireDelayedEvent(event);
  1.1128 +    return;
  1.1129 +  }
  1.1130 +
  1.1131 +  // Fire value change event whenever aria-valuetext is changed, or
  1.1132 +  // when aria-valuenow is changed and aria-valuetext is empty
  1.1133 +  if (aAttribute == nsGkAtoms::aria_valuetext ||
  1.1134 +      (aAttribute == nsGkAtoms::aria_valuenow &&
  1.1135 +       (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
  1.1136 +        elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
  1.1137 +                         nsGkAtoms::_empty, eCaseMatters)))) {
  1.1138 +    FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
  1.1139 +    return;
  1.1140 +  }
  1.1141 +}
  1.1142 +
  1.1143 +void
  1.1144 +DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
  1.1145 +{
  1.1146 +  nsIContent* elm = aAccessible->GetContent();
  1.1147 +  if (elm && aAccessible->IsActiveWidget()) {
  1.1148 +    nsAutoString id;
  1.1149 +    if (elm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
  1.1150 +      dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
  1.1151 +      if (activeDescendantElm) {
  1.1152 +        Accessible* activeDescendant = GetAccessible(activeDescendantElm);
  1.1153 +        if (activeDescendant) {
  1.1154 +          FocusMgr()->ActiveItemChanged(activeDescendant, false);
  1.1155 +#ifdef A11Y_LOG
  1.1156 +          if (logging::IsEnabled(logging::eFocus))
  1.1157 +            logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
  1.1158 +                                              activeDescendant);
  1.1159 +#endif
  1.1160 +        }
  1.1161 +      }
  1.1162 +    }
  1.1163 +  }
  1.1164 +}
  1.1165 +
  1.1166 +void
  1.1167 +DocAccessible::ContentAppended(nsIDocument* aDocument,
  1.1168 +                               nsIContent* aContainer,
  1.1169 +                               nsIContent* aFirstNewContent,
  1.1170 +                               int32_t /* unused */)
  1.1171 +{
  1.1172 +}
  1.1173 +
  1.1174 +void
  1.1175 +DocAccessible::ContentStateChanged(nsIDocument* aDocument,
  1.1176 +                                   nsIContent* aContent,
  1.1177 +                                   EventStates aStateMask)
  1.1178 +{
  1.1179 +  Accessible* accessible = GetAccessible(aContent);
  1.1180 +  if (!accessible)
  1.1181 +    return;
  1.1182 +
  1.1183 +  if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
  1.1184 +    Accessible* widget = accessible->ContainerWidget();
  1.1185 +    if (widget && widget->IsSelect()) {
  1.1186 +      AccSelChangeEvent::SelChangeType selChangeType =
  1.1187 +        aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
  1.1188 +          AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
  1.1189 +      nsRefPtr<AccEvent> event =
  1.1190 +        new AccSelChangeEvent(widget, accessible, selChangeType);
  1.1191 +      FireDelayedEvent(event);
  1.1192 +      return;
  1.1193 +    }
  1.1194 +
  1.1195 +    nsRefPtr<AccEvent> event =
  1.1196 +      new AccStateChangeEvent(accessible, states::CHECKED,
  1.1197 +                              aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED));
  1.1198 +    FireDelayedEvent(event);
  1.1199 +  }
  1.1200 +
  1.1201 +  if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
  1.1202 +    nsRefPtr<AccEvent> event =
  1.1203 +      new AccStateChangeEvent(accessible, states::INVALID, true);
  1.1204 +    FireDelayedEvent(event);
  1.1205 +  }
  1.1206 +
  1.1207 +  if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
  1.1208 +    nsRefPtr<AccEvent> event =
  1.1209 +      new AccStateChangeEvent(accessible, states::TRAVERSED, true);
  1.1210 +    FireDelayedEvent(event);
  1.1211 +  }
  1.1212 +}
  1.1213 +
  1.1214 +void
  1.1215 +DocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
  1.1216 +                                     EventStates aStateMask)
  1.1217 +{
  1.1218 +}
  1.1219 +
  1.1220 +void
  1.1221 +DocAccessible::CharacterDataWillChange(nsIDocument* aDocument,
  1.1222 +                                       nsIContent* aContent,
  1.1223 +                                       CharacterDataChangeInfo* aInfo)
  1.1224 +{
  1.1225 +}
  1.1226 +
  1.1227 +void
  1.1228 +DocAccessible::CharacterDataChanged(nsIDocument* aDocument,
  1.1229 +                                    nsIContent* aContent,
  1.1230 +                                    CharacterDataChangeInfo* aInfo)
  1.1231 +{
  1.1232 +}
  1.1233 +
  1.1234 +void
  1.1235 +DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
  1.1236 +                               nsIContent* aChild, int32_t /* unused */)
  1.1237 +{
  1.1238 +}
  1.1239 +
  1.1240 +void
  1.1241 +DocAccessible::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer,
  1.1242 +                              nsIContent* aChild, int32_t /* unused */,
  1.1243 +                              nsIContent* aPreviousSibling)
  1.1244 +{
  1.1245 +}
  1.1246 +
  1.1247 +void
  1.1248 +DocAccessible::ParentChainChanged(nsIContent* aContent)
  1.1249 +{
  1.1250 +}
  1.1251 +
  1.1252 +
  1.1253 +////////////////////////////////////////////////////////////////////////////////
  1.1254 +// Accessible
  1.1255 +
  1.1256 +#ifdef A11Y_LOG
  1.1257 +nsresult
  1.1258 +DocAccessible::HandleAccEvent(AccEvent* aEvent)
  1.1259 +{
  1.1260 +  if (logging::IsEnabled(logging::eDocLoad))
  1.1261 +    logging::DocLoadEventHandled(aEvent);
  1.1262 +
  1.1263 +  return HyperTextAccessible::HandleAccEvent(aEvent);
  1.1264 +}
  1.1265 +#endif
  1.1266 +
  1.1267 +////////////////////////////////////////////////////////////////////////////////
  1.1268 +// Public members
  1.1269 +
  1.1270 +void*
  1.1271 +DocAccessible::GetNativeWindow() const
  1.1272 +{
  1.1273 +  if (!mPresShell)
  1.1274 +    return nullptr;
  1.1275 +
  1.1276 +  nsViewManager* vm = mPresShell->GetViewManager();
  1.1277 +  if (!vm)
  1.1278 +    return nullptr;
  1.1279 +
  1.1280 +  nsCOMPtr<nsIWidget> widget;
  1.1281 +  vm->GetRootWidget(getter_AddRefs(widget));
  1.1282 +  if (widget)
  1.1283 +    return widget->GetNativeData(NS_NATIVE_WINDOW);
  1.1284 +
  1.1285 +  return nullptr;
  1.1286 +}
  1.1287 +
  1.1288 +Accessible*
  1.1289 +DocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
  1.1290 +{
  1.1291 +  Accessible* child = GetAccessibleByUniqueID(aUniqueID);
  1.1292 +  if (child)
  1.1293 +    return child;
  1.1294 +
  1.1295 +  uint32_t childDocCount = mChildDocuments.Length();
  1.1296 +  for (uint32_t childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
  1.1297 +    DocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
  1.1298 +    child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
  1.1299 +    if (child)
  1.1300 +      return child;
  1.1301 +  }
  1.1302 +
  1.1303 +  return nullptr;
  1.1304 +}
  1.1305 +
  1.1306 +Accessible*
  1.1307 +DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const
  1.1308 +{
  1.1309 +  if (!aNode || !aNode->IsInDoc())
  1.1310 +    return nullptr;
  1.1311 +
  1.1312 +  nsINode* currNode = aNode;
  1.1313 +  Accessible* accessible = nullptr;
  1.1314 +  while (!(accessible = GetAccessible(currNode)) &&
  1.1315 +         (currNode = currNode->GetParentNode()));
  1.1316 +
  1.1317 +  return accessible;
  1.1318 +}
  1.1319 +
  1.1320 +Accessible*
  1.1321 +DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const
  1.1322 +{
  1.1323 +  Accessible* acc = GetAccessible(aNode);
  1.1324 +  if (acc)
  1.1325 +    return acc;
  1.1326 +
  1.1327 +  acc = GetContainerAccessible(aNode);
  1.1328 +  if (acc) {
  1.1329 +    uint32_t childCnt = acc->ChildCount();
  1.1330 +    for (uint32_t idx = 0; idx < childCnt; idx++) {
  1.1331 +      Accessible* child = acc->GetChildAt(idx);
  1.1332 +      for (nsIContent* elm = child->GetContent();
  1.1333 +           elm && elm != acc->GetContent();
  1.1334 +           elm = elm->GetFlattenedTreeParent()) {
  1.1335 +        if (elm == aNode)
  1.1336 +          return child;
  1.1337 +      }
  1.1338 +    }
  1.1339 +  }
  1.1340 +
  1.1341 +  return nullptr;
  1.1342 +}
  1.1343 +
  1.1344 +void
  1.1345 +DocAccessible::BindToDocument(Accessible* aAccessible,
  1.1346 +                              nsRoleMapEntry* aRoleMapEntry)
  1.1347 +{
  1.1348 +  // Put into DOM node cache.
  1.1349 +  if (aAccessible->IsNodeMapEntry())
  1.1350 +    mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible);
  1.1351 +
  1.1352 +  // Put into unique ID cache.
  1.1353 +  mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
  1.1354 +
  1.1355 +  aAccessible->SetRoleMapEntry(aRoleMapEntry);
  1.1356 +
  1.1357 +  nsIContent* content = aAccessible->GetContent();
  1.1358 +  if (content && content->IsElement())
  1.1359 +    AddDependentIDsFor(content->AsElement());
  1.1360 +}
  1.1361 +
  1.1362 +void
  1.1363 +DocAccessible::UnbindFromDocument(Accessible* aAccessible)
  1.1364 +{
  1.1365 +  NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
  1.1366 +               "Unbinding the unbound accessible!");
  1.1367 +
  1.1368 +  // Fire focus event on accessible having DOM focus if active item was removed
  1.1369 +  // from the tree.
  1.1370 +  if (FocusMgr()->IsActiveItem(aAccessible)) {
  1.1371 +    FocusMgr()->ActiveItemChanged(nullptr);
  1.1372 +#ifdef A11Y_LOG
  1.1373 +          if (logging::IsEnabled(logging::eFocus))
  1.1374 +            logging::ActiveItemChangeCausedBy("tree shutdown", aAccessible);
  1.1375 +#endif
  1.1376 +  }
  1.1377 +
  1.1378 +  // Remove an accessible from node-to-accessible map if it exists there.
  1.1379 +  if (aAccessible->IsNodeMapEntry() &&
  1.1380 +      mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
  1.1381 +    mNodeToAccessibleMap.Remove(aAccessible->GetNode());
  1.1382 +
  1.1383 +  void* uniqueID = aAccessible->UniqueID();
  1.1384 +
  1.1385 +  NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
  1.1386 +  aAccessible->Shutdown();
  1.1387 +
  1.1388 +  mAccessibleCache.Remove(uniqueID);
  1.1389 +}
  1.1390 +
  1.1391 +void
  1.1392 +DocAccessible::ContentInserted(nsIContent* aContainerNode,
  1.1393 +                               nsIContent* aStartChildNode,
  1.1394 +                               nsIContent* aEndChildNode)
  1.1395 +{
  1.1396 +  // Ignore content insertions until we constructed accessible tree. Otherwise
  1.1397 +  // schedule tree update on content insertion after layout.
  1.1398 +  if (mNotificationController && HasLoadState(eTreeConstructed)) {
  1.1399 +    // Update the whole tree of this document accessible when the container is
  1.1400 +    // null (document element is inserted or removed).
  1.1401 +    Accessible* container = aContainerNode ?
  1.1402 +      GetAccessibleOrContainer(aContainerNode) : this;
  1.1403 +
  1.1404 +    mNotificationController->ScheduleContentInsertion(container,
  1.1405 +                                                      aStartChildNode,
  1.1406 +                                                      aEndChildNode);
  1.1407 +  }
  1.1408 +}
  1.1409 +
  1.1410 +void
  1.1411 +DocAccessible::ContentRemoved(nsIContent* aContainerNode,
  1.1412 +                              nsIContent* aChildNode)
  1.1413 +{
  1.1414 +  // Update the whole tree of this document accessible when the container is
  1.1415 +  // null (document element is removed).
  1.1416 +  Accessible* container = aContainerNode ?
  1.1417 +    GetAccessibleOrContainer(aContainerNode) : this;
  1.1418 +
  1.1419 +  UpdateTree(container, aChildNode, false);
  1.1420 +}
  1.1421 +
  1.1422 +void
  1.1423 +DocAccessible::RecreateAccessible(nsIContent* aContent)
  1.1424 +{
  1.1425 +#ifdef A11Y_LOG
  1.1426 +  if (logging::IsEnabled(logging::eTree)) {
  1.1427 +    logging::MsgBegin("TREE", "accessible recreated");
  1.1428 +    logging::Node("content", aContent);
  1.1429 +    logging::MsgEnd();
  1.1430 +  }
  1.1431 +#endif
  1.1432 +
  1.1433 +  // XXX: we shouldn't recreate whole accessible subtree, instead we should
  1.1434 +  // subclass hide and show events to handle them separately and implement their
  1.1435 +  // coalescence with normal hide and show events. Note, in this case they
  1.1436 +  // should be coalesced with normal show/hide events.
  1.1437 +
  1.1438 +  nsIContent* parent = aContent->GetFlattenedTreeParent();
  1.1439 +  ContentRemoved(parent, aContent);
  1.1440 +  ContentInserted(parent, aContent, aContent->GetNextSibling());
  1.1441 +}
  1.1442 +
  1.1443 +void
  1.1444 +DocAccessible::ProcessInvalidationList()
  1.1445 +{
  1.1446 +  // Invalidate children of container accessible for each element in
  1.1447 +  // invalidation list. Allow invalidation list insertions while container
  1.1448 +  // children are recached.
  1.1449 +  for (uint32_t idx = 0; idx < mInvalidationList.Length(); idx++) {
  1.1450 +    nsIContent* content = mInvalidationList[idx];
  1.1451 +    Accessible* accessible = GetAccessible(content);
  1.1452 +    if (!accessible) {
  1.1453 +      Accessible* container = GetContainerAccessible(content);
  1.1454 +      if (container) {
  1.1455 +        container->UpdateChildren();
  1.1456 +        accessible = GetAccessible(content);
  1.1457 +      }
  1.1458 +    }
  1.1459 +
  1.1460 +    // Make sure the subtree is created.
  1.1461 +    if (accessible)
  1.1462 +      CacheChildrenInSubtree(accessible);
  1.1463 +  }
  1.1464 +
  1.1465 +  mInvalidationList.Clear();
  1.1466 +}
  1.1467 +
  1.1468 +Accessible*
  1.1469 +DocAccessible::GetAccessibleEvenIfNotInMap(nsINode* aNode) const
  1.1470 +{
  1.1471 +if (!aNode->IsContent() || !aNode->AsContent()->IsHTML(nsGkAtoms::area))
  1.1472 +    return GetAccessible(aNode);
  1.1473 +
  1.1474 +  // XXX Bug 135040, incorrect when multiple images use the same map.
  1.1475 +  nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
  1.1476 +  nsImageFrame* imageFrame = do_QueryFrame(frame);
  1.1477 +  if (imageFrame) {
  1.1478 +    Accessible* parent = GetAccessible(imageFrame->GetContent());
  1.1479 +    if (parent) {
  1.1480 +      Accessible* area =
  1.1481 +        parent->AsImageMap()->GetChildAccessibleFor(aNode);
  1.1482 +      if (area)
  1.1483 +        return area;
  1.1484 +
  1.1485 +      return nullptr;
  1.1486 +    }
  1.1487 +  }
  1.1488 +
  1.1489 +  return GetAccessible(aNode);
  1.1490 +}
  1.1491 +
  1.1492 +////////////////////////////////////////////////////////////////////////////////
  1.1493 +// Accessible protected
  1.1494 +
  1.1495 +void
  1.1496 +DocAccessible::CacheChildren()
  1.1497 +{
  1.1498 +  // Search for accessible children starting from the document element since
  1.1499 +  // some web pages tend to insert elements under it rather than document body.
  1.1500 +  dom::Element* rootElm = mDocumentNode->GetRootElement();
  1.1501 +  if (!rootElm)
  1.1502 +    return;
  1.1503 +
  1.1504 +  // Ignore last HTML:br, copied from HyperTextAccessible.
  1.1505 +  TreeWalker walker(this, rootElm);
  1.1506 +  Accessible* lastChild = nullptr;
  1.1507 +  while (Accessible* child = walker.NextChild()) {
  1.1508 +    if (lastChild)
  1.1509 +      AppendChild(lastChild);
  1.1510 +
  1.1511 +    lastChild = child;
  1.1512 +  }
  1.1513 +
  1.1514 +  if (lastChild) {
  1.1515 +    if (lastChild->IsHTMLBr())
  1.1516 +      Document()->UnbindFromDocument(lastChild);
  1.1517 +    else
  1.1518 +      AppendChild(lastChild);
  1.1519 +  }
  1.1520 +}
  1.1521 +
  1.1522 +////////////////////////////////////////////////////////////////////////////////
  1.1523 +// Protected members
  1.1524 +
  1.1525 +void
  1.1526 +DocAccessible::NotifyOfLoading(bool aIsReloading)
  1.1527 +{
  1.1528 +  // Mark the document accessible as loading, if it stays alive then we'll mark
  1.1529 +  // it as loaded when we receive proper notification.
  1.1530 +  mLoadState &= ~eDOMLoaded;
  1.1531 +
  1.1532 +  if (!IsLoadEventTarget())
  1.1533 +    return;
  1.1534 +
  1.1535 +  if (aIsReloading) {
  1.1536 +    // Fire reload and state busy events on existing document accessible while
  1.1537 +    // event from user input flag can be calculated properly and accessible
  1.1538 +    // is alive. When new document gets loaded then this one is destroyed.
  1.1539 +    nsRefPtr<AccEvent> reloadEvent =
  1.1540 +      new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
  1.1541 +    nsEventShell::FireEvent(reloadEvent);
  1.1542 +  }
  1.1543 +
  1.1544 +  // Fire state busy change event. Use delayed event since we don't care
  1.1545 +  // actually if event isn't delivered when the document goes away like a shot.
  1.1546 +  nsRefPtr<AccEvent> stateEvent =
  1.1547 +    new AccStateChangeEvent(this, states::BUSY, true);
  1.1548 +  FireDelayedEvent(stateEvent);
  1.1549 +}
  1.1550 +
  1.1551 +void
  1.1552 +DocAccessible::DoInitialUpdate()
  1.1553 +{
  1.1554 +  if (nsCoreUtils::IsTabDocument(mDocumentNode))
  1.1555 +    mDocFlags |= eTabDocument;
  1.1556 +
  1.1557 +  mLoadState |= eTreeConstructed;
  1.1558 +
  1.1559 +  // The content element may be changed before the initial update and then we
  1.1560 +  // miss the notification (since content tree change notifications are ignored
  1.1561 +  // prior to initial update). Make sure the content element is valid.
  1.1562 +  nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
  1.1563 +  if (mContent != contentElm) {
  1.1564 +    mContent = contentElm;
  1.1565 +    SetRoleMapEntry(aria::GetRoleMap(mContent));
  1.1566 +  }
  1.1567 +
  1.1568 +  // Build initial tree.
  1.1569 +  CacheChildrenInSubtree(this);
  1.1570 +
  1.1571 +  // Fire reorder event after the document tree is constructed. Note, since
  1.1572 +  // this reorder event is processed by parent document then events targeted to
  1.1573 +  // this document may be fired prior to this reorder event. If this is
  1.1574 +  // a problem then consider to keep event processing per tab document.
  1.1575 +  if (!IsRoot()) {
  1.1576 +    nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
  1.1577 +    ParentDocument()->FireDelayedEvent(reorderEvent);
  1.1578 +  }
  1.1579 +}
  1.1580 +
  1.1581 +void
  1.1582 +DocAccessible::ProcessLoad()
  1.1583 +{
  1.1584 +  mLoadState |= eCompletelyLoaded;
  1.1585 +
  1.1586 +#ifdef A11Y_LOG
  1.1587 +  if (logging::IsEnabled(logging::eDocLoad))
  1.1588 +    logging::DocCompleteLoad(this, IsLoadEventTarget());
  1.1589 +#endif
  1.1590 +
  1.1591 +  // Do not fire document complete/stop events for root chrome document
  1.1592 +  // accessibles and for frame/iframe documents because
  1.1593 +  // a) screen readers start working on focus event in the case of root chrome
  1.1594 +  // documents
  1.1595 +  // b) document load event on sub documents causes screen readers to act is if
  1.1596 +  // entire page is reloaded.
  1.1597 +  if (!IsLoadEventTarget())
  1.1598 +    return;
  1.1599 +
  1.1600 +  // Fire complete/load stopped if the load event type is given.
  1.1601 +  if (mLoadEventType) {
  1.1602 +    nsRefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
  1.1603 +    FireDelayedEvent(loadEvent);
  1.1604 +
  1.1605 +    mLoadEventType = 0;
  1.1606 +  }
  1.1607 +
  1.1608 +  // Fire busy state change event.
  1.1609 +  nsRefPtr<AccEvent> stateEvent =
  1.1610 +    new AccStateChangeEvent(this, states::BUSY, false);
  1.1611 +  FireDelayedEvent(stateEvent);
  1.1612 +}
  1.1613 +
  1.1614 +void
  1.1615 +DocAccessible::AddDependentIDsFor(dom::Element* aRelProviderElm,
  1.1616 +                                  nsIAtom* aRelAttr)
  1.1617 +{
  1.1618 +  for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
  1.1619 +    nsIAtom* relAttr = *kRelationAttrs[idx];
  1.1620 +    if (aRelAttr && aRelAttr != relAttr)
  1.1621 +      continue;
  1.1622 +
  1.1623 +    if (relAttr == nsGkAtoms::_for) {
  1.1624 +      if (!aRelProviderElm->IsHTML() ||
  1.1625 +          (aRelProviderElm->Tag() != nsGkAtoms::label &&
  1.1626 +           aRelProviderElm->Tag() != nsGkAtoms::output))
  1.1627 +        continue;
  1.1628 +
  1.1629 +    } else if (relAttr == nsGkAtoms::control) {
  1.1630 +      if (!aRelProviderElm->IsXUL() ||
  1.1631 +          (aRelProviderElm->Tag() != nsGkAtoms::label &&
  1.1632 +           aRelProviderElm->Tag() != nsGkAtoms::description))
  1.1633 +        continue;
  1.1634 +    }
  1.1635 +
  1.1636 +    IDRefsIterator iter(this, aRelProviderElm, relAttr);
  1.1637 +    while (true) {
  1.1638 +      const nsDependentSubstring id = iter.NextID();
  1.1639 +      if (id.IsEmpty())
  1.1640 +        break;
  1.1641 +
  1.1642 +      AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
  1.1643 +      if (!providers) {
  1.1644 +        providers = new AttrRelProviderArray();
  1.1645 +        if (providers) {
  1.1646 +          mDependentIDsHash.Put(id, providers);
  1.1647 +        }
  1.1648 +      }
  1.1649 +
  1.1650 +      if (providers) {
  1.1651 +        AttrRelProvider* provider =
  1.1652 +          new AttrRelProvider(relAttr, aRelProviderElm);
  1.1653 +        if (provider) {
  1.1654 +          providers->AppendElement(provider);
  1.1655 +
  1.1656 +          // We've got here during the children caching. If the referenced
  1.1657 +          // content is not accessible then store it to pend its container
  1.1658 +          // children invalidation (this happens immediately after the caching
  1.1659 +          // is finished).
  1.1660 +          nsIContent* dependentContent = iter.GetElem(id);
  1.1661 +          if (dependentContent && !HasAccessible(dependentContent)) {
  1.1662 +            mInvalidationList.AppendElement(dependentContent);
  1.1663 +          }
  1.1664 +        }
  1.1665 +      }
  1.1666 +    }
  1.1667 +
  1.1668 +    // If the relation attribute is given then we don't have anything else to
  1.1669 +    // check.
  1.1670 +    if (aRelAttr)
  1.1671 +      break;
  1.1672 +  }
  1.1673 +}
  1.1674 +
  1.1675 +void
  1.1676 +DocAccessible::RemoveDependentIDsFor(dom::Element* aRelProviderElm,
  1.1677 +                                     nsIAtom* aRelAttr)
  1.1678 +{
  1.1679 +  for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
  1.1680 +    nsIAtom* relAttr = *kRelationAttrs[idx];
  1.1681 +    if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
  1.1682 +      continue;
  1.1683 +
  1.1684 +    IDRefsIterator iter(this, aRelProviderElm, relAttr);
  1.1685 +    while (true) {
  1.1686 +      const nsDependentSubstring id = iter.NextID();
  1.1687 +      if (id.IsEmpty())
  1.1688 +        break;
  1.1689 +
  1.1690 +      AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
  1.1691 +      if (providers) {
  1.1692 +        for (uint32_t jdx = 0; jdx < providers->Length(); ) {
  1.1693 +          AttrRelProvider* provider = (*providers)[jdx];
  1.1694 +          if (provider->mRelAttr == relAttr &&
  1.1695 +              provider->mContent == aRelProviderElm)
  1.1696 +            providers->RemoveElement(provider);
  1.1697 +          else
  1.1698 +            jdx++;
  1.1699 +        }
  1.1700 +        if (providers->Length() == 0)
  1.1701 +          mDependentIDsHash.Remove(id);
  1.1702 +      }
  1.1703 +    }
  1.1704 +
  1.1705 +    // If the relation attribute is given then we don't have anything else to
  1.1706 +    // check.
  1.1707 +    if (aRelAttr)
  1.1708 +      break;
  1.1709 +  }
  1.1710 +}
  1.1711 +
  1.1712 +bool
  1.1713 +DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
  1.1714 +                                            nsIAtom* aAttribute)
  1.1715 +{
  1.1716 +  if (aAttribute == nsGkAtoms::role) {
  1.1717 +    // It is common for js libraries to set the role on the body element after
  1.1718 +    // the document has loaded. In this case we just update the role map entry.
  1.1719 +    if (mContent == aElement) {
  1.1720 +      SetRoleMapEntry(aria::GetRoleMap(aElement));
  1.1721 +      return true;
  1.1722 +    }
  1.1723 +
  1.1724 +    // Recreate the accessible when role is changed because we might require a
  1.1725 +    // different accessible class for the new role or the accessible may expose
  1.1726 +    // a different sets of interfaces (COM restriction).
  1.1727 +    RecreateAccessible(aElement);
  1.1728 +
  1.1729 +    return true;
  1.1730 +  }
  1.1731 +
  1.1732 +  if (aAttribute == nsGkAtoms::href ||
  1.1733 +      aAttribute == nsGkAtoms::onclick) {
  1.1734 +    // Not worth the expense to ensure which namespace these are in. It doesn't
  1.1735 +    // kill use to recreate the accessible even if the attribute was used in
  1.1736 +    // the wrong namespace or an element that doesn't support it.
  1.1737 +
  1.1738 +    // Make sure the accessible is recreated asynchronously to allow the content
  1.1739 +    // to handle the attribute change.
  1.1740 +    RecreateAccessible(aElement);
  1.1741 +    return true;
  1.1742 +  }
  1.1743 +
  1.1744 +  if (aAttribute == nsGkAtoms::aria_multiselectable &&
  1.1745 +      aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
  1.1746 +    // This affects whether the accessible supports SelectAccessible.
  1.1747 +    // COM says we cannot change what interfaces are supported on-the-fly,
  1.1748 +    // so invalidate this object. A new one will be created on demand.
  1.1749 +    RecreateAccessible(aElement);
  1.1750 +
  1.1751 +    return true;
  1.1752 +  }
  1.1753 +
  1.1754 +  return false;
  1.1755 +}
  1.1756 +
  1.1757 +void
  1.1758 +DocAccessible::ProcessContentInserted(Accessible* aContainer,
  1.1759 +                                      const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
  1.1760 +{
  1.1761 +  // Process insertions if the container accessible is still in tree.
  1.1762 +  if (!HasAccessible(aContainer->GetNode()))
  1.1763 +    return;
  1.1764 +
  1.1765 +  bool containerNotUpdated = true;
  1.1766 +
  1.1767 +  for (uint32_t idx = 0; idx < aInsertedContent->Length(); idx++) {
  1.1768 +    // The container might be changed, for example, because of the subsequent
  1.1769 +    // overlapping content insertion (i.e. other content was inserted between
  1.1770 +    // this inserted content and its container or the content was reinserted
  1.1771 +    // into different container of unrelated part of tree). To avoid a double
  1.1772 +    // processing of the content insertion ignore this insertion notification.
  1.1773 +    // Note, the inserted content might be not in tree at all at this point what
  1.1774 +    // means there's no container. Ignore the insertion too.
  1.1775 +
  1.1776 +    Accessible* presentContainer =
  1.1777 +      GetContainerAccessible(aInsertedContent->ElementAt(idx));
  1.1778 +    if (presentContainer != aContainer)
  1.1779 +      continue;
  1.1780 +
  1.1781 +    if (containerNotUpdated) {
  1.1782 +      containerNotUpdated = false;
  1.1783 +
  1.1784 +      if (aContainer == this) {
  1.1785 +        // If new root content has been inserted then update it.
  1.1786 +        nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
  1.1787 +        if (rootContent != mContent) {
  1.1788 +          mContent = rootContent;
  1.1789 +          SetRoleMapEntry(aria::GetRoleMap(mContent));
  1.1790 +        }
  1.1791 +
  1.1792 +        // Continue to update the tree even if we don't have root content.
  1.1793 +        // For example, elements may be inserted under the document element while
  1.1794 +        // there is no HTML body element.
  1.1795 +      }
  1.1796 +
  1.1797 +      // XXX: Invalidate parent-child relations for container accessible and its
  1.1798 +      // children because there's no good way to find insertion point of new child
  1.1799 +      // accessibles into accessible tree. We need to invalidate children even
  1.1800 +      // there's no inserted accessibles in the end because accessible children
  1.1801 +      // are created while parent recaches child accessibles.
  1.1802 +      aContainer->InvalidateChildren();
  1.1803 +      CacheChildrenInSubtree(aContainer);
  1.1804 +    }
  1.1805 +
  1.1806 +    UpdateTree(aContainer, aInsertedContent->ElementAt(idx), true);
  1.1807 +  }
  1.1808 +}
  1.1809 +
  1.1810 +void
  1.1811 +DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
  1.1812 +                          bool aIsInsert)
  1.1813 +{
  1.1814 +  uint32_t updateFlags = eNoAccessible;
  1.1815 +
  1.1816 +  // If child node is not accessible then look for its accessible children.
  1.1817 +  Accessible* child = GetAccessible(aChildNode);
  1.1818 +#ifdef A11Y_LOG
  1.1819 +  if (logging::IsEnabled(logging::eTree)) {
  1.1820 +    logging::MsgBegin("TREE", "process content %s",
  1.1821 +                      (aIsInsert ? "insertion" : "removal"));
  1.1822 +    logging::Node("container", aContainer->GetNode());
  1.1823 +    logging::Node("child", aChildNode);
  1.1824 +    if (child)
  1.1825 +      logging::Address("child", child);
  1.1826 +    else
  1.1827 +      logging::MsgEntry("child accessible: null");
  1.1828 +
  1.1829 +    logging::MsgEnd();
  1.1830 +  }
  1.1831 +#endif
  1.1832 +
  1.1833 +  nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
  1.1834 +
  1.1835 +  if (child) {
  1.1836 +    updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
  1.1837 +  } else {
  1.1838 +    if (aIsInsert) {
  1.1839 +      TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
  1.1840 +
  1.1841 +      while ((child = walker.NextChild()))
  1.1842 +        updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
  1.1843 +    } else {
  1.1844 +      // aChildNode may not coorespond to a particular accessible, to handle
  1.1845 +      // this we go through all the children of aContainer.  Then if a child
  1.1846 +      // has aChildNode as an ancestor, or does not have the node for
  1.1847 +      // aContainer as an ancestor remove that child of aContainer.  Note that
  1.1848 +      // when we are called aChildNode may already have been removed
  1.1849 +      // from the DOM so we can't expect it to have a parent or what was it's
  1.1850 +      // parent to have it as a child.
  1.1851 +      nsINode* containerNode = aContainer->GetNode();
  1.1852 +      for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) {
  1.1853 +        Accessible* child = aContainer->ContentChildAt(idx);
  1.1854 +
  1.1855 +        // If accessible doesn't have its own content then we assume parent
  1.1856 +        // will handle its update.  If child is DocAccessible then we don't
  1.1857 +        // handle updating it here either.
  1.1858 +        if (!child->HasOwnContent() || child->IsDoc()) {
  1.1859 +          idx++;
  1.1860 +          continue;
  1.1861 +        }
  1.1862 +
  1.1863 +        nsINode* childNode = child->GetContent();
  1.1864 +        while (childNode != aChildNode && childNode != containerNode &&
  1.1865 +               (childNode = childNode->GetParentNode()));
  1.1866 +
  1.1867 +        if (childNode != containerNode) {
  1.1868 +          updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
  1.1869 +        } else {
  1.1870 +          idx++;
  1.1871 +        }
  1.1872 +      }
  1.1873 +    }
  1.1874 +  }
  1.1875 +
  1.1876 +  // Content insertion/removal is not cause of accessible tree change.
  1.1877 +  if (updateFlags == eNoAccessible)
  1.1878 +    return;
  1.1879 +
  1.1880 +  // Check to see if change occurred inside an alert, and fire an EVENT_ALERT
  1.1881 +  // if it did.
  1.1882 +  if (aIsInsert && !(updateFlags & eAlertAccessible)) {
  1.1883 +    // XXX: tree traversal is perf issue, accessible should know if they are
  1.1884 +    // children of alert accessible to avoid this.
  1.1885 +    Accessible* ancestor = aContainer;
  1.1886 +    while (ancestor) {
  1.1887 +      if (ancestor->ARIARole() == roles::ALERT) {
  1.1888 +        FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
  1.1889 +        break;
  1.1890 +      }
  1.1891 +
  1.1892 +      // Don't climb above this document.
  1.1893 +      if (ancestor == this)
  1.1894 +        break;
  1.1895 +
  1.1896 +      ancestor = ancestor->Parent();
  1.1897 +    }
  1.1898 +  }
  1.1899 +
  1.1900 +  MaybeNotifyOfValueChange(aContainer);
  1.1901 +
  1.1902 +  // Fire reorder event so the MSAA clients know the children have changed. Also
  1.1903 +  // the event is used internally by MSAA layer.
  1.1904 +  FireDelayedEvent(reorderEvent);
  1.1905 +}
  1.1906 +
  1.1907 +uint32_t
  1.1908 +DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
  1.1909 +                                  AccReorderEvent* aReorderEvent)
  1.1910 +{
  1.1911 +  uint32_t updateFlags = eAccessible;
  1.1912 +
  1.1913 +  // If a focused node has been shown then it could mean its frame was recreated
  1.1914 +  // while the node stays focused and we need to fire focus event on
  1.1915 +  // the accessible we just created. If the queue contains a focus event for
  1.1916 +  // this node already then it will be suppressed by this one.
  1.1917 +  Accessible* focusedAcc = nullptr;
  1.1918 +
  1.1919 +  nsINode* node = aChild->GetNode();
  1.1920 +  if (aIsInsert) {
  1.1921 +    // Create accessible tree for shown accessible.
  1.1922 +    CacheChildrenInSubtree(aChild, &focusedAcc);
  1.1923 +
  1.1924 +  } else {
  1.1925 +    // Fire menupopup end event before hide event if a menu goes away.
  1.1926 +
  1.1927 +    // XXX: We don't look into children of hidden subtree to find hiding
  1.1928 +    // menupopup (as we did prior bug 570275) because we don't do that when
  1.1929 +    // menu is showing (and that's impossible until bug 606924 is fixed).
  1.1930 +    // Nevertheless we should do this at least because layout coalesces
  1.1931 +    // the changes before our processing and we may miss some menupopup
  1.1932 +    // events. Now we just want to be consistent in content insertion/removal
  1.1933 +    // handling.
  1.1934 +    if (aChild->ARIARole() == roles::MENUPOPUP)
  1.1935 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
  1.1936 +  }
  1.1937 +
  1.1938 +  // Fire show/hide event.
  1.1939 +  nsRefPtr<AccMutationEvent> event;
  1.1940 +  if (aIsInsert)
  1.1941 +    event = new AccShowEvent(aChild, node);
  1.1942 +  else
  1.1943 +    event = new AccHideEvent(aChild, node);
  1.1944 +
  1.1945 +  FireDelayedEvent(event);
  1.1946 +  aReorderEvent->AddSubMutationEvent(event);
  1.1947 +
  1.1948 +  if (aIsInsert) {
  1.1949 +    roles::Role ariaRole = aChild->ARIARole();
  1.1950 +    if (ariaRole == roles::MENUPOPUP) {
  1.1951 +      // Fire EVENT_MENUPOPUP_START if ARIA menu appears.
  1.1952 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
  1.1953 +
  1.1954 +    } else if (ariaRole == roles::ALERT) {
  1.1955 +      // Fire EVENT_ALERT if ARIA alert appears.
  1.1956 +      updateFlags = eAlertAccessible;
  1.1957 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
  1.1958 +    }
  1.1959 +  } else {
  1.1960 +    // Update the tree for content removal.
  1.1961 +    // The accessible parent may differ from container accessible if
  1.1962 +    // the parent doesn't have own DOM node like list accessible for HTML
  1.1963 +    // selects.
  1.1964 +    Accessible* parent = aChild->Parent();
  1.1965 +    NS_ASSERTION(parent, "No accessible parent?!");
  1.1966 +    if (parent)
  1.1967 +      parent->RemoveChild(aChild);
  1.1968 +
  1.1969 +    UncacheChildrenInSubtree(aChild);
  1.1970 +  }
  1.1971 +
  1.1972 +  // XXX: do we really want to send focus to focused DOM node not taking into
  1.1973 +  // account active item?
  1.1974 +  if (focusedAcc) {
  1.1975 +    FocusMgr()->DispatchFocusEvent(this, focusedAcc);
  1.1976 +    SelectionMgr()->SetControlSelectionListener(focusedAcc->GetNode()->AsElement());
  1.1977 +  }
  1.1978 +
  1.1979 +  return updateFlags;
  1.1980 +}
  1.1981 +
  1.1982 +void
  1.1983 +DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
  1.1984 +                                      Accessible** aFocusedAcc)
  1.1985 +{
  1.1986 +  // If the accessible is focused then report a focus event after all related
  1.1987 +  // mutation events.
  1.1988 +  if (aFocusedAcc && !*aFocusedAcc &&
  1.1989 +      FocusMgr()->HasDOMFocus(aRoot->GetContent()))
  1.1990 +    *aFocusedAcc = aRoot;
  1.1991 +
  1.1992 +  aRoot->EnsureChildren();
  1.1993 +
  1.1994 +  // Make sure we create accessible tree defined in DOM only, i.e. if accessible
  1.1995 +  // provides specific tree (like XUL trees) then tree creation is handled by
  1.1996 +  // this accessible.
  1.1997 +  uint32_t count = aRoot->ContentChildCount();
  1.1998 +  for (uint32_t idx = 0; idx < count; idx++) {
  1.1999 +    Accessible* child = aRoot->ContentChildAt(idx);
  1.2000 +    NS_ASSERTION(child, "Illicit tree change while tree is created!");
  1.2001 +    // Don't cross document boundaries.
  1.2002 +    if (child && child->IsContent())
  1.2003 +      CacheChildrenInSubtree(child, aFocusedAcc);
  1.2004 +  }
  1.2005 +
  1.2006 +  // Fire document load complete on ARIA documents.
  1.2007 +  // XXX: we should delay an event if the ARIA document has aria-busy.
  1.2008 +  if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
  1.2009 +    a11y::role role = aRoot->ARIARole();
  1.2010 +    if (role == roles::DIALOG || role == roles::DOCUMENT)
  1.2011 +      FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
  1.2012 +  }
  1.2013 +}
  1.2014 +
  1.2015 +void
  1.2016 +DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
  1.2017 +{
  1.2018 +  aRoot->mStateFlags |= eIsNotInDocument;
  1.2019 +
  1.2020 +  nsIContent* rootContent = aRoot->GetContent();
  1.2021 +  if (rootContent && rootContent->IsElement())
  1.2022 +    RemoveDependentIDsFor(rootContent->AsElement());
  1.2023 +
  1.2024 +  uint32_t count = aRoot->ContentChildCount();
  1.2025 +  for (uint32_t idx = 0; idx < count; idx++)
  1.2026 +    UncacheChildrenInSubtree(aRoot->ContentChildAt(idx));
  1.2027 +
  1.2028 +  if (aRoot->IsNodeMapEntry() &&
  1.2029 +      mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
  1.2030 +    mNodeToAccessibleMap.Remove(aRoot->GetNode());
  1.2031 +}
  1.2032 +
  1.2033 +void
  1.2034 +DocAccessible::ShutdownChildrenInSubtree(Accessible* aAccessible)
  1.2035 +{
  1.2036 +  // Traverse through children and shutdown them before this accessible. When
  1.2037 +  // child gets shutdown then it removes itself from children array of its
  1.2038 +  //parent. Use jdx index to process the cases if child is not attached to the
  1.2039 +  // parent and as result doesn't remove itself from its children.
  1.2040 +  uint32_t count = aAccessible->ContentChildCount();
  1.2041 +  for (uint32_t idx = 0, jdx = 0; idx < count; idx++) {
  1.2042 +    Accessible* child = aAccessible->ContentChildAt(jdx);
  1.2043 +    if (!child->IsBoundToParent()) {
  1.2044 +      NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
  1.2045 +      jdx++;
  1.2046 +    }
  1.2047 +
  1.2048 +    // Don't cross document boundaries. The outerdoc shutdown takes care about
  1.2049 +    // its subdocument.
  1.2050 +    if (!child->IsDoc())
  1.2051 +      ShutdownChildrenInSubtree(child);
  1.2052 +  }
  1.2053 +
  1.2054 +  UnbindFromDocument(aAccessible);
  1.2055 +}
  1.2056 +
  1.2057 +bool
  1.2058 +DocAccessible::IsLoadEventTarget() const
  1.2059 +{
  1.2060 +  nsCOMPtr<nsIDocShellTreeItem> treeItem = mDocumentNode->GetDocShell();
  1.2061 +  NS_ASSERTION(treeItem, "No document shell for document!");
  1.2062 +
  1.2063 +  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
  1.2064 +  treeItem->GetParent(getter_AddRefs(parentTreeItem));
  1.2065 +
  1.2066 +  // Not a root document.
  1.2067 +  if (parentTreeItem) {
  1.2068 +    // Return true if it's either:
  1.2069 +    // a) tab document;
  1.2070 +    nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
  1.2071 +    treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
  1.2072 +    if (parentTreeItem == rootTreeItem)
  1.2073 +      return true;
  1.2074 +
  1.2075 +    // b) frame/iframe document and its parent document is not in loading state
  1.2076 +    // Note: we can get notifications while document is loading (and thus
  1.2077 +    // while there's no parent document yet).
  1.2078 +    DocAccessible* parentDoc = ParentDocument();
  1.2079 +    return parentDoc && parentDoc->HasLoadState(eCompletelyLoaded);
  1.2080 +  }
  1.2081 +
  1.2082 +  // It's content (not chrome) root document.
  1.2083 +  return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
  1.2084 +}
  1.2085 +
  1.2086 +PLDHashOperator
  1.2087 +DocAccessible::CycleCollectorTraverseDepIDsEntry(const nsAString& aKey,
  1.2088 +                                                 AttrRelProviderArray* aProviders,
  1.2089 +                                                 void* aUserArg)
  1.2090 +{
  1.2091 +  nsCycleCollectionTraversalCallback* cb =
  1.2092 +    static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
  1.2093 +
  1.2094 +  for (int32_t jdx = aProviders->Length() - 1; jdx >= 0; jdx--) {
  1.2095 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
  1.2096 +                                       "content of dependent ids hash entry of document accessible");
  1.2097 +
  1.2098 +    AttrRelProvider* provider = (*aProviders)[jdx];
  1.2099 +    cb->NoteXPCOMChild(provider->mContent);
  1.2100 +
  1.2101 +    NS_ASSERTION(provider->mContent->IsInDoc(),
  1.2102 +                 "Referred content is not in document!");
  1.2103 +  }
  1.2104 +
  1.2105 +  return PL_DHASH_NEXT;
  1.2106 +}
  1.2107 +

mercurial