tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Pivot.cpp (10030B)


      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/. */
      5 
      6 #include "Pivot.h"
      7 
      8 #include "AccIterator.h"
      9 #include "LocalAccessible.h"
     10 #include "RemoteAccessible.h"
     11 #include "nsAccUtils.h"
     12 #include "nsIAccessiblePivot.h"
     13 
     14 #include "mozilla/a11y/Accessible.h"
     15 
     16 using namespace mozilla;
     17 using namespace mozilla::a11y;
     18 
     19 ////////////////////////////////////////////////////////////////////////////////
     20 // Pivot
     21 ////////////////////////////////////////////////////////////////////////////////
     22 
     23 Pivot::Pivot(Accessible* aRoot) : mRoot(aRoot) { MOZ_COUNT_CTOR(Pivot); }
     24 
     25 Pivot::~Pivot() { MOZ_COUNT_DTOR(Pivot); }
     26 
     27 Accessible* Pivot::AdjustStartPosition(Accessible* aAnchor, PivotRule& aRule,
     28                                       uint16_t* aFilterResult) {
     29  Accessible* matched = aAnchor;
     30  *aFilterResult = aRule.Match(aAnchor);
     31 
     32  if (aAnchor && aAnchor != mRoot) {
     33    for (Accessible* temp = aAnchor->Parent(); temp && temp != mRoot;
     34         temp = temp->Parent()) {
     35      uint16_t filtered = aRule.Match(temp);
     36      if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
     37        *aFilterResult = filtered;
     38        matched = temp;
     39      }
     40    }
     41  }
     42 
     43  return matched;
     44 }
     45 
     46 Accessible* Pivot::SearchBackward(Accessible* aAnchor, PivotRule& aRule,
     47                                  bool aSearchCurrent) {
     48  // Initial position could be unset, in that case return null.
     49  if (!aAnchor) {
     50    return nullptr;
     51  }
     52 
     53  uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
     54 
     55  Accessible* acc = AdjustStartPosition(aAnchor, aRule, &filtered);
     56 
     57  if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
     58    return acc;
     59  }
     60 
     61  while (acc && acc != mRoot) {
     62    Accessible* parent = acc->Parent();
     63 #if defined(ANDROID)
     64    MOZ_ASSERT(
     65        acc->IsLocal() || (acc->IsRemote() && parent->IsRemote()),
     66        "Pivot::SearchBackward climbed out of remote subtree in Android!");
     67 #endif
     68    int32_t idxInParent = acc->IndexInParent();
     69    while (idxInParent > 0 && parent) {
     70      acc = parent->ChildAt(--idxInParent);
     71      if (!acc) {
     72        continue;
     73      }
     74 
     75      filtered = aRule.Match(acc);
     76 
     77      Accessible* lastChild = acc->LastChild();
     78      while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
     79             lastChild) {
     80        parent = acc;
     81        acc = lastChild;
     82        idxInParent = acc->IndexInParent();
     83        filtered = aRule.Match(acc);
     84        lastChild = acc->LastChild();
     85      }
     86 
     87      if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
     88        return acc;
     89      }
     90    }
     91 
     92    acc = parent;
     93    if (!acc) {
     94      break;
     95    }
     96 
     97    filtered = aRule.Match(acc);
     98 
     99    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
    100      return acc;
    101    }
    102  }
    103 
    104  return nullptr;
    105 }
    106 
    107 Accessible* Pivot::SearchForward(Accessible* aAnchor, PivotRule& aRule,
    108                                 bool aSearchCurrent) {
    109  // Initial position could be not set, in that case begin search from root.
    110  Accessible* acc = aAnchor ? aAnchor : mRoot;
    111 
    112  uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
    113  acc = AdjustStartPosition(acc, aRule, &filtered);
    114  if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
    115    return acc;
    116  }
    117 
    118  while (acc) {
    119    Accessible* firstChild = acc->FirstChild();
    120    while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
    121           firstChild) {
    122      acc = firstChild;
    123      filtered = aRule.Match(acc);
    124 
    125      if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
    126        return acc;
    127      }
    128      firstChild = acc->FirstChild();
    129    }
    130 
    131    Accessible* sibling = nullptr;
    132    Accessible* temp = acc;
    133    do {
    134      if (temp == mRoot) {
    135        break;
    136      }
    137 
    138      sibling = temp->NextSibling();
    139 
    140      if (sibling) {
    141        break;
    142      }
    143      temp = temp->Parent();
    144 #if defined(ANDROID)
    145      MOZ_ASSERT(
    146          acc->IsLocal() || (acc->IsRemote() && temp->IsRemote()),
    147          "Pivot::SearchForward climbed out of remote subtree in Android!");
    148 #endif
    149 
    150    } while (temp);
    151 
    152    if (!sibling) {
    153      break;
    154    }
    155 
    156    acc = sibling;
    157    filtered = aRule.Match(acc);
    158    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
    159      return acc;
    160    }
    161  }
    162 
    163  return nullptr;
    164 }
    165 
    166 Accessible* Pivot::Next(Accessible* aAnchor, PivotRule& aRule,
    167                        bool aIncludeStart) {
    168  return SearchForward(aAnchor, aRule, aIncludeStart);
    169 }
    170 
    171 Accessible* Pivot::Prev(Accessible* aAnchor, PivotRule& aRule,
    172                        bool aIncludeStart) {
    173  return SearchBackward(aAnchor, aRule, aIncludeStart);
    174 }
    175 
    176 Accessible* Pivot::First(PivotRule& aRule) {
    177  return SearchForward(mRoot, aRule, true);
    178 }
    179 
    180 Accessible* Pivot::Last(PivotRule& aRule) {
    181  Accessible* lastAcc = mRoot;
    182 
    183  // First go to the last accessible in pre-order
    184  while (lastAcc && lastAcc->HasChildren()) {
    185    lastAcc = lastAcc->LastChild();
    186  }
    187 
    188  // Search backwards from last accessible and find the last occurrence in the
    189  // doc
    190  return SearchBackward(lastAcc, aRule, true);
    191 }
    192 
    193 Accessible* Pivot::AtPoint(int32_t aX, int32_t aY, PivotRule& aRule) {
    194  Accessible* match = nullptr;
    195  Accessible* child =
    196      mRoot ? mRoot->ChildAtPoint(aX, aY,
    197                                  Accessible::EWhichChildAtPoint::DeepestChild)
    198            : nullptr;
    199  while (child && (mRoot != child)) {
    200    uint16_t filtered = aRule.Match(child);
    201 
    202    // Ignore any matching nodes that were below this one
    203    if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
    204      match = nullptr;
    205    }
    206 
    207    // Match if no node below this is a match
    208    if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) {
    209      LayoutDeviceIntRect childRect = child->IsLocal()
    210                                          ? child->AsLocal()->Bounds()
    211                                          : child->AsRemote()->Bounds();
    212      // Double-check child's bounds since the deepest child may have been out
    213      // of bounds. This assures we don't return a false positive.
    214      if (childRect.Contains(aX, aY)) {
    215        match = child;
    216      }
    217    }
    218 
    219    child = child->Parent();
    220  }
    221 
    222  return match;
    223 }
    224 
    225 // Role Rule
    226 
    227 PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole)
    228    : mRole(aRole), mDirectDescendantsFrom(nullptr) {}
    229 
    230 PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole,
    231                             Accessible* aDirectDescendantsFrom)
    232    : mRole(aRole), mDirectDescendantsFrom(aDirectDescendantsFrom) {}
    233 
    234 uint16_t PivotRoleRule::Match(Accessible* aAcc) {
    235  uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
    236 
    237  if (nsAccUtils::MustPrune(aAcc)) {
    238    result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    239  }
    240 
    241  if (mDirectDescendantsFrom && (aAcc != mDirectDescendantsFrom)) {
    242    // If we've specified mDirectDescendantsFrom, we should ignore
    243    // non-direct descendants of from the specified AoP. Because
    244    // pivot performs a preorder traversal, the first aAcc
    245    // object(s) that don't equal mDirectDescendantsFrom will be
    246    // mDirectDescendantsFrom's children. We'll process them, but ignore
    247    // their subtrees thereby processing direct descendants of
    248    // mDirectDescendantsFrom only.
    249    result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    250  }
    251 
    252  if (aAcc && aAcc->Role() == mRole) {
    253    result |= nsIAccessibleTraversalRule::FILTER_MATCH;
    254  }
    255 
    256  return result;
    257 }
    258 
    259 // State Rule
    260 
    261 PivotStateRule::PivotStateRule(uint64_t aState) : mState(aState) {}
    262 
    263 uint16_t PivotStateRule::Match(Accessible* aAcc) {
    264  uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
    265 
    266  if (nsAccUtils::MustPrune(aAcc)) {
    267    result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    268  }
    269 
    270  if (aAcc && (aAcc->State() & mState)) {
    271    result = nsIAccessibleTraversalRule::FILTER_MATCH |
    272             nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    273  }
    274 
    275  return result;
    276 }
    277 
    278 // LocalAccInSameDocRule
    279 
    280 uint16_t LocalAccInSameDocRule::Match(Accessible* aAcc) {
    281  LocalAccessible* acc = aAcc ? aAcc->AsLocal() : nullptr;
    282  if (!acc) {
    283    return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    284  }
    285  if (acc->IsOuterDoc()) {
    286    return nsIAccessibleTraversalRule::FILTER_MATCH |
    287           nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    288  }
    289  return nsIAccessibleTraversalRule::FILTER_MATCH;
    290 }
    291 
    292 // Radio Button Name Rule
    293 
    294 PivotRadioNameRule::PivotRadioNameRule(const nsString& aName) : mName(aName) {}
    295 
    296 uint16_t PivotRadioNameRule::Match(Accessible* aAcc) {
    297  uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
    298  RemoteAccessible* remote = aAcc->AsRemote();
    299  if (!remote) {
    300    // We need the cache to be able to fetch the name attribute below.
    301    return result;
    302  }
    303 
    304  if (nsAccUtils::MustPrune(aAcc) || aAcc->IsOuterDoc()) {
    305    result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    306  }
    307 
    308  if (remote->IsHTMLRadioButton()) {
    309    nsString currName = remote->GetCachedHTMLNameAttribute();
    310    if (!currName.IsEmpty() && mName.Equals(currName)) {
    311      result |= nsIAccessibleTraversalRule::FILTER_MATCH;
    312    }
    313  }
    314 
    315  return result;
    316 }
    317 
    318 // MustPruneSameDocRule
    319 
    320 uint16_t MustPruneSameDocRule::Match(Accessible* aAcc) {
    321  if (!aAcc) {
    322    return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    323  }
    324 
    325  if (nsAccUtils::MustPrune(aAcc) || aAcc->IsOuterDoc()) {
    326    return nsIAccessibleTraversalRule::FILTER_MATCH |
    327           nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    328  }
    329 
    330  return nsIAccessibleTraversalRule::FILTER_MATCH;
    331 }
    332 
    333 // ARIA Selected Rule
    334 
    335 uint16_t PivotARIASelectedRule::Match(Accessible* aAcc) {
    336  uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
    337 
    338  if (nsAccUtils::MustPrune(aAcc)) {
    339    result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
    340  }
    341 
    342  if (aAcc && aAcc->ARIASelected()) {
    343    result = nsIAccessibleTraversalRule::FILTER_MATCH;
    344  }
    345 
    346  return result;
    347 }