accessible/src/generic/Accessible.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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 "Accessible-inl.h"
michael@0 7
michael@0 8 #include "nsIXBLAccessible.h"
michael@0 9
michael@0 10 #include "AccCollector.h"
michael@0 11 #include "AccGroupInfo.h"
michael@0 12 #include "AccIterator.h"
michael@0 13 #include "nsAccUtils.h"
michael@0 14 #include "nsAccessibleRelation.h"
michael@0 15 #include "nsAccessibilityService.h"
michael@0 16 #include "ApplicationAccessible.h"
michael@0 17 #include "nsCoreUtils.h"
michael@0 18 #include "nsIAccessibleRelation.h"
michael@0 19 #include "nsIAccessibleRole.h"
michael@0 20 #include "nsEventShell.h"
michael@0 21 #include "nsTextEquivUtils.h"
michael@0 22 #include "Relation.h"
michael@0 23 #include "Role.h"
michael@0 24 #include "RootAccessible.h"
michael@0 25 #include "States.h"
michael@0 26 #include "StyleInfo.h"
michael@0 27 #include "TableAccessible.h"
michael@0 28 #include "TableCellAccessible.h"
michael@0 29 #include "TreeWalker.h"
michael@0 30
michael@0 31 #include "nsIDOMElement.h"
michael@0 32 #include "nsIDOMNodeFilter.h"
michael@0 33 #include "nsIDOMHTMLElement.h"
michael@0 34 #include "nsIDOMKeyEvent.h"
michael@0 35 #include "nsIDOMTreeWalker.h"
michael@0 36 #include "nsIDOMXULButtonElement.h"
michael@0 37 #include "nsIDOMXULDocument.h"
michael@0 38 #include "nsIDOMXULElement.h"
michael@0 39 #include "nsIDOMXULLabelElement.h"
michael@0 40 #include "nsIDOMXULSelectCntrlEl.h"
michael@0 41 #include "nsIDOMXULSelectCntrlItemEl.h"
michael@0 42 #include "nsPIDOMWindow.h"
michael@0 43
michael@0 44 #include "nsIDocument.h"
michael@0 45 #include "nsIContent.h"
michael@0 46 #include "nsIForm.h"
michael@0 47 #include "nsIFormControl.h"
michael@0 48
michael@0 49 #include "nsDeckFrame.h"
michael@0 50 #include "nsLayoutUtils.h"
michael@0 51 #include "nsIPresShell.h"
michael@0 52 #include "nsIStringBundle.h"
michael@0 53 #include "nsPresContext.h"
michael@0 54 #include "nsIFrame.h"
michael@0 55 #include "nsView.h"
michael@0 56 #include "nsIDocShellTreeItem.h"
michael@0 57 #include "nsIScrollableFrame.h"
michael@0 58 #include "nsFocusManager.h"
michael@0 59
michael@0 60 #include "nsXPIDLString.h"
michael@0 61 #include "nsUnicharUtils.h"
michael@0 62 #include "nsReadableUtils.h"
michael@0 63 #include "prdtoa.h"
michael@0 64 #include "nsIAtom.h"
michael@0 65 #include "nsIURI.h"
michael@0 66 #include "nsArrayUtils.h"
michael@0 67 #include "nsIMutableArray.h"
michael@0 68 #include "nsIObserverService.h"
michael@0 69 #include "nsIServiceManager.h"
michael@0 70 #include "nsWhitespaceTokenizer.h"
michael@0 71 #include "nsAttrName.h"
michael@0 72 #include "nsNetUtil.h"
michael@0 73
michael@0 74 #ifdef DEBUG
michael@0 75 #include "nsIDOMCharacterData.h"
michael@0 76 #endif
michael@0 77
michael@0 78 #include "mozilla/Assertions.h"
michael@0 79 #include "mozilla/EventStates.h"
michael@0 80 #include "mozilla/FloatingPoint.h"
michael@0 81 #include "mozilla/MouseEvents.h"
michael@0 82 #include "mozilla/unused.h"
michael@0 83 #include "mozilla/Preferences.h"
michael@0 84 #include "mozilla/dom/Element.h"
michael@0 85 #include "mozilla/dom/TreeWalker.h"
michael@0 86
michael@0 87 using namespace mozilla;
michael@0 88 using namespace mozilla::a11y;
michael@0 89
michael@0 90
michael@0 91 ////////////////////////////////////////////////////////////////////////////////
michael@0 92 // Accessible: nsISupports and cycle collection
michael@0 93
michael@0 94 NS_IMPL_CYCLE_COLLECTION(Accessible,
michael@0 95 mContent, mParent, mChildren)
michael@0 96
michael@0 97 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
michael@0 98 NS_INTERFACE_MAP_ENTRY(nsIAccessible)
michael@0 99 if (aIID.Equals(NS_GET_IID(Accessible)))
michael@0 100 foundInterface = static_cast<nsIAccessible*>(this);
michael@0 101 else
michael@0 102 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleSelectable, IsSelect())
michael@0 103 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleValue, HasNumericValue())
michael@0 104 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleHyperLink, IsLink())
michael@0 105 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessible)
michael@0 106 NS_INTERFACE_MAP_END
michael@0 107
michael@0 108 NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
michael@0 109 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
michael@0 110
michael@0 111 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
michael@0 112 mContent(aContent), mDoc(aDoc),
michael@0 113 mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
michael@0 114 mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
michael@0 115 mIndexOfEmbeddedChild(-1), mRoleMapEntry(nullptr)
michael@0 116 {
michael@0 117 #ifdef NS_DEBUG_X
michael@0 118 {
michael@0 119 nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
michael@0 120 printf(">>> %p Created Acc - DOM: %p PS: %p",
michael@0 121 (void*)static_cast<nsIAccessible*>(this), (void*)aNode,
michael@0 122 (void*)shell.get());
michael@0 123 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
michael@0 124 if (content) {
michael@0 125 printf(" Con: %s@%p",
michael@0 126 NS_ConvertUTF16toUTF8(content->NodeInfo()->QualifiedName()).get(),
michael@0 127 (void *)content.get());
michael@0 128 nsAutoString buf;
michael@0 129 Name(buf);
michael@0 130 printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf).get());
michael@0 131 }
michael@0 132 printf("\n");
michael@0 133 }
michael@0 134 #endif
michael@0 135 }
michael@0 136
michael@0 137 Accessible::~Accessible()
michael@0 138 {
michael@0 139 NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
michael@0 140 }
michael@0 141
michael@0 142 NS_IMETHODIMP
michael@0 143 Accessible::GetDocument(nsIAccessibleDocument** aDocument)
michael@0 144 {
michael@0 145 NS_ENSURE_ARG_POINTER(aDocument);
michael@0 146
michael@0 147 NS_IF_ADDREF(*aDocument = Document());
michael@0 148 return NS_OK;
michael@0 149 }
michael@0 150
michael@0 151 NS_IMETHODIMP
michael@0 152 Accessible::GetDOMNode(nsIDOMNode** aDOMNode)
michael@0 153 {
michael@0 154 NS_ENSURE_ARG_POINTER(aDOMNode);
michael@0 155 *aDOMNode = nullptr;
michael@0 156
michael@0 157 nsINode *node = GetNode();
michael@0 158 if (node)
michael@0 159 CallQueryInterface(node, aDOMNode);
michael@0 160
michael@0 161 return NS_OK;
michael@0 162 }
michael@0 163
michael@0 164 NS_IMETHODIMP
michael@0 165 Accessible::GetRootDocument(nsIAccessibleDocument** aRootDocument)
michael@0 166 {
michael@0 167 NS_ENSURE_ARG_POINTER(aRootDocument);
michael@0 168
michael@0 169 NS_IF_ADDREF(*aRootDocument = RootAccessible());
michael@0 170 return NS_OK;
michael@0 171 }
michael@0 172
michael@0 173 NS_IMETHODIMP
michael@0 174 Accessible::GetLanguage(nsAString& aLanguage)
michael@0 175 {
michael@0 176 Language(aLanguage);
michael@0 177 return NS_OK;
michael@0 178 }
michael@0 179
michael@0 180 NS_IMETHODIMP
michael@0 181 Accessible::GetName(nsAString& aName)
michael@0 182 {
michael@0 183 aName.Truncate();
michael@0 184
michael@0 185 if (IsDefunct())
michael@0 186 return NS_ERROR_FAILURE;
michael@0 187
michael@0 188 nsAutoString name;
michael@0 189 Name(name);
michael@0 190 aName.Assign(name);
michael@0 191
michael@0 192 return NS_OK;
michael@0 193 }
michael@0 194
michael@0 195 ENameValueFlag
michael@0 196 Accessible::Name(nsString& aName)
michael@0 197 {
michael@0 198 aName.Truncate();
michael@0 199
michael@0 200 if (!HasOwnContent())
michael@0 201 return eNameOK;
michael@0 202
michael@0 203 ARIAName(aName);
michael@0 204 if (!aName.IsEmpty())
michael@0 205 return eNameOK;
michael@0 206
michael@0 207 nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
michael@0 208 if (xblAccessible) {
michael@0 209 xblAccessible->GetAccessibleName(aName);
michael@0 210 if (!aName.IsEmpty())
michael@0 211 return eNameOK;
michael@0 212 }
michael@0 213
michael@0 214 ENameValueFlag nameFlag = NativeName(aName);
michael@0 215 if (!aName.IsEmpty())
michael@0 216 return nameFlag;
michael@0 217
michael@0 218 // In the end get the name from tooltip.
michael@0 219 if (mContent->IsHTML()) {
michael@0 220 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
michael@0 221 aName.CompressWhitespace();
michael@0 222 return eNameFromTooltip;
michael@0 223 }
michael@0 224 } else if (mContent->IsXUL()) {
michael@0 225 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
michael@0 226 aName.CompressWhitespace();
michael@0 227 return eNameFromTooltip;
michael@0 228 }
michael@0 229 } else if (mContent->IsSVG()) {
michael@0 230 // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
michael@0 231 // for processing, the user agent shall choose the first one.
michael@0 232 for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
michael@0 233 childElm = childElm->GetNextSibling()) {
michael@0 234 if (childElm->IsSVG(nsGkAtoms::desc)) {
michael@0 235 nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
michael@0 236 return eNameFromTooltip;
michael@0 237 }
michael@0 238 }
michael@0 239 }
michael@0 240
michael@0 241 if (nameFlag != eNoNameOnPurpose)
michael@0 242 aName.SetIsVoid(true);
michael@0 243
michael@0 244 return nameFlag;
michael@0 245 }
michael@0 246
michael@0 247 NS_IMETHODIMP
michael@0 248 Accessible::GetDescription(nsAString& aDescription)
michael@0 249 {
michael@0 250 if (IsDefunct())
michael@0 251 return NS_ERROR_FAILURE;
michael@0 252
michael@0 253 nsAutoString desc;
michael@0 254 Description(desc);
michael@0 255 aDescription.Assign(desc);
michael@0 256
michael@0 257 return NS_OK;
michael@0 258 }
michael@0 259
michael@0 260 void
michael@0 261 Accessible::Description(nsString& aDescription)
michael@0 262 {
michael@0 263 // There are 4 conditions that make an accessible have no accDescription:
michael@0 264 // 1. it's a text node; or
michael@0 265 // 2. It has no DHTML describedby property
michael@0 266 // 3. it doesn't have an accName; or
michael@0 267 // 4. its title attribute already equals to its accName nsAutoString name;
michael@0 268
michael@0 269 if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
michael@0 270 return;
michael@0 271
michael@0 272 nsTextEquivUtils::
michael@0 273 GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
michael@0 274 aDescription);
michael@0 275
michael@0 276 if (aDescription.IsEmpty()) {
michael@0 277 bool isXUL = mContent->IsXUL();
michael@0 278 if (isXUL) {
michael@0 279 // Try XUL <description control="[id]">description text</description>
michael@0 280 XULDescriptionIterator iter(Document(), mContent);
michael@0 281 Accessible* descr = nullptr;
michael@0 282 while ((descr = iter.Next())) {
michael@0 283 nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
michael@0 284 &aDescription);
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 if (aDescription.IsEmpty()) {
michael@0 289 // Keep the Name() method logic.
michael@0 290 if (mContent->IsHTML()) {
michael@0 291 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
michael@0 292 } else if (mContent->IsXUL()) {
michael@0 293 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
michael@0 294 } else if (mContent->IsSVG()) {
michael@0 295 for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
michael@0 296 childElm = childElm->GetNextSibling()) {
michael@0 297 if (childElm->IsSVG(nsGkAtoms::desc)) {
michael@0 298 nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
michael@0 299 &aDescription);
michael@0 300 break;
michael@0 301 }
michael@0 302 }
michael@0 303 }
michael@0 304
michael@0 305 if (!aDescription.IsEmpty()) {
michael@0 306 nsAutoString name;
michael@0 307 ENameValueFlag nameFlag = Name(name);
michael@0 308
michael@0 309 // Don't use tooltip for a description if it was used for a name.
michael@0 310 if (nameFlag == eNameFromTooltip)
michael@0 311 aDescription.Truncate();
michael@0 312 }
michael@0 313 }
michael@0 314 }
michael@0 315 aDescription.CompressWhitespace();
michael@0 316 }
michael@0 317
michael@0 318 NS_IMETHODIMP
michael@0 319 Accessible::GetAccessKey(nsAString& aAccessKey)
michael@0 320 {
michael@0 321 aAccessKey.Truncate();
michael@0 322
michael@0 323 if (IsDefunct())
michael@0 324 return NS_ERROR_FAILURE;
michael@0 325
michael@0 326 AccessKey().ToString(aAccessKey);
michael@0 327 return NS_OK;
michael@0 328 }
michael@0 329
michael@0 330 KeyBinding
michael@0 331 Accessible::AccessKey() const
michael@0 332 {
michael@0 333 if (!HasOwnContent())
michael@0 334 return KeyBinding();
michael@0 335
michael@0 336 uint32_t key = nsCoreUtils::GetAccessKeyFor(mContent);
michael@0 337 if (!key && mContent->IsElement()) {
michael@0 338 Accessible* label = nullptr;
michael@0 339
michael@0 340 // Copy access key from label node.
michael@0 341 if (mContent->IsHTML()) {
michael@0 342 // Unless it is labeled via an ancestor <label>, in which case that would
michael@0 343 // be redundant.
michael@0 344 HTMLLabelIterator iter(Document(), this,
michael@0 345 HTMLLabelIterator::eSkipAncestorLabel);
michael@0 346 label = iter.Next();
michael@0 347
michael@0 348 } else if (mContent->IsXUL()) {
michael@0 349 XULLabelIterator iter(Document(), mContent);
michael@0 350 label = iter.Next();
michael@0 351 }
michael@0 352
michael@0 353 if (label)
michael@0 354 key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
michael@0 355 }
michael@0 356
michael@0 357 if (!key)
michael@0 358 return KeyBinding();
michael@0 359
michael@0 360 // Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
michael@0 361 switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
michael@0 362 case -1:
michael@0 363 break;
michael@0 364 case nsIDOMKeyEvent::DOM_VK_SHIFT:
michael@0 365 return KeyBinding(key, KeyBinding::kShift);
michael@0 366 case nsIDOMKeyEvent::DOM_VK_CONTROL:
michael@0 367 return KeyBinding(key, KeyBinding::kControl);
michael@0 368 case nsIDOMKeyEvent::DOM_VK_ALT:
michael@0 369 return KeyBinding(key, KeyBinding::kAlt);
michael@0 370 case nsIDOMKeyEvent::DOM_VK_META:
michael@0 371 return KeyBinding(key, KeyBinding::kMeta);
michael@0 372 default:
michael@0 373 return KeyBinding();
michael@0 374 }
michael@0 375
michael@0 376 // Determine the access modifier used in this context.
michael@0 377 nsIDocument* document = mContent->GetCurrentDoc();
michael@0 378 if (!document)
michael@0 379 return KeyBinding();
michael@0 380
michael@0 381 nsCOMPtr<nsIDocShellTreeItem> treeItem(document->GetDocShell());
michael@0 382 if (!treeItem)
michael@0 383 return KeyBinding();
michael@0 384
michael@0 385 nsresult rv = NS_ERROR_FAILURE;
michael@0 386 int32_t modifierMask = 0;
michael@0 387 switch (treeItem->ItemType()) {
michael@0 388 case nsIDocShellTreeItem::typeChrome:
michael@0 389 rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
michael@0 390 break;
michael@0 391 case nsIDocShellTreeItem::typeContent:
michael@0 392 rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
michael@0 393 break;
michael@0 394 }
michael@0 395
michael@0 396 return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
michael@0 397 }
michael@0 398
michael@0 399 KeyBinding
michael@0 400 Accessible::KeyboardShortcut() const
michael@0 401 {
michael@0 402 return KeyBinding();
michael@0 403 }
michael@0 404
michael@0 405 NS_IMETHODIMP
michael@0 406 Accessible::GetParent(nsIAccessible** aParent)
michael@0 407 {
michael@0 408 NS_ENSURE_ARG_POINTER(aParent);
michael@0 409 if (IsDefunct())
michael@0 410 return NS_ERROR_FAILURE;
michael@0 411
michael@0 412 NS_IF_ADDREF(*aParent = Parent());
michael@0 413 return *aParent ? NS_OK : NS_ERROR_FAILURE;
michael@0 414 }
michael@0 415
michael@0 416 /* readonly attribute nsIAccessible nextSibling; */
michael@0 417 NS_IMETHODIMP
michael@0 418 Accessible::GetNextSibling(nsIAccessible** aNextSibling)
michael@0 419 {
michael@0 420 NS_ENSURE_ARG_POINTER(aNextSibling);
michael@0 421 *aNextSibling = nullptr;
michael@0 422
michael@0 423 if (IsDefunct())
michael@0 424 return NS_ERROR_FAILURE;
michael@0 425
michael@0 426 nsresult rv = NS_OK;
michael@0 427 NS_IF_ADDREF(*aNextSibling = GetSiblingAtOffset(1, &rv));
michael@0 428 return rv;
michael@0 429 }
michael@0 430
michael@0 431 /* readonly attribute nsIAccessible previousSibling; */
michael@0 432 NS_IMETHODIMP
michael@0 433 Accessible::GetPreviousSibling(nsIAccessible ** aPreviousSibling)
michael@0 434 {
michael@0 435 NS_ENSURE_ARG_POINTER(aPreviousSibling);
michael@0 436 *aPreviousSibling = nullptr;
michael@0 437
michael@0 438 if (IsDefunct())
michael@0 439 return NS_ERROR_FAILURE;
michael@0 440
michael@0 441 nsresult rv = NS_OK;
michael@0 442 NS_IF_ADDREF(*aPreviousSibling = GetSiblingAtOffset(-1, &rv));
michael@0 443 return rv;
michael@0 444 }
michael@0 445
michael@0 446 /* readonly attribute nsIAccessible firstChild; */
michael@0 447 NS_IMETHODIMP
michael@0 448 Accessible::GetFirstChild(nsIAccessible** aFirstChild)
michael@0 449 {
michael@0 450 NS_ENSURE_ARG_POINTER(aFirstChild);
michael@0 451 *aFirstChild = nullptr;
michael@0 452
michael@0 453 if (IsDefunct())
michael@0 454 return NS_ERROR_FAILURE;
michael@0 455
michael@0 456 NS_IF_ADDREF(*aFirstChild = FirstChild());
michael@0 457 return NS_OK;
michael@0 458 }
michael@0 459
michael@0 460 /* readonly attribute nsIAccessible lastChild; */
michael@0 461 NS_IMETHODIMP
michael@0 462 Accessible::GetLastChild(nsIAccessible** aLastChild)
michael@0 463 {
michael@0 464 NS_ENSURE_ARG_POINTER(aLastChild);
michael@0 465 *aLastChild = nullptr;
michael@0 466
michael@0 467 if (IsDefunct())
michael@0 468 return NS_ERROR_FAILURE;
michael@0 469
michael@0 470 NS_IF_ADDREF(*aLastChild = LastChild());
michael@0 471 return NS_OK;
michael@0 472 }
michael@0 473
michael@0 474 NS_IMETHODIMP
michael@0 475 Accessible::GetChildAt(int32_t aChildIndex, nsIAccessible** aChild)
michael@0 476 {
michael@0 477 NS_ENSURE_ARG_POINTER(aChild);
michael@0 478 *aChild = nullptr;
michael@0 479
michael@0 480 if (IsDefunct())
michael@0 481 return NS_ERROR_FAILURE;
michael@0 482
michael@0 483 // If child index is negative, then return last child.
michael@0 484 // XXX: do we really need this?
michael@0 485 if (aChildIndex < 0)
michael@0 486 aChildIndex = ChildCount() - 1;
michael@0 487
michael@0 488 Accessible* child = GetChildAt(aChildIndex);
michael@0 489 if (!child)
michael@0 490 return NS_ERROR_INVALID_ARG;
michael@0 491
michael@0 492 NS_ADDREF(*aChild = child);
michael@0 493 return NS_OK;
michael@0 494 }
michael@0 495
michael@0 496 // readonly attribute nsIArray children;
michael@0 497 NS_IMETHODIMP
michael@0 498 Accessible::GetChildren(nsIArray** aOutChildren)
michael@0 499 {
michael@0 500 NS_ENSURE_ARG_POINTER(aOutChildren);
michael@0 501 *aOutChildren = nullptr;
michael@0 502
michael@0 503 if (IsDefunct())
michael@0 504 return NS_ERROR_FAILURE;
michael@0 505
michael@0 506 nsresult rv = NS_OK;
michael@0 507 nsCOMPtr<nsIMutableArray> children =
michael@0 508 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
michael@0 509 NS_ENSURE_SUCCESS(rv, rv);
michael@0 510
michael@0 511 uint32_t childCount = ChildCount();
michael@0 512 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 513 nsIAccessible* child = GetChildAt(childIdx);
michael@0 514 children->AppendElement(child, false);
michael@0 515 }
michael@0 516
michael@0 517 NS_ADDREF(*aOutChildren = children);
michael@0 518 return NS_OK;
michael@0 519 }
michael@0 520
michael@0 521 bool
michael@0 522 Accessible::CanHaveAnonChildren()
michael@0 523 {
michael@0 524 return true;
michael@0 525 }
michael@0 526
michael@0 527 /* readonly attribute long childCount; */
michael@0 528 NS_IMETHODIMP
michael@0 529 Accessible::GetChildCount(int32_t* aChildCount)
michael@0 530 {
michael@0 531 NS_ENSURE_ARG_POINTER(aChildCount);
michael@0 532
michael@0 533 if (IsDefunct())
michael@0 534 return NS_ERROR_FAILURE;
michael@0 535
michael@0 536 *aChildCount = ChildCount();
michael@0 537 return NS_OK;
michael@0 538 }
michael@0 539
michael@0 540 /* readonly attribute long indexInParent; */
michael@0 541 NS_IMETHODIMP
michael@0 542 Accessible::GetIndexInParent(int32_t* aIndexInParent)
michael@0 543 {
michael@0 544 NS_ENSURE_ARG_POINTER(aIndexInParent);
michael@0 545
michael@0 546 *aIndexInParent = IndexInParent();
michael@0 547 return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
michael@0 548 }
michael@0 549
michael@0 550 void
michael@0 551 Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
michael@0 552 {
michael@0 553 nsCOMPtr<nsIStringBundleService> stringBundleService =
michael@0 554 services::GetStringBundleService();
michael@0 555 if (!stringBundleService)
michael@0 556 return;
michael@0 557
michael@0 558 nsCOMPtr<nsIStringBundle> stringBundle;
michael@0 559 stringBundleService->CreateBundle(
michael@0 560 "chrome://global-platform/locale/accessible.properties",
michael@0 561 getter_AddRefs(stringBundle));
michael@0 562 if (!stringBundle)
michael@0 563 return;
michael@0 564
michael@0 565 nsXPIDLString xsValue;
michael@0 566 nsresult rv = stringBundle->GetStringFromName(aKey.get(), getter_Copies(xsValue));
michael@0 567 if (NS_SUCCEEDED(rv))
michael@0 568 aStringOut.Assign(xsValue);
michael@0 569 }
michael@0 570
michael@0 571 uint64_t
michael@0 572 Accessible::VisibilityState()
michael@0 573 {
michael@0 574 nsIFrame* frame = GetFrame();
michael@0 575 if (!frame)
michael@0 576 return states::INVISIBLE;
michael@0 577
michael@0 578 // Walk the parent frame chain to see if there's invisible parent or the frame
michael@0 579 // is in background tab.
michael@0 580 if (!frame->StyleVisibility()->IsVisible())
michael@0 581 return states::INVISIBLE;
michael@0 582
michael@0 583 nsIFrame* curFrame = frame;
michael@0 584 do {
michael@0 585 nsView* view = curFrame->GetView();
michael@0 586 if (view && view->GetVisibility() == nsViewVisibility_kHide)
michael@0 587 return states::INVISIBLE;
michael@0 588
michael@0 589 if (nsLayoutUtils::IsPopup(curFrame))
michael@0 590 return 0;
michael@0 591
michael@0 592 // Offscreen state for background tab content and invisible for not selected
michael@0 593 // deck panel.
michael@0 594 nsIFrame* parentFrame = curFrame->GetParent();
michael@0 595 nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
michael@0 596 if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
michael@0 597 if (deckFrame->GetContent()->IsXUL() &&
michael@0 598 deckFrame->GetContent()->Tag() == nsGkAtoms::tabpanels)
michael@0 599 return states::OFFSCREEN;
michael@0 600
michael@0 601 NS_NOTREACHED("Children of not selected deck panel are not accessible.");
michael@0 602 return states::INVISIBLE;
michael@0 603 }
michael@0 604
michael@0 605 // If contained by scrollable frame then check that at least 12 pixels
michael@0 606 // around the object is visible, otherwise the object is offscreen.
michael@0 607 nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
michael@0 608 if (scrollableFrame) {
michael@0 609 nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
michael@0 610 nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
michael@0 611 frame, frame->GetRectRelativeToSelf(), parentFrame);
michael@0 612 if (!scrollPortRect.Contains(frameRect)) {
michael@0 613 const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
michael@0 614 scrollPortRect.Deflate(kMinPixels, kMinPixels);
michael@0 615 if (!scrollPortRect.Intersects(frameRect))
michael@0 616 return states::OFFSCREEN;
michael@0 617 }
michael@0 618 }
michael@0 619
michael@0 620 if (!parentFrame) {
michael@0 621 parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
michael@0 622 if (parentFrame && !parentFrame->StyleVisibility()->IsVisible())
michael@0 623 return states::INVISIBLE;
michael@0 624 }
michael@0 625
michael@0 626 curFrame = parentFrame;
michael@0 627 } while (curFrame);
michael@0 628
michael@0 629 // Zero area rects can occur in the first frame of a multi-frame text flow,
michael@0 630 // in which case the rendered text is not empty and the frame should not be
michael@0 631 // marked invisible.
michael@0 632 // XXX Can we just remove this check? Why do we need to mark empty
michael@0 633 // text invisible?
michael@0 634 if (frame->GetType() == nsGkAtoms::textFrame &&
michael@0 635 !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
michael@0 636 frame->GetRect().IsEmpty()) {
michael@0 637 nsAutoString renderedText;
michael@0 638 frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1);
michael@0 639 if (renderedText.IsEmpty())
michael@0 640 return states::INVISIBLE;
michael@0 641 }
michael@0 642
michael@0 643 return 0;
michael@0 644 }
michael@0 645
michael@0 646 uint64_t
michael@0 647 Accessible::NativeState()
michael@0 648 {
michael@0 649 uint64_t state = 0;
michael@0 650
michael@0 651 if (!IsInDocument())
michael@0 652 state |= states::STALE;
michael@0 653
michael@0 654 if (HasOwnContent() && mContent->IsElement()) {
michael@0 655 EventStates elementState = mContent->AsElement()->State();
michael@0 656
michael@0 657 if (elementState.HasState(NS_EVENT_STATE_INVALID))
michael@0 658 state |= states::INVALID;
michael@0 659
michael@0 660 if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
michael@0 661 state |= states::REQUIRED;
michael@0 662
michael@0 663 state |= NativeInteractiveState();
michael@0 664 if (FocusMgr()->IsFocused(this))
michael@0 665 state |= states::FOCUSED;
michael@0 666 }
michael@0 667
michael@0 668 // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
michael@0 669 state |= VisibilityState();
michael@0 670
michael@0 671 nsIFrame *frame = GetFrame();
michael@0 672 if (frame) {
michael@0 673 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
michael@0 674 state |= states::FLOATING;
michael@0 675
michael@0 676 // XXX we should look at layout for non XUL box frames, but need to decide
michael@0 677 // how that interacts with ARIA.
michael@0 678 if (HasOwnContent() && mContent->IsXUL() && frame->IsBoxFrame()) {
michael@0 679 const nsStyleXUL* xulStyle = frame->StyleXUL();
michael@0 680 if (xulStyle && frame->IsBoxFrame()) {
michael@0 681 // In XUL all boxes are either vertical or horizontal
michael@0 682 if (xulStyle->mBoxOrient == NS_STYLE_BOX_ORIENT_VERTICAL)
michael@0 683 state |= states::VERTICAL;
michael@0 684 else
michael@0 685 state |= states::HORIZONTAL;
michael@0 686 }
michael@0 687 }
michael@0 688 }
michael@0 689
michael@0 690 // Check if a XUL element has the popup attribute (an attached popup menu).
michael@0 691 if (HasOwnContent() && mContent->IsXUL() &&
michael@0 692 mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
michael@0 693 state |= states::HASPOPUP;
michael@0 694
michael@0 695 // Bypass the link states specialization for non links.
michael@0 696 if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
michael@0 697 mRoleMapEntry->role == roles::LINK)
michael@0 698 state |= NativeLinkState();
michael@0 699
michael@0 700 return state;
michael@0 701 }
michael@0 702
michael@0 703 uint64_t
michael@0 704 Accessible::NativeInteractiveState() const
michael@0 705 {
michael@0 706 if (!mContent->IsElement())
michael@0 707 return 0;
michael@0 708
michael@0 709 if (NativelyUnavailable())
michael@0 710 return states::UNAVAILABLE;
michael@0 711
michael@0 712 nsIFrame* frame = GetFrame();
michael@0 713 if (frame && frame->IsFocusable())
michael@0 714 return states::FOCUSABLE;
michael@0 715
michael@0 716 return 0;
michael@0 717 }
michael@0 718
michael@0 719 uint64_t
michael@0 720 Accessible::NativeLinkState() const
michael@0 721 {
michael@0 722 return 0;
michael@0 723 }
michael@0 724
michael@0 725 bool
michael@0 726 Accessible::NativelyUnavailable() const
michael@0 727 {
michael@0 728 if (mContent->IsHTML())
michael@0 729 return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
michael@0 730
michael@0 731 return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
michael@0 732 nsGkAtoms::_true, eCaseMatters);
michael@0 733 }
michael@0 734
michael@0 735 /* readonly attribute boolean focusedChild; */
michael@0 736 NS_IMETHODIMP
michael@0 737 Accessible::GetFocusedChild(nsIAccessible** aChild)
michael@0 738 {
michael@0 739 NS_ENSURE_ARG_POINTER(aChild);
michael@0 740 *aChild = nullptr;
michael@0 741
michael@0 742 if (IsDefunct())
michael@0 743 return NS_ERROR_FAILURE;
michael@0 744
michael@0 745 NS_IF_ADDREF(*aChild = FocusedChild());
michael@0 746 return NS_OK;
michael@0 747 }
michael@0 748
michael@0 749 Accessible*
michael@0 750 Accessible::FocusedChild()
michael@0 751 {
michael@0 752 Accessible* focus = FocusMgr()->FocusedAccessible();
michael@0 753 if (focus && (focus == this || focus->Parent() == this))
michael@0 754 return focus;
michael@0 755
michael@0 756 return nullptr;
michael@0 757 }
michael@0 758
michael@0 759 Accessible*
michael@0 760 Accessible::ChildAtPoint(int32_t aX, int32_t aY,
michael@0 761 EWhichChildAtPoint aWhichChild)
michael@0 762 {
michael@0 763 // If we can't find the point in a child, we will return the fallback answer:
michael@0 764 // we return |this| if the point is within it, otherwise nullptr.
michael@0 765 int32_t x = 0, y = 0, width = 0, height = 0;
michael@0 766 nsresult rv = GetBounds(&x, &y, &width, &height);
michael@0 767 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 768
michael@0 769 Accessible* fallbackAnswer = nullptr;
michael@0 770 if (aX >= x && aX < x + width && aY >= y && aY < y + height)
michael@0 771 fallbackAnswer = this;
michael@0 772
michael@0 773 if (nsAccUtils::MustPrune(this)) // Do not dig any further
michael@0 774 return fallbackAnswer;
michael@0 775
michael@0 776 // Search an accessible at the given point starting from accessible document
michael@0 777 // because containing block (see CSS2) for out of flow element (for example,
michael@0 778 // absolutely positioned element) may be different from its DOM parent and
michael@0 779 // therefore accessible for containing block may be different from accessible
michael@0 780 // for DOM parent but GetFrameForPoint() should be called for containing block
michael@0 781 // to get an out of flow element.
michael@0 782 DocAccessible* accDocument = Document();
michael@0 783 NS_ENSURE_TRUE(accDocument, nullptr);
michael@0 784
michael@0 785 nsIFrame* rootFrame = accDocument->GetFrame();
michael@0 786 NS_ENSURE_TRUE(rootFrame, nullptr);
michael@0 787
michael@0 788 nsIFrame* startFrame = rootFrame;
michael@0 789
michael@0 790 // Check whether the point is at popup content.
michael@0 791 nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
michael@0 792 NS_ENSURE_TRUE(rootWidget, nullptr);
michael@0 793
michael@0 794 nsIntRect rootRect;
michael@0 795 rootWidget->GetScreenBounds(rootRect);
michael@0 796
michael@0 797 WidgetMouseEvent dummyEvent(true, NS_MOUSE_MOVE, rootWidget,
michael@0 798 WidgetMouseEvent::eSynthesized);
michael@0 799 dummyEvent.refPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
michael@0 800
michael@0 801 nsIFrame* popupFrame = nsLayoutUtils::
michael@0 802 GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
michael@0 803 &dummyEvent);
michael@0 804 if (popupFrame) {
michael@0 805 // If 'this' accessible is not inside the popup then ignore the popup when
michael@0 806 // searching an accessible at point.
michael@0 807 DocAccessible* popupDoc =
michael@0 808 GetAccService()->GetDocAccessible(popupFrame->GetContent()->OwnerDoc());
michael@0 809 Accessible* popupAcc =
michael@0 810 popupDoc->GetAccessibleOrContainer(popupFrame->GetContent());
michael@0 811 Accessible* popupChild = this;
michael@0 812 while (popupChild && !popupChild->IsDoc() && popupChild != popupAcc)
michael@0 813 popupChild = popupChild->Parent();
michael@0 814
michael@0 815 if (popupChild == popupAcc)
michael@0 816 startFrame = popupFrame;
michael@0 817 }
michael@0 818
michael@0 819 nsPresContext* presContext = startFrame->PresContext();
michael@0 820 nsRect screenRect = startFrame->GetScreenRectInAppUnits();
michael@0 821 nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
michael@0 822 presContext->DevPixelsToAppUnits(aY) - screenRect.y);
michael@0 823 nsIFrame* foundFrame = nsLayoutUtils::GetFrameForPoint(startFrame, offset);
michael@0 824
michael@0 825 nsIContent* content = nullptr;
michael@0 826 if (!foundFrame || !(content = foundFrame->GetContent()))
michael@0 827 return fallbackAnswer;
michael@0 828
michael@0 829 // Get accessible for the node with the point or the first accessible in
michael@0 830 // the DOM parent chain.
michael@0 831 DocAccessible* contentDocAcc = GetAccService()->
michael@0 832 GetDocAccessible(content->OwnerDoc());
michael@0 833
michael@0 834 // contentDocAcc in some circumstances can be nullptr. See bug 729861
michael@0 835 NS_ASSERTION(contentDocAcc, "could not get the document accessible");
michael@0 836 if (!contentDocAcc)
michael@0 837 return fallbackAnswer;
michael@0 838
michael@0 839 Accessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
michael@0 840 if (!accessible)
michael@0 841 return fallbackAnswer;
michael@0 842
michael@0 843 // Hurray! We have an accessible for the frame that layout gave us.
michael@0 844 // Since DOM node of obtained accessible may be out of flow then we should
michael@0 845 // ensure obtained accessible is a child of this accessible.
michael@0 846 Accessible* child = accessible;
michael@0 847 while (child != this) {
michael@0 848 Accessible* parent = child->Parent();
michael@0 849 if (!parent) {
michael@0 850 // Reached the top of the hierarchy. These bounds were inside an
michael@0 851 // accessible that is not a descendant of this one.
michael@0 852 return fallbackAnswer;
michael@0 853 }
michael@0 854
michael@0 855 // If we landed on a legitimate child of |this|, and we want the direct
michael@0 856 // child, return it here.
michael@0 857 if (parent == this && aWhichChild == eDirectChild)
michael@0 858 return child;
michael@0 859
michael@0 860 child = parent;
michael@0 861 }
michael@0 862
michael@0 863 // Manually walk through accessible children and see if the are within this
michael@0 864 // point. Skip offscreen or invisible accessibles. This takes care of cases
michael@0 865 // where layout won't walk into things for us, such as image map areas and
michael@0 866 // sub documents (XXX: subdocuments should be handled by methods of
michael@0 867 // OuterDocAccessibles).
michael@0 868 uint32_t childCount = accessible->ChildCount();
michael@0 869 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 870 Accessible* child = accessible->GetChildAt(childIdx);
michael@0 871
michael@0 872 int32_t childX, childY, childWidth, childHeight;
michael@0 873 child->GetBounds(&childX, &childY, &childWidth, &childHeight);
michael@0 874 if (aX >= childX && aX < childX + childWidth &&
michael@0 875 aY >= childY && aY < childY + childHeight &&
michael@0 876 (child->State() & states::INVISIBLE) == 0) {
michael@0 877
michael@0 878 if (aWhichChild == eDeepestChild)
michael@0 879 return child->ChildAtPoint(aX, aY, eDeepestChild);
michael@0 880
michael@0 881 return child;
michael@0 882 }
michael@0 883 }
michael@0 884
michael@0 885 return accessible;
michael@0 886 }
michael@0 887
michael@0 888 // nsIAccessible getChildAtPoint(in long x, in long y)
michael@0 889 NS_IMETHODIMP
michael@0 890 Accessible::GetChildAtPoint(int32_t aX, int32_t aY,
michael@0 891 nsIAccessible** aAccessible)
michael@0 892 {
michael@0 893 NS_ENSURE_ARG_POINTER(aAccessible);
michael@0 894 *aAccessible = nullptr;
michael@0 895
michael@0 896 if (IsDefunct())
michael@0 897 return NS_ERROR_FAILURE;
michael@0 898
michael@0 899 NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDirectChild));
michael@0 900 return NS_OK;
michael@0 901 }
michael@0 902
michael@0 903 // nsIAccessible getDeepestChildAtPoint(in long x, in long y)
michael@0 904 NS_IMETHODIMP
michael@0 905 Accessible::GetDeepestChildAtPoint(int32_t aX, int32_t aY,
michael@0 906 nsIAccessible** aAccessible)
michael@0 907 {
michael@0 908 NS_ENSURE_ARG_POINTER(aAccessible);
michael@0 909 *aAccessible = nullptr;
michael@0 910
michael@0 911 if (IsDefunct())
michael@0 912 return NS_ERROR_FAILURE;
michael@0 913
michael@0 914 NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDeepestChild));
michael@0 915 return NS_OK;
michael@0 916 }
michael@0 917
michael@0 918 void
michael@0 919 Accessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
michael@0 920 {
michael@0 921 nsIFrame* frame = GetFrame();
michael@0 922 if (frame && mContent) {
michael@0 923 nsRect* hitRegionRect = static_cast<nsRect*>(mContent->GetProperty(nsGkAtoms::hitregion));
michael@0 924
michael@0 925 if (hitRegionRect) {
michael@0 926 // This is for canvas fallback content
michael@0 927 // Find a canvas frame the found hit region is relative to.
michael@0 928 nsIFrame* canvasFrame = frame->GetParent();
michael@0 929 while (canvasFrame && (canvasFrame->GetType() != nsGkAtoms::HTMLCanvasFrame))
michael@0 930 canvasFrame = canvasFrame->GetParent();
michael@0 931
michael@0 932 // make the canvas the bounding frame
michael@0 933 if (canvasFrame) {
michael@0 934 *aBoundingFrame = canvasFrame;
michael@0 935
michael@0 936 aTotalBounds = *hitRegionRect;
michael@0 937
michael@0 938 return;
michael@0 939 }
michael@0 940 }
michael@0 941
michael@0 942 *aBoundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
michael@0 943 aTotalBounds = nsLayoutUtils::
michael@0 944 GetAllInFlowRectsUnion(frame, *aBoundingFrame,
michael@0 945 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
michael@0 946 }
michael@0 947 }
michael@0 948
michael@0 949 /* void getBounds (out long x, out long y, out long width, out long height); */
michael@0 950 NS_IMETHODIMP
michael@0 951 Accessible::GetBounds(int32_t* aX, int32_t* aY,
michael@0 952 int32_t* aWidth, int32_t* aHeight)
michael@0 953 {
michael@0 954 NS_ENSURE_ARG_POINTER(aX);
michael@0 955 *aX = 0;
michael@0 956 NS_ENSURE_ARG_POINTER(aY);
michael@0 957 *aY = 0;
michael@0 958 NS_ENSURE_ARG_POINTER(aWidth);
michael@0 959 *aWidth = 0;
michael@0 960 NS_ENSURE_ARG_POINTER(aHeight);
michael@0 961 *aHeight = 0;
michael@0 962
michael@0 963 if (IsDefunct())
michael@0 964 return NS_ERROR_FAILURE;
michael@0 965
michael@0 966 // This routine will get the entire rectangle for all the frames in this node.
michael@0 967 // -------------------------------------------------------------------------
michael@0 968 // Primary Frame for node
michael@0 969 // Another frame, same node <- Example
michael@0 970 // Another frame, same node
michael@0 971
michael@0 972 nsRect unionRectTwips;
michael@0 973 nsIFrame* boundingFrame = nullptr;
michael@0 974 GetBoundsRect(unionRectTwips, &boundingFrame); // Unions up all primary frames for this node and all siblings after it
michael@0 975 NS_ENSURE_STATE(boundingFrame);
michael@0 976
michael@0 977 nsPresContext* presContext = mDoc->PresContext();
michael@0 978 *aX = presContext->AppUnitsToDevPixels(unionRectTwips.x);
michael@0 979 *aY = presContext->AppUnitsToDevPixels(unionRectTwips.y);
michael@0 980 *aWidth = presContext->AppUnitsToDevPixels(unionRectTwips.width);
michael@0 981 *aHeight = presContext->AppUnitsToDevPixels(unionRectTwips.height);
michael@0 982
michael@0 983 // We have the union of the rectangle, now we need to put it in absolute screen coords
michael@0 984 nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
michael@0 985 ToNearestPixels(presContext->AppUnitsPerDevPixel());
michael@0 986 *aX += orgRectPixels.x;
michael@0 987 *aY += orgRectPixels.y;
michael@0 988
michael@0 989 return NS_OK;
michael@0 990 }
michael@0 991
michael@0 992 NS_IMETHODIMP
michael@0 993 Accessible::SetSelected(bool aSelect)
michael@0 994 {
michael@0 995 if (IsDefunct())
michael@0 996 return NS_ERROR_FAILURE;
michael@0 997
michael@0 998 if (!HasOwnContent())
michael@0 999 return NS_OK;
michael@0 1000
michael@0 1001 Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
michael@0 1002 if (select) {
michael@0 1003 if (select->State() & states::MULTISELECTABLE) {
michael@0 1004 if (mRoleMapEntry) {
michael@0 1005 if (aSelect) {
michael@0 1006 return mContent->SetAttr(kNameSpaceID_None,
michael@0 1007 nsGkAtoms::aria_selected,
michael@0 1008 NS_LITERAL_STRING("true"), true);
michael@0 1009 }
michael@0 1010 return mContent->UnsetAttr(kNameSpaceID_None,
michael@0 1011 nsGkAtoms::aria_selected, true);
michael@0 1012 }
michael@0 1013
michael@0 1014 return NS_OK;
michael@0 1015 }
michael@0 1016
michael@0 1017 return aSelect ? TakeFocus() : NS_ERROR_FAILURE;
michael@0 1018 }
michael@0 1019
michael@0 1020 return NS_OK;
michael@0 1021 }
michael@0 1022
michael@0 1023 NS_IMETHODIMP
michael@0 1024 Accessible::TakeSelection()
michael@0 1025 {
michael@0 1026 if (IsDefunct())
michael@0 1027 return NS_ERROR_FAILURE;
michael@0 1028
michael@0 1029 Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
michael@0 1030 if (select) {
michael@0 1031 if (select->State() & states::MULTISELECTABLE)
michael@0 1032 select->UnselectAll();
michael@0 1033 return SetSelected(true);
michael@0 1034 }
michael@0 1035
michael@0 1036 return NS_ERROR_FAILURE;
michael@0 1037 }
michael@0 1038
michael@0 1039 NS_IMETHODIMP
michael@0 1040 Accessible::TakeFocus()
michael@0 1041 {
michael@0 1042 if (IsDefunct())
michael@0 1043 return NS_ERROR_FAILURE;
michael@0 1044
michael@0 1045 nsIFrame *frame = GetFrame();
michael@0 1046 NS_ENSURE_STATE(frame);
michael@0 1047
michael@0 1048 nsIContent* focusContent = mContent;
michael@0 1049
michael@0 1050 // If the accessible focus is managed by container widget then focus the
michael@0 1051 // widget and set the accessible as its current item.
michael@0 1052 if (!frame->IsFocusable()) {
michael@0 1053 Accessible* widget = ContainerWidget();
michael@0 1054 if (widget && widget->AreItemsOperable()) {
michael@0 1055 nsIContent* widgetElm = widget->GetContent();
michael@0 1056 nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
michael@0 1057 if (widgetFrame && widgetFrame->IsFocusable()) {
michael@0 1058 focusContent = widgetElm;
michael@0 1059 widget->SetCurrentItem(this);
michael@0 1060 }
michael@0 1061 }
michael@0 1062 }
michael@0 1063
michael@0 1064 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
michael@0 1065 nsFocusManager* fm = nsFocusManager::GetFocusManager();
michael@0 1066 if (fm)
michael@0 1067 fm->SetFocus(element, 0);
michael@0 1068
michael@0 1069 return NS_OK;
michael@0 1070 }
michael@0 1071
michael@0 1072 void
michael@0 1073 Accessible::XULElmName(DocAccessible* aDocument,
michael@0 1074 nsIContent* aElm, nsString& aName)
michael@0 1075 {
michael@0 1076 /**
michael@0 1077 * 3 main cases for XUL Controls to be labeled
michael@0 1078 * 1 - control contains label="foo"
michael@0 1079 * 2 - control has, as a child, a label element
michael@0 1080 * - label has either value="foo" or children
michael@0 1081 * 3 - non-child label contains control="controlID"
michael@0 1082 * - label has either value="foo" or children
michael@0 1083 * Once a label is found, the search is discontinued, so a control
michael@0 1084 * that has a label child as well as having a label external to
michael@0 1085 * the control that uses the control="controlID" syntax will use
michael@0 1086 * the child label for its Name.
michael@0 1087 */
michael@0 1088
michael@0 1089 // CASE #1 (via label attribute) -- great majority of the cases
michael@0 1090 nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
michael@0 1091 if (labeledEl) {
michael@0 1092 labeledEl->GetLabel(aName);
michael@0 1093 } else {
michael@0 1094 nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
michael@0 1095 if (itemEl) {
michael@0 1096 itemEl->GetLabel(aName);
michael@0 1097 } else {
michael@0 1098 nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
michael@0 1099 // Use label if this is not a select control element which
michael@0 1100 // uses label attribute to indicate which option is selected
michael@0 1101 if (!select) {
michael@0 1102 nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
michael@0 1103 if (xulEl)
michael@0 1104 xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
michael@0 1105 }
michael@0 1106 }
michael@0 1107 }
michael@0 1108
michael@0 1109 // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
michael@0 1110 if (aName.IsEmpty()) {
michael@0 1111 Accessible* labelAcc = nullptr;
michael@0 1112 XULLabelIterator iter(aDocument, aElm);
michael@0 1113 while ((labelAcc = iter.Next())) {
michael@0 1114 nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
michael@0 1115 do_QueryInterface(labelAcc->GetContent());
michael@0 1116 // Check if label's value attribute is used
michael@0 1117 if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(aName)) && aName.IsEmpty()) {
michael@0 1118 // If no value attribute, a non-empty label must contain
michael@0 1119 // children that define its text -- possibly using HTML
michael@0 1120 nsTextEquivUtils::
michael@0 1121 AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
michael@0 1122 }
michael@0 1123 }
michael@0 1124 }
michael@0 1125
michael@0 1126 aName.CompressWhitespace();
michael@0 1127 if (!aName.IsEmpty())
michael@0 1128 return;
michael@0 1129
michael@0 1130 // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
michael@0 1131 nsIContent *bindingParent = aElm->GetBindingParent();
michael@0 1132 nsIContent* parent =
michael@0 1133 bindingParent? bindingParent->GetParent() : aElm->GetParent();
michael@0 1134 while (parent) {
michael@0 1135 if (parent->Tag() == nsGkAtoms::toolbaritem &&
michael@0 1136 parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
michael@0 1137 aName.CompressWhitespace();
michael@0 1138 return;
michael@0 1139 }
michael@0 1140 parent = parent->GetParent();
michael@0 1141 }
michael@0 1142 }
michael@0 1143
michael@0 1144 nsresult
michael@0 1145 Accessible::HandleAccEvent(AccEvent* aEvent)
michael@0 1146 {
michael@0 1147 NS_ENSURE_ARG_POINTER(aEvent);
michael@0 1148
michael@0 1149 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
michael@0 1150 NS_ENSURE_TRUE(obsService, NS_ERROR_FAILURE);
michael@0 1151
michael@0 1152 nsCOMPtr<nsISimpleEnumerator> observers;
michael@0 1153 obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC,
michael@0 1154 getter_AddRefs(observers));
michael@0 1155
michael@0 1156 NS_ENSURE_STATE(observers);
michael@0 1157
michael@0 1158 bool hasObservers = false;
michael@0 1159 observers->HasMoreElements(&hasObservers);
michael@0 1160 if (hasObservers) {
michael@0 1161 nsCOMPtr<nsIAccessibleEvent> event = MakeXPCEvent(aEvent);
michael@0 1162 return obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
michael@0 1163 }
michael@0 1164
michael@0 1165 return NS_OK;
michael@0 1166 }
michael@0 1167
michael@0 1168 NS_IMETHODIMP
michael@0 1169 Accessible::GetRole(uint32_t *aRole)
michael@0 1170 {
michael@0 1171 NS_ENSURE_ARG_POINTER(aRole);
michael@0 1172 *aRole = nsIAccessibleRole::ROLE_NOTHING;
michael@0 1173
michael@0 1174 if (IsDefunct())
michael@0 1175 return NS_ERROR_FAILURE;
michael@0 1176
michael@0 1177 *aRole = Role();
michael@0 1178 return NS_OK;
michael@0 1179 }
michael@0 1180
michael@0 1181 NS_IMETHODIMP
michael@0 1182 Accessible::GetAttributes(nsIPersistentProperties** aAttributes)
michael@0 1183 {
michael@0 1184 NS_ENSURE_ARG_POINTER(aAttributes);
michael@0 1185 *aAttributes = nullptr;
michael@0 1186
michael@0 1187 if (IsDefunct())
michael@0 1188 return NS_ERROR_FAILURE;
michael@0 1189
michael@0 1190 nsCOMPtr<nsIPersistentProperties> attributes = Attributes();
michael@0 1191 attributes.swap(*aAttributes);
michael@0 1192
michael@0 1193 return NS_OK;
michael@0 1194 }
michael@0 1195
michael@0 1196 already_AddRefed<nsIPersistentProperties>
michael@0 1197 Accessible::Attributes()
michael@0 1198 {
michael@0 1199 nsCOMPtr<nsIPersistentProperties> attributes = NativeAttributes();
michael@0 1200 if (!HasOwnContent() || !mContent->IsElement())
michael@0 1201 return attributes.forget();
michael@0 1202
michael@0 1203 // 'xml-roles' attribute coming from ARIA.
michael@0 1204 nsAutoString xmlRoles, unused;
michael@0 1205 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles)) {
michael@0 1206 attributes->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"),
michael@0 1207 xmlRoles, unused);
michael@0 1208 }
michael@0 1209
michael@0 1210 // Expose object attributes from ARIA attributes.
michael@0 1211 aria::AttrIterator attribIter(mContent);
michael@0 1212 nsAutoString name, value;
michael@0 1213 while(attribIter.Next(name, value))
michael@0 1214 attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
michael@0 1215
michael@0 1216 // If there is no aria-live attribute then expose default value of 'live'
michael@0 1217 // object attribute used for ARIA role of this accessible.
michael@0 1218 if (mRoleMapEntry) {
michael@0 1219 nsAutoString live;
michael@0 1220 nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
michael@0 1221 if (live.IsEmpty()) {
michael@0 1222 if (nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live))
michael@0 1223 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
michael@0 1224 }
michael@0 1225 }
michael@0 1226
michael@0 1227 return attributes.forget();
michael@0 1228 }
michael@0 1229
michael@0 1230 already_AddRefed<nsIPersistentProperties>
michael@0 1231 Accessible::NativeAttributes()
michael@0 1232 {
michael@0 1233 nsCOMPtr<nsIPersistentProperties> attributes =
michael@0 1234 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
michael@0 1235
michael@0 1236 nsAutoString unused;
michael@0 1237
michael@0 1238 // We support values, so expose the string value as well, via the valuetext
michael@0 1239 // object attribute. We test for the value interface because we don't want
michael@0 1240 // to expose traditional Value() information such as URL's on links and
michael@0 1241 // documents, or text in an input.
michael@0 1242 if (HasNumericValue()) {
michael@0 1243 nsAutoString valuetext;
michael@0 1244 GetValue(valuetext);
michael@0 1245 attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext,
michael@0 1246 unused);
michael@0 1247 }
michael@0 1248
michael@0 1249 // Expose checkable object attribute if the accessible has checkable state
michael@0 1250 if (State() & states::CHECKABLE) {
michael@0 1251 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable,
michael@0 1252 NS_LITERAL_STRING("true"));
michael@0 1253 }
michael@0 1254
michael@0 1255 // Expose 'explicit-name' attribute.
michael@0 1256 nsAutoString name;
michael@0 1257 if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
michael@0 1258 attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
michael@0 1259 NS_LITERAL_STRING("true"), unused);
michael@0 1260 }
michael@0 1261
michael@0 1262 // Group attributes (level/setsize/posinset)
michael@0 1263 GroupPos groupPos = GroupPosition();
michael@0 1264 nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
michael@0 1265 groupPos.setSize, groupPos.posInSet);
michael@0 1266
michael@0 1267 // If the accessible doesn't have own content (such as list item bullet or
michael@0 1268 // xul tree item) then don't calculate content based attributes.
michael@0 1269 if (!HasOwnContent())
michael@0 1270 return attributes.forget();
michael@0 1271
michael@0 1272 nsEventShell::GetEventAttributes(GetNode(), attributes);
michael@0 1273
michael@0 1274 // Get container-foo computed live region properties based on the closest
michael@0 1275 // container with the live region attribute. Inner nodes override outer nodes
michael@0 1276 // within the same document. The inner nodes can be used to override live
michael@0 1277 // region behavior on more general outer nodes. However, nodes in outer
michael@0 1278 // documents override nodes in inner documents: outer doc author may want to
michael@0 1279 // override properties on a widget they used in an iframe.
michael@0 1280 nsIContent* startContent = mContent;
michael@0 1281 while (startContent) {
michael@0 1282 nsIDocument* doc = startContent->GetDocument();
michael@0 1283 if (!doc)
michael@0 1284 break;
michael@0 1285
michael@0 1286 nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
michael@0 1287 nsCoreUtils::GetRoleContent(doc));
michael@0 1288
michael@0 1289 // Allow ARIA live region markup from outer documents to override
michael@0 1290 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
michael@0 1291 if (!docShellTreeItem)
michael@0 1292 break;
michael@0 1293
michael@0 1294 nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
michael@0 1295 docShellTreeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
michael@0 1296 if (!sameTypeParent || sameTypeParent == docShellTreeItem)
michael@0 1297 break;
michael@0 1298
michael@0 1299 nsIDocument* parentDoc = doc->GetParentDocument();
michael@0 1300 if (!parentDoc)
michael@0 1301 break;
michael@0 1302
michael@0 1303 startContent = parentDoc->FindContentForSubDocument(doc);
michael@0 1304 }
michael@0 1305
michael@0 1306 if (!mContent->IsElement())
michael@0 1307 return attributes.forget();
michael@0 1308
michael@0 1309 nsAutoString id;
michael@0 1310 if (nsCoreUtils::GetID(mContent, id))
michael@0 1311 attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, unused);
michael@0 1312
michael@0 1313 // Expose class because it may have useful microformat information.
michael@0 1314 nsAutoString _class;
michael@0 1315 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
michael@0 1316 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::_class, _class);
michael@0 1317
michael@0 1318 // Expose tag.
michael@0 1319 nsAutoString tagName;
michael@0 1320 mContent->NodeInfo()->GetName(tagName);
michael@0 1321 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tag, tagName);
michael@0 1322
michael@0 1323 // Expose draggable object attribute.
michael@0 1324 nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
michael@0 1325 if (htmlElement) {
michael@0 1326 bool draggable = false;
michael@0 1327 htmlElement->GetDraggable(&draggable);
michael@0 1328 if (draggable) {
michael@0 1329 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::draggable,
michael@0 1330 NS_LITERAL_STRING("true"));
michael@0 1331 }
michael@0 1332 }
michael@0 1333
michael@0 1334 // Don't calculate CSS-based object attributes when no frame (i.e.
michael@0 1335 // the accessible is unattached from the tree).
michael@0 1336 if (!mContent->GetPrimaryFrame())
michael@0 1337 return attributes.forget();
michael@0 1338
michael@0 1339 // CSS style based object attributes.
michael@0 1340 nsAutoString value;
michael@0 1341 StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell());
michael@0 1342
michael@0 1343 // Expose 'display' attribute.
michael@0 1344 styleInfo.Display(value);
michael@0 1345 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::display, value);
michael@0 1346
michael@0 1347 // Expose 'text-align' attribute.
michael@0 1348 styleInfo.TextAlign(value);
michael@0 1349 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textAlign, value);
michael@0 1350
michael@0 1351 // Expose 'text-indent' attribute.
michael@0 1352 styleInfo.TextIndent(value);
michael@0 1353 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textIndent, value);
michael@0 1354
michael@0 1355 // Expose 'margin-left' attribute.
michael@0 1356 styleInfo.MarginLeft(value);
michael@0 1357 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginLeft, value);
michael@0 1358
michael@0 1359 // Expose 'margin-right' attribute.
michael@0 1360 styleInfo.MarginRight(value);
michael@0 1361 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginRight, value);
michael@0 1362
michael@0 1363 // Expose 'margin-top' attribute.
michael@0 1364 styleInfo.MarginTop(value);
michael@0 1365 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginTop, value);
michael@0 1366
michael@0 1367 // Expose 'margin-bottom' attribute.
michael@0 1368 styleInfo.MarginBottom(value);
michael@0 1369 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginBottom, value);
michael@0 1370
michael@0 1371 return attributes.forget();
michael@0 1372 }
michael@0 1373
michael@0 1374 GroupPos
michael@0 1375 Accessible::GroupPosition()
michael@0 1376 {
michael@0 1377 GroupPos groupPos;
michael@0 1378 if (!HasOwnContent())
michael@0 1379 return groupPos;
michael@0 1380
michael@0 1381 // Get group position from ARIA attributes.
michael@0 1382 nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_level, &groupPos.level);
michael@0 1383 nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_setsize, &groupPos.setSize);
michael@0 1384 nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_posinset, &groupPos.posInSet);
michael@0 1385
michael@0 1386 // If ARIA is missed and the accessible is visible then calculate group
michael@0 1387 // position from hierarchy.
michael@0 1388 if (State() & states::INVISIBLE)
michael@0 1389 return groupPos;
michael@0 1390
michael@0 1391 // Calculate group level if ARIA is missed.
michael@0 1392 if (groupPos.level == 0) {
michael@0 1393 int32_t level = GetLevelInternal();
michael@0 1394 if (level != 0)
michael@0 1395 groupPos.level = level;
michael@0 1396 }
michael@0 1397
michael@0 1398 // Calculate position in group and group size if ARIA is missed.
michael@0 1399 if (groupPos.posInSet == 0 || groupPos.setSize == 0) {
michael@0 1400 int32_t posInSet = 0, setSize = 0;
michael@0 1401 GetPositionAndSizeInternal(&posInSet, &setSize);
michael@0 1402 if (posInSet != 0 && setSize != 0) {
michael@0 1403 if (groupPos.posInSet == 0)
michael@0 1404 groupPos.posInSet = posInSet;
michael@0 1405
michael@0 1406 if (groupPos.setSize == 0)
michael@0 1407 groupPos.setSize = setSize;
michael@0 1408 }
michael@0 1409 }
michael@0 1410
michael@0 1411 return groupPos;
michael@0 1412 }
michael@0 1413
michael@0 1414 NS_IMETHODIMP
michael@0 1415 Accessible::ScriptableGroupPosition(int32_t* aGroupLevel,
michael@0 1416 int32_t* aSimilarItemsInGroup,
michael@0 1417 int32_t* aPositionInGroup)
michael@0 1418 {
michael@0 1419 NS_ENSURE_ARG_POINTER(aGroupLevel);
michael@0 1420 *aGroupLevel = 0;
michael@0 1421
michael@0 1422 NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
michael@0 1423 *aSimilarItemsInGroup = 0;
michael@0 1424
michael@0 1425 NS_ENSURE_ARG_POINTER(aPositionInGroup);
michael@0 1426 *aPositionInGroup = 0;
michael@0 1427
michael@0 1428 if (IsDefunct())
michael@0 1429 return NS_ERROR_FAILURE;
michael@0 1430
michael@0 1431 GroupPos groupPos = GroupPosition();
michael@0 1432
michael@0 1433 *aGroupLevel = groupPos.level;
michael@0 1434 *aSimilarItemsInGroup = groupPos.setSize;
michael@0 1435 *aPositionInGroup = groupPos.posInSet;
michael@0 1436
michael@0 1437 return NS_OK;
michael@0 1438 }
michael@0 1439
michael@0 1440 NS_IMETHODIMP
michael@0 1441 Accessible::GetState(uint32_t* aState, uint32_t* aExtraState)
michael@0 1442 {
michael@0 1443 NS_ENSURE_ARG_POINTER(aState);
michael@0 1444
michael@0 1445 nsAccUtils::To32States(State(), aState, aExtraState);
michael@0 1446 return NS_OK;
michael@0 1447 }
michael@0 1448
michael@0 1449 uint64_t
michael@0 1450 Accessible::State()
michael@0 1451 {
michael@0 1452 if (IsDefunct())
michael@0 1453 return states::DEFUNCT;
michael@0 1454
michael@0 1455 uint64_t state = NativeState();
michael@0 1456 // Apply ARIA states to be sure accessible states will be overridden.
michael@0 1457 ApplyARIAState(&state);
michael@0 1458
michael@0 1459 // If this is an ARIA item of the selectable widget and if it's focused and
michael@0 1460 // not marked unselected explicitly (i.e. aria-selected="false") then expose
michael@0 1461 // it as selected to make ARIA widget authors life easier.
michael@0 1462 if (mRoleMapEntry && !(state & states::SELECTED) &&
michael@0 1463 !mContent->AttrValueIs(kNameSpaceID_None,
michael@0 1464 nsGkAtoms::aria_selected,
michael@0 1465 nsGkAtoms::_false, eCaseMatters)) {
michael@0 1466 // Special case for tabs: focused tab or focus inside related tab panel
michael@0 1467 // implies selected state.
michael@0 1468 if (mRoleMapEntry->role == roles::PAGETAB) {
michael@0 1469 if (state & states::FOCUSED) {
michael@0 1470 state |= states::SELECTED;
michael@0 1471 } else {
michael@0 1472 // If focus is in a child of the tab panel surely the tab is selected!
michael@0 1473 Relation rel = RelationByType(RelationType::LABEL_FOR);
michael@0 1474 Accessible* relTarget = nullptr;
michael@0 1475 while ((relTarget = rel.Next())) {
michael@0 1476 if (relTarget->Role() == roles::PROPERTYPAGE &&
michael@0 1477 FocusMgr()->IsFocusWithin(relTarget))
michael@0 1478 state |= states::SELECTED;
michael@0 1479 }
michael@0 1480 }
michael@0 1481 } else if (state & states::FOCUSED) {
michael@0 1482 Accessible* container = nsAccUtils::GetSelectableContainer(this, state);
michael@0 1483 if (container &&
michael@0 1484 !nsAccUtils::HasDefinedARIAToken(container->GetContent(),
michael@0 1485 nsGkAtoms::aria_multiselectable)) {
michael@0 1486 state |= states::SELECTED;
michael@0 1487 }
michael@0 1488 }
michael@0 1489 }
michael@0 1490
michael@0 1491 const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
michael@0 1492 if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
michael@0 1493 // Cannot be both expanded and collapsed -- this happens in ARIA expanded
michael@0 1494 // combobox because of limitation of ARIAMap.
michael@0 1495 // XXX: Perhaps we will be able to make this less hacky if we support
michael@0 1496 // extended states in ARIAMap, e.g. derive COLLAPSED from
michael@0 1497 // EXPANDABLE && !EXPANDED.
michael@0 1498 state &= ~states::COLLAPSED;
michael@0 1499 }
michael@0 1500
michael@0 1501 if (!(state & states::UNAVAILABLE)) {
michael@0 1502 state |= states::ENABLED | states::SENSITIVE;
michael@0 1503
michael@0 1504 // If the object is a current item of container widget then mark it as
michael@0 1505 // ACTIVE. This allows screen reader virtual buffer modes to know which
michael@0 1506 // descendant is the current one that would get focus if the user navigates
michael@0 1507 // to the container widget.
michael@0 1508 Accessible* widget = ContainerWidget();
michael@0 1509 if (widget && widget->CurrentItem() == this)
michael@0 1510 state |= states::ACTIVE;
michael@0 1511 }
michael@0 1512
michael@0 1513 if ((state & states::COLLAPSED) || (state & states::EXPANDED))
michael@0 1514 state |= states::EXPANDABLE;
michael@0 1515
michael@0 1516 // For some reasons DOM node may have not a frame. We tract such accessibles
michael@0 1517 // as invisible.
michael@0 1518 nsIFrame *frame = GetFrame();
michael@0 1519 if (!frame)
michael@0 1520 return state;
michael@0 1521
michael@0 1522 const nsStyleDisplay* display = frame->StyleDisplay();
michael@0 1523 if (display && display->mOpacity == 1.0f &&
michael@0 1524 !(state & states::INVISIBLE)) {
michael@0 1525 state |= states::OPAQUE1;
michael@0 1526 }
michael@0 1527
michael@0 1528 return state;
michael@0 1529 }
michael@0 1530
michael@0 1531 void
michael@0 1532 Accessible::ApplyARIAState(uint64_t* aState) const
michael@0 1533 {
michael@0 1534 if (!mContent->IsElement())
michael@0 1535 return;
michael@0 1536
michael@0 1537 dom::Element* element = mContent->AsElement();
michael@0 1538
michael@0 1539 // Test for universal states first
michael@0 1540 *aState |= aria::UniversalStatesFor(element);
michael@0 1541
michael@0 1542 if (mRoleMapEntry) {
michael@0 1543
michael@0 1544 // We only force the readonly bit off if we have a real mapping for the aria
michael@0 1545 // role. This preserves the ability for screen readers to use readonly
michael@0 1546 // (primarily on the document) as the hint for creating a virtual buffer.
michael@0 1547 if (mRoleMapEntry->role != roles::NOTHING)
michael@0 1548 *aState &= ~states::READONLY;
michael@0 1549
michael@0 1550 if (mContent->HasAttr(kNameSpaceID_None, mContent->GetIDAttributeName())) {
michael@0 1551 // If has a role & ID and aria-activedescendant on the container, assume focusable
michael@0 1552 nsIContent *ancestorContent = mContent;
michael@0 1553 while ((ancestorContent = ancestorContent->GetParent()) != nullptr) {
michael@0 1554 if (ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant)) {
michael@0 1555 // ancestor has activedescendant property, this content could be active
michael@0 1556 *aState |= states::FOCUSABLE;
michael@0 1557 break;
michael@0 1558 }
michael@0 1559 }
michael@0 1560 }
michael@0 1561 }
michael@0 1562
michael@0 1563 if (*aState & states::FOCUSABLE) {
michael@0 1564 // Special case: aria-disabled propagates from ancestors down to any focusable descendant
michael@0 1565 nsIContent *ancestorContent = mContent;
michael@0 1566 while ((ancestorContent = ancestorContent->GetParent()) != nullptr) {
michael@0 1567 if (ancestorContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
michael@0 1568 nsGkAtoms::_true, eCaseMatters)) {
michael@0 1569 // ancestor has aria-disabled property, this is disabled
michael@0 1570 *aState |= states::UNAVAILABLE;
michael@0 1571 break;
michael@0 1572 }
michael@0 1573 }
michael@0 1574 }
michael@0 1575
michael@0 1576 // special case: A native button element whose role got transformed by ARIA to a toggle button
michael@0 1577 if (IsButton())
michael@0 1578 aria::MapToState(aria::eARIAPressed, element, aState);
michael@0 1579
michael@0 1580 if (!mRoleMapEntry)
michael@0 1581 return;
michael@0 1582
michael@0 1583 *aState |= mRoleMapEntry->state;
michael@0 1584
michael@0 1585 if (aria::MapToState(mRoleMapEntry->attributeMap1, element, aState) &&
michael@0 1586 aria::MapToState(mRoleMapEntry->attributeMap2, element, aState))
michael@0 1587 aria::MapToState(mRoleMapEntry->attributeMap3, element, aState);
michael@0 1588
michael@0 1589 // ARIA gridcell inherits editable/readonly states from the grid until it's
michael@0 1590 // overridden.
michael@0 1591 if ((mRoleMapEntry->Is(nsGkAtoms::gridcell) ||
michael@0 1592 mRoleMapEntry->Is(nsGkAtoms::columnheader) ||
michael@0 1593 mRoleMapEntry->Is(nsGkAtoms::rowheader)) &&
michael@0 1594 !(*aState & (states::READONLY | states::EDITABLE))) {
michael@0 1595 const TableCellAccessible* cell = AsTableCell();
michael@0 1596 if (cell) {
michael@0 1597 TableAccessible* table = cell->Table();
michael@0 1598 if (table) {
michael@0 1599 Accessible* grid = table->AsAccessible();
michael@0 1600 uint64_t gridState = 0;
michael@0 1601 grid->ApplyARIAState(&gridState);
michael@0 1602 *aState |= (gridState & (states::READONLY | states::EDITABLE));
michael@0 1603 }
michael@0 1604 }
michael@0 1605 }
michael@0 1606 }
michael@0 1607
michael@0 1608 NS_IMETHODIMP
michael@0 1609 Accessible::GetValue(nsAString& aValue)
michael@0 1610 {
michael@0 1611 if (IsDefunct())
michael@0 1612 return NS_ERROR_FAILURE;
michael@0 1613
michael@0 1614 nsAutoString value;
michael@0 1615 Value(value);
michael@0 1616 aValue.Assign(value);
michael@0 1617
michael@0 1618 return NS_OK;
michael@0 1619 }
michael@0 1620
michael@0 1621 void
michael@0 1622 Accessible::Value(nsString& aValue)
michael@0 1623 {
michael@0 1624 if (!mRoleMapEntry)
michael@0 1625 return;
michael@0 1626
michael@0 1627 if (mRoleMapEntry->valueRule != eNoValue) {
michael@0 1628 // aria-valuenow is a number, and aria-valuetext is the optional text
michael@0 1629 // equivalent. For the string value, we will try the optional text
michael@0 1630 // equivalent first.
michael@0 1631 if (!mContent->GetAttr(kNameSpaceID_None,
michael@0 1632 nsGkAtoms::aria_valuetext, aValue)) {
michael@0 1633 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
michael@0 1634 aValue);
michael@0 1635 }
michael@0 1636 return;
michael@0 1637 }
michael@0 1638
michael@0 1639 // Value of textbox is a textified subtree.
michael@0 1640 if (mRoleMapEntry->Is(nsGkAtoms::textbox)) {
michael@0 1641 nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
michael@0 1642 return;
michael@0 1643 }
michael@0 1644
michael@0 1645 // Value of combobox is a text of current or selected item.
michael@0 1646 if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
michael@0 1647 Accessible* option = CurrentItem();
michael@0 1648 if (!option) {
michael@0 1649 Accessible* listbox = nullptr;
michael@0 1650 IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
michael@0 1651 while ((listbox = iter.Next()) && !listbox->IsListControl());
michael@0 1652
michael@0 1653 if (!listbox) {
michael@0 1654 uint32_t childCount = ChildCount();
michael@0 1655 for (uint32_t idx = 0; idx < childCount; idx++) {
michael@0 1656 Accessible* child = mChildren.ElementAt(idx);
michael@0 1657 if (child->IsListControl())
michael@0 1658 listbox = child;
michael@0 1659 }
michael@0 1660 }
michael@0 1661
michael@0 1662 if (listbox)
michael@0 1663 option = listbox->GetSelectedItem(0);
michael@0 1664 }
michael@0 1665
michael@0 1666 if (option)
michael@0 1667 nsTextEquivUtils::GetTextEquivFromSubtree(option, aValue);
michael@0 1668 }
michael@0 1669 }
michael@0 1670
michael@0 1671 double
michael@0 1672 Accessible::MaxValue() const
michael@0 1673 {
michael@0 1674 return AttrNumericValue(nsGkAtoms::aria_valuemax);
michael@0 1675 }
michael@0 1676
michael@0 1677 double
michael@0 1678 Accessible::MinValue() const
michael@0 1679 {
michael@0 1680 return AttrNumericValue(nsGkAtoms::aria_valuemin);
michael@0 1681 }
michael@0 1682
michael@0 1683 double
michael@0 1684 Accessible::Step() const
michael@0 1685 {
michael@0 1686 return UnspecifiedNaN<double>(); // no mimimum increment (step) in ARIA.
michael@0 1687 }
michael@0 1688
michael@0 1689 double
michael@0 1690 Accessible::CurValue() const
michael@0 1691 {
michael@0 1692 return AttrNumericValue(nsGkAtoms::aria_valuenow);
michael@0 1693 }
michael@0 1694
michael@0 1695 bool
michael@0 1696 Accessible::SetCurValue(double aValue)
michael@0 1697 {
michael@0 1698 if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
michael@0 1699 return false;
michael@0 1700
michael@0 1701 const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE;
michael@0 1702 if (State() & kValueCannotChange)
michael@0 1703 return false;
michael@0 1704
michael@0 1705 double checkValue = MinValue();
michael@0 1706 if (!IsNaN(checkValue) && aValue < checkValue)
michael@0 1707 return false;
michael@0 1708
michael@0 1709 checkValue = MaxValue();
michael@0 1710 if (!IsNaN(checkValue) && aValue > checkValue)
michael@0 1711 return false;
michael@0 1712
michael@0 1713 nsAutoString strValue;
michael@0 1714 strValue.AppendFloat(aValue);
michael@0 1715
michael@0 1716 return NS_SUCCEEDED(
michael@0 1717 mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
michael@0 1718 }
michael@0 1719
michael@0 1720 /* void setName (in DOMString name); */
michael@0 1721 NS_IMETHODIMP
michael@0 1722 Accessible::SetName(const nsAString& aName)
michael@0 1723 {
michael@0 1724 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1725 }
michael@0 1726
michael@0 1727 NS_IMETHODIMP
michael@0 1728 Accessible::GetKeyboardShortcut(nsAString& aKeyBinding)
michael@0 1729 {
michael@0 1730 aKeyBinding.Truncate();
michael@0 1731 if (IsDefunct())
michael@0 1732 return NS_ERROR_FAILURE;
michael@0 1733
michael@0 1734 KeyboardShortcut().ToString(aKeyBinding);
michael@0 1735 return NS_OK;
michael@0 1736 }
michael@0 1737
michael@0 1738 role
michael@0 1739 Accessible::ARIATransformRole(role aRole)
michael@0 1740 {
michael@0 1741 // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
michael@0 1742 // where the accessible role depends on both the role and ARIA state.
michael@0 1743 if (aRole == roles::PUSHBUTTON) {
michael@0 1744 if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
michael@0 1745 // For simplicity, any existing pressed attribute except "" or "undefined"
michael@0 1746 // indicates a toggle.
michael@0 1747 return roles::TOGGLE_BUTTON;
michael@0 1748 }
michael@0 1749
michael@0 1750 if (mContent->AttrValueIs(kNameSpaceID_None,
michael@0 1751 nsGkAtoms::aria_haspopup,
michael@0 1752 nsGkAtoms::_true,
michael@0 1753 eCaseMatters)) {
michael@0 1754 // For button with aria-haspopup="true".
michael@0 1755 return roles::BUTTONMENU;
michael@0 1756 }
michael@0 1757
michael@0 1758 } else if (aRole == roles::LISTBOX) {
michael@0 1759 // A listbox inside of a combobox needs a special role because of ATK
michael@0 1760 // mapping to menu.
michael@0 1761 if (mParent && mParent->Role() == roles::COMBOBOX) {
michael@0 1762 return roles::COMBOBOX_LIST;
michael@0 1763
michael@0 1764 Relation rel = RelationByType(RelationType::NODE_CHILD_OF);
michael@0 1765 Accessible* targetAcc = nullptr;
michael@0 1766 while ((targetAcc = rel.Next()))
michael@0 1767 if (targetAcc->Role() == roles::COMBOBOX)
michael@0 1768 return roles::COMBOBOX_LIST;
michael@0 1769 }
michael@0 1770
michael@0 1771 } else if (aRole == roles::OPTION) {
michael@0 1772 if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
michael@0 1773 return roles::COMBOBOX_OPTION;
michael@0 1774
michael@0 1775 } else if (aRole == roles::MENUITEM) {
michael@0 1776 // Menuitem has a submenu.
michael@0 1777 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
michael@0 1778 nsGkAtoms::_true, eCaseMatters)) {
michael@0 1779 return roles::PARENT_MENUITEM;
michael@0 1780 }
michael@0 1781 }
michael@0 1782
michael@0 1783 return aRole;
michael@0 1784 }
michael@0 1785
michael@0 1786 role
michael@0 1787 Accessible::NativeRole()
michael@0 1788 {
michael@0 1789 return roles::NOTHING;
michael@0 1790 }
michael@0 1791
michael@0 1792 // readonly attribute uint8_t actionCount
michael@0 1793 NS_IMETHODIMP
michael@0 1794 Accessible::GetActionCount(uint8_t* aActionCount)
michael@0 1795 {
michael@0 1796 NS_ENSURE_ARG_POINTER(aActionCount);
michael@0 1797 *aActionCount = 0;
michael@0 1798 if (IsDefunct())
michael@0 1799 return NS_ERROR_FAILURE;
michael@0 1800
michael@0 1801 *aActionCount = ActionCount();
michael@0 1802 return NS_OK;
michael@0 1803 }
michael@0 1804
michael@0 1805 uint8_t
michael@0 1806 Accessible::ActionCount()
michael@0 1807 {
michael@0 1808 return GetActionRule() == eNoAction ? 0 : 1;
michael@0 1809 }
michael@0 1810
michael@0 1811 /* DOMString getAccActionName (in uint8_t index); */
michael@0 1812 NS_IMETHODIMP
michael@0 1813 Accessible::GetActionName(uint8_t aIndex, nsAString& aName)
michael@0 1814 {
michael@0 1815 aName.Truncate();
michael@0 1816
michael@0 1817 if (aIndex != 0)
michael@0 1818 return NS_ERROR_INVALID_ARG;
michael@0 1819
michael@0 1820 if (IsDefunct())
michael@0 1821 return NS_ERROR_FAILURE;
michael@0 1822
michael@0 1823 uint32_t actionRule = GetActionRule();
michael@0 1824
michael@0 1825 switch (actionRule) {
michael@0 1826 case eActivateAction:
michael@0 1827 aName.AssignLiteral("activate");
michael@0 1828 return NS_OK;
michael@0 1829
michael@0 1830 case eClickAction:
michael@0 1831 aName.AssignLiteral("click");
michael@0 1832 return NS_OK;
michael@0 1833
michael@0 1834 case ePressAction:
michael@0 1835 aName.AssignLiteral("press");
michael@0 1836 return NS_OK;
michael@0 1837
michael@0 1838 case eCheckUncheckAction:
michael@0 1839 {
michael@0 1840 uint64_t state = State();
michael@0 1841 if (state & states::CHECKED)
michael@0 1842 aName.AssignLiteral("uncheck");
michael@0 1843 else if (state & states::MIXED)
michael@0 1844 aName.AssignLiteral("cycle");
michael@0 1845 else
michael@0 1846 aName.AssignLiteral("check");
michael@0 1847 return NS_OK;
michael@0 1848 }
michael@0 1849
michael@0 1850 case eJumpAction:
michael@0 1851 aName.AssignLiteral("jump");
michael@0 1852 return NS_OK;
michael@0 1853
michael@0 1854 case eOpenCloseAction:
michael@0 1855 if (State() & states::COLLAPSED)
michael@0 1856 aName.AssignLiteral("open");
michael@0 1857 else
michael@0 1858 aName.AssignLiteral("close");
michael@0 1859 return NS_OK;
michael@0 1860
michael@0 1861 case eSelectAction:
michael@0 1862 aName.AssignLiteral("select");
michael@0 1863 return NS_OK;
michael@0 1864
michael@0 1865 case eSwitchAction:
michael@0 1866 aName.AssignLiteral("switch");
michael@0 1867 return NS_OK;
michael@0 1868
michael@0 1869 case eSortAction:
michael@0 1870 aName.AssignLiteral("sort");
michael@0 1871 return NS_OK;
michael@0 1872
michael@0 1873 case eExpandAction:
michael@0 1874 if (State() & states::COLLAPSED)
michael@0 1875 aName.AssignLiteral("expand");
michael@0 1876 else
michael@0 1877 aName.AssignLiteral("collapse");
michael@0 1878 return NS_OK;
michael@0 1879 }
michael@0 1880
michael@0 1881 return NS_ERROR_INVALID_ARG;
michael@0 1882 }
michael@0 1883
michael@0 1884 // AString getActionDescription(in uint8_t index)
michael@0 1885 NS_IMETHODIMP
michael@0 1886 Accessible::GetActionDescription(uint8_t aIndex, nsAString& aDescription)
michael@0 1887 {
michael@0 1888 // default to localized action name.
michael@0 1889 nsAutoString name;
michael@0 1890 nsresult rv = GetActionName(aIndex, name);
michael@0 1891 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1892
michael@0 1893 TranslateString(name, aDescription);
michael@0 1894 return NS_OK;
michael@0 1895 }
michael@0 1896
michael@0 1897 // void doAction(in uint8_t index)
michael@0 1898 NS_IMETHODIMP
michael@0 1899 Accessible::DoAction(uint8_t aIndex)
michael@0 1900 {
michael@0 1901 if (aIndex != 0)
michael@0 1902 return NS_ERROR_INVALID_ARG;
michael@0 1903
michael@0 1904 if (IsDefunct())
michael@0 1905 return NS_ERROR_FAILURE;
michael@0 1906
michael@0 1907 if (GetActionRule() != eNoAction) {
michael@0 1908 DoCommand();
michael@0 1909 return NS_OK;
michael@0 1910 }
michael@0 1911
michael@0 1912 return NS_ERROR_INVALID_ARG;
michael@0 1913 }
michael@0 1914
michael@0 1915 /* DOMString getHelp (); */
michael@0 1916 NS_IMETHODIMP Accessible::GetHelp(nsAString& _retval)
michael@0 1917 {
michael@0 1918 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1919 }
michael@0 1920
michael@0 1921 nsIContent*
michael@0 1922 Accessible::GetAtomicRegion() const
michael@0 1923 {
michael@0 1924 nsIContent *loopContent = mContent;
michael@0 1925 nsAutoString atomic;
michael@0 1926 while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
michael@0 1927 loopContent = loopContent->GetParent();
michael@0 1928
michael@0 1929 return atomic.EqualsLiteral("true") ? loopContent : nullptr;
michael@0 1930 }
michael@0 1931
michael@0 1932 // nsIAccessible getRelationByType()
michael@0 1933 NS_IMETHODIMP
michael@0 1934 Accessible::GetRelationByType(uint32_t aType, nsIAccessibleRelation** aRelation)
michael@0 1935 {
michael@0 1936 NS_ENSURE_ARG_POINTER(aRelation);
michael@0 1937 *aRelation = nullptr;
michael@0 1938
michael@0 1939 NS_ENSURE_ARG(aType <= static_cast<uint32_t>(RelationType::LAST));
michael@0 1940
michael@0 1941 if (IsDefunct())
michael@0 1942 return NS_ERROR_FAILURE;
michael@0 1943
michael@0 1944 Relation rel = RelationByType(static_cast<RelationType>(aType));
michael@0 1945 NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel));
michael@0 1946 return *aRelation ? NS_OK : NS_ERROR_FAILURE;
michael@0 1947 }
michael@0 1948
michael@0 1949 Relation
michael@0 1950 Accessible::RelationByType(RelationType aType)
michael@0 1951 {
michael@0 1952 if (!HasOwnContent())
michael@0 1953 return Relation();
michael@0 1954
michael@0 1955 // Relationships are defined on the same content node that the role would be
michael@0 1956 // defined on.
michael@0 1957 switch (aType) {
michael@0 1958 case RelationType::LABELLED_BY: {
michael@0 1959 Relation rel(new IDRefsIterator(mDoc, mContent,
michael@0 1960 nsGkAtoms::aria_labelledby));
michael@0 1961 if (mContent->IsHTML()) {
michael@0 1962 rel.AppendIter(new HTMLLabelIterator(Document(), this));
michael@0 1963 } else if (mContent->IsXUL()) {
michael@0 1964 rel.AppendIter(new XULLabelIterator(Document(), mContent));
michael@0 1965 }
michael@0 1966
michael@0 1967 return rel;
michael@0 1968 }
michael@0 1969
michael@0 1970 case RelationType::LABEL_FOR: {
michael@0 1971 Relation rel(new RelatedAccIterator(Document(), mContent,
michael@0 1972 nsGkAtoms::aria_labelledby));
michael@0 1973 if (mContent->Tag() == nsGkAtoms::label && mContent->IsXUL())
michael@0 1974 rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
michael@0 1975
michael@0 1976 return rel;
michael@0 1977 }
michael@0 1978
michael@0 1979 case RelationType::DESCRIBED_BY: {
michael@0 1980 Relation rel(new IDRefsIterator(mDoc, mContent,
michael@0 1981 nsGkAtoms::aria_describedby));
michael@0 1982 if (mContent->IsXUL())
michael@0 1983 rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
michael@0 1984
michael@0 1985 return rel;
michael@0 1986 }
michael@0 1987
michael@0 1988 case RelationType::DESCRIPTION_FOR: {
michael@0 1989 Relation rel(new RelatedAccIterator(Document(), mContent,
michael@0 1990 nsGkAtoms::aria_describedby));
michael@0 1991
michael@0 1992 // This affectively adds an optional control attribute to xul:description,
michael@0 1993 // which only affects accessibility, by allowing the description to be
michael@0 1994 // tied to a control.
michael@0 1995 if (mContent->Tag() == nsGkAtoms::description &&
michael@0 1996 mContent->IsXUL())
michael@0 1997 rel.AppendIter(new IDRefsIterator(mDoc, mContent,
michael@0 1998 nsGkAtoms::control));
michael@0 1999
michael@0 2000 return rel;
michael@0 2001 }
michael@0 2002
michael@0 2003 case RelationType::NODE_CHILD_OF: {
michael@0 2004 Relation rel(new RelatedAccIterator(Document(), mContent,
michael@0 2005 nsGkAtoms::aria_owns));
michael@0 2006
michael@0 2007 // This is an ARIA tree or treegrid that doesn't use owns, so we need to
michael@0 2008 // get the parent the hard way.
michael@0 2009 if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
michael@0 2010 mRoleMapEntry->role == roles::LISTITEM ||
michael@0 2011 mRoleMapEntry->role == roles::ROW)) {
michael@0 2012 rel.AppendTarget(GetGroupInfo()->ConceptualParent());
michael@0 2013 }
michael@0 2014
michael@0 2015 // If accessible is in its own Window, or is the root of a document,
michael@0 2016 // then we should provide NODE_CHILD_OF relation so that MSAA clients
michael@0 2017 // can easily get to true parent instead of getting to oleacc's
michael@0 2018 // ROLE_WINDOW accessible which will prevent us from going up further
michael@0 2019 // (because it is system generated and has no idea about the hierarchy
michael@0 2020 // above it).
michael@0 2021 nsIFrame *frame = GetFrame();
michael@0 2022 if (frame) {
michael@0 2023 nsView *view = frame->GetViewExternal();
michael@0 2024 if (view) {
michael@0 2025 nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
michael@0 2026 if (scrollFrame || view->GetWidget() || !frame->GetParent())
michael@0 2027 rel.AppendTarget(Parent());
michael@0 2028 }
michael@0 2029 }
michael@0 2030
michael@0 2031 return rel;
michael@0 2032 }
michael@0 2033
michael@0 2034 case RelationType::NODE_PARENT_OF: {
michael@0 2035 Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
michael@0 2036
michael@0 2037 // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
michael@0 2038 // also can be organized by groups.
michael@0 2039 if (mRoleMapEntry &&
michael@0 2040 (mRoleMapEntry->role == roles::OUTLINEITEM ||
michael@0 2041 mRoleMapEntry->role == roles::LISTITEM ||
michael@0 2042 mRoleMapEntry->role == roles::ROW ||
michael@0 2043 mRoleMapEntry->role == roles::OUTLINE ||
michael@0 2044 mRoleMapEntry->role == roles::LIST ||
michael@0 2045 mRoleMapEntry->role == roles::TREE_TABLE)) {
michael@0 2046 rel.AppendIter(new ItemIterator(this));
michael@0 2047 }
michael@0 2048
michael@0 2049 return rel;
michael@0 2050 }
michael@0 2051
michael@0 2052 case RelationType::CONTROLLED_BY:
michael@0 2053 return Relation(new RelatedAccIterator(Document(), mContent,
michael@0 2054 nsGkAtoms::aria_controls));
michael@0 2055
michael@0 2056 case RelationType::CONTROLLER_FOR: {
michael@0 2057 Relation rel(new IDRefsIterator(mDoc, mContent,
michael@0 2058 nsGkAtoms::aria_controls));
michael@0 2059 rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
michael@0 2060 return rel;
michael@0 2061 }
michael@0 2062
michael@0 2063 case RelationType::FLOWS_TO:
michael@0 2064 return Relation(new IDRefsIterator(mDoc, mContent,
michael@0 2065 nsGkAtoms::aria_flowto));
michael@0 2066
michael@0 2067 case RelationType::FLOWS_FROM:
michael@0 2068 return Relation(new RelatedAccIterator(Document(), mContent,
michael@0 2069 nsGkAtoms::aria_flowto));
michael@0 2070
michael@0 2071 case RelationType::MEMBER_OF:
michael@0 2072 return Relation(mDoc, GetAtomicRegion());
michael@0 2073
michael@0 2074 case RelationType::SUBWINDOW_OF:
michael@0 2075 case RelationType::EMBEDS:
michael@0 2076 case RelationType::EMBEDDED_BY:
michael@0 2077 case RelationType::POPUP_FOR:
michael@0 2078 case RelationType::PARENT_WINDOW_OF:
michael@0 2079 return Relation();
michael@0 2080
michael@0 2081 case RelationType::DEFAULT_BUTTON: {
michael@0 2082 if (mContent->IsHTML()) {
michael@0 2083 // HTML form controls implements nsIFormControl interface.
michael@0 2084 nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
michael@0 2085 if (control) {
michael@0 2086 nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
michael@0 2087 if (form) {
michael@0 2088 nsCOMPtr<nsIContent> formContent =
michael@0 2089 do_QueryInterface(form->GetDefaultSubmitElement());
michael@0 2090 return Relation(mDoc, formContent);
michael@0 2091 }
michael@0 2092 }
michael@0 2093 } else {
michael@0 2094 // In XUL, use first <button default="true" .../> in the document
michael@0 2095 nsCOMPtr<nsIDOMXULDocument> xulDoc =
michael@0 2096 do_QueryInterface(mContent->OwnerDoc());
michael@0 2097 nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
michael@0 2098 if (xulDoc) {
michael@0 2099 nsCOMPtr<nsIDOMNodeList> possibleDefaultButtons;
michael@0 2100 xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
michael@0 2101 NS_LITERAL_STRING("true"),
michael@0 2102 getter_AddRefs(possibleDefaultButtons));
michael@0 2103 if (possibleDefaultButtons) {
michael@0 2104 uint32_t length;
michael@0 2105 possibleDefaultButtons->GetLength(&length);
michael@0 2106 nsCOMPtr<nsIDOMNode> possibleButton;
michael@0 2107 // Check for button in list of default="true" elements
michael@0 2108 for (uint32_t count = 0; count < length && !buttonEl; count ++) {
michael@0 2109 possibleDefaultButtons->Item(count, getter_AddRefs(possibleButton));
michael@0 2110 buttonEl = do_QueryInterface(possibleButton);
michael@0 2111 }
michael@0 2112 }
michael@0 2113 if (!buttonEl) { // Check for anonymous accept button in <dialog>
michael@0 2114 dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
michael@0 2115 if (rootElm) {
michael@0 2116 nsIContent* possibleButtonEl = rootElm->OwnerDoc()->
michael@0 2117 GetAnonymousElementByAttribute(rootElm, nsGkAtoms::_default,
michael@0 2118 NS_LITERAL_STRING("true"));
michael@0 2119 buttonEl = do_QueryInterface(possibleButtonEl);
michael@0 2120 }
michael@0 2121 }
michael@0 2122 nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
michael@0 2123 return Relation(mDoc, relatedContent);
michael@0 2124 }
michael@0 2125 }
michael@0 2126 return Relation();
michael@0 2127 }
michael@0 2128
michael@0 2129 case RelationType::CONTAINING_DOCUMENT:
michael@0 2130 return Relation(mDoc);
michael@0 2131
michael@0 2132 case RelationType::CONTAINING_TAB_PANE: {
michael@0 2133 nsCOMPtr<nsIDocShell> docShell =
michael@0 2134 nsCoreUtils::GetDocShellFor(GetNode());
michael@0 2135 if (docShell) {
michael@0 2136 // Walk up the parent chain without crossing the boundary at which item
michael@0 2137 // types change, preventing us from walking up out of tab content.
michael@0 2138 nsCOMPtr<nsIDocShellTreeItem> root;
michael@0 2139 docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
michael@0 2140 if (root) {
michael@0 2141 // If the item type is typeContent, we assume we are in browser tab
michael@0 2142 // content. Note, this includes content such as about:addons,
michael@0 2143 // for consistency.
michael@0 2144 if (root->ItemType() == nsIDocShellTreeItem::typeContent) {
michael@0 2145 return Relation(nsAccUtils::GetDocAccessibleFor(root));
michael@0 2146 }
michael@0 2147 }
michael@0 2148 }
michael@0 2149 return Relation();
michael@0 2150 }
michael@0 2151
michael@0 2152 case RelationType::CONTAINING_APPLICATION:
michael@0 2153 return Relation(ApplicationAcc());
michael@0 2154
michael@0 2155 default:
michael@0 2156 return Relation();
michael@0 2157 }
michael@0 2158 }
michael@0 2159
michael@0 2160 NS_IMETHODIMP
michael@0 2161 Accessible::GetRelations(nsIArray **aRelations)
michael@0 2162 {
michael@0 2163 NS_ENSURE_ARG_POINTER(aRelations);
michael@0 2164 *aRelations = nullptr;
michael@0 2165
michael@0 2166 if (IsDefunct())
michael@0 2167 return NS_ERROR_FAILURE;
michael@0 2168
michael@0 2169 nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
michael@0 2170 NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
michael@0 2171
michael@0 2172 static const uint32_t relationTypes[] = {
michael@0 2173 nsIAccessibleRelation::RELATION_LABELLED_BY,
michael@0 2174 nsIAccessibleRelation::RELATION_LABEL_FOR,
michael@0 2175 nsIAccessibleRelation::RELATION_DESCRIBED_BY,
michael@0 2176 nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
michael@0 2177 nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
michael@0 2178 nsIAccessibleRelation::RELATION_NODE_PARENT_OF,
michael@0 2179 nsIAccessibleRelation::RELATION_CONTROLLED_BY,
michael@0 2180 nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
michael@0 2181 nsIAccessibleRelation::RELATION_FLOWS_TO,
michael@0 2182 nsIAccessibleRelation::RELATION_FLOWS_FROM,
michael@0 2183 nsIAccessibleRelation::RELATION_MEMBER_OF,
michael@0 2184 nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
michael@0 2185 nsIAccessibleRelation::RELATION_EMBEDS,
michael@0 2186 nsIAccessibleRelation::RELATION_EMBEDDED_BY,
michael@0 2187 nsIAccessibleRelation::RELATION_POPUP_FOR,
michael@0 2188 nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
michael@0 2189 nsIAccessibleRelation::RELATION_DEFAULT_BUTTON,
michael@0 2190 nsIAccessibleRelation::RELATION_CONTAINING_DOCUMENT,
michael@0 2191 nsIAccessibleRelation::RELATION_CONTAINING_TAB_PANE,
michael@0 2192 nsIAccessibleRelation::RELATION_CONTAINING_APPLICATION
michael@0 2193 };
michael@0 2194
michael@0 2195 for (uint32_t idx = 0; idx < ArrayLength(relationTypes); idx++) {
michael@0 2196 nsCOMPtr<nsIAccessibleRelation> relation;
michael@0 2197 nsresult rv = GetRelationByType(relationTypes[idx], getter_AddRefs(relation));
michael@0 2198
michael@0 2199 if (NS_SUCCEEDED(rv) && relation) {
michael@0 2200 uint32_t targets = 0;
michael@0 2201 relation->GetTargetsCount(&targets);
michael@0 2202 if (targets)
michael@0 2203 relations->AppendElement(relation, false);
michael@0 2204 }
michael@0 2205 }
michael@0 2206
michael@0 2207 NS_ADDREF(*aRelations = relations);
michael@0 2208 return NS_OK;
michael@0 2209 }
michael@0 2210
michael@0 2211 /* void extendSelection (); */
michael@0 2212 NS_IMETHODIMP Accessible::ExtendSelection()
michael@0 2213 {
michael@0 2214 // XXX Should be implemented, but not high priority
michael@0 2215 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2216 }
michael@0 2217
michael@0 2218 /* [noscript] void getNativeInterface(out voidPtr aOutAccessible); */
michael@0 2219 NS_IMETHODIMP Accessible::GetNativeInterface(void **aOutAccessible)
michael@0 2220 {
michael@0 2221 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2222 }
michael@0 2223
michael@0 2224 void
michael@0 2225 Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
michael@0 2226 {
michael@0 2227 class Runnable MOZ_FINAL : public nsRunnable
michael@0 2228 {
michael@0 2229 public:
michael@0 2230 Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
michael@0 2231 mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
michael@0 2232
michael@0 2233 NS_IMETHOD Run()
michael@0 2234 {
michael@0 2235 if (mAcc)
michael@0 2236 mAcc->DispatchClickEvent(mContent, mIdx);
michael@0 2237
michael@0 2238 return NS_OK;
michael@0 2239 }
michael@0 2240
michael@0 2241 void Revoke()
michael@0 2242 {
michael@0 2243 mAcc = nullptr;
michael@0 2244 mContent = nullptr;
michael@0 2245 }
michael@0 2246
michael@0 2247 private:
michael@0 2248 nsRefPtr<Accessible> mAcc;
michael@0 2249 nsCOMPtr<nsIContent> mContent;
michael@0 2250 uint32_t mIdx;
michael@0 2251 };
michael@0 2252
michael@0 2253 nsIContent* content = aContent ? aContent : mContent.get();
michael@0 2254 nsCOMPtr<nsIRunnable> runnable = new Runnable(this, content, aActionIndex);
michael@0 2255 NS_DispatchToMainThread(runnable);
michael@0 2256 }
michael@0 2257
michael@0 2258 void
michael@0 2259 Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex)
michael@0 2260 {
michael@0 2261 if (IsDefunct())
michael@0 2262 return;
michael@0 2263
michael@0 2264 nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
michael@0 2265
michael@0 2266 // Scroll into view.
michael@0 2267 presShell->ScrollContentIntoView(aContent,
michael@0 2268 nsIPresShell::ScrollAxis(),
michael@0 2269 nsIPresShell::ScrollAxis(),
michael@0 2270 nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
michael@0 2271
michael@0 2272 nsWeakFrame frame = aContent->GetPrimaryFrame();
michael@0 2273 if (!frame)
michael@0 2274 return;
michael@0 2275
michael@0 2276 // Compute x and y coordinates.
michael@0 2277 nsPoint point;
michael@0 2278 nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
michael@0 2279 if (!widget)
michael@0 2280 return;
michael@0 2281
michael@0 2282 nsSize size = frame->GetSize();
michael@0 2283
michael@0 2284 nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
michael@0 2285 int32_t x = presContext->AppUnitsToDevPixels(point.x + size.width / 2);
michael@0 2286 int32_t y = presContext->AppUnitsToDevPixels(point.y + size.height / 2);
michael@0 2287
michael@0 2288 // Simulate a touch interaction by dispatching touch events with mouse events.
michael@0 2289 nsCoreUtils::DispatchTouchEvent(NS_TOUCH_START, x, y, aContent, frame, presShell, widget);
michael@0 2290 nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, x, y, aContent, frame, presShell, widget);
michael@0 2291 nsCoreUtils::DispatchTouchEvent(NS_TOUCH_END, x, y, aContent, frame, presShell, widget);
michael@0 2292 nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_UP, x, y, aContent, frame, presShell, widget);
michael@0 2293 }
michael@0 2294
michael@0 2295 NS_IMETHODIMP
michael@0 2296 Accessible::ScrollTo(uint32_t aHow)
michael@0 2297 {
michael@0 2298 if (IsDefunct())
michael@0 2299 return NS_ERROR_FAILURE;
michael@0 2300
michael@0 2301 nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
michael@0 2302 return NS_OK;
michael@0 2303 }
michael@0 2304
michael@0 2305 NS_IMETHODIMP
michael@0 2306 Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
michael@0 2307 {
michael@0 2308 nsIFrame *frame = GetFrame();
michael@0 2309 if (!frame)
michael@0 2310 return NS_ERROR_FAILURE;
michael@0 2311
michael@0 2312 nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
michael@0 2313 this);
michael@0 2314
michael@0 2315 nsIFrame *parentFrame = frame;
michael@0 2316 while ((parentFrame = parentFrame->GetParent()))
michael@0 2317 nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
michael@0 2318
michael@0 2319 return NS_OK;
michael@0 2320 }
michael@0 2321
michael@0 2322 // nsIAccessibleHyperLink
michael@0 2323 // Because of new-atk design, any embedded object in text can implement
michael@0 2324 // nsIAccessibleHyperLink, which helps determine where it is located
michael@0 2325 // within containing text
michael@0 2326
michael@0 2327 // readonly attribute long nsIAccessibleHyperLink::anchorCount
michael@0 2328 NS_IMETHODIMP
michael@0 2329 Accessible::GetAnchorCount(int32_t *aAnchorCount)
michael@0 2330 {
michael@0 2331 NS_ENSURE_ARG_POINTER(aAnchorCount);
michael@0 2332 *aAnchorCount = 0;
michael@0 2333
michael@0 2334 if (IsDefunct())
michael@0 2335 return NS_ERROR_FAILURE;
michael@0 2336
michael@0 2337 *aAnchorCount = AnchorCount();
michael@0 2338 return NS_OK;
michael@0 2339 }
michael@0 2340
michael@0 2341 // readonly attribute long nsIAccessibleHyperLink::startIndex
michael@0 2342 NS_IMETHODIMP
michael@0 2343 Accessible::GetStartIndex(int32_t *aStartIndex)
michael@0 2344 {
michael@0 2345 NS_ENSURE_ARG_POINTER(aStartIndex);
michael@0 2346 *aStartIndex = 0;
michael@0 2347
michael@0 2348 if (IsDefunct())
michael@0 2349 return NS_ERROR_FAILURE;
michael@0 2350
michael@0 2351 *aStartIndex = StartOffset();
michael@0 2352 return NS_OK;
michael@0 2353 }
michael@0 2354
michael@0 2355 // readonly attribute long nsIAccessibleHyperLink::endIndex
michael@0 2356 NS_IMETHODIMP
michael@0 2357 Accessible::GetEndIndex(int32_t *aEndIndex)
michael@0 2358 {
michael@0 2359 NS_ENSURE_ARG_POINTER(aEndIndex);
michael@0 2360 *aEndIndex = 0;
michael@0 2361
michael@0 2362 if (IsDefunct())
michael@0 2363 return NS_ERROR_FAILURE;
michael@0 2364
michael@0 2365 *aEndIndex = EndOffset();
michael@0 2366 return NS_OK;
michael@0 2367 }
michael@0 2368
michael@0 2369 NS_IMETHODIMP
michael@0 2370 Accessible::GetURI(int32_t aIndex, nsIURI **aURI)
michael@0 2371 {
michael@0 2372 NS_ENSURE_ARG_POINTER(aURI);
michael@0 2373
michael@0 2374 if (IsDefunct())
michael@0 2375 return NS_ERROR_FAILURE;
michael@0 2376
michael@0 2377 if (aIndex < 0 || aIndex >= static_cast<int32_t>(AnchorCount()))
michael@0 2378 return NS_ERROR_INVALID_ARG;
michael@0 2379
michael@0 2380 nsRefPtr<nsIURI>(AnchorURIAt(aIndex)).forget(aURI);
michael@0 2381 return NS_OK;
michael@0 2382 }
michael@0 2383
michael@0 2384
michael@0 2385 NS_IMETHODIMP
michael@0 2386 Accessible::GetAnchor(int32_t aIndex, nsIAccessible** aAccessible)
michael@0 2387 {
michael@0 2388 NS_ENSURE_ARG_POINTER(aAccessible);
michael@0 2389 *aAccessible = nullptr;
michael@0 2390
michael@0 2391 if (IsDefunct())
michael@0 2392 return NS_ERROR_FAILURE;
michael@0 2393
michael@0 2394 if (aIndex < 0 || aIndex >= static_cast<int32_t>(AnchorCount()))
michael@0 2395 return NS_ERROR_INVALID_ARG;
michael@0 2396
michael@0 2397 NS_IF_ADDREF(*aAccessible = AnchorAt(aIndex));
michael@0 2398 return NS_OK;
michael@0 2399 }
michael@0 2400
michael@0 2401 // readonly attribute boolean nsIAccessibleHyperLink::valid
michael@0 2402 NS_IMETHODIMP
michael@0 2403 Accessible::GetValid(bool *aValid)
michael@0 2404 {
michael@0 2405 NS_ENSURE_ARG_POINTER(aValid);
michael@0 2406 *aValid = false;
michael@0 2407
michael@0 2408 if (IsDefunct())
michael@0 2409 return NS_ERROR_FAILURE;
michael@0 2410
michael@0 2411 *aValid = IsLinkValid();
michael@0 2412 return NS_OK;
michael@0 2413 }
michael@0 2414
michael@0 2415 void
michael@0 2416 Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
michael@0 2417 uint32_t aLength)
michael@0 2418 {
michael@0 2419 // Return text representation of non-text accessible within hypertext
michael@0 2420 // accessible. Text accessible overrides this method to return enclosed text.
michael@0 2421 if (aStartOffset != 0 || aLength == 0)
michael@0 2422 return;
michael@0 2423
michael@0 2424 nsIFrame *frame = GetFrame();
michael@0 2425 if (!frame)
michael@0 2426 return;
michael@0 2427
michael@0 2428 NS_ASSERTION(mParent,
michael@0 2429 "Called on accessible unbound from tree. Result can be wrong.");
michael@0 2430
michael@0 2431 if (frame->GetType() == nsGkAtoms::brFrame) {
michael@0 2432 aText += kForcedNewLineChar;
michael@0 2433 } else if (mParent && nsAccUtils::MustPrune(mParent)) {
michael@0 2434 // Expose the embedded object accessible as imaginary embedded object
michael@0 2435 // character if its parent hypertext accessible doesn't expose children to
michael@0 2436 // AT.
michael@0 2437 aText += kImaginaryEmbeddedObjectChar;
michael@0 2438 } else {
michael@0 2439 aText += kEmbeddedObjectChar;
michael@0 2440 }
michael@0 2441 }
michael@0 2442
michael@0 2443 void
michael@0 2444 Accessible::Shutdown()
michael@0 2445 {
michael@0 2446 // Mark the accessible as defunct, invalidate the child count and pointers to
michael@0 2447 // other accessibles, also make sure none of its children point to this parent
michael@0 2448 mStateFlags |= eIsDefunct;
michael@0 2449
michael@0 2450 InvalidateChildren();
michael@0 2451 if (mParent)
michael@0 2452 mParent->RemoveChild(this);
michael@0 2453
michael@0 2454 mContent = nullptr;
michael@0 2455 mDoc = nullptr;
michael@0 2456 }
michael@0 2457
michael@0 2458 // Accessible protected
michael@0 2459 void
michael@0 2460 Accessible::ARIAName(nsString& aName)
michael@0 2461 {
michael@0 2462 // aria-labelledby now takes precedence over aria-label
michael@0 2463 nsresult rv = nsTextEquivUtils::
michael@0 2464 GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
michael@0 2465 if (NS_SUCCEEDED(rv)) {
michael@0 2466 aName.CompressWhitespace();
michael@0 2467 }
michael@0 2468
michael@0 2469 if (aName.IsEmpty() &&
michael@0 2470 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
michael@0 2471 aName.CompressWhitespace();
michael@0 2472 }
michael@0 2473 }
michael@0 2474
michael@0 2475 // Accessible protected
michael@0 2476 ENameValueFlag
michael@0 2477 Accessible::NativeName(nsString& aName)
michael@0 2478 {
michael@0 2479 if (mContent->IsHTML()) {
michael@0 2480 Accessible* label = nullptr;
michael@0 2481 HTMLLabelIterator iter(Document(), this);
michael@0 2482 while ((label = iter.Next())) {
michael@0 2483 nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
michael@0 2484 &aName);
michael@0 2485 aName.CompressWhitespace();
michael@0 2486 }
michael@0 2487
michael@0 2488 if (!aName.IsEmpty())
michael@0 2489 return eNameOK;
michael@0 2490
michael@0 2491 nsTextEquivUtils::GetNameFromSubtree(this, aName);
michael@0 2492 return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
michael@0 2493 }
michael@0 2494
michael@0 2495 if (mContent->IsXUL()) {
michael@0 2496 XULElmName(mDoc, mContent, aName);
michael@0 2497 if (!aName.IsEmpty())
michael@0 2498 return eNameOK;
michael@0 2499
michael@0 2500 nsTextEquivUtils::GetNameFromSubtree(this, aName);
michael@0 2501 return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
michael@0 2502 }
michael@0 2503
michael@0 2504 if (mContent->IsSVG()) {
michael@0 2505 // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
michael@0 2506 // for processing, the user agent shall choose the first one.
michael@0 2507 for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
michael@0 2508 childElm = childElm->GetNextSibling()) {
michael@0 2509 if (childElm->IsSVG(nsGkAtoms::title)) {
michael@0 2510 nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
michael@0 2511 return eNameOK;
michael@0 2512 }
michael@0 2513 }
michael@0 2514 }
michael@0 2515
michael@0 2516 return eNameOK;
michael@0 2517 }
michael@0 2518
michael@0 2519 // Accessible protected
michael@0 2520 void
michael@0 2521 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
michael@0 2522 {
michael@0 2523 NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
michael@0 2524
michael@0 2525 if (mParent) {
michael@0 2526 if (mParent != aParent) {
michael@0 2527 NS_ERROR("Adopting child!");
michael@0 2528 mParent->RemoveChild(this);
michael@0 2529 mParent->InvalidateChildrenGroupInfo();
michael@0 2530 } else {
michael@0 2531 NS_ERROR("Binding to the same parent!");
michael@0 2532 return;
michael@0 2533 }
michael@0 2534 }
michael@0 2535
michael@0 2536 mParent = aParent;
michael@0 2537 mIndexInParent = aIndexInParent;
michael@0 2538
michael@0 2539 mParent->InvalidateChildrenGroupInfo();
michael@0 2540
michael@0 2541 // Note: this is currently only used for richlistitems and their children.
michael@0 2542 if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
michael@0 2543 mContextFlags |= eHasNameDependentParent;
michael@0 2544 else
michael@0 2545 mContextFlags &= ~eHasNameDependentParent;
michael@0 2546 }
michael@0 2547
michael@0 2548 // Accessible protected
michael@0 2549 void
michael@0 2550 Accessible::UnbindFromParent()
michael@0 2551 {
michael@0 2552 mParent->InvalidateChildrenGroupInfo();
michael@0 2553 mParent = nullptr;
michael@0 2554 mIndexInParent = -1;
michael@0 2555 mIndexOfEmbeddedChild = -1;
michael@0 2556 mGroupInfo = nullptr;
michael@0 2557 mContextFlags &= ~eHasNameDependentParent;
michael@0 2558 }
michael@0 2559
michael@0 2560 ////////////////////////////////////////////////////////////////////////////////
michael@0 2561 // Accessible public methods
michael@0 2562
michael@0 2563 RootAccessible*
michael@0 2564 Accessible::RootAccessible() const
michael@0 2565 {
michael@0 2566 nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(GetNode());
michael@0 2567 NS_ASSERTION(docShell, "No docshell for mContent");
michael@0 2568 if (!docShell) {
michael@0 2569 return nullptr;
michael@0 2570 }
michael@0 2571
michael@0 2572 nsCOMPtr<nsIDocShellTreeItem> root;
michael@0 2573 docShell->GetRootTreeItem(getter_AddRefs(root));
michael@0 2574 NS_ASSERTION(root, "No root content tree item");
michael@0 2575 if (!root) {
michael@0 2576 return nullptr;
michael@0 2577 }
michael@0 2578
michael@0 2579 DocAccessible* docAcc = nsAccUtils::GetDocAccessibleFor(root);
michael@0 2580 return docAcc ? docAcc->AsRoot() : nullptr;
michael@0 2581 }
michael@0 2582
michael@0 2583 nsIFrame*
michael@0 2584 Accessible::GetFrame() const
michael@0 2585 {
michael@0 2586 return mContent ? mContent->GetPrimaryFrame() : nullptr;
michael@0 2587 }
michael@0 2588
michael@0 2589 nsINode*
michael@0 2590 Accessible::GetNode() const
michael@0 2591 {
michael@0 2592 return mContent;
michael@0 2593 }
michael@0 2594
michael@0 2595 void
michael@0 2596 Accessible::Language(nsAString& aLanguage)
michael@0 2597 {
michael@0 2598 aLanguage.Truncate();
michael@0 2599
michael@0 2600 if (!mDoc)
michael@0 2601 return;
michael@0 2602
michael@0 2603 nsCoreUtils::GetLanguageFor(mContent, nullptr, aLanguage);
michael@0 2604 if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
michael@0 2605 mDoc->DocumentNode()->GetHeaderData(nsGkAtoms::headerContentLanguage,
michael@0 2606 aLanguage);
michael@0 2607 }
michael@0 2608 }
michael@0 2609
michael@0 2610 void
michael@0 2611 Accessible::InvalidateChildren()
michael@0 2612 {
michael@0 2613 int32_t childCount = mChildren.Length();
michael@0 2614 for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 2615 Accessible* child = mChildren.ElementAt(childIdx);
michael@0 2616 child->UnbindFromParent();
michael@0 2617 }
michael@0 2618
michael@0 2619 mEmbeddedObjCollector = nullptr;
michael@0 2620 mChildren.Clear();
michael@0 2621 SetChildrenFlag(eChildrenUninitialized);
michael@0 2622 }
michael@0 2623
michael@0 2624 bool
michael@0 2625 Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
michael@0 2626 {
michael@0 2627 if (!aChild)
michael@0 2628 return false;
michael@0 2629
michael@0 2630 if (aIndex == mChildren.Length()) {
michael@0 2631 if (!mChildren.AppendElement(aChild))
michael@0 2632 return false;
michael@0 2633
michael@0 2634 } else {
michael@0 2635 if (!mChildren.InsertElementAt(aIndex, aChild))
michael@0 2636 return false;
michael@0 2637
michael@0 2638 for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
michael@0 2639 NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
michael@0 2640 "Accessible child index doesn't match");
michael@0 2641 mChildren[idx]->mIndexInParent = idx;
michael@0 2642 }
michael@0 2643
michael@0 2644 mEmbeddedObjCollector = nullptr;
michael@0 2645 }
michael@0 2646
michael@0 2647 if (!nsAccUtils::IsEmbeddedObject(aChild))
michael@0 2648 SetChildrenFlag(eMixedChildren);
michael@0 2649
michael@0 2650 aChild->BindToParent(this, aIndex);
michael@0 2651 return true;
michael@0 2652 }
michael@0 2653
michael@0 2654 bool
michael@0 2655 Accessible::RemoveChild(Accessible* aChild)
michael@0 2656 {
michael@0 2657 if (!aChild)
michael@0 2658 return false;
michael@0 2659
michael@0 2660 if (aChild->mParent != this || aChild->mIndexInParent == -1)
michael@0 2661 return false;
michael@0 2662
michael@0 2663 uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
michael@0 2664 if (index >= mChildren.Length() || mChildren[index] != aChild) {
michael@0 2665 NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
michael@0 2666 aChild->UnbindFromParent();
michael@0 2667 return false;
michael@0 2668 }
michael@0 2669
michael@0 2670 for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
michael@0 2671 NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
michael@0 2672 "Accessible child index doesn't match");
michael@0 2673 mChildren[idx]->mIndexInParent = idx - 1;
michael@0 2674 }
michael@0 2675
michael@0 2676 aChild->UnbindFromParent();
michael@0 2677 mChildren.RemoveElementAt(index);
michael@0 2678 mEmbeddedObjCollector = nullptr;
michael@0 2679
michael@0 2680 return true;
michael@0 2681 }
michael@0 2682
michael@0 2683 Accessible*
michael@0 2684 Accessible::GetChildAt(uint32_t aIndex) const
michael@0 2685 {
michael@0 2686 Accessible* child = mChildren.SafeElementAt(aIndex, nullptr);
michael@0 2687 if (!child)
michael@0 2688 return nullptr;
michael@0 2689
michael@0 2690 #ifdef DEBUG
michael@0 2691 Accessible* realParent = child->mParent;
michael@0 2692 NS_ASSERTION(!realParent || realParent == this,
michael@0 2693 "Two accessibles have the same first child accessible!");
michael@0 2694 #endif
michael@0 2695
michael@0 2696 return child;
michael@0 2697 }
michael@0 2698
michael@0 2699 uint32_t
michael@0 2700 Accessible::ChildCount() const
michael@0 2701 {
michael@0 2702 return mChildren.Length();
michael@0 2703 }
michael@0 2704
michael@0 2705 int32_t
michael@0 2706 Accessible::IndexInParent() const
michael@0 2707 {
michael@0 2708 return mIndexInParent;
michael@0 2709 }
michael@0 2710
michael@0 2711 uint32_t
michael@0 2712 Accessible::EmbeddedChildCount()
michael@0 2713 {
michael@0 2714 if (IsChildrenFlag(eMixedChildren)) {
michael@0 2715 if (!mEmbeddedObjCollector)
michael@0 2716 mEmbeddedObjCollector = new EmbeddedObjCollector(this);
michael@0 2717 return mEmbeddedObjCollector->Count();
michael@0 2718 }
michael@0 2719
michael@0 2720 return ChildCount();
michael@0 2721 }
michael@0 2722
michael@0 2723 Accessible*
michael@0 2724 Accessible::GetEmbeddedChildAt(uint32_t aIndex)
michael@0 2725 {
michael@0 2726 if (IsChildrenFlag(eMixedChildren)) {
michael@0 2727 if (!mEmbeddedObjCollector)
michael@0 2728 mEmbeddedObjCollector = new EmbeddedObjCollector(this);
michael@0 2729 return mEmbeddedObjCollector ?
michael@0 2730 mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
michael@0 2731 }
michael@0 2732
michael@0 2733 return GetChildAt(aIndex);
michael@0 2734 }
michael@0 2735
michael@0 2736 int32_t
michael@0 2737 Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
michael@0 2738 {
michael@0 2739 if (IsChildrenFlag(eMixedChildren)) {
michael@0 2740 if (!mEmbeddedObjCollector)
michael@0 2741 mEmbeddedObjCollector = new EmbeddedObjCollector(this);
michael@0 2742 return mEmbeddedObjCollector ?
michael@0 2743 mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
michael@0 2744 }
michael@0 2745
michael@0 2746 return GetIndexOf(aChild);
michael@0 2747 }
michael@0 2748
michael@0 2749 ////////////////////////////////////////////////////////////////////////////////
michael@0 2750 // HyperLinkAccessible methods
michael@0 2751
michael@0 2752 bool
michael@0 2753 Accessible::IsLink()
michael@0 2754 {
michael@0 2755 // Every embedded accessible within hypertext accessible implements
michael@0 2756 // hyperlink interface.
michael@0 2757 return mParent && mParent->IsHyperText() && nsAccUtils::IsEmbeddedObject(this);
michael@0 2758 }
michael@0 2759
michael@0 2760 uint32_t
michael@0 2761 Accessible::StartOffset()
michael@0 2762 {
michael@0 2763 NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
michael@0 2764
michael@0 2765 HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
michael@0 2766 return hyperText ? hyperText->GetChildOffset(this) : 0;
michael@0 2767 }
michael@0 2768
michael@0 2769 uint32_t
michael@0 2770 Accessible::EndOffset()
michael@0 2771 {
michael@0 2772 NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
michael@0 2773
michael@0 2774 HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
michael@0 2775 return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
michael@0 2776 }
michael@0 2777
michael@0 2778 uint32_t
michael@0 2779 Accessible::AnchorCount()
michael@0 2780 {
michael@0 2781 NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
michael@0 2782 return 1;
michael@0 2783 }
michael@0 2784
michael@0 2785 Accessible*
michael@0 2786 Accessible::AnchorAt(uint32_t aAnchorIndex)
michael@0 2787 {
michael@0 2788 NS_PRECONDITION(IsLink(), "GetAnchor is called on not hyper link!");
michael@0 2789 return aAnchorIndex == 0 ? this : nullptr;
michael@0 2790 }
michael@0 2791
michael@0 2792 already_AddRefed<nsIURI>
michael@0 2793 Accessible::AnchorURIAt(uint32_t aAnchorIndex)
michael@0 2794 {
michael@0 2795 NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
michael@0 2796 return nullptr;
michael@0 2797 }
michael@0 2798
michael@0 2799
michael@0 2800 ////////////////////////////////////////////////////////////////////////////////
michael@0 2801 // SelectAccessible
michael@0 2802
michael@0 2803 already_AddRefed<nsIArray>
michael@0 2804 Accessible::SelectedItems()
michael@0 2805 {
michael@0 2806 nsCOMPtr<nsIMutableArray> selectedItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
michael@0 2807 if (!selectedItems)
michael@0 2808 return nullptr;
michael@0 2809
michael@0 2810 AccIterator iter(this, filters::GetSelected);
michael@0 2811 nsIAccessible* selected = nullptr;
michael@0 2812 while ((selected = iter.Next()))
michael@0 2813 selectedItems->AppendElement(selected, false);
michael@0 2814
michael@0 2815 return selectedItems.forget();
michael@0 2816 }
michael@0 2817
michael@0 2818 uint32_t
michael@0 2819 Accessible::SelectedItemCount()
michael@0 2820 {
michael@0 2821 uint32_t count = 0;
michael@0 2822 AccIterator iter(this, filters::GetSelected);
michael@0 2823 Accessible* selected = nullptr;
michael@0 2824 while ((selected = iter.Next()))
michael@0 2825 ++count;
michael@0 2826
michael@0 2827 return count;
michael@0 2828 }
michael@0 2829
michael@0 2830 Accessible*
michael@0 2831 Accessible::GetSelectedItem(uint32_t aIndex)
michael@0 2832 {
michael@0 2833 AccIterator iter(this, filters::GetSelected);
michael@0 2834 Accessible* selected = nullptr;
michael@0 2835
michael@0 2836 uint32_t index = 0;
michael@0 2837 while ((selected = iter.Next()) && index < aIndex)
michael@0 2838 index++;
michael@0 2839
michael@0 2840 return selected;
michael@0 2841 }
michael@0 2842
michael@0 2843 bool
michael@0 2844 Accessible::IsItemSelected(uint32_t aIndex)
michael@0 2845 {
michael@0 2846 uint32_t index = 0;
michael@0 2847 AccIterator iter(this, filters::GetSelectable);
michael@0 2848 Accessible* selected = nullptr;
michael@0 2849 while ((selected = iter.Next()) && index < aIndex)
michael@0 2850 index++;
michael@0 2851
michael@0 2852 return selected &&
michael@0 2853 selected->State() & states::SELECTED;
michael@0 2854 }
michael@0 2855
michael@0 2856 bool
michael@0 2857 Accessible::AddItemToSelection(uint32_t aIndex)
michael@0 2858 {
michael@0 2859 uint32_t index = 0;
michael@0 2860 AccIterator iter(this, filters::GetSelectable);
michael@0 2861 Accessible* selected = nullptr;
michael@0 2862 while ((selected = iter.Next()) && index < aIndex)
michael@0 2863 index++;
michael@0 2864
michael@0 2865 if (selected)
michael@0 2866 selected->SetSelected(true);
michael@0 2867
michael@0 2868 return static_cast<bool>(selected);
michael@0 2869 }
michael@0 2870
michael@0 2871 bool
michael@0 2872 Accessible::RemoveItemFromSelection(uint32_t aIndex)
michael@0 2873 {
michael@0 2874 uint32_t index = 0;
michael@0 2875 AccIterator iter(this, filters::GetSelectable);
michael@0 2876 Accessible* selected = nullptr;
michael@0 2877 while ((selected = iter.Next()) && index < aIndex)
michael@0 2878 index++;
michael@0 2879
michael@0 2880 if (selected)
michael@0 2881 selected->SetSelected(false);
michael@0 2882
michael@0 2883 return static_cast<bool>(selected);
michael@0 2884 }
michael@0 2885
michael@0 2886 bool
michael@0 2887 Accessible::SelectAll()
michael@0 2888 {
michael@0 2889 bool success = false;
michael@0 2890 Accessible* selectable = nullptr;
michael@0 2891
michael@0 2892 AccIterator iter(this, filters::GetSelectable);
michael@0 2893 while((selectable = iter.Next())) {
michael@0 2894 success = true;
michael@0 2895 selectable->SetSelected(true);
michael@0 2896 }
michael@0 2897 return success;
michael@0 2898 }
michael@0 2899
michael@0 2900 bool
michael@0 2901 Accessible::UnselectAll()
michael@0 2902 {
michael@0 2903 bool success = false;
michael@0 2904 Accessible* selected = nullptr;
michael@0 2905
michael@0 2906 AccIterator iter(this, filters::GetSelected);
michael@0 2907 while ((selected = iter.Next())) {
michael@0 2908 success = true;
michael@0 2909 selected->SetSelected(false);
michael@0 2910 }
michael@0 2911 return success;
michael@0 2912 }
michael@0 2913
michael@0 2914 ////////////////////////////////////////////////////////////////////////////////
michael@0 2915 // Widgets
michael@0 2916
michael@0 2917 bool
michael@0 2918 Accessible::IsWidget() const
michael@0 2919 {
michael@0 2920 return false;
michael@0 2921 }
michael@0 2922
michael@0 2923 bool
michael@0 2924 Accessible::IsActiveWidget() const
michael@0 2925 {
michael@0 2926 if (FocusMgr()->HasDOMFocus(mContent))
michael@0 2927 return true;
michael@0 2928
michael@0 2929 // If text entry of combobox widget has a focus then the combobox widget is
michael@0 2930 // active.
michael@0 2931 if (mRoleMapEntry && mRoleMapEntry->Is(nsGkAtoms::combobox)) {
michael@0 2932 uint32_t childCount = ChildCount();
michael@0 2933 for (uint32_t idx = 0; idx < childCount; idx++) {
michael@0 2934 Accessible* child = mChildren.ElementAt(idx);
michael@0 2935 if (child->Role() == roles::ENTRY)
michael@0 2936 return FocusMgr()->HasDOMFocus(child->GetContent());
michael@0 2937 }
michael@0 2938 }
michael@0 2939
michael@0 2940 return false;
michael@0 2941 }
michael@0 2942
michael@0 2943 bool
michael@0 2944 Accessible::AreItemsOperable() const
michael@0 2945 {
michael@0 2946 return HasOwnContent() &&
michael@0 2947 mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
michael@0 2948 }
michael@0 2949
michael@0 2950 Accessible*
michael@0 2951 Accessible::CurrentItem()
michael@0 2952 {
michael@0 2953 // Check for aria-activedescendant, which changes which element has focus.
michael@0 2954 // For activedescendant, the ARIA spec does not require that the user agent
michael@0 2955 // checks whether pointed node is actually a DOM descendant of the element
michael@0 2956 // with the aria-activedescendant attribute.
michael@0 2957 nsAutoString id;
michael@0 2958 if (HasOwnContent() &&
michael@0 2959 mContent->GetAttr(kNameSpaceID_None,
michael@0 2960 nsGkAtoms::aria_activedescendant, id)) {
michael@0 2961 nsIDocument* DOMDoc = mContent->OwnerDoc();
michael@0 2962 dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
michael@0 2963 if (activeDescendantElm) {
michael@0 2964 DocAccessible* document = Document();
michael@0 2965 if (document)
michael@0 2966 return document->GetAccessible(activeDescendantElm);
michael@0 2967 }
michael@0 2968 }
michael@0 2969 return nullptr;
michael@0 2970 }
michael@0 2971
michael@0 2972 void
michael@0 2973 Accessible::SetCurrentItem(Accessible* aItem)
michael@0 2974 {
michael@0 2975 nsIAtom* id = aItem->GetContent()->GetID();
michael@0 2976 if (id) {
michael@0 2977 nsAutoString idStr;
michael@0 2978 id->ToString(idStr);
michael@0 2979 mContent->SetAttr(kNameSpaceID_None,
michael@0 2980 nsGkAtoms::aria_activedescendant, idStr, true);
michael@0 2981 }
michael@0 2982 }
michael@0 2983
michael@0 2984 Accessible*
michael@0 2985 Accessible::ContainerWidget() const
michael@0 2986 {
michael@0 2987 if (HasARIARole() && mContent->HasID()) {
michael@0 2988 for (Accessible* parent = Parent(); parent; parent = parent->Parent()) {
michael@0 2989 nsIContent* parentContent = parent->GetContent();
michael@0 2990 if (parentContent &&
michael@0 2991 parentContent->HasAttr(kNameSpaceID_None,
michael@0 2992 nsGkAtoms::aria_activedescendant)) {
michael@0 2993 return parent;
michael@0 2994 }
michael@0 2995
michael@0 2996 // Don't cross DOM document boundaries.
michael@0 2997 if (parent->IsDoc())
michael@0 2998 break;
michael@0 2999 }
michael@0 3000 }
michael@0 3001 return nullptr;
michael@0 3002 }
michael@0 3003
michael@0 3004 ////////////////////////////////////////////////////////////////////////////////
michael@0 3005 // Accessible protected methods
michael@0 3006
michael@0 3007 void
michael@0 3008 Accessible::LastRelease()
michael@0 3009 {
michael@0 3010 // First cleanup if needed...
michael@0 3011 if (mDoc) {
michael@0 3012 Shutdown();
michael@0 3013 NS_ASSERTION(!mDoc,
michael@0 3014 "A Shutdown() impl forgot to call its parent's Shutdown?");
michael@0 3015 }
michael@0 3016 // ... then die.
michael@0 3017 delete this;
michael@0 3018 }
michael@0 3019
michael@0 3020 void
michael@0 3021 Accessible::CacheChildren()
michael@0 3022 {
michael@0 3023 DocAccessible* doc = Document();
michael@0 3024 NS_ENSURE_TRUE_VOID(doc);
michael@0 3025
michael@0 3026 TreeWalker walker(this, mContent);
michael@0 3027
michael@0 3028 Accessible* child = nullptr;
michael@0 3029 while ((child = walker.NextChild()) && AppendChild(child));
michael@0 3030 }
michael@0 3031
michael@0 3032 void
michael@0 3033 Accessible::TestChildCache(Accessible* aCachedChild) const
michael@0 3034 {
michael@0 3035 #ifdef DEBUG
michael@0 3036 int32_t childCount = mChildren.Length();
michael@0 3037 if (childCount == 0) {
michael@0 3038 NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
michael@0 3039 "No children but initialized!");
michael@0 3040 return;
michael@0 3041 }
michael@0 3042
michael@0 3043 Accessible* child = nullptr;
michael@0 3044 for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 3045 child = mChildren[childIdx];
michael@0 3046 if (child == aCachedChild)
michael@0 3047 break;
michael@0 3048 }
michael@0 3049
michael@0 3050 NS_ASSERTION(child == aCachedChild,
michael@0 3051 "[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
michael@0 3052 #endif
michael@0 3053 }
michael@0 3054
michael@0 3055 // Accessible public
michael@0 3056 bool
michael@0 3057 Accessible::EnsureChildren()
michael@0 3058 {
michael@0 3059 if (IsDefunct()) {
michael@0 3060 SetChildrenFlag(eChildrenUninitialized);
michael@0 3061 return true;
michael@0 3062 }
michael@0 3063
michael@0 3064 if (!IsChildrenFlag(eChildrenUninitialized))
michael@0 3065 return false;
michael@0 3066
michael@0 3067 // State is embedded children until text leaf accessible is appended.
michael@0 3068 SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
michael@0 3069
michael@0 3070 CacheChildren();
michael@0 3071 return false;
michael@0 3072 }
michael@0 3073
michael@0 3074 Accessible*
michael@0 3075 Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
michael@0 3076 {
michael@0 3077 if (!mParent || mIndexInParent == -1) {
michael@0 3078 if (aError)
michael@0 3079 *aError = NS_ERROR_UNEXPECTED;
michael@0 3080
michael@0 3081 return nullptr;
michael@0 3082 }
michael@0 3083
michael@0 3084 if (aError &&
michael@0 3085 mIndexInParent + aOffset >= static_cast<int32_t>(mParent->ChildCount())) {
michael@0 3086 *aError = NS_OK; // fail peacefully
michael@0 3087 return nullptr;
michael@0 3088 }
michael@0 3089
michael@0 3090 Accessible* child = mParent->GetChildAt(mIndexInParent + aOffset);
michael@0 3091 if (aError && !child)
michael@0 3092 *aError = NS_ERROR_UNEXPECTED;
michael@0 3093
michael@0 3094 return child;
michael@0 3095 }
michael@0 3096
michael@0 3097 double
michael@0 3098 Accessible::AttrNumericValue(nsIAtom* aAttr) const
michael@0 3099 {
michael@0 3100 if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
michael@0 3101 return UnspecifiedNaN<double>();
michael@0 3102
michael@0 3103 nsAutoString attrValue;
michael@0 3104 if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
michael@0 3105 return UnspecifiedNaN<double>();
michael@0 3106
michael@0 3107 nsresult error = NS_OK;
michael@0 3108 double value = attrValue.ToDouble(&error);
michael@0 3109 return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
michael@0 3110 }
michael@0 3111
michael@0 3112 uint32_t
michael@0 3113 Accessible::GetActionRule()
michael@0 3114 {
michael@0 3115 if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
michael@0 3116 return eNoAction;
michael@0 3117
michael@0 3118 // Return "click" action on elements that have an attached popup menu.
michael@0 3119 if (mContent->IsXUL())
michael@0 3120 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
michael@0 3121 return eClickAction;
michael@0 3122
michael@0 3123 // Has registered 'click' event handler.
michael@0 3124 bool isOnclick = nsCoreUtils::HasClickListener(mContent);
michael@0 3125
michael@0 3126 if (isOnclick)
michael@0 3127 return eClickAction;
michael@0 3128
michael@0 3129 // Get an action based on ARIA role.
michael@0 3130 if (mRoleMapEntry &&
michael@0 3131 mRoleMapEntry->actionRule != eNoAction)
michael@0 3132 return mRoleMapEntry->actionRule;
michael@0 3133
michael@0 3134 // Get an action based on ARIA attribute.
michael@0 3135 if (nsAccUtils::HasDefinedARIAToken(mContent,
michael@0 3136 nsGkAtoms::aria_expanded))
michael@0 3137 return eExpandAction;
michael@0 3138
michael@0 3139 return eNoAction;
michael@0 3140 }
michael@0 3141
michael@0 3142 AccGroupInfo*
michael@0 3143 Accessible::GetGroupInfo()
michael@0 3144 {
michael@0 3145 if (mGroupInfo){
michael@0 3146 if (HasDirtyGroupInfo()) {
michael@0 3147 mGroupInfo->Update();
michael@0 3148 SetDirtyGroupInfo(false);
michael@0 3149 }
michael@0 3150
michael@0 3151 return mGroupInfo;
michael@0 3152 }
michael@0 3153
michael@0 3154 mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
michael@0 3155 return mGroupInfo;
michael@0 3156 }
michael@0 3157
michael@0 3158 void
michael@0 3159 Accessible::InvalidateChildrenGroupInfo()
michael@0 3160 {
michael@0 3161 uint32_t length = mChildren.Length();
michael@0 3162 for (uint32_t i = 0; i < length; i++) {
michael@0 3163 Accessible* child = mChildren[i];
michael@0 3164 child->SetDirtyGroupInfo(true);
michael@0 3165 }
michael@0 3166 }
michael@0 3167
michael@0 3168 void
michael@0 3169 Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
michael@0 3170 {
michael@0 3171 AccGroupInfo* groupInfo = GetGroupInfo();
michael@0 3172 if (groupInfo) {
michael@0 3173 *aPosInSet = groupInfo->PosInSet();
michael@0 3174 *aSetSize = groupInfo->SetSize();
michael@0 3175 }
michael@0 3176 }
michael@0 3177
michael@0 3178 int32_t
michael@0 3179 Accessible::GetLevelInternal()
michael@0 3180 {
michael@0 3181 int32_t level = nsAccUtils::GetDefaultLevel(this);
michael@0 3182
michael@0 3183 if (!IsBoundToParent())
michael@0 3184 return level;
michael@0 3185
michael@0 3186 roles::Role role = Role();
michael@0 3187 if (role == roles::OUTLINEITEM) {
michael@0 3188 // Always expose 'level' attribute for 'outlineitem' accessible. The number
michael@0 3189 // of nested 'grouping' accessibles containing 'outlineitem' accessible is
michael@0 3190 // its level.
michael@0 3191 level = 1;
michael@0 3192
michael@0 3193 Accessible* parent = this;
michael@0 3194 while ((parent = parent->Parent())) {
michael@0 3195 roles::Role parentRole = parent->Role();
michael@0 3196
michael@0 3197 if (parentRole == roles::OUTLINE)
michael@0 3198 break;
michael@0 3199 if (parentRole == roles::GROUPING)
michael@0 3200 ++ level;
michael@0 3201
michael@0 3202 }
michael@0 3203
michael@0 3204 } else if (role == roles::LISTITEM) {
michael@0 3205 // Expose 'level' attribute on nested lists. We support two hierarchies:
michael@0 3206 // a) list -> listitem -> list -> listitem (nested list is a last child
michael@0 3207 // of listitem of the parent list);
michael@0 3208 // b) list -> listitem -> group -> listitem (nested listitems are contained
michael@0 3209 // by group that is a last child of the parent listitem).
michael@0 3210
michael@0 3211 // Calculate 'level' attribute based on number of parent listitems.
michael@0 3212 level = 0;
michael@0 3213 Accessible* parent = this;
michael@0 3214 while ((parent = parent->Parent())) {
michael@0 3215 roles::Role parentRole = parent->Role();
michael@0 3216
michael@0 3217 if (parentRole == roles::LISTITEM)
michael@0 3218 ++ level;
michael@0 3219 else if (parentRole != roles::LIST && parentRole != roles::GROUPING)
michael@0 3220 break;
michael@0 3221 }
michael@0 3222
michael@0 3223 if (level == 0) {
michael@0 3224 // If this listitem is on top of nested lists then expose 'level'
michael@0 3225 // attribute.
michael@0 3226 parent = Parent();
michael@0 3227 uint32_t siblingCount = parent->ChildCount();
michael@0 3228 for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
michael@0 3229 Accessible* sibling = parent->GetChildAt(siblingIdx);
michael@0 3230
michael@0 3231 Accessible* siblingChild = sibling->LastChild();
michael@0 3232 if (siblingChild) {
michael@0 3233 roles::Role lastChildRole = siblingChild->Role();
michael@0 3234 if (lastChildRole == roles::LIST || lastChildRole == roles::GROUPING)
michael@0 3235 return 1;
michael@0 3236 }
michael@0 3237 }
michael@0 3238 } else {
michael@0 3239 ++ level; // level is 1-index based
michael@0 3240 }
michael@0 3241 }
michael@0 3242
michael@0 3243 return level;
michael@0 3244 }
michael@0 3245
michael@0 3246 void
michael@0 3247 Accessible::StaticAsserts() const
michael@0 3248 {
michael@0 3249 static_assert(eLastChildrenFlag <= (1 << kChildrenFlagsBits) - 1,
michael@0 3250 "Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
michael@0 3251 static_assert(eLastStateFlag <= (1 << kStateFlagsBits) - 1,
michael@0 3252 "Accessible::mStateFlags was oversized by eLastStateFlag!");
michael@0 3253 static_assert(eLastAccType <= (1 << kTypeBits) - 1,
michael@0 3254 "Accessible::mType was oversized by eLastAccType!");
michael@0 3255 static_assert(eLastContextFlag <= (1 << kContextFlagsBits) - 1,
michael@0 3256 "Accessible::mContextFlags was oversized by eLastContextFlag!");
michael@0 3257 static_assert(eLastAccGenericType <= (1 << kGenericTypesBits) - 1,
michael@0 3258 "Accessible::mGenericType was oversized by eLastAccGenericType!");
michael@0 3259 }
michael@0 3260
michael@0 3261
michael@0 3262 ////////////////////////////////////////////////////////////////////////////////
michael@0 3263 // KeyBinding class
michael@0 3264
michael@0 3265 void
michael@0 3266 KeyBinding::ToPlatformFormat(nsAString& aValue) const
michael@0 3267 {
michael@0 3268 nsCOMPtr<nsIStringBundle> keyStringBundle;
michael@0 3269 nsCOMPtr<nsIStringBundleService> stringBundleService =
michael@0 3270 mozilla::services::GetStringBundleService();
michael@0 3271 if (stringBundleService)
michael@0 3272 stringBundleService->CreateBundle(
michael@0 3273 "chrome://global-platform/locale/platformKeys.properties",
michael@0 3274 getter_AddRefs(keyStringBundle));
michael@0 3275
michael@0 3276 if (!keyStringBundle)
michael@0 3277 return;
michael@0 3278
michael@0 3279 nsAutoString separator;
michael@0 3280 keyStringBundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"),
michael@0 3281 getter_Copies(separator));
michael@0 3282
michael@0 3283 nsAutoString modifierName;
michael@0 3284 if (mModifierMask & kControl) {
michael@0 3285 keyStringBundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"),
michael@0 3286 getter_Copies(modifierName));
michael@0 3287
michael@0 3288 aValue.Append(modifierName);
michael@0 3289 aValue.Append(separator);
michael@0 3290 }
michael@0 3291
michael@0 3292 if (mModifierMask & kAlt) {
michael@0 3293 keyStringBundle->GetStringFromName(MOZ_UTF16("VK_ALT"),
michael@0 3294 getter_Copies(modifierName));
michael@0 3295
michael@0 3296 aValue.Append(modifierName);
michael@0 3297 aValue.Append(separator);
michael@0 3298 }
michael@0 3299
michael@0 3300 if (mModifierMask & kShift) {
michael@0 3301 keyStringBundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"),
michael@0 3302 getter_Copies(modifierName));
michael@0 3303
michael@0 3304 aValue.Append(modifierName);
michael@0 3305 aValue.Append(separator);
michael@0 3306 }
michael@0 3307
michael@0 3308 if (mModifierMask & kMeta) {
michael@0 3309 keyStringBundle->GetStringFromName(MOZ_UTF16("VK_META"),
michael@0 3310 getter_Copies(modifierName));
michael@0 3311
michael@0 3312 aValue.Append(modifierName);
michael@0 3313 aValue.Append(separator);
michael@0 3314 }
michael@0 3315
michael@0 3316 aValue.Append(mKey);
michael@0 3317 }
michael@0 3318
michael@0 3319 void
michael@0 3320 KeyBinding::ToAtkFormat(nsAString& aValue) const
michael@0 3321 {
michael@0 3322 nsAutoString modifierName;
michael@0 3323 if (mModifierMask & kControl)
michael@0 3324 aValue.Append(NS_LITERAL_STRING("<Control>"));
michael@0 3325
michael@0 3326 if (mModifierMask & kAlt)
michael@0 3327 aValue.Append(NS_LITERAL_STRING("<Alt>"));
michael@0 3328
michael@0 3329 if (mModifierMask & kShift)
michael@0 3330 aValue.Append(NS_LITERAL_STRING("<Shift>"));
michael@0 3331
michael@0 3332 if (mModifierMask & kMeta)
michael@0 3333 aValue.Append(NS_LITERAL_STRING("<Meta>"));
michael@0 3334
michael@0 3335 aValue.Append(mKey);
michael@0 3336 }
michael@0 3337

mercurial