[iOS] Synchronize the WKContentView and UIScrollView updates with the tiles being...
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / ios / WKContentView.mm
1 /*
2  * Copyright (C) 2013 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 "WKContentViewInternal.h"
28
29 #import "InteractionInformationAtPosition.h"
30 #import "PageClientImplIOS.h"
31 #import "RemoteLayerTreeDrawingAreaProxy.h"
32 #import "RemoteScrollingCoordinatorProxy.h"
33 #import "WebKit2Initialize.h"
34 #import "WKBrowsingContextControllerInternal.h"
35 #import "WKBrowsingContextGroupPrivate.h"
36 #import "WKGeolocationProviderIOS.h"
37 #import "WKInteractionView.h"
38 #import "WKProcessGroupPrivate.h"
39 #import "WKProcessClassInternal.h"
40 #import "WKWebViewConfiguration.h"
41 #import "WebContext.h"
42 #import "WebFrameProxy.h"
43 #import "WebPageGroup.h"
44 #import "WebSystemInterface.h"
45 #import <UIKit/UIWindow_Private.h>
46 #import <WebCore/ViewportArguments.h>
47 #import <wtf/RetainPtr.h>
48
49 #if __has_include(<QuartzCore/QuartzCorePrivate.h>)
50 #import <QuartzCore/QuartzCorePrivate.h>
51 #endif
52
53 @interface CALayer (Details)
54 @property BOOL hitTestsAsOpaque;
55 @end
56
57 using namespace WebCore;
58 using namespace WebKit;
59
60 @implementation WKContentView {
61     std::unique_ptr<PageClientImpl> _pageClient;
62     RetainPtr<WKBrowsingContextController> _browsingContextController;
63
64     RetainPtr<UIView> _rootContentView;
65     RetainPtr<WKInteractionView> _interactionView;
66
67     WebCore::FloatPoint _currentExposedRectPosition;
68 }
69
70 - (id)initWithFrame:(CGRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef
71 {
72     return [self initWithFrame:frame contextRef:contextRef pageGroupRef:pageGroupRef relatedToPage:nullptr];
73 }
74
75 - (id)initWithFrame:(CGRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef relatedToPage:(WKPageRef)relatedPage
76 {
77     if (!(self = [super initWithFrame:frame]))
78         return nil;
79
80     [self _commonInitializationWithContextRef:contextRef pageGroupRef:pageGroupRef relatedToPage:relatedPage];
81     return self;
82 }
83
84 - (id)initWithFrame:(CGRect)frame processGroup:(WKProcessGroup *)processGroup browsingContextGroup:(WKBrowsingContextGroup *)browsingContextGroup
85 {
86     if (!(self = [super initWithFrame:frame]))
87         return nil;
88
89     [self _commonInitializationWithContextRef:processGroup._contextRef pageGroupRef:browsingContextGroup._pageGroupRef relatedToPage:nullptr];
90     return self;
91 }
92
93 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
94 {
95     if (!(self = [super initWithFrame:frame]))
96         return nil;
97
98     InitializeWebKit2();
99     RunLoop::initializeMainRunLoop();
100
101     _pageClient = std::make_unique<PageClientImpl>(self);
102
103     _page = configuration.processClass->_context->createWebPage(*_pageClient, nullptr);
104     _page->initializeWebPage();
105     _page->setIntrinsicDeviceScaleFactor([UIScreen mainScreen].scale);
106     _page->setUseFixedLayout(true);
107
108     WebContext::statistics().wkViewCount++;
109
110     _rootContentView = adoptNS([[UIView alloc] init]);
111     [_rootContentView layer].masksToBounds = NO;
112     [_rootContentView setUserInteractionEnabled:NO];
113
114     [self addSubview:_rootContentView.get()];
115
116     _interactionView = adoptNS([[WKInteractionView alloc] init]);
117     [_interactionView setPage:_page];
118     [self addSubview:_interactionView.get()];
119
120     self.layer.hitTestsAsOpaque = YES;
121
122     return self;
123 }
124
125 - (void)dealloc
126 {
127     _page->close();
128
129     WebContext::statistics().wkViewCount--;
130
131     [super dealloc];
132 }
133
134 - (void)willMoveToWindow:(UIWindow *)newWindow
135 {
136     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
137     UIWindow *window = self.window;
138
139     if (window)
140         [defaultCenter removeObserver:self name:UIWindowDidMoveToScreenNotification object:window];
141
142     if (newWindow)
143         [defaultCenter addObserver:self selector:@selector(_windowDidMoveToScreenNotification:) name:UIWindowDidMoveToScreenNotification object:newWindow];
144 }
145
146 - (void)didMoveToWindow
147 {
148     if (self.window)
149         [self _updateForScreen:self.window.screen];
150     _page->viewStateDidChange(ViewState::IsInWindow);
151 }
152
153 - (WKBrowsingContextController *)browsingContextController
154 {
155     if (!_browsingContextController)
156         _browsingContextController = [[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_page.get())];
157
158     return _browsingContextController.get();
159 }
160
161 - (WKContentType)contentType
162 {
163     if (_page->mainFrame()->mimeType() == "text/plain")
164         return WKContentType::PlainText;
165     else if (_page->mainFrame()->isDisplayingStandaloneImageDocument())
166         return WKContentType::Image;
167     return WKContentType::Standard;
168 }
169
170 - (WKPageRef)_pageRef
171 {
172     return toAPI(_page.get());
173 }
174
175 - (void)setMinimumSize:(CGSize)size
176 {
177     _page->drawingArea()->setSize(IntSize(size), IntSize(), IntSize());
178 }
179
180 - (FloatRect)fixedPositionRectFromExposedRect:(FloatRect)exposedRect scale:(float)scale
181 {
182     // FIXME: This should modify the rect based on the scale.
183     UNUSED_PARAM(scale);
184     return exposedRect;
185 }
186
187 - (void)_updateViewExposedRect
188 {
189     FloatPoint exposedRectPosition = _currentExposedRectPosition;
190     exposedRectPosition.scale(_page->pageScaleFactor(), _page->pageScaleFactor());
191
192     if (auto drawingArea = _page->drawingArea())
193         drawingArea->setExposedRect(FloatRect(exposedRectPosition, _page->drawingArea()->size()));
194 }
195
196 - (void)_updateFixedPositionRect
197 {
198     auto drawingArea = _page->drawingArea();
199     if (!drawingArea)
200         return;
201     FloatRect exposedRect(_currentExposedRectPosition, drawingArea->size());
202     FloatRect fixedPosRect = [self fixedPositionRectFromExposedRect:exposedRect scale:_page->pageScaleFactor()];
203     drawingArea->setCustomFixedPositionRect(fixedPosRect);
204 }
205
206 - (void)setViewportSize:(CGSize)size
207 {
208     _page->setFixedLayoutSize(IntSize(size));
209     [self _updateViewExposedRect];
210 }
211
212 - (void)setMinimumLayoutSize:(CGSize)size
213 {
214     _page->setViewportConfigurationMinimumLayoutSize(IntSize(CGCeiling(size.width), CGCeiling(size.height)));
215 }
216
217 - (void)didFinishScrollTo:(CGPoint)contentOffset
218 {
219     _currentExposedRectPosition = contentOffset;
220     _page->didFinishScrolling(contentOffset);
221     [self _updateViewExposedRect];
222     [self _updateFixedPositionRect];
223     [_interactionView _didEndScrollingOrZooming];
224 }
225
226 - (void)didScrollTo:(CGPoint)contentOffset
227 {
228     _currentExposedRectPosition = contentOffset;
229     [self _updateViewExposedRect];
230
231     _page->scrollingCoordinatorProxy()->scrollPositionChangedViaDelegatedScrolling(_page->scrollingCoordinatorProxy()->rootScrollingNodeID(), roundedIntPoint(contentOffset));
232 }
233
234 - (void)willStartZoomOrScroll
235 {
236     [_interactionView _willStartScrollingOrZooming];
237 }
238
239 - (void)willStartUserTriggeredZoom
240 {
241     _page->willStartUserTriggeredZooming();
242 }
243
244 - (void)didZoomToScale:(CGFloat)scale
245 {
246     _page->didFinishZooming(scale);
247     [self _updateViewExposedRect];
248     [self _updateFixedPositionRect];
249     [_interactionView _didEndScrollingOrZooming];
250 }
251
252 #pragma mark Internal
253
254 - (void)_commonInitializationWithContextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef relatedToPage:(WKPageRef)relatedPage
255 {
256     InitializeWebKit2();
257     RunLoop::initializeMainRunLoop();
258
259     _pageClient = std::make_unique<PageClientImpl>(self);
260     _page = toImpl(contextRef)->createWebPage(*_pageClient, toImpl(pageGroupRef), toImpl(relatedPage));
261     _page->initializeWebPage();
262     _page->setIntrinsicDeviceScaleFactor([UIScreen mainScreen].scale);
263     _page->setUseFixedLayout(true);
264
265     WebContext::statistics().wkViewCount++;
266
267     _rootContentView = adoptNS([[UIView alloc] init]);
268     [[_rootContentView layer] setMasksToBounds:NO];
269     [_rootContentView setUserInteractionEnabled:NO];
270
271     [self addSubview:_rootContentView.get()];
272
273     _interactionView = adoptNS([[WKInteractionView alloc] init]);
274     [_interactionView setPage:_page];
275     [self addSubview:_interactionView.get()];
276
277     self.layer.hitTestsAsOpaque = YES;
278 }
279
280 - (void)_windowDidMoveToScreenNotification:(NSNotification *)notification
281 {
282     ASSERT(notification.object == self.window);
283
284     UIScreen *screen = notification.userInfo[UIWindowNewScreenUserInfoKey];
285     [self _updateForScreen:screen];
286 }
287
288 - (void)_updateForScreen:(UIScreen *)screen
289 {
290     ASSERT(screen);
291     _page->setIntrinsicDeviceScaleFactor(screen.scale);
292 }
293
294 #pragma mark PageClientImpl methods
295
296 - (std::unique_ptr<DrawingAreaProxy>)_createDrawingAreaProxy
297 {
298     return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(_page.get());
299 }
300
301 - (void)_processDidExit
302 {
303     // FIXME: Implement.
304 }
305
306 - (void)_didRelaunchProcess
307 {
308     // FIXME: Implement.
309 }
310
311 - (void)_didCommitLoadForMainFrame
312 {
313     if ([_delegate respondsToSelector:@selector(contentViewDidCommitLoadForMainFrame:)])
314         [_delegate contentViewDidCommitLoadForMainFrame:self];
315 }
316
317 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
318 {
319     CGSize contentsSize = layerTreeTransaction.contentsSize();
320
321     [self setBounds:{CGPointZero, contentsSize}];
322     [_interactionView setFrame:CGRectMake(0, 0, contentsSize.width, contentsSize.height)];
323     [_rootContentView setFrame:CGRectMake(0, 0, contentsSize.width, contentsSize.height)];
324
325     if ([_delegate respondsToSelector:@selector(contentView:didCommitLayerTree:)])
326         [_delegate contentView:self didCommitLayerTree:layerTreeTransaction];
327 }
328
329 - (void)_didGetTapHighlightForRequest:(uint64_t)requestID color:(const Color&)color quads:(const Vector<FloatQuad>&)highlightedQuads topLeftRadius:(const IntSize&)topLeftRadius topRightRadius:(const IntSize&)topRightRadius bottomLeftRadius:(const IntSize&)bottomLeftRadius bottomRightRadius:(const IntSize&)bottomRightRadius
330 {
331     [_interactionView _didGetTapHighlightForRequest:requestID color:color quads:highlightedQuads topLeftRadius:topLeftRadius topRightRadius:topRightRadius bottomLeftRadius:bottomLeftRadius bottomRightRadius:bottomRightRadius];
332 }
333
334 - (void)_setAcceleratedCompositingRootLayer:(CALayer *)rootLayer
335 {
336     [[_rootContentView layer] setSublayers:@[rootLayer]];
337 }
338
339 // FIXME: change the name. Leave it for now to make it easier to refer to the UIKit implementation.
340 - (void)_startAssistingNode
341 {
342     [_interactionView _startAssistingNode];
343 }
344
345 - (void)_stopAssistingNode
346 {
347     [_interactionView _stopAssistingNode];
348 }
349
350 - (void)_selectionChanged
351 {
352     [_interactionView _selectionChanged];
353 }
354
355 - (BOOL)_interpretKeyEvent:(WebIOSEvent *)theEvent isCharEvent:(BOOL)isCharEvent
356 {
357     return [_interactionView _interpretKeyEvent:theEvent isCharEvent:isCharEvent];
358 }
359
360 - (void)_positionInformationDidChange:(const InteractionInformationAtPosition&)info
361 {
362     [_interactionView _positionInformationDidChange:info];
363 }
364
365 - (void)_decidePolicyForGeolocationRequestFromOrigin:(WebSecurityOrigin&)origin frame:(WebFrameProxy&)frame request:(GeolocationPermissionRequestProxy&)permissionRequest
366 {
367     // FIXME: The line below is commented out since wrapper(WebContext&) now returns a WKProcessClass.
368     // As part of the new API we should figure out where geolocation fits in, see <rdar://problem/15885544>.
369     // [[wrapper(_page->process().context()) _geolocationProvider] decidePolicyForGeolocationRequestFromOrigin:toAPI(&origin) frame:toAPI(&frame) request:toAPI(&permissionRequest) window:[self window]];
370 }
371
372 @end