Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / accessible / base / nsAccUtils.cpp
blobd709388a6f26f799ff3c264e82d015b27bd92eef
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"
9 #include "ARIAMap.h"
10 #include "nsAccessibilityService.h"
11 #include "nsCoreUtils.h"
12 #include "DocAccessible.h"
13 #include "HyperTextAccessible.h"
14 #include "nsIAccessibleTypes.h"
15 #include "Role.h"
16 #include "States.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;
26 void
27 nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes,
28 nsIAtom *aAttrName, nsAString& aAttrValue)
30 aAttrValue.Truncate();
32 aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
35 void
36 nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
37 nsIAtom *aAttrName, const nsAString& aAttrValue)
39 nsAutoString oldValue;
40 nsAutoCString attrName;
42 aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
45 void
46 nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
47 int32_t aLevel, int32_t aSetSize,
48 int32_t aPosInSet)
50 nsAutoString value;
52 if (aLevel) {
53 value.AppendInt(aLevel);
54 SetAccAttr(aAttributes, nsGkAtoms::level, value);
57 if (aSetSize && aPosInSet) {
58 value.Truncate();
59 value.AppendInt(aPosInSet);
60 SetAccAttr(aAttributes, nsGkAtoms::posinset, value);
62 value.Truncate();
63 value.AppendInt(aSetSize);
64 SetAccAttr(aAttributes, nsGkAtoms::setsize, value);
68 int32_t
69 nsAccUtils::GetDefaultLevel(Accessible* aAccessible)
71 roles::Role role = aAccessible->Role();
73 if (role == roles::OUTLINEITEM)
74 return 1;
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)
81 return 1;
84 return 0;
87 int32_t
88 nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible)
90 int32_t level = 0;
91 nsCoreUtils::GetUIntAttr(aAccessible->GetContent(),
92 nsGkAtoms::aria_level, &level);
94 if (level != 0)
95 return level;
97 return GetDefaultLevel(aAccessible);
100 int32_t
101 nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
103 nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
104 if (!item)
105 return 0;
107 nsCOMPtr<nsIDOMXULContainerElement> container;
108 item->GetParentContainer(getter_AddRefs(container));
109 if (!container)
110 return 0;
112 // Get level of the item.
113 int32_t level = -1;
114 while (container) {
115 level++;
117 nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
118 container->GetParentContainer(getter_AddRefs(parentContainer));
119 parentContainer.swap(container);
122 return level;
125 void
126 nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
127 nsIContent *aStartContent,
128 nsIContent *aTopContent)
130 nsAutoString live, relevant, busy;
131 nsIContent *ancestor = aStartContent;
132 while (ancestor) {
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,
145 live);
146 } else if (role) {
147 GetLiveAttrValue(role->liveAttRule, live);
149 if (!live.IsEmpty()) {
150 SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
151 if (role) {
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)
172 break;
174 ancestor = ancestor->GetParent();
175 if (!ancestor)
176 ancestor = aTopContent; // Use <body>/<frameset>
180 bool
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)) {
190 return false;
192 return true;
195 nsIAtom*
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);
207 if (idx >= 0)
208 return *(tokens[idx]);
210 return nullptr;
213 Accessible*
214 nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
216 if (!aAccessible)
217 return nullptr;
219 if (!(aState & states::SELECTABLE))
220 return nullptr;
222 Accessible* parent = aAccessible;
223 while ((parent = parent->Parent()) && !parent->IsSelect()) {
224 if (parent->Role() == roles::PANE)
225 return nullptr;
227 return parent;
230 bool
231 nsAccUtils::IsARIASelected(Accessible* aAccessible)
233 return aAccessible->GetContent()->
234 AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
235 nsGkAtoms::_true, eCaseMatters);
238 HyperTextAccessible*
239 nsAccUtils::GetTextContainer(nsINode* aNode)
241 // Get text accessible containing the result node.
242 DocAccessible* doc =
243 GetAccService()->GetDocAccessible(aNode->OwnerDoc());
244 Accessible* accessible =
245 doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
246 if (!accessible)
247 return nullptr;
249 do {
250 HyperTextAccessible* textAcc = accessible->AsHyperText();
251 if (textAcc)
252 return textAcc;
254 accessible = accessible->Parent();
255 } while (accessible);
257 return nullptr;
260 nsIntPoint
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:
269 break;
271 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
273 coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
274 break;
277 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
279 coords += GetScreenCoordsForParent(aAccessible);
280 break;
283 default:
284 NS_NOTREACHED("invalid coord type!");
287 return coords;
290 void
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:
297 break;
299 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
301 nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
302 *aX -= coords.x;
303 *aY -= coords.y;
304 break;
307 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
309 nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
310 *aX -= coords.x;
311 *aY -= coords.y;
312 break;
315 default:
316 NS_NOTREACHED("invalid coord type!");
320 nsIntPoint
321 nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
323 Accessible* parent = aAccessible->Parent();
324 if (!parent)
325 return nsIntPoint(0, 0);
327 nsIFrame *parentFrame = parent->GetFrame();
328 if (!parentFrame)
329 return nsIntPoint(0, 0);
331 nsRect rect = parentFrame->GetScreenRectInAppUnits();
332 return nsPoint(rect.x, rect.y).
333 ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel());
336 bool
337 nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
339 switch (aRule) {
340 case eOffLiveAttr:
341 aValue = NS_LITERAL_STRING("off");
342 return true;
343 case ePoliteLiveAttr:
344 aValue = NS_LITERAL_STRING("polite");
345 return true;
348 return false;
351 #ifdef DEBUG
353 bool
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())
359 return true;
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)) {
366 foundText = true;
367 break;
371 return !foundText || aAccessible->IsHyperText();
373 #endif
375 uint32_t
376 nsAccUtils::TextLength(Accessible* aAccessible)
378 if (IsEmbeddedObject(aAccessible))
379 return 1;
381 TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
382 if (textLeaf)
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
389 nsAutoString text;
390 aAccessible->AppendTextTo(text); // Get all the text
391 return text.Length();
394 bool
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.
401 return
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();