Add a DragImage class that wraps a DragImageRef
[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
38 namespace WebCore {
39
40 DragImageRef fitDragImageToMaxSize(DragImageRef image, const IntSize& layoutSize, const IntSize& maxSize)
41 {
42     float heightResizeRatio = 0.0f;
43     float widthResizeRatio = 0.0f;
44     float resizeRatio = -1.0f;
45     IntSize originalSize = dragImageSize(image);
46
47     if (layoutSize.width() > maxSize.width()) {
48         widthResizeRatio = maxSize.width() / (float)layoutSize.width();
49         resizeRatio = widthResizeRatio;
50     }
51
52     if (layoutSize.height() > maxSize.height()) {
53         heightResizeRatio = maxSize.height() / (float)layoutSize.height();
54         if ((resizeRatio < 0.0f) || (resizeRatio > heightResizeRatio))
55             resizeRatio = heightResizeRatio;
56     }
57
58     if (layoutSize == originalSize)
59         return resizeRatio > 0.0f ? scaleDragImage(image, FloatSize(resizeRatio, resizeRatio)) : image;
60
61     // The image was scaled in the webpage so at minimum we must account for that scaling.
62     float scaleX = layoutSize.width() / (float)originalSize.width();
63     float scaleY = layoutSize.height() / (float)originalSize.height();
64     if (resizeRatio > 0.0f) {
65         scaleX *= resizeRatio;
66         scaleY *= resizeRatio;
67     }
68
69     return scaleDragImage(image, FloatSize(scaleX, scaleY));
70 }
71
72 struct ScopedNodeDragEnabler {
73     ScopedNodeDragEnabler(Frame& frame, Node& node)
74         : frame(frame)
75         , node(node)
76     {
77         if (node.renderer())
78             node.renderer()->updateDragState(true);
79         frame.document()->updateLayout();
80     }
81
82     ~ScopedNodeDragEnabler()
83     {
84         if (node.renderer())
85             node.renderer()->updateDragState(false);
86     }
87
88     const Frame& frame;
89     const Node& node;
90 };
91
92 static DragImageRef createDragImageFromSnapshot(std::unique_ptr<ImageBuffer> snapshot, Node* node)
93 {
94     if (!snapshot)
95         return nullptr;
96
97     ImageOrientationDescription orientation;
98 #if ENABLE(CSS_IMAGE_ORIENTATION)
99     if (node) {
100         RenderObject* renderer = node->renderer();
101         if (!renderer || !is<RenderElement>(renderer))
102             return nullptr;
103
104         auto& renderElement = downcast<RenderElement>(*renderer);
105         orientation.setRespectImageOrientation(renderElement.shouldRespectImageOrientation());
106         orientation.setImageOrientationEnum(renderElement.style().imageOrientation());
107     }
108 #else
109     UNUSED_PARAM(node);
110 #endif
111     RefPtr<Image> image = ImageBuffer::sinkIntoImage(WTFMove(snapshot), Unscaled);
112     if (!image)
113         return nullptr;
114     return createDragImageFromImage(image.get(), orientation);
115 }
116
117 DragImageRef createDragImageForNode(Frame& frame, Node& node)
118 {
119     ScopedNodeDragEnabler enableDrag(frame, node);
120     return createDragImageFromSnapshot(snapshotNode(frame, node), &node);
121 }
122
123 #if !ENABLE(DATA_INTERACTION)
124
125 DragImageRef createDragImageForSelection(Frame& frame, bool forceBlackText)
126 {
127     SnapshotOptions options = forceBlackText ? SnapshotOptionsForceBlackText : SnapshotOptionsNone;
128     return createDragImageFromSnapshot(snapshotSelection(frame, options), nullptr);
129 }
130
131 #endif
132
133 struct ScopedFrameSelectionState {
134     ScopedFrameSelectionState(Frame& frame)
135         : frame(frame)
136     {
137         if (RenderView* root = frame.contentRenderer())
138             root->getSelection(startRenderer, startOffset, endRenderer, endOffset);
139     }
140
141     ~ScopedFrameSelectionState()
142     {
143         if (RenderView* root = frame.contentRenderer())
144             root->setSelection(startRenderer, startOffset, endRenderer, endOffset, RenderView::RepaintNothing);
145     }
146
147     const Frame& frame;
148     RenderObject* startRenderer;
149     RenderObject* endRenderer;
150     std::optional<unsigned> startOffset;
151     std::optional<unsigned> endOffset;
152 };
153
154 DragImageRef createDragImageForRange(Frame& frame, Range& range, bool forceBlackText)
155 {
156     frame.document()->updateLayout();
157     RenderView* view = frame.contentRenderer();
158     if (!view)
159         return nullptr;
160
161     // To snapshot the range, temporarily select it and take selection snapshot.
162     Position start = range.startPosition();
163     Position candidate = start.downstream();
164     if (candidate.deprecatedNode() && candidate.deprecatedNode()->renderer())
165         start = candidate;
166
167     Position end = range.endPosition();
168     candidate = end.upstream();
169     if (candidate.deprecatedNode() && candidate.deprecatedNode()->renderer())
170         end = candidate;
171
172     if (start.isNull() || end.isNull() || start == end)
173         return nullptr;
174
175     const ScopedFrameSelectionState selectionState(frame);
176
177     RenderObject* startRenderer = start.deprecatedNode()->renderer();
178     RenderObject* endRenderer = end.deprecatedNode()->renderer();
179     if (!startRenderer || !endRenderer)
180         return nullptr;
181
182     SnapshotOptions options = SnapshotOptionsPaintSelectionOnly | (forceBlackText ? SnapshotOptionsForceBlackText : SnapshotOptionsNone);
183     int startOffset = start.deprecatedEditingOffset();
184     int endOffset = end.deprecatedEditingOffset();
185     ASSERT(startOffset >= 0 && endOffset >= 0);
186     view->setSelection(startRenderer, startOffset, endRenderer, endOffset, RenderView::RepaintNothing);
187     // We capture using snapshotFrameRect() because we fake up the selection using
188     // FrameView but snapshotSelection() uses the selection from the Frame itself.
189     return createDragImageFromSnapshot(snapshotFrameRect(frame, view->selectionBounds(), options), nullptr);
190 }
191
192 DragImageRef createDragImageForImage(Frame& frame, Node& node, IntRect& imageRect, IntRect& elementRect)
193 {
194     ScopedNodeDragEnabler enableDrag(frame, node);
195
196     RenderObject* renderer = node.renderer();
197     if (!renderer)
198         return nullptr;
199
200     // Calculate image and element metrics for the client, then create drag image.
201     LayoutRect topLevelRect;
202     IntRect paintingRect = snappedIntRect(renderer->paintingRootRect(topLevelRect));
203
204     if (paintingRect.isEmpty())
205         return nullptr;
206
207     elementRect = snappedIntRect(topLevelRect);
208     imageRect = paintingRect;
209
210     return createDragImageFromSnapshot(snapshotNode(frame, node), &node);
211 }
212
213 #if !PLATFORM(COCOA) && !PLATFORM(WIN)
214 DragImageRef createDragImageForLink(URL&, const String&, FontRenderingMode)
215 {
216     return nullptr;
217 }
218 #endif
219
220 DragImage::DragImage()
221     : m_dragImageRef { nullptr }
222 {
223 }
224
225 DragImage::DragImage(DragImageRef dragImageRef)
226     : m_dragImageRef { dragImageRef }
227 {
228 }
229
230 DragImage::DragImage(DragImage&& other)
231     : m_dragImageRef { std::exchange(other.m_dragImageRef, nullptr) }
232 {
233 }
234
235 DragImage& DragImage::operator=(DragImage&& other)
236 {
237     if (m_dragImageRef)
238         deleteDragImage(m_dragImageRef);
239
240     m_dragImageRef = std::exchange(other.m_dragImageRef, nullptr);
241
242     return *this;
243 }
244
245 DragImage::~DragImage()
246 {
247     if (m_dragImageRef)
248         deleteDragImage(m_dragImageRef);
249 }
250
251 } // namespace WebCore
252