accessible/src/base/nsAccUtils.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsAccUtils.h"
michael@0 7
michael@0 8 #include "Accessible-inl.h"
michael@0 9 #include "ARIAMap.h"
michael@0 10 #include "nsAccessibilityService.h"
michael@0 11 #include "nsCoreUtils.h"
michael@0 12 #include "DocAccessible.h"
michael@0 13 #include "HyperTextAccessible.h"
michael@0 14 #include "nsIAccessibleTypes.h"
michael@0 15 #include "Role.h"
michael@0 16 #include "States.h"
michael@0 17 #include "TextLeafAccessible.h"
michael@0 18
michael@0 19 #include "nsIDOMXULContainerElement.h"
michael@0 20 #include "nsIPersistentProperties2.h"
michael@0 21 #include "mozilla/dom/Element.h"
michael@0 22
michael@0 23 using namespace mozilla;
michael@0 24 using namespace mozilla::a11y;
michael@0 25
michael@0 26 void
michael@0 27 nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes,
michael@0 28 nsIAtom *aAttrName, nsAString& aAttrValue)
michael@0 29 {
michael@0 30 aAttrValue.Truncate();
michael@0 31
michael@0 32 aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
michael@0 33 }
michael@0 34
michael@0 35 void
michael@0 36 nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
michael@0 37 nsIAtom *aAttrName, const nsAString& aAttrValue)
michael@0 38 {
michael@0 39 nsAutoString oldValue;
michael@0 40 nsAutoCString attrName;
michael@0 41
michael@0 42 aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
michael@0 43 }
michael@0 44
michael@0 45 void
michael@0 46 nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
michael@0 47 int32_t aLevel, int32_t aSetSize,
michael@0 48 int32_t aPosInSet)
michael@0 49 {
michael@0 50 nsAutoString value;
michael@0 51
michael@0 52 if (aLevel) {
michael@0 53 value.AppendInt(aLevel);
michael@0 54 SetAccAttr(aAttributes, nsGkAtoms::level, value);
michael@0 55 }
michael@0 56
michael@0 57 if (aSetSize && aPosInSet) {
michael@0 58 value.Truncate();
michael@0 59 value.AppendInt(aPosInSet);
michael@0 60 SetAccAttr(aAttributes, nsGkAtoms::posinset, value);
michael@0 61
michael@0 62 value.Truncate();
michael@0 63 value.AppendInt(aSetSize);
michael@0 64 SetAccAttr(aAttributes, nsGkAtoms::setsize, value);
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 int32_t
michael@0 69 nsAccUtils::GetDefaultLevel(Accessible* aAccessible)
michael@0 70 {
michael@0 71 roles::Role role = aAccessible->Role();
michael@0 72
michael@0 73 if (role == roles::OUTLINEITEM)
michael@0 74 return 1;
michael@0 75
michael@0 76 if (role == roles::ROW) {
michael@0 77 Accessible* parent = aAccessible->Parent();
michael@0 78 // It is a row inside flatten treegrid. Group level is always 1 until it
michael@0 79 // is overriden by aria-level attribute.
michael@0 80 if (parent && parent->Role() == roles::TREE_TABLE)
michael@0 81 return 1;
michael@0 82 }
michael@0 83
michael@0 84 return 0;
michael@0 85 }
michael@0 86
michael@0 87 int32_t
michael@0 88 nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible)
michael@0 89 {
michael@0 90 int32_t level = 0;
michael@0 91 nsCoreUtils::GetUIntAttr(aAccessible->GetContent(),
michael@0 92 nsGkAtoms::aria_level, &level);
michael@0 93
michael@0 94 if (level != 0)
michael@0 95 return level;
michael@0 96
michael@0 97 return GetDefaultLevel(aAccessible);
michael@0 98 }
michael@0 99
michael@0 100 int32_t
michael@0 101 nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
michael@0 102 {
michael@0 103 nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
michael@0 104 if (!item)
michael@0 105 return 0;
michael@0 106
michael@0 107 nsCOMPtr<nsIDOMXULContainerElement> container;
michael@0 108 item->GetParentContainer(getter_AddRefs(container));
michael@0 109 if (!container)
michael@0 110 return 0;
michael@0 111
michael@0 112 // Get level of the item.
michael@0 113 int32_t level = -1;
michael@0 114 while (container) {
michael@0 115 level++;
michael@0 116
michael@0 117 nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
michael@0 118 container->GetParentContainer(getter_AddRefs(parentContainer));
michael@0 119 parentContainer.swap(container);
michael@0 120 }
michael@0 121
michael@0 122 return level;
michael@0 123 }
michael@0 124
michael@0 125 void
michael@0 126 nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
michael@0 127 nsIContent *aStartContent,
michael@0 128 nsIContent *aTopContent)
michael@0 129 {
michael@0 130 nsAutoString live, relevant, busy;
michael@0 131 nsIContent *ancestor = aStartContent;
michael@0 132 while (ancestor) {
michael@0 133
michael@0 134 // container-relevant attribute
michael@0 135 if (relevant.IsEmpty() &&
michael@0 136 HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
michael@0 137 ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant))
michael@0 138 SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
michael@0 139
michael@0 140 // container-live, and container-live-role attributes
michael@0 141 if (live.IsEmpty()) {
michael@0 142 nsRoleMapEntry* role = aria::GetRoleMap(ancestor);
michael@0 143 if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
michael@0 144 ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live,
michael@0 145 live);
michael@0 146 } else if (role) {
michael@0 147 GetLiveAttrValue(role->liveAttRule, live);
michael@0 148 }
michael@0 149 if (!live.IsEmpty()) {
michael@0 150 SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
michael@0 151 if (role) {
michael@0 152 SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
michael@0 153 role->ARIARoleString());
michael@0 154 }
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 // container-atomic attribute
michael@0 159 if (ancestor->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_atomic,
michael@0 160 nsGkAtoms::_true, eCaseMatters)) {
michael@0 161 SetAccAttr(aAttributes, nsGkAtoms::containerAtomic,
michael@0 162 NS_LITERAL_STRING("true"));
michael@0 163 }
michael@0 164
michael@0 165 // container-busy attribute
michael@0 166 if (busy.IsEmpty() &&
michael@0 167 HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
michael@0 168 ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
michael@0 169 SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
michael@0 170
michael@0 171 if (ancestor == aTopContent)
michael@0 172 break;
michael@0 173
michael@0 174 ancestor = ancestor->GetParent();
michael@0 175 if (!ancestor)
michael@0 176 ancestor = aTopContent; // Use <body>/<frameset>
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 bool
michael@0 181 nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
michael@0 182 {
michael@0 183 NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
michael@0 184
michael@0 185 if (!aContent->HasAttr(kNameSpaceID_None, aAtom) ||
michael@0 186 aContent->AttrValueIs(kNameSpaceID_None, aAtom,
michael@0 187 nsGkAtoms::_empty, eCaseMatters) ||
michael@0 188 aContent->AttrValueIs(kNameSpaceID_None, aAtom,
michael@0 189 nsGkAtoms::_undefined, eCaseMatters)) {
michael@0 190 return false;
michael@0 191 }
michael@0 192 return true;
michael@0 193 }
michael@0 194
michael@0 195 nsIAtom*
michael@0 196 nsAccUtils::GetARIAToken(dom::Element* aElement, nsIAtom* aAttr)
michael@0 197 {
michael@0 198 if (!HasDefinedARIAToken(aElement, aAttr))
michael@0 199 return nsGkAtoms::_empty;
michael@0 200
michael@0 201 static nsIContent::AttrValuesArray tokens[] =
michael@0 202 { &nsGkAtoms::_false, &nsGkAtoms::_true,
michael@0 203 &nsGkAtoms::mixed, nullptr};
michael@0 204
michael@0 205 int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
michael@0 206 aAttr, tokens, eCaseMatters);
michael@0 207 if (idx >= 0)
michael@0 208 return *(tokens[idx]);
michael@0 209
michael@0 210 return nullptr;
michael@0 211 }
michael@0 212
michael@0 213 Accessible*
michael@0 214 nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
michael@0 215 {
michael@0 216 if (!aAccessible)
michael@0 217 return nullptr;
michael@0 218
michael@0 219 if (!(aState & states::SELECTABLE))
michael@0 220 return nullptr;
michael@0 221
michael@0 222 Accessible* parent = aAccessible;
michael@0 223 while ((parent = parent->Parent()) && !parent->IsSelect()) {
michael@0 224 if (parent->Role() == roles::PANE)
michael@0 225 return nullptr;
michael@0 226 }
michael@0 227 return parent;
michael@0 228 }
michael@0 229
michael@0 230 bool
michael@0 231 nsAccUtils::IsARIASelected(Accessible* aAccessible)
michael@0 232 {
michael@0 233 return aAccessible->GetContent()->
michael@0 234 AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
michael@0 235 nsGkAtoms::_true, eCaseMatters);
michael@0 236 }
michael@0 237
michael@0 238 HyperTextAccessible*
michael@0 239 nsAccUtils::GetTextContainer(nsINode* aNode)
michael@0 240 {
michael@0 241 // Get text accessible containing the result node.
michael@0 242 DocAccessible* doc =
michael@0 243 GetAccService()->GetDocAccessible(aNode->OwnerDoc());
michael@0 244 Accessible* accessible =
michael@0 245 doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
michael@0 246 if (!accessible)
michael@0 247 return nullptr;
michael@0 248
michael@0 249 do {
michael@0 250 HyperTextAccessible* textAcc = accessible->AsHyperText();
michael@0 251 if (textAcc)
michael@0 252 return textAcc;
michael@0 253
michael@0 254 accessible = accessible->Parent();
michael@0 255 } while (accessible);
michael@0 256
michael@0 257 return nullptr;
michael@0 258 }
michael@0 259
michael@0 260 nsIntPoint
michael@0 261 nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
michael@0 262 uint32_t aCoordinateType,
michael@0 263 Accessible* aAccessible)
michael@0 264 {
michael@0 265 nsIntPoint coords(aX, aY);
michael@0 266
michael@0 267 switch (aCoordinateType) {
michael@0 268 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
michael@0 269 break;
michael@0 270
michael@0 271 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
michael@0 272 {
michael@0 273 coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
michael@0 274 break;
michael@0 275 }
michael@0 276
michael@0 277 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
michael@0 278 {
michael@0 279 coords += GetScreenCoordsForParent(aAccessible);
michael@0 280 break;
michael@0 281 }
michael@0 282
michael@0 283 default:
michael@0 284 NS_NOTREACHED("invalid coord type!");
michael@0 285 }
michael@0 286
michael@0 287 return coords;
michael@0 288 }
michael@0 289
michael@0 290 void
michael@0 291 nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
michael@0 292 uint32_t aCoordinateType,
michael@0 293 Accessible* aAccessible)
michael@0 294 {
michael@0 295 switch (aCoordinateType) {
michael@0 296 case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
michael@0 297 break;
michael@0 298
michael@0 299 case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
michael@0 300 {
michael@0 301 nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
michael@0 302 *aX -= coords.x;
michael@0 303 *aY -= coords.y;
michael@0 304 break;
michael@0 305 }
michael@0 306
michael@0 307 case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
michael@0 308 {
michael@0 309 nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
michael@0 310 *aX -= coords.x;
michael@0 311 *aY -= coords.y;
michael@0 312 break;
michael@0 313 }
michael@0 314
michael@0 315 default:
michael@0 316 NS_NOTREACHED("invalid coord type!");
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 nsIntPoint
michael@0 321 nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
michael@0 322 {
michael@0 323 Accessible* parent = aAccessible->Parent();
michael@0 324 if (!parent)
michael@0 325 return nsIntPoint(0, 0);
michael@0 326
michael@0 327 nsIFrame *parentFrame = parent->GetFrame();
michael@0 328 if (!parentFrame)
michael@0 329 return nsIntPoint(0, 0);
michael@0 330
michael@0 331 nsRect rect = parentFrame->GetScreenRectInAppUnits();
michael@0 332 return nsPoint(rect.x, rect.y).
michael@0 333 ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel());
michael@0 334 }
michael@0 335
michael@0 336 bool
michael@0 337 nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
michael@0 338 {
michael@0 339 switch (aRule) {
michael@0 340 case eOffLiveAttr:
michael@0 341 aValue = NS_LITERAL_STRING("off");
michael@0 342 return true;
michael@0 343 case ePoliteLiveAttr:
michael@0 344 aValue = NS_LITERAL_STRING("polite");
michael@0 345 return true;
michael@0 346 }
michael@0 347
michael@0 348 return false;
michael@0 349 }
michael@0 350
michael@0 351 #ifdef DEBUG
michael@0 352
michael@0 353 bool
michael@0 354 nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
michael@0 355 {
michael@0 356 // Don't test for accessible docs, it makes us create accessibles too
michael@0 357 // early and fire mutation events before we need to
michael@0 358 if (aAccessible->IsDoc())
michael@0 359 return true;
michael@0 360
michael@0 361 bool foundText = false;
michael@0 362 uint32_t childCount = aAccessible->ChildCount();
michael@0 363 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 364 Accessible* child = aAccessible->GetChildAt(childIdx);
michael@0 365 if (!IsEmbeddedObject(child)) {
michael@0 366 foundText = true;
michael@0 367 break;
michael@0 368 }
michael@0 369 }
michael@0 370
michael@0 371 if (foundText) {
michael@0 372 // found text child node
michael@0 373 nsCOMPtr<nsIAccessibleText> text = do_QueryObject(aAccessible);
michael@0 374 if (!text)
michael@0 375 return false;
michael@0 376 }
michael@0 377
michael@0 378 return true;
michael@0 379 }
michael@0 380 #endif
michael@0 381
michael@0 382 uint32_t
michael@0 383 nsAccUtils::TextLength(Accessible* aAccessible)
michael@0 384 {
michael@0 385 if (IsEmbeddedObject(aAccessible))
michael@0 386 return 1;
michael@0 387
michael@0 388 TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
michael@0 389 if (textLeaf)
michael@0 390 return textLeaf->Text().Length();
michael@0 391
michael@0 392 // For list bullets (or anything other accessible which would compute its own
michael@0 393 // text. They don't have their own frame.
michael@0 394 // XXX In the future, list bullets may have frame and anon content, so
michael@0 395 // we should be able to remove this at that point
michael@0 396 nsAutoString text;
michael@0 397 aAccessible->AppendTextTo(text); // Get all the text
michael@0 398 return text.Length();
michael@0 399 }
michael@0 400
michael@0 401 bool
michael@0 402 nsAccUtils::MustPrune(Accessible* aAccessible)
michael@0 403 {
michael@0 404 roles::Role role = aAccessible->Role();
michael@0 405
michael@0 406 // Don't prune the tree for certain roles if the tree is more complex than
michael@0 407 // a single text leaf.
michael@0 408 return
michael@0 409 (role == roles::MENUITEM ||
michael@0 410 role == roles::COMBOBOX_OPTION ||
michael@0 411 role == roles::OPTION ||
michael@0 412 role == roles::ENTRY ||
michael@0 413 role == roles::FLAT_EQUATION ||
michael@0 414 role == roles::PASSWORD_TEXT ||
michael@0 415 role == roles::PUSHBUTTON ||
michael@0 416 role == roles::TOGGLE_BUTTON ||
michael@0 417 role == roles::GRAPHIC ||
michael@0 418 role == roles::SLIDER ||
michael@0 419 role == roles::PROGRESSBAR ||
michael@0 420 role == roles::SEPARATOR) &&
michael@0 421 aAccessible->ContentChildCount() == 1 &&
michael@0 422 aAccessible->ContentChildAt(0)->IsTextLeaf();
michael@0 423 }

mercurial