Introduce RenderTreeBuilder
[WebKit-https.git] / Source / WebCore / style / RenderTreeUpdaterMultiColumn.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2007 David Smith (catfish.man@gmail.com)
5  * Copyright (C) 2003-2015, 2017 Apple Inc. All rights reserved.
6  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "RenderTreeUpdaterMultiColumn.h"
26
27 #include "RenderBlockFlow.h"
28 #include "RenderChildIterator.h"
29 #include "RenderMultiColumnFlow.h"
30 #include "RenderMultiColumnSet.h"
31 #include "RenderMultiColumnSpannerPlaceholder.h"
32 #include "RenderTreeBuilder.h"
33
34 namespace WebCore {
35
36 void RenderTreeUpdater::MultiColumn::update(RenderBlockFlow& flow)
37 {
38     bool needsFragmentedFlow = flow.requiresColumns(flow.style().columnCount());
39     bool hasFragmentedFlow = flow.multiColumnFlow();
40
41     if (!hasFragmentedFlow && needsFragmentedFlow) {
42         createFragmentedFlow(flow);
43         return;
44     }
45     if (hasFragmentedFlow && !needsFragmentedFlow) {
46         destroyFragmentedFlow(flow);
47         return;
48     }
49 }
50
51 void RenderTreeUpdater::MultiColumn::createFragmentedFlow(RenderBlockFlow& flow)
52 {
53     flow.setChildrenInline(false); // Do this to avoid wrapping inline children that are just going to move into the flow thread.
54     flow.deleteLines();
55     // If this soon-to-be multicolumn flow is already part of a multicolumn context, we need to move back the descendant spanners
56     // to their original position before moving subtrees around.
57     auto* enclosingflow = flow.enclosingFragmentedFlow();
58     if (is<RenderMultiColumnFlow>(enclosingflow)) {
59         auto& spanners = downcast<RenderMultiColumnFlow>(enclosingflow)->spannerMap();
60         Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
61         for (auto& spannerAndPlaceholder : spanners) {
62             auto& placeholder = *spannerAndPlaceholder.value;
63             if (!placeholder.isDescendantOf(&flow))
64                 continue;
65             placeholdersToDelete.append(&placeholder);
66         }
67         for (auto* placeholder : placeholdersToDelete) {
68             auto* spanner = placeholder->spanner();
69             if (!spanner) {
70                 ASSERT_NOT_REACHED();
71                 continue;
72             }
73             // Move the spanner back to its original position.
74             auto& spannerOriginalParent = *placeholder->parent();
75             // Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
76             auto spannerToReInsert = spanner->parent()->takeChild(*spanner);
77             RenderTreeBuilder::current()->insertChild(spannerOriginalParent, WTFMove(spannerToReInsert));
78         }
79     }
80
81     auto newFragmentedFlow = WebCore::createRenderer<RenderMultiColumnFlow>(flow.document(), RenderStyle::createAnonymousStyleWithDisplay(flow.style(), BLOCK));
82     newFragmentedFlow->initializeStyle();
83     auto& fragmentedFlow = *newFragmentedFlow;
84     flow.RenderBlock::addChild(*RenderTreeBuilder::current(), WTFMove(newFragmentedFlow));
85
86     // Reparent children preceding the fragmented flow into the fragmented flow.
87     flow.moveChildrenTo(&fragmentedFlow, flow.firstChild(), &fragmentedFlow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
88     if (flow.isFieldset()) {
89         // Keep legends out of the flow thread.
90         for (auto& box : childrenOfType<RenderBox>(fragmentedFlow)) {
91             if (box.isLegend())
92                 fragmentedFlow.moveChildTo(&flow, &box, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
93         }
94     }
95
96     flow.setMultiColumnFlow(fragmentedFlow);
97 }
98
99 void RenderTreeUpdater::MultiColumn::destroyFragmentedFlow(RenderBlockFlow& flow)
100 {
101     auto& multiColumnFlow = *flow.multiColumnFlow();
102     multiColumnFlow.deleteLines();
103
104     // Move spanners back to their original DOM position in the tree, and destroy the placeholders.
105     auto& spanners = multiColumnFlow.spannerMap();
106     Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
107     for (auto& spannerAndPlaceholder : spanners)
108         placeholdersToDelete.append(spannerAndPlaceholder.value.get());
109     Vector<std::pair<RenderElement*, RenderPtr<RenderObject>>> parentAndSpannerList;
110     for (auto* placeholder : placeholdersToDelete) {
111         auto* spannerOriginalParent = placeholder->parent();
112         if (spannerOriginalParent == &multiColumnFlow)
113             spannerOriginalParent = &flow;
114         // Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
115         auto* spanner = placeholder->spanner();
116         parentAndSpannerList.append(std::make_pair(spannerOriginalParent, spanner->parent()->takeChild(*spanner)));
117     }
118     while (auto* columnSet = multiColumnFlow.firstMultiColumnSet())
119         columnSet->removeFromParentAndDestroy();
120
121     flow.clearMultiColumnFlow();
122     multiColumnFlow.moveAllChildrenTo(&flow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
123     multiColumnFlow.removeFromParentAndDestroy();
124     for (auto& parentAndSpanner : parentAndSpannerList)
125         RenderTreeBuilder::current()->insertChild(*parentAndSpanner.first, WTFMove(parentAndSpanner.second));
126 }
127
128 }