1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsAccUtils.h"
8 #include "Accessible-inl.h"
10 #include "nsAccessibilityService.h"
11 #include "nsCoreUtils.h"
12 #include "DocAccessible.h"
13 #include "HyperTextAccessible.h"
14 #include "nsIAccessibleTypes.h"
17 #include "TextLeafAccessible.h"
19 #include "nsIDOMXULContainerElement.h"
20 #include "nsIPersistentProperties2.h"
21 #include "mozilla/dom/Element.h"
23 using namespace mozilla
;
24 using namespace mozilla::a11y
;
27 nsAccUtils::GetAccAttr(nsIPersistentProperties
*aAttributes
,
28 nsIAtom
*aAttrName
, nsAString
& aAttrValue
)
30 aAttrValue
.Truncate();
32 aAttributes
->GetStringProperty(nsAtomCString(aAttrName
), aAttrValue
);
36 nsAccUtils::SetAccAttr(nsIPersistentProperties
*aAttributes
,
37 nsIAtom
*aAttrName
, const nsAString
& aAttrValue
)
39 nsAutoString oldValue
;
40 nsAutoCString attrName
;
42 aAttributes
->SetStringProperty(nsAtomCString(aAttrName
), aAttrValue
, oldValue
);
46 nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties
*aAttributes
,
47 int32_t aLevel
, int32_t aSetSize
,
53 value
.AppendInt(aLevel
);
54 SetAccAttr(aAttributes
, nsGkAtoms::level
, value
);
57 if (aSetSize
&& aPosInSet
) {
59 value
.AppendInt(aPosInSet
);
60 SetAccAttr(aAttributes
, nsGkAtoms::posinset
, value
);
63 value
.AppendInt(aSetSize
);
64 SetAccAttr(aAttributes
, nsGkAtoms::setsize
, value
);
69 nsAccUtils::GetDefaultLevel(Accessible
* aAccessible
)
71 roles::Role role
= aAccessible
->Role();
73 if (role
== roles::OUTLINEITEM
)
76 if (role
== roles::ROW
) {
77 Accessible
* parent
= aAccessible
->Parent();
78 // It is a row inside flatten treegrid. Group level is always 1 until it
79 // is overriden by aria-level attribute.
80 if (parent
&& parent
->Role() == roles::TREE_TABLE
)
88 nsAccUtils::GetARIAOrDefaultLevel(Accessible
* aAccessible
)
91 nsCoreUtils::GetUIntAttr(aAccessible
->GetContent(),
92 nsGkAtoms::aria_level
, &level
);
97 return GetDefaultLevel(aAccessible
);
101 nsAccUtils::GetLevelForXULContainerItem(nsIContent
*aContent
)
103 nsCOMPtr
<nsIDOMXULContainerItemElement
> item(do_QueryInterface(aContent
));
107 nsCOMPtr
<nsIDOMXULContainerElement
> container
;
108 item
->GetParentContainer(getter_AddRefs(container
));
112 // Get level of the item.
117 nsCOMPtr
<nsIDOMXULContainerElement
> parentContainer
;
118 container
->GetParentContainer(getter_AddRefs(parentContainer
));
119 parentContainer
.swap(container
);
126 nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties
*aAttributes
,
127 nsIContent
*aStartContent
,
128 nsIContent
*aTopContent
)
130 nsAutoString live
, relevant
, busy
;
131 nsIContent
*ancestor
= aStartContent
;
134 // container-relevant attribute
135 if (relevant
.IsEmpty() &&
136 HasDefinedARIAToken(ancestor
, nsGkAtoms::aria_relevant
) &&
137 ancestor
->GetAttr(kNameSpaceID_None
, nsGkAtoms::aria_relevant
, relevant
))
138 SetAccAttr(aAttributes
, nsGkAtoms::containerRelevant
, relevant
);
140 // container-live, and container-live-role attributes
141 if (live
.IsEmpty()) {
142 nsRoleMapEntry
* role
= aria::GetRoleMap(ancestor
);
143 if (HasDefinedARIAToken(ancestor
, nsGkAtoms::aria_live
)) {
144 ancestor
->GetAttr(kNameSpaceID_None
, nsGkAtoms::aria_live
,
147 GetLiveAttrValue(role
->liveAttRule
, live
);
149 if (!live
.IsEmpty()) {
150 SetAccAttr(aAttributes
, nsGkAtoms::containerLive
, live
);
152 SetAccAttr(aAttributes
, nsGkAtoms::containerLiveRole
,
153 role
->ARIARoleString());
158 // container-atomic attribute
159 if (ancestor
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::aria_atomic
,
160 nsGkAtoms::_true
, eCaseMatters
)) {
161 SetAccAttr(aAttributes
, nsGkAtoms::containerAtomic
,
162 NS_LITERAL_STRING("true"));
165 // container-busy attribute
166 if (busy
.IsEmpty() &&
167 HasDefinedARIAToken(ancestor
, nsGkAtoms::aria_busy
) &&
168 ancestor
->GetAttr(kNameSpaceID_None
, nsGkAtoms::aria_busy
, busy
))
169 SetAccAttr(aAttributes
, nsGkAtoms::containerBusy
, busy
);
171 if (ancestor
== aTopContent
)
174 ancestor
= ancestor
->GetParent();
176 ancestor
= aTopContent
; // Use <body>/<frameset>
181 nsAccUtils::HasDefinedARIAToken(nsIContent
*aContent
, nsIAtom
*aAtom
)
183 NS_ASSERTION(aContent
, "aContent is null in call to HasDefinedARIAToken!");
185 if (!aContent
->HasAttr(kNameSpaceID_None
, aAtom
) ||
186 aContent
->AttrValueIs(kNameSpaceID_None
, aAtom
,
187 nsGkAtoms::_empty
, eCaseMatters
) ||
188 aContent
->AttrValueIs(kNameSpaceID_None
, aAtom
,
189 nsGkAtoms::_undefined
, eCaseMatters
)) {
196 nsAccUtils::GetARIAToken(dom::Element
* aElement
, nsIAtom
* aAttr
)
198 if (!HasDefinedARIAToken(aElement
, aAttr
))
199 return nsGkAtoms::_empty
;
201 static nsIContent::AttrValuesArray tokens
[] =
202 { &nsGkAtoms::_false
, &nsGkAtoms::_true
,
203 &nsGkAtoms::mixed
, nullptr};
205 int32_t idx
= aElement
->FindAttrValueIn(kNameSpaceID_None
,
206 aAttr
, tokens
, eCaseMatters
);
208 return *(tokens
[idx
]);
214 nsAccUtils::GetSelectableContainer(Accessible
* aAccessible
, uint64_t aState
)
219 if (!(aState
& states::SELECTABLE
))
222 Accessible
* parent
= aAccessible
;
223 while ((parent
= parent
->Parent()) && !parent
->IsSelect()) {
224 if (parent
->Role() == roles::PANE
)
231 nsAccUtils::IsARIASelected(Accessible
* aAccessible
)
233 return aAccessible
->GetContent()->
234 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::aria_selected
,
235 nsGkAtoms::_true
, eCaseMatters
);
239 nsAccUtils::GetTextContainer(nsINode
* aNode
)
241 // Get text accessible containing the result node.
243 GetAccService()->GetDocAccessible(aNode
->OwnerDoc());
244 Accessible
* accessible
=
245 doc
? doc
->GetAccessibleOrContainer(aNode
) : nullptr;
250 HyperTextAccessible
* textAcc
= accessible
->AsHyperText();
254 accessible
= accessible
->Parent();
255 } while (accessible
);
261 nsAccUtils::ConvertToScreenCoords(int32_t aX
, int32_t aY
,
262 uint32_t aCoordinateType
,
263 Accessible
* aAccessible
)
265 nsIntPoint
coords(aX
, aY
);
267 switch (aCoordinateType
) {
268 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
:
271 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
:
273 coords
+= nsCoreUtils::GetScreenCoordsForWindow(aAccessible
->GetNode());
277 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE
:
279 coords
+= GetScreenCoordsForParent(aAccessible
);
284 NS_NOTREACHED("invalid coord type!");
291 nsAccUtils::ConvertScreenCoordsTo(int32_t *aX
, int32_t *aY
,
292 uint32_t aCoordinateType
,
293 Accessible
* aAccessible
)
295 switch (aCoordinateType
) {
296 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
:
299 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
:
301 nsIntPoint coords
= nsCoreUtils::GetScreenCoordsForWindow(aAccessible
->GetNode());
307 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE
:
309 nsIntPoint coords
= GetScreenCoordsForParent(aAccessible
);
316 NS_NOTREACHED("invalid coord type!");
321 nsAccUtils::GetScreenCoordsForParent(Accessible
* aAccessible
)
323 Accessible
* parent
= aAccessible
->Parent();
325 return nsIntPoint(0, 0);
327 nsIFrame
*parentFrame
= parent
->GetFrame();
329 return nsIntPoint(0, 0);
331 nsRect rect
= parentFrame
->GetScreenRectInAppUnits();
332 return nsPoint(rect
.x
, rect
.y
).
333 ToNearestPixels(parentFrame
->PresContext()->AppUnitsPerDevPixel());
337 nsAccUtils::GetLiveAttrValue(uint32_t aRule
, nsAString
& aValue
)
341 aValue
= NS_LITERAL_STRING("off");
343 case ePoliteLiveAttr
:
344 aValue
= NS_LITERAL_STRING("polite");
354 nsAccUtils::IsTextInterfaceSupportCorrect(Accessible
* aAccessible
)
356 // Don't test for accessible docs, it makes us create accessibles too
357 // early and fire mutation events before we need to
358 if (aAccessible
->IsDoc())
361 bool foundText
= false;
362 uint32_t childCount
= aAccessible
->ChildCount();
363 for (uint32_t childIdx
= 0; childIdx
< childCount
; childIdx
++) {
364 Accessible
* child
= aAccessible
->GetChildAt(childIdx
);
365 if (!IsEmbeddedObject(child
)) {
371 return !foundText
|| aAccessible
->IsHyperText();
376 nsAccUtils::TextLength(Accessible
* aAccessible
)
378 if (IsEmbeddedObject(aAccessible
))
381 TextLeafAccessible
* textLeaf
= aAccessible
->AsTextLeaf();
383 return textLeaf
->Text().Length();
385 // For list bullets (or anything other accessible which would compute its own
386 // text. They don't have their own frame.
387 // XXX In the future, list bullets may have frame and anon content, so
388 // we should be able to remove this at that point
390 aAccessible
->AppendTextTo(text
); // Get all the text
391 return text
.Length();
395 nsAccUtils::MustPrune(Accessible
* aAccessible
)
397 roles::Role role
= aAccessible
->Role();
399 // Don't prune the tree for certain roles if the tree is more complex than
400 // a single text leaf.
402 (role
== roles::MENUITEM
||
403 role
== roles::COMBOBOX_OPTION
||
404 role
== roles::OPTION
||
405 role
== roles::ENTRY
||
406 role
== roles::FLAT_EQUATION
||
407 role
== roles::PASSWORD_TEXT
||
408 role
== roles::PUSHBUTTON
||
409 role
== roles::TOGGLE_BUTTON
||
410 role
== roles::GRAPHIC
||
411 role
== roles::SLIDER
||
412 role
== roles::PROGRESSBAR
||
413 role
== roles::SEPARATOR
) &&
414 aAccessible
->ContentChildCount() == 1 &&
415 aAccessible
->ContentChildAt(0)->IsTextLeaf();