52362f2c11082c619cc47824fdb27cb2c9b539ed
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / RemoteLayerTreeDrawingAreaProxy.mm
1 /*
2  * Copyright (C) 2012-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 "RemoteLayerTreeDrawingAreaProxy.h"
28
29 #import "Logging.h"
30 #import "RemoteLayerTreeDrawingAreaProxyMessages.h"
31 #import "DrawingAreaMessages.h"
32 #import "RemoteScrollingCoordinatorProxy.h"
33 #import "RemoteScrollingCoordinatorTransaction.h"
34 #import "WebPageProxy.h"
35 #import "WebProcessProxy.h"
36 #import <QuartzCore/QuartzCore.h>
37 #import <WebCore/IOSurfacePool.h>
38 #import <WebCore/WebActionDisablingCALayerDelegate.h>
39
40 using namespace IPC;
41 using namespace WebCore;
42
43 // FIXME: Mac will need something similar; we should figure out how to share this with DisplayRefreshMonitor without
44 // breaking WebKit1 behavior or WebKit2-WebKit1 coexistence.
45 #if PLATFORM(IOS)
46 @interface OneShotDisplayLinkHandler : NSObject {
47     WebKit::RemoteLayerTreeDrawingAreaProxy* _drawingAreaProxy;
48     CADisplayLink *_displayLink;
49 }
50
51 - (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy;
52 - (void)displayLinkFired:(CADisplayLink *)sender;
53 - (void)invalidate;
54 - (void)schedule;
55
56 @end
57
58 @implementation OneShotDisplayLinkHandler
59
60 - (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy
61 {
62     if (self = [super init]) {
63         _drawingAreaProxy = drawingAreaProxy;
64         // Note that CADisplayLink retains its target (self), so a call to -invalidate is needed on teardown.
65         _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFired:)];
66         [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
67         _displayLink.paused = YES;
68     }
69     return self;
70 }
71
72 - (void)dealloc
73 {
74     ASSERT(!_displayLink);
75     [super dealloc];
76 }
77
78 - (void)displayLinkFired:(CADisplayLink *)sender
79 {
80     ASSERT(isUIThread());
81     _drawingAreaProxy->didRefreshDisplay(sender.timestamp);
82     _displayLink.paused = YES;
83 }
84
85 - (void)invalidate
86 {
87     [_displayLink invalidate];
88     _displayLink = nullptr;
89 }
90
91 - (void)schedule
92 {
93     _displayLink.paused = NO;
94 }
95
96 @end
97 #endif
98
99 namespace WebKit {
100
101 RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy(WebPageProxy* webPageProxy)
102     : DrawingAreaProxy(DrawingAreaTypeRemoteLayerTree, webPageProxy)
103     , m_remoteLayerTreeHost(*this)
104     , m_isWaitingForDidUpdateGeometry(false)
105     , m_pendingLayerTreeTransactionID(0)
106     , m_lastVisibleTransactionID(0)
107     , m_transactionIDForPendingCACommit(0)
108 #if PLATFORM(IOS)
109     , m_displayLinkHandler(adoptNS([[OneShotDisplayLinkHandler alloc] initWithDrawingAreaProxy:this]))
110 #endif
111 {
112 #if USE(IOSURFACE)
113     // We don't want to pool surfaces in the UI process.
114     // FIXME: We should do this somewhere else.
115     IOSurfacePool::sharedPool().setPoolSize(0);
116 #endif
117
118     m_webPageProxy->process().addMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy->pageID(), *this);
119
120     if (m_webPageProxy->preferences().tiledScrollingIndicatorVisible())
121         initializeDebugIndicator();
122 }
123
124 RemoteLayerTreeDrawingAreaProxy::~RemoteLayerTreeDrawingAreaProxy()
125 {
126     m_callbacks.invalidate(CallbackBase::Error::OwnerWasInvalidated);
127     m_webPageProxy->process().removeMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy->pageID());
128
129 #if PLATFORM(IOS)
130     [m_displayLinkHandler invalidate];
131 #endif
132 }
133
134 void RemoteLayerTreeDrawingAreaProxy::sizeDidChange()
135 {
136     if (!m_webPageProxy->isValid())
137         return;
138
139     if (m_isWaitingForDidUpdateGeometry)
140         return;
141
142     sendUpdateGeometry();
143 }
144
145 void RemoteLayerTreeDrawingAreaProxy::deviceScaleFactorDidChange()
146 {
147     m_webPageProxy->process().send(Messages::DrawingArea::SetDeviceScaleFactor(m_webPageProxy->deviceScaleFactor()), m_webPageProxy->pageID());
148 }
149
150 void RemoteLayerTreeDrawingAreaProxy::didUpdateGeometry()
151 {
152     ASSERT(m_isWaitingForDidUpdateGeometry);
153
154     m_isWaitingForDidUpdateGeometry = false;
155
156     // If the WKView was resized while we were waiting for a DidUpdateGeometry reply from the web process,
157     // we need to resend the new size here.
158     if (m_lastSentSize != m_size || m_lastSentLayerPosition != m_layerPosition)
159         sendUpdateGeometry();
160 }
161
162 FloatRect RemoteLayerTreeDrawingAreaProxy::scaledExposedRect() const
163 {
164 #if PLATFORM(IOS)
165     return m_webPageProxy->exposedContentRect();
166 #else
167     FloatRect scaledExposedRect = exposedRect();
168     float scale = 1 / m_webPageProxy->pageScaleFactor();
169     scaledExposedRect.scale(scale, scale);
170     return scaledExposedRect;
171 #endif
172 }
173
174 void RemoteLayerTreeDrawingAreaProxy::sendUpdateGeometry()
175 {
176     m_lastSentSize = m_size;
177     m_lastSentLayerPosition = m_layerPosition;
178     m_webPageProxy->process().send(Messages::DrawingArea::UpdateGeometry(m_size, m_layerPosition), m_webPageProxy->pageID());
179     m_isWaitingForDidUpdateGeometry = true;
180 }
181
182 void RemoteLayerTreeDrawingAreaProxy::willCommitLayerTree(uint64_t transactionID)
183 {
184     m_pendingLayerTreeTransactionID = transactionID;
185 }
186
187 void RemoteLayerTreeDrawingAreaProxy::commitLayerTree(const RemoteLayerTreeTransaction& layerTreeTransaction, const RemoteScrollingCoordinatorTransaction& scrollingTreeTransaction)
188 {
189     LOG(RemoteLayerTree, "%s", layerTreeTransaction.description().data());
190     LOG(RemoteLayerTree, "%s", scrollingTreeTransaction.description().data());
191
192     ASSERT(layerTreeTransaction.transactionID() == m_lastVisibleTransactionID + 1);
193     m_transactionIDForPendingCACommit = layerTreeTransaction.transactionID();
194
195     for (auto& callbackID : layerTreeTransaction.callbackIDs()) {
196         if (auto callback = m_callbacks.take<VoidCallback>(callbackID))
197             callback->performCallback();
198     }
199
200     if (m_remoteLayerTreeHost.updateLayerTree(layerTreeTransaction))
201         m_webPageProxy->setAcceleratedCompositingRootLayer(m_remoteLayerTreeHost.rootLayer());
202
203 #if PLATFORM(IOS)
204     m_webPageProxy->didCommitLayerTree(layerTreeTransaction);
205 #endif
206
207 #if ENABLE(ASYNC_SCROLLING)
208     m_webPageProxy->scrollingCoordinatorProxy()->updateScrollingTree(scrollingTreeTransaction);
209
210 #if PLATFORM(IOS)
211     if (m_webPageProxy->scrollingCoordinatorProxy()->hasFixedOrSticky()) {
212         FloatRect customFixedPositionRect = m_webPageProxy->computeCustomFixedPositionRect(m_webPageProxy->unobscuredContentRect(), m_webPageProxy->displayedContentScale());
213         // If we got a new layer for a fixed or sticky node, its position from the WebProcess is probably stale. We need to re-run the "viewport" changed logic to udpate it with our UI-side state.
214         m_webPageProxy->scrollingCoordinatorProxy()->viewportChangedViaDelegatedScrolling(m_webPageProxy->scrollingCoordinatorProxy()->rootScrollingNodeID(), customFixedPositionRect, m_webPageProxy->displayedContentScale());
215     }
216 #endif
217 #endif // ENABLE(ASYNC_SCROLLING)
218
219     if (m_debugIndicatorLayerTreeHost) {
220         float scale = indicatorScale(layerTreeTransaction.contentsSize());
221         bool rootLayerChanged = m_debugIndicatorLayerTreeHost->updateLayerTree(layerTreeTransaction, scale);
222         updateDebugIndicator(layerTreeTransaction.contentsSize(), rootLayerChanged, scale);
223         asLayer(m_debugIndicatorLayerTreeHost->rootLayer()).name = @"Indicator host root";
224     }
225
226 #if PLATFORM(IOS)
227     [m_displayLinkHandler schedule];
228 #else
229     didRefreshDisplay(monotonicallyIncreasingTime());
230 #endif
231 }
232
233 void RemoteLayerTreeDrawingAreaProxy::acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime)
234 {
235     m_webPageProxy->process().send(Messages::DrawingArea::AcceleratedAnimationDidStart(layerID, key, startTime), m_webPageProxy->pageID());
236 }
237
238 static const float indicatorInset = 10;
239
240 #if PLATFORM(MAC)
241 void RemoteLayerTreeDrawingAreaProxy::setExposedRect(const WebCore::FloatRect& r)
242 {
243     DrawingAreaProxy::setExposedRect(r);
244     updateDebugIndicatorPosition();
245 }
246 #endif
247
248 FloatPoint RemoteLayerTreeDrawingAreaProxy::indicatorLocation() const
249 {
250     if (m_webPageProxy->delegatesScrolling()) {
251 #if PLATFORM(IOS)
252         FloatPoint tiledMapLocation = m_webPageProxy->unobscuredContentRect().location();
253         float absoluteInset = indicatorInset / m_webPageProxy->displayedContentScale();
254         tiledMapLocation += FloatSize(absoluteInset, absoluteInset);
255 #else
256         FloatPoint tiledMapLocation = exposedRect().location();
257         tiledMapLocation += FloatSize(indicatorInset, indicatorInset);
258         float scale = 1 / m_webPageProxy->pageScaleFactor();;
259         tiledMapLocation.scale(scale, scale);
260 #endif
261         return tiledMapLocation;
262     }
263     
264     return FloatPoint(indicatorInset, indicatorInset);
265 }
266
267 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicatorPosition()
268 {
269     if (!m_tileMapHostLayer)
270         return;
271
272     [m_tileMapHostLayer setPosition:indicatorLocation()];
273 }
274
275 float RemoteLayerTreeDrawingAreaProxy::indicatorScale(IntSize contentsSize) const
276 {
277     // Pick a good scale.
278     IntSize viewSize = m_webPageProxy->viewSize();
279
280     float scale = 1;
281     if (!contentsSize.isEmpty()) {
282         float widthScale = std::min<float>((viewSize.width() - 2 * indicatorInset) / contentsSize.width(), 0.05);
283         scale = std::min(widthScale, static_cast<float>(viewSize.height() - 2 * indicatorInset) / contentsSize.height());
284     }
285     
286     return scale;
287 }
288
289 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator()
290 {
291     // FIXME: we should also update live information during scale.
292     updateDebugIndicatorPosition();
293 }
294
295 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator(IntSize contentsSize, bool rootLayerChanged, float scale)
296 {
297     // Make sure we're the last sublayer.
298     CALayer *rootLayer = asLayer(m_remoteLayerTreeHost.rootLayer());
299     [m_tileMapHostLayer removeFromSuperlayer];
300     [rootLayer addSublayer:m_tileMapHostLayer.get()];
301
302     // Pick a good scale.
303     IntSize viewSize = m_webPageProxy->viewSize();
304
305     [m_tileMapHostLayer setBounds:FloatRect(FloatPoint(), contentsSize)];
306     [m_tileMapHostLayer setPosition:indicatorLocation()];
307     [m_tileMapHostLayer setTransform:CATransform3DMakeScale(scale, scale, 1)];
308
309     if (rootLayerChanged) {
310         [m_tileMapHostLayer setSublayers:@[]];
311         [m_tileMapHostLayer addSublayer:asLayer(m_debugIndicatorLayerTreeHost->rootLayer())];
312         [m_tileMapHostLayer addSublayer:m_exposedRectIndicatorLayer.get()];
313     }
314     
315     const float indicatorBorderWidth = 1;
316     float counterScaledBorder = indicatorBorderWidth / scale;
317
318     [m_exposedRectIndicatorLayer setBorderWidth:counterScaledBorder];
319
320     if (m_webPageProxy->delegatesScrolling()) {
321         FloatRect scaledExposedRect = this->scaledExposedRect();
322         [m_exposedRectIndicatorLayer setPosition:scaledExposedRect.location()];
323         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), scaledExposedRect.size())];
324     } else {
325         // FIXME: Get the correct scroll position.
326         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), viewSize)];
327     }
328 }
329
330 void RemoteLayerTreeDrawingAreaProxy::initializeDebugIndicator()
331 {
332     m_debugIndicatorLayerTreeHost = std::make_unique<RemoteLayerTreeHost>(*this);
333     m_debugIndicatorLayerTreeHost->setIsDebugLayerTreeHost(true);
334
335     m_tileMapHostLayer = adoptNS([[CALayer alloc] init]);
336     [m_tileMapHostLayer setName:@"Tile map host"];
337     [m_tileMapHostLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
338     [m_tileMapHostLayer setAnchorPoint:CGPointZero];
339     [m_tileMapHostLayer setOpacity:0.8];
340     [m_tileMapHostLayer setMasksToBounds:YES];
341     [m_tileMapHostLayer setBorderWidth:2];
342
343     RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
344     {
345         const CGFloat components[] = { 1, 1, 1, 0.6 };
346         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
347         [m_tileMapHostLayer setBackgroundColor:color.get()];
348
349         const CGFloat borderComponents[] = { 0, 0, 0, 1 };
350         RetainPtr<CGColorRef> borderColor = adoptCF(CGColorCreate(colorSpace.get(), borderComponents));
351         [m_tileMapHostLayer setBorderColor:borderColor.get()];
352     }
353     
354     m_exposedRectIndicatorLayer = adoptNS([[CALayer alloc] init]);
355     [m_exposedRectIndicatorLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
356     [m_exposedRectIndicatorLayer setAnchorPoint:CGPointZero];
357
358     {
359         const CGFloat components[] = { 0, 1, 0, 1 };
360         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
361         [m_exposedRectIndicatorLayer setBorderColor:color.get()];
362     }
363 }
364
365 void RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay(double)
366 {
367     if (!m_webPageProxy->isValid())
368         return;
369
370     // Waiting for CA to commit is insufficient, because the render server can still be
371     // using our backing store. We can improve this by waiting for the render server to commit
372     // if we find API to do so, but for now we will make extra buffers if need be.
373     m_webPageProxy->process().send(Messages::DrawingArea::DidUpdate(), m_webPageProxy->pageID());
374
375     m_lastVisibleTransactionID = m_transactionIDForPendingCACommit;
376
377     m_webPageProxy->didUpdateViewState();
378 }
379
380 void RemoteLayerTreeDrawingAreaProxy::waitForDidUpdateViewState()
381 {
382 #if PLATFORM(IOS)
383     auto viewStateUpdateTimeout = std::chrono::milliseconds(500);
384 #else
385     auto viewStateUpdateTimeout = std::chrono::milliseconds(250);
386 #endif
387     m_webPageProxy->process().connection()->waitForAndDispatchImmediately<Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree>(m_webPageProxy->pageID(), viewStateUpdateTimeout, InterruptWaitingIfSyncMessageArrives);
388 }
389
390 void RemoteLayerTreeDrawingAreaProxy::dispatchAfterEnsuringDrawing(std::function<void (CallbackBase::Error)> callbackFunction)
391 {
392     if (!m_webPageProxy->isValid()) {
393         callbackFunction(CallbackBase::Error::OwnerWasInvalidated);
394         return;
395     }
396
397     m_webPageProxy->process().send(Messages::DrawingArea::AddTransactionCallbackID(m_callbacks.put(WTF::move(callbackFunction), nullptr)), m_webPageProxy->pageID());
398 }
399
400 void RemoteLayerTreeDrawingAreaProxy::hideContentUntilNextUpdate()
401 {
402     m_remoteLayerTreeHost.detachRootLayer();
403 }
404
405 } // namespace WebKit