blob: 1e4fc819e2992b007907d15453786af1bedf5c3f [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2007 David Smith (catfish.man@gmail.com)
* Copyright (C) 2003-2015, 2017 Apple Inc. All rights reserved.
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "RenderTreeBuilderMultiColumn.h"
#include "RenderBlockFlow.h"
#include "RenderChildIterator.h"
#include "RenderLinesClampFlow.h"
#include "RenderMultiColumnFlow.h"
#include "RenderMultiColumnSet.h"
#include "RenderMultiColumnSpannerPlaceholder.h"
#include "RenderTreeBuilder.h"
namespace WebCore {
RenderTreeBuilder::MultiColumn::MultiColumn(RenderTreeBuilder& builder)
: m_builder(builder)
{
}
void RenderTreeBuilder::MultiColumn::updateAfterDescendants(RenderBlockFlow& flow)
{
bool needsFragmentedFlow = flow.requiresColumns(flow.style().columnCount());
bool hasFragmentedFlow = flow.multiColumnFlow();
if (!hasFragmentedFlow && needsFragmentedFlow) {
createFragmentedFlow(flow);
return;
}
if (hasFragmentedFlow && !needsFragmentedFlow) {
destroyFragmentedFlow(flow);
return;
}
}
void RenderTreeBuilder::MultiColumn::createFragmentedFlow(RenderBlockFlow& flow)
{
flow.setChildrenInline(false); // Do this to avoid wrapping inline children that are just going to move into the flow thread.
flow.deleteLines();
// If this soon-to-be multicolumn flow is already part of a multicolumn context, we need to move back the descendant spanners
// to their original position before moving subtrees around.
auto* enclosingflow = flow.enclosingFragmentedFlow();
if (is<RenderMultiColumnFlow>(enclosingflow)) {
auto& spanners = downcast<RenderMultiColumnFlow>(enclosingflow)->spannerMap();
Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
for (auto& spannerAndPlaceholder : spanners) {
auto& placeholder = *spannerAndPlaceholder.value;
if (!placeholder.isDescendantOf(&flow))
continue;
placeholdersToDelete.append(&placeholder);
}
for (auto* placeholder : placeholdersToDelete) {
auto* spanner = placeholder->spanner();
if (!spanner) {
ASSERT_NOT_REACHED();
continue;
}
// Move the spanner back to its original position.
auto& spannerOriginalParent = *placeholder->parent();
// Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
auto spannerToReInsert = spanner->parent()->takeChild(*spanner);
m_builder.insertChild(spannerOriginalParent, WTFMove(spannerToReInsert));
}
}
auto newFragmentedFlow = !flow.style().hasLinesClamp() ? WebCore::createRenderer<RenderMultiColumnFlow>(flow.document(), RenderStyle::createAnonymousStyleWithDisplay(flow.style(), BLOCK)) : WebCore::createRenderer<RenderLinesClampFlow>(flow.document(), RenderStyle::createAnonymousStyleWithDisplay(flow.style(), BLOCK));
newFragmentedFlow->initializeStyle();
auto& fragmentedFlow = *newFragmentedFlow;
m_builder.insertChildToRenderBlock(flow, WTFMove(newFragmentedFlow));
// Reparent children preceding the fragmented flow into the fragmented flow.
flow.moveChildrenTo(&fragmentedFlow, flow.firstChild(), &fragmentedFlow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
if (flow.isFieldset()) {
// Keep legends out of the flow thread.
for (auto& box : childrenOfType<RenderBox>(fragmentedFlow)) {
if (box.isLegend())
fragmentedFlow.moveChildTo(&flow, &box, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
}
}
if (flow.style().hasLinesClamp()) {
// Keep the middle block out of the flow thread.
for (auto& element : childrenOfType<RenderElement>(fragmentedFlow)) {
if (!downcast<RenderLinesClampFlow>(fragmentedFlow).isChildAllowedInFragmentedFlow(flow, element))
fragmentedFlow.moveChildTo(&flow, &element, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
}
}
flow.setMultiColumnFlow(fragmentedFlow);
}
void RenderTreeBuilder::MultiColumn::destroyFragmentedFlow(RenderBlockFlow& flow)
{
auto& multiColumnFlow = *flow.multiColumnFlow();
multiColumnFlow.deleteLines();
// Move spanners back to their original DOM position in the tree, and destroy the placeholders.
auto& spanners = multiColumnFlow.spannerMap();
Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
for (auto& spannerAndPlaceholder : spanners)
placeholdersToDelete.append(spannerAndPlaceholder.value.get());
Vector<std::pair<RenderElement*, RenderPtr<RenderObject>>> parentAndSpannerList;
for (auto* placeholder : placeholdersToDelete) {
auto* spannerOriginalParent = placeholder->parent();
if (spannerOriginalParent == &multiColumnFlow)
spannerOriginalParent = &flow;
// Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
auto* spanner = placeholder->spanner();
parentAndSpannerList.append(std::make_pair(spannerOriginalParent, spanner->parent()->takeChild(*spanner)));
}
while (auto* columnSet = multiColumnFlow.firstMultiColumnSet())
columnSet->removeFromParentAndDestroy();
flow.clearMultiColumnFlow();
multiColumnFlow.moveAllChildrenTo(&flow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
multiColumnFlow.removeFromParentAndDestroy();
for (auto& parentAndSpanner : parentAndSpannerList)
m_builder.insertChild(*parentAndSpanner.first, WTFMove(parentAndSpanner.second));
}
}