Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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)
29 {
30 aAttrValue.Truncate();
32 aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
33 }
35 void
36 nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
37 nsIAtom *aAttrName, const nsAString& aAttrValue)
38 {
39 nsAutoString oldValue;
40 nsAutoCString attrName;
42 aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
43 }
45 void
46 nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
47 int32_t aLevel, int32_t aSetSize,
48 int32_t aPosInSet)
49 {
50 nsAutoString value;
52 if (aLevel) {
53 value.AppendInt(aLevel);
54 SetAccAttr(aAttributes, nsGkAtoms::level, value);
55 }
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);
65 }
66 }
68 int32_t
69 nsAccUtils::GetDefaultLevel(Accessible* aAccessible)
70 {
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;
82 }
84 return 0;
85 }
87 int32_t
88 nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible)
89 {
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);
98 }
100 int32_t
101 nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
102 {
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);
120 }
122 return level;
123 }
125 void
126 nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
127 nsIContent *aStartContent,
128 nsIContent *aTopContent)
129 {
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);
148 }
149 if (!live.IsEmpty()) {
150 SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
151 if (role) {
152 SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
153 role->ARIARoleString());
154 }
155 }
156 }
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"));
163 }
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>
177 }
178 }
180 bool
181 nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
182 {
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;
191 }
192 return true;
193 }
195 nsIAtom*
196 nsAccUtils::GetARIAToken(dom::Element* aElement, nsIAtom* aAttr)
197 {
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;
211 }
213 Accessible*
214 nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
215 {
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;
226 }
227 return parent;
228 }
230 bool
231 nsAccUtils::IsARIASelected(Accessible* aAccessible)
232 {
233 return aAccessible->GetContent()->
234 AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
235 nsGkAtoms::_true, eCaseMatters);
236 }
238 HyperTextAccessible*
239 nsAccUtils::GetTextContainer(nsINode* aNode)
240 {
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;
258 }
260 nsIntPoint
261 nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
262 uint32_t aCoordinateType,
263 Accessible* aAccessible)
264 {
265 nsIntPoint coords(aX, aY);
267 switch (aCoordinateType) {
268 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
269 break;
271 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
272 {
273 coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
274 break;
275 }
277 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
278 {
279 coords += GetScreenCoordsForParent(aAccessible);
280 break;
281 }
283 default:
284 NS_NOTREACHED("invalid coord type!");
285 }
287 return coords;
288 }
290 void
291 nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
292 uint32_t aCoordinateType,
293 Accessible* aAccessible)
294 {
295 switch (aCoordinateType) {
296 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
297 break;
299 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
300 {
301 nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
302 *aX -= coords.x;
303 *aY -= coords.y;
304 break;
305 }
307 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
308 {
309 nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
310 *aX -= coords.x;
311 *aY -= coords.y;
312 break;
313 }
315 default:
316 NS_NOTREACHED("invalid coord type!");
317 }
318 }
320 nsIntPoint
321 nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
322 {
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());
334 }
336 bool
337 nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
338 {
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;
346 }
348 return false;
349 }
351 #ifdef DEBUG
353 bool
354 nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
355 {
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;
368 }
369 }
371 if (foundText) {
372 // found text child node
373 nsCOMPtr<nsIAccessibleText> text = do_QueryObject(aAccessible);
374 if (!text)
375 return false;
376 }
378 return true;
379 }
380 #endif
382 uint32_t
383 nsAccUtils::TextLength(Accessible* aAccessible)
384 {
385 if (IsEmbeddedObject(aAccessible))
386 return 1;
388 TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
389 if (textLeaf)
390 return textLeaf->Text().Length();
392 // For list bullets (or anything other accessible which would compute its own
393 // text. They don't have their own frame.
394 // XXX In the future, list bullets may have frame and anon content, so
395 // we should be able to remove this at that point
396 nsAutoString text;
397 aAccessible->AppendTextTo(text); // Get all the text
398 return text.Length();
399 }
401 bool
402 nsAccUtils::MustPrune(Accessible* aAccessible)
403 {
404 roles::Role role = aAccessible->Role();
406 // Don't prune the tree for certain roles if the tree is more complex than
407 // a single text leaf.
408 return
409 (role == roles::MENUITEM ||
410 role == roles::COMBOBOX_OPTION ||
411 role == roles::OPTION ||
412 role == roles::ENTRY ||
413 role == roles::FLAT_EQUATION ||
414 role == roles::PASSWORD_TEXT ||
415 role == roles::PUSHBUTTON ||
416 role == roles::TOGGLE_BUTTON ||
417 role == roles::GRAPHIC ||
418 role == roles::SLIDER ||
419 role == roles::PROGRESSBAR ||
420 role == roles::SEPARATOR) &&
421 aAccessible->ContentChildCount() == 1 &&
422 aAccessible->ContentChildAt(0)->IsTextLeaf();
423 }