accessible/src/base/nsAccUtils.cpp

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

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

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

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

mercurial