2e1f7124b26d2002e0b9ca278e6de9cd302bb0e4
[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
33 namespace WebCore {
34
35 void RenderTreeUpdater::MultiColumn::update(RenderBlockFlow& flow)
36 {
37     bool needsFragmentedFlow = flow.requiresColumns(flow.style().columnCount());
38     bool hasFragmentedFlow = flow.multiColumnFlow();
39
40     if (!hasFragmentedFlow && needsFragmentedFlow) {
41         createFragmentedFlow(flow);
42         return;
43     }
44     if (hasFragmentedFlow && !needsFragmentedFlow) {
45         destroyFragmentedFlow(flow);
46         return;
47     }
48 }
49
50 void RenderTreeUpdater::MultiColumn::createFragmentedFlow(RenderBlockFlow& flow)
51 {
52     flow.setChildrenInline(false); // Do this to avoid wrapping inline children that are just going to move into the flow thread.
53     flow.deleteLines();
54     // If this soon-to-be multicolumn flow is already part of a multicolumn context, we need to move back the descendant spanners
55     // to their original position before moving subtrees around.
56     auto* enclosingflow = flow.enclosingFragmentedFlow();
57     if (is<RenderMultiColumnFlow>(enclosingflow)) {
58         auto& spanners = downcast<RenderMultiColumnFlow>(enclosingflow)->spannerMap();
59         Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
60         for (auto& spannerAndPlaceholder : spanners) {
61             auto& placeholder = *spannerAndPlaceholder.value;
62             if (!placeholder.isDescendantOf(&flow))
63                 continue;
64             placeholdersToDelete.append(&placeholder);
65         }
66         for (auto* placeholder : placeholdersToDelete) {
67             auto* spanner = placeholder->spanner();
68             if (!spanner) {
69                 ASSERT_NOT_REACHED();
70                 continue;
71             }
72             // Move the spanner back to its original position.
73             auto& spannerOriginalParent = *placeholder->parent();
74             // Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
75             auto spannerToReInsert = spanner->parent()->takeChild(*spanner);
76             spannerOriginalParent.addChild(WTFMove(spannerToReInsert));
77         }
78     }
79
80     auto newFragmentedFlow = WebCore::createRenderer<RenderMultiColumnFlow>(flow.document(), RenderStyle::createAnonymousStyleWithDisplay(flow.style(), BLOCK));
81     newFragmentedFlow->initializeStyle();
82     auto& fragmentedFlow = *newFragmentedFlow;
83     flow.RenderBlock::addChild(WTFMove(newFragmentedFlow));
84
85     // Reparent children preceding the fragmented flow into the fragmented flow.
86     flow.moveChildrenTo(&fragmentedFlow, flow.firstChild(), &fragmentedFlow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
87     if (flow.isFieldset()) {
88         // Keep legends out of the flow thread.
89         for (auto& box : childrenOfType<RenderBox>(fragmentedFlow)) {
90             if (box.isLegend())
91                 fragmentedFlow.moveChildTo(&flow, &box, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
92         }
93     }
94
95     flow.setMultiColumnFlow(fragmentedFlow);
96 }
97
98 void RenderTreeUpdater::MultiColumn::destroyFragmentedFlow(RenderBlockFlow& flow)
99 {
100     auto& multiColumnFlow = *flow.multiColumnFlow();
101     multiColumnFlow.deleteLines();
102
103     // Move spanners back to their original DOM position in the tree, and destroy the placeholders.
104     auto& spanners = multiColumnFlow.spannerMap();
105     Vector<RenderMultiColumnSpannerPlaceholder*> placeholdersToDelete;
106     for (auto& spannerAndPlaceholder : spanners)
107         placeholdersToDelete.append(spannerAndPlaceholder.value.get());
108     Vector<std::pair<RenderElement*, RenderPtr<RenderObject>>> parentAndSpannerList;
109     for (auto* placeholder : placeholdersToDelete) {
110         auto* spannerOriginalParent = placeholder->parent();
111         if (spannerOriginalParent == &multiColumnFlow)
112             spannerOriginalParent = &flow;
113         // Detaching the spanner takes care of removing the placeholder (and merges the RenderMultiColumnSets).
114         auto* spanner = placeholder->spanner();
115         parentAndSpannerList.append(std::make_pair(spannerOriginalParent, spanner->parent()->takeChild(*spanner)));
116     }
117     while (auto* columnSet = multiColumnFlow.firstMultiColumnSet())
118         columnSet->removeFromParentAndDestroy();
119
120     flow.clearMultiColumnFlow();
121     multiColumnFlow.moveAllChildrenTo(&flow, RenderBoxModelObject::NormalizeAfterInsertion::Yes);
122     multiColumnFlow.removeFromParentAndDestroy();
123     for (auto& parentAndSpanner : parentAndSpannerList)
124         parentAndSpanner.first->addChild(WTFMove(parentAndSpanner.second));
125 }
126
127 }