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