tor-browser

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

ColumnSetWrapperFrame.cpp (12460B)


      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ColumnSetWrapperFrame.h"
      8 
      9 #include "mozilla/ColumnUtils.h"
     10 #include "mozilla/PresShell.h"
     11 #include "nsContentUtils.h"
     12 #include "nsIFrame.h"
     13 #include "nsIFrameInlines.h"
     14 
     15 using namespace mozilla;
     16 
     17 nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell,
     18                                          ComputedStyle* aStyle,
     19                                          nsFrameState aStateFlags) {
     20  ColumnSetWrapperFrame* frame = new (aPresShell)
     21      ColumnSetWrapperFrame(aStyle, aPresShell->GetPresContext());
     22  frame->AddStateBits(aStateFlags);
     23  return frame;
     24 }
     25 
     26 NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame)
     27 
     28 NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)
     29  NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame)
     30 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
     31 
     32 ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle,
     33                                             nsPresContext* aPresContext)
     34    : nsBlockFrame(aStyle, aPresContext, kClassID) {}
     35 
     36 void ColumnSetWrapperFrame::Init(nsIContent* aContent,
     37                                 nsContainerFrame* aParent,
     38                                 nsIFrame* aPrevInFlow) {
     39  nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
     40 
     41  // ColumnSetWrapperFrame doesn't need to call ResolveBidi().
     42  RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
     43 }
     44 
     45 nsContainerFrame* ColumnSetWrapperFrame::GetContentInsertionFrame() {
     46  nsIFrame* columnSet = PrincipalChildList().OnlyChild();
     47  if (columnSet) {
     48    // We have only one child, which means we don't have any column-span
     49    // descendants. Thus we can safely return our only ColumnSet child's
     50    // insertion frame as ours.
     51    MOZ_ASSERT(columnSet->IsColumnSetFrame());
     52    return columnSet->GetContentInsertionFrame();
     53  }
     54 
     55  // We have column-span descendants. Return ourselves as the insertion
     56  // frame to let nsCSSFrameConstructor::WipeContainingBlock() figure out
     57  // what to do.
     58  return this;
     59 }
     60 
     61 void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(
     62    nsTArray<OwnedAnonBox>& aResult) {
     63  MOZ_ASSERT(!GetPrevContinuation(),
     64             "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?");
     65 
     66  // It's sufficient to append the first ColumnSet child, which is the first
     67  // continuation of all the other ColumnSets.
     68  //
     69  // We don't need to append -moz-column-span-wrapper children because
     70  // they're non-inheriting anon boxes, and they cannot have any directly
     71  // owned anon boxes nor generate any native anonymous content themselves.
     72  // Thus, no need to restyle them. AssertColumnSpanWrapperSubtreeIsSane()
     73  // asserts all the conditions above which allow us to skip appending
     74  // -moz-column-span-wrappers.
     75  auto FindFirstChildInChildLists = [this]() -> nsIFrame* {
     76    const ChildListID listIDs[] = {FrameChildListID::Principal,
     77                                   FrameChildListID::Overflow};
     78    for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) {
     79      for (ChildListID id : listIDs) {
     80        const nsFrameList& list = frag->GetChildList(id);
     81        if (nsIFrame* firstChild = list.FirstChild()) {
     82          return firstChild;
     83        }
     84      }
     85    }
     86    return nullptr;
     87  };
     88 
     89  nsIFrame* columnSet = FindFirstChildInChildLists();
     90  MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(),
     91             "The first child should always be ColumnSet!");
     92  aResult.AppendElement(OwnedAnonBox(columnSet));
     93 }
     94 
     95 #ifdef DEBUG_FRAME_DUMP
     96 nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const {
     97  return MakeFrameName(u"ColumnSetWrapper"_ns, aResult);
     98 }
     99 #endif
    100 
    101 // Disallow any append, insert, or remove operations after building the
    102 // column hierarchy since any change to the column hierarchy in the column
    103 // sub-tree need to be re-created.
    104 void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID,
    105                                         nsFrameList&& aFrameList) {
    106 #ifdef DEBUG
    107  MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!");
    108  mFinishedBuildingColumns = true;
    109 #endif
    110 
    111  nsBlockFrame::AppendFrames(aListID, std::move(aFrameList));
    112 
    113 #ifdef DEBUG
    114  nsIFrame* firstColumnSet = PrincipalChildList().FirstChild();
    115  for (nsIFrame* child : PrincipalChildList()) {
    116    if (child->IsColumnSpan()) {
    117      AssertColumnSpanWrapperSubtreeIsSane(child);
    118    } else if (child != firstColumnSet) {
    119      // All the other ColumnSets are the continuation of the first ColumnSet.
    120      MOZ_ASSERT(child->IsColumnSetFrame() && child->GetPrevContinuation(),
    121                 "ColumnSet's prev-continuation is not set properly?");
    122    }
    123  }
    124 #endif
    125 }
    126 
    127 void ColumnSetWrapperFrame::InsertFrames(
    128    ChildListID aListID, nsIFrame* aPrevFrame,
    129    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
    130  MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
    131  nsBlockFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
    132                             std::move(aFrameList));
    133 }
    134 
    135 void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext,
    136                                        ChildListID aListID,
    137                                        nsIFrame* aOldFrame) {
    138  MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
    139  nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame);
    140 }
    141 
    142 void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
    143  nsBlockFrame::MarkIntrinsicISizesDirty();
    144 
    145  // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
    146  // continuations. Clear the bit because we don't want to call ResolveBidi().
    147  for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) {
    148    f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
    149  }
    150 }
    151 
    152 nscoord ColumnSetWrapperFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    153                                              IntrinsicISizeType aType) {
    154  return mCachedIntrinsics.GetOrSet(*this, aType, aInput, [&] {
    155    return aType == IntrinsicISizeType::MinISize ? MinISize(aInput)
    156                                                 : PrefISize(aInput);
    157  });
    158 }
    159 
    160 nscoord ColumnSetWrapperFrame::MinISize(const IntrinsicSizeInput& aInput) {
    161  nscoord iSize = 0;
    162 
    163  if (Maybe<nscoord> containISize =
    164          ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
    165    // If we're size-contained in inline axis and contain-intrinsic-inline-size
    166    // is not 'none', then use that size.
    167    if (*containISize != NS_UNCONSTRAINEDSIZE) {
    168      return *containISize;
    169    }
    170 
    171    // In the 'none' case, we determine our minimum intrinsic size purely from
    172    // our column styling, as if we had no descendants. This should match what
    173    // happens in nsColumnSetFrame::MinISize in an actual no-descendants
    174    // scenario.
    175    const nsStyleColumn* colStyle = StyleColumn();
    176    if (colStyle->mColumnWidth.IsLength()) {
    177      // As available inline size reduces to zero, our number of columns reduces
    178      // to one, so no column gaps contribute to our minimum intrinsic size.
    179      // Also, column-width doesn't set a lower bound on our minimum intrinsic
    180      // size, either. Just use 0 because we're size-contained.
    181      iSize = 0;
    182    } else {
    183      MOZ_ASSERT(!colStyle->mColumnCount.IsAuto(),
    184                 "column-count and column-width can't both be auto!");
    185      // As available inline size reduces to zero, we still have mColumnCount
    186      // columns, so compute our minimum intrinsic size based on N zero-width
    187      // columns, with specified gap size between them.
    188      const nscoord colGap =
    189          ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
    190      iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount.AsInteger(),
    191                                          colGap, 0);
    192    }
    193  } else {
    194    for (nsIFrame* f : PrincipalChildList()) {
    195      const IntrinsicSizeInput childInput(aInput, f->GetWritingMode(),
    196                                          GetWritingMode());
    197      iSize = std::max(iSize, f->GetMinISize(childInput));
    198    }
    199  }
    200 
    201  return iSize;
    202 }
    203 
    204 nscoord ColumnSetWrapperFrame::PrefISize(const IntrinsicSizeInput& aInput) {
    205  nscoord iSize = 0;
    206 
    207  if (Maybe<nscoord> containISize =
    208          ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
    209    if (*containISize != NS_UNCONSTRAINEDSIZE) {
    210      return *containISize;
    211    }
    212 
    213    const nsStyleColumn* colStyle = StyleColumn();
    214    nscoord colISize;
    215    if (colStyle->mColumnWidth.IsLength()) {
    216      colISize =
    217          ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
    218    } else {
    219      MOZ_ASSERT(!colStyle->mColumnCount.IsAuto(),
    220                 "column-count and column-width can't both be auto!");
    221      colISize = 0;
    222    }
    223 
    224    // If column-count is auto, assume one column.
    225    const uint32_t numColumns = colStyle->mColumnCount.IsAuto()
    226                                    ? 1
    227                                    : colStyle->mColumnCount.AsInteger();
    228    const nscoord colGap =
    229        ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
    230    iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
    231  } else {
    232    for (nsIFrame* f : PrincipalChildList()) {
    233      const IntrinsicSizeInput childInput(aInput, f->GetWritingMode(),
    234                                          GetWritingMode());
    235      iSize = std::max(iSize, f->GetPrefISize(childInput));
    236    }
    237  }
    238 
    239  return iSize;
    240 }
    241 
    242 template <typename Iterator>
    243 Maybe<nscoord> ColumnSetWrapperFrame::GetBaselineBOffset(
    244    Iterator aStart, Iterator aEnd, WritingMode aWM,
    245    BaselineSharingGroup aBaselineGroup,
    246    BaselineExportContext aExportContext) const {
    247  // Either forward iterator + first baseline, or reverse iterator + last
    248  // baseline
    249  MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() &&
    250              aBaselineGroup == BaselineSharingGroup::First) ||
    251                 (*aStart == PrincipalChildList().LastChild() &&
    252                  aBaselineGroup == BaselineSharingGroup::Last),
    253             "Iterator direction must match baseline sharing group.");
    254  // Start from start/end of principal child list, and use the first valid
    255  // baseline.
    256  for (auto itr = aStart; itr != aEnd; ++itr) {
    257    const nsIFrame* kid = *itr;
    258    auto kidBaseline =
    259        kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext);
    260    if (!kidBaseline) {
    261      continue;
    262    }
    263    // Baseline is offset from the kid's rectangle, so find the offset to the
    264    // kid's rectangle.
    265    LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
    266                        kid->GetLogicalSize(aWM)};
    267    if (aBaselineGroup == BaselineSharingGroup::First) {
    268      *kidBaseline += kidRect.BStart(aWM);
    269    } else {
    270      *kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
    271    }
    272    return kidBaseline;
    273  }
    274  return Nothing{};
    275 }
    276 
    277 Maybe<nscoord> ColumnSetWrapperFrame::GetNaturalBaselineBOffset(
    278    WritingMode aWM, BaselineSharingGroup aBaselineGroup,
    279    BaselineExportContext aExportContext) const {
    280  if (StyleDisplay()->IsContainLayout()) {
    281    return Nothing{};
    282  }
    283  if (aBaselineGroup == BaselineSharingGroup::First) {
    284    return GetBaselineBOffset(PrincipalChildList().cbegin(),
    285                              PrincipalChildList().cend(), aWM, aBaselineGroup,
    286                              aExportContext);
    287  }
    288  return GetBaselineBOffset(PrincipalChildList().crbegin(),
    289                            PrincipalChildList().crend(), aWM, aBaselineGroup,
    290                            aExportContext);
    291 }
    292 
    293 #ifdef DEBUG
    294 
    295 /* static */
    296 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
    297    const nsIFrame* aFrame) {
    298  MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
    299 
    300  if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
    301           ->Style()
    302           ->IsAnonBox()) {
    303    // aFrame's style frame has "column-span: all". Traverse no further.
    304    return;
    305  }
    306 
    307  MOZ_ASSERT(
    308      aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper,
    309      "aFrame should be ::-moz-column-span-wrapper");
    310 
    311  MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES),
    312             "::-moz-column-span-wrapper anonymous blocks cannot own "
    313             "other types of anonymous blocks!");
    314 
    315  for (const nsIFrame* child : aFrame->PrincipalChildList()) {
    316    AssertColumnSpanWrapperSubtreeIsSane(child);
    317  }
    318 }
    319 
    320 #endif