summaryrefslogtreecommitdiffstats
path: root/layout/generic/ColumnSetWrapperFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic/ColumnSetWrapperFrame.cpp')
-rw-r--r--layout/generic/ColumnSetWrapperFrame.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/layout/generic/ColumnSetWrapperFrame.cpp b/layout/generic/ColumnSetWrapperFrame.cpp
new file mode 100644
index 0000000000..0f142ef4f3
--- /dev/null
+++ b/layout/generic/ColumnSetWrapperFrame.cpp
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "ColumnSetWrapperFrame.h"
+
+#include "mozilla/ColumnUtils.h"
+#include "mozilla/PresShell.h"
+#include "nsContentUtils.h"
+#include "nsIFrame.h"
+#include "nsIFrameInlines.h"
+
+using namespace mozilla;
+
+nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell,
+ ComputedStyle* aStyle,
+ nsFrameState aStateFlags) {
+ ColumnSetWrapperFrame* frame = new (aPresShell)
+ ColumnSetWrapperFrame(aStyle, aPresShell->GetPresContext());
+
+ // CSS Multi-column level 1 section 2: A multi-column container
+ // establishes a new block formatting context, as per CSS 2.1 section
+ // 9.4.1.
+ frame->AddStateBits(aStateFlags | NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
+ return frame;
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame)
+
+NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)
+ NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
+
+ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle,
+ nsPresContext* aPresContext)
+ : nsBlockFrame(aStyle, aPresContext, kClassID) {}
+
+void ColumnSetWrapperFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) {
+ nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
+
+ // ColumnSetWrapperFrame doesn't need to call ResolveBidi().
+ RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
+}
+
+nsContainerFrame* ColumnSetWrapperFrame::GetContentInsertionFrame() {
+ nsIFrame* columnSet = PrincipalChildList().OnlyChild();
+ if (columnSet) {
+ // We have only one child, which means we don't have any column-span
+ // descendants. Thus we can safely return our only ColumnSet child's
+ // insertion frame as ours.
+ MOZ_ASSERT(columnSet->IsColumnSetFrame());
+ return columnSet->GetContentInsertionFrame();
+ }
+
+ // We have column-span descendants. Return ourselves as the insertion
+ // frame to let nsCSSFrameConstructor::WipeContainingBlock() figure out
+ // what to do.
+ return this;
+}
+
+void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(
+ nsTArray<OwnedAnonBox>& aResult) {
+ MOZ_ASSERT(!GetPrevContinuation(),
+ "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?");
+
+ // It's sufficient to append the first ColumnSet child, which is the first
+ // continuation of all the other ColumnSets.
+ //
+ // We don't need to append -moz-column-span-wrapper children because
+ // they're non-inheriting anon boxes, and they cannot have any directly
+ // owned anon boxes nor generate any native anonymous content themselves.
+ // Thus, no need to restyle them. AssertColumnSpanWrapperSubtreeIsSane()
+ // asserts all the conditions above which allow us to skip appending
+ // -moz-column-span-wrappers.
+ auto FindFirstChildInChildLists = [this]() -> nsIFrame* {
+ const ChildListID listIDs[] = {FrameChildListID::Principal,
+ FrameChildListID::Overflow};
+ for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) {
+ for (ChildListID id : listIDs) {
+ const nsFrameList& list = frag->GetChildList(id);
+ if (nsIFrame* firstChild = list.FirstChild()) {
+ return firstChild;
+ }
+ }
+ }
+ return nullptr;
+ };
+
+ nsIFrame* columnSet = FindFirstChildInChildLists();
+ MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(),
+ "The first child should always be ColumnSet!");
+ aResult.AppendElement(OwnedAnonBox(columnSet));
+}
+
+#ifdef DEBUG_FRAME_DUMP
+nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const {
+ return MakeFrameName(u"ColumnSetWrapper"_ns, aResult);
+}
+#endif
+
+// Disallow any append, insert, or remove operations after building the
+// column hierarchy since any change to the column hierarchy in the column
+// sub-tree need to be re-created.
+void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID,
+ nsFrameList&& aFrameList) {
+#ifdef DEBUG
+ MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!");
+ mFinishedBuildingColumns = true;
+#endif
+
+ nsBlockFrame::AppendFrames(aListID, std::move(aFrameList));
+
+#ifdef DEBUG
+ nsIFrame* firstColumnSet = PrincipalChildList().FirstChild();
+ for (nsIFrame* child : PrincipalChildList()) {
+ if (child->IsColumnSpan()) {
+ AssertColumnSpanWrapperSubtreeIsSane(child);
+ } else if (child != firstColumnSet) {
+ // All the other ColumnSets are the continuation of the first ColumnSet.
+ MOZ_ASSERT(child->IsColumnSetFrame() && child->GetPrevContinuation(),
+ "ColumnSet's prev-continuation is not set properly?");
+ }
+ }
+#endif
+}
+
+void ColumnSetWrapperFrame::InsertFrames(
+ ChildListID aListID, nsIFrame* aPrevFrame,
+ const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
+ MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
+ nsBlockFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
+ std::move(aFrameList));
+}
+
+void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext,
+ ChildListID aListID,
+ nsIFrame* aOldFrame) {
+ MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
+ nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame);
+}
+
+void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
+ nsBlockFrame::MarkIntrinsicISizesDirty();
+
+ // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
+ // continuations. Clear the bit because we don't want to call ResolveBidi().
+ for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) {
+ f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
+ }
+}
+
+nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
+ nscoord iSize = 0;
+ DISPLAY_MIN_INLINE_SIZE(this, iSize);
+
+ if (Maybe<nscoord> containISize =
+ ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
+ // If we're size-contained in inline axis and contain-intrinsic-inline-size
+ // is not 'none', then use that size.
+ if (*containISize != NS_UNCONSTRAINEDSIZE) {
+ return *containISize;
+ }
+
+ // In the 'none' case, we determine our minimum intrinsic size purely from
+ // our column styling, as if we had no descendants. This should match what
+ // happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
+ // scenario.
+ const nsStyleColumn* colStyle = StyleColumn();
+ if (colStyle->mColumnWidth.IsLength()) {
+ // As available inline size reduces to zero, our number of columns reduces
+ // to one, so no column gaps contribute to our minimum intrinsic size.
+ // Also, column-width doesn't set a lower bound on our minimum intrinsic
+ // size, either. Just use 0 because we're size-contained.
+ iSize = 0;
+ } else {
+ MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
+ "column-count and column-width can't both be auto!");
+ // As available inline size reduces to zero, we still have mColumnCount
+ // columns, so compute our minimum intrinsic size based on N zero-width
+ // columns, with specified gap size between them.
+ const nscoord colGap =
+ ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+ iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, 0);
+ }
+ } else {
+ for (nsIFrame* f : PrincipalChildList()) {
+ iSize = std::max(iSize, f->GetMinISize(aRenderingContext));
+ }
+ }
+
+ return iSize;
+}
+
+nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
+ nscoord iSize = 0;
+ DISPLAY_PREF_INLINE_SIZE(this, iSize);
+
+ if (Maybe<nscoord> containISize =
+ ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
+ if (*containISize != NS_UNCONSTRAINEDSIZE) {
+ return *containISize;
+ }
+
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord colISize;
+ if (colStyle->mColumnWidth.IsLength()) {
+ colISize =
+ ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
+ } else {
+ MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
+ "column-count and column-width can't both be auto!");
+ colISize = 0;
+ }
+
+ // If column-count is auto, assume one column.
+ const uint32_t numColumns =
+ colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
+ ? 1
+ : colStyle->mColumnCount;
+ const nscoord colGap =
+ ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+ iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
+ } else {
+ for (nsIFrame* f : PrincipalChildList()) {
+ iSize = std::max(iSize, f->GetPrefISize(aRenderingContext));
+ }
+ }
+
+ return iSize;
+}
+
+template <typename Iterator>
+Maybe<nscoord> ColumnSetWrapperFrame::GetBaselineBOffset(
+ Iterator aStart, Iterator aEnd, WritingMode aWM,
+ BaselineSharingGroup aBaselineGroup,
+ BaselineExportContext aExportContext) const {
+ // Either forward iterator + first baseline, or reverse iterator + last
+ // baseline
+ MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() &&
+ aBaselineGroup == BaselineSharingGroup::First) ||
+ (*aStart == PrincipalChildList().LastChild() &&
+ aBaselineGroup == BaselineSharingGroup::Last),
+ "Iterator direction must match baseline sharing group.");
+ if (StyleDisplay()->IsContainLayout()) {
+ return Nothing{};
+ }
+
+ // Start from start/end of principal child list, and use the first valid
+ // baseline.
+ for (auto itr = aStart; itr != aEnd; ++itr) {
+ const nsIFrame* kid = *itr;
+ auto kidBaseline =
+ kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext);
+ if (!kidBaseline) {
+ continue;
+ }
+ // Baseline is offset from the kid's rectangle, so find the offset to the
+ // kid's rectangle.
+ LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
+ kid->GetLogicalSize(aWM)};
+ if (aBaselineGroup == BaselineSharingGroup::First) {
+ *kidBaseline += kidRect.BStart(aWM);
+ } else {
+ *kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
+ }
+ return kidBaseline;
+ }
+ return Nothing{};
+}
+
+Maybe<nscoord> ColumnSetWrapperFrame::GetNaturalBaselineBOffset(
+ WritingMode aWM, BaselineSharingGroup aBaselineGroup,
+ BaselineExportContext aExportContext) const {
+ if (aBaselineGroup == BaselineSharingGroup::First) {
+ return GetBaselineBOffset(PrincipalChildList().cbegin(),
+ PrincipalChildList().cend(), aWM, aBaselineGroup,
+ aExportContext);
+ }
+ return GetBaselineBOffset(PrincipalChildList().crbegin(),
+ PrincipalChildList().crend(), aWM, aBaselineGroup,
+ aExportContext);
+}
+
+#ifdef DEBUG
+
+/* static */
+void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
+ const nsIFrame* aFrame) {
+ MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
+
+ if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
+ ->Style()
+ ->IsAnonBox()) {
+ // aFrame's style frame has "column-span: all". Traverse no further.
+ return;
+ }
+
+ MOZ_ASSERT(
+ aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper,
+ "aFrame should be ::-moz-column-span-wrapper");
+
+ MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES),
+ "::-moz-column-span-wrapper anonymous blocks cannot own "
+ "other types of anonymous blocks!");
+
+ for (const nsIFrame* child : aFrame->PrincipalChildList()) {
+ AssertColumnSpanWrapperSubtreeIsSane(child);
+ }
+}
+
+#endif