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