tor-browser

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

nsFrameTraversal.cpp (10197B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsFrameTraversal.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/dom/Element.h"
     11 #include "mozilla/dom/PopoverData.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsContainerFrame.h"
     14 #include "nsFrameList.h"
     15 #include "nsGkAtoms.h"
     16 #include "nsPlaceholderFrame.h"
     17 #include "nsPresContext.h"
     18 
     19 using namespace mozilla;
     20 using namespace mozilla::dom;
     21 
     22 nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame* aStart,
     23                                 Type aType, bool aVisual,
     24                                 bool aLockInScrollView, bool aFollowOOFs,
     25                                 bool aSkipPopupChecks, const Element* aLimiter)
     26    : mPresContext(aPresContext),
     27      mLockScroll(aLockInScrollView),
     28      mFollowOOFs(aFollowOOFs),
     29      mSkipPopupChecks(aSkipPopupChecks),
     30      mVisual(aVisual),
     31      mType(aType),
     32      mStart(aFollowOOFs ? nsPlaceholderFrame::GetRealFrameFor(aStart)
     33                         : aStart),
     34      mCurrent(aStart),
     35      mLast(aStart),
     36      mLimiter(aLimiter),
     37      mOffEdge(0) {}
     38 
     39 nsIFrame* nsFrameIterator::CurrentItem() {
     40  if (mOffEdge) {
     41    return nullptr;
     42  }
     43 
     44  return mCurrent;
     45 }
     46 
     47 bool nsFrameIterator::IsDone() { return mOffEdge != 0; }
     48 
     49 void nsFrameIterator::First() { mCurrent = mStart; }
     50 
     51 static bool IsRootFrame(nsIFrame* aFrame) { return aFrame->IsCanvasFrame(); }
     52 
     53 void nsFrameIterator::Last() {
     54  nsIFrame* result;
     55  nsIFrame* parent = GetCurrent();
     56  // If the current frame is a popup, don't move farther up the tree.
     57  // Otherwise, get the nearest root frame or popup.
     58  if (mSkipPopupChecks || !parent->IsMenuPopupFrame()) {
     59    while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent))) {
     60      parent = result;
     61    }
     62  }
     63 
     64  while ((result = GetLastChild(parent))) {
     65    parent = result;
     66  }
     67 
     68  SetCurrent(parent);
     69  if (!parent) {
     70    SetOffEdge(1);
     71  }
     72 }
     73 
     74 void nsFrameIterator::Next() {
     75  // recursive-oid method to get next frame
     76  nsIFrame* result = nullptr;
     77  nsIFrame* parent = GetCurrent();
     78  if (!parent) {
     79    parent = GetLast();
     80  }
     81 
     82  if (mType == Type::Leaf) {
     83    // Drill down to first leaf
     84    while ((result = GetFirstChild(parent))) {
     85      parent = result;
     86    }
     87  } else if (mType == Type::PreOrder) {
     88    result = GetFirstChild(parent);
     89    if (result) {
     90      parent = result;
     91    }
     92  }
     93 
     94  if (parent != GetCurrent()) {
     95    result = parent;
     96  } else {
     97    while (parent) {
     98      result = GetNextSibling(parent);
     99      if (result) {
    100        if (mType != Type::PreOrder) {
    101          parent = result;
    102          while ((result = GetFirstChild(parent))) {
    103            parent = result;
    104          }
    105          result = parent;
    106        }
    107        break;
    108      }
    109      result = GetParentFrameNotPopup(parent);
    110      if (!result || IsRootFrame(result) ||
    111          (mLockScroll && result->IsScrollContainerFrame())) {
    112        result = nullptr;
    113        break;
    114      }
    115      if (mType == Type::PostOrder) {
    116        break;
    117      }
    118      parent = result;
    119    }
    120  }
    121 
    122  SetCurrent(result);
    123  if (!result) {
    124    SetOffEdge(1);
    125    SetLast(parent);
    126  }
    127 }
    128 
    129 void nsFrameIterator::Prev() {
    130  // recursive-oid method to get prev frame
    131  nsIFrame* result = nullptr;
    132  nsIFrame* parent = GetCurrent();
    133  if (!parent) {
    134    parent = GetLast();
    135  }
    136 
    137  if (mType == Type::Leaf) {
    138    // Drill down to last leaf
    139    while ((result = GetLastChild(parent))) {
    140      parent = result;
    141    }
    142  } else if (mType == Type::PostOrder) {
    143    result = GetLastChild(parent);
    144    if (result) {
    145      parent = result;
    146    }
    147  }
    148 
    149  if (parent != GetCurrent()) {
    150    result = parent;
    151  } else {
    152    while (parent) {
    153      result = GetPrevSibling(parent);
    154      if (result) {
    155        if (mType != Type::PostOrder) {
    156          parent = result;
    157          while ((result = GetLastChild(parent))) {
    158            parent = result;
    159          }
    160          result = parent;
    161        }
    162        break;
    163      }
    164      result = GetParentFrameNotPopup(parent);
    165      if (!result || IsRootFrame(result) ||
    166          (mLockScroll && result->IsScrollContainerFrame())) {
    167        result = nullptr;
    168        break;
    169      }
    170      if (mType == Type::PreOrder) {
    171        break;
    172      }
    173      parent = result;
    174    }
    175  }
    176 
    177  SetCurrent(result);
    178  if (!result) {
    179    SetOffEdge(-1);
    180    SetLast(parent);
    181  }
    182 }
    183 
    184 nsIFrame* nsFrameIterator::GetParentFrame(nsIFrame* aFrame,
    185                                          const Element* aAncestorLimiter) {
    186  if (mFollowOOFs) {
    187    aFrame = GetPlaceholderFrame(aFrame);
    188  }
    189  if (!aFrame) {
    190    return nullptr;
    191  }
    192  if (aAncestorLimiter && aFrame->GetContent() == aAncestorLimiter) {
    193    return nullptr;
    194  }
    195  return aFrame->GetParent();
    196 }
    197 
    198 nsIFrame* nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame) {
    199  if (mFollowOOFs) {
    200    aFrame = GetPlaceholderFrame(aFrame);
    201  }
    202  if (!aFrame) {
    203    return nullptr;
    204  }
    205 
    206  if (mLimiter && aFrame->GetContent() == mLimiter) {
    207    return nullptr;
    208  }
    209  nsIFrame* const parent = aFrame->GetParent();
    210  return IsPopupFrame(parent) ? nullptr : parent;
    211 }
    212 
    213 nsIFrame* nsFrameIterator::GetFirstChild(nsIFrame* aFrame) {
    214  nsIFrame* result = GetFirstChildInner(aFrame);
    215  if (mLockScroll && result && result->IsScrollContainerFrame()) {
    216    return nullptr;
    217  }
    218  if (result && mFollowOOFs) {
    219    result = nsPlaceholderFrame::GetRealFrameFor(result);
    220 
    221    if (IsPopupFrame(result) || IsInvokerOpenPopoverFrame(result)) {
    222      result = GetNextSibling(result);
    223    }
    224  }
    225 
    226  return result;
    227 }
    228 
    229 nsIFrame* nsFrameIterator::GetLastChild(nsIFrame* aFrame) {
    230  nsIFrame* result = GetLastChildInner(aFrame);
    231  if (mLockScroll && result && result->IsScrollContainerFrame()) {
    232    return nullptr;
    233  }
    234  if (result && mFollowOOFs) {
    235    result = nsPlaceholderFrame::GetRealFrameFor(result);
    236 
    237    if (IsPopupFrame(result) || IsInvokerOpenPopoverFrame(result)) {
    238      result = GetPrevSibling(result);
    239    }
    240  }
    241 
    242  return result;
    243 }
    244 
    245 nsIFrame* nsFrameIterator::GetNextSibling(nsIFrame* aFrame) {
    246  nsIFrame* result = nullptr;
    247  if (mFollowOOFs) {
    248    aFrame = GetPlaceholderFrame(aFrame);
    249  }
    250  if (aFrame) {
    251    result = GetNextSiblingInner(aFrame);
    252    if (result && mFollowOOFs) {
    253      result = nsPlaceholderFrame::GetRealFrameFor(result);
    254      if (IsPopupFrame(result) || IsInvokerOpenPopoverFrame(result)) {
    255        result = GetNextSibling(result);
    256      }
    257    }
    258  }
    259 
    260  return result;
    261 }
    262 
    263 nsIFrame* nsFrameIterator::GetPrevSibling(nsIFrame* aFrame) {
    264  nsIFrame* result = nullptr;
    265  if (mFollowOOFs) {
    266    aFrame = GetPlaceholderFrame(aFrame);
    267  }
    268  if (aFrame) {
    269    result = GetPrevSiblingInner(aFrame);
    270    if (result && mFollowOOFs) {
    271      result = nsPlaceholderFrame::GetRealFrameFor(result);
    272      if (IsPopupFrame(result) || IsInvokerOpenPopoverFrame(result)) {
    273        result = GetPrevSibling(result);
    274      }
    275    }
    276  }
    277 
    278  return result;
    279 }
    280 
    281 nsIFrame* nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
    282  return mVisual ? aFrame->PrincipalChildList().GetNextVisualFor(nullptr)
    283                 : aFrame->PrincipalChildList().FirstChild();
    284 }
    285 
    286 nsIFrame* nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
    287  return mVisual ? aFrame->PrincipalChildList().GetPrevVisualFor(nullptr)
    288                 : aFrame->PrincipalChildList().LastChild();
    289 }
    290 
    291 /**
    292 * Check whether aDestFrame is still in aLimiter if aLimiter is not nullptr.
    293 * aDestFrame should be next or previous frame of aOriginFrame.
    294 */
    295 static bool DidCrossLimiterBoundary(nsIFrame* aOriginFrame,
    296                                    nsIFrame* aDestFrame,
    297                                    const Element* aLimiter) {
    298  MOZ_ASSERT(aOriginFrame);
    299  MOZ_ASSERT(aDestFrame);
    300  MOZ_ASSERT(aOriginFrame->GetContent());
    301  MOZ_ASSERT_IF(
    302      aLimiter,
    303      aOriginFrame->GetContent()->IsInclusiveFlatTreeDescendantOf(aLimiter));
    304  if (!aLimiter || aOriginFrame->GetContent() == aDestFrame->GetContent() ||
    305      aOriginFrame->GetContent() != aLimiter) {
    306    return false;
    307  }
    308  return !aDestFrame->GetContent() ||
    309         !aDestFrame->GetContent()->IsInclusiveFlatTreeDescendantOf(aLimiter);
    310 }
    311 
    312 nsIFrame* nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
    313  if (!mVisual) {
    314    nsIFrame* const next = aFrame->GetNextSibling();
    315    if (!next || DidCrossLimiterBoundary(aFrame, next, mLimiter)) {
    316      return nullptr;
    317    }
    318    return next;
    319  }
    320  nsIFrame* const parent = GetParentFrame(aFrame, nullptr);
    321  if (!parent) {
    322    return nullptr;
    323  }
    324  nsIFrame* const next = parent->PrincipalChildList().GetNextVisualFor(aFrame);
    325  if (!next || DidCrossLimiterBoundary(aFrame, next, mLimiter)) {
    326    return nullptr;
    327  }
    328  return next;
    329 }
    330 
    331 nsIFrame* nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
    332  if (!mVisual) {
    333    nsIFrame* const prev = aFrame->GetPrevSibling();
    334    if (!prev || DidCrossLimiterBoundary(aFrame, prev, mLimiter)) {
    335      return nullptr;
    336    }
    337    return prev;
    338  }
    339  nsIFrame* const parent = GetParentFrame(aFrame, nullptr);
    340  if (!parent) {
    341    return nullptr;
    342  }
    343  nsIFrame* const prev = parent->PrincipalChildList().GetPrevVisualFor(aFrame);
    344  if (!prev || DidCrossLimiterBoundary(aFrame, prev, mLimiter)) {
    345    return nullptr;
    346  }
    347  return prev;
    348 }
    349 
    350 nsIFrame* nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame) {
    351  if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
    352    return aFrame;
    353  }
    354  nsIFrame* placeholder = aFrame->GetPlaceholderFrame();
    355  return placeholder ? placeholder : aFrame;
    356 }
    357 
    358 bool nsFrameIterator::IsPopupFrame(nsIFrame* aFrame) {
    359  // If skipping popup checks, pretend this isn't one.
    360  if (mSkipPopupChecks) {
    361    return false;
    362  }
    363  return aFrame && aFrame->IsMenuPopupFrame();
    364 }
    365 
    366 bool nsFrameIterator::IsInvokerOpenPopoverFrame(nsIFrame* aFrame) {
    367  if (const nsIContent* currentContent = aFrame->GetContent()) {
    368    if (const auto* popover = Element::FromNode(currentContent)) {
    369      return popover && popover->IsPopoverOpen() &&
    370             popover->GetPopoverData()->GetInvoker();
    371    }
    372  }
    373  return false;
    374 }