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/. */
5 
6 #include "Accessible-inl.h"
7 
8 #include "nsIXBLAccessible.h"
9 
10 #include "EmbeddedObjCollector.h"
11 #include "AccGroupInfo.h"
12 #include "AccIterator.h"
13 #include "nsAccUtils.h"
14 #include "nsAccessibilityService.h"
15 #include "ApplicationAccessible.h"
16 #include "NotificationController.h"
17 #include "nsEventShell.h"
18 #include "nsTextEquivUtils.h"
19 #include "DocAccessibleChild.h"
20 #include "EventTree.h"
21 #include "Relation.h"
22 #include "Role.h"
23 #include "RootAccessible.h"
24 #include "States.h"
25 #include "StyleInfo.h"
26 #include "TableAccessible.h"
27 #include "TableCellAccessible.h"
28 #include "TreeWalker.h"
29 
30 #include "nsIDOMElement.h"
31 #include "nsIDOMNodeFilter.h"
32 #include "nsIDOMHTMLElement.h"
33 #include "nsIDOMKeyEvent.h"
34 #include "nsIDOMTreeWalker.h"
35 #include "nsIDOMXULButtonElement.h"
36 #include "nsIDOMXULDocument.h"
37 #include "nsIDOMXULElement.h"
38 #include "nsIDOMXULLabelElement.h"
39 #include "nsIDOMXULSelectCntrlEl.h"
40 #include "nsIDOMXULSelectCntrlItemEl.h"
41 #include "nsPIDOMWindow.h"
42 
43 #include "nsIDocument.h"
44 #include "nsIContent.h"
45 #include "nsIForm.h"
46 #include "nsIFormControl.h"
47 
48 #include "nsDeckFrame.h"
49 #include "nsLayoutUtils.h"
50 #include "nsIPresShell.h"
51 #include "nsIStringBundle.h"
52 #include "nsPresContext.h"
53 #include "nsIFrame.h"
54 #include "nsView.h"
55 #include "nsIDocShellTreeItem.h"
56 #include "nsIScrollableFrame.h"
57 #include "nsFocusManager.h"
58 
59 #include "nsXPIDLString.h"
60 #include "nsUnicharUtils.h"
61 #include "nsReadableUtils.h"
62 #include "prdtoa.h"
63 #include "nsIAtom.h"
64 #include "nsIURI.h"
65 #include "nsArrayUtils.h"
66 #include "nsIMutableArray.h"
67 #include "nsIObserverService.h"
68 #include "nsIServiceManager.h"
69 #include "nsWhitespaceTokenizer.h"
70 #include "nsAttrName.h"
71 
72 #ifdef DEBUG
73 #include "nsIDOMCharacterData.h"
74 #endif
75 
76 #include "mozilla/Assertions.h"
77 #include "mozilla/BasicEvents.h"
78 #include "mozilla/EventStates.h"
79 #include "mozilla/FloatingPoint.h"
80 #include "mozilla/MouseEvents.h"
81 #include "mozilla/Unused.h"
82 #include "mozilla/Preferences.h"
83 #include "mozilla/dom/CanvasRenderingContext2D.h"
84 #include "mozilla/dom/Element.h"
85 #include "mozilla/dom/HTMLCanvasElement.h"
86 #include "mozilla/dom/HTMLBodyElement.h"
87 #include "mozilla/dom/TreeWalker.h"
88 
89 using namespace mozilla;
90 using namespace mozilla::a11y;
91 
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 // Accessible: nsISupports and cycle collection
95 
96 NS_IMPL_CYCLE_COLLECTION_CLASS(Accessible)
97 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Accessible)
98   tmp->Shutdown();
99 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
100 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Accessible)
101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent, mDoc)
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
103 
104 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
105   if (aIID.Equals(NS_GET_IID(Accessible)))
106     foundInterface = this;
107   else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,Accessible)108   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, Accessible)
109 NS_INTERFACE_MAP_END
110 
111 NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
112 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
113 
114 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
115   mContent(aContent), mDoc(aDoc),
116   mParent(nullptr), mIndexInParent(-1),
117   mRoleMapEntryIndex(aria::NO_ROLE_MAP_ENTRY_INDEX),
118   mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
119   mReorderEventTarget(false), mShowEventTarget(false), mHideEventTarget(false)
120 {
121   mBits.groupInfo = nullptr;
122   mInt.mIndexOfEmbeddedChild = -1;
123 }
124 
~Accessible()125 Accessible::~Accessible()
126 {
127   NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
128 }
129 
130 ENameValueFlag
Name(nsString & aName)131 Accessible::Name(nsString& aName)
132 {
133   aName.Truncate();
134 
135   if (!HasOwnContent())
136     return eNameOK;
137 
138   ARIAName(aName);
139   if (!aName.IsEmpty())
140     return eNameOK;
141 
142   nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
143   if (xblAccessible) {
144     xblAccessible->GetAccessibleName(aName);
145     if (!aName.IsEmpty())
146       return eNameOK;
147   }
148 
149   ENameValueFlag nameFlag = NativeName(aName);
150   if (!aName.IsEmpty())
151     return nameFlag;
152 
153   // In the end get the name from tooltip.
154   if (mContent->IsHTMLElement()) {
155     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
156       aName.CompressWhitespace();
157       return eNameFromTooltip;
158     }
159   } else if (mContent->IsXULElement()) {
160     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
161       aName.CompressWhitespace();
162       return eNameFromTooltip;
163     }
164   } else if (mContent->IsSVGElement()) {
165     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
166     // for processing, the user agent shall choose the first one.
167     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
168          childElm = childElm->GetNextSibling()) {
169       if (childElm->IsSVGElement(nsGkAtoms::desc)) {
170         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
171         return eNameFromTooltip;
172       }
173     }
174   }
175 
176   if (nameFlag != eNoNameOnPurpose)
177     aName.SetIsVoid(true);
178 
179   return nameFlag;
180 }
181 
182 void
Description(nsString & aDescription)183 Accessible::Description(nsString& aDescription)
184 {
185   // There are 4 conditions that make an accessible have no accDescription:
186   // 1. it's a text node; or
187   // 2. It has no DHTML describedby property
188   // 3. it doesn't have an accName; or
189   // 4. its title attribute already equals to its accName nsAutoString name;
190 
191   if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
192     return;
193 
194   nsTextEquivUtils::
195     GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
196                            aDescription);
197 
198   if (aDescription.IsEmpty()) {
199     NativeDescription(aDescription);
200 
201     if (aDescription.IsEmpty()) {
202       // Keep the Name() method logic.
203       if (mContent->IsHTMLElement()) {
204         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
205       } else if (mContent->IsXULElement()) {
206         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
207       } else if (mContent->IsSVGElement()) {
208         for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
209              childElm = childElm->GetNextSibling()) {
210           if (childElm->IsSVGElement(nsGkAtoms::desc)) {
211             nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
212                                                          &aDescription);
213             break;
214           }
215         }
216       }
217     }
218   }
219 
220   if (!aDescription.IsEmpty()) {
221     aDescription.CompressWhitespace();
222     nsAutoString name;
223     Name(name);
224     // Don't expose a description if it is the same as the name.
225     if (aDescription.Equals(name))
226       aDescription.Truncate();
227   }
228 }
229 
230 KeyBinding
AccessKey() const231 Accessible::AccessKey() const
232 {
233   if (!HasOwnContent())
234     return KeyBinding();
235 
236   uint32_t key = nsCoreUtils::GetAccessKeyFor(mContent);
237   if (!key && mContent->IsElement()) {
238     Accessible* label = nullptr;
239 
240     // Copy access key from label node.
241     if (mContent->IsHTMLElement()) {
242       // Unless it is labeled via an ancestor <label>, in which case that would
243       // be redundant.
244       HTMLLabelIterator iter(Document(), this,
245                              HTMLLabelIterator::eSkipAncestorLabel);
246       label = iter.Next();
247 
248     } else if (mContent->IsXULElement()) {
249       XULLabelIterator iter(Document(), mContent);
250       label = iter.Next();
251     }
252 
253     if (label)
254       key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
255   }
256 
257   if (!key)
258     return KeyBinding();
259 
260   // Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
261   switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
262   case -1:
263     break;
264   case nsIDOMKeyEvent::DOM_VK_SHIFT:
265     return KeyBinding(key, KeyBinding::kShift);
266   case nsIDOMKeyEvent::DOM_VK_CONTROL:
267     return KeyBinding(key, KeyBinding::kControl);
268   case nsIDOMKeyEvent::DOM_VK_ALT:
269     return KeyBinding(key, KeyBinding::kAlt);
270   case nsIDOMKeyEvent::DOM_VK_META:
271     return KeyBinding(key, KeyBinding::kMeta);
272   default:
273     return KeyBinding();
274   }
275 
276   // Determine the access modifier used in this context.
277   nsIDocument* document = mContent->GetUncomposedDoc();
278   if (!document)
279     return KeyBinding();
280 
281   nsCOMPtr<nsIDocShellTreeItem> treeItem(document->GetDocShell());
282   if (!treeItem)
283     return KeyBinding();
284 
285   nsresult rv = NS_ERROR_FAILURE;
286   int32_t modifierMask = 0;
287   switch (treeItem->ItemType()) {
288     case nsIDocShellTreeItem::typeChrome:
289       rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
290       break;
291     case nsIDocShellTreeItem::typeContent:
292       rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
293       break;
294   }
295 
296   return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
297 }
298 
299 KeyBinding
KeyboardShortcut() const300 Accessible::KeyboardShortcut() const
301 {
302   return KeyBinding();
303 }
304 
305 void
TranslateString(const nsString & aKey,nsAString & aStringOut)306 Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
307 {
308   nsCOMPtr<nsIStringBundleService> stringBundleService =
309     services::GetStringBundleService();
310   if (!stringBundleService)
311     return;
312 
313   nsCOMPtr<nsIStringBundle> stringBundle;
314   stringBundleService->CreateBundle(
315     "chrome://global-platform/locale/accessible.properties",
316     getter_AddRefs(stringBundle));
317   if (!stringBundle)
318     return;
319 
320   nsXPIDLString xsValue;
321   nsresult rv = stringBundle->GetStringFromName(aKey.get(), getter_Copies(xsValue));
322   if (NS_SUCCEEDED(rv))
323     aStringOut.Assign(xsValue);
324 }
325 
326 uint64_t
VisibilityState()327 Accessible::VisibilityState()
328 {
329   nsIFrame* frame = GetFrame();
330   if (!frame)
331     return states::INVISIBLE;
332 
333   // Walk the parent frame chain to see if there's invisible parent or the frame
334   // is in background tab.
335   if (!frame->StyleVisibility()->IsVisible())
336     return states::INVISIBLE;
337 
338   nsIFrame* curFrame = frame;
339   do {
340     nsView* view = curFrame->GetView();
341     if (view && view->GetVisibility() == nsViewVisibility_kHide)
342       return states::INVISIBLE;
343 
344     if (nsLayoutUtils::IsPopup(curFrame))
345       return 0;
346 
347     // Offscreen state for background tab content and invisible for not selected
348     // deck panel.
349     nsIFrame* parentFrame = curFrame->GetParent();
350     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
351     if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
352       if (deckFrame->GetContent()->IsXULElement(nsGkAtoms::tabpanels))
353         return states::OFFSCREEN;
354 
355       NS_NOTREACHED("Children of not selected deck panel are not accessible.");
356       return states::INVISIBLE;
357     }
358 
359     // If contained by scrollable frame then check that at least 12 pixels
360     // around the object is visible, otherwise the object is offscreen.
361     nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
362     if (scrollableFrame) {
363       nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
364       nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
365         frame, frame->GetRectRelativeToSelf(), parentFrame);
366       if (!scrollPortRect.Contains(frameRect)) {
367         const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
368         scrollPortRect.Deflate(kMinPixels, kMinPixels);
369         if (!scrollPortRect.Intersects(frameRect))
370           return states::OFFSCREEN;
371       }
372     }
373 
374     if (!parentFrame) {
375       parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
376       if (parentFrame && !parentFrame->StyleVisibility()->IsVisible())
377         return states::INVISIBLE;
378     }
379 
380     curFrame = parentFrame;
381   } while (curFrame);
382 
383   // Zero area rects can occur in the first frame of a multi-frame text flow,
384   // in which case the rendered text is not empty and the frame should not be
385   // marked invisible.
386   // XXX Can we just remove this check? Why do we need to mark empty
387   // text invisible?
388   if (frame->GetType() == nsGkAtoms::textFrame &&
389       !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
390       frame->GetRect().IsEmpty()) {
391     nsIFrame::RenderedText text = frame->GetRenderedText(0,
392         UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
393         nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
394     if (text.mString.IsEmpty()) {
395       return states::INVISIBLE;
396     }
397   }
398 
399   return 0;
400 }
401 
402 uint64_t
NativeState()403 Accessible::NativeState()
404 {
405   uint64_t state = 0;
406 
407   if (!IsInDocument())
408     state |= states::STALE;
409 
410   if (HasOwnContent() && mContent->IsElement()) {
411     EventStates elementState = mContent->AsElement()->State();
412 
413     if (elementState.HasState(NS_EVENT_STATE_INVALID))
414       state |= states::INVALID;
415 
416     if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
417       state |= states::REQUIRED;
418 
419     state |= NativeInteractiveState();
420     if (FocusMgr()->IsFocused(this))
421       state |= states::FOCUSED;
422   }
423 
424   // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
425   state |= VisibilityState();
426 
427   nsIFrame *frame = GetFrame();
428   if (frame) {
429     if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
430       state |= states::FLOATING;
431 
432     // XXX we should look at layout for non XUL box frames, but need to decide
433     // how that interacts with ARIA.
434     if (HasOwnContent() && mContent->IsXULElement() && frame->IsXULBoxFrame()) {
435       const nsStyleXUL* xulStyle = frame->StyleXUL();
436       if (xulStyle && frame->IsXULBoxFrame()) {
437         // In XUL all boxes are either vertical or horizontal
438         if (xulStyle->mBoxOrient == StyleBoxOrient::Vertical)
439           state |= states::VERTICAL;
440         else
441           state |= states::HORIZONTAL;
442       }
443     }
444   }
445 
446   // Check if a XUL element has the popup attribute (an attached popup menu).
447   if (HasOwnContent() && mContent->IsXULElement() &&
448       mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
449     state |= states::HASPOPUP;
450 
451   // Bypass the link states specialization for non links.
452   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
453   if (!roleMapEntry || roleMapEntry->roleRule == kUseNativeRole ||
454       roleMapEntry->role == roles::LINK)
455     state |= NativeLinkState();
456 
457   return state;
458 }
459 
460 uint64_t
NativeInteractiveState() const461 Accessible::NativeInteractiveState() const
462 {
463   if (!mContent->IsElement())
464     return 0;
465 
466   if (NativelyUnavailable())
467     return states::UNAVAILABLE;
468 
469   nsIFrame* frame = GetFrame();
470   if (frame && frame->IsFocusable())
471     return states::FOCUSABLE;
472 
473   return 0;
474 }
475 
476 uint64_t
NativeLinkState() const477 Accessible::NativeLinkState() const
478 {
479   return 0;
480 }
481 
482 bool
NativelyUnavailable() const483 Accessible::NativelyUnavailable() const
484 {
485   if (mContent->IsHTMLElement())
486     return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
487 
488   return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
489                                nsGkAtoms::_true, eCaseMatters);
490 }
491 
492 Accessible*
FocusedChild()493 Accessible::FocusedChild()
494 {
495   Accessible* focus = FocusMgr()->FocusedAccessible();
496   if (focus && (focus == this || focus->Parent() == this))
497     return focus;
498 
499   return nullptr;
500 }
501 
502 Accessible*
ChildAtPoint(int32_t aX,int32_t aY,EWhichChildAtPoint aWhichChild)503 Accessible::ChildAtPoint(int32_t aX, int32_t aY,
504                          EWhichChildAtPoint aWhichChild)
505 {
506   // If we can't find the point in a child, we will return the fallback answer:
507   // we return |this| if the point is within it, otherwise nullptr.
508   Accessible* fallbackAnswer = nullptr;
509   nsIntRect rect = Bounds();
510   if (aX >= rect.x && aX < rect.x + rect.width &&
511       aY >= rect.y && aY < rect.y + rect.height)
512     fallbackAnswer = this;
513 
514   if (nsAccUtils::MustPrune(this))  // Do not dig any further
515     return fallbackAnswer;
516 
517   // Search an accessible at the given point starting from accessible document
518   // because containing block (see CSS2) for out of flow element (for example,
519   // absolutely positioned element) may be different from its DOM parent and
520   // therefore accessible for containing block may be different from accessible
521   // for DOM parent but GetFrameForPoint() should be called for containing block
522   // to get an out of flow element.
523   DocAccessible* accDocument = Document();
524   NS_ENSURE_TRUE(accDocument, nullptr);
525 
526   nsIFrame* rootFrame = accDocument->GetFrame();
527   NS_ENSURE_TRUE(rootFrame, nullptr);
528 
529   nsIFrame* startFrame = rootFrame;
530 
531   // Check whether the point is at popup content.
532   nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
533   NS_ENSURE_TRUE(rootWidget, nullptr);
534 
535   LayoutDeviceIntRect rootRect = rootWidget->GetScreenBounds();
536 
537   WidgetMouseEvent dummyEvent(true, eMouseMove, rootWidget,
538                               WidgetMouseEvent::eSynthesized);
539   dummyEvent.mRefPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
540 
541   nsIFrame* popupFrame = nsLayoutUtils::
542     GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
543                                      &dummyEvent);
544   if (popupFrame) {
545     // If 'this' accessible is not inside the popup then ignore the popup when
546     // searching an accessible at point.
547     DocAccessible* popupDoc =
548       GetAccService()->GetDocAccessible(popupFrame->GetContent()->OwnerDoc());
549     Accessible* popupAcc =
550       popupDoc->GetAccessibleOrContainer(popupFrame->GetContent());
551     Accessible* popupChild = this;
552     while (popupChild && !popupChild->IsDoc() && popupChild != popupAcc)
553       popupChild = popupChild->Parent();
554 
555     if (popupChild == popupAcc)
556       startFrame = popupFrame;
557   }
558 
559   nsPresContext* presContext = startFrame->PresContext();
560   nsRect screenRect = startFrame->GetScreenRectInAppUnits();
561     nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
562                    presContext->DevPixelsToAppUnits(aY) - screenRect.y);
563   nsIFrame* foundFrame = nsLayoutUtils::GetFrameForPoint(startFrame, offset);
564 
565   nsIContent* content = nullptr;
566   if (!foundFrame || !(content = foundFrame->GetContent()))
567     return fallbackAnswer;
568 
569   // Get accessible for the node with the point or the first accessible in
570   // the DOM parent chain.
571   DocAccessible* contentDocAcc = GetAccService()->
572     GetDocAccessible(content->OwnerDoc());
573 
574   // contentDocAcc in some circumstances can be nullptr. See bug 729861
575   NS_ASSERTION(contentDocAcc, "could not get the document accessible");
576   if (!contentDocAcc)
577     return fallbackAnswer;
578 
579   Accessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
580   if (!accessible)
581     return fallbackAnswer;
582 
583   // Hurray! We have an accessible for the frame that layout gave us.
584   // Since DOM node of obtained accessible may be out of flow then we should
585   // ensure obtained accessible is a child of this accessible.
586   Accessible* child = accessible;
587   while (child != this) {
588     Accessible* parent = child->Parent();
589     if (!parent) {
590       // Reached the top of the hierarchy. These bounds were inside an
591       // accessible that is not a descendant of this one.
592       return fallbackAnswer;
593     }
594 
595     // If we landed on a legitimate child of |this|, and we want the direct
596     // child, return it here.
597     if (parent == this && aWhichChild == eDirectChild)
598         return child;
599 
600     child = parent;
601   }
602 
603   // Manually walk through accessible children and see if the are within this
604   // point. Skip offscreen or invisible accessibles. This takes care of cases
605   // where layout won't walk into things for us, such as image map areas and
606   // sub documents (XXX: subdocuments should be handled by methods of
607   // OuterDocAccessibles).
608   uint32_t childCount = accessible->ChildCount();
609   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
610     Accessible* child = accessible->GetChildAt(childIdx);
611 
612     nsIntRect childRect = child->Bounds();
613     if (aX >= childRect.x && aX < childRect.x + childRect.width &&
614         aY >= childRect.y && aY < childRect.y + childRect.height &&
615         (child->State() & states::INVISIBLE) == 0) {
616 
617       if (aWhichChild == eDeepestChild)
618         return child->ChildAtPoint(aX, aY, eDeepestChild);
619 
620       return child;
621     }
622   }
623 
624   return accessible;
625 }
626 
627 nsRect
RelativeBounds(nsIFrame ** aBoundingFrame) const628 Accessible::RelativeBounds(nsIFrame** aBoundingFrame) const
629 {
630   nsIFrame* frame = GetFrame();
631   if (frame && mContent) {
632     bool* hasHitRegionRect = static_cast<bool*>(mContent->GetProperty(nsGkAtoms::hitregion));
633 
634     if (hasHitRegionRect && mContent->IsElement()) {
635       // This is for canvas fallback content
636       // Find a canvas frame the found hit region is relative to.
637       nsIFrame* canvasFrame = frame->GetParent();
638       if (canvasFrame) {
639         canvasFrame = nsLayoutUtils::GetClosestFrameOfType(canvasFrame, nsGkAtoms::HTMLCanvasFrame);
640       }
641 
642       // make the canvas the bounding frame
643       if (canvasFrame) {
644         *aBoundingFrame = canvasFrame;
645         dom::HTMLCanvasElement *canvas =
646           dom::HTMLCanvasElement::FromContent(canvasFrame->GetContent());
647 
648         // get the bounding rect of the hit region
649         nsRect bounds;
650         if (canvas && canvas->CountContexts() &&
651           canvas->GetContextAtIndex(0)->GetHitRegionRect(mContent->AsElement(), bounds)) {
652           return bounds;
653         }
654       }
655     }
656 
657     *aBoundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
658     return nsLayoutUtils::
659       GetAllInFlowRectsUnion(frame, *aBoundingFrame,
660                              nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
661   }
662 
663   return nsRect();
664 }
665 
666 nsIntRect
Bounds() const667 Accessible::Bounds() const
668 {
669   nsIFrame* boundingFrame = nullptr;
670   nsRect unionRectTwips = RelativeBounds(&boundingFrame);
671   if (!boundingFrame)
672     return nsIntRect();
673 
674   nsIntRect screenRect;
675   nsPresContext* presContext = mDoc->PresContext();
676   screenRect.x = presContext->AppUnitsToDevPixels(unionRectTwips.x);
677   screenRect.y = presContext->AppUnitsToDevPixels(unionRectTwips.y);
678   screenRect.width = presContext->AppUnitsToDevPixels(unionRectTwips.width);
679   screenRect.height = presContext->AppUnitsToDevPixels(unionRectTwips.height);
680 
681   // We have the union of the rectangle, now we need to put it in absolute
682   // screen coords.
683   nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
684     ToNearestPixels(presContext->AppUnitsPerDevPixel());
685   screenRect.x += orgRectPixels.x;
686   screenRect.y += orgRectPixels.y;
687 
688   return screenRect;
689 }
690 
691 void
SetSelected(bool aSelect)692 Accessible::SetSelected(bool aSelect)
693 {
694   if (!HasOwnContent())
695     return;
696 
697   Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
698   if (select) {
699     if (select->State() & states::MULTISELECTABLE) {
700       if (ARIARoleMap()) {
701         if (aSelect) {
702           mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
703                             NS_LITERAL_STRING("true"), true);
704         } else {
705           mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true);
706         }
707       }
708       return;
709     }
710 
711     if (aSelect)
712       TakeFocus();
713   }
714 }
715 
716 void
TakeSelection()717 Accessible::TakeSelection()
718 {
719   Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
720   if (select) {
721     if (select->State() & states::MULTISELECTABLE)
722       select->UnselectAll();
723     SetSelected(true);
724   }
725 }
726 
727 void
TakeFocus()728 Accessible::TakeFocus()
729 {
730   nsIFrame* frame = GetFrame();
731   if (!frame)
732     return;
733 
734   nsIContent* focusContent = mContent;
735 
736   // If the accessible focus is managed by container widget then focus the
737   // widget and set the accessible as its current item.
738   if (!frame->IsFocusable()) {
739     Accessible* widget = ContainerWidget();
740     if (widget && widget->AreItemsOperable()) {
741       nsIContent* widgetElm = widget->GetContent();
742       nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
743       if (widgetFrame && widgetFrame->IsFocusable()) {
744         focusContent = widgetElm;
745         widget->SetCurrentItem(this);
746       }
747     }
748   }
749 
750   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
751   nsFocusManager* fm = nsFocusManager::GetFocusManager();
752   if (fm)
753     fm->SetFocus(element, 0);
754 }
755 
756 void
XULElmName(DocAccessible * aDocument,nsIContent * aElm,nsString & aName)757 Accessible::XULElmName(DocAccessible* aDocument,
758                        nsIContent* aElm, nsString& aName)
759 {
760   /**
761    * 3 main cases for XUL Controls to be labeled
762    *   1 - control contains label="foo"
763    *   2 - control has, as a child, a label element
764    *        - label has either value="foo" or children
765    *   3 - non-child label contains control="controlID"
766    *        - label has either value="foo" or children
767    * Once a label is found, the search is discontinued, so a control
768    *  that has a label child as well as having a label external to
769    *  the control that uses the control="controlID" syntax will use
770    *  the child label for its Name.
771    */
772 
773   // CASE #1 (via label attribute) -- great majority of the cases
774   nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
775   if (labeledEl) {
776     labeledEl->GetLabel(aName);
777   } else {
778     nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
779     if (itemEl) {
780       itemEl->GetLabel(aName);
781     } else {
782       nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
783       // Use label if this is not a select control element which
784       // uses label attribute to indicate which option is selected
785       if (!select) {
786         nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
787         if (xulEl)
788           xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
789       }
790     }
791   }
792 
793   // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
794   if (aName.IsEmpty()) {
795     Accessible* labelAcc = nullptr;
796     XULLabelIterator iter(aDocument, aElm);
797     while ((labelAcc = iter.Next())) {
798       nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
799         do_QueryInterface(labelAcc->GetContent());
800       // Check if label's value attribute is used
801       if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(aName)) && aName.IsEmpty()) {
802         // If no value attribute, a non-empty label must contain
803         // children that define its text -- possibly using HTML
804         nsTextEquivUtils::
805           AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
806       }
807     }
808   }
809 
810   aName.CompressWhitespace();
811   if (!aName.IsEmpty())
812     return;
813 
814   // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
815   nsIContent *bindingParent = aElm->GetBindingParent();
816   nsIContent* parent =
817     bindingParent? bindingParent->GetParent() : aElm->GetParent();
818   nsAutoString ancestorTitle;
819   while (parent) {
820     if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
821         parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
822       // Before returning this, check if the element itself has a tooltip:
823       if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
824         aName.CompressWhitespace();
825         return;
826       }
827 
828       aName.Assign(ancestorTitle);
829       aName.CompressWhitespace();
830       return;
831     }
832     parent = parent->GetParent();
833   }
834 }
835 
836 nsresult
HandleAccEvent(AccEvent * aEvent)837 Accessible::HandleAccEvent(AccEvent* aEvent)
838 {
839   NS_ENSURE_ARG_POINTER(aEvent);
840 
841   if (IPCAccessibilityActive() && Document()) {
842     DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
843     MOZ_ASSERT(ipcDoc);
844     if (ipcDoc) {
845       uint64_t id = aEvent->GetAccessible()->IsDoc() ? 0 :
846         reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
847 
848       switch(aEvent->GetEventType()) {
849         case nsIAccessibleEvent::EVENT_SHOW:
850           ipcDoc->ShowEvent(downcast_accEvent(aEvent));
851           break;
852 
853         case nsIAccessibleEvent::EVENT_HIDE:
854           ipcDoc->SendHideEvent(id, aEvent->IsFromUserInput());
855           break;
856 
857         case nsIAccessibleEvent::EVENT_REORDER:
858           // reorder events on the application acc aren't necessary to tell the parent
859           // about new top level documents.
860           if (!aEvent->GetAccessible()->IsApplication())
861             ipcDoc->SendEvent(id, aEvent->GetEventType());
862           break;
863         case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
864           AccStateChangeEvent* event = downcast_accEvent(aEvent);
865           ipcDoc->SendStateChangeEvent(id, event->GetState(),
866                                        event->IsStateEnabled());
867           break;
868         }
869         case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
870           AccCaretMoveEvent* event = downcast_accEvent(aEvent);
871           ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset());
872           break;
873         }
874         case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
875         case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
876           AccTextChangeEvent* event = downcast_accEvent(aEvent);
877           ipcDoc->SendTextChangeEvent(id, event->ModifiedText(),
878                                       event->GetStartOffset(),
879                                       event->GetLength(),
880                                       event->IsTextInserted(),
881                                       event->IsFromUserInput());
882           break;
883         }
884         case nsIAccessibleEvent::EVENT_SELECTION:
885         case nsIAccessibleEvent::EVENT_SELECTION_ADD:
886         case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
887           AccSelChangeEvent* selEvent = downcast_accEvent(aEvent);
888           uint64_t widgetID = selEvent->Widget()->IsDoc() ? 0 :
889             reinterpret_cast<uintptr_t>(selEvent->Widget());
890           ipcDoc->SendSelectionEvent(id, widgetID, aEvent->GetEventType());
891           break;
892         }
893         default:
894           ipcDoc->SendEvent(id, aEvent->GetEventType());
895       }
896     }
897   }
898 
899   if (nsCoreUtils::AccEventObserversExist()) {
900     nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
901   }
902 
903   return NS_OK;
904 }
905 
906 already_AddRefed<nsIPersistentProperties>
Attributes()907 Accessible::Attributes()
908 {
909   nsCOMPtr<nsIPersistentProperties> attributes = NativeAttributes();
910   if (!HasOwnContent() || !mContent->IsElement())
911     return attributes.forget();
912 
913   // 'xml-roles' attribute for landmark.
914   nsIAtom* landmark = LandmarkRole();
915   if (landmark) {
916     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, landmark);
917 
918   } else {
919     // 'xml-roles' attribute coming from ARIA.
920     nsAutoString xmlRoles;
921     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles))
922       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles);
923   }
924 
925   // Expose object attributes from ARIA attributes.
926   nsAutoString unused;
927   aria::AttrIterator attribIter(mContent);
928   nsAutoString name, value;
929   while(attribIter.Next(name, value))
930     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
931 
932   if (IsARIAHidden()) {
933     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
934                            NS_LITERAL_STRING("true"));
935   }
936 
937   // If there is no aria-live attribute then expose default value of 'live'
938   // object attribute used for ARIA role of this accessible.
939   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
940   if (roleMapEntry) {
941     if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
942       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
943                              NS_LITERAL_STRING("search"));
944     }
945 
946     nsAutoString live;
947     nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
948     if (live.IsEmpty()) {
949       if (nsAccUtils::GetLiveAttrValue(roleMapEntry->liveAttRule, live))
950         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
951     }
952   }
953 
954   return attributes.forget();
955 }
956 
957 already_AddRefed<nsIPersistentProperties>
NativeAttributes()958 Accessible::NativeAttributes()
959 {
960   nsCOMPtr<nsIPersistentProperties> attributes =
961     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
962 
963   nsAutoString unused;
964 
965   // We support values, so expose the string value as well, via the valuetext
966   // object attribute. We test for the value interface because we don't want
967   // to expose traditional Value() information such as URL's on links and
968   // documents, or text in an input.
969   if (HasNumericValue()) {
970     nsAutoString valuetext;
971     Value(valuetext);
972     attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext,
973                                   unused);
974   }
975 
976   // Expose checkable object attribute if the accessible has checkable state
977   if (State() & states::CHECKABLE) {
978     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable,
979                            NS_LITERAL_STRING("true"));
980   }
981 
982   // Expose 'explicit-name' attribute.
983   nsAutoString name;
984   if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
985     attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
986                                   NS_LITERAL_STRING("true"), unused);
987   }
988 
989   // Group attributes (level/setsize/posinset)
990   GroupPos groupPos = GroupPosition();
991   nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
992                                groupPos.setSize, groupPos.posInSet);
993 
994   // If the accessible doesn't have own content (such as list item bullet or
995   // xul tree item) then don't calculate content based attributes.
996   if (!HasOwnContent())
997     return attributes.forget();
998 
999   nsEventShell::GetEventAttributes(GetNode(), attributes);
1000 
1001   // Get container-foo computed live region properties based on the closest
1002   // container with the live region attribute. Inner nodes override outer nodes
1003   // within the same document. The inner nodes can be used to override live
1004   // region behavior on more general outer nodes. However, nodes in outer
1005   // documents override nodes in inner documents: outer doc author may want to
1006   // override properties on a widget they used in an iframe.
1007   nsIContent* startContent = mContent;
1008   while (startContent) {
1009     nsIDocument* doc = startContent->GetComposedDoc();
1010     if (!doc)
1011       break;
1012 
1013     nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
1014                                            doc->GetRootElement());
1015 
1016     // Allow ARIA live region markup from outer documents to override
1017     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
1018     if (!docShellTreeItem)
1019       break;
1020 
1021     nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
1022     docShellTreeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
1023     if (!sameTypeParent || sameTypeParent == docShellTreeItem)
1024       break;
1025 
1026     nsIDocument* parentDoc = doc->GetParentDocument();
1027     if (!parentDoc)
1028       break;
1029 
1030     startContent = parentDoc->FindContentForSubDocument(doc);
1031   }
1032 
1033   if (!mContent->IsElement())
1034     return attributes.forget();
1035 
1036   nsAutoString id;
1037   if (nsCoreUtils::GetID(mContent, id))
1038     attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, unused);
1039 
1040   // Expose class because it may have useful microformat information.
1041   nsAutoString _class;
1042   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
1043     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::_class, _class);
1044 
1045   // Expose tag.
1046   nsAutoString tagName;
1047   mContent->NodeInfo()->GetName(tagName);
1048   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tag, tagName);
1049 
1050   // Expose draggable object attribute.
1051   nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
1052   if (htmlElement) {
1053     bool draggable = false;
1054     htmlElement->GetDraggable(&draggable);
1055     if (draggable) {
1056       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::draggable,
1057                              NS_LITERAL_STRING("true"));
1058     }
1059   }
1060 
1061   // Don't calculate CSS-based object attributes when no frame (i.e.
1062   // the accessible is unattached from the tree).
1063   if (!mContent->GetPrimaryFrame())
1064     return attributes.forget();
1065 
1066   // CSS style based object attributes.
1067   nsAutoString value;
1068   StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell());
1069 
1070   // Expose 'display' attribute.
1071   styleInfo.Display(value);
1072   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::display, value);
1073 
1074   // Expose 'text-align' attribute.
1075   styleInfo.TextAlign(value);
1076   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textAlign, value);
1077 
1078   // Expose 'text-indent' attribute.
1079   styleInfo.TextIndent(value);
1080   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textIndent, value);
1081 
1082   // Expose 'margin-left' attribute.
1083   styleInfo.MarginLeft(value);
1084   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginLeft, value);
1085 
1086   // Expose 'margin-right' attribute.
1087   styleInfo.MarginRight(value);
1088   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginRight, value);
1089 
1090   // Expose 'margin-top' attribute.
1091   styleInfo.MarginTop(value);
1092   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginTop, value);
1093 
1094   // Expose 'margin-bottom' attribute.
1095   styleInfo.MarginBottom(value);
1096   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginBottom, value);
1097 
1098   return attributes.forget();
1099 }
1100 
1101 GroupPos
GroupPosition()1102 Accessible::GroupPosition()
1103 {
1104   GroupPos groupPos;
1105   if (!HasOwnContent())
1106     return groupPos;
1107 
1108   // Get group position from ARIA attributes.
1109   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_level, &groupPos.level);
1110   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_setsize, &groupPos.setSize);
1111   nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_posinset, &groupPos.posInSet);
1112 
1113   // If ARIA is missed and the accessible is visible then calculate group
1114   // position from hierarchy.
1115   if (State() & states::INVISIBLE)
1116     return groupPos;
1117 
1118   // Calculate group level if ARIA is missed.
1119   if (groupPos.level == 0) {
1120     int32_t level = GetLevelInternal();
1121     if (level != 0)
1122       groupPos.level = level;
1123   }
1124 
1125   // Calculate position in group and group size if ARIA is missed.
1126   if (groupPos.posInSet == 0 || groupPos.setSize == 0) {
1127     int32_t posInSet = 0, setSize = 0;
1128     GetPositionAndSizeInternal(&posInSet, &setSize);
1129     if (posInSet != 0 && setSize != 0) {
1130       if (groupPos.posInSet == 0)
1131         groupPos.posInSet = posInSet;
1132 
1133       if (groupPos.setSize == 0)
1134         groupPos.setSize = setSize;
1135     }
1136   }
1137 
1138   return groupPos;
1139 }
1140 
1141 uint64_t
State()1142 Accessible::State()
1143 {
1144   if (IsDefunct())
1145     return states::DEFUNCT;
1146 
1147   uint64_t state = NativeState();
1148   // Apply ARIA states to be sure accessible states will be overridden.
1149   ApplyARIAState(&state);
1150 
1151   // If this is an ARIA item of the selectable widget and if it's focused and
1152   // not marked unselected explicitly (i.e. aria-selected="false") then expose
1153   // it as selected to make ARIA widget authors life easier.
1154   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1155   if (roleMapEntry && !(state & states::SELECTED) &&
1156       !mContent->AttrValueIs(kNameSpaceID_None,
1157                              nsGkAtoms::aria_selected,
1158                              nsGkAtoms::_false, eCaseMatters)) {
1159     // Special case for tabs: focused tab or focus inside related tab panel
1160     // implies selected state.
1161     if (roleMapEntry->role == roles::PAGETAB) {
1162       if (state & states::FOCUSED) {
1163         state |= states::SELECTED;
1164       } else {
1165         // If focus is in a child of the tab panel surely the tab is selected!
1166         Relation rel = RelationByType(RelationType::LABEL_FOR);
1167         Accessible* relTarget = nullptr;
1168         while ((relTarget = rel.Next())) {
1169           if (relTarget->Role() == roles::PROPERTYPAGE &&
1170               FocusMgr()->IsFocusWithin(relTarget))
1171             state |= states::SELECTED;
1172         }
1173       }
1174     } else if (state & states::FOCUSED) {
1175       Accessible* container = nsAccUtils::GetSelectableContainer(this, state);
1176       if (container &&
1177           !nsAccUtils::HasDefinedARIAToken(container->GetContent(),
1178                                            nsGkAtoms::aria_multiselectable)) {
1179         state |= states::SELECTED;
1180       }
1181     }
1182   }
1183 
1184   const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
1185   if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
1186     // Cannot be both expanded and collapsed -- this happens in ARIA expanded
1187     // combobox because of limitation of ARIAMap.
1188     // XXX: Perhaps we will be able to make this less hacky if we support
1189     // extended states in ARIAMap, e.g. derive COLLAPSED from
1190     // EXPANDABLE && !EXPANDED.
1191     state &= ~states::COLLAPSED;
1192   }
1193 
1194   if (!(state & states::UNAVAILABLE)) {
1195     state |= states::ENABLED | states::SENSITIVE;
1196 
1197     // If the object is a current item of container widget then mark it as
1198     // ACTIVE. This allows screen reader virtual buffer modes to know which
1199     // descendant is the current one that would get focus if the user navigates
1200     // to the container widget.
1201     Accessible* widget = ContainerWidget();
1202     if (widget && widget->CurrentItem() == this)
1203       state |= states::ACTIVE;
1204   }
1205 
1206   if ((state & states::COLLAPSED) || (state & states::EXPANDED))
1207     state |= states::EXPANDABLE;
1208 
1209   // For some reasons DOM node may have not a frame. We tract such accessibles
1210   // as invisible.
1211   nsIFrame *frame = GetFrame();
1212   if (!frame)
1213     return state;
1214 
1215   if (frame->StyleEffects()->mOpacity == 1.0f &&
1216       !(state & states::INVISIBLE)) {
1217     state |= states::OPAQUE1;
1218   }
1219 
1220   return state;
1221 }
1222 
1223 void
ApplyARIAState(uint64_t * aState) const1224 Accessible::ApplyARIAState(uint64_t* aState) const
1225 {
1226   if (!mContent->IsElement())
1227     return;
1228 
1229   dom::Element* element = mContent->AsElement();
1230 
1231   // Test for universal states first
1232   *aState |= aria::UniversalStatesFor(element);
1233 
1234   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1235   if (roleMapEntry) {
1236 
1237     // We only force the readonly bit off if we have a real mapping for the aria
1238     // role. This preserves the ability for screen readers to use readonly
1239     // (primarily on the document) as the hint for creating a virtual buffer.
1240     if (roleMapEntry->role != roles::NOTHING)
1241       *aState &= ~states::READONLY;
1242 
1243     if (mContent->HasID()) {
1244       // If has a role & ID and aria-activedescendant on the container, assume
1245       // focusable.
1246       const Accessible* ancestor = this;
1247       while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
1248         dom::Element* el = ancestor->Elm();
1249         if (el &&
1250             el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant)) {
1251           *aState |= states::FOCUSABLE;
1252           break;
1253         }
1254       }
1255     }
1256   }
1257 
1258   if (*aState & states::FOCUSABLE) {
1259     // Propogate aria-disabled from ancestors down to any focusable descendant.
1260     const Accessible* ancestor = this;
1261     while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
1262       dom::Element* el = ancestor->Elm();
1263       if (el && el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
1264                                 nsGkAtoms::_true, eCaseMatters)) {
1265         *aState |= states::UNAVAILABLE;
1266         break;
1267       }
1268     }
1269   }
1270 
1271   // special case: A native button element whose role got transformed by ARIA to a toggle button
1272   // Also applies to togglable button menus, like in the Dev Tools Web Console.
1273   if (IsButton() || IsMenuButton())
1274     aria::MapToState(aria::eARIAPressed, element, aState);
1275 
1276   if (!roleMapEntry)
1277     return;
1278 
1279   *aState |= roleMapEntry->state;
1280 
1281   if (aria::MapToState(roleMapEntry->attributeMap1, element, aState) &&
1282       aria::MapToState(roleMapEntry->attributeMap2, element, aState) &&
1283       aria::MapToState(roleMapEntry->attributeMap3, element, aState))
1284     aria::MapToState(roleMapEntry->attributeMap4, element, aState);
1285 
1286   // ARIA gridcell inherits editable/readonly states from the grid until it's
1287   // overridden.
1288   if ((roleMapEntry->Is(nsGkAtoms::gridcell) ||
1289        roleMapEntry->Is(nsGkAtoms::columnheader) ||
1290        roleMapEntry->Is(nsGkAtoms::rowheader)) &&
1291       !(*aState & (states::READONLY | states::EDITABLE))) {
1292     const TableCellAccessible* cell = AsTableCell();
1293     if (cell) {
1294       TableAccessible* table = cell->Table();
1295       if (table) {
1296         Accessible* grid = table->AsAccessible();
1297         uint64_t gridState = 0;
1298         grid->ApplyARIAState(&gridState);
1299         *aState |= (gridState & (states::READONLY | states::EDITABLE));
1300       }
1301     }
1302   }
1303 }
1304 
1305 void
Value(nsString & aValue)1306 Accessible::Value(nsString& aValue)
1307 {
1308   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1309   if (!roleMapEntry)
1310     return;
1311 
1312   if (roleMapEntry->valueRule != eNoValue) {
1313     // aria-valuenow is a number, and aria-valuetext is the optional text
1314     // equivalent. For the string value, we will try the optional text
1315     // equivalent first.
1316     if (!mContent->GetAttr(kNameSpaceID_None,
1317                            nsGkAtoms::aria_valuetext, aValue)) {
1318       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
1319                         aValue);
1320     }
1321     return;
1322   }
1323 
1324   // Value of textbox is a textified subtree.
1325   if (roleMapEntry->Is(nsGkAtoms::textbox)) {
1326     nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
1327     return;
1328   }
1329 
1330   // Value of combobox is a text of current or selected item.
1331   if (roleMapEntry->Is(nsGkAtoms::combobox)) {
1332     Accessible* option = CurrentItem();
1333     if (!option) {
1334       uint32_t childCount = ChildCount();
1335       for (uint32_t idx = 0; idx < childCount; idx++) {
1336         Accessible* child = mChildren.ElementAt(idx);
1337         if (child->IsListControl()) {
1338           option = child->GetSelectedItem(0);
1339           break;
1340         }
1341       }
1342     }
1343 
1344     if (option)
1345       nsTextEquivUtils::GetTextEquivFromSubtree(option, aValue);
1346   }
1347 }
1348 
1349 double
MaxValue() const1350 Accessible::MaxValue() const
1351 {
1352   return AttrNumericValue(nsGkAtoms::aria_valuemax);
1353 }
1354 
1355 double
MinValue() const1356 Accessible::MinValue() const
1357 {
1358   return AttrNumericValue(nsGkAtoms::aria_valuemin);
1359 }
1360 
1361 double
Step() const1362 Accessible::Step() const
1363 {
1364   return UnspecifiedNaN<double>(); // no mimimum increment (step) in ARIA.
1365 }
1366 
1367 double
CurValue() const1368 Accessible::CurValue() const
1369 {
1370   return AttrNumericValue(nsGkAtoms::aria_valuenow);
1371 }
1372 
1373 bool
SetCurValue(double aValue)1374 Accessible::SetCurValue(double aValue)
1375 {
1376   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1377   if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
1378     return false;
1379 
1380   const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE;
1381   if (State() & kValueCannotChange)
1382     return false;
1383 
1384   double checkValue = MinValue();
1385   if (!IsNaN(checkValue) && aValue < checkValue)
1386     return false;
1387 
1388   checkValue = MaxValue();
1389   if (!IsNaN(checkValue) && aValue > checkValue)
1390     return false;
1391 
1392   nsAutoString strValue;
1393   strValue.AppendFloat(aValue);
1394 
1395   return NS_SUCCEEDED(
1396     mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
1397 }
1398 
1399 role
ARIATransformRole(role aRole)1400 Accessible::ARIATransformRole(role aRole)
1401 {
1402   // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
1403   // where the accessible role depends on both the role and ARIA state.
1404   if (aRole == roles::PUSHBUTTON) {
1405     if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
1406       // For simplicity, any existing pressed attribute except "" or "undefined"
1407       // indicates a toggle.
1408       return roles::TOGGLE_BUTTON;
1409     }
1410 
1411     if (mContent->AttrValueIs(kNameSpaceID_None,
1412                               nsGkAtoms::aria_haspopup,
1413                               nsGkAtoms::_true,
1414                               eCaseMatters)) {
1415       // For button with aria-haspopup="true".
1416       return roles::BUTTONMENU;
1417     }
1418 
1419   } else if (aRole == roles::LISTBOX) {
1420     // A listbox inside of a combobox needs a special role because of ATK
1421     // mapping to menu.
1422     if (mParent && mParent->Role() == roles::COMBOBOX) {
1423       return roles::COMBOBOX_LIST;
1424     } else {
1425       // Listbox is owned by a combobox
1426       Relation rel = RelationByType(RelationType::NODE_CHILD_OF);
1427       Accessible* targetAcc = nullptr;
1428       while ((targetAcc = rel.Next()))
1429         if (targetAcc->Role() == roles::COMBOBOX)
1430           return roles::COMBOBOX_LIST;
1431     }
1432 
1433   } else if (aRole == roles::OPTION) {
1434     if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
1435       return roles::COMBOBOX_OPTION;
1436 
1437   } else if (aRole == roles::MENUITEM) {
1438     // Menuitem has a submenu.
1439     if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
1440                               nsGkAtoms::_true, eCaseMatters)) {
1441       return roles::PARENT_MENUITEM;
1442     }
1443   }
1444 
1445   return aRole;
1446 }
1447 
1448 nsIAtom*
LandmarkRole() const1449 Accessible::LandmarkRole() const
1450 {
1451   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1452   return roleMapEntry && roleMapEntry->IsOfType(eLandmark) ?
1453     *(roleMapEntry->roleAtom) : nullptr;
1454 }
1455 
1456 role
NativeRole()1457 Accessible::NativeRole()
1458 {
1459   return roles::NOTHING;
1460 }
1461 
1462 uint8_t
ActionCount()1463 Accessible::ActionCount()
1464 {
1465   return GetActionRule() == eNoAction ? 0 : 1;
1466 }
1467 
1468 void
ActionNameAt(uint8_t aIndex,nsAString & aName)1469 Accessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
1470 {
1471   aName.Truncate();
1472 
1473   if (aIndex != 0)
1474     return;
1475 
1476   uint32_t actionRule = GetActionRule();
1477 
1478  switch (actionRule) {
1479    case eActivateAction:
1480      aName.AssignLiteral("activate");
1481      return;
1482 
1483    case eClickAction:
1484      aName.AssignLiteral("click");
1485      return;
1486 
1487    case ePressAction:
1488      aName.AssignLiteral("press");
1489      return;
1490 
1491    case eCheckUncheckAction:
1492    {
1493      uint64_t state = State();
1494      if (state & states::CHECKED)
1495        aName.AssignLiteral("uncheck");
1496      else if (state & states::MIXED)
1497        aName.AssignLiteral("cycle");
1498      else
1499        aName.AssignLiteral("check");
1500      return;
1501    }
1502 
1503    case eJumpAction:
1504      aName.AssignLiteral("jump");
1505      return;
1506 
1507    case eOpenCloseAction:
1508      if (State() & states::COLLAPSED)
1509        aName.AssignLiteral("open");
1510      else
1511        aName.AssignLiteral("close");
1512      return;
1513 
1514    case eSelectAction:
1515      aName.AssignLiteral("select");
1516      return;
1517 
1518    case eSwitchAction:
1519      aName.AssignLiteral("switch");
1520      return;
1521 
1522    case eSortAction:
1523      aName.AssignLiteral("sort");
1524      return;
1525 
1526    case eExpandAction:
1527      if (State() & states::COLLAPSED)
1528        aName.AssignLiteral("expand");
1529      else
1530        aName.AssignLiteral("collapse");
1531      return;
1532   }
1533 }
1534 
1535 bool
DoAction(uint8_t aIndex)1536 Accessible::DoAction(uint8_t aIndex)
1537 {
1538   if (aIndex != 0)
1539     return false;
1540 
1541   if (GetActionRule() != eNoAction) {
1542     DoCommand();
1543     return true;
1544   }
1545 
1546   return false;
1547 }
1548 
1549 nsIContent*
GetAtomicRegion() const1550 Accessible::GetAtomicRegion() const
1551 {
1552   nsIContent *loopContent = mContent;
1553   nsAutoString atomic;
1554   while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
1555     loopContent = loopContent->GetParent();
1556 
1557   return atomic.EqualsLiteral("true") ? loopContent : nullptr;
1558 }
1559 
1560 Relation
RelationByType(RelationType aType)1561 Accessible::RelationByType(RelationType aType)
1562 {
1563   if (!HasOwnContent())
1564     return Relation();
1565 
1566   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
1567 
1568   // Relationships are defined on the same content node that the role would be
1569   // defined on.
1570   switch (aType) {
1571     case RelationType::LABELLED_BY: {
1572       Relation rel(new IDRefsIterator(mDoc, mContent,
1573                                       nsGkAtoms::aria_labelledby));
1574       if (mContent->IsHTMLElement()) {
1575         rel.AppendIter(new HTMLLabelIterator(Document(), this));
1576       } else if (mContent->IsXULElement()) {
1577         rel.AppendIter(new XULLabelIterator(Document(), mContent));
1578       }
1579 
1580       return rel;
1581     }
1582 
1583     case RelationType::LABEL_FOR: {
1584       Relation rel(new RelatedAccIterator(Document(), mContent,
1585                                           nsGkAtoms::aria_labelledby));
1586       if (mContent->IsXULElement(nsGkAtoms::label))
1587         rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
1588 
1589       return rel;
1590     }
1591 
1592     case RelationType::DESCRIBED_BY: {
1593       Relation rel(new IDRefsIterator(mDoc, mContent,
1594                                       nsGkAtoms::aria_describedby));
1595       if (mContent->IsXULElement())
1596         rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
1597 
1598       return rel;
1599     }
1600 
1601     case RelationType::DESCRIPTION_FOR: {
1602       Relation rel(new RelatedAccIterator(Document(), mContent,
1603                                           nsGkAtoms::aria_describedby));
1604 
1605       // This affectively adds an optional control attribute to xul:description,
1606       // which only affects accessibility, by allowing the description to be
1607       // tied to a control.
1608       if (mContent->IsXULElement(nsGkAtoms::description))
1609         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
1610                                           nsGkAtoms::control));
1611 
1612       return rel;
1613     }
1614 
1615     case RelationType::NODE_CHILD_OF: {
1616       Relation rel;
1617       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
1618       // get the parent the hard way.
1619       if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
1620                             roleMapEntry->role == roles::LISTITEM ||
1621                             roleMapEntry->role == roles::ROW)) {
1622         rel.AppendTarget(GetGroupInfo()->ConceptualParent());
1623       }
1624 
1625       // If accessible is in its own Window, or is the root of a document,
1626       // then we should provide NODE_CHILD_OF relation so that MSAA clients
1627       // can easily get to true parent instead of getting to oleacc's
1628       // ROLE_WINDOW accessible which will prevent us from going up further
1629       // (because it is system generated and has no idea about the hierarchy
1630       // above it).
1631       nsIFrame *frame = GetFrame();
1632       if (frame) {
1633         nsView *view = frame->GetView();
1634         if (view) {
1635           nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
1636           if (scrollFrame || view->GetWidget() || !frame->GetParent())
1637             rel.AppendTarget(Parent());
1638         }
1639       }
1640 
1641       return rel;
1642     }
1643 
1644     case RelationType::NODE_PARENT_OF: {
1645       // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
1646       // also can be organized by groups.
1647       if (roleMapEntry &&
1648           (roleMapEntry->role == roles::OUTLINEITEM ||
1649            roleMapEntry->role == roles::LISTITEM ||
1650            roleMapEntry->role == roles::ROW ||
1651            roleMapEntry->role == roles::OUTLINE ||
1652            roleMapEntry->role == roles::LIST ||
1653            roleMapEntry->role == roles::TREE_TABLE)) {
1654         return Relation(new ItemIterator(this));
1655       }
1656 
1657       return Relation();
1658     }
1659 
1660     case RelationType::CONTROLLED_BY:
1661       return Relation(new RelatedAccIterator(Document(), mContent,
1662                                              nsGkAtoms::aria_controls));
1663 
1664     case RelationType::CONTROLLER_FOR: {
1665       Relation rel(new IDRefsIterator(mDoc, mContent,
1666                                       nsGkAtoms::aria_controls));
1667       rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
1668       return rel;
1669     }
1670 
1671     case RelationType::FLOWS_TO:
1672       return Relation(new IDRefsIterator(mDoc, mContent,
1673                                          nsGkAtoms::aria_flowto));
1674 
1675     case RelationType::FLOWS_FROM:
1676       return Relation(new RelatedAccIterator(Document(), mContent,
1677                                              nsGkAtoms::aria_flowto));
1678 
1679     case RelationType::MEMBER_OF:
1680           return Relation(mDoc, GetAtomicRegion());
1681 
1682     case RelationType::SUBWINDOW_OF:
1683     case RelationType::EMBEDS:
1684     case RelationType::EMBEDDED_BY:
1685     case RelationType::POPUP_FOR:
1686     case RelationType::PARENT_WINDOW_OF:
1687       return Relation();
1688 
1689     case RelationType::DEFAULT_BUTTON: {
1690       if (mContent->IsHTMLElement()) {
1691         // HTML form controls implements nsIFormControl interface.
1692         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
1693         if (control) {
1694           nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
1695           if (form) {
1696             nsCOMPtr<nsIContent> formContent =
1697               do_QueryInterface(form->GetDefaultSubmitElement());
1698             return Relation(mDoc, formContent);
1699           }
1700         }
1701       } else {
1702         // In XUL, use first <button default="true" .../> in the document
1703         nsCOMPtr<nsIDOMXULDocument> xulDoc =
1704           do_QueryInterface(mContent->OwnerDoc());
1705         nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
1706         if (xulDoc) {
1707           nsCOMPtr<nsIDOMNodeList> possibleDefaultButtons;
1708           xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
1709                                          NS_LITERAL_STRING("true"),
1710                                          getter_AddRefs(possibleDefaultButtons));
1711           if (possibleDefaultButtons) {
1712             uint32_t length;
1713             possibleDefaultButtons->GetLength(&length);
1714             nsCOMPtr<nsIDOMNode> possibleButton;
1715             // Check for button in list of default="true" elements
1716             for (uint32_t count = 0; count < length && !buttonEl; count ++) {
1717               possibleDefaultButtons->Item(count, getter_AddRefs(possibleButton));
1718               buttonEl = do_QueryInterface(possibleButton);
1719             }
1720           }
1721           if (!buttonEl) { // Check for anonymous accept button in <dialog>
1722             dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
1723             if (rootElm) {
1724               nsIContent* possibleButtonEl = rootElm->OwnerDoc()->
1725                 GetAnonymousElementByAttribute(rootElm, nsGkAtoms::_default,
1726                                                NS_LITERAL_STRING("true"));
1727               buttonEl = do_QueryInterface(possibleButtonEl);
1728             }
1729           }
1730           nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
1731           return Relation(mDoc, relatedContent);
1732         }
1733       }
1734       return Relation();
1735     }
1736 
1737     case RelationType::CONTAINING_DOCUMENT:
1738       return Relation(mDoc);
1739 
1740     case RelationType::CONTAINING_TAB_PANE: {
1741       nsCOMPtr<nsIDocShell> docShell =
1742         nsCoreUtils::GetDocShellFor(GetNode());
1743       if (docShell) {
1744         // Walk up the parent chain without crossing the boundary at which item
1745         // types change, preventing us from walking up out of tab content.
1746         nsCOMPtr<nsIDocShellTreeItem> root;
1747         docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
1748         if (root) {
1749           // If the item type is typeContent, we assume we are in browser tab
1750           // content. Note, this includes content such as about:addons,
1751           // for consistency.
1752           if (root->ItemType() == nsIDocShellTreeItem::typeContent) {
1753             return Relation(nsAccUtils::GetDocAccessibleFor(root));
1754           }
1755         }
1756       }
1757       return Relation();
1758     }
1759 
1760     case RelationType::CONTAINING_APPLICATION:
1761       return Relation(ApplicationAcc());
1762 
1763     case RelationType::DETAILS:
1764       return Relation(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_details));
1765 
1766     case RelationType::DETAILS_FOR:
1767       return Relation(new RelatedAccIterator(mDoc, mContent, nsGkAtoms::aria_details));
1768 
1769     case RelationType::ERRORMSG:
1770       return Relation(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_errormessage));
1771 
1772     case RelationType::ERRORMSG_FOR:
1773       return Relation(new RelatedAccIterator(mDoc, mContent, nsGkAtoms::aria_errormessage));
1774 
1775     default:
1776       return Relation();
1777   }
1778 }
1779 
1780 void
GetNativeInterface(void ** aNativeAccessible)1781 Accessible::GetNativeInterface(void** aNativeAccessible)
1782 {
1783 }
1784 
1785 void
DoCommand(nsIContent * aContent,uint32_t aActionIndex)1786 Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
1787 {
1788   class Runnable final : public mozilla::Runnable
1789   {
1790   public:
1791     Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
1792       mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
1793 
1794     NS_IMETHOD Run() override
1795     {
1796       if (mAcc)
1797         mAcc->DispatchClickEvent(mContent, mIdx);
1798 
1799       return NS_OK;
1800     }
1801 
1802     void Revoke()
1803     {
1804       mAcc = nullptr;
1805       mContent = nullptr;
1806     }
1807 
1808   private:
1809     RefPtr<Accessible> mAcc;
1810     nsCOMPtr<nsIContent> mContent;
1811     uint32_t mIdx;
1812   };
1813 
1814   nsIContent* content = aContent ? aContent : mContent.get();
1815   nsCOMPtr<nsIRunnable> runnable = new Runnable(this, content, aActionIndex);
1816   NS_DispatchToMainThread(runnable);
1817 }
1818 
1819 void
DispatchClickEvent(nsIContent * aContent,uint32_t aActionIndex)1820 Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex)
1821 {
1822   if (IsDefunct())
1823     return;
1824 
1825   nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
1826 
1827   // Scroll into view.
1828   presShell->ScrollContentIntoView(aContent,
1829                                    nsIPresShell::ScrollAxis(),
1830                                    nsIPresShell::ScrollAxis(),
1831                                    nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
1832 
1833   nsWeakFrame frame = aContent->GetPrimaryFrame();
1834   if (!frame)
1835     return;
1836 
1837   // Compute x and y coordinates.
1838   nsPoint point;
1839   nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
1840   if (!widget)
1841     return;
1842 
1843   nsSize size = frame->GetSize();
1844 
1845   RefPtr<nsPresContext> presContext = presShell->GetPresContext();
1846   int32_t x = presContext->AppUnitsToDevPixels(point.x + size.width / 2);
1847   int32_t y = presContext->AppUnitsToDevPixels(point.y + size.height / 2);
1848 
1849   // Simulate a touch interaction by dispatching touch events with mouse events.
1850   nsCoreUtils::DispatchTouchEvent(eTouchStart, x, y, aContent, frame,
1851                                   presShell, widget);
1852   nsCoreUtils::DispatchMouseEvent(eMouseDown, x, y, aContent, frame,
1853                                   presShell, widget);
1854   nsCoreUtils::DispatchTouchEvent(eTouchEnd, x, y, aContent, frame,
1855                                   presShell, widget);
1856   nsCoreUtils::DispatchMouseEvent(eMouseUp, x, y, aContent, frame,
1857                                   presShell, widget);
1858 }
1859 
1860 void
ScrollToPoint(uint32_t aCoordinateType,int32_t aX,int32_t aY)1861 Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
1862 {
1863   nsIFrame* frame = GetFrame();
1864   if (!frame)
1865     return;
1866 
1867   nsIntPoint coords =
1868     nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType, this);
1869 
1870   nsIFrame* parentFrame = frame;
1871   while ((parentFrame = parentFrame->GetParent()))
1872     nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
1873 }
1874 
1875 void
AppendTextTo(nsAString & aText,uint32_t aStartOffset,uint32_t aLength)1876 Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
1877                          uint32_t aLength)
1878 {
1879   // Return text representation of non-text accessible within hypertext
1880   // accessible. Text accessible overrides this method to return enclosed text.
1881   if (aStartOffset != 0 || aLength == 0)
1882     return;
1883 
1884   nsIFrame *frame = GetFrame();
1885   if (!frame)
1886     return;
1887 
1888   NS_ASSERTION(mParent,
1889                "Called on accessible unbound from tree. Result can be wrong.");
1890 
1891   if (frame->GetType() == nsGkAtoms::brFrame) {
1892     aText += kForcedNewLineChar;
1893   } else if (mParent && nsAccUtils::MustPrune(mParent)) {
1894     // Expose the embedded object accessible as imaginary embedded object
1895     // character if its parent hypertext accessible doesn't expose children to
1896     // AT.
1897     aText += kImaginaryEmbeddedObjectChar;
1898   } else {
1899     aText += kEmbeddedObjectChar;
1900   }
1901 }
1902 
1903 void
Shutdown()1904 Accessible::Shutdown()
1905 {
1906   // Mark the accessible as defunct, invalidate the child count and pointers to
1907   // other accessibles, also make sure none of its children point to this parent
1908   mStateFlags |= eIsDefunct;
1909 
1910   int32_t childCount = mChildren.Length();
1911   for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
1912     mChildren.ElementAt(childIdx)->UnbindFromParent();
1913   }
1914   mChildren.Clear();
1915 
1916   mEmbeddedObjCollector = nullptr;
1917 
1918   if (mParent)
1919     mParent->RemoveChild(this);
1920 
1921   mContent = nullptr;
1922   mDoc = nullptr;
1923   if (SelectionMgr() && SelectionMgr()->AccessibleWithCaret(nullptr) == this)
1924     SelectionMgr()->ResetCaretOffset();
1925 }
1926 
1927 // Accessible protected
1928 void
ARIAName(nsString & aName)1929 Accessible::ARIAName(nsString& aName)
1930 {
1931   // aria-labelledby now takes precedence over aria-label
1932   nsresult rv = nsTextEquivUtils::
1933     GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
1934   if (NS_SUCCEEDED(rv)) {
1935     aName.CompressWhitespace();
1936   }
1937 
1938   if (aName.IsEmpty() &&
1939       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
1940     aName.CompressWhitespace();
1941   }
1942 }
1943 
1944 // Accessible protected
1945 ENameValueFlag
NativeName(nsString & aName)1946 Accessible::NativeName(nsString& aName)
1947 {
1948   if (mContent->IsHTMLElement()) {
1949     Accessible* label = nullptr;
1950     HTMLLabelIterator iter(Document(), this);
1951     while ((label = iter.Next())) {
1952       nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
1953                                                    &aName);
1954       aName.CompressWhitespace();
1955     }
1956 
1957     if (!aName.IsEmpty())
1958       return eNameOK;
1959 
1960     nsTextEquivUtils::GetNameFromSubtree(this, aName);
1961     return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
1962   }
1963 
1964   if (mContent->IsXULElement()) {
1965     XULElmName(mDoc, mContent, aName);
1966     if (!aName.IsEmpty())
1967       return eNameOK;
1968 
1969     nsTextEquivUtils::GetNameFromSubtree(this, aName);
1970     return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
1971   }
1972 
1973   if (mContent->IsSVGElement()) {
1974     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
1975     // for processing, the user agent shall choose the first one.
1976     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
1977          childElm = childElm->GetNextSibling()) {
1978       if (childElm->IsSVGElement(nsGkAtoms::title)) {
1979         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
1980         return eNameOK;
1981       }
1982     }
1983   }
1984 
1985   return eNameOK;
1986 }
1987 
1988 // Accessible protected
1989 void
NativeDescription(nsString & aDescription)1990 Accessible::NativeDescription(nsString& aDescription)
1991 {
1992   bool isXUL = mContent->IsXULElement();
1993   if (isXUL) {
1994     // Try XUL <description control="[id]">description text</description>
1995     XULDescriptionIterator iter(Document(), mContent);
1996     Accessible* descr = nullptr;
1997     while ((descr = iter.Next())) {
1998       nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
1999                                                    &aDescription);
2000     }
2001   }
2002 }
2003 
2004 // Accessible protected
2005 void
BindToParent(Accessible * aParent,uint32_t aIndexInParent)2006 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
2007 {
2008   MOZ_ASSERT(aParent, "This method isn't used to set null parent");
2009   MOZ_ASSERT(!mParent, "The child was expected to be moved");
2010 
2011 #ifdef A11Y_LOG
2012   if (mParent) {
2013     logging::TreeInfo("BindToParent: stealing accessible", 0,
2014                       "old parent", mParent,
2015                       "new parent", aParent,
2016                       "child", this, nullptr);
2017   }
2018 #endif
2019 
2020   mParent = aParent;
2021   mIndexInParent = aIndexInParent;
2022 
2023   // Note: this is currently only used for richlistitems and their children.
2024   if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
2025     mContextFlags |= eHasNameDependentParent;
2026   else
2027     mContextFlags &= ~eHasNameDependentParent;
2028 
2029   if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
2030     SetARIAHidden(true);
2031 
2032   mContextFlags |=
2033     static_cast<uint32_t>((mParent->IsAlert() ||
2034                            mParent->IsInsideAlert())) & eInsideAlert;
2035 }
2036 
2037 // Accessible protected
2038 void
UnbindFromParent()2039 Accessible::UnbindFromParent()
2040 {
2041   mParent = nullptr;
2042   mIndexInParent = -1;
2043   mInt.mIndexOfEmbeddedChild = -1;
2044   if (IsProxy())
2045     MOZ_CRASH("this should never be called on proxy wrappers");
2046 
2047   delete mBits.groupInfo;
2048   mBits.groupInfo = nullptr;
2049   mContextFlags &= ~eHasNameDependentParent & ~eInsideAlert;
2050 }
2051 
2052 ////////////////////////////////////////////////////////////////////////////////
2053 // Accessible public methods
2054 
2055 RootAccessible*
RootAccessible() const2056 Accessible::RootAccessible() const
2057 {
2058   nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(GetNode());
2059   NS_ASSERTION(docShell, "No docshell for mContent");
2060   if (!docShell) {
2061     return nullptr;
2062   }
2063 
2064   nsCOMPtr<nsIDocShellTreeItem> root;
2065   docShell->GetRootTreeItem(getter_AddRefs(root));
2066   NS_ASSERTION(root, "No root content tree item");
2067   if (!root) {
2068     return nullptr;
2069   }
2070 
2071   DocAccessible* docAcc = nsAccUtils::GetDocAccessibleFor(root);
2072   return docAcc ? docAcc->AsRoot() : nullptr;
2073 }
2074 
2075 nsIFrame*
GetFrame() const2076 Accessible::GetFrame() const
2077 {
2078   return mContent ? mContent->GetPrimaryFrame() : nullptr;
2079 }
2080 
2081 nsINode*
GetNode() const2082 Accessible::GetNode() const
2083 {
2084   return mContent;
2085 }
2086 
2087 void
Language(nsAString & aLanguage)2088 Accessible::Language(nsAString& aLanguage)
2089 {
2090   aLanguage.Truncate();
2091 
2092   if (!mDoc)
2093     return;
2094 
2095   nsCoreUtils::GetLanguageFor(mContent, nullptr, aLanguage);
2096   if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
2097     mDoc->DocumentNode()->GetHeaderData(nsGkAtoms::headerContentLanguage,
2098                                         aLanguage);
2099   }
2100 }
2101 
2102 bool
InsertChildAt(uint32_t aIndex,Accessible * aChild)2103 Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
2104 {
2105   if (!aChild)
2106     return false;
2107 
2108   if (aIndex == mChildren.Length()) {
2109     if (!mChildren.AppendElement(aChild))
2110       return false;
2111 
2112   } else {
2113     if (!mChildren.InsertElementAt(aIndex, aChild))
2114       return false;
2115 
2116     MOZ_ASSERT(mStateFlags & eKidsMutating, "Illicit children change");
2117 
2118     for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
2119       mChildren[idx]->mIndexInParent = idx;
2120     }
2121   }
2122 
2123   if (aChild->IsText()) {
2124     mStateFlags |= eHasTextKids;
2125   }
2126 
2127   aChild->BindToParent(this, aIndex);
2128   return true;
2129 }
2130 
2131 bool
RemoveChild(Accessible * aChild)2132 Accessible::RemoveChild(Accessible* aChild)
2133 {
2134   if (!aChild)
2135     return false;
2136 
2137   if (aChild->mParent != this || aChild->mIndexInParent == -1)
2138     return false;
2139 
2140   MOZ_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() || aChild->IsDoc(),
2141              "Illicit children change");
2142 
2143   int32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
2144   if (mChildren.SafeElementAt(index) != aChild) {
2145     MOZ_ASSERT_UNREACHABLE("A wrong child index");
2146     index = mChildren.IndexOf(aChild);
2147     if (index == -1) {
2148       MOZ_ASSERT_UNREACHABLE("No child was found");
2149       return false;
2150     }
2151   }
2152 
2153   aChild->UnbindFromParent();
2154   mChildren.RemoveElementAt(index);
2155 
2156   for (uint32_t idx = index; idx < mChildren.Length(); idx++) {
2157     mChildren[idx]->mIndexInParent = idx;
2158   }
2159 
2160   return true;
2161 }
2162 
2163 void
MoveChild(uint32_t aNewIndex,Accessible * aChild)2164 Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
2165 {
2166   MOZ_ASSERT(aChild, "No child was given");
2167   MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
2168   MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
2169   MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
2170              "No move, same index");
2171   MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
2172 
2173   RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false);
2174   if (mDoc->Controller()->QueueMutationEvent(hideEvent)) {
2175     aChild->SetHideEventTarget(true);
2176   }
2177 
2178   mEmbeddedObjCollector = nullptr;
2179   mChildren.RemoveElementAt(aChild->mIndexInParent);
2180 
2181   uint32_t startIdx = aNewIndex, endIdx = aChild->mIndexInParent;
2182 
2183   // If the child is moved after its current position.
2184   if (static_cast<uint32_t>(aChild->mIndexInParent) < aNewIndex) {
2185     startIdx = aChild->mIndexInParent;
2186     if (aNewIndex == mChildren.Length() + 1) {
2187       // The child is moved to the end.
2188       mChildren.AppendElement(aChild);
2189       endIdx = mChildren.Length() - 1;
2190     }
2191     else {
2192       mChildren.InsertElementAt(aNewIndex - 1, aChild);
2193       endIdx = aNewIndex;
2194     }
2195   }
2196   else {
2197     // The child is moved prior its current position.
2198     mChildren.InsertElementAt(aNewIndex, aChild);
2199   }
2200 
2201   for (uint32_t idx = startIdx; idx <= endIdx; idx++) {
2202     mChildren[idx]->mIndexInParent = idx;
2203     mChildren[idx]->mStateFlags |= eGroupInfoDirty;
2204     mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
2205   }
2206 
2207   RefPtr<AccShowEvent> showEvent = new AccShowEvent(aChild);
2208   DebugOnly<bool> added = mDoc->Controller()->QueueMutationEvent(showEvent);
2209   MOZ_ASSERT(added);
2210   aChild->SetShowEventTarget(true);
2211 }
2212 
2213 Accessible*
GetChildAt(uint32_t aIndex) const2214 Accessible::GetChildAt(uint32_t aIndex) const
2215 {
2216   Accessible* child = mChildren.SafeElementAt(aIndex, nullptr);
2217   if (!child)
2218     return nullptr;
2219 
2220 #ifdef DEBUG
2221   Accessible* realParent = child->mParent;
2222   NS_ASSERTION(!realParent || realParent == this,
2223                "Two accessibles have the same first child accessible!");
2224 #endif
2225 
2226   return child;
2227 }
2228 
2229 uint32_t
ChildCount() const2230 Accessible::ChildCount() const
2231 {
2232   return mChildren.Length();
2233 }
2234 
2235 int32_t
IndexInParent() const2236 Accessible::IndexInParent() const
2237 {
2238   return mIndexInParent;
2239 }
2240 
2241 uint32_t
EmbeddedChildCount()2242 Accessible::EmbeddedChildCount()
2243 {
2244   if (mStateFlags & eHasTextKids) {
2245     if (!mEmbeddedObjCollector)
2246       mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
2247     return mEmbeddedObjCollector->Count();
2248   }
2249 
2250   return ChildCount();
2251 }
2252 
2253 Accessible*
GetEmbeddedChildAt(uint32_t aIndex)2254 Accessible::GetEmbeddedChildAt(uint32_t aIndex)
2255 {
2256   if (mStateFlags & eHasTextKids) {
2257     if (!mEmbeddedObjCollector)
2258       mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
2259     return mEmbeddedObjCollector.get() ?
2260       mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
2261   }
2262 
2263   return GetChildAt(aIndex);
2264 }
2265 
2266 int32_t
GetIndexOfEmbeddedChild(Accessible * aChild)2267 Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
2268 {
2269   if (mStateFlags & eHasTextKids) {
2270     if (!mEmbeddedObjCollector)
2271       mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
2272     return mEmbeddedObjCollector.get() ?
2273       mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
2274   }
2275 
2276   return GetIndexOf(aChild);
2277 }
2278 
2279 ////////////////////////////////////////////////////////////////////////////////
2280 // HyperLinkAccessible methods
2281 
2282 bool
IsLink()2283 Accessible::IsLink()
2284 {
2285   // Every embedded accessible within hypertext accessible implements
2286   // hyperlink interface.
2287   return mParent && mParent->IsHyperText() && !IsText();
2288 }
2289 
2290 uint32_t
StartOffset()2291 Accessible::StartOffset()
2292 {
2293   NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
2294 
2295   HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
2296   return hyperText ? hyperText->GetChildOffset(this) : 0;
2297 }
2298 
2299 uint32_t
EndOffset()2300 Accessible::EndOffset()
2301 {
2302   NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
2303 
2304   HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
2305   return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
2306 }
2307 
2308 uint32_t
AnchorCount()2309 Accessible::AnchorCount()
2310 {
2311   NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
2312   return 1;
2313 }
2314 
2315 Accessible*
AnchorAt(uint32_t aAnchorIndex)2316 Accessible::AnchorAt(uint32_t aAnchorIndex)
2317 {
2318   NS_PRECONDITION(IsLink(), "GetAnchor is called on not hyper link!");
2319   return aAnchorIndex == 0 ? this : nullptr;
2320 }
2321 
2322 already_AddRefed<nsIURI>
AnchorURIAt(uint32_t aAnchorIndex)2323 Accessible::AnchorURIAt(uint32_t aAnchorIndex)
2324 {
2325   NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
2326   return nullptr;
2327 }
2328 
2329 void
ToTextPoint(HyperTextAccessible ** aContainer,int32_t * aOffset,bool aIsBefore) const2330 Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
2331                         bool aIsBefore) const
2332 {
2333   if (IsHyperText()) {
2334     *aContainer = const_cast<Accessible*>(this)->AsHyperText();
2335     *aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount();
2336     return;
2337   }
2338 
2339   const Accessible* child = nullptr;
2340   const Accessible* parent = this;
2341   do {
2342     child = parent;
2343     parent = parent->Parent();
2344   } while (parent && !parent->IsHyperText());
2345 
2346   if (parent) {
2347     *aContainer = const_cast<Accessible*>(parent)->AsHyperText();
2348     *aOffset = (*aContainer)->GetChildOffset(
2349       child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
2350   }
2351 }
2352 
2353 
2354 ////////////////////////////////////////////////////////////////////////////////
2355 // SelectAccessible
2356 
2357 void
SelectedItems(nsTArray<Accessible * > * aItems)2358 Accessible::SelectedItems(nsTArray<Accessible*>* aItems)
2359 {
2360   AccIterator iter(this, filters::GetSelected);
2361   Accessible* selected = nullptr;
2362   while ((selected = iter.Next()))
2363     aItems->AppendElement(selected);
2364 }
2365 
2366 uint32_t
SelectedItemCount()2367 Accessible::SelectedItemCount()
2368 {
2369   uint32_t count = 0;
2370   AccIterator iter(this, filters::GetSelected);
2371   Accessible* selected = nullptr;
2372   while ((selected = iter.Next()))
2373     ++count;
2374 
2375   return count;
2376 }
2377 
2378 Accessible*
GetSelectedItem(uint32_t aIndex)2379 Accessible::GetSelectedItem(uint32_t aIndex)
2380 {
2381   AccIterator iter(this, filters::GetSelected);
2382   Accessible* selected = nullptr;
2383 
2384   uint32_t index = 0;
2385   while ((selected = iter.Next()) && index < aIndex)
2386     index++;
2387 
2388   return selected;
2389 }
2390 
2391 bool
IsItemSelected(uint32_t aIndex)2392 Accessible::IsItemSelected(uint32_t aIndex)
2393 {
2394   uint32_t index = 0;
2395   AccIterator iter(this, filters::GetSelectable);
2396   Accessible* selected = nullptr;
2397   while ((selected = iter.Next()) && index < aIndex)
2398     index++;
2399 
2400   return selected &&
2401     selected->State() & states::SELECTED;
2402 }
2403 
2404 bool
AddItemToSelection(uint32_t aIndex)2405 Accessible::AddItemToSelection(uint32_t aIndex)
2406 {
2407   uint32_t index = 0;
2408   AccIterator iter(this, filters::GetSelectable);
2409   Accessible* selected = nullptr;
2410   while ((selected = iter.Next()) && index < aIndex)
2411     index++;
2412 
2413   if (selected)
2414     selected->SetSelected(true);
2415 
2416   return static_cast<bool>(selected);
2417 }
2418 
2419 bool
RemoveItemFromSelection(uint32_t aIndex)2420 Accessible::RemoveItemFromSelection(uint32_t aIndex)
2421 {
2422   uint32_t index = 0;
2423   AccIterator iter(this, filters::GetSelectable);
2424   Accessible* selected = nullptr;
2425   while ((selected = iter.Next()) && index < aIndex)
2426     index++;
2427 
2428   if (selected)
2429     selected->SetSelected(false);
2430 
2431   return static_cast<bool>(selected);
2432 }
2433 
2434 bool
SelectAll()2435 Accessible::SelectAll()
2436 {
2437   bool success = false;
2438   Accessible* selectable = nullptr;
2439 
2440   AccIterator iter(this, filters::GetSelectable);
2441   while((selectable = iter.Next())) {
2442     success = true;
2443     selectable->SetSelected(true);
2444   }
2445   return success;
2446 }
2447 
2448 bool
UnselectAll()2449 Accessible::UnselectAll()
2450 {
2451   bool success = false;
2452   Accessible* selected = nullptr;
2453 
2454   AccIterator iter(this, filters::GetSelected);
2455   while ((selected = iter.Next())) {
2456     success = true;
2457     selected->SetSelected(false);
2458   }
2459   return success;
2460 }
2461 
2462 ////////////////////////////////////////////////////////////////////////////////
2463 // Widgets
2464 
2465 bool
IsWidget() const2466 Accessible::IsWidget() const
2467 {
2468   return false;
2469 }
2470 
2471 bool
IsActiveWidget() const2472 Accessible::IsActiveWidget() const
2473 {
2474   if (FocusMgr()->HasDOMFocus(mContent))
2475     return true;
2476 
2477   // If text entry of combobox widget has a focus then the combobox widget is
2478   // active.
2479   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
2480   if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::combobox)) {
2481     uint32_t childCount = ChildCount();
2482     for (uint32_t idx = 0; idx < childCount; idx++) {
2483       Accessible* child = mChildren.ElementAt(idx);
2484       if (child->Role() == roles::ENTRY)
2485         return FocusMgr()->HasDOMFocus(child->GetContent());
2486     }
2487   }
2488 
2489   return false;
2490 }
2491 
2492 bool
AreItemsOperable() const2493 Accessible::AreItemsOperable() const
2494 {
2495   return HasOwnContent() &&
2496     mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
2497 }
2498 
2499 Accessible*
CurrentItem()2500 Accessible::CurrentItem()
2501 {
2502   // Check for aria-activedescendant, which changes which element has focus.
2503   // For activedescendant, the ARIA spec does not require that the user agent
2504   // checks whether pointed node is actually a DOM descendant of the element
2505   // with the aria-activedescendant attribute.
2506   nsAutoString id;
2507   if (HasOwnContent() &&
2508       mContent->GetAttr(kNameSpaceID_None,
2509                         nsGkAtoms::aria_activedescendant, id)) {
2510     nsIDocument* DOMDoc = mContent->OwnerDoc();
2511     dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
2512     if (activeDescendantElm) {
2513       DocAccessible* document = Document();
2514       if (document)
2515         return document->GetAccessible(activeDescendantElm);
2516     }
2517   }
2518   return nullptr;
2519 }
2520 
2521 void
SetCurrentItem(Accessible * aItem)2522 Accessible::SetCurrentItem(Accessible* aItem)
2523 {
2524   nsIAtom* id = aItem->GetContent()->GetID();
2525   if (id) {
2526     nsAutoString idStr;
2527     id->ToString(idStr);
2528     mContent->SetAttr(kNameSpaceID_None,
2529                       nsGkAtoms::aria_activedescendant, idStr, true);
2530   }
2531 }
2532 
2533 Accessible*
ContainerWidget() const2534 Accessible::ContainerWidget() const
2535 {
2536   if (HasARIARole() && mContent->HasID()) {
2537     for (Accessible* parent = Parent(); parent; parent = parent->Parent()) {
2538       nsIContent* parentContent = parent->GetContent();
2539       if (parentContent &&
2540         parentContent->HasAttr(kNameSpaceID_None,
2541                                nsGkAtoms::aria_activedescendant)) {
2542         return parent;
2543       }
2544 
2545       // Don't cross DOM document boundaries.
2546       if (parent->IsDoc())
2547         break;
2548     }
2549   }
2550   return nullptr;
2551 }
2552 
2553 void
SetARIAHidden(bool aIsDefined)2554 Accessible::SetARIAHidden(bool aIsDefined)
2555 {
2556   if (aIsDefined)
2557     mContextFlags |= eARIAHidden;
2558   else
2559     mContextFlags &= ~eARIAHidden;
2560 
2561   uint32_t length = mChildren.Length();
2562   for (uint32_t i = 0; i < length; i++) {
2563     mChildren[i]->SetARIAHidden(aIsDefined);
2564   }
2565 }
2566 
2567 ////////////////////////////////////////////////////////////////////////////////
2568 // Accessible protected methods
2569 
2570 void
LastRelease()2571 Accessible::LastRelease()
2572 {
2573   // First cleanup if needed...
2574   if (mDoc) {
2575     Shutdown();
2576     NS_ASSERTION(!mDoc,
2577                  "A Shutdown() impl forgot to call its parent's Shutdown?");
2578   }
2579   // ... then die.
2580   delete this;
2581 }
2582 
2583 Accessible*
GetSiblingAtOffset(int32_t aOffset,nsresult * aError) const2584 Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
2585 {
2586   if (!mParent || mIndexInParent == -1) {
2587     if (aError)
2588       *aError = NS_ERROR_UNEXPECTED;
2589 
2590     return nullptr;
2591   }
2592 
2593   if (aError &&
2594       mIndexInParent + aOffset >= static_cast<int32_t>(mParent->ChildCount())) {
2595     *aError = NS_OK; // fail peacefully
2596     return nullptr;
2597   }
2598 
2599   Accessible* child = mParent->GetChildAt(mIndexInParent + aOffset);
2600   if (aError && !child)
2601     *aError = NS_ERROR_UNEXPECTED;
2602 
2603   return child;
2604 }
2605 
2606 double
AttrNumericValue(nsIAtom * aAttr) const2607 Accessible::AttrNumericValue(nsIAtom* aAttr) const
2608 {
2609   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
2610   if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
2611     return UnspecifiedNaN<double>();
2612 
2613   nsAutoString attrValue;
2614   if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
2615     return UnspecifiedNaN<double>();
2616 
2617   nsresult error = NS_OK;
2618   double value = attrValue.ToDouble(&error);
2619   return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
2620 }
2621 
2622 uint32_t
GetActionRule() const2623 Accessible::GetActionRule() const
2624 {
2625   if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
2626     return eNoAction;
2627 
2628   // Return "click" action on elements that have an attached popup menu.
2629   if (mContent->IsXULElement())
2630     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
2631       return eClickAction;
2632 
2633   // Has registered 'click' event handler.
2634   bool isOnclick = nsCoreUtils::HasClickListener(mContent);
2635 
2636   if (isOnclick)
2637     return eClickAction;
2638 
2639   // Get an action based on ARIA role.
2640   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
2641   if (roleMapEntry &&
2642       roleMapEntry->actionRule != eNoAction)
2643     return roleMapEntry->actionRule;
2644 
2645   // Get an action based on ARIA attribute.
2646   if (nsAccUtils::HasDefinedARIAToken(mContent,
2647                                       nsGkAtoms::aria_expanded))
2648     return eExpandAction;
2649 
2650   return eNoAction;
2651 }
2652 
2653 AccGroupInfo*
GetGroupInfo()2654 Accessible::GetGroupInfo()
2655 {
2656   if (IsProxy())
2657     MOZ_CRASH("This should never be called on proxy wrappers");
2658 
2659   if (mBits.groupInfo){
2660     if (HasDirtyGroupInfo()) {
2661       mBits.groupInfo->Update();
2662       mStateFlags &= ~eGroupInfoDirty;
2663     }
2664 
2665     return mBits.groupInfo;
2666   }
2667 
2668   mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
2669   return mBits.groupInfo;
2670 }
2671 
2672 void
GetPositionAndSizeInternal(int32_t * aPosInSet,int32_t * aSetSize)2673 Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
2674 {
2675   AccGroupInfo* groupInfo = GetGroupInfo();
2676   if (groupInfo) {
2677     *aPosInSet = groupInfo->PosInSet();
2678     *aSetSize = groupInfo->SetSize();
2679   }
2680 }
2681 
2682 int32_t
GetLevelInternal()2683 Accessible::GetLevelInternal()
2684 {
2685   int32_t level = nsAccUtils::GetDefaultLevel(this);
2686 
2687   if (!IsBoundToParent())
2688     return level;
2689 
2690   roles::Role role = Role();
2691   if (role == roles::OUTLINEITEM) {
2692     // Always expose 'level' attribute for 'outlineitem' accessible. The number
2693     // of nested 'grouping' accessibles containing 'outlineitem' accessible is
2694     // its level.
2695     level = 1;
2696 
2697     Accessible* parent = this;
2698     while ((parent = parent->Parent())) {
2699       roles::Role parentRole = parent->Role();
2700 
2701       if (parentRole == roles::OUTLINE)
2702         break;
2703       if (parentRole == roles::GROUPING)
2704         ++ level;
2705 
2706     }
2707 
2708   } else if (role == roles::LISTITEM) {
2709     // Expose 'level' attribute on nested lists. We support two hierarchies:
2710     // a) list -> listitem -> list -> listitem (nested list is a last child
2711     //   of listitem of the parent list);
2712     // b) list -> listitem -> group -> listitem (nested listitems are contained
2713     //   by group that is a last child of the parent listitem).
2714 
2715     // Calculate 'level' attribute based on number of parent listitems.
2716     level = 0;
2717     Accessible* parent = this;
2718     while ((parent = parent->Parent())) {
2719       roles::Role parentRole = parent->Role();
2720 
2721       if (parentRole == roles::LISTITEM)
2722         ++ level;
2723       else if (parentRole != roles::LIST && parentRole != roles::GROUPING)
2724         break;
2725     }
2726 
2727     if (level == 0) {
2728       // If this listitem is on top of nested lists then expose 'level'
2729       // attribute.
2730       parent = Parent();
2731       uint32_t siblingCount = parent->ChildCount();
2732       for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
2733         Accessible* sibling = parent->GetChildAt(siblingIdx);
2734 
2735         Accessible* siblingChild = sibling->LastChild();
2736         if (siblingChild) {
2737           roles::Role lastChildRole = siblingChild->Role();
2738           if (lastChildRole == roles::LIST || lastChildRole == roles::GROUPING)
2739             return 1;
2740         }
2741       }
2742     } else {
2743       ++ level; // level is 1-index based
2744     }
2745   }
2746 
2747   return level;
2748 }
2749 
2750 void
StaticAsserts() const2751 Accessible::StaticAsserts() const
2752 {
2753   static_assert(eLastStateFlag <= (1 << kStateFlagsBits) - 1,
2754                 "Accessible::mStateFlags was oversized by eLastStateFlag!");
2755   static_assert(eLastAccType <= (1 << kTypeBits) - 1,
2756                 "Accessible::mType was oversized by eLastAccType!");
2757   static_assert(eLastContextFlag <= (1 << kContextFlagsBits) - 1,
2758                 "Accessible::mContextFlags was oversized by eLastContextFlag!");
2759   static_assert(eLastAccGenericType <= (1 << kGenericTypesBits) - 1,
2760                 "Accessible::mGenericType was oversized by eLastAccGenericType!");
2761 }
2762 
2763 ////////////////////////////////////////////////////////////////////////////////
2764 // KeyBinding class
2765 
2766 // static
2767 uint32_t
AccelModifier()2768 KeyBinding::AccelModifier()
2769 {
2770   switch (WidgetInputEvent::AccelModifier()) {
2771     case MODIFIER_ALT:
2772       return kAlt;
2773     case MODIFIER_CONTROL:
2774       return kControl;
2775     case MODIFIER_META:
2776       return kMeta;
2777     case MODIFIER_OS:
2778       return kOS;
2779     default:
2780       MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()");
2781       return 0;
2782   }
2783 }
2784 
2785 void
ToPlatformFormat(nsAString & aValue) const2786 KeyBinding::ToPlatformFormat(nsAString& aValue) const
2787 {
2788   nsCOMPtr<nsIStringBundle> keyStringBundle;
2789   nsCOMPtr<nsIStringBundleService> stringBundleService =
2790       mozilla::services::GetStringBundleService();
2791   if (stringBundleService)
2792     stringBundleService->CreateBundle(
2793       "chrome://global-platform/locale/platformKeys.properties",
2794       getter_AddRefs(keyStringBundle));
2795 
2796   if (!keyStringBundle)
2797     return;
2798 
2799   nsAutoString separator;
2800   keyStringBundle->GetStringFromName(u"MODIFIER_SEPARATOR",
2801                                      getter_Copies(separator));
2802 
2803   nsAutoString modifierName;
2804   if (mModifierMask & kControl) {
2805     keyStringBundle->GetStringFromName(u"VK_CONTROL",
2806                                        getter_Copies(modifierName));
2807 
2808     aValue.Append(modifierName);
2809     aValue.Append(separator);
2810   }
2811 
2812   if (mModifierMask & kAlt) {
2813     keyStringBundle->GetStringFromName(u"VK_ALT",
2814                                        getter_Copies(modifierName));
2815 
2816     aValue.Append(modifierName);
2817     aValue.Append(separator);
2818   }
2819 
2820   if (mModifierMask & kShift) {
2821     keyStringBundle->GetStringFromName(u"VK_SHIFT",
2822                                        getter_Copies(modifierName));
2823 
2824     aValue.Append(modifierName);
2825     aValue.Append(separator);
2826   }
2827 
2828   if (mModifierMask & kMeta) {
2829     keyStringBundle->GetStringFromName(u"VK_META",
2830                                        getter_Copies(modifierName));
2831 
2832     aValue.Append(modifierName);
2833     aValue.Append(separator);
2834   }
2835 
2836   aValue.Append(mKey);
2837 }
2838 
2839 void
ToAtkFormat(nsAString & aValue) const2840 KeyBinding::ToAtkFormat(nsAString& aValue) const
2841 {
2842   nsAutoString modifierName;
2843   if (mModifierMask & kControl)
2844     aValue.AppendLiteral("<Control>");
2845 
2846   if (mModifierMask & kAlt)
2847     aValue.AppendLiteral("<Alt>");
2848 
2849   if (mModifierMask & kShift)
2850     aValue.AppendLiteral("<Shift>");
2851 
2852   if (mModifierMask & kMeta)
2853       aValue.AppendLiteral("<Meta>");
2854 
2855   aValue.Append(mKey);
2856 }
2857