Unreviewed, rolling out r240630.
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / _WKThumbnailView.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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "_WKThumbnailViewInternal.h"
28
29 #if WK_API_ENABLED
30
31 #if PLATFORM(MAC)
32
33 #import "ImageOptions.h"
34 #import "WKAPICast.h"
35 #import "WKView.h"
36 #import "WKViewInternal.h"
37 #import "WKWebViewInternal.h"
38 #import "WebPageProxy.h"
39
40 // FIXME: Make it possible to leave a snapshot of the content presented in the WKView while the thumbnail is live.
41 // FIXME: Don't make new speculative tiles while thumbnailed.
42 // FIXME: Hide scrollbars in the thumbnail.
43 // FIXME: We should re-use existing tiles for unparented views, if we have them (we need to know if they've been purged; if so, repaint at scaled-down size).
44 // FIXME: We should switch to the low-resolution scale if a view we have high-resolution tiles for repaints.
45
46 @implementation _WKThumbnailView {
47     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
48     RetainPtr<WKView> _wkView;
49     ALLOW_DEPRECATED_DECLARATIONS_END
50     RetainPtr<WKWebView> _wkWebView;
51     WebKit::WebPageProxy* _webPageProxy;
52
53     BOOL _originalMayStartMediaWhenInWindow;
54     BOOL _originalSourceViewIsInWindow;
55
56     BOOL _snapshotWasDeferred;
57     CGFloat _lastSnapshotScale;
58     CGSize _lastSnapshotMaximumSize;
59
60     RetainPtr<NSColor> _overrideBackgroundColor;
61 }
62
63 @synthesize snapshotSize=_snapshotSize;
64 @synthesize _waitingForSnapshot=_waitingForSnapshot;
65 @synthesize exclusivelyUsesSnapshot=_exclusivelyUsesSnapshot;
66 @synthesize shouldKeepSnapshotWhenRemovedFromSuperview=_shouldKeepSnapshotWhenRemovedFromSuperview;
67
68 - (instancetype)initWithFrame:(NSRect)frame
69 {
70     if (!(self = [super initWithFrame:frame]))
71         return nil;
72
73     self.wantsLayer = YES;
74     _scale = 1;
75     _lastSnapshotScale = NAN;
76     
77     return self;
78 }
79
80 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
81 - (instancetype)initWithFrame:(NSRect)frame fromWKView:(WKView *)wkView
82 {
83     if (!(self = [self initWithFrame:frame]))
84         return nil;
85
86     _wkView = wkView;
87     _webPageProxy = WebKit::toImpl([_wkView pageRef]);
88     _originalMayStartMediaWhenInWindow = _webPageProxy->mayStartMediaWhenInWindow();
89     _originalSourceViewIsInWindow = !![_wkView window];
90
91     return self;
92 }
93 ALLOW_DEPRECATED_DECLARATIONS_END
94
95 - (instancetype)initWithFrame:(NSRect)frame fromWKWebView:(WKWebView *)webView
96 {
97     if (!(self = [self initWithFrame:frame]))
98         return nil;
99     
100     _wkWebView = webView;
101     _webPageProxy = [_wkWebView _page];
102     _originalMayStartMediaWhenInWindow = _webPageProxy->mayStartMediaWhenInWindow();
103     _originalSourceViewIsInWindow = !![_wkWebView window];
104     
105     return self;
106 }
107
108 - (BOOL)wantsUpdateLayer
109 {
110     return YES;
111 }
112
113 - (void)updateLayer
114 {
115     [super updateLayer];
116
117     NSColor *backgroundColor = self.overrideBackgroundColor ?: [NSColor quaternaryLabelColor];
118     self.layer.backgroundColor = backgroundColor.CGColor;
119 }
120
121 - (void)requestSnapshot
122 {
123     if (_waitingForSnapshot) {
124         _snapshotWasDeferred = YES;
125         return;
126     }
127
128     _waitingForSnapshot = YES;
129
130     RetainPtr<_WKThumbnailView> thumbnailView = self;
131     WebCore::IntRect snapshotRect(WebCore::IntPoint(), _webPageProxy->viewSize() - WebCore::IntSize(0, _webPageProxy->topContentInset()));
132     WebKit::SnapshotOptions options = WebKit::SnapshotOptionsInViewCoordinates | WebKit::SnapshotOptionsUseScreenColorSpace;
133     WebCore::IntSize bitmapSize = snapshotRect.size();
134     bitmapSize.scale(_scale * _webPageProxy->deviceScaleFactor());
135
136     if (!CGSizeEqualToSize(_maximumSnapshotSize, CGSizeZero)) {
137         double sizeConstraintScale = 1;
138         if (_maximumSnapshotSize.width)
139             sizeConstraintScale = CGFloatMin(sizeConstraintScale, _maximumSnapshotSize.width / bitmapSize.width());
140         if (_maximumSnapshotSize.height)
141             sizeConstraintScale = CGFloatMin(sizeConstraintScale, _maximumSnapshotSize.height / bitmapSize.height());
142         bitmapSize = WebCore::IntSize(CGCeiling(bitmapSize.width() * sizeConstraintScale), CGCeiling(bitmapSize.height() * sizeConstraintScale));
143     }
144
145     _lastSnapshotScale = _scale;
146     _lastSnapshotMaximumSize = _maximumSnapshotSize;
147     _webPageProxy->takeSnapshot(snapshotRect, bitmapSize, options, [thumbnailView](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
148         auto bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
149         RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
150         [thumbnailView _didTakeSnapshot:cgImage.get()];
151     });
152 }
153
154 - (void)setOverrideBackgroundColor:(NSColor *)overrideBackgroundColor
155 {
156     if ([_overrideBackgroundColor isEqual:overrideBackgroundColor])
157         return;
158
159     _overrideBackgroundColor = overrideBackgroundColor;
160     [self setNeedsDisplay:YES];
161 }
162
163 - (NSColor *)overrideBackgroundColor
164 {
165     return _overrideBackgroundColor.get();
166 }
167
168 - (void)_viewWasUnparented
169 {
170     if (!_exclusivelyUsesSnapshot) {
171         if (_wkView) {
172             [_wkView _setThumbnailView:nil];
173             [_wkView _setIgnoresAllEvents:NO];
174         } else {
175             ASSERT(_wkWebView);
176             [_wkWebView _setThumbnailView:nil];
177             [_wkWebView _setIgnoresAllEvents:NO];
178         }
179         _webPageProxy->setMayStartMediaWhenInWindow(_originalMayStartMediaWhenInWindow);
180     }
181
182     if (_shouldKeepSnapshotWhenRemovedFromSuperview)
183         return;
184
185     self.layer.contents = nil;
186     _lastSnapshotScale = NAN;
187 }
188
189 - (void)_viewWasParented
190 {
191     if (_wkView && [_wkView _thumbnailView])
192         return;
193     if (_wkWebView && [_wkWebView _thumbnailView])
194         return;
195
196     if (!_exclusivelyUsesSnapshot && !_originalSourceViewIsInWindow)
197         _webPageProxy->setMayStartMediaWhenInWindow(false);
198
199     [self _requestSnapshotIfNeeded];
200
201     if (!_exclusivelyUsesSnapshot) {
202         if (_wkView) {
203             [_wkView _setThumbnailView:self];
204             [_wkView _setIgnoresAllEvents:YES];
205         } else {
206             ASSERT(_wkWebView);
207             [_wkWebView _setThumbnailView:self];
208             [_wkWebView _setIgnoresAllEvents:YES];
209         }
210     }
211 }
212
213 - (void)_requestSnapshotIfNeeded
214 {
215     if (self.layer.contents && _lastSnapshotScale == _scale && CGSizeEqualToSize(_lastSnapshotMaximumSize, _maximumSnapshotSize))
216         return;
217
218     [self requestSnapshot];
219 }
220
221 - (void)_didTakeSnapshot:(CGImageRef)image
222 {
223     [self willChangeValueForKey:@"snapshotSize"];
224
225     _snapshotSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
226     _waitingForSnapshot = NO;
227     self.layer.sublayers = @[];
228     self.layer.contentsGravity = kCAGravityResizeAspectFill;
229     self.layer.contents = (__bridge id)image;
230
231     // If we got a scale change while snapshotting, we'll take another snapshot once the first one returns.
232     if (_snapshotWasDeferred) {
233         _snapshotWasDeferred = NO;
234         [self _requestSnapshotIfNeeded];
235     }
236
237     [self didChangeValueForKey:@"snapshotSize"];
238 }
239
240 - (void)viewDidMoveToWindow
241 {
242     if (self.window)
243         [self _viewWasParented];
244     else
245         [self _viewWasUnparented];
246 }
247
248 - (void)setScale:(CGFloat)scale
249 {
250     if (_scale == scale)
251         return;
252
253     _scale = scale;
254
255     [self _requestSnapshotIfNeeded];
256
257     self.layer.sublayerTransform = CATransform3DMakeScale(_scale, _scale, 1);
258 }
259
260 - (void)setMaximumSnapshotSize:(CGSize)maximumSnapshotSize
261 {
262     if (CGSizeEqualToSize(_maximumSnapshotSize, maximumSnapshotSize))
263         return;
264
265     _maximumSnapshotSize = maximumSnapshotSize;
266
267     [self _requestSnapshotIfNeeded];
268 }
269
270 // This should be removed when all clients go away; it is always YES now.
271 - (void)setUsesSnapshot:(BOOL)usesSnapshot
272 {
273 }
274
275 - (BOOL)usesSnapshot
276 {
277     return YES;
278 }
279
280 - (void)_setThumbnailLayer:(CALayer *)layer
281 {
282     self.layer.sublayers = layer ? @[ layer ] : @[ ];
283 }
284
285 - (CALayer *)_thumbnailLayer
286 {
287     if (!self.layer.sublayers.count)
288         return nil;
289
290     return [self.layer.sublayers objectAtIndex:0];
291 }
292
293 @end
294
295 #endif // PLATFORM(MAC)
296
297 #endif // WK_API_ENABLED