accessible/src/base/nsTextEquivUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     8 #include "nsTextEquivUtils.h"
    10 #include "Accessible-inl.h"
    11 #include "AccIterator.h"
    12 #include "nsCoreUtils.h"
    13 #include "nsIDOMXULLabeledControlEl.h"
    15 using namespace mozilla::a11y;
    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;
    24 ////////////////////////////////////////////////////////////////////////////////
    25 // nsTextEquivUtils. Public.
    27 nsresult
    28 nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
    29                                      nsAString& aName)
    30 {
    31   aName.Truncate();
    33   if (sInitiatorAcc)
    34     return NS_OK;
    36   sInitiatorAcc = aAccessible;
    37   if (GetRoleRule(aAccessible->Role()) == eNameFromSubtreeRule) {
    38     //XXX: is it necessary to care the accessible is not a document?
    39     if (aAccessible->IsContent()) {
    40       nsAutoString name;
    41       AppendFromAccessibleChildren(aAccessible, &name);
    42       name.CompressWhitespace();
    43       if (!nsCoreUtils::IsWhitespaceString(name))
    44         aName = name;
    45     }
    46   }
    48   sInitiatorAcc = nullptr;
    50   return NS_OK;
    51 }
    53 nsresult
    54 nsTextEquivUtils::GetTextEquivFromIDRefs(Accessible* aAccessible,
    55                                          nsIAtom *aIDRefsAttr,
    56                                          nsAString& aTextEquiv)
    57 {
    58   aTextEquiv.Truncate();
    60   nsIContent* content = aAccessible->GetContent();
    61   if (!content)
    62     return NS_OK;
    64   nsIContent* refContent = nullptr;
    65   IDRefsIterator iter(aAccessible->Document(), content, aIDRefsAttr);
    66   while ((refContent = iter.NextElem())) {
    67     if (!aTextEquiv.IsEmpty())
    68       aTextEquiv += ' ';
    70     nsresult rv = AppendTextEquivFromContent(aAccessible, refContent,
    71                                              &aTextEquiv);
    72     NS_ENSURE_SUCCESS(rv, rv);
    73   }
    75   return NS_OK;
    76 }
    78 nsresult
    79 nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
    80                                              nsIContent *aContent,
    81                                              nsAString *aString)
    82 {
    83   // Prevent recursion which can cause infinite loops.
    84   if (sInitiatorAcc)
    85     return NS_OK;
    87   sInitiatorAcc = aInitiatorAcc;
    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   nsIFrame *frame = aContent->GetPrimaryFrame();
    93   bool isVisible = frame && frame->StyleVisibility()->IsVisible();
    95   nsresult rv = NS_ERROR_FAILURE;
    96   bool goThroughDOMSubtree = true;
    98   if (isVisible) {
    99     Accessible* accessible =
   100       sInitiatorAcc->Document()->GetAccessible(aContent);
   101     if (accessible) {
   102       rv = AppendFromAccessible(accessible, aString);
   103       goThroughDOMSubtree = false;
   104     }
   105   }
   107   if (goThroughDOMSubtree)
   108     rv = AppendFromDOMNode(aContent, aString);
   110   sInitiatorAcc = nullptr;
   111   return rv;
   112 }
   114 nsresult
   115 nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
   116                                                  nsAString *aString)
   117 {
   118   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
   119     bool isHTMLBlock = false;
   121     nsIContent *parentContent = aContent->GetFlattenedTreeParent();
   122     if (parentContent) {
   123       nsIFrame *frame = parentContent->GetPrimaryFrame();
   124       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         const nsStyleDisplay* display = frame->StyleDisplay();
   129         if (display->IsBlockOutsideStyle() ||
   130             display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
   131           isHTMLBlock = true;
   132           if (!aString->IsEmpty()) {
   133             aString->Append(char16_t(' '));
   134           }
   135         }
   136       }
   137     }
   139     if (aContent->TextLength() > 0) {
   140       nsIFrame *frame = aContent->GetPrimaryFrame();
   141       if (frame) {
   142         nsresult rv = frame->GetRenderedText(aString);
   143         NS_ENSURE_SUCCESS(rv, rv);
   144       } else {
   145         // If aContent is an object that is display: none, we have no a frame.
   146         aContent->AppendTextTo(*aString);
   147       }
   148       if (isHTMLBlock && !aString->IsEmpty()) {
   149         aString->Append(char16_t(' '));
   150       }
   151     }
   153     return NS_OK;
   154   }
   156   if (aContent->IsHTML() &&
   157       aContent->NodeInfo()->Equals(nsGkAtoms::br)) {
   158     aString->AppendLiteral("\r\n");
   159     return NS_OK;
   160   }
   162   return NS_OK_NO_NAME_CLAUSE_HANDLED;
   163 }
   165 ////////////////////////////////////////////////////////////////////////////////
   166 // nsTextEquivUtils. Private.
   168 nsresult
   169 nsTextEquivUtils::AppendFromAccessibleChildren(Accessible* aAccessible,
   170                                                nsAString *aString)
   171 {
   172   nsresult rv = NS_OK_NO_NAME_CLAUSE_HANDLED;
   174   uint32_t childCount = aAccessible->ChildCount();
   175   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
   176     Accessible* child = aAccessible->GetChildAt(childIdx);
   177     rv = AppendFromAccessible(child, aString);
   178     NS_ENSURE_SUCCESS(rv, rv);
   179   }
   181   return rv;
   182 }
   184 nsresult
   185 nsTextEquivUtils::AppendFromAccessible(Accessible* aAccessible,
   186                                        nsAString *aString)
   187 {
   188   //XXX: is it necessary to care the accessible is not a document?
   189   if (aAccessible->IsContent()) {
   190     nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(),
   191                                                  aString);
   192     if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
   193       return rv;
   194   }
   196   bool isEmptyTextEquiv = true;
   198   // If the name is from tooltip then append it to result string in the end
   199   // (see h. step of name computation guide).
   200   nsAutoString text;
   201   if (aAccessible->Name(text) != eNameFromTooltip)
   202     isEmptyTextEquiv = !AppendString(aString, text);
   204   // Implementation of f. step.
   205   nsresult rv = AppendFromValue(aAccessible, aString);
   206   NS_ENSURE_SUCCESS(rv, rv);
   208   if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
   209     isEmptyTextEquiv = false;
   211   // Implementation of g) step of text equivalent computation guide. Go down
   212   // into subtree if accessible allows "text equivalent from subtree rule" or
   213   // it's not root and not control.
   214   if (isEmptyTextEquiv) {
   215     uint32_t nameRule = GetRoleRule(aAccessible->Role());
   216     if (nameRule & eNameFromSubtreeIfReqRule) {
   217       rv = AppendFromAccessibleChildren(aAccessible, aString);
   218       NS_ENSURE_SUCCESS(rv, rv);
   220       if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
   221         isEmptyTextEquiv = false;
   222     }
   223   }
   225   // Implementation of h. step
   226   if (isEmptyTextEquiv && !text.IsEmpty()) {
   227     AppendString(aString, text);
   228     return NS_OK;
   229   }
   231   return rv;
   232 }
   234 nsresult
   235 nsTextEquivUtils::AppendFromValue(Accessible* aAccessible,
   236                                   nsAString *aString)
   237 {
   238   if (GetRoleRule(aAccessible->Role()) != eNameFromValueRule)
   239     return NS_OK_NO_NAME_CLAUSE_HANDLED;
   241   // Implementation of step f. of text equivalent computation. If the given
   242   // accessible is not root accessible (the accessible the text equivalent is
   243   // computed for in the end) then append accessible value. Otherwise append
   244   // value if and only if the given accessible is in the middle of its parent.
   246   nsAutoString text;
   247   if (aAccessible != sInitiatorAcc) {
   248     aAccessible->Value(text);
   250     return AppendString(aString, text) ?
   251       NS_OK : NS_OK_NO_NAME_CLAUSE_HANDLED;
   252   }
   254   //XXX: is it necessary to care the accessible is not a document?
   255   if (aAccessible->IsDoc())
   256     return NS_ERROR_UNEXPECTED;
   258   nsIContent *content = aAccessible->GetContent();
   260   for (nsIContent* childContent = content->GetPreviousSibling(); childContent;
   261        childContent = childContent->GetPreviousSibling()) {
   262     // check for preceding text...
   263     if (!childContent->TextIsOnlyWhitespace()) {
   264       for (nsIContent* siblingContent = content->GetNextSibling(); siblingContent;
   265            siblingContent = siblingContent->GetNextSibling()) {
   266         // .. and subsequent text
   267         if (!siblingContent->TextIsOnlyWhitespace()) {
   268           aAccessible->Value(text);
   270           return AppendString(aString, text) ?
   271             NS_OK : NS_OK_NO_NAME_CLAUSE_HANDLED;
   272           break;
   273         }
   274       }
   275       break;
   276     }
   277   }
   279   return NS_OK_NO_NAME_CLAUSE_HANDLED;
   280 }
   282 nsresult
   283 nsTextEquivUtils::AppendFromDOMChildren(nsIContent *aContent,
   284                                         nsAString *aString)
   285 {
   286   for (nsIContent* childContent = aContent->GetFirstChild(); childContent;
   287        childContent = childContent->GetNextSibling()) {
   288     nsresult rv = AppendFromDOMNode(childContent, aString);
   289     NS_ENSURE_SUCCESS(rv, rv);
   290   }
   292   return NS_OK;
   293 }
   295 nsresult
   296 nsTextEquivUtils::AppendFromDOMNode(nsIContent *aContent, nsAString *aString)
   297 {
   298   nsresult rv = AppendTextEquivFromTextContent(aContent, aString);
   299   NS_ENSURE_SUCCESS(rv, rv);
   301   if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
   302     return NS_OK;
   304   if (aContent->IsXUL()) {
   305     nsAutoString textEquivalent;
   306     nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
   307       do_QueryInterface(aContent);
   309     if (labeledEl) {
   310       labeledEl->GetLabel(textEquivalent);
   311     } else {
   312       if (aContent->NodeInfo()->Equals(nsGkAtoms::label,
   313                                        kNameSpaceID_XUL))
   314         aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
   315                           textEquivalent);
   317       if (textEquivalent.IsEmpty())
   318         aContent->GetAttr(kNameSpaceID_None,
   319                           nsGkAtoms::tooltiptext, textEquivalent);
   320     }
   322     AppendString(aString, textEquivalent);
   323   }
   325   return AppendFromDOMChildren(aContent, aString);
   326 }
   328 bool
   329 nsTextEquivUtils::AppendString(nsAString *aString,
   330                                const nsAString& aTextEquivalent)
   331 {
   332   if (aTextEquivalent.IsEmpty())
   333     return false;
   335   // Insert spaces to insure that words from controls aren't jammed together.
   336   if (!aString->IsEmpty() && !nsCoreUtils::IsWhitespace(aString->Last()))
   337     aString->Append(char16_t(' '));
   339   aString->Append(aTextEquivalent);
   341   if (!nsCoreUtils::IsWhitespace(aString->Last()))
   342     aString->Append(char16_t(' '));
   344   return true;
   345 }
   347 uint32_t
   348 nsTextEquivUtils::GetRoleRule(role aRole)
   349 {
   350 #define ROLE(geckoRole, stringRole, atkRole, \
   351              macRole, msaaRole, ia2Role, nameRule) \
   352   case roles::geckoRole: \
   353     return nameRule;
   355   switch (aRole) {
   356 #include "RoleMap.h"
   357     default:
   358       MOZ_CRASH("Unknown role.");
   359   }
   361 #undef ROLE
   362 }

mercurial