[GTK][WPE] Use mobile user-agent on tablet
[WebKit.git] / Source / WebCore / platform / ios / DragImageIOS.mm
1 /*
2  * Copyright (C) 2014 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 #import "config.h"
27 #import "DragImage.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "Document.h"
32 #import "Element.h"
33 #import "FloatRoundedRect.h"
34 #import "FontCascade.h"
35 #import "FontPlatformData.h"
36 #import "Frame.h"
37 #import "GeometryUtilities.h"
38 #import "GraphicsContext.h"
39 #import "Image.h"
40 #import "NotImplemented.h"
41 #import "Page.h"
42 #import "Range.h"
43 #import "SimpleRange.h"
44 #import "StringTruncator.h"
45 #import "TextIndicator.h"
46 #import "TextRun.h"
47 #import <CoreGraphics/CoreGraphics.h>
48 #import <CoreText/CoreText.h>
49 #import <UIKit/UIColor.h>
50 #import <UIKit/UIFont.h>
51 #import <UIKit/UIGraphicsImageRenderer.h>
52 #import <UIKit/UIImage.h>
53 #import <pal/ios/UIKitSoftLink.h>
54 #import <wtf/NeverDestroyed.h>
55
56 namespace WebCore {
57
58 #if ENABLE(DRAG_SUPPORT)
59
60 IntSize dragImageSize(DragImageRef image)
61 {
62     return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get()));
63 }
64
65 DragImageRef scaleDragImage(DragImageRef image, FloatSize scale)
66 {
67     CGSize imageSize = CGSizeMake(scale.width() * CGImageGetWidth(image.get()), scale.height() * CGImageGetHeight(image.get()));
68     CGRect imageRect = { CGPointZero, imageSize };
69
70     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
71     UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
72         CGContextRef context = rendererContext.CGContext;
73         CGContextTranslateCTM(context, 0, imageSize.height);
74         CGContextScaleCTM(context, 1, -1);
75         CGContextDrawImage(context, imageRect, image.get());
76     }];
77     return imageCopy.CGImage;
78 }
79
80 static float maximumAllowedDragImageArea = 600 * 1024;
81
82 DragImageRef createDragImageFromImage(Image* image, ImageOrientation orientation)
83 {
84     if (!image || !image->width() || !image->height())
85         return nil;
86
87     float adjustedImageScale = 1;
88     CGSize imageSize(image->size());
89     if (imageSize.width * imageSize.height > maximumAllowedDragImageArea) {
90         auto adjustedSize = roundedIntSize(sizeWithAreaAndAspectRatio(maximumAllowedDragImageArea, imageSize.width / imageSize.height));
91         adjustedImageScale = adjustedSize.width() / imageSize.width;
92         imageSize = adjustedSize;
93     }
94
95     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
96     UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
97         GraphicsContext context(rendererContext.CGContext);
98         context.translate(0, imageSize.height);
99         context.scale({ adjustedImageScale, -adjustedImageScale });
100         context.drawImage(*image, FloatPoint(), { orientation });
101     }];
102     return imageCopy.CGImage;
103 }
104
105 void deleteDragImage(DragImageRef)
106 {
107 }
108
109 static FontCascade cascadeForSystemFont(CGFloat size)
110 {
111     UIFont *font = [PAL::getUIFontClass() systemFontOfSize:size];
112     return FontCascade(FontPlatformData(CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize, nil), font.pointSize));
113 }
114
115 DragImageRef createDragImageForLink(Element& linkElement, URL& url, const String& title, TextIndicatorData& indicatorData, FontRenderingMode, float)
116 {
117     // FIXME: Most of this can go away once we can use UIURLDragPreviewView unconditionally.
118     static const CGFloat dragImagePadding = 10;
119     static const auto titleFontCascade = makeNeverDestroyed(cascadeForSystemFont(16));
120     static const auto urlFontCascade = makeNeverDestroyed(cascadeForSystemFont(14));
121
122     String topString(title.stripWhiteSpace());
123     String bottomString([(NSURL *)url absoluteString]);
124     if (topString.isEmpty()) {
125         topString = bottomString;
126         bottomString = emptyString();
127     }
128
129     static CGFloat maxTextWidth = 320;
130     auto truncatedTopString = StringTruncator::rightTruncate(topString, maxTextWidth, titleFontCascade);
131     auto truncatedBottomString = StringTruncator::centerTruncate(bottomString, maxTextWidth, urlFontCascade);
132     CGFloat textWidth = std::max(StringTruncator::width(truncatedTopString, titleFontCascade), StringTruncator::width(truncatedBottomString, urlFontCascade));
133     CGFloat textHeight = truncatedBottomString.isEmpty() ? 22 : 44;
134
135     CGRect imageRect = CGRectMake(0, 0, textWidth + 2 * dragImagePadding, textHeight + 2 * dragImagePadding);
136
137     auto renderer = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size]);
138     auto image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
139         GraphicsContext context(rendererContext.CGContext);
140         context.translate(0, CGRectGetHeight(imageRect));
141         context.scale({ 1, -1 });
142         context.fillRoundedRect(FloatRoundedRect(imageRect, FloatRoundedRect::Radii(4)), makeSimpleColor(255, 255, 255));
143         titleFontCascade.get().drawText(context, TextRun(truncatedTopString), FloatPoint(dragImagePadding, 18 + dragImagePadding));
144         if (!truncatedBottomString.isEmpty())
145             urlFontCascade.get().drawText(context, TextRun(truncatedBottomString), FloatPoint(dragImagePadding, 40 + dragImagePadding));
146     }];
147
148     constexpr OptionSet<TextIndicatorOption> defaultLinkIndicatorOptions {
149         TextIndicatorOption::TightlyFitContent,
150         TextIndicatorOption::RespectTextColor,
151         TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges,
152         TextIndicatorOption::ExpandClipBeyondVisibleRect,
153         TextIndicatorOption::ComputeEstimatedBackgroundColor
154     };
155
156     if (auto textIndicator = TextIndicator::createWithRange(makeRangeSelectingNodeContents(linkElement), defaultLinkIndicatorOptions, TextIndicatorPresentationTransition::None, FloatSize()))
157         indicatorData = textIndicator->data();
158
159     return image.CGImage;
160 }
161
162 DragImageRef createDragImageIconForCachedImageFilename(const String&)
163 {
164     notImplemented();
165     return nullptr;
166 }
167
168 DragImageRef platformAdjustDragImageForDeviceScaleFactor(DragImageRef image, float)
169 {
170     // On iOS, we just create the drag image at the right device scale factor, so we don't need to scale it by 1 / deviceScaleFactor later.
171     return image;
172 }
173
174 constexpr OptionSet<TextIndicatorOption> defaultSelectionDragImageTextIndicatorOptions {
175     TextIndicatorOption::ExpandClipBeyondVisibleRect,
176     TextIndicatorOption::PaintAllContent,
177     TextIndicatorOption::UseSelectionRectForSizing,
178     TextIndicatorOption::ComputeEstimatedBackgroundColor
179 };
180
181 DragImageRef createDragImageForSelection(Frame& frame, TextIndicatorData& indicatorData, bool forceBlackText)
182 {
183     if (auto document = frame.document())
184         document->updateLayout();
185
186     auto options = defaultSelectionDragImageTextIndicatorOptions;
187     if (!forceBlackText)
188         options.add(TextIndicatorOption::RespectTextColor);
189
190     auto textIndicator = TextIndicator::createWithSelectionInFrame(frame, options, TextIndicatorPresentationTransition::None, FloatSize());
191     if (!textIndicator)
192         return nullptr;
193
194     auto image = textIndicator->contentImage();
195     if (image)
196         indicatorData = textIndicator->data();
197     else
198         return nullptr;
199
200     FloatRect imageRect(0, 0, image->width(), image->height());
201     if (auto page = frame.page())
202         imageRect.scale(1 / page->deviceScaleFactor());
203
204
205     auto renderer = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]);
206     return [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
207         GraphicsContext context(rendererContext.CGContext);
208         // FIXME: The context flip here should not be necessary, and suggests that somewhere else in the regular
209         // drag initiation flow, we unnecessarily flip the graphics context.
210         context.translate(0, imageRect.height());
211         context.scale({ 1, -1 });
212         context.drawImage(*image, imageRect);
213     }].CGImage;
214 }
215
216 DragImageRef dissolveDragImageToFraction(DragImageRef image, float)
217 {
218     notImplemented();
219     return image;
220 }
221
222 DragImageRef createDragImageForRange(Frame& frame, Range& range, bool forceBlackText)
223 {
224     if (auto document = frame.document())
225         document->updateLayout();
226
227     if (range.collapsed())
228         return nil;
229
230     auto options = defaultSelectionDragImageTextIndicatorOptions;
231     if (!forceBlackText)
232         options.add(TextIndicatorOption::RespectTextColor);
233
234     auto textIndicator = TextIndicator::createWithRange(range, options, TextIndicatorPresentationTransition::None);
235     if (!textIndicator || !textIndicator->contentImage())
236         return nil;
237
238     auto& image = *textIndicator->contentImage();
239     auto render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:image.size()]);
240     UIImage *finalImage = [render.get() imageWithActions:[&image](UIGraphicsImageRendererContext *rendererContext) {
241         GraphicsContext context(rendererContext.CGContext);
242         context.drawImage(image, FloatPoint());
243     }];
244
245     return finalImage.CGImage;
246 }
247
248 DragImageRef createDragImageForColor(const Color& color, const FloatRect& elementRect, float pageScaleFactor, Path& visiblePath)
249 {
250     FloatRect imageRect { 0, 0, elementRect.width() * pageScaleFactor, elementRect.height() * pageScaleFactor };
251     FloatRoundedRect swatch { imageRect, FloatRoundedRect::Radii(ColorSwatchCornerRadius * pageScaleFactor) };
252
253     auto render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]);
254     UIImage *image = [render imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
255         GraphicsContext context { rendererContext.CGContext };
256         context.translate(0, CGRectGetHeight(imageRect));
257         context.scale({ 1, -1 });
258         context.fillRoundedRect(swatch, color);
259     }];
260
261     visiblePath.addRoundedRect(swatch);
262     return image.CGImage;
263 }
264
265 #else
266
267 void deleteDragImage(RetainPtr<CGImageRef>)
268 {
269     // Since this is a RetainPtr, there's nothing additional we need to do to
270     // delete it. It will be released when it falls out of scope.
271 }
272
273 // FIXME: fix signature of dragImageSize() to avoid copying the argument.
274 IntSize dragImageSize(RetainPtr<CGImageRef> image)
275 {
276     return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get()));
277 }
278
279 RetainPtr<CGImageRef> scaleDragImage(RetainPtr<CGImageRef>, FloatSize)
280 {
281     return nullptr;
282 }
283
284 RetainPtr<CGImageRef> createDragImageFromImage(Image*, ImageOrientation)
285 {
286     return nullptr;
287 }
288
289 DragImageRef createDragImageForRange(Frame&, Range&, bool)
290 {
291     return nullptr;
292 }
293
294 #endif
295
296 } // namespace WebCore
297
298 #endif // PLATFORM(IOS_FAMILY)