760eff7e25294926e048904e5c964e080dabc3dc
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / WKViewLayoutStrategy.mm
1 /*
2  * Copyright (C) 2015 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 "WKViewLayoutStrategy.h"
28
29 #if PLATFORM(MAC)
30
31 #import "WKViewInternal.h"
32 #import "WebPageProxy.h"
33 #import <WebCore/MachSendRight.h>
34 #import <WebCore/QuartzCoreSPI.h>
35
36 using namespace WebCore;
37 using namespace WebKit;
38
39 @interface WKViewViewSizeLayoutStrategy : WKViewLayoutStrategy
40 @end
41
42 @interface WKViewFixedSizeLayoutStrategy : WKViewLayoutStrategy
43 @end
44
45 @interface WKViewDynamicSizeComputedFromViewScaleLayoutStrategy : WKViewLayoutStrategy
46 @end
47
48 @interface WKViewDynamicSizeWithMinimumViewSizeLayoutStrategy : WKViewLayoutStrategy {
49     BOOL _isWaitingForCommit;
50     BOOL _hasPendingLayout;
51     CGFloat _lastCommittedViewScale;
52 }
53 @end
54
55 @interface WKViewDynamicSizeComputedFromMinimumDocumentSizeLayoutStrategy : WKViewLayoutStrategy
56 @end
57
58 @implementation WKViewLayoutStrategy
59
60 + (instancetype)layoutStrategyWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
61 {
62     WKViewLayoutStrategy *strategy;
63
64     switch (mode) {
65     case kWKLayoutModeFixedSize:
66         strategy = [[WKViewFixedSizeLayoutStrategy alloc] initWithPage:page view:wkView mode:mode];
67         break;
68     case kWKLayoutModeDynamicSizeComputedFromViewScale:
69         strategy = [[WKViewDynamicSizeComputedFromViewScaleLayoutStrategy alloc] initWithPage:page view:wkView mode:mode];
70         break;
71     case kWKLayoutModeDynamicSizeWithMinimumViewSize:
72         strategy = [[WKViewDynamicSizeWithMinimumViewSizeLayoutStrategy alloc] initWithPage:page view:wkView mode:mode];
73         break;
74     case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
75         strategy = [[WKViewDynamicSizeComputedFromMinimumDocumentSizeLayoutStrategy alloc] initWithPage:page view:wkView mode:mode];
76         break;
77     case kWKLayoutModeViewSize:
78     default:
79         strategy = [[WKViewViewSizeLayoutStrategy alloc] initWithPage:page view:wkView mode:mode];
80         break;
81     }
82
83     [strategy updateLayout];
84
85     return [strategy autorelease];
86 }
87
88 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
89 {
90     self = [super init];
91
92     if (!self)
93         return nil;
94
95     _page = &page;
96     _wkView = wkView;
97     _layoutMode = mode;
98
99     return self;
100 }
101
102 - (void)willDestroyView:(WKView *)view
103 {
104     _page = nullptr;
105     _wkView = nil;
106 }
107
108 - (WKLayoutMode)layoutMode
109 {
110     return _layoutMode;
111 }
112
113 - (void)updateLayout
114 {
115 }
116
117 - (void)disableFrameSizeUpdates
118 {
119     _frameSizeUpdatesDisabledCount++;
120 }
121
122 - (void)enableFrameSizeUpdates
123 {
124     if (!_frameSizeUpdatesDisabledCount)
125         return;
126
127     if (!(--_frameSizeUpdatesDisabledCount))
128         [self didChangeFrameSize];
129 }
130
131 - (BOOL)frameSizeUpdatesDisabled
132 {
133     return _frameSizeUpdatesDisabledCount > 0;
134 }
135
136 - (void)didChangeViewScale
137 {
138 }
139
140 - (void)didChangeMinimumViewSize
141 {
142 }
143
144 - (void)willStartLiveResize
145 {
146 }
147
148 - (void)didEndLiveResize
149 {
150 }
151
152 - (void)didChangeFrameSize
153 {
154     if ([self frameSizeUpdatesDisabled])
155         return;
156
157     if (_wkView.shouldClipToVisibleRect)
158         [_wkView _updateViewExposedRect];
159     [_wkView _setDrawingAreaSize:_wkView.frame.size];
160 }
161
162 - (void)willChangeLayoutStrategy
163 {
164 }
165
166 @end
167
168 @implementation WKViewViewSizeLayoutStrategy
169
170 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
171 {
172     self = [super initWithPage:page view:wkView mode:mode];
173
174     if (!self)
175         return nil;
176
177     page.setUseFixedLayout(false);
178
179     return self;
180 }
181
182 - (void)updateLayout
183 {
184 }
185
186 @end
187
188 @implementation WKViewFixedSizeLayoutStrategy
189
190 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
191 {
192     self = [super initWithPage:page view:wkView mode:mode];
193
194     if (!self)
195         return nil;
196
197     page.setUseFixedLayout(true);
198
199     return self;
200 }
201
202 - (void)updateLayout
203 {
204 }
205
206 @end
207
208 @implementation WKViewDynamicSizeComputedFromViewScaleLayoutStrategy
209
210 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
211 {
212     self = [super initWithPage:page view:wkView mode:mode];
213
214     if (!self)
215         return nil;
216
217     page.setUseFixedLayout(true);
218
219     return self;
220 }
221
222 - (void)updateLayout
223 {
224     CGFloat inverseScale = 1 / _page->viewScaleFactor();
225     [_wkView _setFixedLayoutSize:CGSizeMake(_wkView.frame.size.width * inverseScale, _wkView.frame.size.height * inverseScale)];
226 }
227
228 - (void)didChangeViewScale
229 {
230     [super didChangeViewScale];
231
232     [self updateLayout];
233 }
234
235 - (void)didChangeFrameSize
236 {
237     [super didChangeFrameSize];
238
239     if ([_wkView frameSizeUpdatesDisabled])
240         return;
241
242     [self updateLayout];
243 }
244
245 @end
246
247 @implementation WKViewDynamicSizeWithMinimumViewSizeLayoutStrategy
248
249 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
250 {
251     self = [super initWithPage:page view:wkView mode:mode];
252
253     if (!self)
254         return nil;
255
256     page.setUseFixedLayout(true);
257     _lastCommittedViewScale = _page->viewScaleFactor();
258
259     return self;
260 }
261
262 - (void)_updateTransientScale:(CGFloat)scale
263 {
264     float topContentInset = _page->topContentInset();
265
266     CGFloat relativeScale = scale / _lastCommittedViewScale;
267
268     CATransform3D transform = CATransform3DMakeTranslation(0, topContentInset - (topContentInset * relativeScale), 0);
269     transform = CATransform3DScale(transform, relativeScale, relativeScale, 1);
270
271     _wkView._rootLayer.transform = transform;
272 }
273
274 - (void)updateLayout
275 {
276     _hasPendingLayout = NO;
277
278     CGFloat scale = 1;
279
280     CGFloat minimumViewWidth = _wkView._minimumViewSize.width;
281     CGFloat minimumViewHeight = _wkView._minimumViewSize.height;
282
283     CGFloat fixedLayoutWidth = _wkView.frame.size.width;
284     CGFloat fixedLayoutHeight = _wkView.frame.size.height;
285
286     if (NSIsEmptyRect(_wkView.frame))
287         return;
288
289     if (_wkView.frame.size.width < minimumViewWidth) {
290         scale = _wkView.frame.size.width / minimumViewWidth;
291         fixedLayoutWidth = minimumViewWidth;
292     }
293
294     if (_wkView.frame.size.height < minimumViewHeight) {
295         scale = std::min(_wkView.frame.size.height / minimumViewHeight, scale);
296         fixedLayoutWidth = minimumViewHeight;
297     }
298
299     _page->setFixedLayoutSize(IntSize(fixedLayoutWidth, fixedLayoutHeight));
300
301     [self _updateTransientScale:scale];
302
303     if (_isWaitingForCommit) {
304         _hasPendingLayout = YES;
305         return;
306     }
307
308     if ([_wkView inLiveResize] && _lastCommittedViewScale == 1 && scale == 1 && _frameSizeUpdatesDisabledCount == 1) {
309         // Send frame size updates if we're the only ones disabling them,
310         // if we're not scaling down. That way, everything will behave like a normal
311         // resize except in the critical section.
312         if (_wkView.shouldClipToVisibleRect)
313             [_wkView _updateViewExposedRect];
314         [_wkView _setDrawingAreaSize:[_wkView frame].size];
315         return;
316     }
317
318     if (_lastCommittedViewScale == scale)
319         return;
320
321     _isWaitingForCommit = YES;
322
323 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
324     RetainPtr<CAContext> context = [_wkView.layer context];
325     RetainPtr<WKViewDynamicSizeWithMinimumViewSizeLayoutStrategy> retainedSelf = self;
326     _page->scaleViewAndUpdateGeometryFenced(scale, IntSize(_wkView.frame.size), [retainedSelf, context, scale] (const WebCore::MachSendRight& fencePort, CallbackBase::Error error) {
327         if (error != CallbackBase::Error::None)
328             return;
329
330         [context setFencePort:fencePort.sendRight() commitHandler:[retainedSelf, scale] {
331             WKViewDynamicSizeWithMinimumViewSizeLayoutStrategy *layoutStrategy = retainedSelf.get();
332             layoutStrategy->_lastCommittedViewScale = scale;
333             [layoutStrategy _updateTransientScale:scale];
334             layoutStrategy->_isWaitingForCommit = NO;
335
336             if (layoutStrategy->_hasPendingLayout)
337                 [layoutStrategy updateLayout];
338         }];
339     });
340 #else
341     _page->scaleView(scale);
342     _wkView._rootLayer.transform = CATransform3DIdentity;
343 #endif
344 }
345
346 - (void)didChangeMinimumViewSize
347 {
348     [super didChangeMinimumViewSize];
349
350     [self updateLayout];
351 }
352
353 - (void)willStartLiveResize
354 {
355     [super willStartLiveResize];
356
357     [_wkView disableFrameSizeUpdates];
358 }
359
360 - (void)didEndLiveResize
361 {
362     [super didEndLiveResize];
363
364     [self updateLayout];
365     [_wkView enableFrameSizeUpdates];
366 }
367
368 - (void)didChangeFrameSize
369 {
370     [super didChangeFrameSize];
371
372     [self updateLayout];
373 }
374
375 - (void)willChangeLayoutStrategy
376 {
377     _wkView._rootLayer.transform = CATransform3DIdentity;
378     _page->scaleView(1);
379 }
380
381 @end
382
383 @implementation WKViewDynamicSizeComputedFromMinimumDocumentSizeLayoutStrategy
384
385 - (instancetype)initWithPage:(WebPageProxy&)page view:(WKView *)wkView mode:(WKLayoutMode)mode
386 {
387     self = [super initWithPage:page view:wkView mode:mode];
388
389     if (!self)
390         return nil;
391
392     _page->setShouldScaleViewToFitDocument(true);
393
394     return self;
395 }
396
397 - (void)updateLayout
398 {
399 }
400
401 - (void)willChangeLayoutStrategy
402 {
403     _page->setShouldScaleViewToFitDocument(false);
404 }
405
406 @end
407
408 #endif // PLATFORM(MAC)