Rollout r231818, as it introduced regression on tickets.com.
[WebKit-https.git] / Source / WebKit / UIProcess / DrawingAreaProxyImpl.cpp
1 /*
2  * Copyright (C) 2011 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 #include "config.h"
27 #include "DrawingAreaProxyImpl.h"
28
29 #include "DrawingAreaMessages.h"
30 #include "DrawingAreaProxyMessages.h"
31 #include "LayerTreeContext.h"
32 #include "UpdateInfo.h"
33 #include "WebPageGroup.h"
34 #include "WebPageProxy.h"
35 #include "WebPreferences.h"
36 #include "WebProcessProxy.h"
37 #include <WebCore/Region.h>
38
39 #if PLATFORM(GTK)
40 #include <gtk/gtk.h>
41 #endif
42
43 #if USE(GLIB_EVENT_LOOP)
44 #include <wtf/glib/RunLoopSourcePriority.h>
45 #endif
46
47 namespace WebKit {
48 using namespace WebCore;
49
50 DrawingAreaProxyImpl::DrawingAreaProxyImpl(WebPageProxy& webPageProxy)
51     : AcceleratedDrawingAreaProxy(webPageProxy)
52     , m_discardBackingStoreTimer(RunLoop::current(), this, &DrawingAreaProxyImpl::discardBackingStore)
53 {
54 #if USE(GLIB_EVENT_LOOP)
55     m_discardBackingStoreTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
56 #endif
57 }
58
59 DrawingAreaProxyImpl::~DrawingAreaProxyImpl()
60 {
61 }
62
63 void DrawingAreaProxyImpl::paint(BackingStore::PlatformGraphicsContext context, const IntRect& rect, Region& unpaintedRegion)
64 {
65     unpaintedRegion = rect;
66
67     if (isInAcceleratedCompositingMode())
68         return;
69
70     ASSERT(m_currentBackingStoreStateID <= m_nextBackingStoreStateID);
71     if (m_currentBackingStoreStateID < m_nextBackingStoreStateID) {
72         // Tell the web process to do a full backing store update now, in case we previously told
73         // it about our next state but didn't request an immediate update.
74         sendUpdateBackingStoreState(RespondImmediately);
75
76         // If we haven't yet received our first bits from the WebProcess then don't paint anything.
77         if (!m_hasReceivedFirstUpdate)
78             return;
79
80         if (m_isWaitingForDidUpdateBackingStoreState) {
81             // Wait for a DidUpdateBackingStoreState message that contains the new bits before we paint
82             // what's currently in the backing store.
83             waitForAndDispatchDidUpdateBackingStoreState();
84         }
85
86         // Dispatching DidUpdateBackingStoreState (either beneath sendUpdateBackingStoreState or
87         // beneath waitForAndDispatchDidUpdateBackingStoreState) could destroy our backing store or
88         // change the compositing mode.
89         if (!m_backingStore || isInAcceleratedCompositingMode())
90             return;
91     } else {
92         ASSERT(!m_isWaitingForDidUpdateBackingStoreState);
93         if (!m_backingStore) {
94             // The view has asked us to paint before the web process has painted anything. There's
95             // nothing we can do.
96             return;
97         }
98     }
99
100     m_backingStore->paint(context, rect);
101     unpaintedRegion.subtract(IntRect(IntPoint(), m_backingStore->size()));
102
103     discardBackingStoreSoon();
104 }
105
106 void DrawingAreaProxyImpl::setBackingStoreIsDiscardable(bool isBackingStoreDiscardable)
107 {
108     if (m_isBackingStoreDiscardable == isBackingStoreDiscardable)
109         return;
110
111     m_isBackingStoreDiscardable = isBackingStoreDiscardable;
112     if (m_isBackingStoreDiscardable)
113         discardBackingStoreSoon();
114     else
115         m_discardBackingStoreTimer.stop();
116 }
117
118 void DrawingAreaProxyImpl::update(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
119 {
120     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
121     if (backingStoreStateID < m_currentBackingStoreStateID)
122         return;
123
124     // FIXME: Handle the case where the view is hidden.
125
126     incorporateUpdate(updateInfo);
127     m_webPageProxy.process().send(Messages::DrawingArea::DidUpdate(), m_webPageProxy.pageID());
128 }
129
130 void DrawingAreaProxyImpl::didUpdateBackingStoreState(uint64_t backingStoreStateID, const UpdateInfo& updateInfo, const LayerTreeContext& layerTreeContext)
131 {
132     AcceleratedDrawingAreaProxy::didUpdateBackingStoreState(backingStoreStateID, updateInfo, layerTreeContext);
133     if (isInAcceleratedCompositingMode()) {
134         ASSERT(!m_backingStore);
135         return;
136     }
137
138     // If we have a backing store the right size, reuse it.
139     if (m_backingStore && (m_backingStore->size() != updateInfo.viewSize || m_backingStore->deviceScaleFactor() != updateInfo.deviceScaleFactor))
140         m_backingStore = nullptr;
141     incorporateUpdate(updateInfo);
142 }
143
144 void DrawingAreaProxyImpl::exitAcceleratedCompositingMode(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
145 {
146     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
147     if (backingStoreStateID < m_currentBackingStoreStateID)
148         return;
149
150     AcceleratedDrawingAreaProxy::exitAcceleratedCompositingMode();
151
152     incorporateUpdate(updateInfo);
153 }
154
155 void DrawingAreaProxyImpl::incorporateUpdate(const UpdateInfo& updateInfo)
156 {
157     ASSERT(!isInAcceleratedCompositingMode());
158
159     if (updateInfo.updateRectBounds.isEmpty())
160         return;
161
162     if (!m_backingStore)
163         m_backingStore = std::make_unique<BackingStore>(updateInfo.viewSize, updateInfo.deviceScaleFactor, m_webPageProxy);
164
165     m_backingStore->incorporateUpdate(updateInfo);
166
167     Region damageRegion;
168     if (updateInfo.scrollRect.isEmpty()) {
169         for (const auto& rect : updateInfo.updateRects)
170             damageRegion.unite(rect);
171     } else
172         damageRegion = IntRect(IntPoint(), m_webPageProxy.viewSize());
173     m_webPageProxy.setViewNeedsDisplay(damageRegion);
174 }
175
176 void DrawingAreaProxyImpl::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
177 {
178     m_backingStore = nullptr;
179     AcceleratedDrawingAreaProxy::enterAcceleratedCompositingMode(layerTreeContext);
180 }
181
182 void DrawingAreaProxyImpl::discardBackingStoreSoon()
183 {
184     if (!m_backingStore || !m_isBackingStoreDiscardable || m_discardBackingStoreTimer.isActive())
185         return;
186
187     // We'll wait this many seconds after the last paint before throwing away our backing store to save memory.
188     // FIXME: It would be smarter to make this delay based on how expensive painting is. See <http://webkit.org/b/55733>.
189     static const Seconds discardBackingStoreDelay = 2_s;
190
191     m_discardBackingStoreTimer.startOneShot(discardBackingStoreDelay);
192 }
193
194 void DrawingAreaProxyImpl::discardBackingStore()
195 {
196     if (!m_backingStore)
197         return;
198     m_backingStore = nullptr;
199     backingStoreStateDidChange(DoNotRespondImmediately);
200 }
201
202 DrawingAreaProxyImpl::DrawingMonitor::DrawingMonitor(WebPageProxy& webPage)
203     : m_webPage(webPage)
204     , m_timer(RunLoop::main(), this, &DrawingMonitor::stop)
205 {
206 #if USE(GLIB_EVENT_LOOP)
207     // Give redraws more priority.
208     m_timer.setPriority(GDK_PRIORITY_REDRAW - 10);
209 #endif
210 }
211
212 DrawingAreaProxyImpl::DrawingMonitor::~DrawingMonitor()
213 {
214     m_callback = nullptr;
215     stop();
216 }
217
218 int DrawingAreaProxyImpl::DrawingMonitor::webViewDrawCallback(DrawingAreaProxyImpl::DrawingMonitor* monitor)
219 {
220     monitor->didDraw();
221     return FALSE;
222 }
223
224 void DrawingAreaProxyImpl::DrawingMonitor::start(WTF::Function<void (CallbackBase::Error)>&& callback)
225 {
226     m_startTime = MonotonicTime::now();
227     m_callback = WTFMove(callback);
228 #if PLATFORM(GTK)
229     g_signal_connect_swapped(m_webPage.viewWidget(), "draw", reinterpret_cast<GCallback>(webViewDrawCallback), this);
230     m_timer.startOneShot(1_s);
231 #else
232     m_timer.startOneShot(0_s);
233 #endif
234 }
235
236 void DrawingAreaProxyImpl::DrawingMonitor::stop()
237 {
238     m_timer.stop();
239 #if PLATFORM(GTK)
240     g_signal_handlers_disconnect_by_func(m_webPage.viewWidget(), reinterpret_cast<gpointer>(webViewDrawCallback), this);
241 #endif
242     m_startTime = MonotonicTime();
243     if (m_callback) {
244         m_callback(CallbackBase::Error::None);
245         m_callback = nullptr;
246     }
247 }
248
249 void DrawingAreaProxyImpl::DrawingMonitor::didDraw()
250 {
251     // We wait up to 1 second for draw events. If there are several draw events queued quickly,
252     // we want to wait until all of them have been processed, so after receiving a draw, we wait
253     // up to 100ms for the next one or stop.
254     if (MonotonicTime::now() - m_startTime > 1_s)
255         stop();
256     else
257         m_timer.startOneShot(100_ms);
258 }
259
260 void DrawingAreaProxyImpl::dispatchAfterEnsuringDrawing(WTF::Function<void(CallbackBase::Error)>&& callbackFunction)
261 {
262     if (!m_webPageProxy.isValid()) {
263         callbackFunction(CallbackBase::Error::OwnerWasInvalidated);
264         return;
265     }
266
267     if (!m_drawingMonitor)
268         m_drawingMonitor = std::make_unique<DrawingAreaProxyImpl::DrawingMonitor>(m_webPageProxy);
269     m_drawingMonitor->start(WTFMove(callbackFunction));
270 }
271
272 } // namespace WebKit