Remove expectations for fast/scrolling/ios/body-overflow-hidden-height-100-percent...
[WebKit-https.git] / Source / WebCore / platform / mac / DragImageMac.mm
1 /*
2  * Copyright (C) 2007, 2009, 2012 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 ENABLE(DRAG_SUPPORT) && PLATFORM(MAC)
30
31 #import "BitmapImage.h"
32 #import "ColorMac.h"
33 #import "Element.h"
34 #import "FloatRoundedRect.h"
35 #import "FontCascade.h"
36 #import "FontDescription.h"
37 #import "FontSelector.h"
38 #import "GraphicsContext.h"
39 #import "Image.h"
40 #import "LocalDefaultSystemAppearance.h"
41 #import "Page.h"
42 #import "StringTruncator.h"
43 #import "TextIndicator.h"
44 #import "WebKitNSImageExtras.h"
45 #import <pal/spi/cg/CoreGraphicsSPI.h>
46 #import <pal/spi/cocoa/CoreTextSPI.h>
47 #import <pal/spi/cocoa/URLFormattingSPI.h>
48 #import <wtf/SoftLinking.h>
49 #import <wtf/URL.h>
50
51 #if !HAVE(URL_FORMATTING)
52 SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(LinkPresentation)
53 #endif
54
55 namespace WebCore {
56
57 IntSize dragImageSize(RetainPtr<NSImage> image)
58 {
59     return (IntSize)[image size];
60 }
61
62 void deleteDragImage(RetainPtr<NSImage>)
63 {
64     // Since this is a RetainPtr, there's nothing additional we need to do to
65     // delete it. It will be released when it falls out of scope.
66 }
67
68 RetainPtr<NSImage> scaleDragImage(RetainPtr<NSImage> image, FloatSize scale)
69 {
70     NSSize originalSize = [image size];
71     NSSize newSize = NSMakeSize((originalSize.width * scale.width()), (originalSize.height * scale.height()));
72     newSize.width = roundf(newSize.width);
73     newSize.height = roundf(newSize.height);
74     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
75     [image setScalesWhenResized:YES];
76     ALLOW_DEPRECATED_DECLARATIONS_END
77     [image setSize:newSize];
78     return image;
79 }
80     
81 RetainPtr<NSImage> dissolveDragImageToFraction(RetainPtr<NSImage> image, float delta)
82 {
83     if (!image)
84         return nil;
85
86     RetainPtr<NSImage> dissolvedImage = adoptNS([[NSImage alloc] initWithSize:[image size]]);
87     
88     [dissolvedImage lockFocus];
89     [image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, [image size].width, [image size].height) operation:NSCompositingOperationCopy fraction:delta];
90     [dissolvedImage unlockFocus];
91
92     return dissolvedImage;
93 }
94         
95 RetainPtr<NSImage> createDragImageFromImage(Image* image, ImageOrientation orientation)
96 {
97     if (is<BitmapImage>(*image)) {
98         BitmapImage& bitmapImage = downcast<BitmapImage>(*image);
99
100         if (orientation == ImageOrientation::FromImage)
101             orientation = bitmapImage.orientationForCurrentFrame();
102
103         if (orientation != ImageOrientation::None) {
104             // Construct a correctly-rotated copy of the image to use as the drag image.
105             FloatSize imageSize = image->size(orientation);
106             RetainPtr<NSImage> rotatedDragImage = adoptNS([[NSImage alloc] initWithSize:(NSSize)(imageSize)]);
107             [rotatedDragImage lockFocus];
108
109             // ImageOrientation uses top-left coordinates, need to flip to bottom-left, apply...
110             CGAffineTransform transform = CGAffineTransformMakeTranslation(0, imageSize.height());
111             transform = CGAffineTransformScale(transform, 1, -1);
112             transform = CGAffineTransformConcat(orientation.transformFromDefault(imageSize), transform);
113
114             if (orientation.usesWidthAsHeight())
115                 imageSize = imageSize.transposedSize();
116
117             // ...and flip back.
118             transform = CGAffineTransformTranslate(transform, 0, imageSize.height());
119             transform = CGAffineTransformScale(transform, 1, -1);
120
121             RetainPtr<NSAffineTransform> cocoaTransform = adoptNS([[NSAffineTransform alloc] init]);
122             [cocoaTransform setTransformStruct:*(NSAffineTransformStruct*)&transform];
123             [cocoaTransform concat];
124
125             FloatRect imageRect(FloatPoint(), imageSize);
126             [image->snapshotNSImage() drawInRect:imageRect fromRect:imageRect operation:NSCompositingOperationSourceOver fraction:1.0];
127
128             [rotatedDragImage unlockFocus];
129
130             return rotatedDragImage;
131         }
132     }
133
134     FloatSize imageSize = image->size();
135     auto dragImage = image->snapshotNSImage();
136     [dragImage setSize:(NSSize)imageSize];
137     return dragImage;
138 }
139     
140 RetainPtr<NSImage> createDragImageIconForCachedImageFilename(const String& filename)
141 {
142     NSString *extension = nil;
143     size_t dotIndex = filename.reverseFind('.');
144     
145     if (dotIndex != notFound && dotIndex < (filename.length() - 1)) // require that a . exists after the first character and before the last
146         extension = filename.substring(dotIndex + 1);
147     else {
148         // It might be worth doing a further lookup to pull the extension from the MIME type.
149         extension = @"";
150     }
151     
152     return [[NSWorkspace sharedWorkspace] iconForFileType:extension];
153 }
154
155 const CGFloat linkImagePadding = 10;
156 const CGFloat linkImageDomainBaselineToTitleBaseline = 18;
157 const CGFloat linkImageCornerRadius = 5;
158 const CGFloat linkImageMaximumWidth = 400;
159 const CGFloat linkImageFontSize = 11;
160 const CFIndex linkImageTitleMaximumLineCount = 2;
161 const int linkImageShadowRadius = 0;
162 const int linkImageShadowOffsetY = 0;
163 const int linkImageDragCornerOutsetX = 6 - linkImageShadowRadius;
164 const int linkImageDragCornerOutsetY = 10 - linkImageShadowRadius + linkImageShadowOffsetY;
165
166 IntPoint dragOffsetForLinkDragImage(DragImageRef dragImage)
167 {
168     IntSize size = dragImageSize(dragImage);
169     return { linkImageDragCornerOutsetX, size.height() + linkImageDragCornerOutsetY };
170 }
171
172 FloatPoint anchorPointForLinkDragImage(DragImageRef dragImage)
173 {
174     IntSize size = dragImageSize(dragImage);
175     return { -static_cast<float>(linkImageDragCornerOutsetX) / size.width(), -static_cast<float>(linkImageDragCornerOutsetY) / size.height() };
176 }
177
178 struct LinkImageLayout {
179     LinkImageLayout(URL&, const String& title);
180
181     struct Label {
182         FloatPoint origin;
183         RetainPtr<CTFrameRef> frame;
184     };
185     Vector<Label> labels;
186
187     FloatRect boundingRect;
188 };
189
190 LinkImageLayout::LinkImageLayout(URL& url, const String& titleString)
191 {
192     NSString *title = nsStringNilIfEmpty(titleString);
193     NSURL *cocoaURL = url;
194     NSString *absoluteURLString = [cocoaURL absoluteString];
195
196     NSString *domain = absoluteURLString;
197 #if HAVE(URL_FORMATTING)
198     domain = [cocoaURL _lp_simplifiedDisplayString];
199 #else
200     if (LinkPresentationLibrary())
201         domain = [cocoaURL _lp_simplifiedDisplayString];
202 #endif
203
204     if ([title isEqualToString:absoluteURLString])
205         title = nil;
206
207     NSFont *titleFont = [NSFont boldSystemFontOfSize:linkImageFontSize];
208     NSFont *domainFont = [NSFont systemFontOfSize:linkImageFontSize];
209
210     NSColor *titleColor = [NSColor labelColor];
211     NSColor *domainColor = [NSColor secondaryLabelColor];
212
213     CGFloat maximumAvailableWidth = linkImageMaximumWidth - linkImagePadding * 2;
214
215     CGFloat currentY = linkImagePadding;
216     CGFloat maximumUsedTextWidth = 0;
217
218     auto buildLines = [this, maximumAvailableWidth, &maximumUsedTextWidth, &currentY] (NSString *text, NSColor *color, NSFont *font, CFIndex maximumLines, CTLineBreakMode lineBreakMode) {
219         CTParagraphStyleSetting paragraphStyleSettings[1];
220         paragraphStyleSettings[0].spec = kCTParagraphStyleSpecifierLineBreakMode;
221         paragraphStyleSettings[0].valueSize = sizeof(CTLineBreakMode);
222         paragraphStyleSettings[0].value = &lineBreakMode;
223         RetainPtr<CTParagraphStyleRef> paragraphStyle = adoptCF(CTParagraphStyleCreate(paragraphStyleSettings, 1));
224
225         NSDictionary *textAttributes = @{
226             (id)kCTFontAttributeName: font,
227             (id)kCTForegroundColorAttributeName: color,
228             (id)kCTParagraphStyleAttributeName: (id)paragraphStyle.get()
229         };
230         NSDictionary *frameAttributes = @{
231             (id)kCTFrameMaximumNumberOfLinesAttributeName: @(maximumLines)
232         };
233
234         RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
235         RetainPtr<CTFramesetterRef> textFramesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText.get()));
236
237         CFRange fitRange;
238         CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(textFramesetter.get(), CFRangeMake(0, 0), (CFDictionaryRef)frameAttributes, CGSizeMake(maximumAvailableWidth, CGFLOAT_MAX), &fitRange);
239
240         RetainPtr<CGPathRef> textPath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, textSize.width, textSize.height), nullptr));
241         RetainPtr<CTFrameRef> textFrame = adoptCF(CTFramesetterCreateFrame(textFramesetter.get(), fitRange, textPath.get(), (CFDictionaryRef)frameAttributes));
242
243         CFArrayRef ctLines = CTFrameGetLines(textFrame.get());
244         CFIndex lineCount = CFArrayGetCount(ctLines);
245         if (!lineCount)
246             return;
247
248         Vector<CGPoint> origins(lineCount);
249         CGRect lineBounds;
250         CGFloat height = 0;
251         CTFrameGetLineOrigins(textFrame.get(), CFRangeMake(0, 0), origins.data());
252         for (CFIndex lineIndex = 0; lineIndex < lineCount; ++lineIndex) {
253             CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
254
255             lineBounds = CTLineGetBoundsWithOptions(line, 0);
256             CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
257             CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
258             maximumUsedTextWidth = std::max(maximumUsedTextWidth, lineWidthIgnoringTrailingWhitespace);
259
260             if (lineIndex)
261                 height += origins[lineIndex - 1].y - origins[lineIndex].y;
262         }
263
264         LinkImageLayout::Label label;
265         label.frame = textFrame;
266         label.origin = FloatPoint(linkImagePadding, currentY + origins[0].y);
267         labels.append(label);
268
269         currentY += height + lineBounds.size.height;
270     };
271
272     if (title)
273         buildLines(title, titleColor, titleFont, linkImageTitleMaximumLineCount, kCTLineBreakByTruncatingTail);
274
275     if (title && domain)
276         currentY += linkImageDomainBaselineToTitleBaseline - (domainFont.ascender - domainFont.descender);
277
278     if (domain)
279         buildLines(domain, domainColor, domainFont, 1, kCTLineBreakByTruncatingMiddle);
280
281     currentY += linkImagePadding;
282
283     boundingRect = FloatRect(0, 0, maximumUsedTextWidth + linkImagePadding * 2, currentY);
284
285     // To work around blurry drag images on 1x displays, make the width and height a multiple of 2.
286     // FIXME: remove this workaround when <rdar://problem/33059739> is fixed.
287     boundingRect.setWidth((static_cast<int>(boundingRect.width()) / 2) * 2);
288     boundingRect.setHeight((static_cast<int>(boundingRect.height() / 2) * 2));
289 }
290
291 DragImageRef createDragImageForLink(Element& element, URL& url, const String& title, TextIndicatorData&, FontRenderingMode, float deviceScaleFactor)
292 {
293     LinkImageLayout layout(url, title);
294
295     LocalDefaultSystemAppearance localAppearance(element.document().useDarkAppearance(element.computedStyle()));
296
297     auto imageSize = layout.boundingRect.size();
298     RetainPtr<NSImage> dragImage = adoptNS([[NSImage alloc] initWithSize:imageSize]);
299     [dragImage _web_lockFocusWithDeviceScaleFactor:deviceScaleFactor];
300
301     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
302     GraphicsContext context((CGContextRef)[NSGraphicsContext currentContext].graphicsPort);
303     ALLOW_DEPRECATED_DECLARATIONS_END
304
305     context.fillRoundedRect(FloatRoundedRect(layout.boundingRect, FloatRoundedRect::Radii(linkImageCornerRadius)), colorFromNSColor([NSColor controlBackgroundColor]));
306
307     for (const auto& label : layout.labels) {
308         GraphicsContextStateSaver saver(context);
309         context.translate(label.origin.x(), layout.boundingRect.height() - label.origin.y() - linkImagePadding);
310         CTFrameDraw(label.frame.get(), context.platformContext());
311     }
312
313     [dragImage unlockFocus];
314
315     return dragImage;
316 }
317
318 DragImageRef createDragImageForColor(const Color& color, const FloatRect&, float, Path&)
319 {
320     auto dragImage = adoptNS([[NSImage alloc] initWithSize:NSMakeSize(ColorSwatchWidth, ColorSwatchWidth)]);
321
322     [dragImage lockFocus];
323
324     NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(0, 0, ColorSwatchWidth, ColorSwatchWidth) xRadius:ColorSwatchCornerRadius yRadius:ColorSwatchCornerRadius];
325     [path setLineWidth:ColorSwatchStrokeSize];
326
327     [nsColor(color) setFill];
328     [path fill];
329
330     [[NSColor quaternaryLabelColor] setStroke];
331     [path stroke];
332
333     [dragImage unlockFocus];
334
335     return dragImage;
336 }
337    
338 } // namespace WebCore
339
340 #endif // ENABLE(DRAG_SUPPORT) && PLATFORM(MAC)