accessible/src/generic/Accessible.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "Accessible-inl.h"
     8 #include "nsIXBLAccessible.h"
    10 #include "AccCollector.h"
    11 #include "AccGroupInfo.h"
    12 #include "AccIterator.h"
    13 #include "nsAccUtils.h"
    14 #include "nsAccessibleRelation.h"
    15 #include "nsAccessibilityService.h"
    16 #include "ApplicationAccessible.h"
    17 #include "nsCoreUtils.h"
    18 #include "nsIAccessibleRelation.h"
    19 #include "nsIAccessibleRole.h"
    20 #include "nsEventShell.h"
    21 #include "nsTextEquivUtils.h"
    22 #include "Relation.h"
    23 #include "Role.h"
    24 #include "RootAccessible.h"
    25 #include "States.h"
    26 #include "StyleInfo.h"
    27 #include "TableAccessible.h"
    28 #include "TableCellAccessible.h"
    29 #include "TreeWalker.h"
    31 #include "nsIDOMElement.h"
    32 #include "nsIDOMNodeFilter.h"
    33 #include "nsIDOMHTMLElement.h"
    34 #include "nsIDOMKeyEvent.h"
    35 #include "nsIDOMTreeWalker.h"
    36 #include "nsIDOMXULButtonElement.h"
    37 #include "nsIDOMXULDocument.h"
    38 #include "nsIDOMXULElement.h"
    39 #include "nsIDOMXULLabelElement.h"
    40 #include "nsIDOMXULSelectCntrlEl.h"
    41 #include "nsIDOMXULSelectCntrlItemEl.h"
    42 #include "nsPIDOMWindow.h"
    44 #include "nsIDocument.h"
    45 #include "nsIContent.h"
    46 #include "nsIForm.h"
    47 #include "nsIFormControl.h"
    49 #include "nsDeckFrame.h"
    50 #include "nsLayoutUtils.h"
    51 #include "nsIPresShell.h"
    52 #include "nsIStringBundle.h"
    53 #include "nsPresContext.h"
    54 #include "nsIFrame.h"
    55 #include "nsView.h"
    56 #include "nsIDocShellTreeItem.h"
    57 #include "nsIScrollableFrame.h"
    58 #include "nsFocusManager.h"
    60 #include "nsXPIDLString.h"
    61 #include "nsUnicharUtils.h"
    62 #include "nsReadableUtils.h"
    63 #include "prdtoa.h"
    64 #include "nsIAtom.h"
    65 #include "nsIURI.h"
    66 #include "nsArrayUtils.h"
    67 #include "nsIMutableArray.h"
    68 #include "nsIObserverService.h"
    69 #include "nsIServiceManager.h"
    70 #include "nsWhitespaceTokenizer.h"
    71 #include "nsAttrName.h"
    72 #include "nsNetUtil.h"
    74 #ifdef DEBUG
    75 #include "nsIDOMCharacterData.h"
    76 #endif
    78 #include "mozilla/Assertions.h"
    79 #include "mozilla/EventStates.h"
    80 #include "mozilla/FloatingPoint.h"
    81 #include "mozilla/MouseEvents.h"
    82 #include "mozilla/unused.h"
    83 #include "mozilla/Preferences.h"
    84 #include "mozilla/dom/Element.h"
    85 #include "mozilla/dom/TreeWalker.h"
    87 using namespace mozilla;
    88 using namespace mozilla::a11y;
    91 ////////////////////////////////////////////////////////////////////////////////
    92 // Accessible: nsISupports and cycle collection
    94 NS_IMPL_CYCLE_COLLECTION(Accessible,
    95                          mContent, mParent, mChildren)
    97 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
    98   NS_INTERFACE_MAP_ENTRY(nsIAccessible)
    99   if (aIID.Equals(NS_GET_IID(Accessible)))
   100     foundInterface = static_cast<nsIAccessible*>(this);
   101   else
   102   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleSelectable, IsSelect())
   103   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleValue, HasNumericValue())
   104   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleHyperLink, IsLink())
   105   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessible)
   106 NS_INTERFACE_MAP_END
   108 NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
   109 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
   111 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
   112   mContent(aContent), mDoc(aDoc),
   113   mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
   114   mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
   115   mIndexOfEmbeddedChild(-1), mRoleMapEntry(nullptr)
   116 {
   117 #ifdef NS_DEBUG_X
   118    {
   119      nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
   120      printf(">>> %p Created Acc - DOM: %p  PS: %p", 
   121             (void*)static_cast<nsIAccessible*>(this), (void*)aNode,
   122             (void*)shell.get());
   123     nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   124     if (content) {
   125       printf(" Con: %s@%p",
   126              NS_ConvertUTF16toUTF8(content->NodeInfo()->QualifiedName()).get(),
   127              (void *)content.get());
   128       nsAutoString buf;
   129       Name(buf);
   130       printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf).get());
   131      }
   132      printf("\n");
   133    }
   134 #endif
   135 }
   137 Accessible::~Accessible()
   138 {
   139   NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
   140 }
   142 NS_IMETHODIMP
   143 Accessible::GetDocument(nsIAccessibleDocument** aDocument)
   144 {
   145   NS_ENSURE_ARG_POINTER(aDocument);
   147   NS_IF_ADDREF(*aDocument = Document());
   148   return NS_OK;
   149 }
   151 NS_IMETHODIMP
   152 Accessible::GetDOMNode(nsIDOMNode** aDOMNode)
   153 {
   154   NS_ENSURE_ARG_POINTER(aDOMNode);
   155   *aDOMNode = nullptr;
   157   nsINode *node = GetNode();
   158   if (node)
   159     CallQueryInterface(node, aDOMNode);
   161   return NS_OK;
   162 }
   164 NS_IMETHODIMP
   165 Accessible::GetRootDocument(nsIAccessibleDocument** aRootDocument)
   166 {
   167   NS_ENSURE_ARG_POINTER(aRootDocument);
   169   NS_IF_ADDREF(*aRootDocument = RootAccessible());
   170   return NS_OK;
   171 }
   173 NS_IMETHODIMP
   174 Accessible::GetLanguage(nsAString& aLanguage)
   175 {
   176   Language(aLanguage);
   177   return NS_OK;
   178 }
   180 NS_IMETHODIMP
   181 Accessible::GetName(nsAString& aName)
   182 {
   183   aName.Truncate();
   185   if (IsDefunct())
   186     return NS_ERROR_FAILURE;
   188   nsAutoString name;
   189   Name(name);
   190   aName.Assign(name);
   192   return NS_OK;
   193 }
   195 ENameValueFlag
   196 Accessible::Name(nsString& aName)
   197 {
   198   aName.Truncate();
   200   if (!HasOwnContent())
   201     return eNameOK;
   203   ARIAName(aName);
   204   if (!aName.IsEmpty())
   205     return eNameOK;
   207   nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
   208   if (xblAccessible) {
   209     xblAccessible->GetAccessibleName(aName);
   210     if (!aName.IsEmpty())
   211       return eNameOK;
   212   }
   214   ENameValueFlag nameFlag = NativeName(aName);
   215   if (!aName.IsEmpty())
   216     return nameFlag;
   218   // In the end get the name from tooltip.
   219   if (mContent->IsHTML()) {
   220     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
   221       aName.CompressWhitespace();
   222       return eNameFromTooltip;
   223     }
   224   } else if (mContent->IsXUL()) {
   225     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
   226       aName.CompressWhitespace();
   227       return eNameFromTooltip;
   228     }
   229   } else if (mContent->IsSVG()) {
   230     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
   231     // for processing, the user agent shall choose the first one.
   232     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
   233          childElm = childElm->GetNextSibling()) {
   234       if (childElm->IsSVG(nsGkAtoms::desc)) {
   235         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
   236         return eNameFromTooltip;
   237       }
   238     }
   239   }
   241   if (nameFlag != eNoNameOnPurpose)
   242     aName.SetIsVoid(true);
   244   return nameFlag;
   245 }
   247 NS_IMETHODIMP
   248 Accessible::GetDescription(nsAString& aDescription)
   249 {
   250   if (IsDefunct())
   251     return NS_ERROR_FAILURE;
   253   nsAutoString desc;
   254   Description(desc);
   255   aDescription.Assign(desc);
   257   return NS_OK;
   258 }
   260 void
   261 Accessible::Description(nsString& aDescription)
   262 {
   263   // There are 4 conditions that make an accessible have no accDescription:
   264   // 1. it's a text node; or
   265   // 2. It has no DHTML describedby property
   266   // 3. it doesn't have an accName; or
   267   // 4. its title attribute already equals to its accName nsAutoString name; 
   269   if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
   270     return;
   272   nsTextEquivUtils::
   273     GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
   274                            aDescription);
   276   if (aDescription.IsEmpty()) {
   277     bool isXUL = mContent->IsXUL();
   278     if (isXUL) {
   279       // Try XUL <description control="[id]">description text</description>
   280       XULDescriptionIterator iter(Document(), mContent);
   281       Accessible* descr = nullptr;
   282       while ((descr = iter.Next())) {
   283         nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
   284                                                      &aDescription);
   285       }
   286     }
   288     if (aDescription.IsEmpty()) {
   289       // Keep the Name() method logic.
   290       if (mContent->IsHTML()) {
   291         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
   292       } else if (mContent->IsXUL()) {
   293         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
   294       } else if (mContent->IsSVG()) {
   295         for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
   296              childElm = childElm->GetNextSibling()) {
   297           if (childElm->IsSVG(nsGkAtoms::desc)) {
   298             nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
   299                                                          &aDescription);
   300             break;
   301           }
   302         }
   303       }
   305       if (!aDescription.IsEmpty()) {
   306         nsAutoString name;
   307         ENameValueFlag nameFlag = Name(name);
   309         // Don't use tooltip for a description if it was used for a name.
   310         if (nameFlag == eNameFromTooltip)
   311           aDescription.Truncate();
   312       }
   313     }
   314   }
   315   aDescription.CompressWhitespace();
   316 }
   318 NS_IMETHODIMP
   319 Accessible::GetAccessKey(nsAString& aAccessKey)
   320 {
   321   aAccessKey.Truncate();
   323   if (IsDefunct())
   324     return NS_ERROR_FAILURE;
   326   AccessKey().ToString(aAccessKey);
   327   return NS_OK;
   328 }
   330 KeyBinding
   331 Accessible::AccessKey() const
   332 {
   333   if (!HasOwnContent())
   334     return KeyBinding();
   336   uint32_t key = nsCoreUtils::GetAccessKeyFor(mContent);
   337   if (!key && mContent->IsElement()) {
   338     Accessible* label = nullptr;
   340     // Copy access key from label node.
   341     if (mContent->IsHTML()) {
   342       // Unless it is labeled via an ancestor <label>, in which case that would
   343       // be redundant.
   344       HTMLLabelIterator iter(Document(), this,
   345                              HTMLLabelIterator::eSkipAncestorLabel);
   346       label = iter.Next();
   348     } else if (mContent->IsXUL()) {
   349       XULLabelIterator iter(Document(), mContent);
   350       label = iter.Next();
   351     }
   353     if (label)
   354       key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
   355   }
   357   if (!key)
   358     return KeyBinding();
   360   // Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
   361   switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
   362   case -1:
   363     break;
   364   case nsIDOMKeyEvent::DOM_VK_SHIFT:
   365     return KeyBinding(key, KeyBinding::kShift);
   366   case nsIDOMKeyEvent::DOM_VK_CONTROL:
   367     return KeyBinding(key, KeyBinding::kControl);
   368   case nsIDOMKeyEvent::DOM_VK_ALT:
   369     return KeyBinding(key, KeyBinding::kAlt);
   370   case nsIDOMKeyEvent::DOM_VK_META:
   371     return KeyBinding(key, KeyBinding::kMeta);
   372   default:
   373     return KeyBinding();
   374   }
   376   // Determine the access modifier used in this context.
   377   nsIDocument* document = mContent->GetCurrentDoc();
   378   if (!document)
   379     return KeyBinding();
   381   nsCOMPtr<nsIDocShellTreeItem> treeItem(document->GetDocShell());
   382   if (!treeItem)
   383     return KeyBinding();
   385   nsresult rv = NS_ERROR_FAILURE;
   386   int32_t modifierMask = 0;
   387   switch (treeItem->ItemType()) {
   388     case nsIDocShellTreeItem::typeChrome:
   389       rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
   390       break;
   391     case nsIDocShellTreeItem::typeContent:
   392       rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
   393       break;
   394   }
   396   return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
   397 }
   399 KeyBinding
   400 Accessible::KeyboardShortcut() const
   401 {
   402   return KeyBinding();
   403 }
   405 NS_IMETHODIMP
   406 Accessible::GetParent(nsIAccessible** aParent)
   407 {
   408   NS_ENSURE_ARG_POINTER(aParent);
   409   if (IsDefunct())
   410     return NS_ERROR_FAILURE;
   412   NS_IF_ADDREF(*aParent = Parent());
   413   return *aParent ? NS_OK : NS_ERROR_FAILURE;
   414 }
   416   /* readonly attribute nsIAccessible nextSibling; */
   417 NS_IMETHODIMP
   418 Accessible::GetNextSibling(nsIAccessible** aNextSibling)
   419 {
   420   NS_ENSURE_ARG_POINTER(aNextSibling);
   421   *aNextSibling = nullptr;
   423   if (IsDefunct())
   424     return NS_ERROR_FAILURE;
   426   nsresult rv = NS_OK;
   427   NS_IF_ADDREF(*aNextSibling = GetSiblingAtOffset(1, &rv));
   428   return rv;
   429 }
   431   /* readonly attribute nsIAccessible previousSibling; */
   432 NS_IMETHODIMP
   433 Accessible::GetPreviousSibling(nsIAccessible ** aPreviousSibling)
   434 {
   435   NS_ENSURE_ARG_POINTER(aPreviousSibling);
   436   *aPreviousSibling = nullptr;
   438   if (IsDefunct())
   439     return NS_ERROR_FAILURE;
   441   nsresult rv = NS_OK;
   442   NS_IF_ADDREF(*aPreviousSibling = GetSiblingAtOffset(-1, &rv));
   443   return rv;
   444 }
   446   /* readonly attribute nsIAccessible firstChild; */
   447 NS_IMETHODIMP
   448 Accessible::GetFirstChild(nsIAccessible** aFirstChild)
   449 {
   450   NS_ENSURE_ARG_POINTER(aFirstChild);
   451   *aFirstChild = nullptr;
   453   if (IsDefunct())
   454     return NS_ERROR_FAILURE;
   456   NS_IF_ADDREF(*aFirstChild = FirstChild());
   457   return NS_OK;
   458 }
   460   /* readonly attribute nsIAccessible lastChild; */
   461 NS_IMETHODIMP
   462 Accessible::GetLastChild(nsIAccessible** aLastChild)
   463 {
   464   NS_ENSURE_ARG_POINTER(aLastChild);
   465   *aLastChild = nullptr;
   467   if (IsDefunct())
   468     return NS_ERROR_FAILURE;
   470   NS_IF_ADDREF(*aLastChild = LastChild());
   471   return NS_OK;
   472 }
   474 NS_IMETHODIMP
   475 Accessible::GetChildAt(int32_t aChildIndex, nsIAccessible** aChild)
   476 {
   477   NS_ENSURE_ARG_POINTER(aChild);
   478   *aChild = nullptr;
   480   if (IsDefunct())
   481     return NS_ERROR_FAILURE;
   483   // If child index is negative, then return last child.
   484   // XXX: do we really need this?
   485   if (aChildIndex < 0)
   486     aChildIndex = ChildCount() - 1;
   488   Accessible* child = GetChildAt(aChildIndex);
   489   if (!child)
   490     return NS_ERROR_INVALID_ARG;
   492   NS_ADDREF(*aChild = child);
   493   return NS_OK;
   494 }
   496 // readonly attribute nsIArray children;
   497 NS_IMETHODIMP
   498 Accessible::GetChildren(nsIArray** aOutChildren)
   499 {
   500   NS_ENSURE_ARG_POINTER(aOutChildren);
   501   *aOutChildren = nullptr;
   503   if (IsDefunct())
   504     return NS_ERROR_FAILURE;
   506   nsresult rv = NS_OK;
   507   nsCOMPtr<nsIMutableArray> children =
   508     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
   509   NS_ENSURE_SUCCESS(rv, rv);
   511   uint32_t childCount = ChildCount();
   512   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
   513     nsIAccessible* child = GetChildAt(childIdx);
   514     children->AppendElement(child, false);
   515   }
   517   NS_ADDREF(*aOutChildren = children);
   518   return NS_OK;
   519 }
   521 bool
   522 Accessible::CanHaveAnonChildren()
   523 {
   524   return true;
   525 }
   527 /* readonly attribute long childCount; */
   528 NS_IMETHODIMP
   529 Accessible::GetChildCount(int32_t* aChildCount)
   530 {
   531   NS_ENSURE_ARG_POINTER(aChildCount);
   533   if (IsDefunct())
   534     return NS_ERROR_FAILURE;
   536   *aChildCount = ChildCount();
   537   return NS_OK;
   538 }
   540 /* readonly attribute long indexInParent; */
   541 NS_IMETHODIMP
   542 Accessible::GetIndexInParent(int32_t* aIndexInParent)
   543 {
   544   NS_ENSURE_ARG_POINTER(aIndexInParent);
   546   *aIndexInParent = IndexInParent();
   547   return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
   548 }
   550 void 
   551 Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
   552 {
   553   nsCOMPtr<nsIStringBundleService> stringBundleService =
   554     services::GetStringBundleService();
   555   if (!stringBundleService)
   556     return;
   558   nsCOMPtr<nsIStringBundle> stringBundle;
   559   stringBundleService->CreateBundle(
   560     "chrome://global-platform/locale/accessible.properties", 
   561     getter_AddRefs(stringBundle));
   562   if (!stringBundle)
   563     return;
   565   nsXPIDLString xsValue;
   566   nsresult rv = stringBundle->GetStringFromName(aKey.get(), getter_Copies(xsValue));
   567   if (NS_SUCCEEDED(rv))
   568     aStringOut.Assign(xsValue);
   569 }
   571 uint64_t
   572 Accessible::VisibilityState()
   573 {
   574   nsIFrame* frame = GetFrame();
   575   if (!frame)
   576     return states::INVISIBLE;
   578   // Walk the parent frame chain to see if there's invisible parent or the frame
   579   // is in background tab.
   580   if (!frame->StyleVisibility()->IsVisible())
   581     return states::INVISIBLE;
   583   nsIFrame* curFrame = frame;
   584   do {
   585     nsView* view = curFrame->GetView();
   586     if (view && view->GetVisibility() == nsViewVisibility_kHide)
   587       return states::INVISIBLE;
   589     if (nsLayoutUtils::IsPopup(curFrame))
   590       return 0;
   592     // Offscreen state for background tab content and invisible for not selected
   593     // deck panel.
   594     nsIFrame* parentFrame = curFrame->GetParent();
   595     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
   596     if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
   597       if (deckFrame->GetContent()->IsXUL() &&
   598           deckFrame->GetContent()->Tag() == nsGkAtoms::tabpanels)
   599         return states::OFFSCREEN;
   601       NS_NOTREACHED("Children of not selected deck panel are not accessible.");
   602       return states::INVISIBLE;
   603     }
   605     // If contained by scrollable frame then check that at least 12 pixels
   606     // around the object is visible, otherwise the object is offscreen.
   607     nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
   608     if (scrollableFrame) {
   609       nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
   610       nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
   611         frame, frame->GetRectRelativeToSelf(), parentFrame);
   612       if (!scrollPortRect.Contains(frameRect)) {
   613         const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
   614         scrollPortRect.Deflate(kMinPixels, kMinPixels);
   615         if (!scrollPortRect.Intersects(frameRect))
   616           return states::OFFSCREEN;
   617       }
   618     }
   620     if (!parentFrame) {
   621       parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
   622       if (parentFrame && !parentFrame->StyleVisibility()->IsVisible())
   623         return states::INVISIBLE;
   624     }
   626     curFrame = parentFrame;
   627   } while (curFrame);
   629   // Zero area rects can occur in the first frame of a multi-frame text flow,
   630   // in which case the rendered text is not empty and the frame should not be
   631   // marked invisible.
   632   // XXX Can we just remove this check? Why do we need to mark empty
   633   // text invisible?
   634   if (frame->GetType() == nsGkAtoms::textFrame &&
   635       !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
   636       frame->GetRect().IsEmpty()) {
   637     nsAutoString renderedText;
   638     frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1);
   639     if (renderedText.IsEmpty())
   640       return states::INVISIBLE;
   641   }
   643   return 0;
   644 }
   646 uint64_t
   647 Accessible::NativeState()
   648 {
   649   uint64_t state = 0;
   651   if (!IsInDocument())
   652     state |= states::STALE;
   654   if (HasOwnContent() && mContent->IsElement()) {
   655     EventStates elementState = mContent->AsElement()->State();
   657     if (elementState.HasState(NS_EVENT_STATE_INVALID))
   658       state |= states::INVALID;
   660     if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
   661       state |= states::REQUIRED;
   663     state |= NativeInteractiveState();
   664     if (FocusMgr()->IsFocused(this))
   665       state |= states::FOCUSED;
   666   }
   668   // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
   669   state |= VisibilityState();
   671   nsIFrame *frame = GetFrame();
   672   if (frame) {
   673     if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
   674       state |= states::FLOATING;
   676     // XXX we should look at layout for non XUL box frames, but need to decide
   677     // how that interacts with ARIA.
   678     if (HasOwnContent() && mContent->IsXUL() && frame->IsBoxFrame()) {
   679       const nsStyleXUL* xulStyle = frame->StyleXUL();
   680       if (xulStyle && frame->IsBoxFrame()) {
   681         // In XUL all boxes are either vertical or horizontal
   682         if (xulStyle->mBoxOrient == NS_STYLE_BOX_ORIENT_VERTICAL)
   683           state |= states::VERTICAL;
   684         else
   685           state |= states::HORIZONTAL;
   686       }
   687     }
   688   }
   690   // Check if a XUL element has the popup attribute (an attached popup menu).
   691   if (HasOwnContent() && mContent->IsXUL() &&
   692       mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
   693     state |= states::HASPOPUP;
   695   // Bypass the link states specialization for non links.
   696   if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
   697       mRoleMapEntry->role == roles::LINK)
   698     state |= NativeLinkState();
   700   return state;
   701 }
   703 uint64_t
   704 Accessible::NativeInteractiveState() const
   705 {
   706   if (!mContent->IsElement())
   707     return 0;
   709   if (NativelyUnavailable())
   710     return states::UNAVAILABLE;
   712   nsIFrame* frame = GetFrame();
   713   if (frame && frame->IsFocusable())
   714     return states::FOCUSABLE;
   716   return 0;
   717 }
   719 uint64_t
   720 Accessible::NativeLinkState() const
   721 {
   722   return 0;
   723 }
   725 bool
   726 Accessible::NativelyUnavailable() const
   727 {
   728   if (mContent->IsHTML())
   729     return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
   731   return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
   732                                nsGkAtoms::_true, eCaseMatters);
   733 }
   735   /* readonly attribute boolean focusedChild; */
   736 NS_IMETHODIMP
   737 Accessible::GetFocusedChild(nsIAccessible** aChild)
   738 {
   739   NS_ENSURE_ARG_POINTER(aChild);
   740   *aChild = nullptr;
   742   if (IsDefunct())
   743     return NS_ERROR_FAILURE;
   745   NS_IF_ADDREF(*aChild = FocusedChild());
   746   return NS_OK;
   747 }
   749 Accessible*
   750 Accessible::FocusedChild()
   751 {
   752   Accessible* focus = FocusMgr()->FocusedAccessible();
   753   if (focus && (focus == this || focus->Parent() == this))
   754     return focus;
   756   return nullptr;
   757 }
   759 Accessible*
   760 Accessible::ChildAtPoint(int32_t aX, int32_t aY,
   761                          EWhichChildAtPoint aWhichChild)
   762 {
   763   // If we can't find the point in a child, we will return the fallback answer:
   764   // we return |this| if the point is within it, otherwise nullptr.
   765   int32_t x = 0, y = 0, width = 0, height = 0;
   766   nsresult rv = GetBounds(&x, &y, &width, &height);
   767   NS_ENSURE_SUCCESS(rv, nullptr);
   769   Accessible* fallbackAnswer = nullptr;
   770   if (aX >= x && aX < x + width && aY >= y && aY < y + height)
   771     fallbackAnswer = this;
   773   if (nsAccUtils::MustPrune(this))  // Do not dig any further
   774     return fallbackAnswer;
   776   // Search an accessible at the given point starting from accessible document
   777   // because containing block (see CSS2) for out of flow element (for example,
   778   // absolutely positioned element) may be different from its DOM parent and
   779   // therefore accessible for containing block may be different from accessible
   780   // for DOM parent but GetFrameForPoint() should be called for containing block
   781   // to get an out of flow element.
   782   DocAccessible* accDocument = Document();
   783   NS_ENSURE_TRUE(accDocument, nullptr);
   785   nsIFrame* rootFrame = accDocument->GetFrame();
   786   NS_ENSURE_TRUE(rootFrame, nullptr);
   788   nsIFrame* startFrame = rootFrame;
   790   // Check whether the point is at popup content.
   791   nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
   792   NS_ENSURE_TRUE(rootWidget, nullptr);
   794   nsIntRect rootRect;
   795   rootWidget->GetScreenBounds(rootRect);
   797   WidgetMouseEvent dummyEvent(true, NS_MOUSE_MOVE, rootWidget,
   798                               WidgetMouseEvent::eSynthesized);
   799   dummyEvent.refPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
   801   nsIFrame* popupFrame = nsLayoutUtils::
   802     GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
   803                                      &dummyEvent);
   804   if (popupFrame) {
   805     // If 'this' accessible is not inside the popup then ignore the popup when
   806     // searching an accessible at point.
   807     DocAccessible* popupDoc =
   808       GetAccService()->GetDocAccessible(popupFrame->GetContent()->OwnerDoc());
   809     Accessible* popupAcc =
   810       popupDoc->GetAccessibleOrContainer(popupFrame->GetContent());
   811     Accessible* popupChild = this;
   812     while (popupChild && !popupChild->IsDoc() && popupChild != popupAcc)
   813       popupChild = popupChild->Parent();
   815     if (popupChild == popupAcc)
   816       startFrame = popupFrame;
   817   }
   819   nsPresContext* presContext = startFrame->PresContext();
   820   nsRect screenRect = startFrame->GetScreenRectInAppUnits();
   821     nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
   822                    presContext->DevPixelsToAppUnits(aY) - screenRect.y);
   823   nsIFrame* foundFrame = nsLayoutUtils::GetFrameForPoint(startFrame, offset);
   825   nsIContent* content = nullptr;
   826   if (!foundFrame || !(content = foundFrame->GetContent()))
   827     return fallbackAnswer;
   829   // Get accessible for the node with the point or the first accessible in
   830   // the DOM parent chain.
   831   DocAccessible* contentDocAcc = GetAccService()->
   832     GetDocAccessible(content->OwnerDoc());
   834   // contentDocAcc in some circumstances can be nullptr. See bug 729861
   835   NS_ASSERTION(contentDocAcc, "could not get the document accessible");
   836   if (!contentDocAcc)
   837     return fallbackAnswer;
   839   Accessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
   840   if (!accessible)
   841     return fallbackAnswer;
   843   // Hurray! We have an accessible for the frame that layout gave us.
   844   // Since DOM node of obtained accessible may be out of flow then we should
   845   // ensure obtained accessible is a child of this accessible.
   846   Accessible* child = accessible;
   847   while (child != this) {
   848     Accessible* parent = child->Parent();
   849     if (!parent) {
   850       // Reached the top of the hierarchy. These bounds were inside an
   851       // accessible that is not a descendant of this one.
   852       return fallbackAnswer;
   853     }
   855     // If we landed on a legitimate child of |this|, and we want the direct
   856     // child, return it here.
   857     if (parent == this && aWhichChild == eDirectChild)
   858         return child;
   860     child = parent;
   861   }
   863   // Manually walk through accessible children and see if the are within this
   864   // point. Skip offscreen or invisible accessibles. This takes care of cases
   865   // where layout won't walk into things for us, such as image map areas and
   866   // sub documents (XXX: subdocuments should be handled by methods of
   867   // OuterDocAccessibles).
   868   uint32_t childCount = accessible->ChildCount();
   869   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
   870     Accessible* child = accessible->GetChildAt(childIdx);
   872     int32_t childX, childY, childWidth, childHeight;
   873     child->GetBounds(&childX, &childY, &childWidth, &childHeight);
   874     if (aX >= childX && aX < childX + childWidth &&
   875         aY >= childY && aY < childY + childHeight &&
   876         (child->State() & states::INVISIBLE) == 0) {
   878       if (aWhichChild == eDeepestChild)
   879         return child->ChildAtPoint(aX, aY, eDeepestChild);
   881       return child;
   882     }
   883   }
   885   return accessible;
   886 }
   888 // nsIAccessible getChildAtPoint(in long x, in long y)
   889 NS_IMETHODIMP
   890 Accessible::GetChildAtPoint(int32_t aX, int32_t aY,
   891                             nsIAccessible** aAccessible)
   892 {
   893   NS_ENSURE_ARG_POINTER(aAccessible);
   894   *aAccessible = nullptr;
   896   if (IsDefunct())
   897     return NS_ERROR_FAILURE;
   899   NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDirectChild));
   900   return NS_OK;
   901 }
   903 // nsIAccessible getDeepestChildAtPoint(in long x, in long y)
   904 NS_IMETHODIMP
   905 Accessible::GetDeepestChildAtPoint(int32_t aX, int32_t aY,
   906                                    nsIAccessible** aAccessible)
   907 {
   908   NS_ENSURE_ARG_POINTER(aAccessible);
   909   *aAccessible = nullptr;
   911   if (IsDefunct())
   912     return NS_ERROR_FAILURE;
   914   NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDeepestChild));
   915   return NS_OK;
   916 }
   918 void
   919 Accessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
   920 {
   921   nsIFrame* frame = GetFrame();
   922   if (frame && mContent) {
   923     nsRect* hitRegionRect = static_cast<nsRect*>(mContent->GetProperty(nsGkAtoms::hitregion));
   925     if (hitRegionRect) {
   926       // This is for canvas fallback content
   927       // Find a canvas frame the found hit region is relative to.
   928       nsIFrame* canvasFrame = frame->GetParent();
   929       while (canvasFrame && (canvasFrame->GetType() != nsGkAtoms::HTMLCanvasFrame))
   930         canvasFrame = canvasFrame->GetParent();
   932       // make the canvas the bounding frame
   933       if (canvasFrame) {
   934         *aBoundingFrame = canvasFrame;
   936         aTotalBounds = *hitRegionRect;
   938         return;
   939       }
   940     }
   942     *aBoundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
   943     aTotalBounds = nsLayoutUtils::
   944       GetAllInFlowRectsUnion(frame, *aBoundingFrame,
   945                              nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   946   }
   947 }
   949 /* void getBounds (out long x, out long y, out long width, out long height); */
   950 NS_IMETHODIMP
   951 Accessible::GetBounds(int32_t* aX, int32_t* aY,
   952                       int32_t* aWidth, int32_t* aHeight)
   953 {
   954   NS_ENSURE_ARG_POINTER(aX);
   955   *aX = 0;
   956   NS_ENSURE_ARG_POINTER(aY);
   957   *aY = 0;
   958   NS_ENSURE_ARG_POINTER(aWidth);
   959   *aWidth = 0;
   960   NS_ENSURE_ARG_POINTER(aHeight);
   961   *aHeight = 0;
   963   if (IsDefunct())
   964     return NS_ERROR_FAILURE;
   966   // This routine will get the entire rectangle for all the frames in this node.
   967   // -------------------------------------------------------------------------
   968   //      Primary Frame for node
   969   //  Another frame, same node                <- Example
   970   //  Another frame, same node
   972   nsRect unionRectTwips;
   973   nsIFrame* boundingFrame = nullptr;
   974   GetBoundsRect(unionRectTwips, &boundingFrame);   // Unions up all primary frames for this node and all siblings after it
   975   NS_ENSURE_STATE(boundingFrame);
   977   nsPresContext* presContext = mDoc->PresContext();
   978   *aX = presContext->AppUnitsToDevPixels(unionRectTwips.x);
   979   *aY = presContext->AppUnitsToDevPixels(unionRectTwips.y);
   980   *aWidth = presContext->AppUnitsToDevPixels(unionRectTwips.width);
   981   *aHeight = presContext->AppUnitsToDevPixels(unionRectTwips.height);
   983   // We have the union of the rectangle, now we need to put it in absolute screen coords
   984   nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
   985     ToNearestPixels(presContext->AppUnitsPerDevPixel());
   986   *aX += orgRectPixels.x;
   987   *aY += orgRectPixels.y;
   989   return NS_OK;
   990 }
   992 NS_IMETHODIMP
   993 Accessible::SetSelected(bool aSelect)
   994 {
   995   if (IsDefunct())
   996     return NS_ERROR_FAILURE;
   998   if (!HasOwnContent())
   999     return NS_OK;
  1001   Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
  1002   if (select) {
  1003     if (select->State() & states::MULTISELECTABLE) {
  1004       if (mRoleMapEntry) {
  1005         if (aSelect) {
  1006           return mContent->SetAttr(kNameSpaceID_None,
  1007                                    nsGkAtoms::aria_selected,
  1008                                    NS_LITERAL_STRING("true"), true);
  1010         return mContent->UnsetAttr(kNameSpaceID_None,
  1011                                    nsGkAtoms::aria_selected, true);
  1014       return NS_OK;
  1017     return aSelect ? TakeFocus() : NS_ERROR_FAILURE;
  1020   return NS_OK;
  1023 NS_IMETHODIMP
  1024 Accessible::TakeSelection()
  1026   if (IsDefunct())
  1027     return NS_ERROR_FAILURE;
  1029   Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
  1030   if (select) {
  1031     if (select->State() & states::MULTISELECTABLE)
  1032       select->UnselectAll();
  1033     return SetSelected(true);
  1036   return NS_ERROR_FAILURE;
  1039 NS_IMETHODIMP
  1040 Accessible::TakeFocus()
  1042   if (IsDefunct())
  1043     return NS_ERROR_FAILURE;
  1045   nsIFrame *frame = GetFrame();
  1046   NS_ENSURE_STATE(frame);
  1048   nsIContent* focusContent = mContent;
  1050   // If the accessible focus is managed by container widget then focus the
  1051   // widget and set the accessible as its current item.
  1052   if (!frame->IsFocusable()) {
  1053     Accessible* widget = ContainerWidget();
  1054     if (widget && widget->AreItemsOperable()) {
  1055       nsIContent* widgetElm = widget->GetContent();
  1056       nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
  1057       if (widgetFrame && widgetFrame->IsFocusable()) {
  1058         focusContent = widgetElm;
  1059         widget->SetCurrentItem(this);
  1064   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
  1065   nsFocusManager* fm = nsFocusManager::GetFocusManager();
  1066   if (fm)
  1067     fm->SetFocus(element, 0);
  1069   return NS_OK;
  1072 void
  1073 Accessible::XULElmName(DocAccessible* aDocument,
  1074                        nsIContent* aElm, nsString& aName)
  1076   /**
  1077    * 3 main cases for XUL Controls to be labeled
  1078    *   1 - control contains label="foo"
  1079    *   2 - control has, as a child, a label element
  1080    *        - label has either value="foo" or children
  1081    *   3 - non-child label contains control="controlID"
  1082    *        - label has either value="foo" or children
  1083    * Once a label is found, the search is discontinued, so a control
  1084    *  that has a label child as well as having a label external to
  1085    *  the control that uses the control="controlID" syntax will use
  1086    *  the child label for its Name.
  1087    */
  1089   // CASE #1 (via label attribute) -- great majority of the cases
  1090   nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
  1091   if (labeledEl) {
  1092     labeledEl->GetLabel(aName);
  1093   } else {
  1094     nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
  1095     if (itemEl) {
  1096       itemEl->GetLabel(aName);
  1097     } else {
  1098       nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
  1099       // Use label if this is not a select control element which 
  1100       // uses label attribute to indicate which option is selected
  1101       if (!select) {
  1102         nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
  1103         if (xulEl)
  1104           xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
  1109   // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
  1110   if (aName.IsEmpty()) {
  1111     Accessible* labelAcc = nullptr;
  1112     XULLabelIterator iter(aDocument, aElm);
  1113     while ((labelAcc = iter.Next())) {
  1114       nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
  1115         do_QueryInterface(labelAcc->GetContent());
  1116       // Check if label's value attribute is used
  1117       if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(aName)) && aName.IsEmpty()) {
  1118         // If no value attribute, a non-empty label must contain
  1119         // children that define its text -- possibly using HTML
  1120         nsTextEquivUtils::
  1121           AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
  1126   aName.CompressWhitespace();
  1127   if (!aName.IsEmpty())
  1128     return;
  1130   // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
  1131   nsIContent *bindingParent = aElm->GetBindingParent();
  1132   nsIContent* parent =
  1133     bindingParent? bindingParent->GetParent() : aElm->GetParent();
  1134   while (parent) {
  1135     if (parent->Tag() == nsGkAtoms::toolbaritem &&
  1136         parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
  1137       aName.CompressWhitespace();
  1138       return;
  1140     parent = parent->GetParent();
  1144 nsresult
  1145 Accessible::HandleAccEvent(AccEvent* aEvent)
  1147   NS_ENSURE_ARG_POINTER(aEvent);
  1149   nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
  1150   NS_ENSURE_TRUE(obsService, NS_ERROR_FAILURE);
  1152   nsCOMPtr<nsISimpleEnumerator> observers;
  1153   obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC,
  1154                                  getter_AddRefs(observers));
  1156   NS_ENSURE_STATE(observers);
  1158   bool hasObservers = false;
  1159   observers->HasMoreElements(&hasObservers);
  1160   if (hasObservers) {
  1161     nsCOMPtr<nsIAccessibleEvent> event = MakeXPCEvent(aEvent);
  1162     return obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
  1165   return NS_OK;
  1168 NS_IMETHODIMP
  1169 Accessible::GetRole(uint32_t *aRole)
  1171   NS_ENSURE_ARG_POINTER(aRole);
  1172   *aRole = nsIAccessibleRole::ROLE_NOTHING;
  1174   if (IsDefunct())
  1175     return NS_ERROR_FAILURE;
  1177   *aRole = Role();
  1178   return NS_OK;
  1181 NS_IMETHODIMP
  1182 Accessible::GetAttributes(nsIPersistentProperties** aAttributes)
  1184   NS_ENSURE_ARG_POINTER(aAttributes);
  1185   *aAttributes = nullptr;
  1187   if (IsDefunct())
  1188     return NS_ERROR_FAILURE;
  1190   nsCOMPtr<nsIPersistentProperties> attributes = Attributes();
  1191   attributes.swap(*aAttributes);
  1193   return NS_OK;
  1196 already_AddRefed<nsIPersistentProperties>
  1197 Accessible::Attributes()
  1199   nsCOMPtr<nsIPersistentProperties> attributes = NativeAttributes();
  1200   if (!HasOwnContent() || !mContent->IsElement())
  1201     return attributes.forget();
  1203   // 'xml-roles' attribute coming from ARIA.
  1204   nsAutoString xmlRoles, unused;
  1205   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles)) {
  1206     attributes->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"),
  1207                                   xmlRoles, unused);
  1210   // Expose object attributes from ARIA attributes.
  1211   aria::AttrIterator attribIter(mContent);
  1212   nsAutoString name, value;
  1213   while(attribIter.Next(name, value))
  1214     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
  1216   // If there is no aria-live attribute then expose default value of 'live'
  1217   // object attribute used for ARIA role of this accessible.
  1218   if (mRoleMapEntry) {
  1219     nsAutoString live;
  1220     nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
  1221     if (live.IsEmpty()) {
  1222       if (nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live))
  1223         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
  1227   return attributes.forget();
  1230 already_AddRefed<nsIPersistentProperties>
  1231 Accessible::NativeAttributes()
  1233   nsCOMPtr<nsIPersistentProperties> attributes =
  1234     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
  1236   nsAutoString unused;
  1238   // We support values, so expose the string value as well, via the valuetext
  1239   // object attribute. We test for the value interface because we don't want
  1240   // to expose traditional Value() information such as URL's on links and
  1241   // documents, or text in an input.
  1242   if (HasNumericValue()) {
  1243     nsAutoString valuetext;
  1244     GetValue(valuetext);
  1245     attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext,
  1246                                   unused);
  1249   // Expose checkable object attribute if the accessible has checkable state
  1250   if (State() & states::CHECKABLE) {
  1251     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable,
  1252                            NS_LITERAL_STRING("true"));
  1255   // Expose 'explicit-name' attribute.
  1256   nsAutoString name;
  1257   if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
  1258     attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
  1259                                   NS_LITERAL_STRING("true"), unused);
  1262   // Group attributes (level/setsize/posinset)
  1263   GroupPos groupPos = GroupPosition();
  1264   nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
  1265                                groupPos.setSize, groupPos.posInSet);
  1267   // If the accessible doesn't have own content (such as list item bullet or
  1268   // xul tree item) then don't calculate content based attributes.
  1269   if (!HasOwnContent())
  1270     return attributes.forget();
  1272   nsEventShell::GetEventAttributes(GetNode(), attributes);
  1274   // Get container-foo computed live region properties based on the closest
  1275   // container with the live region attribute. Inner nodes override outer nodes
  1276   // within the same document. The inner nodes can be used to override live
  1277   // region behavior on more general outer nodes. However, nodes in outer
  1278   // documents override nodes in inner documents: outer doc author may want to
  1279   // override properties on a widget they used in an iframe.
  1280   nsIContent* startContent = mContent;
  1281   while (startContent) {
  1282     nsIDocument* doc = startContent->GetDocument();
  1283     if (!doc)
  1284       break;
  1286     nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
  1287                                            nsCoreUtils::GetRoleContent(doc));
  1289     // Allow ARIA live region markup from outer documents to override
  1290     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
  1291     if (!docShellTreeItem)
  1292       break;
  1294     nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
  1295     docShellTreeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
  1296     if (!sameTypeParent || sameTypeParent == docShellTreeItem)
  1297       break;
  1299     nsIDocument* parentDoc = doc->GetParentDocument();
  1300     if (!parentDoc)
  1301       break;
  1303     startContent = parentDoc->FindContentForSubDocument(doc);
  1306   if (!mContent->IsElement())
  1307     return attributes.forget();
  1309   nsAutoString id;
  1310   if (nsCoreUtils::GetID(mContent, id))
  1311     attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, unused);
  1313   // Expose class because it may have useful microformat information.
  1314   nsAutoString _class;
  1315   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
  1316     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::_class, _class);
  1318   // Expose tag.
  1319   nsAutoString tagName;
  1320   mContent->NodeInfo()->GetName(tagName);
  1321   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tag, tagName);
  1323   // Expose draggable object attribute.
  1324   nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
  1325   if (htmlElement) {
  1326     bool draggable = false;
  1327     htmlElement->GetDraggable(&draggable);
  1328     if (draggable) {
  1329       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::draggable,
  1330                              NS_LITERAL_STRING("true"));
  1334   // Don't calculate CSS-based object attributes when no frame (i.e.
  1335   // the accessible is unattached from the tree).
  1336   if (!mContent->GetPrimaryFrame())
  1337     return attributes.forget();
  1339   // CSS style based object attributes.
  1340   nsAutoString value;
  1341   StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell());
  1343   // Expose 'display' attribute.
  1344   styleInfo.Display(value);
  1345   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::display, value);
  1347   // Expose 'text-align' attribute.
  1348   styleInfo.TextAlign(value);
  1349   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textAlign, value);
  1351   // Expose 'text-indent' attribute.
  1352   styleInfo.TextIndent(value);
  1353   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textIndent, value);
  1355   // Expose 'margin-left' attribute.
  1356   styleInfo.MarginLeft(value);
  1357   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginLeft, value);
  1359   // Expose 'margin-right' attribute.
  1360   styleInfo.MarginRight(value);
  1361   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginRight, value);
  1363   // Expose 'margin-top' attribute.
  1364   styleInfo.MarginTop(value);
  1365   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginTop, value);
  1367   // Expose 'margin-bottom' attribute.
  1368   styleInfo.MarginBottom(value);
  1369   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginBottom, value);
  1371   return attributes.forget();
  1374 GroupPos
  1375 Accessible::GroupPosition()
  1377   GroupPos groupPos;
  1378   if (!HasOwnContent())
  1379     return groupPos;
  1381   // Get group position from ARIA attributes.
  1382   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_level, &groupPos.level);
  1383   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_setsize, &groupPos.setSize);
  1384   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_posinset, &groupPos.posInSet);
  1386   // If ARIA is missed and the accessible is visible then calculate group
  1387   // position from hierarchy.
  1388   if (State() & states::INVISIBLE)
  1389     return groupPos;
  1391   // Calculate group level if ARIA is missed.
  1392   if (groupPos.level == 0) {
  1393     int32_t level = GetLevelInternal();
  1394     if (level != 0)
  1395       groupPos.level = level;
  1398   // Calculate position in group and group size if ARIA is missed.
  1399   if (groupPos.posInSet == 0 || groupPos.setSize == 0) {
  1400     int32_t posInSet = 0, setSize = 0;
  1401     GetPositionAndSizeInternal(&posInSet, &setSize);
  1402     if (posInSet != 0 && setSize != 0) {
  1403       if (groupPos.posInSet == 0)
  1404         groupPos.posInSet = posInSet;
  1406       if (groupPos.setSize == 0)
  1407         groupPos.setSize = setSize;
  1411   return groupPos;
  1414 NS_IMETHODIMP
  1415 Accessible::ScriptableGroupPosition(int32_t* aGroupLevel,
  1416                                     int32_t* aSimilarItemsInGroup,
  1417                                     int32_t* aPositionInGroup)
  1419   NS_ENSURE_ARG_POINTER(aGroupLevel);
  1420   *aGroupLevel = 0;
  1422   NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
  1423   *aSimilarItemsInGroup = 0;
  1425   NS_ENSURE_ARG_POINTER(aPositionInGroup);
  1426   *aPositionInGroup = 0;
  1428   if (IsDefunct())
  1429     return NS_ERROR_FAILURE;
  1431   GroupPos groupPos = GroupPosition();
  1433   *aGroupLevel = groupPos.level;
  1434   *aSimilarItemsInGroup = groupPos.setSize;
  1435   *aPositionInGroup = groupPos.posInSet;
  1437   return NS_OK;
  1440 NS_IMETHODIMP
  1441 Accessible::GetState(uint32_t* aState, uint32_t* aExtraState)
  1443   NS_ENSURE_ARG_POINTER(aState);
  1445   nsAccUtils::To32States(State(), aState, aExtraState);
  1446   return NS_OK;
  1449 uint64_t
  1450 Accessible::State()
  1452   if (IsDefunct())
  1453     return states::DEFUNCT;
  1455   uint64_t state = NativeState();
  1456   // Apply ARIA states to be sure accessible states will be overridden.
  1457   ApplyARIAState(&state);
  1459   // If this is an ARIA item of the selectable widget and if it's focused and
  1460   // not marked unselected explicitly (i.e. aria-selected="false") then expose
  1461   // it as selected to make ARIA widget authors life easier.
  1462   if (mRoleMapEntry && !(state & states::SELECTED) &&
  1463       !mContent->AttrValueIs(kNameSpaceID_None,
  1464                              nsGkAtoms::aria_selected,
  1465                              nsGkAtoms::_false, eCaseMatters)) {
  1466     // Special case for tabs: focused tab or focus inside related tab panel
  1467     // implies selected state.
  1468     if (mRoleMapEntry->role == roles::PAGETAB) {
  1469       if (state & states::FOCUSED) {
  1470         state |= states::SELECTED;
  1471       } else {
  1472         // If focus is in a child of the tab panel surely the tab is selected!
  1473         Relation rel = RelationByType(RelationType::LABEL_FOR);
  1474         Accessible* relTarget = nullptr;
  1475         while ((relTarget = rel.Next())) {
  1476           if (relTarget->Role() == roles::PROPERTYPAGE &&
  1477               FocusMgr()->IsFocusWithin(relTarget))
  1478             state |= states::SELECTED;
  1481     } else if (state & states::FOCUSED) {
  1482       Accessible* container = nsAccUtils::GetSelectableContainer(this, state);
  1483       if (container &&
  1484           !nsAccUtils::HasDefinedARIAToken(container->GetContent(),
  1485                                            nsGkAtoms::aria_multiselectable)) {
  1486         state |= states::SELECTED;
  1491   const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
  1492   if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
  1493     // Cannot be both expanded and collapsed -- this happens in ARIA expanded
  1494     // combobox because of limitation of ARIAMap.
  1495     // XXX: Perhaps we will be able to make this less hacky if we support
  1496     // extended states in ARIAMap, e.g. derive COLLAPSED from
  1497     // EXPANDABLE && !EXPANDED.
  1498     state &= ~states::COLLAPSED;
  1501   if (!(state & states::UNAVAILABLE)) {
  1502     state |= states::ENABLED | states::SENSITIVE;
  1504     // If the object is a current item of container widget then mark it as
  1505     // ACTIVE. This allows screen reader virtual buffer modes to know which
  1506     // descendant is the current one that would get focus if the user navigates
  1507     // to the container widget.
  1508     Accessible* widget = ContainerWidget();
  1509     if (widget && widget->CurrentItem() == this)
  1510       state |= states::ACTIVE;
  1513   if ((state & states::COLLAPSED) || (state & states::EXPANDED))
  1514     state |= states::EXPANDABLE;
  1516   // For some reasons DOM node may have not a frame. We tract such accessibles
  1517   // as invisible.
  1518   nsIFrame *frame = GetFrame();
  1519   if (!frame)
  1520     return state;
  1522   const nsStyleDisplay* display = frame->StyleDisplay();
  1523   if (display && display->mOpacity == 1.0f &&
  1524       !(state & states::INVISIBLE)) {
  1525     state |= states::OPAQUE1;
  1528   return state;
  1531 void
  1532 Accessible::ApplyARIAState(uint64_t* aState) const
  1534   if (!mContent->IsElement())
  1535     return;
  1537   dom::Element* element = mContent->AsElement();
  1539   // Test for universal states first
  1540   *aState |= aria::UniversalStatesFor(element);
  1542   if (mRoleMapEntry) {
  1544     // We only force the readonly bit off if we have a real mapping for the aria
  1545     // role. This preserves the ability for screen readers to use readonly
  1546     // (primarily on the document) as the hint for creating a virtual buffer.
  1547     if (mRoleMapEntry->role != roles::NOTHING)
  1548       *aState &= ~states::READONLY;
  1550     if (mContent->HasAttr(kNameSpaceID_None, mContent->GetIDAttributeName())) {
  1551       // If has a role & ID and aria-activedescendant on the container, assume focusable
  1552       nsIContent *ancestorContent = mContent;
  1553       while ((ancestorContent = ancestorContent->GetParent()) != nullptr) {
  1554         if (ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant)) {
  1555             // ancestor has activedescendant property, this content could be active
  1556           *aState |= states::FOCUSABLE;
  1557           break;
  1563   if (*aState & states::FOCUSABLE) {
  1564     // Special case: aria-disabled propagates from ancestors down to any focusable descendant
  1565     nsIContent *ancestorContent = mContent;
  1566     while ((ancestorContent = ancestorContent->GetParent()) != nullptr) {
  1567       if (ancestorContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
  1568                                        nsGkAtoms::_true, eCaseMatters)) {
  1569           // ancestor has aria-disabled property, this is disabled
  1570         *aState |= states::UNAVAILABLE;
  1571         break;
  1576   // special case: A native button element whose role got transformed by ARIA to a toggle button
  1577   if (IsButton())
  1578     aria::MapToState(aria::eARIAPressed, element, aState);
  1580   if (!mRoleMapEntry)
  1581     return;
  1583   *aState |= mRoleMapEntry->state;
  1585   if (aria::MapToState(mRoleMapEntry->attributeMap1, element, aState) &&
  1586       aria::MapToState(mRoleMapEntry->attributeMap2, element, aState))
  1587     aria::MapToState(mRoleMapEntry->attributeMap3, element, aState);
  1589   // ARIA gridcell inherits editable/readonly states from the grid until it's
  1590   // overridden.
  1591   if ((mRoleMapEntry->Is(nsGkAtoms::gridcell) ||
  1592        mRoleMapEntry->Is(nsGkAtoms::columnheader) ||
  1593        mRoleMapEntry->Is(nsGkAtoms::rowheader)) &&
  1594       !(*aState & (states::READONLY | states::EDITABLE))) {
  1595     const TableCellAccessible* cell = AsTableCell();
  1596     if (cell) {
  1597       TableAccessible* table = cell->Table();
  1598       if (table) {
  1599         Accessible* grid = table->AsAccessible();
  1600         uint64_t gridState = 0;
  1601         grid->ApplyARIAState(&gridState);
  1602         *aState |= (gridState & (states::READONLY | states::EDITABLE));
  1608 NS_IMETHODIMP
  1609 Accessible::GetValue(nsAString& aValue)
  1611   if (IsDefunct())
  1612     return NS_ERROR_FAILURE;
  1614   nsAutoString value;
  1615   Value(value);
  1616   aValue.Assign(value);
  1618   return NS_OK;
  1621 void
  1622 Accessible::Value(nsString& aValue)
  1624   if (!mRoleMapEntry)
  1625     return;
  1627   if (mRoleMapEntry->valueRule != eNoValue) {
  1628     // aria-valuenow is a number, and aria-valuetext is the optional text
  1629     // equivalent. For the string value, we will try the optional text
  1630     // equivalent first.
  1631     if (!mContent->GetAttr(kNameSpaceID_None,
  1632                            nsGkAtoms::aria_valuetext, aValue)) {
  1633       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
  1634                         aValue);
  1636     return;
  1639   // Value of textbox is a textified subtree.
  1640   if (mRoleMapEntry->Is(nsGkAtoms::textbox)) {
  1641     nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
  1642     return;
  1645   // Value of combobox is a text of current or selected item.
  1646   if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
  1647     Accessible* option = CurrentItem();
  1648     if (!option) {
  1649       Accessible* listbox = nullptr;
  1650       IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
  1651       while ((listbox = iter.Next()) && !listbox->IsListControl());
  1653       if (!listbox) {
  1654         uint32_t childCount = ChildCount();
  1655         for (uint32_t idx = 0; idx < childCount; idx++) {
  1656           Accessible* child = mChildren.ElementAt(idx);
  1657           if (child->IsListControl())
  1658             listbox = child;
  1662       if (listbox)
  1663         option = listbox->GetSelectedItem(0);
  1666     if (option)
  1667       nsTextEquivUtils::GetTextEquivFromSubtree(option, aValue);
  1671 double
  1672 Accessible::MaxValue() const
  1674   return AttrNumericValue(nsGkAtoms::aria_valuemax);
  1677 double
  1678 Accessible::MinValue() const
  1680   return AttrNumericValue(nsGkAtoms::aria_valuemin);
  1683 double
  1684 Accessible::Step() const
  1686   return UnspecifiedNaN<double>(); // no mimimum increment (step) in ARIA.
  1689 double
  1690 Accessible::CurValue() const
  1692   return AttrNumericValue(nsGkAtoms::aria_valuenow);
  1695 bool
  1696 Accessible::SetCurValue(double aValue)
  1698   if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
  1699     return false;
  1701   const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE;
  1702   if (State() & kValueCannotChange)
  1703     return false;
  1705   double checkValue = MinValue();
  1706   if (!IsNaN(checkValue) && aValue < checkValue)
  1707     return false;
  1709   checkValue = MaxValue();
  1710   if (!IsNaN(checkValue) && aValue > checkValue)
  1711     return false;
  1713   nsAutoString strValue;
  1714   strValue.AppendFloat(aValue);
  1716   return NS_SUCCEEDED(
  1717     mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
  1720 /* void setName (in DOMString name); */
  1721 NS_IMETHODIMP
  1722 Accessible::SetName(const nsAString& aName)
  1724   return NS_ERROR_NOT_IMPLEMENTED;
  1727 NS_IMETHODIMP
  1728 Accessible::GetKeyboardShortcut(nsAString& aKeyBinding)
  1730   aKeyBinding.Truncate();
  1731   if (IsDefunct())
  1732     return NS_ERROR_FAILURE;
  1734   KeyboardShortcut().ToString(aKeyBinding);
  1735   return NS_OK;
  1738 role
  1739 Accessible::ARIATransformRole(role aRole)
  1741   // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
  1742   // where the accessible role depends on both the role and ARIA state.
  1743   if (aRole == roles::PUSHBUTTON) {
  1744     if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
  1745       // For simplicity, any existing pressed attribute except "" or "undefined"
  1746       // indicates a toggle.
  1747       return roles::TOGGLE_BUTTON;
  1750     if (mContent->AttrValueIs(kNameSpaceID_None,
  1751                               nsGkAtoms::aria_haspopup,
  1752                               nsGkAtoms::_true,
  1753                               eCaseMatters)) {
  1754       // For button with aria-haspopup="true".
  1755       return roles::BUTTONMENU;
  1758   } else if (aRole == roles::LISTBOX) {
  1759     // A listbox inside of a combobox needs a special role because of ATK
  1760     // mapping to menu.
  1761     if (mParent && mParent->Role() == roles::COMBOBOX) {
  1762       return roles::COMBOBOX_LIST;
  1764       Relation rel = RelationByType(RelationType::NODE_CHILD_OF);
  1765       Accessible* targetAcc = nullptr;
  1766       while ((targetAcc = rel.Next()))
  1767         if (targetAcc->Role() == roles::COMBOBOX)
  1768           return roles::COMBOBOX_LIST;
  1771   } else if (aRole == roles::OPTION) {
  1772     if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
  1773       return roles::COMBOBOX_OPTION;
  1775   } else if (aRole == roles::MENUITEM) {
  1776     // Menuitem has a submenu.
  1777     if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
  1778                               nsGkAtoms::_true, eCaseMatters)) {
  1779       return roles::PARENT_MENUITEM;
  1783   return aRole;
  1786 role
  1787 Accessible::NativeRole()
  1789   return roles::NOTHING;
  1792 // readonly attribute uint8_t actionCount
  1793 NS_IMETHODIMP
  1794 Accessible::GetActionCount(uint8_t* aActionCount)
  1796   NS_ENSURE_ARG_POINTER(aActionCount);
  1797   *aActionCount = 0;
  1798   if (IsDefunct())
  1799     return NS_ERROR_FAILURE;
  1801   *aActionCount = ActionCount();
  1802   return NS_OK;
  1805 uint8_t
  1806 Accessible::ActionCount()
  1808   return GetActionRule() == eNoAction ? 0 : 1;
  1811 /* DOMString getAccActionName (in uint8_t index); */
  1812 NS_IMETHODIMP
  1813 Accessible::GetActionName(uint8_t aIndex, nsAString& aName)
  1815   aName.Truncate();
  1817   if (aIndex != 0)
  1818     return NS_ERROR_INVALID_ARG;
  1820   if (IsDefunct())
  1821     return NS_ERROR_FAILURE;
  1823   uint32_t actionRule = GetActionRule();
  1825  switch (actionRule) {
  1826    case eActivateAction:
  1827      aName.AssignLiteral("activate");
  1828      return NS_OK;
  1830    case eClickAction:
  1831      aName.AssignLiteral("click");
  1832      return NS_OK;
  1834    case ePressAction:
  1835      aName.AssignLiteral("press");
  1836      return NS_OK;
  1838    case eCheckUncheckAction:
  1840      uint64_t state = State();
  1841      if (state & states::CHECKED)
  1842        aName.AssignLiteral("uncheck");
  1843      else if (state & states::MIXED)
  1844        aName.AssignLiteral("cycle");
  1845      else
  1846        aName.AssignLiteral("check");
  1847      return NS_OK;
  1850    case eJumpAction:
  1851      aName.AssignLiteral("jump");
  1852      return NS_OK;
  1854    case eOpenCloseAction:
  1855      if (State() & states::COLLAPSED)
  1856        aName.AssignLiteral("open");
  1857      else
  1858        aName.AssignLiteral("close");
  1859      return NS_OK;
  1861    case eSelectAction:
  1862      aName.AssignLiteral("select");
  1863      return NS_OK;
  1865    case eSwitchAction:
  1866      aName.AssignLiteral("switch");
  1867      return NS_OK;
  1869    case eSortAction:
  1870      aName.AssignLiteral("sort");
  1871      return NS_OK;
  1873    case eExpandAction:
  1874      if (State() & states::COLLAPSED)
  1875        aName.AssignLiteral("expand");
  1876      else
  1877        aName.AssignLiteral("collapse");
  1878      return NS_OK;
  1881   return NS_ERROR_INVALID_ARG;
  1884 // AString getActionDescription(in uint8_t index)
  1885 NS_IMETHODIMP
  1886 Accessible::GetActionDescription(uint8_t aIndex, nsAString& aDescription)
  1888   // default to localized action name.
  1889   nsAutoString name;
  1890   nsresult rv = GetActionName(aIndex, name);
  1891   NS_ENSURE_SUCCESS(rv, rv);
  1893   TranslateString(name, aDescription);
  1894   return NS_OK;
  1897 // void doAction(in uint8_t index)
  1898 NS_IMETHODIMP
  1899 Accessible::DoAction(uint8_t aIndex)
  1901   if (aIndex != 0)
  1902     return NS_ERROR_INVALID_ARG;
  1904   if (IsDefunct())
  1905     return NS_ERROR_FAILURE;
  1907   if (GetActionRule() != eNoAction) {
  1908     DoCommand();
  1909     return NS_OK;
  1912   return NS_ERROR_INVALID_ARG;
  1915 /* DOMString getHelp (); */
  1916 NS_IMETHODIMP Accessible::GetHelp(nsAString& _retval)
  1918   return NS_ERROR_NOT_IMPLEMENTED;
  1921 nsIContent*
  1922 Accessible::GetAtomicRegion() const
  1924   nsIContent *loopContent = mContent;
  1925   nsAutoString atomic;
  1926   while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
  1927     loopContent = loopContent->GetParent();
  1929   return atomic.EqualsLiteral("true") ? loopContent : nullptr;
  1932 // nsIAccessible getRelationByType()
  1933 NS_IMETHODIMP
  1934 Accessible::GetRelationByType(uint32_t aType, nsIAccessibleRelation** aRelation)
  1936   NS_ENSURE_ARG_POINTER(aRelation);
  1937   *aRelation = nullptr;
  1939   NS_ENSURE_ARG(aType <= static_cast<uint32_t>(RelationType::LAST));
  1941   if (IsDefunct())
  1942     return NS_ERROR_FAILURE;
  1944   Relation rel = RelationByType(static_cast<RelationType>(aType));
  1945   NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel));
  1946   return *aRelation ? NS_OK : NS_ERROR_FAILURE;
  1949 Relation
  1950 Accessible::RelationByType(RelationType aType)
  1952   if (!HasOwnContent())
  1953     return Relation();
  1955   // Relationships are defined on the same content node that the role would be
  1956   // defined on.
  1957   switch (aType) {
  1958     case RelationType::LABELLED_BY: {
  1959       Relation rel(new IDRefsIterator(mDoc, mContent,
  1960                                       nsGkAtoms::aria_labelledby));
  1961       if (mContent->IsHTML()) {
  1962         rel.AppendIter(new HTMLLabelIterator(Document(), this));
  1963       } else if (mContent->IsXUL()) {
  1964         rel.AppendIter(new XULLabelIterator(Document(), mContent));
  1967       return rel;
  1970     case RelationType::LABEL_FOR: {
  1971       Relation rel(new RelatedAccIterator(Document(), mContent,
  1972                                           nsGkAtoms::aria_labelledby));
  1973       if (mContent->Tag() == nsGkAtoms::label && mContent->IsXUL())
  1974         rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
  1976       return rel;
  1979     case RelationType::DESCRIBED_BY: {
  1980       Relation rel(new IDRefsIterator(mDoc, mContent,
  1981                                       nsGkAtoms::aria_describedby));
  1982       if (mContent->IsXUL())
  1983         rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
  1985       return rel;
  1988     case RelationType::DESCRIPTION_FOR: {
  1989       Relation rel(new RelatedAccIterator(Document(), mContent,
  1990                                           nsGkAtoms::aria_describedby));
  1992       // This affectively adds an optional control attribute to xul:description,
  1993       // which only affects accessibility, by allowing the description to be
  1994       // tied to a control.
  1995       if (mContent->Tag() == nsGkAtoms::description &&
  1996           mContent->IsXUL())
  1997         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
  1998                                           nsGkAtoms::control));
  2000       return rel;
  2003     case RelationType::NODE_CHILD_OF: {
  2004       Relation rel(new RelatedAccIterator(Document(), mContent,
  2005                                           nsGkAtoms::aria_owns));
  2007       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
  2008       // get the parent the hard way.
  2009       if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
  2010                             mRoleMapEntry->role == roles::LISTITEM ||
  2011                             mRoleMapEntry->role == roles::ROW)) {
  2012         rel.AppendTarget(GetGroupInfo()->ConceptualParent());
  2015       // If accessible is in its own Window, or is the root of a document,
  2016       // then we should provide NODE_CHILD_OF relation so that MSAA clients
  2017       // can easily get to true parent instead of getting to oleacc's
  2018       // ROLE_WINDOW accessible which will prevent us from going up further
  2019       // (because it is system generated and has no idea about the hierarchy
  2020       // above it).
  2021       nsIFrame *frame = GetFrame();
  2022       if (frame) {
  2023         nsView *view = frame->GetViewExternal();
  2024         if (view) {
  2025           nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
  2026           if (scrollFrame || view->GetWidget() || !frame->GetParent())
  2027             rel.AppendTarget(Parent());
  2031       return rel;
  2034     case RelationType::NODE_PARENT_OF: {
  2035       Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
  2037       // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
  2038       // also can be organized by groups.
  2039       if (mRoleMapEntry &&
  2040           (mRoleMapEntry->role == roles::OUTLINEITEM ||
  2041            mRoleMapEntry->role == roles::LISTITEM ||
  2042            mRoleMapEntry->role == roles::ROW ||
  2043            mRoleMapEntry->role == roles::OUTLINE ||
  2044            mRoleMapEntry->role == roles::LIST ||
  2045            mRoleMapEntry->role == roles::TREE_TABLE)) {
  2046         rel.AppendIter(new ItemIterator(this));
  2049       return rel;
  2052     case RelationType::CONTROLLED_BY:
  2053       return Relation(new RelatedAccIterator(Document(), mContent,
  2054                                              nsGkAtoms::aria_controls));
  2056     case RelationType::CONTROLLER_FOR: {
  2057       Relation rel(new IDRefsIterator(mDoc, mContent,
  2058                                       nsGkAtoms::aria_controls));
  2059       rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
  2060       return rel;
  2063     case RelationType::FLOWS_TO:
  2064       return Relation(new IDRefsIterator(mDoc, mContent,
  2065                                          nsGkAtoms::aria_flowto));
  2067     case RelationType::FLOWS_FROM:
  2068       return Relation(new RelatedAccIterator(Document(), mContent,
  2069                                              nsGkAtoms::aria_flowto));
  2071     case RelationType::MEMBER_OF:
  2072           return Relation(mDoc, GetAtomicRegion());
  2074     case RelationType::SUBWINDOW_OF:
  2075     case RelationType::EMBEDS:
  2076     case RelationType::EMBEDDED_BY:
  2077     case RelationType::POPUP_FOR:
  2078     case RelationType::PARENT_WINDOW_OF:
  2079       return Relation();
  2081     case RelationType::DEFAULT_BUTTON: {
  2082       if (mContent->IsHTML()) {
  2083         // HTML form controls implements nsIFormControl interface.
  2084         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
  2085         if (control) {
  2086           nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
  2087           if (form) {
  2088             nsCOMPtr<nsIContent> formContent =
  2089               do_QueryInterface(form->GetDefaultSubmitElement());
  2090             return Relation(mDoc, formContent);
  2093       } else {
  2094         // In XUL, use first <button default="true" .../> in the document
  2095         nsCOMPtr<nsIDOMXULDocument> xulDoc =
  2096           do_QueryInterface(mContent->OwnerDoc());
  2097         nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
  2098         if (xulDoc) {
  2099           nsCOMPtr<nsIDOMNodeList> possibleDefaultButtons;
  2100           xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
  2101                                          NS_LITERAL_STRING("true"),
  2102                                          getter_AddRefs(possibleDefaultButtons));
  2103           if (possibleDefaultButtons) {
  2104             uint32_t length;
  2105             possibleDefaultButtons->GetLength(&length);
  2106             nsCOMPtr<nsIDOMNode> possibleButton;
  2107             // Check for button in list of default="true" elements
  2108             for (uint32_t count = 0; count < length && !buttonEl; count ++) {
  2109               possibleDefaultButtons->Item(count, getter_AddRefs(possibleButton));
  2110               buttonEl = do_QueryInterface(possibleButton);
  2113           if (!buttonEl) { // Check for anonymous accept button in <dialog>
  2114             dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
  2115             if (rootElm) {
  2116               nsIContent* possibleButtonEl = rootElm->OwnerDoc()->
  2117                 GetAnonymousElementByAttribute(rootElm, nsGkAtoms::_default,
  2118                                                NS_LITERAL_STRING("true"));
  2119               buttonEl = do_QueryInterface(possibleButtonEl);
  2122           nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
  2123           return Relation(mDoc, relatedContent);
  2126       return Relation();
  2129     case RelationType::CONTAINING_DOCUMENT:
  2130       return Relation(mDoc);
  2132     case RelationType::CONTAINING_TAB_PANE: {
  2133       nsCOMPtr<nsIDocShell> docShell =
  2134         nsCoreUtils::GetDocShellFor(GetNode());
  2135       if (docShell) {
  2136         // Walk up the parent chain without crossing the boundary at which item
  2137         // types change, preventing us from walking up out of tab content.
  2138         nsCOMPtr<nsIDocShellTreeItem> root;
  2139         docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
  2140         if (root) {
  2141           // If the item type is typeContent, we assume we are in browser tab
  2142           // content. Note, this includes content such as about:addons,
  2143           // for consistency.
  2144           if (root->ItemType() == nsIDocShellTreeItem::typeContent) {
  2145             return Relation(nsAccUtils::GetDocAccessibleFor(root));
  2149       return  Relation();
  2152     case RelationType::CONTAINING_APPLICATION:
  2153       return Relation(ApplicationAcc());
  2155     default:
  2156       return Relation();
  2160 NS_IMETHODIMP
  2161 Accessible::GetRelations(nsIArray **aRelations)
  2163   NS_ENSURE_ARG_POINTER(aRelations);
  2164   *aRelations = nullptr;
  2166   if (IsDefunct())
  2167     return NS_ERROR_FAILURE;
  2169   nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
  2170   NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
  2172   static const uint32_t relationTypes[] = {
  2173     nsIAccessibleRelation::RELATION_LABELLED_BY,
  2174     nsIAccessibleRelation::RELATION_LABEL_FOR,
  2175     nsIAccessibleRelation::RELATION_DESCRIBED_BY,
  2176     nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
  2177     nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
  2178     nsIAccessibleRelation::RELATION_NODE_PARENT_OF,
  2179     nsIAccessibleRelation::RELATION_CONTROLLED_BY,
  2180     nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
  2181     nsIAccessibleRelation::RELATION_FLOWS_TO,
  2182     nsIAccessibleRelation::RELATION_FLOWS_FROM,
  2183     nsIAccessibleRelation::RELATION_MEMBER_OF,
  2184     nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
  2185     nsIAccessibleRelation::RELATION_EMBEDS,
  2186     nsIAccessibleRelation::RELATION_EMBEDDED_BY,
  2187     nsIAccessibleRelation::RELATION_POPUP_FOR,
  2188     nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
  2189     nsIAccessibleRelation::RELATION_DEFAULT_BUTTON,
  2190     nsIAccessibleRelation::RELATION_CONTAINING_DOCUMENT,
  2191     nsIAccessibleRelation::RELATION_CONTAINING_TAB_PANE,
  2192     nsIAccessibleRelation::RELATION_CONTAINING_APPLICATION
  2193   };
  2195   for (uint32_t idx = 0; idx < ArrayLength(relationTypes); idx++) {
  2196     nsCOMPtr<nsIAccessibleRelation> relation;
  2197     nsresult rv = GetRelationByType(relationTypes[idx], getter_AddRefs(relation));
  2199     if (NS_SUCCEEDED(rv) && relation) {
  2200       uint32_t targets = 0;
  2201       relation->GetTargetsCount(&targets);
  2202       if (targets)
  2203         relations->AppendElement(relation, false);
  2207   NS_ADDREF(*aRelations = relations);
  2208   return NS_OK;
  2211 /* void extendSelection (); */
  2212 NS_IMETHODIMP Accessible::ExtendSelection()
  2214   // XXX Should be implemented, but not high priority
  2215   return NS_ERROR_NOT_IMPLEMENTED;
  2218 /* [noscript] void getNativeInterface(out voidPtr aOutAccessible); */
  2219 NS_IMETHODIMP Accessible::GetNativeInterface(void **aOutAccessible)
  2221   return NS_ERROR_NOT_IMPLEMENTED;
  2224 void
  2225 Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
  2227   class Runnable MOZ_FINAL : public nsRunnable
  2229   public:
  2230     Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
  2231       mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
  2233     NS_IMETHOD Run()
  2235       if (mAcc)
  2236         mAcc->DispatchClickEvent(mContent, mIdx);
  2238       return NS_OK;
  2241     void Revoke()
  2243       mAcc = nullptr;
  2244       mContent = nullptr;
  2247   private:
  2248     nsRefPtr<Accessible> mAcc;
  2249     nsCOMPtr<nsIContent> mContent;
  2250     uint32_t mIdx;
  2251   };
  2253   nsIContent* content = aContent ? aContent : mContent.get();
  2254   nsCOMPtr<nsIRunnable> runnable = new Runnable(this, content, aActionIndex);
  2255   NS_DispatchToMainThread(runnable);
  2258 void
  2259 Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex)
  2261   if (IsDefunct())
  2262     return;
  2264   nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
  2266   // Scroll into view.
  2267   presShell->ScrollContentIntoView(aContent,
  2268                                    nsIPresShell::ScrollAxis(),
  2269                                    nsIPresShell::ScrollAxis(),
  2270                                    nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  2272   nsWeakFrame frame = aContent->GetPrimaryFrame();
  2273   if (!frame)
  2274     return;
  2276   // Compute x and y coordinates.
  2277   nsPoint point;
  2278   nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
  2279   if (!widget)
  2280     return;
  2282   nsSize size = frame->GetSize();
  2284   nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
  2285   int32_t x = presContext->AppUnitsToDevPixels(point.x + size.width / 2);
  2286   int32_t y = presContext->AppUnitsToDevPixels(point.y + size.height / 2);
  2288   // Simulate a touch interaction by dispatching touch events with mouse events.
  2289   nsCoreUtils::DispatchTouchEvent(NS_TOUCH_START, x, y, aContent, frame, presShell, widget);
  2290   nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, x, y, aContent, frame, presShell, widget);
  2291   nsCoreUtils::DispatchTouchEvent(NS_TOUCH_END, x, y, aContent, frame, presShell, widget);
  2292   nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_UP, x, y, aContent, frame, presShell, widget);
  2295 NS_IMETHODIMP
  2296 Accessible::ScrollTo(uint32_t aHow)
  2298   if (IsDefunct())
  2299     return NS_ERROR_FAILURE;
  2301   nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
  2302   return NS_OK;
  2305 NS_IMETHODIMP
  2306 Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
  2308   nsIFrame *frame = GetFrame();
  2309   if (!frame)
  2310     return NS_ERROR_FAILURE;
  2312   nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
  2313                                                         this);
  2315   nsIFrame *parentFrame = frame;
  2316   while ((parentFrame = parentFrame->GetParent()))
  2317     nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
  2319   return NS_OK;
  2322 // nsIAccessibleHyperLink
  2323 // Because of new-atk design, any embedded object in text can implement
  2324 // nsIAccessibleHyperLink, which helps determine where it is located
  2325 // within containing text
  2327 // readonly attribute long nsIAccessibleHyperLink::anchorCount
  2328 NS_IMETHODIMP
  2329 Accessible::GetAnchorCount(int32_t *aAnchorCount)
  2331   NS_ENSURE_ARG_POINTER(aAnchorCount);
  2332   *aAnchorCount = 0;
  2334   if (IsDefunct())
  2335     return NS_ERROR_FAILURE;
  2337   *aAnchorCount = AnchorCount();
  2338   return NS_OK;
  2341 // readonly attribute long nsIAccessibleHyperLink::startIndex
  2342 NS_IMETHODIMP
  2343 Accessible::GetStartIndex(int32_t *aStartIndex)
  2345   NS_ENSURE_ARG_POINTER(aStartIndex);
  2346   *aStartIndex = 0;
  2348   if (IsDefunct())
  2349     return NS_ERROR_FAILURE;
  2351   *aStartIndex = StartOffset();
  2352   return NS_OK;
  2355 // readonly attribute long nsIAccessibleHyperLink::endIndex
  2356 NS_IMETHODIMP
  2357 Accessible::GetEndIndex(int32_t *aEndIndex)
  2359   NS_ENSURE_ARG_POINTER(aEndIndex);
  2360   *aEndIndex = 0;
  2362   if (IsDefunct())
  2363     return NS_ERROR_FAILURE;
  2365   *aEndIndex = EndOffset();
  2366   return NS_OK;
  2369 NS_IMETHODIMP
  2370 Accessible::GetURI(int32_t aIndex, nsIURI **aURI)
  2372   NS_ENSURE_ARG_POINTER(aURI);
  2374   if (IsDefunct())
  2375     return NS_ERROR_FAILURE;
  2377   if (aIndex < 0 || aIndex >= static_cast<int32_t>(AnchorCount()))
  2378     return NS_ERROR_INVALID_ARG;
  2380   nsRefPtr<nsIURI>(AnchorURIAt(aIndex)).forget(aURI);
  2381   return NS_OK;
  2385 NS_IMETHODIMP
  2386 Accessible::GetAnchor(int32_t aIndex, nsIAccessible** aAccessible)
  2388   NS_ENSURE_ARG_POINTER(aAccessible);
  2389   *aAccessible = nullptr;
  2391   if (IsDefunct())
  2392     return NS_ERROR_FAILURE;
  2394   if (aIndex < 0 || aIndex >= static_cast<int32_t>(AnchorCount()))
  2395     return NS_ERROR_INVALID_ARG;
  2397   NS_IF_ADDREF(*aAccessible = AnchorAt(aIndex));
  2398   return NS_OK;
  2401 // readonly attribute boolean nsIAccessibleHyperLink::valid
  2402 NS_IMETHODIMP
  2403 Accessible::GetValid(bool *aValid)
  2405   NS_ENSURE_ARG_POINTER(aValid);
  2406   *aValid = false;
  2408   if (IsDefunct())
  2409     return NS_ERROR_FAILURE;
  2411   *aValid = IsLinkValid();
  2412   return NS_OK;
  2415 void
  2416 Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
  2417                          uint32_t aLength)
  2419   // Return text representation of non-text accessible within hypertext
  2420   // accessible. Text accessible overrides this method to return enclosed text.
  2421   if (aStartOffset != 0 || aLength == 0)
  2422     return;
  2424   nsIFrame *frame = GetFrame();
  2425   if (!frame)
  2426     return;
  2428   NS_ASSERTION(mParent,
  2429                "Called on accessible unbound from tree. Result can be wrong.");
  2431   if (frame->GetType() == nsGkAtoms::brFrame) {
  2432     aText += kForcedNewLineChar;
  2433   } else if (mParent && nsAccUtils::MustPrune(mParent)) {
  2434     // Expose the embedded object accessible as imaginary embedded object
  2435     // character if its parent hypertext accessible doesn't expose children to
  2436     // AT.
  2437     aText += kImaginaryEmbeddedObjectChar;
  2438   } else {
  2439     aText += kEmbeddedObjectChar;
  2443 void
  2444 Accessible::Shutdown()
  2446   // Mark the accessible as defunct, invalidate the child count and pointers to 
  2447   // other accessibles, also make sure none of its children point to this parent
  2448   mStateFlags |= eIsDefunct;
  2450   InvalidateChildren();
  2451   if (mParent)
  2452     mParent->RemoveChild(this);
  2454   mContent = nullptr;
  2455   mDoc = nullptr;
  2458 // Accessible protected
  2459 void
  2460 Accessible::ARIAName(nsString& aName)
  2462   // aria-labelledby now takes precedence over aria-label
  2463   nsresult rv = nsTextEquivUtils::
  2464     GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
  2465   if (NS_SUCCEEDED(rv)) {
  2466     aName.CompressWhitespace();
  2469   if (aName.IsEmpty() &&
  2470       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
  2471     aName.CompressWhitespace();
  2475 // Accessible protected
  2476 ENameValueFlag
  2477 Accessible::NativeName(nsString& aName)
  2479   if (mContent->IsHTML()) {
  2480     Accessible* label = nullptr;
  2481     HTMLLabelIterator iter(Document(), this);
  2482     while ((label = iter.Next())) {
  2483       nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
  2484                                                    &aName);
  2485       aName.CompressWhitespace();
  2488     if (!aName.IsEmpty())
  2489       return eNameOK;
  2491     nsTextEquivUtils::GetNameFromSubtree(this, aName);
  2492     return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
  2495   if (mContent->IsXUL()) {
  2496     XULElmName(mDoc, mContent, aName);
  2497     if (!aName.IsEmpty())
  2498       return eNameOK;
  2500     nsTextEquivUtils::GetNameFromSubtree(this, aName);
  2501     return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
  2504   if (mContent->IsSVG()) {
  2505     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
  2506     // for processing, the user agent shall choose the first one.
  2507     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
  2508          childElm = childElm->GetNextSibling()) {
  2509       if (childElm->IsSVG(nsGkAtoms::title)) {
  2510         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
  2511         return eNameOK;
  2516   return eNameOK;
  2519 // Accessible protected
  2520 void
  2521 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
  2523   NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
  2525   if (mParent) {
  2526     if (mParent != aParent) {
  2527       NS_ERROR("Adopting child!");
  2528       mParent->RemoveChild(this);
  2529       mParent->InvalidateChildrenGroupInfo();
  2530     } else {
  2531       NS_ERROR("Binding to the same parent!");
  2532       return;
  2536   mParent = aParent;
  2537   mIndexInParent = aIndexInParent;
  2539   mParent->InvalidateChildrenGroupInfo();
  2541   // Note: this is currently only used for richlistitems and their children.
  2542   if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
  2543     mContextFlags |= eHasNameDependentParent;
  2544   else
  2545     mContextFlags &= ~eHasNameDependentParent;
  2548 // Accessible protected
  2549 void
  2550 Accessible::UnbindFromParent()
  2552   mParent->InvalidateChildrenGroupInfo();
  2553   mParent = nullptr;
  2554   mIndexInParent = -1;
  2555   mIndexOfEmbeddedChild = -1;
  2556   mGroupInfo = nullptr;
  2557   mContextFlags &= ~eHasNameDependentParent;
  2560 ////////////////////////////////////////////////////////////////////////////////
  2561 // Accessible public methods
  2563 RootAccessible*
  2564 Accessible::RootAccessible() const
  2566   nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(GetNode());
  2567   NS_ASSERTION(docShell, "No docshell for mContent");
  2568   if (!docShell) {
  2569     return nullptr;
  2572   nsCOMPtr<nsIDocShellTreeItem> root;
  2573   docShell->GetRootTreeItem(getter_AddRefs(root));
  2574   NS_ASSERTION(root, "No root content tree item");
  2575   if (!root) {
  2576     return nullptr;
  2579   DocAccessible* docAcc = nsAccUtils::GetDocAccessibleFor(root);
  2580   return docAcc ? docAcc->AsRoot() : nullptr;
  2583 nsIFrame*
  2584 Accessible::GetFrame() const
  2586   return mContent ? mContent->GetPrimaryFrame() : nullptr;
  2589 nsINode*
  2590 Accessible::GetNode() const
  2592   return mContent;
  2595 void
  2596 Accessible::Language(nsAString& aLanguage)
  2598   aLanguage.Truncate();
  2600   if (!mDoc)
  2601     return;
  2603   nsCoreUtils::GetLanguageFor(mContent, nullptr, aLanguage);
  2604   if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
  2605     mDoc->DocumentNode()->GetHeaderData(nsGkAtoms::headerContentLanguage,
  2606                                         aLanguage);
  2610 void
  2611 Accessible::InvalidateChildren()
  2613   int32_t childCount = mChildren.Length();
  2614   for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
  2615     Accessible* child = mChildren.ElementAt(childIdx);
  2616     child->UnbindFromParent();
  2619   mEmbeddedObjCollector = nullptr;
  2620   mChildren.Clear();
  2621   SetChildrenFlag(eChildrenUninitialized);
  2624 bool
  2625 Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
  2627   if (!aChild)
  2628     return false;
  2630   if (aIndex == mChildren.Length()) {
  2631     if (!mChildren.AppendElement(aChild))
  2632       return false;
  2634   } else {
  2635     if (!mChildren.InsertElementAt(aIndex, aChild))
  2636       return false;
  2638     for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
  2639       NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
  2640                    "Accessible child index doesn't match");
  2641       mChildren[idx]->mIndexInParent = idx;
  2644     mEmbeddedObjCollector = nullptr;
  2647   if (!nsAccUtils::IsEmbeddedObject(aChild))
  2648     SetChildrenFlag(eMixedChildren);
  2650   aChild->BindToParent(this, aIndex);
  2651   return true;
  2654 bool
  2655 Accessible::RemoveChild(Accessible* aChild)
  2657   if (!aChild)
  2658     return false;
  2660   if (aChild->mParent != this || aChild->mIndexInParent == -1)
  2661     return false;
  2663   uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
  2664   if (index >= mChildren.Length() || mChildren[index] != aChild) {
  2665     NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
  2666     aChild->UnbindFromParent();
  2667     return false;
  2670   for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
  2671     NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
  2672                  "Accessible child index doesn't match");
  2673     mChildren[idx]->mIndexInParent = idx - 1;
  2676   aChild->UnbindFromParent();
  2677   mChildren.RemoveElementAt(index);
  2678   mEmbeddedObjCollector = nullptr;
  2680   return true;
  2683 Accessible*
  2684 Accessible::GetChildAt(uint32_t aIndex) const
  2686   Accessible* child = mChildren.SafeElementAt(aIndex, nullptr);
  2687   if (!child)
  2688     return nullptr;
  2690 #ifdef DEBUG
  2691   Accessible* realParent = child->mParent;
  2692   NS_ASSERTION(!realParent || realParent == this,
  2693                "Two accessibles have the same first child accessible!");
  2694 #endif
  2696   return child;
  2699 uint32_t
  2700 Accessible::ChildCount() const
  2702   return mChildren.Length();
  2705 int32_t
  2706 Accessible::IndexInParent() const
  2708   return mIndexInParent;
  2711 uint32_t
  2712 Accessible::EmbeddedChildCount()
  2714   if (IsChildrenFlag(eMixedChildren)) {
  2715     if (!mEmbeddedObjCollector)
  2716       mEmbeddedObjCollector = new EmbeddedObjCollector(this);
  2717     return mEmbeddedObjCollector->Count();
  2720   return ChildCount();
  2723 Accessible*
  2724 Accessible::GetEmbeddedChildAt(uint32_t aIndex)
  2726   if (IsChildrenFlag(eMixedChildren)) {
  2727     if (!mEmbeddedObjCollector)
  2728       mEmbeddedObjCollector = new EmbeddedObjCollector(this);
  2729     return mEmbeddedObjCollector ?
  2730       mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
  2733   return GetChildAt(aIndex);
  2736 int32_t
  2737 Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
  2739   if (IsChildrenFlag(eMixedChildren)) {
  2740     if (!mEmbeddedObjCollector)
  2741       mEmbeddedObjCollector = new EmbeddedObjCollector(this);
  2742     return mEmbeddedObjCollector ?
  2743       mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
  2746   return GetIndexOf(aChild);
  2749 ////////////////////////////////////////////////////////////////////////////////
  2750 // HyperLinkAccessible methods
  2752 bool
  2753 Accessible::IsLink()
  2755   // Every embedded accessible within hypertext accessible implements
  2756   // hyperlink interface.
  2757   return mParent && mParent->IsHyperText() && nsAccUtils::IsEmbeddedObject(this);
  2760 uint32_t
  2761 Accessible::StartOffset()
  2763   NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
  2765   HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
  2766   return hyperText ? hyperText->GetChildOffset(this) : 0;
  2769 uint32_t
  2770 Accessible::EndOffset()
  2772   NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
  2774   HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
  2775   return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
  2778 uint32_t
  2779 Accessible::AnchorCount()
  2781   NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
  2782   return 1;
  2785 Accessible*
  2786 Accessible::AnchorAt(uint32_t aAnchorIndex)
  2788   NS_PRECONDITION(IsLink(), "GetAnchor is called on not hyper link!");
  2789   return aAnchorIndex == 0 ? this : nullptr;
  2792 already_AddRefed<nsIURI>
  2793 Accessible::AnchorURIAt(uint32_t aAnchorIndex)
  2795   NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
  2796   return nullptr;
  2800 ////////////////////////////////////////////////////////////////////////////////
  2801 // SelectAccessible
  2803 already_AddRefed<nsIArray>
  2804 Accessible::SelectedItems()
  2806   nsCOMPtr<nsIMutableArray> selectedItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
  2807   if (!selectedItems)
  2808     return nullptr;
  2810   AccIterator iter(this, filters::GetSelected);
  2811   nsIAccessible* selected = nullptr;
  2812   while ((selected = iter.Next()))
  2813     selectedItems->AppendElement(selected, false);
  2815   return selectedItems.forget();
  2818 uint32_t
  2819 Accessible::SelectedItemCount()
  2821   uint32_t count = 0;
  2822   AccIterator iter(this, filters::GetSelected);
  2823   Accessible* selected = nullptr;
  2824   while ((selected = iter.Next()))
  2825     ++count;
  2827   return count;
  2830 Accessible*
  2831 Accessible::GetSelectedItem(uint32_t aIndex)
  2833   AccIterator iter(this, filters::GetSelected);
  2834   Accessible* selected = nullptr;
  2836   uint32_t index = 0;
  2837   while ((selected = iter.Next()) && index < aIndex)
  2838     index++;
  2840   return selected;
  2843 bool
  2844 Accessible::IsItemSelected(uint32_t aIndex)
  2846   uint32_t index = 0;
  2847   AccIterator iter(this, filters::GetSelectable);
  2848   Accessible* selected = nullptr;
  2849   while ((selected = iter.Next()) && index < aIndex)
  2850     index++;
  2852   return selected &&
  2853     selected->State() & states::SELECTED;
  2856 bool
  2857 Accessible::AddItemToSelection(uint32_t aIndex)
  2859   uint32_t index = 0;
  2860   AccIterator iter(this, filters::GetSelectable);
  2861   Accessible* selected = nullptr;
  2862   while ((selected = iter.Next()) && index < aIndex)
  2863     index++;
  2865   if (selected)
  2866     selected->SetSelected(true);
  2868   return static_cast<bool>(selected);
  2871 bool
  2872 Accessible::RemoveItemFromSelection(uint32_t aIndex)
  2874   uint32_t index = 0;
  2875   AccIterator iter(this, filters::GetSelectable);
  2876   Accessible* selected = nullptr;
  2877   while ((selected = iter.Next()) && index < aIndex)
  2878     index++;
  2880   if (selected)
  2881     selected->SetSelected(false);
  2883   return static_cast<bool>(selected);
  2886 bool
  2887 Accessible::SelectAll()
  2889   bool success = false;
  2890   Accessible* selectable = nullptr;
  2892   AccIterator iter(this, filters::GetSelectable);
  2893   while((selectable = iter.Next())) {
  2894     success = true;
  2895     selectable->SetSelected(true);
  2897   return success;
  2900 bool
  2901 Accessible::UnselectAll()
  2903   bool success = false;
  2904   Accessible* selected = nullptr;
  2906   AccIterator iter(this, filters::GetSelected);
  2907   while ((selected = iter.Next())) {
  2908     success = true;
  2909     selected->SetSelected(false);
  2911   return success;
  2914 ////////////////////////////////////////////////////////////////////////////////
  2915 // Widgets
  2917 bool
  2918 Accessible::IsWidget() const
  2920   return false;
  2923 bool
  2924 Accessible::IsActiveWidget() const
  2926   if (FocusMgr()->HasDOMFocus(mContent))
  2927     return true;
  2929   // If text entry of combobox widget has a focus then the combobox widget is
  2930   // active.
  2931   if (mRoleMapEntry && mRoleMapEntry->Is(nsGkAtoms::combobox)) {
  2932     uint32_t childCount = ChildCount();
  2933     for (uint32_t idx = 0; idx < childCount; idx++) {
  2934       Accessible* child = mChildren.ElementAt(idx);
  2935       if (child->Role() == roles::ENTRY)
  2936         return FocusMgr()->HasDOMFocus(child->GetContent());
  2940   return false;
  2943 bool
  2944 Accessible::AreItemsOperable() const
  2946   return HasOwnContent() &&
  2947     mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
  2950 Accessible*
  2951 Accessible::CurrentItem()
  2953   // Check for aria-activedescendant, which changes which element has focus.
  2954   // For activedescendant, the ARIA spec does not require that the user agent
  2955   // checks whether pointed node is actually a DOM descendant of the element
  2956   // with the aria-activedescendant attribute.
  2957   nsAutoString id;
  2958   if (HasOwnContent() &&
  2959       mContent->GetAttr(kNameSpaceID_None,
  2960                         nsGkAtoms::aria_activedescendant, id)) {
  2961     nsIDocument* DOMDoc = mContent->OwnerDoc();
  2962     dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
  2963     if (activeDescendantElm) {
  2964       DocAccessible* document = Document();
  2965       if (document)
  2966         return document->GetAccessible(activeDescendantElm);
  2969   return nullptr;
  2972 void
  2973 Accessible::SetCurrentItem(Accessible* aItem)
  2975   nsIAtom* id = aItem->GetContent()->GetID();
  2976   if (id) {
  2977     nsAutoString idStr;
  2978     id->ToString(idStr);
  2979     mContent->SetAttr(kNameSpaceID_None,
  2980                       nsGkAtoms::aria_activedescendant, idStr, true);
  2984 Accessible*
  2985 Accessible::ContainerWidget() const
  2987   if (HasARIARole() && mContent->HasID()) {
  2988     for (Accessible* parent = Parent(); parent; parent = parent->Parent()) {
  2989       nsIContent* parentContent = parent->GetContent();
  2990       if (parentContent &&
  2991         parentContent->HasAttr(kNameSpaceID_None,
  2992                                nsGkAtoms::aria_activedescendant)) {
  2993         return parent;
  2996       // Don't cross DOM document boundaries.
  2997       if (parent->IsDoc())
  2998         break;
  3001   return nullptr;
  3004 ////////////////////////////////////////////////////////////////////////////////
  3005 // Accessible protected methods
  3007 void
  3008 Accessible::LastRelease()
  3010   // First cleanup if needed...
  3011   if (mDoc) {
  3012     Shutdown();
  3013     NS_ASSERTION(!mDoc,
  3014                  "A Shutdown() impl forgot to call its parent's Shutdown?");
  3016   // ... then die.
  3017   delete this;
  3020 void
  3021 Accessible::CacheChildren()
  3023   DocAccessible* doc = Document();
  3024   NS_ENSURE_TRUE_VOID(doc);
  3026   TreeWalker walker(this, mContent);
  3028   Accessible* child = nullptr;
  3029   while ((child = walker.NextChild()) && AppendChild(child));
  3032 void
  3033 Accessible::TestChildCache(Accessible* aCachedChild) const
  3035 #ifdef DEBUG
  3036   int32_t childCount = mChildren.Length();
  3037   if (childCount == 0) {
  3038     NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
  3039                  "No children but initialized!");
  3040     return;
  3043   Accessible* child = nullptr;
  3044   for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
  3045     child = mChildren[childIdx];
  3046     if (child == aCachedChild)
  3047       break;
  3050   NS_ASSERTION(child == aCachedChild,
  3051                "[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");  
  3052 #endif
  3055 // Accessible public
  3056 bool
  3057 Accessible::EnsureChildren()
  3059   if (IsDefunct()) {
  3060     SetChildrenFlag(eChildrenUninitialized);
  3061     return true;
  3064   if (!IsChildrenFlag(eChildrenUninitialized))
  3065     return false;
  3067   // State is embedded children until text leaf accessible is appended.
  3068   SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
  3070   CacheChildren();
  3071   return false;
  3074 Accessible*
  3075 Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
  3077   if (!mParent || mIndexInParent == -1) {
  3078     if (aError)
  3079       *aError = NS_ERROR_UNEXPECTED;
  3081     return nullptr;
  3084   if (aError &&
  3085       mIndexInParent + aOffset >= static_cast<int32_t>(mParent->ChildCount())) {
  3086     *aError = NS_OK; // fail peacefully
  3087     return nullptr;
  3090   Accessible* child = mParent->GetChildAt(mIndexInParent + aOffset);
  3091   if (aError && !child)
  3092     *aError = NS_ERROR_UNEXPECTED;
  3094   return child;
  3097 double
  3098 Accessible::AttrNumericValue(nsIAtom* aAttr) const
  3100   if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
  3101     return UnspecifiedNaN<double>();
  3103   nsAutoString attrValue;
  3104   if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
  3105     return UnspecifiedNaN<double>();
  3107   nsresult error = NS_OK;
  3108   double value = attrValue.ToDouble(&error);
  3109   return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
  3112 uint32_t
  3113 Accessible::GetActionRule()
  3115   if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
  3116     return eNoAction;
  3118   // Return "click" action on elements that have an attached popup menu.
  3119   if (mContent->IsXUL())
  3120     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
  3121       return eClickAction;
  3123   // Has registered 'click' event handler.
  3124   bool isOnclick = nsCoreUtils::HasClickListener(mContent);
  3126   if (isOnclick)
  3127     return eClickAction;
  3129   // Get an action based on ARIA role.
  3130   if (mRoleMapEntry &&
  3131       mRoleMapEntry->actionRule != eNoAction)
  3132     return mRoleMapEntry->actionRule;
  3134   // Get an action based on ARIA attribute.
  3135   if (nsAccUtils::HasDefinedARIAToken(mContent,
  3136                                       nsGkAtoms::aria_expanded))
  3137     return eExpandAction;
  3139   return eNoAction;
  3142 AccGroupInfo*
  3143 Accessible::GetGroupInfo()
  3145   if (mGroupInfo){
  3146     if (HasDirtyGroupInfo()) {
  3147       mGroupInfo->Update();
  3148       SetDirtyGroupInfo(false);
  3151     return mGroupInfo;
  3154   mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
  3155   return mGroupInfo;
  3158 void
  3159 Accessible::InvalidateChildrenGroupInfo()
  3161   uint32_t length = mChildren.Length();
  3162   for (uint32_t i = 0; i < length; i++) {
  3163     Accessible* child = mChildren[i];
  3164     child->SetDirtyGroupInfo(true);
  3168 void
  3169 Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
  3171   AccGroupInfo* groupInfo = GetGroupInfo();
  3172   if (groupInfo) {
  3173     *aPosInSet = groupInfo->PosInSet();
  3174     *aSetSize = groupInfo->SetSize();
  3178 int32_t
  3179 Accessible::GetLevelInternal()
  3181   int32_t level = nsAccUtils::GetDefaultLevel(this);
  3183   if (!IsBoundToParent())
  3184     return level;
  3186   roles::Role role = Role();
  3187   if (role == roles::OUTLINEITEM) {
  3188     // Always expose 'level' attribute for 'outlineitem' accessible. The number
  3189     // of nested 'grouping' accessibles containing 'outlineitem' accessible is
  3190     // its level.
  3191     level = 1;
  3193     Accessible* parent = this;
  3194     while ((parent = parent->Parent())) {
  3195       roles::Role parentRole = parent->Role();
  3197       if (parentRole == roles::OUTLINE)
  3198         break;
  3199       if (parentRole == roles::GROUPING)
  3200         ++ level;
  3204   } else if (role == roles::LISTITEM) {
  3205     // Expose 'level' attribute on nested lists. We support two hierarchies:
  3206     // a) list -> listitem -> list -> listitem (nested list is a last child
  3207     //   of listitem of the parent list);
  3208     // b) list -> listitem -> group -> listitem (nested listitems are contained
  3209     //   by group that is a last child of the parent listitem).
  3211     // Calculate 'level' attribute based on number of parent listitems.
  3212     level = 0;
  3213     Accessible* parent = this;
  3214     while ((parent = parent->Parent())) {
  3215       roles::Role parentRole = parent->Role();
  3217       if (parentRole == roles::LISTITEM)
  3218         ++ level;
  3219       else if (parentRole != roles::LIST && parentRole != roles::GROUPING)
  3220         break;
  3223     if (level == 0) {
  3224       // If this listitem is on top of nested lists then expose 'level'
  3225       // attribute.
  3226       parent = Parent();
  3227       uint32_t siblingCount = parent->ChildCount();
  3228       for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
  3229         Accessible* sibling = parent->GetChildAt(siblingIdx);
  3231         Accessible* siblingChild = sibling->LastChild();
  3232         if (siblingChild) {
  3233           roles::Role lastChildRole = siblingChild->Role();
  3234           if (lastChildRole == roles::LIST || lastChildRole == roles::GROUPING)
  3235             return 1;
  3238     } else {
  3239       ++ level; // level is 1-index based
  3243   return level;
  3246 void
  3247 Accessible::StaticAsserts() const
  3249   static_assert(eLastChildrenFlag <= (1 << kChildrenFlagsBits) - 1,
  3250                 "Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
  3251   static_assert(eLastStateFlag <= (1 << kStateFlagsBits) - 1,
  3252                 "Accessible::mStateFlags was oversized by eLastStateFlag!");
  3253   static_assert(eLastAccType <= (1 << kTypeBits) - 1,
  3254                 "Accessible::mType was oversized by eLastAccType!");
  3255   static_assert(eLastContextFlag <= (1 << kContextFlagsBits) - 1,
  3256                 "Accessible::mContextFlags was oversized by eLastContextFlag!");
  3257   static_assert(eLastAccGenericType <= (1 << kGenericTypesBits) - 1,
  3258                 "Accessible::mGenericType was oversized by eLastAccGenericType!");
  3262 ////////////////////////////////////////////////////////////////////////////////
  3263 // KeyBinding class
  3265 void
  3266 KeyBinding::ToPlatformFormat(nsAString& aValue) const
  3268   nsCOMPtr<nsIStringBundle> keyStringBundle;
  3269   nsCOMPtr<nsIStringBundleService> stringBundleService =
  3270       mozilla::services::GetStringBundleService();
  3271   if (stringBundleService)
  3272     stringBundleService->CreateBundle(
  3273       "chrome://global-platform/locale/platformKeys.properties",
  3274       getter_AddRefs(keyStringBundle));
  3276   if (!keyStringBundle)
  3277     return;
  3279   nsAutoString separator;
  3280   keyStringBundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"),
  3281                                      getter_Copies(separator));
  3283   nsAutoString modifierName;
  3284   if (mModifierMask & kControl) {
  3285     keyStringBundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"),
  3286                                        getter_Copies(modifierName));
  3288     aValue.Append(modifierName);
  3289     aValue.Append(separator);
  3292   if (mModifierMask & kAlt) {
  3293     keyStringBundle->GetStringFromName(MOZ_UTF16("VK_ALT"),
  3294                                        getter_Copies(modifierName));
  3296     aValue.Append(modifierName);
  3297     aValue.Append(separator);
  3300   if (mModifierMask & kShift) {
  3301     keyStringBundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"),
  3302                                        getter_Copies(modifierName));
  3304     aValue.Append(modifierName);
  3305     aValue.Append(separator);
  3308   if (mModifierMask & kMeta) {
  3309     keyStringBundle->GetStringFromName(MOZ_UTF16("VK_META"),
  3310                                        getter_Copies(modifierName));
  3312     aValue.Append(modifierName);
  3313     aValue.Append(separator);
  3316   aValue.Append(mKey);
  3319 void
  3320 KeyBinding::ToAtkFormat(nsAString& aValue) const
  3322   nsAutoString modifierName;
  3323   if (mModifierMask & kControl)
  3324     aValue.Append(NS_LITERAL_STRING("<Control>"));
  3326   if (mModifierMask & kAlt)
  3327     aValue.Append(NS_LITERAL_STRING("<Alt>"));
  3329   if (mModifierMask & kShift)
  3330     aValue.Append(NS_LITERAL_STRING("<Shift>"));
  3332   if (mModifierMask & kMeta)
  3333       aValue.Append(NS_LITERAL_STRING("<Meta>"));
  3335   aValue.Append(mKey);

mercurial