SelectionRangeData should not hold raw RenderObject pointers
[WebKit-https.git] / Source / WebCore / platform / DragImage.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DragImage.h"
28
29 #include "Frame.h"
30 #include "FrameSnapshotting.h"
31 #include "FrameView.h"
32 #include "ImageBuffer.h"
33 #include "Range.h"
34 #include "RenderElement.h"
35 #include "RenderObject.h"
36 #include "RenderView.h"
37 #include "TextIndicator.h"
38
39 namespace WebCore {
40
41 DragImageRef fitDragImageToMaxSize(DragImageRef image, const IntSize& layoutSize, const IntSize& maxSize)
42 {
43     float heightResizeRatio = 0.0f;
44     float widthResizeRatio = 0.0f;
45     float resizeRatio = -1.0f;
46     IntSize originalSize = dragImageSize(image);
47
48     if (layoutSize.width() > maxSize.width()) {
49         widthResizeRatio = maxSize.width() / (float)layoutSize.width();
50         resizeRatio = widthResizeRatio;
51     }
52
53     if (layoutSize.height() > maxSize.height()) {
54         heightResizeRatio = maxSize.height() / (float)layoutSize.height();
55         if ((resizeRatio < 0.0f) || (resizeRatio > heightResizeRatio))
56             resizeRatio = heightResizeRatio;
57     }
58
59     if (layoutSize == originalSize)
60         return resizeRatio > 0.0f ? scaleDragImage(image, FloatSize(resizeRatio, resizeRatio)) : image;
61
62     // The image was scaled in the webpage so at minimum we must account for that scaling.
63     float scaleX = layoutSize.width() / (float)originalSize.width();
64     float scaleY = layoutSize.height() / (float)originalSize.height();
65     if (resizeRatio > 0.0f) {
66         scaleX *= resizeRatio;
67         scaleY *= resizeRatio;
68     }
69
70     return scaleDragImage(image, FloatSize(scaleX, scaleY));
71 }
72
73 struct ScopedNodeDragEnabler {
74     ScopedNodeDragEnabler(Frame& frame, Node& node)
75         : frame(frame)
76         , node(node)
77     {
78         if (node.renderer())
79             node.renderer()->updateDragState(true);
80         frame.document()->updateLayout();
81     }
82
83     ~ScopedNodeDragEnabler()
84     {
85         if (node.renderer())
86             node.renderer()->updateDragState(false);
87     }
88
89     const Frame& frame;
90     const Node& node;
91 };
92
93 static DragImageRef createDragImageFromSnapshot(std::unique_ptr<ImageBuffer> snapshot, Node* node)
94 {
95     if (!snapshot)
96         return nullptr;
97
98     ImageOrientationDescription orientation;
99 #if ENABLE(CSS_IMAGE_ORIENTATION)
100     if (node) {
101         RenderObject* renderer = node->renderer();
102         if (!renderer || !is<RenderElement>(renderer))
103             return nullptr;
104
105         auto& renderElement = downcast<RenderElement>(*renderer);
106         orientation.setRespectImageOrientation(renderElement.shouldRespectImageOrientation());
107         orientation.setImageOrientationEnum(renderElement.style().imageOrientation());
108     }
109 #else
110     UNUSED_PARAM(node);
111 #endif
112     RefPtr<Image> image = ImageBuffer::sinkIntoImage(WTFMove(snapshot), Unscaled);
113     if (!image)
114         return nullptr;
115     return createDragImageFromImage(image.get(), orientation);
116 }
117
118 DragImageRef createDragImageForNode(Frame& frame, Node& node)
119 {
120     ScopedNodeDragEnabler enableDrag(frame, node);
121     return createDragImageFromSnapshot(snapshotNode(frame, node), &node);
122 }
123
124 #if !ENABLE(DATA_INTERACTION)
125
126 DragImageRef createDragImageForSelection(Frame& frame, TextIndicatorData&, bool forceBlackText)
127 {
128     SnapshotOptions options = forceBlackText ? SnapshotOptionsForceBlackText : SnapshotOptionsNone;
129     return createDragImageFromSnapshot(snapshotSelection(frame, options), nullptr);
130 }
131
132 #endif
133
134 struct ScopedFrameSelectionState {
135     ScopedFrameSelectionState(Frame& frame)
136         : frame(frame)
137     {
138         if (auto* renderView = frame.contentRenderer())
139             selection = renderView->selection().get();
140     }
141
142     ~ScopedFrameSelectionState()
143     {
144         if (auto* renderView = frame.contentRenderer()) {
145             ASSERT(selection);
146             renderView->selection().set(selection.value(), SelectionRangeData::RepaintMode::Nothing);
147         }
148     }
149
150     const Frame& frame;
151     std::optional<SelectionRangeData::Context> selection;
152 };
153
154 #if !PLATFORM(IOS)
155
156 DragImageRef createDragImageForRange(Frame& frame, Range& range, bool forceBlackText)
157 {
158     frame.document()->updateLayout();
159     RenderView* view = frame.contentRenderer();
160     if (!view)
161         return nullptr;
162
163     // To snapshot the range, temporarily select it and take selection snapshot.
164     Position start = range.startPosition();
165     Position candidate = start.downstream();
166     if (candidate.deprecatedNode() && candidate.deprecatedNode()->renderer())
167         start = candidate;
168
169     Position end = range.endPosition();
170     candidate = end.upstream();
171     if (candidate.deprecatedNode() && candidate.deprecatedNode()->renderer())
172         end = candidate;
173
174     if (start.isNull() || end.isNull() || start == end)
175         return nullptr;
176
177     const ScopedFrameSelectionState selectionState(frame);
178
179     RenderObject* startRenderer = start.deprecatedNode()->renderer();
180     RenderObject* endRenderer = end.deprecatedNode()->renderer();
181     if (!startRenderer || !endRenderer)
182         return nullptr;
183
184     SnapshotOptions options = SnapshotOptionsPaintSelectionOnly | (forceBlackText ? SnapshotOptionsForceBlackText : SnapshotOptionsNone);
185     int startOffset = start.deprecatedEditingOffset();
186     int endOffset = end.deprecatedEditingOffset();
187     ASSERT(startOffset >= 0 && endOffset >= 0);
188     view->selection().set({ startRenderer, endRenderer, static_cast<unsigned>(startOffset), static_cast<unsigned>(endOffset) }, SelectionRangeData::RepaintMode::Nothing);
189     // We capture using snapshotFrameRect() because we fake up the selection using
190     // FrameView but snapshotSelection() uses the selection from the Frame itself.
191     return createDragImageFromSnapshot(snapshotFrameRect(frame, view->selection().boundsClippedToVisibleContent(), options), nullptr);
192 }
193
194 #endif
195
196 DragImageRef createDragImageForImage(Frame& frame, Node& node, IntRect& imageRect, IntRect& elementRect)
197 {
198     ScopedNodeDragEnabler enableDrag(frame, node);
199
200     RenderObject* renderer = node.renderer();
201     if (!renderer)
202         return nullptr;
203
204     // Calculate image and element metrics for the client, then create drag image.
205     LayoutRect topLevelRect;
206     IntRect paintingRect = snappedIntRect(renderer->paintingRootRect(topLevelRect));
207
208     if (paintingRect.isEmpty())
209         return nullptr;
210
211     elementRect = snappedIntRect(topLevelRect);
212     imageRect = paintingRect;
213
214     return createDragImageFromSnapshot(snapshotNode(frame, node), &node);
215 }
216
217 #if !ENABLE(DATA_INTERACTION)
218 DragImageRef platformAdjustDragImageForDeviceScaleFactor(DragImageRef image, float deviceScaleFactor)
219 {
220     // Later code expects the drag image to be scaled by device's scale factor.
221     return scaleDragImage(image, { deviceScaleFactor, deviceScaleFactor });
222 }
223 #endif
224
225 #if !PLATFORM(COCOA) && !PLATFORM(WIN)
226 DragImageRef createDragImageForLink(Element&, URL&, const String&, FontRenderingMode, float)
227 {
228     return nullptr;
229 }
230 #endif
231
232 #if !PLATFORM(MAC)
233 const int linkDragBorderInset = 2;
234
235 IntPoint dragOffsetForLinkDragImage(DragImageRef dragImage)
236 {
237     IntSize size = dragImageSize(dragImage);
238     return { -size.width() / 2, -linkDragBorderInset };
239 }
240
241 FloatPoint anchorPointForLinkDragImage(DragImageRef dragImage)
242 {
243     IntSize size = dragImageSize(dragImage);
244     return { 0.5, static_cast<float>((size.height() - linkDragBorderInset) / size.height()) };
245 }
246 #endif
247
248 DragImage::DragImage()
249     : m_dragImageRef { nullptr }
250 {
251 }
252
253 DragImage::DragImage(DragImageRef dragImageRef)
254     : m_dragImageRef { dragImageRef }
255 {
256 }
257
258 DragImage::DragImage(DragImage&& other)
259     : m_dragImageRef { std::exchange(other.m_dragImageRef, nullptr) }
260 {
261     m_indicatorData = other.m_indicatorData;
262 }
263
264 DragImage& DragImage::operator=(DragImage&& other)
265 {
266     if (m_dragImageRef)
267         deleteDragImage(m_dragImageRef);
268
269     m_dragImageRef = std::exchange(other.m_dragImageRef, nullptr);
270     m_indicatorData = other.m_indicatorData;
271
272     return *this;
273 }
274
275 DragImage::~DragImage()
276 {
277     if (m_dragImageRef)
278         deleteDragImage(m_dragImageRef);
279 }
280
281 } // namespace WebCore
282