1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/base/nsAccUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsAccUtils.h" 1.10 + 1.11 +#include "Accessible-inl.h" 1.12 +#include "ARIAMap.h" 1.13 +#include "nsAccessibilityService.h" 1.14 +#include "nsCoreUtils.h" 1.15 +#include "DocAccessible.h" 1.16 +#include "HyperTextAccessible.h" 1.17 +#include "nsIAccessibleTypes.h" 1.18 +#include "Role.h" 1.19 +#include "States.h" 1.20 +#include "TextLeafAccessible.h" 1.21 + 1.22 +#include "nsIDOMXULContainerElement.h" 1.23 +#include "nsIPersistentProperties2.h" 1.24 +#include "mozilla/dom/Element.h" 1.25 + 1.26 +using namespace mozilla; 1.27 +using namespace mozilla::a11y; 1.28 + 1.29 +void 1.30 +nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes, 1.31 + nsIAtom *aAttrName, nsAString& aAttrValue) 1.32 +{ 1.33 + aAttrValue.Truncate(); 1.34 + 1.35 + aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue); 1.36 +} 1.37 + 1.38 +void 1.39 +nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes, 1.40 + nsIAtom *aAttrName, const nsAString& aAttrValue) 1.41 +{ 1.42 + nsAutoString oldValue; 1.43 + nsAutoCString attrName; 1.44 + 1.45 + aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue); 1.46 +} 1.47 + 1.48 +void 1.49 +nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes, 1.50 + int32_t aLevel, int32_t aSetSize, 1.51 + int32_t aPosInSet) 1.52 +{ 1.53 + nsAutoString value; 1.54 + 1.55 + if (aLevel) { 1.56 + value.AppendInt(aLevel); 1.57 + SetAccAttr(aAttributes, nsGkAtoms::level, value); 1.58 + } 1.59 + 1.60 + if (aSetSize && aPosInSet) { 1.61 + value.Truncate(); 1.62 + value.AppendInt(aPosInSet); 1.63 + SetAccAttr(aAttributes, nsGkAtoms::posinset, value); 1.64 + 1.65 + value.Truncate(); 1.66 + value.AppendInt(aSetSize); 1.67 + SetAccAttr(aAttributes, nsGkAtoms::setsize, value); 1.68 + } 1.69 +} 1.70 + 1.71 +int32_t 1.72 +nsAccUtils::GetDefaultLevel(Accessible* aAccessible) 1.73 +{ 1.74 + roles::Role role = aAccessible->Role(); 1.75 + 1.76 + if (role == roles::OUTLINEITEM) 1.77 + return 1; 1.78 + 1.79 + if (role == roles::ROW) { 1.80 + Accessible* parent = aAccessible->Parent(); 1.81 + // It is a row inside flatten treegrid. Group level is always 1 until it 1.82 + // is overriden by aria-level attribute. 1.83 + if (parent && parent->Role() == roles::TREE_TABLE) 1.84 + return 1; 1.85 + } 1.86 + 1.87 + return 0; 1.88 +} 1.89 + 1.90 +int32_t 1.91 +nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible) 1.92 +{ 1.93 + int32_t level = 0; 1.94 + nsCoreUtils::GetUIntAttr(aAccessible->GetContent(), 1.95 + nsGkAtoms::aria_level, &level); 1.96 + 1.97 + if (level != 0) 1.98 + return level; 1.99 + 1.100 + return GetDefaultLevel(aAccessible); 1.101 +} 1.102 + 1.103 +int32_t 1.104 +nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent) 1.105 +{ 1.106 + nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent)); 1.107 + if (!item) 1.108 + return 0; 1.109 + 1.110 + nsCOMPtr<nsIDOMXULContainerElement> container; 1.111 + item->GetParentContainer(getter_AddRefs(container)); 1.112 + if (!container) 1.113 + return 0; 1.114 + 1.115 + // Get level of the item. 1.116 + int32_t level = -1; 1.117 + while (container) { 1.118 + level++; 1.119 + 1.120 + nsCOMPtr<nsIDOMXULContainerElement> parentContainer; 1.121 + container->GetParentContainer(getter_AddRefs(parentContainer)); 1.122 + parentContainer.swap(container); 1.123 + } 1.124 + 1.125 + return level; 1.126 +} 1.127 + 1.128 +void 1.129 +nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes, 1.130 + nsIContent *aStartContent, 1.131 + nsIContent *aTopContent) 1.132 +{ 1.133 + nsAutoString live, relevant, busy; 1.134 + nsIContent *ancestor = aStartContent; 1.135 + while (ancestor) { 1.136 + 1.137 + // container-relevant attribute 1.138 + if (relevant.IsEmpty() && 1.139 + HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) && 1.140 + ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant)) 1.141 + SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant); 1.142 + 1.143 + // container-live, and container-live-role attributes 1.144 + if (live.IsEmpty()) { 1.145 + nsRoleMapEntry* role = aria::GetRoleMap(ancestor); 1.146 + if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) { 1.147 + ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, 1.148 + live); 1.149 + } else if (role) { 1.150 + GetLiveAttrValue(role->liveAttRule, live); 1.151 + } 1.152 + if (!live.IsEmpty()) { 1.153 + SetAccAttr(aAttributes, nsGkAtoms::containerLive, live); 1.154 + if (role) { 1.155 + SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole, 1.156 + role->ARIARoleString()); 1.157 + } 1.158 + } 1.159 + } 1.160 + 1.161 + // container-atomic attribute 1.162 + if (ancestor->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_atomic, 1.163 + nsGkAtoms::_true, eCaseMatters)) { 1.164 + SetAccAttr(aAttributes, nsGkAtoms::containerAtomic, 1.165 + NS_LITERAL_STRING("true")); 1.166 + } 1.167 + 1.168 + // container-busy attribute 1.169 + if (busy.IsEmpty() && 1.170 + HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) && 1.171 + ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy)) 1.172 + SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy); 1.173 + 1.174 + if (ancestor == aTopContent) 1.175 + break; 1.176 + 1.177 + ancestor = ancestor->GetParent(); 1.178 + if (!ancestor) 1.179 + ancestor = aTopContent; // Use <body>/<frameset> 1.180 + } 1.181 +} 1.182 + 1.183 +bool 1.184 +nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom) 1.185 +{ 1.186 + NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!"); 1.187 + 1.188 + if (!aContent->HasAttr(kNameSpaceID_None, aAtom) || 1.189 + aContent->AttrValueIs(kNameSpaceID_None, aAtom, 1.190 + nsGkAtoms::_empty, eCaseMatters) || 1.191 + aContent->AttrValueIs(kNameSpaceID_None, aAtom, 1.192 + nsGkAtoms::_undefined, eCaseMatters)) { 1.193 + return false; 1.194 + } 1.195 + return true; 1.196 +} 1.197 + 1.198 +nsIAtom* 1.199 +nsAccUtils::GetARIAToken(dom::Element* aElement, nsIAtom* aAttr) 1.200 +{ 1.201 + if (!HasDefinedARIAToken(aElement, aAttr)) 1.202 + return nsGkAtoms::_empty; 1.203 + 1.204 + static nsIContent::AttrValuesArray tokens[] = 1.205 + { &nsGkAtoms::_false, &nsGkAtoms::_true, 1.206 + &nsGkAtoms::mixed, nullptr}; 1.207 + 1.208 + int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None, 1.209 + aAttr, tokens, eCaseMatters); 1.210 + if (idx >= 0) 1.211 + return *(tokens[idx]); 1.212 + 1.213 + return nullptr; 1.214 +} 1.215 + 1.216 +Accessible* 1.217 +nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState) 1.218 +{ 1.219 + if (!aAccessible) 1.220 + return nullptr; 1.221 + 1.222 + if (!(aState & states::SELECTABLE)) 1.223 + return nullptr; 1.224 + 1.225 + Accessible* parent = aAccessible; 1.226 + while ((parent = parent->Parent()) && !parent->IsSelect()) { 1.227 + if (parent->Role() == roles::PANE) 1.228 + return nullptr; 1.229 + } 1.230 + return parent; 1.231 +} 1.232 + 1.233 +bool 1.234 +nsAccUtils::IsARIASelected(Accessible* aAccessible) 1.235 +{ 1.236 + return aAccessible->GetContent()-> 1.237 + AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected, 1.238 + nsGkAtoms::_true, eCaseMatters); 1.239 +} 1.240 + 1.241 +HyperTextAccessible* 1.242 +nsAccUtils::GetTextContainer(nsINode* aNode) 1.243 +{ 1.244 + // Get text accessible containing the result node. 1.245 + DocAccessible* doc = 1.246 + GetAccService()->GetDocAccessible(aNode->OwnerDoc()); 1.247 + Accessible* accessible = 1.248 + doc ? doc->GetAccessibleOrContainer(aNode) : nullptr; 1.249 + if (!accessible) 1.250 + return nullptr; 1.251 + 1.252 + do { 1.253 + HyperTextAccessible* textAcc = accessible->AsHyperText(); 1.254 + if (textAcc) 1.255 + return textAcc; 1.256 + 1.257 + accessible = accessible->Parent(); 1.258 + } while (accessible); 1.259 + 1.260 + return nullptr; 1.261 +} 1.262 + 1.263 +nsIntPoint 1.264 +nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY, 1.265 + uint32_t aCoordinateType, 1.266 + Accessible* aAccessible) 1.267 +{ 1.268 + nsIntPoint coords(aX, aY); 1.269 + 1.270 + switch (aCoordinateType) { 1.271 + case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE: 1.272 + break; 1.273 + 1.274 + case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE: 1.275 + { 1.276 + coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode()); 1.277 + break; 1.278 + } 1.279 + 1.280 + case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE: 1.281 + { 1.282 + coords += GetScreenCoordsForParent(aAccessible); 1.283 + break; 1.284 + } 1.285 + 1.286 + default: 1.287 + NS_NOTREACHED("invalid coord type!"); 1.288 + } 1.289 + 1.290 + return coords; 1.291 +} 1.292 + 1.293 +void 1.294 +nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY, 1.295 + uint32_t aCoordinateType, 1.296 + Accessible* aAccessible) 1.297 +{ 1.298 + switch (aCoordinateType) { 1.299 + case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE: 1.300 + break; 1.301 + 1.302 + case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE: 1.303 + { 1.304 + nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode()); 1.305 + *aX -= coords.x; 1.306 + *aY -= coords.y; 1.307 + break; 1.308 + } 1.309 + 1.310 + case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE: 1.311 + { 1.312 + nsIntPoint coords = GetScreenCoordsForParent(aAccessible); 1.313 + *aX -= coords.x; 1.314 + *aY -= coords.y; 1.315 + break; 1.316 + } 1.317 + 1.318 + default: 1.319 + NS_NOTREACHED("invalid coord type!"); 1.320 + } 1.321 +} 1.322 + 1.323 +nsIntPoint 1.324 +nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible) 1.325 +{ 1.326 + Accessible* parent = aAccessible->Parent(); 1.327 + if (!parent) 1.328 + return nsIntPoint(0, 0); 1.329 + 1.330 + nsIFrame *parentFrame = parent->GetFrame(); 1.331 + if (!parentFrame) 1.332 + return nsIntPoint(0, 0); 1.333 + 1.334 + nsRect rect = parentFrame->GetScreenRectInAppUnits(); 1.335 + return nsPoint(rect.x, rect.y). 1.336 + ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel()); 1.337 +} 1.338 + 1.339 +bool 1.340 +nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue) 1.341 +{ 1.342 + switch (aRule) { 1.343 + case eOffLiveAttr: 1.344 + aValue = NS_LITERAL_STRING("off"); 1.345 + return true; 1.346 + case ePoliteLiveAttr: 1.347 + aValue = NS_LITERAL_STRING("polite"); 1.348 + return true; 1.349 + } 1.350 + 1.351 + return false; 1.352 +} 1.353 + 1.354 +#ifdef DEBUG 1.355 + 1.356 +bool 1.357 +nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible) 1.358 +{ 1.359 + // Don't test for accessible docs, it makes us create accessibles too 1.360 + // early and fire mutation events before we need to 1.361 + if (aAccessible->IsDoc()) 1.362 + return true; 1.363 + 1.364 + bool foundText = false; 1.365 + uint32_t childCount = aAccessible->ChildCount(); 1.366 + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { 1.367 + Accessible* child = aAccessible->GetChildAt(childIdx); 1.368 + if (!IsEmbeddedObject(child)) { 1.369 + foundText = true; 1.370 + break; 1.371 + } 1.372 + } 1.373 + 1.374 + if (foundText) { 1.375 + // found text child node 1.376 + nsCOMPtr<nsIAccessibleText> text = do_QueryObject(aAccessible); 1.377 + if (!text) 1.378 + return false; 1.379 + } 1.380 + 1.381 + return true; 1.382 +} 1.383 +#endif 1.384 + 1.385 +uint32_t 1.386 +nsAccUtils::TextLength(Accessible* aAccessible) 1.387 +{ 1.388 + if (IsEmbeddedObject(aAccessible)) 1.389 + return 1; 1.390 + 1.391 + TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf(); 1.392 + if (textLeaf) 1.393 + return textLeaf->Text().Length(); 1.394 + 1.395 + // For list bullets (or anything other accessible which would compute its own 1.396 + // text. They don't have their own frame. 1.397 + // XXX In the future, list bullets may have frame and anon content, so 1.398 + // we should be able to remove this at that point 1.399 + nsAutoString text; 1.400 + aAccessible->AppendTextTo(text); // Get all the text 1.401 + return text.Length(); 1.402 +} 1.403 + 1.404 +bool 1.405 +nsAccUtils::MustPrune(Accessible* aAccessible) 1.406 +{ 1.407 + roles::Role role = aAccessible->Role(); 1.408 + 1.409 + // Don't prune the tree for certain roles if the tree is more complex than 1.410 + // a single text leaf. 1.411 + return 1.412 + (role == roles::MENUITEM || 1.413 + role == roles::COMBOBOX_OPTION || 1.414 + role == roles::OPTION || 1.415 + role == roles::ENTRY || 1.416 + role == roles::FLAT_EQUATION || 1.417 + role == roles::PASSWORD_TEXT || 1.418 + role == roles::PUSHBUTTON || 1.419 + role == roles::TOGGLE_BUTTON || 1.420 + role == roles::GRAPHIC || 1.421 + role == roles::SLIDER || 1.422 + role == roles::PROGRESSBAR || 1.423 + role == roles::SEPARATOR) && 1.424 + aAccessible->ContentChildCount() == 1 && 1.425 + aAccessible->ContentChildAt(0)->IsTextLeaf(); 1.426 +}