tor-browser

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

nsFrameList.cpp (14749B)


      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 "nsFrameList.h"
      8 
      9 #include "mozilla/ArenaObjectID.h"
     10 #include "mozilla/PresShell.h"
     11 #include "mozilla/intl/BidiEmbeddingLevel.h"
     12 #include "nsBidiPresUtils.h"
     13 #include "nsContainerFrame.h"
     14 #include "nsGkAtoms.h"
     15 #include "nsILineIterator.h"
     16 #include "nsLayoutUtils.h"
     17 #include "nsPresContext.h"
     18 
     19 using namespace mozilla;
     20 
     21 const nsFrameList nsFrameList::sEmptyList;
     22 
     23 void* nsFrameList::operator new(size_t sz, mozilla::PresShell* aPresShell) {
     24  return aPresShell->AllocateByObjectID(eArenaObjectID_nsFrameList, sz);
     25 }
     26 
     27 void nsFrameList::Delete(mozilla::PresShell* aPresShell) {
     28  MOZ_ASSERT(this != &EmptyList(), "Shouldn't Delete() this list");
     29  NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
     30 
     31  aPresShell->FreeByObjectID(eArenaObjectID_nsFrameList, this);
     32 }
     33 
     34 void nsFrameList::DestroyFrames(FrameDestroyContext& aContext) {
     35  while (nsIFrame* frame = RemoveLastChild()) {
     36    frame->Destroy(aContext);
     37  }
     38  MOZ_ASSERT(!mFirstChild && !mLastChild, "We should've destroyed all frames!");
     39 }
     40 
     41 void nsFrameList::RemoveFrame(nsIFrame* aFrame) {
     42  MOZ_ASSERT(aFrame, "null ptr");
     43 #ifdef DEBUG_FRAME_LIST
     44  // ContainsFrame is O(N)
     45  MOZ_ASSERT(ContainsFrame(aFrame), "wrong list");
     46 #endif
     47 
     48  nsIFrame* nextFrame = aFrame->GetNextSibling();
     49  if (aFrame == mFirstChild) {
     50    mFirstChild = nextFrame;
     51    aFrame->SetNextSibling(nullptr);
     52    if (!nextFrame) {
     53      mLastChild = nullptr;
     54    }
     55  } else {
     56    nsIFrame* prevSibling = aFrame->GetPrevSibling();
     57    NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
     58                 "Broken frame linkage");
     59    prevSibling->SetNextSibling(nextFrame);
     60    aFrame->SetNextSibling(nullptr);
     61    if (!nextFrame) {
     62      mLastChild = prevSibling;
     63    }
     64  }
     65 }
     66 
     67 nsFrameList nsFrameList::TakeFramesAfter(nsIFrame* aFrame) {
     68  if (!aFrame) {
     69    return std::move(*this);
     70  }
     71 
     72  MOZ_ASSERT(ContainsFrame(aFrame), "aFrame is not on this list!");
     73 
     74  nsIFrame* newFirstChild = aFrame->GetNextSibling();
     75  if (!newFirstChild) {
     76    return nsFrameList();
     77  }
     78 
     79  nsIFrame* newLastChild = mLastChild;
     80  mLastChild = aFrame;
     81  mLastChild->SetNextSibling(nullptr);
     82  return nsFrameList(newFirstChild, newLastChild);
     83 }
     84 
     85 nsIFrame* nsFrameList::RemoveFirstChild() {
     86  if (mFirstChild) {
     87    nsIFrame* firstChild = mFirstChild;
     88    RemoveFrame(firstChild);
     89    return firstChild;
     90  }
     91  return nullptr;
     92 }
     93 
     94 nsIFrame* nsFrameList::RemoveLastChild() {
     95  if (mLastChild) {
     96    nsIFrame* lastChild = mLastChild;
     97    RemoveFrame(lastChild);
     98    return lastChild;
     99  }
    100  return nullptr;
    101 }
    102 
    103 void nsFrameList::DestroyFrame(FrameDestroyContext& aContext,
    104                               nsIFrame* aFrame) {
    105  MOZ_ASSERT(aFrame, "null ptr");
    106  RemoveFrame(aFrame);
    107  aFrame->Destroy(aContext);
    108 }
    109 
    110 nsFrameList::Slice nsFrameList::InsertFrames(nsContainerFrame* aParent,
    111                                             nsIFrame* aPrevSibling,
    112                                             nsFrameList&& aFrameList) {
    113  MOZ_ASSERT(aFrameList.NotEmpty(), "Unexpected empty list");
    114 
    115  if (aParent) {
    116    aFrameList.ApplySetParent(aParent);
    117  }
    118 
    119  NS_ASSERTION(IsEmpty() || FirstChild()->GetParent() ==
    120                                aFrameList.FirstChild()->GetParent(),
    121               "frame to add has different parent");
    122  NS_ASSERTION(!aPrevSibling || aPrevSibling->GetParent() ==
    123                                    aFrameList.FirstChild()->GetParent(),
    124               "prev sibling has different parent");
    125 #ifdef DEBUG_FRAME_LIST
    126  // ContainsFrame is O(N)
    127  NS_ASSERTION(!aPrevSibling || ContainsFrame(aPrevSibling),
    128               "prev sibling is not on this list");
    129 #endif
    130 
    131  nsIFrame* firstNewFrame = aFrameList.FirstChild();
    132  nsIFrame* nextSibling;
    133  if (aPrevSibling) {
    134    nextSibling = aPrevSibling->GetNextSibling();
    135    aPrevSibling->SetNextSibling(firstNewFrame);
    136  } else {
    137    nextSibling = mFirstChild;
    138    mFirstChild = firstNewFrame;
    139  }
    140 
    141  nsIFrame* lastNewFrame = aFrameList.LastChild();
    142  lastNewFrame->SetNextSibling(nextSibling);
    143  if (!nextSibling) {
    144    mLastChild = lastNewFrame;
    145  }
    146 
    147  VerifyList();
    148 
    149  aFrameList.Clear();
    150  return Slice(firstNewFrame, nextSibling);
    151 }
    152 
    153 nsFrameList nsFrameList::TakeFramesBefore(nsIFrame* aFrame) {
    154  if (!aFrame) {
    155    // We handed over the whole list.
    156    return std::move(*this);
    157  }
    158 
    159  MOZ_ASSERT(ContainsFrame(aFrame), "aFrame is not on this list!");
    160 
    161  if (aFrame == mFirstChild) {
    162    // aFrame is our first child. Nothing to extract.
    163    return nsFrameList();
    164  }
    165 
    166  // Extract all previous siblings of aFrame as a new list.
    167  nsIFrame* prev = aFrame->GetPrevSibling();
    168  nsIFrame* newFirstChild = mFirstChild;
    169  nsIFrame* newLastChild = prev;
    170 
    171  prev->SetNextSibling(nullptr);
    172  mFirstChild = aFrame;
    173 
    174  return nsFrameList(newFirstChild, newLastChild);
    175 }
    176 
    177 nsIFrame* nsFrameList::FrameAt(int32_t aIndex) const {
    178  MOZ_ASSERT(aIndex >= 0, "invalid arg");
    179  if (aIndex < 0) {
    180    return nullptr;
    181  }
    182  nsIFrame* frame = mFirstChild;
    183  while ((aIndex-- > 0) && frame) {
    184    frame = frame->GetNextSibling();
    185  }
    186  return frame;
    187 }
    188 
    189 int32_t nsFrameList::IndexOf(const nsIFrame* aFrame) const {
    190  int32_t count = 0;
    191  for (nsIFrame* f = mFirstChild; f; f = f->GetNextSibling()) {
    192    if (f == aFrame) {
    193      return count;
    194    }
    195    ++count;
    196  }
    197  return -1;
    198 }
    199 
    200 bool nsFrameList::ContainsFrame(const nsIFrame* aFrame) const {
    201  MOZ_ASSERT(aFrame, "null ptr");
    202 
    203  nsIFrame* frame = mFirstChild;
    204  while (frame) {
    205    if (frame == aFrame) {
    206      return true;
    207    }
    208    frame = frame->GetNextSibling();
    209  }
    210  return false;
    211 }
    212 
    213 int32_t nsFrameList::GetLength() const {
    214  int32_t count = 0;
    215  nsIFrame* frame = mFirstChild;
    216  while (frame) {
    217    count++;
    218    frame = frame->GetNextSibling();
    219  }
    220  return count;
    221 }
    222 
    223 void nsFrameList::ApplySetParent(nsContainerFrame* aParent) const {
    224  NS_ASSERTION(aParent, "null ptr");
    225 
    226  for (nsIFrame* f = FirstChild(); f; f = f->GetNextSibling()) {
    227    f->SetParent(aParent);
    228  }
    229 }
    230 
    231 /* static */
    232 void nsFrameList::UnhookFrameFromSiblings(nsIFrame* aFrame) {
    233  MOZ_ASSERT(aFrame->GetPrevSibling() && aFrame->GetNextSibling());
    234  nsIFrame* const nextSibling = aFrame->GetNextSibling();
    235  nsIFrame* const prevSibling = aFrame->GetPrevSibling();
    236  aFrame->SetNextSibling(nullptr);
    237  prevSibling->SetNextSibling(nextSibling);
    238  MOZ_ASSERT(!aFrame->GetPrevSibling() && !aFrame->GetNextSibling());
    239 }
    240 
    241 #ifdef DEBUG_FRAME_DUMP
    242 void nsFrameList::List(FILE* out) const {
    243  fprintf_stderr(out, "<\n");
    244  for (nsIFrame* frame = mFirstChild; frame; frame = frame->GetNextSibling()) {
    245    frame->List(out, "  ");
    246  }
    247  fprintf_stderr(out, ">\n");
    248 }
    249 #endif
    250 
    251 nsIFrame* nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const {
    252  if (!mFirstChild) {
    253    return nullptr;
    254  }
    255 
    256  nsIFrame* parent = mFirstChild->GetParent();
    257  if (!parent) {
    258    return aFrame ? aFrame->GetPrevSibling() : LastChild();
    259  }
    260 
    261  mozilla::intl::BidiDirection paraDir =
    262      nsBidiPresUtils::ParagraphDirection(mFirstChild);
    263 
    264  AutoAssertNoDomMutations guard;
    265  nsILineIterator* iter = parent->GetLineIterator();
    266  if (!iter) {
    267    // Parent is not a block Frame
    268    if (parent->IsLineFrame()) {
    269      // Line frames are not bidi-splittable, so need to consider bidi
    270      // reordering
    271      if (paraDir == mozilla::intl::BidiDirection::LTR) {
    272        return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
    273      } else {  // RTL
    274        return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
    275      }
    276    } else {
    277      // Just get the next or prev sibling, depending on block and frame
    278      // direction.
    279      if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
    280        return aFrame ? aFrame->GetPrevSibling() : LastChild();
    281      } else {
    282        return aFrame ? aFrame->GetNextSibling() : mFirstChild;
    283      }
    284    }
    285  }
    286 
    287  // Parent is a block frame, so use the LineIterator to find the previous
    288  // visual sibling on this line, or the last one on the previous line.
    289 
    290  int32_t thisLine;
    291  if (aFrame) {
    292    thisLine = iter->FindLineContaining(aFrame);
    293    if (thisLine < 0) {
    294      return nullptr;
    295    }
    296  } else {
    297    thisLine = iter->GetNumLines();
    298  }
    299 
    300  nsIFrame* frame = nullptr;
    301 
    302  if (aFrame) {
    303    auto line = iter->GetLine(thisLine).unwrap();
    304 
    305    if (paraDir == mozilla::intl::BidiDirection::LTR) {
    306      frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, line.mFirstFrameOnLine,
    307                                                line.mNumFramesOnLine);
    308    } else {  // RTL
    309      frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, line.mFirstFrameOnLine,
    310                                                 line.mNumFramesOnLine);
    311    }
    312  }
    313 
    314  if (!frame && thisLine > 0) {
    315    // Get the last frame of the previous line
    316    auto line = iter->GetLine(thisLine - 1).unwrap();
    317 
    318    if (paraDir == mozilla::intl::BidiDirection::LTR) {
    319      frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, line.mFirstFrameOnLine,
    320                                                line.mNumFramesOnLine);
    321    } else {  // RTL
    322      frame = nsBidiPresUtils::GetFrameToRightOf(
    323          nullptr, line.mFirstFrameOnLine, line.mNumFramesOnLine);
    324    }
    325  }
    326  return frame;
    327 }
    328 
    329 nsIFrame* nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const {
    330  if (!mFirstChild) {
    331    return nullptr;
    332  }
    333 
    334  nsIFrame* parent = mFirstChild->GetParent();
    335  if (!parent) {
    336    return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
    337  }
    338 
    339  mozilla::intl::BidiDirection paraDir =
    340      nsBidiPresUtils::ParagraphDirection(mFirstChild);
    341 
    342  AutoAssertNoDomMutations guard;
    343  nsILineIterator* iter = parent->GetLineIterator();
    344  if (!iter) {
    345    // Parent is not a block Frame
    346    if (parent->IsLineFrame()) {
    347      // Line frames are not bidi-splittable, so need to consider bidi
    348      // reordering
    349      if (paraDir == mozilla::intl::BidiDirection::LTR) {
    350        return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
    351      } else {  // RTL
    352        return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
    353      }
    354    } else {
    355      // Just get the next or prev sibling, depending on block and frame
    356      // direction.
    357      if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
    358        return aFrame ? aFrame->GetNextSibling() : mFirstChild;
    359      } else {
    360        return aFrame ? aFrame->GetPrevSibling() : LastChild();
    361      }
    362    }
    363  }
    364 
    365  // Parent is a block frame, so use the LineIterator to find the next visual
    366  // sibling on this line, or the first one on the next line.
    367 
    368  int32_t thisLine;
    369  if (aFrame) {
    370    thisLine = iter->FindLineContaining(aFrame);
    371    if (thisLine < 0) {
    372      return nullptr;
    373    }
    374  } else {
    375    thisLine = -1;
    376  }
    377 
    378  nsIFrame* frame = nullptr;
    379 
    380  if (aFrame) {
    381    auto line = iter->GetLine(thisLine).unwrap();
    382 
    383    if (paraDir == mozilla::intl::BidiDirection::LTR) {
    384      frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, line.mFirstFrameOnLine,
    385                                                 line.mNumFramesOnLine);
    386    } else {  // RTL
    387      frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, line.mFirstFrameOnLine,
    388                                                line.mNumFramesOnLine);
    389    }
    390  }
    391 
    392  int32_t numLines = iter->GetNumLines();
    393  if (!frame && thisLine < numLines - 1) {
    394    // Get the first frame of the next line
    395    auto line = iter->GetLine(thisLine + 1).unwrap();
    396 
    397    if (paraDir == mozilla::intl::BidiDirection::LTR) {
    398      frame = nsBidiPresUtils::GetFrameToRightOf(
    399          nullptr, line.mFirstFrameOnLine, line.mNumFramesOnLine);
    400    } else {  // RTL
    401      frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, line.mFirstFrameOnLine,
    402                                                line.mNumFramesOnLine);
    403    }
    404  }
    405  return frame;
    406 }
    407 
    408 #ifdef DEBUG_FRAME_LIST
    409 void nsFrameList::VerifyList() const {
    410  NS_ASSERTION((mFirstChild == nullptr) == (mLastChild == nullptr),
    411               "bad list state");
    412 
    413  if (IsEmpty()) {
    414    return;
    415  }
    416 
    417  // Simple algorithm to find a loop in a linked list -- advance pointers
    418  // through it at speeds of 1 and 2, and if they ever get to be equal bail
    419  NS_ASSERTION(!mFirstChild->GetPrevSibling(), "bad prev sibling pointer");
    420  nsIFrame *first = mFirstChild, *second = mFirstChild;
    421  for (;;) {
    422    first = first->GetNextSibling();
    423    second = second->GetNextSibling();
    424    if (!second) {
    425      break;
    426    }
    427    NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
    428                 "bad prev sibling pointer");
    429    second = second->GetNextSibling();
    430    if (first == second) {
    431      // Loop detected!  Since second advances faster, they can't both be null;
    432      // we would have broken out of the loop long ago.
    433      NS_ERROR("loop in frame list.  This will probably hang soon.");
    434      return;
    435    }
    436    if (!second) {
    437      break;
    438    }
    439    NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
    440                 "bad prev sibling pointer");
    441  }
    442 
    443  NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
    444               "bogus mLastChild");
    445  // XXX we should also assert that all GetParent() are either null or
    446  // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
    447  // prevents that, e.g. table captions.
    448 }
    449 #endif
    450 
    451 namespace mozilla {
    452 
    453 #ifdef DEBUG_FRAME_DUMP
    454 const char* ChildListName(FrameChildListID aListID) {
    455  switch (aListID) {
    456    case FrameChildListID::Principal:
    457      return "";
    458    case FrameChildListID::ColGroup:
    459      return "ColGroupList";
    460    case FrameChildListID::Absolute:
    461      return "AbsoluteList";
    462    case FrameChildListID::PushedAbsolute:
    463      return "PushedAbsoluteList";
    464    case FrameChildListID::Fixed:
    465      return "FixedList";
    466    case FrameChildListID::Overflow:
    467      return "OverflowList";
    468    case FrameChildListID::OverflowContainers:
    469      return "OverflowContainersList";
    470    case FrameChildListID::ExcessOverflowContainers:
    471      return "ExcessOverflowContainersList";
    472    case FrameChildListID::OverflowOutOfFlow:
    473      return "OverflowOutOfFlowList";
    474    case FrameChildListID::Float:
    475      return "FloatList";
    476    case FrameChildListID::Marker:
    477      return "MarkerList";
    478    case FrameChildListID::PushedFloats:
    479      return "PushedFloatsList";
    480    case FrameChildListID::NoReflowPrincipal:
    481      return "NoReflowPrincipalList";
    482  }
    483 
    484  MOZ_ASSERT_UNREACHABLE("unknown list");
    485  return "UNKNOWN_FRAME_CHILD_LIST";
    486 }
    487 #endif
    488 
    489 AutoFrameListPtr::~AutoFrameListPtr() {
    490  if (mFrameList) {
    491    mFrameList->Delete(mPresContext->PresShell());
    492  }
    493 }
    494 
    495 }  // namespace mozilla