[GTK] Crash in WebCore::SelectionRangeData::apply
[WebKit-https.git] / Source / WebCore / rendering / SelectionRangeData.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above
10  *    copyright notice, this list of conditions and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above
13  *    copyright notice, this list of conditions and the following
14  *    disclaimer in the documentation and/or other materials
15  *    provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "SelectionRangeData.h"
33
34 #include "Document.h"
35 #include "FrameSelection.h"
36 #include "Position.h"
37 #include "Range.h"
38 #include "RenderLayer.h"
39 #include "RenderMultiColumnSpannerPlaceholder.h"
40 #include "RenderObject.h"
41 #include "RenderView.h"
42 #include "VisibleSelection.h"
43
44 namespace WebCore {
45
46 namespace { // See bug #177808.
47
48 struct SelectionData {
49     using RendererMap = HashMap<RenderObject*, std::unique_ptr<RenderSelectionInfo>>;
50     using RenderBlockMap = HashMap<const RenderBlock*, std::unique_ptr<RenderBlockSelectionInfo>>;
51
52     std::optional<unsigned> startPosition;
53     std::optional<unsigned> endPosition;
54     RendererMap renderers;
55     RenderBlockMap blocks;
56 };
57
58 class SelectionIterator {
59 public:
60     SelectionIterator(RenderObject* start)
61         : m_current(start)
62     {
63         checkForSpanner();
64     }
65     
66     RenderObject* current() const
67     {
68         return m_current;
69     }
70     
71     RenderObject* next()
72     {
73         RenderObject* currentSpan = m_spannerStack.isEmpty() ? nullptr : m_spannerStack.last()->spanner();
74         m_current = m_current->nextInPreOrder(currentSpan);
75         checkForSpanner();
76         if (!m_current && currentSpan) {
77             RenderObject* placeholder = m_spannerStack.last();
78             m_spannerStack.removeLast();
79             m_current = placeholder->nextInPreOrder();
80             checkForSpanner();
81         }
82         return m_current;
83     }
84
85 private:
86     void checkForSpanner()
87     {
88         if (!is<RenderMultiColumnSpannerPlaceholder>(m_current))
89             return;
90         auto& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*m_current);
91         m_spannerStack.append(&placeholder);
92         m_current = placeholder.spanner();
93     }
94
95     RenderObject* m_current { nullptr };
96     Vector<RenderMultiColumnSpannerPlaceholder*> m_spannerStack;
97 };
98
99 } // anonymous namespace
100
101 static RenderObject* rendererAfterPosition(const RenderObject& renderer, unsigned offset)
102 {
103     auto* child = renderer.childAt(offset);
104     return child ? child : renderer.nextInPreOrderAfterChildren();
105 }
106
107 static bool isValidRendererForSelection(const RenderObject& renderer, const SelectionRangeData::Context& selection)
108 {
109     return (renderer.canBeSelectionLeaf() || &renderer == selection.start() || &renderer == selection.end())
110         && renderer.selectionState() != RenderObject::SelectionNone
111         && renderer.containingBlock();
112 }
113
114 static RenderBlock* containingBlockBelowView(const RenderObject& renderer)
115 {
116     auto* containingBlock = renderer.containingBlock();
117     return is<RenderView>(containingBlock) ? nullptr : containingBlock;
118 }
119
120 static SelectionData collect(const SelectionRangeData::Context& selection, bool repaintDifference)
121 {
122     SelectionData oldSelectionData { selection.startPosition(), selection.endPosition(), { }, { } };
123     // Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks.
124     // In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise
125     // the union of those rects might remain the same even when changes have occurred.
126     auto* start = selection.start();
127     RenderObject* stop = nullptr;
128     if (selection.end())
129         stop = rendererAfterPosition(*selection.end(), selection.endPosition().value());
130     SelectionIterator selectionIterator(start);
131     while (start && start != stop) {
132         if (isValidRendererForSelection(*start, selection)) {
133             // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
134             oldSelectionData.renderers.set(start, std::make_unique<RenderSelectionInfo>(*start, true));
135             if (repaintDifference) {
136                 for (auto* block = containingBlockBelowView(*start); block; block = containingBlockBelowView(*block)) {
137                     auto& blockInfo = oldSelectionData.blocks.add(block, nullptr).iterator->value;
138                     if (blockInfo)
139                         break;
140                     blockInfo = std::make_unique<RenderBlockSelectionInfo>(*block);
141                 }
142             }
143         }
144         start = selectionIterator.next();
145     }
146     return oldSelectionData;
147 }
148
149 SelectionRangeData::SelectionRangeData(RenderView& view)
150     : m_renderView(view)
151 #if ENABLE(SERVICE_CONTROLS)
152     , m_selectionRectGatherer(view)
153 #endif
154 {
155 }
156
157 void SelectionRangeData::set(const Context& selection, RepaintMode blockRepaintMode)
158 {
159     // Make sure both our start and end objects are defined.
160     // Check www.msnbc.com and try clicking around to find the case where this happened.
161     if ((selection.start() && !selection.end()) || (selection.end() && !selection.start()))
162         return;
163     // Just return if the selection hasn't changed.
164     auto isCaret = m_renderView.frame().selection().isCaret();
165     if (selection == m_selectionContext && m_selectionWasCaret == isCaret)
166         return;
167 #if ENABLE(SERVICE_CONTROLS)
168     // Clear the current rects and create a notifier for the new rects we are about to gather.
169     // The Notifier updates the Editor when it goes out of scope and is destroyed.
170     auto rectNotifier = m_selectionRectGatherer.clearAndCreateNotifier();
171 #endif
172     m_selectionWasCaret = isCaret;
173     apply(selection, blockRepaintMode);
174 }
175
176 void SelectionRangeData::clear()
177 {
178     m_renderView.layer()->repaintBlockSelectionGaps();
179     set({ }, SelectionRangeData::RepaintMode::NewMinusOld);
180 }
181
182 void SelectionRangeData::repaint() const
183 {
184     HashSet<RenderBlock*> processedBlocks;
185     RenderObject* end = nullptr;
186     if (m_selectionContext.end())
187         end = rendererAfterPosition(*m_selectionContext.end(), m_selectionContext.endPosition().value());
188     SelectionIterator selectionIterator(m_selectionContext.start());
189     for (auto* renderer = selectionIterator.current(); renderer && renderer != end; renderer = selectionIterator.next()) {
190         if (!renderer->canBeSelectionLeaf() && renderer != m_selectionContext.start() && renderer != m_selectionContext.end())
191             continue;
192         if (renderer->selectionState() == RenderObject::SelectionNone)
193             continue;
194         RenderSelectionInfo(*renderer, true).repaint();
195         // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
196         for (auto* block = containingBlockBelowView(*renderer); block; block = containingBlockBelowView(*block)) {
197             if (!processedBlocks.add(block).isNewEntry)
198                 break;
199             RenderSelectionInfo(*block, true).repaint();
200         }
201     }
202 }
203
204 IntRect SelectionRangeData::collectBounds(ClipToVisibleContent clipToVisibleContent) const
205 {
206     SelectionData::RendererMap renderers;
207     auto* start = m_selectionContext.start();
208     RenderObject* stop = nullptr;
209     if (m_selectionContext.end())
210         stop = rendererAfterPosition(*m_selectionContext.end(), m_selectionContext.endPosition().value());
211     SelectionIterator selectionIterator(start);
212     while (start && start != stop) {
213         if ((start->canBeSelectionLeaf() || start == m_selectionContext.start() || start == m_selectionContext.end())
214             && start->selectionState() != RenderObject::SelectionNone) {
215             // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
216             renderers.set(start, std::make_unique<RenderSelectionInfo>(*start, clipToVisibleContent == ClipToVisibleContent::Yes));
217             auto* block = start->containingBlock();
218             while (block && !is<RenderView>(*block)) {
219                 std::unique_ptr<RenderSelectionInfo>& blockInfo = renderers.add(block, nullptr).iterator->value;
220                 if (blockInfo)
221                     break;
222                 blockInfo = std::make_unique<RenderSelectionInfo>(*block, clipToVisibleContent == ClipToVisibleContent::Yes);
223                 block = block->containingBlock();
224             }
225         }
226         start = selectionIterator.next();
227     }
228
229     // Now create a single bounding box rect that encloses the whole selection.
230     LayoutRect selectionRect;
231     for (auto& info : renderers.values()) {
232         // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates.
233         LayoutRect currentRect = info->rect();
234         if (auto* repaintContainer = info->repaintContainer()) {
235             FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currentRect));
236             currentRect = absQuad.enclosingBoundingBox();
237         }
238         selectionRect.unite(currentRect);
239     }
240     return snappedIntRect(selectionRect);
241 }
242
243 void SelectionRangeData::apply(const Context& newSelection, RepaintMode blockRepaintMode)
244 {
245     auto oldSelectionData = collect(m_selectionContext, blockRepaintMode == RepaintMode::NewXOROld);
246     // Remove current selection.
247     for (auto* renderer : oldSelectionData.renderers.keys())
248         renderer->setSelectionStateIfNeeded(RenderObject::SelectionNone);
249     m_selectionContext = newSelection;
250     auto* selectionStart = m_selectionContext.start();
251     // Update the selection status of all objects between selectionStart and selectionEnd
252     if (selectionStart && selectionStart == m_selectionContext.end())
253         selectionStart->setSelectionStateIfNeeded(RenderObject::SelectionBoth);
254     else {
255         if (selectionStart)
256             selectionStart->setSelectionStateIfNeeded(RenderObject::SelectionStart);
257         if (auto* end = m_selectionContext.end())
258             end->setSelectionStateIfNeeded(RenderObject::SelectionEnd);
259     }
260
261     RenderObject* selectionEnd = nullptr;
262     auto* selectionDataEnd = m_selectionContext.end();
263     if (selectionDataEnd)
264         selectionEnd = rendererAfterPosition(*selectionDataEnd, m_selectionContext.endPosition().value());
265     SelectionIterator selectionIterator(selectionStart);
266     for (auto* currentRenderer = selectionStart; currentRenderer && currentRenderer != selectionEnd; currentRenderer = selectionIterator.next()) {
267         if (currentRenderer == selectionStart || currentRenderer == m_selectionContext.end())
268             continue;
269         if (!currentRenderer->canBeSelectionLeaf())
270             continue;
271         currentRenderer->setSelectionStateIfNeeded(RenderObject::SelectionInside);
272     }
273
274     if (blockRepaintMode != RepaintMode::Nothing)
275         m_renderView.layer()->clearBlockSelectionGapsBounds();
276
277     // Now that the selection state has been updated for the new objects, walk them again and
278     // put them in the new objects list.
279     SelectionData::RendererMap newSelectedRenderers;
280     SelectionData::RenderBlockMap newSelectedBlocks;
281     selectionIterator = SelectionIterator(selectionStart);
282     for (auto* currentRenderer = selectionStart; currentRenderer && currentRenderer != selectionEnd; currentRenderer = selectionIterator.next()) {
283         if (isValidRendererForSelection(*currentRenderer, m_selectionContext)) {
284             std::unique_ptr<RenderSelectionInfo> selectionInfo = std::make_unique<RenderSelectionInfo>(*currentRenderer, true);
285 #if ENABLE(SERVICE_CONTROLS)
286             for (auto& rect : selectionInfo->collectedSelectionRects())
287                 m_selectionRectGatherer.addRect(selectionInfo->repaintContainer(), rect);
288             if (!currentRenderer->isTextOrLineBreak())
289                 m_selectionRectGatherer.setTextOnly(false);
290 #endif
291             newSelectedRenderers.set(currentRenderer, WTFMove(selectionInfo));
292             auto* containingBlock = currentRenderer->containingBlock();
293             while (containingBlock && !is<RenderView>(*containingBlock)) {
294                 std::unique_ptr<RenderBlockSelectionInfo>& blockInfo = newSelectedBlocks.add(containingBlock, nullptr).iterator->value;
295                 if (blockInfo)
296                     break;
297                 blockInfo = std::make_unique<RenderBlockSelectionInfo>(*containingBlock);
298                 containingBlock = containingBlock->containingBlock();
299 #if ENABLE(SERVICE_CONTROLS)
300                 m_selectionRectGatherer.addGapRects(blockInfo->repaintContainer(), blockInfo->rects());
301 #endif
302             }
303         }
304     }
305
306     if (blockRepaintMode == RepaintMode::Nothing)
307         return;
308
309     // Have any of the old selected objects changed compared to the new selection?
310     for (auto& selectedRendererInfo : oldSelectionData.renderers) {
311         auto* renderer = selectedRendererInfo.key;
312         auto* newInfo = newSelectedRenderers.get(renderer);
313         auto* oldInfo = selectedRendererInfo.value.get();
314         if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state()
315             || (m_selectionContext.start() == renderer && oldSelectionData.startPosition != m_selectionContext.startPosition())
316             || (m_selectionContext.end() == renderer && oldSelectionData.endPosition != m_selectionContext.endPosition())) {
317             oldInfo->repaint();
318             if (newInfo) {
319                 newInfo->repaint();
320                 newSelectedRenderers.remove(renderer);
321             }
322         }
323     }
324
325     // Any new objects that remain were not found in the old objects dict, and so they need to be updated.
326     for (auto& selectedRendererInfo : newSelectedRenderers)
327         selectedRendererInfo.value->repaint();
328
329     // Have any of the old blocks changed?
330     for (auto& selectedBlockInfo : oldSelectionData.blocks) {
331         auto* block = selectedBlockInfo.key;
332         auto* newInfo = newSelectedBlocks.get(block);
333         auto* oldInfo = selectedBlockInfo.value.get();
334         if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) {
335             oldInfo->repaint();
336             if (newInfo) {
337                 newInfo->repaint();
338                 newSelectedBlocks.remove(block);
339             }
340         }
341     }
342
343     // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated.
344     for (auto& selectedBlockInfo : newSelectedBlocks)
345         selectedBlockInfo.value->repaint();
346 }
347
348 } // namespace WebCore