LCOV - code coverage report
Current view: top level - accessible/base - nsTextEquivUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 161 0.6 %
Date: 2017-07-14 16:53:18 Functions: 2 13 15.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:expandtab:shiftwidth=2:tabstop=2:
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "nsTextEquivUtils.h"
       9             : 
      10             : #include "Accessible-inl.h"
      11             : #include "AccIterator.h"
      12             : #include "nsCoreUtils.h"
      13             : #include "nsIDOMXULLabeledControlEl.h"
      14             : 
      15             : using namespace mozilla::a11y;
      16             : 
      17             : /**
      18             :  * The accessible for which we are computing a text equivalent. It is useful
      19             :  * for bailing out during recursive text computation, or for special cases
      20             :  * like step f. of the ARIA implementation guide.
      21             :  */
      22             : static Accessible* sInitiatorAcc = nullptr;
      23             : 
      24             : ////////////////////////////////////////////////////////////////////////////////
      25             : // nsTextEquivUtils. Public.
      26             : 
      27             : nsresult
      28           0 : nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
      29             :                                      nsAString& aName)
      30             : {
      31           0 :   aName.Truncate();
      32             : 
      33           0 :   if (sInitiatorAcc)
      34           0 :     return NS_OK;
      35             : 
      36           0 :   sInitiatorAcc = aAccessible;
      37           0 :   if (GetRoleRule(aAccessible->Role()) == eNameFromSubtreeRule) {
      38             :     //XXX: is it necessary to care the accessible is not a document?
      39           0 :     if (aAccessible->IsContent()) {
      40           0 :       nsAutoString name;
      41           0 :       AppendFromAccessibleChildren(aAccessible, &name);
      42           0 :       name.CompressWhitespace();
      43           0 :       if (!nsCoreUtils::IsWhitespaceString(name))
      44           0 :         aName = name;
      45             :     }
      46             :   }
      47             : 
      48           0 :   sInitiatorAcc = nullptr;
      49             : 
      50           0 :   return NS_OK;
      51             : }
      52             : 
      53             : nsresult
      54           0 : nsTextEquivUtils::GetTextEquivFromIDRefs(Accessible* aAccessible,
      55             :                                          nsIAtom *aIDRefsAttr,
      56             :                                          nsAString& aTextEquiv)
      57             : {
      58           0 :   aTextEquiv.Truncate();
      59             : 
      60           0 :   nsIContent* content = aAccessible->GetContent();
      61           0 :   if (!content)
      62           0 :     return NS_OK;
      63             : 
      64           0 :   nsIContent* refContent = nullptr;
      65           0 :   IDRefsIterator iter(aAccessible->Document(), content, aIDRefsAttr);
      66           0 :   while ((refContent = iter.NextElem())) {
      67           0 :     if (!aTextEquiv.IsEmpty())
      68           0 :       aTextEquiv += ' ';
      69             : 
      70             :     nsresult rv = AppendTextEquivFromContent(aAccessible, refContent,
      71           0 :                                              &aTextEquiv);
      72           0 :     NS_ENSURE_SUCCESS(rv, rv);
      73             :   }
      74             : 
      75           0 :   return NS_OK;
      76             : }
      77             : 
      78             : nsresult
      79           0 : nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
      80             :                                              nsIContent *aContent,
      81             :                                              nsAString *aString)
      82             : {
      83             :   // Prevent recursion which can cause infinite loops.
      84           0 :   if (sInitiatorAcc)
      85           0 :     return NS_OK;
      86             : 
      87           0 :   sInitiatorAcc = aInitiatorAcc;
      88             : 
      89             :   // If the given content is not visible or isn't accessible then go down
      90             :   // through the DOM subtree otherwise go down through accessible subtree and
      91             :   // calculate the flat string.
      92           0 :   nsIFrame *frame = aContent->GetPrimaryFrame();
      93           0 :   bool isVisible = frame && frame->StyleVisibility()->IsVisible();
      94             : 
      95           0 :   nsresult rv = NS_ERROR_FAILURE;
      96           0 :   bool goThroughDOMSubtree = true;
      97             : 
      98           0 :   if (isVisible) {
      99             :     Accessible* accessible =
     100           0 :       sInitiatorAcc->Document()->GetAccessible(aContent);
     101           0 :     if (accessible) {
     102           0 :       rv = AppendFromAccessible(accessible, aString);
     103           0 :       goThroughDOMSubtree = false;
     104             :     }
     105             :   }
     106             : 
     107           0 :   if (goThroughDOMSubtree)
     108           0 :     rv = AppendFromDOMNode(aContent, aString);
     109             : 
     110           0 :   sInitiatorAcc = nullptr;
     111           0 :   return rv;
     112             : }
     113             : 
     114             : nsresult
     115           0 : nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
     116             :                                                  nsAString *aString)
     117             : {
     118           0 :   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
     119           0 :     bool isHTMLBlock = false;
     120             : 
     121           0 :     nsIContent *parentContent = aContent->GetFlattenedTreeParent();
     122           0 :     if (parentContent) {
     123           0 :       nsIFrame *frame = parentContent->GetPrimaryFrame();
     124           0 :       if (frame) {
     125             :         // If this text is inside a block level frame (as opposed to span
     126             :         // level), we need to add spaces around that block's text, so we don't
     127             :         // get words jammed together in final name.
     128           0 :         const nsStyleDisplay* display = frame->StyleDisplay();
     129           0 :         if (display->IsBlockOutsideStyle() ||
     130           0 :             display->mDisplay == StyleDisplay::TableCell) {
     131           0 :           isHTMLBlock = true;
     132           0 :           if (!aString->IsEmpty()) {
     133           0 :             aString->Append(char16_t(' '));
     134             :           }
     135             :         }
     136             :       }
     137             :     }
     138             : 
     139           0 :     if (aContent->TextLength() > 0) {
     140           0 :       nsIFrame *frame = aContent->GetPrimaryFrame();
     141           0 :       if (frame) {
     142             :         nsIFrame::RenderedText text = frame->GetRenderedText(0,
     143             :             UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
     144           0 :             nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     145           0 :         aString->Append(text.mString);
     146             :       } else {
     147             :         // If aContent is an object that is display: none, we have no a frame.
     148           0 :         aContent->AppendTextTo(*aString);
     149             :       }
     150           0 :       if (isHTMLBlock && !aString->IsEmpty()) {
     151           0 :         aString->Append(char16_t(' '));
     152             :       }
     153             :     }
     154             : 
     155           0 :     return NS_OK;
     156             :   }
     157             : 
     158           0 :   if (aContent->IsHTMLElement() &&
     159           0 :       aContent->NodeInfo()->Equals(nsGkAtoms::br)) {
     160           0 :     aString->AppendLiteral("\r\n");
     161           0 :     return NS_OK;
     162             :   }
     163             : 
     164           0 :   return NS_OK_NO_NAME_CLAUSE_HANDLED;
     165             : }
     166             : 
     167             : ////////////////////////////////////////////////////////////////////////////////
     168             : // nsTextEquivUtils. Private.
     169             : 
     170             : nsresult
     171           0 : nsTextEquivUtils::AppendFromAccessibleChildren(Accessible* aAccessible,
     172             :                                                nsAString *aString)
     173             : {
     174           0 :   nsresult rv = NS_OK_NO_NAME_CLAUSE_HANDLED;
     175             : 
     176           0 :   uint32_t childCount = aAccessible->ChildCount();
     177           0 :   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
     178           0 :     Accessible* child = aAccessible->GetChildAt(childIdx);
     179           0 :     rv = AppendFromAccessible(child, aString);
     180           0 :     NS_ENSURE_SUCCESS(rv, rv);
     181             :   }
     182             : 
     183           0 :   return rv;
     184             : }
     185             : 
     186             : nsresult
     187           0 : nsTextEquivUtils::AppendFromAccessible(Accessible* aAccessible,
     188             :                                        nsAString *aString)
     189             : {
     190             :   //XXX: is it necessary to care the accessible is not a document?
     191           0 :   if (aAccessible->IsContent()) {
     192           0 :     nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(),
     193           0 :                                                  aString);
     194           0 :     if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
     195           0 :       return rv;
     196             :   }
     197             : 
     198           0 :   bool isEmptyTextEquiv = true;
     199             : 
     200             :   // If the name is from tooltip then append it to result string in the end
     201             :   // (see h. step of name computation guide).
     202           0 :   nsAutoString text;
     203           0 :   if (aAccessible->Name(text) != eNameFromTooltip)
     204           0 :     isEmptyTextEquiv = !AppendString(aString, text);
     205             : 
     206             :   // Implementation of f. step.
     207           0 :   nsresult rv = AppendFromValue(aAccessible, aString);
     208           0 :   NS_ENSURE_SUCCESS(rv, rv);
     209             : 
     210           0 :   if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
     211           0 :     isEmptyTextEquiv = false;
     212             : 
     213             :   // Implementation of g) step of text equivalent computation guide. Go down
     214             :   // into subtree if accessible allows "text equivalent from subtree rule" or
     215             :   // it's not root and not control.
     216           0 :   if (isEmptyTextEquiv) {
     217           0 :     uint32_t nameRule = GetRoleRule(aAccessible->Role());
     218           0 :     if (nameRule & eNameFromSubtreeIfReqRule) {
     219           0 :       rv = AppendFromAccessibleChildren(aAccessible, aString);
     220           0 :       NS_ENSURE_SUCCESS(rv, rv);
     221             : 
     222           0 :       if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
     223           0 :         isEmptyTextEquiv = false;
     224             :     }
     225             :   }
     226             : 
     227             :   // Implementation of h. step
     228           0 :   if (isEmptyTextEquiv && !text.IsEmpty()) {
     229           0 :     AppendString(aString, text);
     230           0 :     return NS_OK;
     231             :   }
     232             : 
     233           0 :   return rv;
     234             : }
     235             : 
     236             : nsresult
     237           0 : nsTextEquivUtils::AppendFromValue(Accessible* aAccessible,
     238             :                                   nsAString *aString)
     239             : {
     240           0 :   if (GetRoleRule(aAccessible->Role()) != eNameFromValueRule)
     241           0 :     return NS_OK_NO_NAME_CLAUSE_HANDLED;
     242             : 
     243             :   // Implementation of step f. of text equivalent computation. If the given
     244             :   // accessible is not root accessible (the accessible the text equivalent is
     245             :   // computed for in the end) then append accessible value. Otherwise append
     246             :   // value if and only if the given accessible is in the middle of its parent.
     247             : 
     248           0 :   nsAutoString text;
     249           0 :   if (aAccessible != sInitiatorAcc) {
     250           0 :     aAccessible->Value(text);
     251             : 
     252           0 :     return AppendString(aString, text) ?
     253           0 :       NS_OK : NS_OK_NO_NAME_CLAUSE_HANDLED;
     254             :   }
     255             : 
     256             :   //XXX: is it necessary to care the accessible is not a document?
     257           0 :   if (aAccessible->IsDoc())
     258           0 :     return NS_ERROR_UNEXPECTED;
     259             : 
     260           0 :   nsIContent *content = aAccessible->GetContent();
     261             : 
     262           0 :   for (nsIContent* childContent = content->GetPreviousSibling(); childContent;
     263           0 :        childContent = childContent->GetPreviousSibling()) {
     264             :     // check for preceding text...
     265           0 :     if (!childContent->TextIsOnlyWhitespace()) {
     266           0 :       for (nsIContent* siblingContent = content->GetNextSibling(); siblingContent;
     267           0 :            siblingContent = siblingContent->GetNextSibling()) {
     268             :         // .. and subsequent text
     269           0 :         if (!siblingContent->TextIsOnlyWhitespace()) {
     270           0 :           aAccessible->Value(text);
     271             : 
     272           0 :           return AppendString(aString, text) ?
     273           0 :             NS_OK : NS_OK_NO_NAME_CLAUSE_HANDLED;
     274             :           break;
     275             :         }
     276             :       }
     277           0 :       break;
     278             :     }
     279             :   }
     280             : 
     281           0 :   return NS_OK_NO_NAME_CLAUSE_HANDLED;
     282             : }
     283             : 
     284             : nsresult
     285           0 : nsTextEquivUtils::AppendFromDOMChildren(nsIContent *aContent,
     286             :                                         nsAString *aString)
     287             : {
     288           0 :   for (nsIContent* childContent = aContent->GetFirstChild(); childContent;
     289           0 :        childContent = childContent->GetNextSibling()) {
     290           0 :     nsresult rv = AppendFromDOMNode(childContent, aString);
     291           0 :     NS_ENSURE_SUCCESS(rv, rv);
     292             :   }
     293             : 
     294           0 :   return NS_OK;
     295             : }
     296             : 
     297             : nsresult
     298           0 : nsTextEquivUtils::AppendFromDOMNode(nsIContent *aContent, nsAString *aString)
     299             : {
     300           0 :   nsresult rv = AppendTextEquivFromTextContent(aContent, aString);
     301           0 :   NS_ENSURE_SUCCESS(rv, rv);
     302             : 
     303           0 :   if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
     304           0 :     return NS_OK;
     305             : 
     306           0 :   if (aContent->IsXULElement()) {
     307           0 :     nsAutoString textEquivalent;
     308             :     nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
     309           0 :       do_QueryInterface(aContent);
     310             : 
     311           0 :     if (labeledEl) {
     312           0 :       labeledEl->GetLabel(textEquivalent);
     313             :     } else {
     314           0 :       if (aContent->NodeInfo()->Equals(nsGkAtoms::label,
     315             :                                        kNameSpaceID_XUL))
     316             :         aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
     317           0 :                           textEquivalent);
     318             : 
     319           0 :       if (textEquivalent.IsEmpty())
     320             :         aContent->GetAttr(kNameSpaceID_None,
     321           0 :                           nsGkAtoms::tooltiptext, textEquivalent);
     322             :     }
     323             : 
     324           0 :     AppendString(aString, textEquivalent);
     325             :   }
     326             : 
     327           0 :   return AppendFromDOMChildren(aContent, aString);
     328             : }
     329             : 
     330             : bool
     331           0 : nsTextEquivUtils::AppendString(nsAString *aString,
     332             :                                const nsAString& aTextEquivalent)
     333             : {
     334           0 :   if (aTextEquivalent.IsEmpty())
     335           0 :     return false;
     336             : 
     337             :   // Insert spaces to insure that words from controls aren't jammed together.
     338           0 :   if (!aString->IsEmpty() && !nsCoreUtils::IsWhitespace(aString->Last()))
     339           0 :     aString->Append(char16_t(' '));
     340             : 
     341           0 :   aString->Append(aTextEquivalent);
     342             : 
     343           0 :   if (!nsCoreUtils::IsWhitespace(aString->Last()))
     344           0 :     aString->Append(char16_t(' '));
     345             : 
     346           0 :   return true;
     347             : }
     348             : 
     349             : uint32_t
     350           0 : nsTextEquivUtils::GetRoleRule(role aRole)
     351             : {
     352             : #define ROLE(geckoRole, stringRole, atkRole, \
     353             :              macRole, msaaRole, ia2Role, nameRule) \
     354             :   case roles::geckoRole: \
     355             :     return nameRule;
     356             : 
     357           0 :   switch (aRole) {
     358             : #include "RoleMap.h"
     359             :     default:
     360           0 :       MOZ_CRASH("Unknown role.");
     361             :   }
     362             : 
     363             : #undef ROLE
     364           9 : }

Generated by: LCOV version 1.13