140672b5b923ad7696c62c8280fe12101bf91e68
[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 static const CFIndex didCommitLayersRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
44
45 namespace WebKit {
46
47 RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy(WebPageProxy* webPageProxy)
48     : DrawingAreaProxy(DrawingAreaTypeRemoteLayerTree, webPageProxy)
49     , m_remoteLayerTreeHost(*this)
50     , m_isWaitingForDidUpdateGeometry(false)
51     , m_pendingLayerTreeTransactionID(0)
52     , m_lastVisibleTransactionID(0)
53     , m_transactionIDForPendingCACommit(0)
54 {
55 #if USE(IOSURFACE)
56     // We don't want to pool surfaces in the UI process.
57     // FIXME: We should do this somewhere else.
58     IOSurfacePool::sharedPool().setPoolSize(0);
59 #endif
60
61     m_webPageProxy->process().addMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy->pageID(), *this);
62
63     if (m_webPageProxy->preferences().tiledScrollingIndicatorVisible())
64         initializeDebugIndicator();
65
66     m_layerCommitObserver = RunLoopObserver::create(didCommitLayersRunLoopOrder, [this]() {
67         this->coreAnimationDidCommitLayers();
68     });
69 }
70
71 RemoteLayerTreeDrawingAreaProxy::~RemoteLayerTreeDrawingAreaProxy()
72 {
73     m_callbacks.invalidate(CallbackBase::Error::OwnerWasInvalidated);
74     m_webPageProxy->process().removeMessageReceiver(Messages::RemoteLayerTreeDrawingAreaProxy::messageReceiverName(), m_webPageProxy->pageID());
75 }
76
77 void RemoteLayerTreeDrawingAreaProxy::sizeDidChange()
78 {
79     if (!m_webPageProxy->isValid())
80         return;
81
82     if (m_isWaitingForDidUpdateGeometry)
83         return;
84
85     sendUpdateGeometry();
86 }
87
88 void RemoteLayerTreeDrawingAreaProxy::deviceScaleFactorDidChange()
89 {
90     m_webPageProxy->process().send(Messages::DrawingArea::SetDeviceScaleFactor(m_webPageProxy->deviceScaleFactor()), m_webPageProxy->pageID());
91 }
92
93 void RemoteLayerTreeDrawingAreaProxy::didUpdateGeometry()
94 {
95     ASSERT(m_isWaitingForDidUpdateGeometry);
96
97     m_isWaitingForDidUpdateGeometry = false;
98
99     // If the WKView was resized while we were waiting for a DidUpdateGeometry reply from the web process,
100     // we need to resend the new size here.
101     if (m_lastSentSize != m_size || m_lastSentLayerPosition != m_layerPosition)
102         sendUpdateGeometry();
103 }
104
105 FloatRect RemoteLayerTreeDrawingAreaProxy::scaledExposedRect() const
106 {
107 #if PLATFORM(IOS)
108     return m_webPageProxy->exposedContentRect();
109 #else
110     FloatRect scaledExposedRect = exposedRect();
111     float scale = 1 / m_webPageProxy->pageScaleFactor();
112     scaledExposedRect.scale(scale, scale);
113     return scaledExposedRect;
114 #endif
115 }
116
117 void RemoteLayerTreeDrawingAreaProxy::sendUpdateGeometry()
118 {
119     m_lastSentSize = m_size;
120     m_lastSentLayerPosition = m_layerPosition;
121     m_webPageProxy->process().send(Messages::DrawingArea::UpdateGeometry(m_size, m_layerPosition), m_webPageProxy->pageID());
122     m_isWaitingForDidUpdateGeometry = true;
123 }
124
125 void RemoteLayerTreeDrawingAreaProxy::willCommitLayerTree(uint64_t transactionID)
126 {
127     m_pendingLayerTreeTransactionID = transactionID;
128 }
129
130 void RemoteLayerTreeDrawingAreaProxy::commitLayerTree(const RemoteLayerTreeTransaction& layerTreeTransaction, const RemoteScrollingCoordinatorTransaction& scrollingTreeTransaction)
131 {
132     LOG(RemoteLayerTree, "%s", layerTreeTransaction.description().data());
133     LOG(RemoteLayerTree, "%s", scrollingTreeTransaction.description().data());
134
135     ASSERT(layerTreeTransaction.transactionID() == m_lastVisibleTransactionID + 1);
136     m_transactionIDForPendingCACommit = layerTreeTransaction.transactionID();
137
138     for (auto& callbackID : layerTreeTransaction.callbackIDs()) {
139         if (auto callback = m_callbacks.take<VoidCallback>(callbackID))
140             callback->performCallback();
141     }
142
143     if (m_remoteLayerTreeHost.updateLayerTree(layerTreeTransaction))
144         m_webPageProxy->setAcceleratedCompositingRootLayer(m_remoteLayerTreeHost.rootLayer());
145
146 #if PLATFORM(IOS)
147     m_webPageProxy->didCommitLayerTree(layerTreeTransaction);
148 #endif
149
150 #if ENABLE(ASYNC_SCROLLING)
151     bool fixedOrStickyLayerChanged = false;
152     m_webPageProxy->scrollingCoordinatorProxy()->updateScrollingTree(scrollingTreeTransaction, fixedOrStickyLayerChanged);
153
154 #if PLATFORM(IOS)
155     if (fixedOrStickyLayerChanged) {
156         FloatRect customFixedPositionRect = m_webPageProxy->computeCustomFixedPositionRect(m_webPageProxy->unobscuredContentRect(), m_webPageProxy->displayedContentScale());
157         // 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.
158         m_webPageProxy->scrollingCoordinatorProxy()->viewportChangedViaDelegatedScrolling(m_webPageProxy->scrollingCoordinatorProxy()->rootScrollingNodeID(), customFixedPositionRect, m_webPageProxy->displayedContentScale());
159     }
160 #endif
161 #endif // ENABLE(ASYNC_SCROLLING)
162
163     if (m_debugIndicatorLayerTreeHost) {
164         float scale = indicatorScale(layerTreeTransaction.contentsSize());
165         bool rootLayerChanged = m_debugIndicatorLayerTreeHost->updateLayerTree(layerTreeTransaction, scale);
166         updateDebugIndicator(layerTreeTransaction.contentsSize(), rootLayerChanged, scale);
167         asLayer(m_debugIndicatorLayerTreeHost->rootLayer()).name = @"Indicator host root";
168     }
169
170     m_layerCommitObserver->schedule();
171 }
172
173 void RemoteLayerTreeDrawingAreaProxy::acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime)
174 {
175     m_webPageProxy->process().send(Messages::DrawingArea::AcceleratedAnimationDidStart(layerID, key, startTime), m_webPageProxy->pageID());
176 }
177
178 static const float indicatorInset = 10;
179
180 #if PLATFORM(MAC)
181 void RemoteLayerTreeDrawingAreaProxy::setExposedRect(const WebCore::FloatRect& r)
182 {
183     DrawingAreaProxy::setExposedRect(r);
184     updateDebugIndicatorPosition();
185 }
186 #endif
187
188 FloatPoint RemoteLayerTreeDrawingAreaProxy::indicatorLocation() const
189 {
190     if (m_webPageProxy->delegatesScrolling()) {
191 #if PLATFORM(IOS)
192         FloatPoint tiledMapLocation = m_webPageProxy->unobscuredContentRect().location();
193         float absoluteInset = indicatorInset / m_webPageProxy->displayedContentScale();
194         tiledMapLocation += FloatSize(absoluteInset, absoluteInset);
195 #else
196         FloatPoint tiledMapLocation = exposedRect().location();
197         tiledMapLocation += FloatSize(indicatorInset, indicatorInset);
198         float scale = 1 / m_webPageProxy->pageScaleFactor();;
199         tiledMapLocation.scale(scale, scale);
200 #endif
201         return tiledMapLocation;
202     }
203     
204     return FloatPoint(indicatorInset, indicatorInset);
205 }
206
207 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicatorPosition()
208 {
209     if (!m_tileMapHostLayer)
210         return;
211
212     [m_tileMapHostLayer setPosition:indicatorLocation()];
213 }
214
215 float RemoteLayerTreeDrawingAreaProxy::indicatorScale(IntSize contentsSize) const
216 {
217     // Pick a good scale.
218     IntSize viewSize = m_webPageProxy->viewSize();
219
220     float scale = 1;
221     if (!contentsSize.isEmpty()) {
222         float widthScale = std::min<float>((viewSize.width() - 2 * indicatorInset) / contentsSize.width(), 0.05);
223         scale = std::min(widthScale, static_cast<float>(viewSize.height() - 2 * indicatorInset) / contentsSize.height());
224     }
225     
226     return scale;
227 }
228
229 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator()
230 {
231     // FIXME: we should also update live information during scale.
232     updateDebugIndicatorPosition();
233 }
234
235 void RemoteLayerTreeDrawingAreaProxy::updateDebugIndicator(IntSize contentsSize, bool rootLayerChanged, float scale)
236 {
237     // Make sure we're the last sublayer.
238     CALayer *rootLayer = asLayer(m_remoteLayerTreeHost.rootLayer());
239     [m_tileMapHostLayer removeFromSuperlayer];
240     [rootLayer addSublayer:m_tileMapHostLayer.get()];
241
242     // Pick a good scale.
243     IntSize viewSize = m_webPageProxy->viewSize();
244
245     [m_tileMapHostLayer setBounds:FloatRect(FloatPoint(), contentsSize)];
246     [m_tileMapHostLayer setPosition:indicatorLocation()];
247     [m_tileMapHostLayer setTransform:CATransform3DMakeScale(scale, scale, 1)];
248
249     if (rootLayerChanged) {
250         [m_tileMapHostLayer setSublayers:@[]];
251         [m_tileMapHostLayer addSublayer:asLayer(m_debugIndicatorLayerTreeHost->rootLayer())];
252         [m_tileMapHostLayer addSublayer:m_exposedRectIndicatorLayer.get()];
253     }
254     
255     const float indicatorBorderWidth = 1;
256     float counterScaledBorder = indicatorBorderWidth / scale;
257
258     [m_exposedRectIndicatorLayer setBorderWidth:counterScaledBorder];
259
260     if (m_webPageProxy->delegatesScrolling()) {
261         FloatRect scaledExposedRect = this->scaledExposedRect();
262         [m_exposedRectIndicatorLayer setPosition:scaledExposedRect.location()];
263         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), scaledExposedRect.size())];
264     } else {
265         // FIXME: Get the correct scroll position.
266         [m_exposedRectIndicatorLayer setBounds:FloatRect(FloatPoint(), viewSize)];
267     }
268 }
269
270 void RemoteLayerTreeDrawingAreaProxy::initializeDebugIndicator()
271 {
272     m_debugIndicatorLayerTreeHost = std::make_unique<RemoteLayerTreeHost>(*this);
273     m_debugIndicatorLayerTreeHost->setIsDebugLayerTreeHost(true);
274
275     m_tileMapHostLayer = adoptNS([[CALayer alloc] init]);
276     [m_tileMapHostLayer setName:@"Tile map host"];
277     [m_tileMapHostLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
278     [m_tileMapHostLayer setAnchorPoint:CGPointZero];
279     [m_tileMapHostLayer setOpacity:0.8];
280     [m_tileMapHostLayer setMasksToBounds:YES];
281     [m_tileMapHostLayer setBorderWidth:2];
282
283     RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
284     {
285         const CGFloat components[] = { 1, 1, 1, 0.6 };
286         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
287         [m_tileMapHostLayer setBackgroundColor:color.get()];
288
289         const CGFloat borderComponents[] = { 0, 0, 0, 1 };
290         RetainPtr<CGColorRef> borderColor = adoptCF(CGColorCreate(colorSpace.get(), borderComponents));
291         [m_tileMapHostLayer setBorderColor:borderColor.get()];
292     }
293     
294     m_exposedRectIndicatorLayer = adoptNS([[CALayer alloc] init]);
295     [m_exposedRectIndicatorLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
296     [m_exposedRectIndicatorLayer setAnchorPoint:CGPointZero];
297
298     {
299         const CGFloat components[] = { 0, 1, 0, 1 };
300         RetainPtr<CGColorRef> color = adoptCF(CGColorCreate(colorSpace.get(), components));
301         [m_exposedRectIndicatorLayer setBorderColor:color.get()];
302     }
303 }
304
305 void RemoteLayerTreeDrawingAreaProxy::coreAnimationDidCommitLayers()
306 {
307     m_layerCommitObserver->invalidate();
308
309     if (!m_webPageProxy->isValid())
310         return;
311
312     // Waiting for CA to commit is insufficient, because the render server can still be
313     // using our backing store. We can improve this by waiting for the render server to commit
314     // if we find API to do so, but for now we will make extra buffers if need be.
315     m_webPageProxy->process().send(Messages::DrawingArea::DidUpdate(), m_webPageProxy->pageID());
316
317     m_lastVisibleTransactionID = m_transactionIDForPendingCACommit;
318
319     m_webPageProxy->didUpdateViewState();
320 }
321
322 void RemoteLayerTreeDrawingAreaProxy::waitForDidUpdateViewState()
323 {
324 #if PLATFORM(IOS)
325     auto viewStateUpdateTimeout = std::chrono::milliseconds(500);
326 #else
327     auto viewStateUpdateTimeout = std::chrono::milliseconds(250);
328 #endif
329     m_webPageProxy->process().connection()->waitForAndDispatchImmediately<Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree>(m_webPageProxy->pageID(), viewStateUpdateTimeout, InterruptWaitingIfSyncMessageArrives);
330 }
331
332 void RemoteLayerTreeDrawingAreaProxy::dispatchAfterEnsuringDrawing(std::function<void (CallbackBase::Error)> callbackFunction)
333 {
334     if (!m_webPageProxy->isValid()) {
335         callbackFunction(CallbackBase::Error::OwnerWasInvalidated);
336         return;
337     }
338
339     m_webPageProxy->process().send(Messages::DrawingArea::AddTransactionCallbackID(m_callbacks.put(WTF::move(callbackFunction), nullptr)), m_webPageProxy->pageID());
340 }
341
342 } // namespace WebKit