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
44namespace WebCore {
45
46namespace { // See bug #177808.
47
48struct SelectionData {
49 using RendererMap = HashMap<RenderObject*, std::unique_ptr<RenderSelectionInfo>>;
50 using RenderBlockMap = HashMap<const RenderBlock*, std::unique_ptr<RenderBlockSelectionInfo>>;
51
52 Optional<unsigned> startPosition;
53 Optional<unsigned> endPosition;
54 RendererMap renderers;
55 RenderBlockMap blocks;
56};
57
58class SelectionIterator {
59public:
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
85private:
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
101static RenderObject* rendererAfterPosition(const RenderObject& renderer, unsigned offset)
102{
103 auto* child = renderer.childAt(offset);
104 return child ? child : renderer.nextInPreOrderAfterChildren();
105}
106
107static 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
114static RenderBlock* containingBlockBelowView(const RenderObject& renderer)
115{
116 auto* containingBlock = renderer.containingBlock();
117 return is<RenderView>(containingBlock) ? nullptr : containingBlock;
118}
119
120static 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
149SelectionRangeData::SelectionRangeData(RenderView& view)
150 : m_renderView(view)
151#if ENABLE(SERVICE_CONTROLS)
152 , m_selectionRectGatherer(view)
153#endif
154{
155}
156
157void 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
176void SelectionRangeData::clear()
177{
178 m_renderView.layer()->repaintBlockSelectionGaps();
179 set({ }, SelectionRangeData::RepaintMode::NewMinusOld);
180}
181
182void 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
204IntRect 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
243void 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
349