Diff
Modified: trunk/Source/WebCore/ChangeLog (228319 => 228320)
--- trunk/Source/WebCore/ChangeLog 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/ChangeLog 2018-02-09 15:05:15 UTC (rev 228320)
@@ -1,3 +1,37 @@
+2018-02-09 Zalan Bujtas <za...@apple.com>
+
+ [RenderTreeBuilder] Move multicolumn spanner mutation logic to RenderTreeBuilder
+ https://bugs.webkit.org/show_bug.cgi?id=182627
+ <rdar://problem/37367284>
+
+ Reviewed by Antti Koivisto.
+
+ Move spanner triggered mutation logic to RenderTreeBuilder.
+
+ No change in functionality.
+
+ * rendering/RenderFragmentedFlow.h:
+ * rendering/RenderMultiColumnFlow.cpp:
+ (WebCore::RenderMultiColumnFlow::isColumnSpanningDescendant const):
+ (WebCore::findSetRendering): Deleted.
+ (WebCore::isValidColumnSpanner): Deleted.
+ (WebCore::spannerPlacehoderCandidate): Deleted.
+ (WebCore::RenderMultiColumnFlow::processPossibleSpannerDescendant): Deleted.
+ (WebCore::RenderMultiColumnFlow::fragmentedFlowDescendantInserted): Deleted.
+ * rendering/RenderMultiColumnFlow.h:
+ * rendering/RenderObject.cpp:
+ (WebCore::RenderObject::insertedIntoTree):
+ * rendering/updating/RenderTreeBuilder.cpp:
+ (WebCore::RenderTreeBuilder::multiColumnDescendantInserted):
+ * rendering/updating/RenderTreeBuilder.h:
+ * rendering/updating/RenderTreeBuilderMultiColumn.cpp:
+ (WebCore::findSetRendering):
+ (WebCore::spannerPlacehoderCandidate):
+ (WebCore::isValidColumnSpanner):
+ (WebCore::RenderTreeBuilder::MultiColumn::multiColumnDescendantInserted):
+ (WebCore::RenderTreeBuilder::MultiColumn::processPossibleSpannerDescendant):
+ * rendering/updating/RenderTreeBuilderMultiColumn.h:
+
2018-02-09 Javier Fernandez <jfernan...@igalia.com>
[css-align] Implement the new behavior of 'legacy' for justify-items
Modified: trunk/Source/WebCore/rendering/RenderFragmentedFlow.h (228319 => 228320)
--- trunk/Source/WebCore/rendering/RenderFragmentedFlow.h 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/RenderFragmentedFlow.h 2018-02-09 15:05:15 UTC (rev 228320)
@@ -86,8 +86,6 @@
virtual bool singleFragmentHasUniformLogicalHeight() const { return true; }
- // Called when a descendant of the flow thread has been inserted.
- virtual void fragmentedFlowDescendantInserted(RenderObject&) { }
// Called when a sibling or descendant of the flow thread is about to be removed.
virtual void fragmentedFlowRelativeWillBeRemoved(RenderObject&) { }
// Called when a descendant box's layout is finished and it has been positioned within its container.
Modified: trunk/Source/WebCore/rendering/RenderMultiColumnFlow.cpp (228319 => 228320)
--- trunk/Source/WebCore/rendering/RenderMultiColumnFlow.cpp 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/RenderMultiColumnFlow.cpp 2018-02-09 15:05:15 UTC (rev 228320)
@@ -40,8 +40,6 @@
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMultiColumnFlow);
-bool RenderMultiColumnFlow::gShiftingSpanner = false;
-
RenderMultiColumnFlow::RenderMultiColumnFlow(Document& document, RenderStyle&& style)
: RenderFragmentedFlow(document, WTFMove(style))
, m_spannerMap(std::make_unique<SpannerMap>())
@@ -135,14 +133,9 @@
m_lastSetWorkedOn = nullptr;
}
-static RenderMultiColumnSet* findSetRendering(const RenderMultiColumnFlow& fragmentedFlow, const RenderObject& renderer)
+bool RenderMultiColumnFlow::isColumnSpanningDescendant(const RenderBox& descendantBox) const
{
- // Find the set inside which the specified renderer would be rendered.
- for (auto* multicolSet = fragmentedFlow.firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
- if (multicolSet->containsRendererInFragmentedFlow(renderer))
- return multicolSet;
- }
- return nullptr;
+ return descendantBox.style().columnSpan() == ColumnSpanAll;
}
void RenderMultiColumnFlow::addFragmentToThread(RenderFragmentContainer* RenderFragmentContainer)
@@ -167,198 +160,11 @@
RenderFragmentedFlow::willBeRemovedFromTree();
}
-bool RenderMultiColumnFlow::isColumnSpanningDescendant(const RenderBox& descendantBox) const
-{
- return descendantBox.style().columnSpan() == ColumnSpanAll;
-}
-
-static bool isValidColumnSpanner(const RenderMultiColumnFlow& fragmentedFlow, const RenderObject& descendant)
-{
- // We assume that we're inside the flow thread. This function is not to be called otherwise.
- ASSERT(descendant.isDescendantOf(&fragmentedFlow));
- // First make sure that the renderer itself has the right properties for becoming a spanner.
- if (!is<RenderBox>(descendant))
- return false;
-
- auto& descendantBox = downcast<RenderBox>(descendant);
- if (descendantBox.isFloatingOrOutOfFlowPositioned())
- return false;
-
- if (!fragmentedFlow.isColumnSpanningDescendant(descendantBox))
- return false;
-
- auto* parent = descendantBox.parent();
- if (!is<RenderBlockFlow>(*parent) || parent->childrenInline()) {
- // Needs to be block-level.
- return false;
- }
-
- // We need to have the flow thread as the containing block. A spanner cannot break out of the flow thread.
- auto* enclosingFragmentedFlow = descendantBox.enclosingFragmentedFlow();
- if (enclosingFragmentedFlow != &fragmentedFlow)
- return false;
-
- // This looks like a spanner, but if we're inside something unbreakable, it's not to be treated as one.
- for (auto* ancestor = descendantBox.containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
- if (is<RenderView>(*ancestor))
- return false;
- if (is<RenderFragmentedFlow>(*ancestor)) {
- // Don't allow any intervening non-multicol fragmentation contexts. The spec doesn't say
- // anything about disallowing this, but it's just going to be too complicated to
- // implement (not to mention specify behavior).
- return ancestor == &fragmentedFlow;
- }
- // This ancestor (descendent of the fragmentedFlow) will create columns later. The spanner belongs to it.
- if (is<RenderBlockFlow>(*ancestor) && downcast<RenderBlockFlow>(*ancestor).willCreateColumns())
- return false;
- ASSERT(ancestor->style().columnSpan() != ColumnSpanAll || !isValidColumnSpanner(fragmentedFlow, *ancestor));
- if (ancestor->isUnsplittableForPagination())
- return false;
- }
- ASSERT_NOT_REACHED();
- return false;
-}
-
-static RenderObject* spannerPlacehoderCandidate(const RenderObject& renderer, const RenderMultiColumnFlow& stayWithin)
-{
- // Spanner candidate is a next sibling/ancestor's next child within the flow thread and
- // it is in the same inflow/out-of-flow layout context.
- if (renderer.isOutOfFlowPositioned())
- return nullptr;
-
- ASSERT(renderer.isDescendantOf(&stayWithin));
- auto* current = &renderer;
- while (true) {
- // Skip to the first in-flow sibling.
- auto* nextSibling = current->nextSibling();
- while (nextSibling && nextSibling->isOutOfFlowPositioned())
- nextSibling = nextSibling->nextSibling();
- if (nextSibling)
- return nextSibling;
- // No sibling candidate, jump to the parent and check its siblings.
- current = current->parent();
- if (!current || current == &stayWithin || current->isOutOfFlowPositioned())
- return nullptr;
- }
- return nullptr;
-}
-
-RenderObject* RenderMultiColumnFlow::processPossibleSpannerDescendant(RenderObject*& subtreeRoot, RenderObject& descendant)
-{
- RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
- RenderObject* nextRendererInFragmentedFlow = spannerPlacehoderCandidate(descendant, *this);
- RenderObject* insertBeforeMulticolChild = nullptr;
- RenderObject* nextDescendant = &descendant;
-
- if (isValidColumnSpanner(*this, descendant)) {
- // This is a spanner (column-span:all). Such renderers are moved from where they would
- // otherwise occur in the render tree to becoming a direct child of the multicol container,
- // so that they live among the column sets. This simplifies the layout implementation, and
- // basically just relies on regular block layout done by the RenderBlockFlow that
- // establishes the multicol container.
- RenderBlockFlow* container = downcast<RenderBlockFlow>(descendant.parent());
- RenderMultiColumnSet* setToSplit = nullptr;
- if (nextRendererInFragmentedFlow) {
- setToSplit = findSetRendering(*this, descendant);
- if (setToSplit) {
- setToSplit->setNeedsLayout();
- insertBeforeMulticolChild = setToSplit->nextSibling();
- }
- }
- // Moving a spanner's renderer so that it becomes a sibling of the column sets requires us
- // to insert an anonymous placeholder in the tree where the spanner's renderer otherwise
- // would have been. This is needed for a two reasons: We need a way of separating inline
- // content before and after the spanner, so that it becomes separate line boxes. Secondly,
- // this placeholder serves as a break point for column sets, so that, when encountered, we
- // end flowing one column set and move to the next one.
- auto newPlaceholder = RenderMultiColumnSpannerPlaceholder::createAnonymous(*this, downcast<RenderBox>(descendant), container->style());
- auto& placeholder = *newPlaceholder;
- RenderTreeBuilder::current()->insertChild(*container, WTFMove(newPlaceholder), descendant.nextSibling());
- auto takenDescendant = container->takeChild(*RenderTreeBuilder::current(), descendant);
-
- // This is a guard to stop an ancestor flow thread from processing the spanner.
- gShiftingSpanner = true;
- RenderTreeBuilder::current()->insertChildToRenderBlock(*multicolContainer, WTFMove(takenDescendant), insertBeforeMulticolChild);
- gShiftingSpanner = false;
-
- // The spanner has now been moved out from the flow thread, but we don't want to
- // examine its children anyway. They are all part of the spanner and shouldn't trigger
- // creation of column sets or anything like that. Continue at its original position in
- // the tree, i.e. where the placeholder was just put.
- if (subtreeRoot == &descendant)
- subtreeRoot = &placeholder;
- nextDescendant = &placeholder;
- } else {
- // This is regular multicol content, i.e. not part of a spanner.
- if (is<RenderMultiColumnSpannerPlaceholder>(nextRendererInFragmentedFlow)) {
- // Inserted right before a spanner. Is there a set for us there?
- RenderMultiColumnSpannerPlaceholder& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*nextRendererInFragmentedFlow);
- if (RenderObject* previous = placeholder.spanner()->previousSibling()) {
- if (is<RenderMultiColumnSet>(*previous))
- return nextDescendant; // There's already a set there. Nothing to do.
- }
- insertBeforeMulticolChild = placeholder.spanner();
- } else if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) {
- // This child is not an immediate predecessor of a spanner, which means that if this
- // child precedes a spanner at all, there has to be a column set created for us there
- // already. If it doesn't precede any spanner at all, on the other hand, we need a
- // column set at the end of the multicol container. We don't really check here if the
- // child inserted precedes any spanner or not (as that's an expensive operation). Just
- // make sure we have a column set at the end. It's no big deal if it remains unused.
- if (!lastSet->nextSibling())
- return nextDescendant;
- }
- }
- // Need to create a new column set when there's no set already created. We also always insert
- // another column set after a spanner. Even if it turns out that there are no renderers
- // following the spanner, there may be bottom margins there, which take up space.
- auto newSet = createMultiColumnSet(RenderStyle::createAnonymousStyleWithDisplay(multicolContainer->style(), BLOCK));
- newSet->initializeStyle();
- auto& set = *newSet;
- RenderTreeBuilder::current()->insertChildToRenderBlock(*multicolContainer, WTFMove(newSet), insertBeforeMulticolChild);
- invalidateFragments();
-
- // We cannot handle immediate column set siblings at the moment (and there's no need for
- // it, either). There has to be at least one spanner separating them.
- ASSERT_UNUSED(set, !previousColumnSetOrSpannerSiblingOf(&set) || !previousColumnSetOrSpannerSiblingOf(&set)->isRenderMultiColumnSet());
- ASSERT(!nextColumnSetOrSpannerSiblingOf(&set) || !nextColumnSetOrSpannerSiblingOf(&set)->isRenderMultiColumnSet());
-
- return nextDescendant;
-}
-
RenderPtr<RenderMultiColumnSet> RenderMultiColumnFlow::createMultiColumnSet(RenderStyle&& style)
{
return createRenderer<RenderMultiColumnSet>(*this, WTFMove(style));
}
-void RenderMultiColumnFlow::fragmentedFlowDescendantInserted(RenderObject& newDescendant)
-{
- if (gShiftingSpanner || newDescendant.isInFlowRenderFragmentedFlow())
- return;
-
- auto* subtreeRoot = &newDescendant;
- auto* descendant = subtreeRoot;
- while (descendant) {
- // Skip nested multicolumn flows.
- if (is<RenderMultiColumnFlow>(*descendant)) {
- descendant = descendant->nextSibling();
- continue;
- }
- if (is<RenderMultiColumnSpannerPlaceholder>(*descendant)) {
- // A spanner's placeholder has been inserted. The actual spanner renderer is moved from
- // where it would otherwise occur (if it weren't a spanner) to becoming a sibling of the
- // column sets.
- RenderMultiColumnSpannerPlaceholder& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*descendant);
- ASSERT(!spannerMap().get(placeholder.spanner()));
- spannerMap().add(placeholder.spanner(), makeWeakPtr(downcast<RenderMultiColumnSpannerPlaceholder>(descendant)));
- ASSERT(!placeholder.firstChild()); // There should be no children here, but if there are, we ought to skip them.
- } else
- descendant = processPossibleSpannerDescendant(subtreeRoot, *descendant);
- if (descendant)
- descendant = descendant->nextInPreOrder(subtreeRoot);
- }
-}
-
void RenderMultiColumnFlow::handleSpannerRemoval(RenderObject& spanner)
{
// The placeholder may already have been removed, but if it hasn't, do so now.
Modified: trunk/Source/WebCore/rendering/RenderMultiColumnFlow.h (228319 => 228320)
--- trunk/Source/WebCore/rendering/RenderMultiColumnFlow.h 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/RenderMultiColumnFlow.h 2018-02-09 15:05:15 UTC (rev 228320)
@@ -100,7 +100,6 @@
virtual bool isColumnSpanningDescendant(const RenderBox&) const;
-protected:
virtual RenderPtr<RenderMultiColumnSet> createMultiColumnSet(RenderStyle&&);
private:
@@ -108,7 +107,6 @@
const char* renderName() const override;
void addFragmentToThread(RenderFragmentContainer*) override;
void willBeRemovedFromTree() override;
- void fragmentedFlowDescendantInserted(RenderObject&) override;
void fragmentedFlowRelativeWillBeRemoved(RenderObject&) override;
void fragmentedFlowDescendantBoxLaidOut(RenderBox*) override;
LogicalExtentComputedValues computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const override;
@@ -121,7 +119,6 @@
bool isPageLogicalHeightKnown() const override;
void handleSpannerRemoval(RenderObject& spanner);
- RenderObject* processPossibleSpannerDescendant(RenderObject*& subtreeRoot, RenderObject& descendant);
private:
std::unique_ptr<SpannerMap> m_spannerMap;
@@ -141,8 +138,6 @@
bool m_progressionIsInline;
bool m_progressionIsReversed;
-
- static bool gShiftingSpanner;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/rendering/RenderObject.cpp (228319 => 228320)
--- trunk/Source/WebCore/rendering/RenderObject.cpp 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/RenderObject.cpp 2018-02-09 15:05:15 UTC (rev 228320)
@@ -1459,8 +1459,9 @@
if (!isFloating() && parent()->childrenInline())
parent()->dirtyLinesFromChangedChild(*this);
- if (RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow())
- fragmentedFlow->fragmentedFlowDescendantInserted(*this);
+ auto* fragmentedFlow = enclosingFragmentedFlow();
+ if (is<RenderMultiColumnFlow>(fragmentedFlow))
+ RenderTreeBuilder::current()->multiColumnDescendantInserted(downcast<RenderMultiColumnFlow>(*fragmentedFlow), *this);
}
void RenderObject::willBeRemovedFromTree()
Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.cpp (228319 => 228320)
--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.cpp 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.cpp 2018-02-09 15:05:15 UTC (rev 228320)
@@ -352,6 +352,11 @@
removeAnonymousWrappersForInlineChildrenIfNeeded(*child.parent());
}
+void RenderTreeBuilder::multiColumnDescendantInserted(RenderMultiColumnFlow& flow, RenderObject& newDescendant)
+{
+ multiColumnBuilder().multiColumnDescendantInserted(flow, newDescendant);
+}
+
static bool isAnonymousAndSafeToDelete(RenderElement& element)
{
if (!element.isAnonymous())
Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.h (228319 => 228320)
--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.h 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilder.h 2018-02-09 15:05:15 UTC (rev 228320)
@@ -79,6 +79,7 @@
void childFlowStateChangesAndNoLongerAffectsParentBlock(RenderElement& child);
RenderObject* resolveMovedChildForMultiColumnFlow(RenderFragmentedFlow& enclosingFragmentedFlow, RenderObject* beforeChild);
void removeFromParentAndDestroyCleaningUpAnonymousWrappers(RenderObject& child);
+ void multiColumnDescendantInserted(RenderMultiColumnFlow&, RenderObject& newDescendant);
private:
class FirstLetter;
Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp (228319 => 228320)
--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp 2018-02-09 15:05:15 UTC (rev 228320)
@@ -34,6 +34,87 @@
namespace WebCore {
+static RenderMultiColumnSet* findSetRendering(const RenderMultiColumnFlow& fragmentedFlow, const RenderObject& renderer)
+{
+ // Find the set inside which the specified renderer would be rendered.
+ for (auto* multicolSet = fragmentedFlow.firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
+ if (multicolSet->containsRendererInFragmentedFlow(renderer))
+ return multicolSet;
+ }
+ return nullptr;
+}
+
+static RenderObject* spannerPlacehoderCandidate(const RenderObject& renderer, const RenderMultiColumnFlow& stayWithin)
+{
+ // Spanner candidate is a next sibling/ancestor's next child within the flow thread and
+ // it is in the same inflow/out-of-flow layout context.
+ if (renderer.isOutOfFlowPositioned())
+ return nullptr;
+
+ ASSERT(renderer.isDescendantOf(&stayWithin));
+ auto* current = &renderer;
+ while (true) {
+ // Skip to the first in-flow sibling.
+ auto* nextSibling = current->nextSibling();
+ while (nextSibling && nextSibling->isOutOfFlowPositioned())
+ nextSibling = nextSibling->nextSibling();
+ if (nextSibling)
+ return nextSibling;
+ // No sibling candidate, jump to the parent and check its siblings.
+ current = current->parent();
+ if (!current || current == &stayWithin || current->isOutOfFlowPositioned())
+ return nullptr;
+ }
+ return nullptr;
+}
+
+static bool isValidColumnSpanner(const RenderMultiColumnFlow& fragmentedFlow, const RenderObject& descendant)
+{
+ // We assume that we're inside the flow thread. This function is not to be called otherwise.
+ ASSERT(descendant.isDescendantOf(&fragmentedFlow));
+ // First make sure that the renderer itself has the right properties for becoming a spanner.
+ if (!is<RenderBox>(descendant))
+ return false;
+
+ auto& descendantBox = downcast<RenderBox>(descendant);
+ if (descendantBox.isFloatingOrOutOfFlowPositioned())
+ return false;
+
+ if (!fragmentedFlow.isColumnSpanningDescendant(descendantBox))
+ return false;
+
+ auto* parent = descendantBox.parent();
+ if (!is<RenderBlockFlow>(*parent) || parent->childrenInline()) {
+ // Needs to be block-level.
+ return false;
+ }
+
+ // We need to have the flow thread as the containing block. A spanner cannot break out of the flow thread.
+ auto* enclosingFragmentedFlow = descendantBox.enclosingFragmentedFlow();
+ if (enclosingFragmentedFlow != &fragmentedFlow)
+ return false;
+
+ // This looks like a spanner, but if we're inside something unbreakable, it's not to be treated as one.
+ for (auto* ancestor = descendantBox.containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
+ if (is<RenderView>(*ancestor))
+ return false;
+ if (is<RenderFragmentedFlow>(*ancestor)) {
+ // Don't allow any intervening non-multicol fragmentation contexts. The spec doesn't say
+ // anything about disallowing this, but it's just going to be too complicated to
+ // implement (not to mention specify behavior).
+ return ancestor == &fragmentedFlow;
+ }
+ // This ancestor (descendent of the fragmentedFlow) will create columns later. The spanner belongs to it.
+ if (is<RenderBlockFlow>(*ancestor) && downcast<RenderBlockFlow>(*ancestor).willCreateColumns())
+ return false;
+ ASSERT(ancestor->style().columnSpan() != ColumnSpanAll || !isValidColumnSpanner(fragmentedFlow, *ancestor));
+ if (ancestor->isUnsplittableForPagination())
+ return false;
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
RenderTreeBuilder::MultiColumn::MultiColumn(RenderTreeBuilder& builder)
: m_builder(builder)
{
@@ -171,4 +252,119 @@
return beforeChild;
}
+static bool gShiftingSpanner = false;
+
+void RenderTreeBuilder::MultiColumn::multiColumnDescendantInserted(RenderMultiColumnFlow& flow, RenderObject& newDescendant)
+{
+ if (gShiftingSpanner || newDescendant.isInFlowRenderFragmentedFlow())
+ return;
+
+ auto* subtreeRoot = &newDescendant;
+ auto* descendant = subtreeRoot;
+ while (descendant) {
+ // Skip nested multicolumn flows.
+ if (is<RenderMultiColumnFlow>(*descendant)) {
+ descendant = descendant->nextSibling();
+ continue;
+ }
+ if (is<RenderMultiColumnSpannerPlaceholder>(*descendant)) {
+ // A spanner's placeholder has been inserted. The actual spanner renderer is moved from
+ // where it would otherwise occur (if it weren't a spanner) to becoming a sibling of the
+ // column sets.
+ RenderMultiColumnSpannerPlaceholder& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*descendant);
+ ASSERT(!flow.spannerMap().get(placeholder.spanner()));
+ flow.spannerMap().add(placeholder.spanner(), makeWeakPtr(downcast<RenderMultiColumnSpannerPlaceholder>(descendant)));
+ ASSERT(!placeholder.firstChild()); // There should be no children here, but if there are, we ought to skip them.
+ } else
+ descendant = processPossibleSpannerDescendant(flow, subtreeRoot, *descendant);
+ if (descendant)
+ descendant = descendant->nextInPreOrder(subtreeRoot);
+ }
}
+
+RenderObject* RenderTreeBuilder::MultiColumn::processPossibleSpannerDescendant(RenderMultiColumnFlow& flow, RenderObject*& subtreeRoot, RenderObject& descendant)
+{
+ RenderBlockFlow* multicolContainer = flow.multiColumnBlockFlow();
+ RenderObject* nextRendererInFragmentedFlow = spannerPlacehoderCandidate(descendant, flow);
+ RenderObject* insertBeforeMulticolChild = nullptr;
+ RenderObject* nextDescendant = &descendant;
+
+ if (isValidColumnSpanner(flow, descendant)) {
+ // This is a spanner (column-span:all). Such renderers are moved from where they would
+ // otherwise occur in the render tree to becoming a direct child of the multicol container,
+ // so that they live among the column sets. This simplifies the layout implementation, and
+ // basically just relies on regular block layout done by the RenderBlockFlow that
+ // establishes the multicol container.
+ RenderBlockFlow* container = downcast<RenderBlockFlow>(descendant.parent());
+ RenderMultiColumnSet* setToSplit = nullptr;
+ if (nextRendererInFragmentedFlow) {
+ setToSplit = findSetRendering(flow, descendant);
+ if (setToSplit) {
+ setToSplit->setNeedsLayout();
+ insertBeforeMulticolChild = setToSplit->nextSibling();
+ }
+ }
+ // Moving a spanner's renderer so that it becomes a sibling of the column sets requires us
+ // to insert an anonymous placeholder in the tree where the spanner's renderer otherwise
+ // would have been. This is needed for a two reasons: We need a way of separating inline
+ // content before and after the spanner, so that it becomes separate line boxes. Secondly,
+ // this placeholder serves as a break point for column sets, so that, when encountered, we
+ // end flowing one column set and move to the next one.
+ auto newPlaceholder = RenderMultiColumnSpannerPlaceholder::createAnonymous(flow, downcast<RenderBox>(descendant), container->style());
+ auto& placeholder = *newPlaceholder;
+ m_builder.insertChild(*container, WTFMove(newPlaceholder), descendant.nextSibling());
+ auto takenDescendant = container->takeChild(m_builder, descendant);
+
+ // This is a guard to stop an ancestor flow thread from processing the spanner.
+ gShiftingSpanner = true;
+ m_builder.insertChildToRenderBlock(*multicolContainer, WTFMove(takenDescendant), insertBeforeMulticolChild);
+ gShiftingSpanner = false;
+
+ // The spanner has now been moved out from the flow thread, but we don't want to
+ // examine its children anyway. They are all part of the spanner and shouldn't trigger
+ // creation of column sets or anything like that. Continue at its original position in
+ // the tree, i.e. where the placeholder was just put.
+ if (subtreeRoot == &descendant)
+ subtreeRoot = &placeholder;
+ nextDescendant = &placeholder;
+ } else {
+ // This is regular multicol content, i.e. not part of a spanner.
+ if (is<RenderMultiColumnSpannerPlaceholder>(nextRendererInFragmentedFlow)) {
+ // Inserted right before a spanner. Is there a set for us there?
+ RenderMultiColumnSpannerPlaceholder& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*nextRendererInFragmentedFlow);
+ if (RenderObject* previous = placeholder.spanner()->previousSibling()) {
+ if (is<RenderMultiColumnSet>(*previous))
+ return nextDescendant; // There's already a set there. Nothing to do.
+ }
+ insertBeforeMulticolChild = placeholder.spanner();
+ } else if (RenderMultiColumnSet* lastSet = flow.lastMultiColumnSet()) {
+ // This child is not an immediate predecessor of a spanner, which means that if this
+ // child precedes a spanner at all, there has to be a column set created for us there
+ // already. If it doesn't precede any spanner at all, on the other hand, we need a
+ // column set at the end of the multicol container. We don't really check here if the
+ // child inserted precedes any spanner or not (as that's an expensive operation). Just
+ // make sure we have a column set at the end. It's no big deal if it remains unused.
+ if (!lastSet->nextSibling())
+ return nextDescendant;
+ }
+ }
+ // Need to create a new column set when there's no set already created. We also always insert
+ // another column set after a spanner. Even if it turns out that there are no renderers
+ // following the spanner, there may be bottom margins there, which take up space.
+ auto newSet = flow.createMultiColumnSet(RenderStyle::createAnonymousStyleWithDisplay(multicolContainer->style(), BLOCK));
+ newSet->initializeStyle();
+ auto& set = *newSet;
+ m_builder.insertChildToRenderBlock(*multicolContainer, WTFMove(newSet), insertBeforeMulticolChild);
+ flow.invalidateFragments();
+
+ // We cannot handle immediate column set siblings at the moment (and there's no need for
+ // it, either). There has to be at least one spanner separating them.
+ ASSERT_UNUSED(set, !RenderMultiColumnFlow::previousColumnSetOrSpannerSiblingOf(&set)
+ || !RenderMultiColumnFlow::previousColumnSetOrSpannerSiblingOf(&set)->isRenderMultiColumnSet());
+ ASSERT(!RenderMultiColumnFlow::nextColumnSetOrSpannerSiblingOf(&set)
+ || !RenderMultiColumnFlow::nextColumnSetOrSpannerSiblingOf(&set)->isRenderMultiColumnSet());
+
+ return nextDescendant;
+}
+
+}
Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.h (228319 => 228320)
--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.h 2018-02-09 14:57:12 UTC (rev 228319)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.h 2018-02-09 15:05:15 UTC (rev 228320)
@@ -40,10 +40,12 @@
// sets. If |child| is such a renderer, resolve it to the placeholder that lives at the original
// location in the tree.
RenderObject* resolveMovedChild(RenderFragmentedFlow& enclosingFragmentedFlow, RenderObject* beforeChild);
+ void multiColumnDescendantInserted(RenderMultiColumnFlow&, RenderObject& newDescendant);
private:
void createFragmentedFlow(RenderBlockFlow&);
void destroyFragmentedFlow(RenderBlockFlow&);
+ RenderObject* processPossibleSpannerDescendant(RenderMultiColumnFlow&, RenderObject*& subtreeRoot, RenderObject& descendant);
RenderTreeBuilder& m_builder;
};