721309073208631cf4cc64529193f8267e78a77b
[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(isMainThread());
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     bool fixedOrStickyLayerChanged = false;
209     m_webPageProxy->scrollingCoordinatorProxy()->updateScrollingTree(scrollingTreeTransaction, fixedOrStickyLayerChanged);
210
211 #if PLATFORM(IOS)
212     if (fixedOrStickyLayerChanged) {
213         FloatRect customFixedPositionRect = m_webPageProxy->computeCustomFixedPositionRect(m_webPageProxy->unobscuredContentRect(), m_webPageProxy->displayedContentScale());
214         // 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.
215         m_webPageProxy->scrollingCoordinatorProxy()->viewportChangedViaDelegatedScrolling(m_webPageProxy->scrollingCoordinatorProxy()->rootScrollingNodeID(), customFixedPositionRect, m_webPageProxy->displayedContentScale());
216     }
217 #endif
218 #endif // ENABLE(ASYNC_SCROLLING)
219
220     if (m_debugIndicatorLayerTreeHost) {
221         float scale = indicatorScale(layerTreeTransaction.contentsSize());
222         bool rootLayerChanged = m_debugIndicatorLayerTreeHost->updateLayerTree(layerTreeTransaction, scale);
223         updateDebugIndicator(layerTreeTransaction.contentsSize(), rootLayerChanged, scale);
224         asLayer(m_debugIndicatorLayerTreeHost->rootLayer()).name = @"Indicator host root";
225     }
226
227 #if PLATFORM(IOS)
228     [m_displayLinkHandler schedule];
229 #else
230     didRefreshDisplay(monotonicallyIncreasingTime());
231 #endif
232 }
233
234 void RemoteLayerTreeDrawingAreaProxy::acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime)
235 {
236     m_webPageProxy->process().send(Messages::DrawingArea::AcceleratedAnimationDidStart(layerID, key, startTime), m_webPageProxy->pageID());
237 }
238
239 static const float indicatorInset = 10;
240
241 #if PLATFORM(MAC)
242 void RemoteLayerTreeDrawingAreaProxy::setExposedRect(const WebCore::FloatRect& r)
243 {
244     DrawingAreaProxy::setExposedRect(r);
245     updateDebugIndicatorPosition();
246 }
247 #endif
248
249 FloatPoint RemoteLayerTreeDrawingAreaProxy::indicatorLocation() const
250 {
251     if (m_webPageProxy->delegatesScrolling()) {
252 #if PLATFORM(IOS)
253         FloatPoint tiledMapLocation = m_webPageProxy->unobscuredContentRect().location();
254         float absoluteInset = indicatorInset / m_webPageProxy->displayedContentScale();
255         tiledMapLocation += FloatSize(absoluteInset, absoluteInset);
256 #else
257         FloatPoint tiledMapLocation = exposedRect().location();
258         tiledMapLocation += FloatSize(indicatorInset, indicatorInset);
259         float scale = 1 / m_webPageProxy->pageScaleFactor();;
260         tiledMapLocation.scale(scale, scale);
261 #endif
262         return tiledMapLocation;
263     }
264     
265     return FloatPoint(indicatorInset, indicatorInset);
266 }
267
268 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicatorPosition()
269 {
270     if (!m_tileMapHostLayer)
271         return;
272
273     [m_tileMapHostLayer setPosition:indicatorLocation()];
274 }
275
276 float RemoteLayerTreeDrawingAreaProxy::indicatorScale(IntSize contentsSize) const
277 {
278     // Pick a good scale.
279     IntSize viewSize = m_webPageProxy->viewSize();
280
281     float scale = 1;
282     if (!contentsSize.isEmpty()) {
283         float widthScale = std::min<float>((viewSize.width() - 2 * indicatorInset) / contentsSize.width(), 0.05);
284         scale = std::min(widthScale, static_cast<float>(viewSize.height() - 2 * indicatorInset) / contentsSize.height());
285     }
286     
287     return scale;
288 }
289
290 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator()
291 {
292     // FIXME: we should also update live information during scale.
293     updateDebugIndicatorPosition();
294 }
295
296 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator(IntSize contentsSize, bool rootLayerChanged, float scale)
297 {
298     // Make sure we're the last sublayer.
299     CALayer *rootLayer = asLayer(m_remoteLayerTreeHost.rootLayer());
300     [m_tileMapHostLayer removeFromSuperlayer];
301     [rootLayer addSublayer:m_tileMapHostLayer.get()];
302
303     // Pick a good scale.
304     IntSize viewSize = m_webPageProxy->viewSize();
305
306     [m_tileMapHostLayer setBounds:FloatRect(FloatPoint(), contentsSize)];
307     [m_tileMapHostLayer setPosition:indicatorLocation()];
308     [m_tileMapHostLayer setTransform:CATransform3DMakeScale(scale, scale, 1)];
309
310     if (rootLayerChanged) {
311         [m_tileMapHostLayer setSublayers:@[]];
312         [m_tileMapHostLayer addSublayer:asLayer(m_debugIndicatorLayerTreeHost->rootLayer())];
313         [m_tileMapHostLayer addSublayer:m_exposedRectIndicatorLayer.get()];
314     }
315     
316     const float indicatorBorderWidth = 1;
317     float counterScaledBorder = indicatorBorderWidth / scale;
318
319     [m_exposedRectIndicatorLayer setBorderWidth:counterScaledBorder];
320
321     if (m_webPageProxy->delegatesScrolling()) {
322         FloatRect scaledExposedRect = this->scaledExposedRect();
323         [m_exposedRectIndicatorLayer setPosition:scaledExposedRect.location()];
324         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), scaledExposedRect.size())];
325     } else {
326         // FIXME: Get the correct scroll position.
327         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), viewSize)];
328     }
329 }
330
331 void RemoteLayerTreeDrawingAreaProxy::initializeDebugIndicator()
332 {
333     m_debugIndicatorLayerTreeHost = std::make_unique<RemoteLayerTreeHost>(*this);
334     m_debugIndicatorLayerTreeHost->setIsDebugLayerTreeHost(true);
335
336     m_tileMapHostLayer = adoptNS([[CALayer alloc] init]);
337     [m_tileMapHostLayer setName:@"Tile map host"];
338     [m_tileMapHostLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
339     [m_tileMapHostLayer setAnchorPoint:CGPointZero];
340     [m_tileMapHostLayer setOpacity:0.8];
341     [m_tileMapHostLayer setMasksToBounds:YES];
342     [m_tileMapHostLayer setBorderWidth:2];
343
344     RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
345     {
346         const CGFloat components[] = { 1, 1, 1, 0.6 };
347         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
348         [m_tileMapHostLayer setBackgroundColor:color.get()];
349
350         const CGFloat borderComponents[] = { 0, 0, 0, 1 };
351         RetainPtr<CGColorRef> borderColor = adoptCF(CGColorCreate(colorSpace.get(), borderComponents));
352         [m_tileMapHostLayer setBorderColor:borderColor.get()];
353     }
354     
355     m_exposedRectIndicatorLayer = adoptNS([[CALayer alloc] init]);
356     [m_exposedRectIndicatorLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
357     [m_exposedRectIndicatorLayer setAnchorPoint:CGPointZero];
358
359     {
360         const CGFloat components[] = { 0, 1, 0, 1 };
361         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
362         [m_exposedRectIndicatorLayer setBorderColor:color.get()];
363     }
364 }
365
366 void RemoteLayerTreeDrawingAreaProxy::didRefreshDisplay(double)
367 {
368     if (!m_webPageProxy->isValid())
369         return;
370
371     // Waiting for CA to commit is insufficient, because the render server can still be
372     // using our backing store. We can improve this by waiting for the render server to commit
373     // if we find API to do so, but for now we will make extra buffers if need be.
374     m_webPageProxy->process().send(Messages::DrawingArea::DidUpdate(), m_webPageProxy->pageID());
375
376     m_lastVisibleTransactionID = m_transactionIDForPendingCACommit;
377
378     m_webPageProxy->didUpdateViewState();
379 }
380
381 void RemoteLayerTreeDrawingAreaProxy::waitForDidUpdateViewState()
382 {
383 #if PLATFORM(IOS)
384     auto viewStateUpdateTimeout = std::chrono::milliseconds(500);
385 #else
386     auto viewStateUpdateTimeout = std::chrono::milliseconds(250);
387 #endif
388     m_webPageProxy->process().connection()->waitForAndDispatchImmediately<Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree>(m_webPageProxy->pageID(), viewStateUpdateTimeout, InterruptWaitingIfSyncMessageArrives);
389 }
390
391 void RemoteLayerTreeDrawingAreaProxy::dispatchAfterEnsuringDrawing(std::function<void (CallbackBase::Error)> callbackFunction)
392 {
393     if (!m_webPageProxy->isValid()) {
394         callbackFunction(CallbackBase::Error::OwnerWasInvalidated);
395         return;
396     }
397
398     m_webPageProxy->process().send(Messages::DrawingArea::AddTransactionCallbackID(m_callbacks.put(WTF::move(callbackFunction), nullptr)), m_webPageProxy->pageID());
399 }
400
401 } // namespace WebKit