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