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 "Region.h"
33 #include "UpdateInfo.h"
34 #include "WebPageProxy.h"
35 #include "WebProcessProxy.h"
36
37 #if !PLATFORM(MAC) && !PLATFORM(WIN)
38 #error "This drawing area is not ready for use by other ports yet."
39 #endif
40
41 using namespace WebCore;
42
43 namespace WebKit {
44
create(WebPageProxy * webPageProxy)45 PassOwnPtr<DrawingAreaProxyImpl> DrawingAreaProxyImpl::create(WebPageProxy* webPageProxy)
46 {
47 return adoptPtr(new DrawingAreaProxyImpl(webPageProxy));
48 }
49
DrawingAreaProxyImpl(WebPageProxy * webPageProxy)50 DrawingAreaProxyImpl::DrawingAreaProxyImpl(WebPageProxy* webPageProxy)
51 : DrawingAreaProxy(DrawingAreaTypeImpl, webPageProxy)
52 , m_currentBackingStoreStateID(0)
53 , m_nextBackingStoreStateID(0)
54 , m_isWaitingForDidUpdateBackingStoreState(false)
55 , m_isBackingStoreDiscardable(true)
56 , m_discardBackingStoreTimer(RunLoop::current(), this, &DrawingAreaProxyImpl::discardBackingStore)
57 {
58 }
59
~DrawingAreaProxyImpl()60 DrawingAreaProxyImpl::~DrawingAreaProxyImpl()
61 {
62 #if USE(ACCELERATED_COMPOSITING)
63 // Make sure to exit accelerated compositing mode.
64 if (isInAcceleratedCompositingMode())
65 exitAcceleratedCompositingMode();
66 #endif
67 }
68
paint(BackingStore::PlatformGraphicsContext context,const IntRect & rect,Region & unpaintedRegion)69 void DrawingAreaProxyImpl::paint(BackingStore::PlatformGraphicsContext context, const IntRect& rect, Region& unpaintedRegion)
70 {
71 unpaintedRegion = rect;
72
73 if (isInAcceleratedCompositingMode())
74 return;
75
76 ASSERT(m_currentBackingStoreStateID <= m_nextBackingStoreStateID);
77 if (m_currentBackingStoreStateID < m_nextBackingStoreStateID) {
78 // Tell the web process to do a full backing store update now, in case we previously told
79 // it about our next state but didn't request an immediate update.
80 sendUpdateBackingStoreState(RespondImmediately);
81
82 if (m_isWaitingForDidUpdateBackingStoreState) {
83 // Wait for a DidUpdateBackingStoreState message that contains the new bits before we paint
84 // what's currently in the backing store.
85 waitForAndDispatchDidUpdateBackingStoreState();
86 }
87
88 // Dispatching DidUpdateBackingStoreState (either beneath sendUpdateBackingStoreState or
89 // beneath waitForAndDispatchDidUpdateBackingStoreState) could destroy our backing store or
90 // change the compositing mode.
91 if (!m_backingStore || isInAcceleratedCompositingMode())
92 return;
93 } else {
94 ASSERT(!m_isWaitingForDidUpdateBackingStoreState);
95 if (!m_backingStore) {
96 // The view has asked us to paint before the web process has painted anything. There's
97 // nothing we can do.
98 return;
99 }
100 }
101
102 m_backingStore->paint(context, rect);
103 unpaintedRegion.subtract(IntRect(IntPoint(), m_backingStore->size()));
104
105 discardBackingStoreSoon();
106 }
107
didReceiveMessage(CoreIPC::Connection *,CoreIPC::MessageID,CoreIPC::ArgumentDecoder *)108 void DrawingAreaProxyImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
109 {
110 ASSERT_NOT_REACHED();
111 }
112
didReceiveSyncMessage(CoreIPC::Connection *,CoreIPC::MessageID,CoreIPC::ArgumentDecoder *,CoreIPC::ArgumentEncoder *)113 void DrawingAreaProxyImpl::didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*)
114 {
115 ASSERT_NOT_REACHED();
116 }
117
paint(const WebCore::IntRect &,PlatformDrawingContext)118 bool DrawingAreaProxyImpl::paint(const WebCore::IntRect&, PlatformDrawingContext)
119 {
120 ASSERT_NOT_REACHED();
121 return false;
122 }
123
sizeDidChange()124 void DrawingAreaProxyImpl::sizeDidChange()
125 {
126 backingStoreStateDidChange(RespondImmediately);
127 }
128
visibilityDidChange()129 void DrawingAreaProxyImpl::visibilityDidChange()
130 {
131 if (!m_webPageProxy->isViewVisible()) {
132 // Suspend painting.
133 m_webPageProxy->process()->send(Messages::DrawingArea::SuspendPainting(), m_webPageProxy->pageID());
134 return;
135 }
136
137 // Resume painting.
138 m_webPageProxy->process()->send(Messages::DrawingArea::ResumePainting(), m_webPageProxy->pageID());
139
140 #if USE(ACCELERATED_COMPOSITING)
141 // If we don't have a backing store, go ahead and mark the backing store as being changed so
142 // that when paint we'll actually wait for something to paint and not flash white.
143 if (!m_backingStore && m_layerTreeContext.isEmpty())
144 backingStoreStateDidChange(DoNotRespondImmediately);
145 #endif
146 }
147
setPageIsVisible(bool)148 void DrawingAreaProxyImpl::setPageIsVisible(bool)
149 {
150 }
151
setBackingStoreIsDiscardable(bool isBackingStoreDiscardable)152 void DrawingAreaProxyImpl::setBackingStoreIsDiscardable(bool isBackingStoreDiscardable)
153 {
154 if (m_isBackingStoreDiscardable == isBackingStoreDiscardable)
155 return;
156
157 m_isBackingStoreDiscardable = isBackingStoreDiscardable;
158 if (m_isBackingStoreDiscardable)
159 discardBackingStoreSoon();
160 else
161 m_discardBackingStoreTimer.stop();
162 }
163
update(uint64_t backingStoreStateID,const UpdateInfo & updateInfo)164 void DrawingAreaProxyImpl::update(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
165 {
166 ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
167 if (backingStoreStateID < m_currentBackingStoreStateID)
168 return;
169
170 // FIXME: Handle the case where the view is hidden.
171
172 incorporateUpdate(updateInfo);
173 m_webPageProxy->process()->send(Messages::DrawingArea::DidUpdate(), m_webPageProxy->pageID());
174 }
175
didUpdateBackingStoreState(uint64_t backingStoreStateID,const UpdateInfo & updateInfo,const LayerTreeContext & layerTreeContext)176 void DrawingAreaProxyImpl::didUpdateBackingStoreState(uint64_t backingStoreStateID, const UpdateInfo& updateInfo, const LayerTreeContext& layerTreeContext)
177 {
178 ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_nextBackingStoreStateID);
179 ASSERT_ARG(backingStoreStateID, backingStoreStateID > m_currentBackingStoreStateID);
180 m_currentBackingStoreStateID = backingStoreStateID;
181
182 m_isWaitingForDidUpdateBackingStoreState = false;
183
184 if (m_nextBackingStoreStateID != m_currentBackingStoreStateID)
185 sendUpdateBackingStoreState(RespondImmediately);
186
187 #if USE(ACCELERATED_COMPOSITING)
188 if (layerTreeContext != m_layerTreeContext) {
189 if (!m_layerTreeContext.isEmpty()) {
190 exitAcceleratedCompositingMode();
191 ASSERT(m_layerTreeContext.isEmpty());
192 }
193
194 if (!layerTreeContext.isEmpty()) {
195 enterAcceleratedCompositingMode(layerTreeContext);
196 ASSERT(layerTreeContext == m_layerTreeContext);
197 }
198 }
199
200 if (isInAcceleratedCompositingMode()) {
201 ASSERT(!m_backingStore);
202 return;
203 }
204 #endif
205
206 // FIXME: We could just reuse our existing backing store if it's the same size as
207 // updateInfo.viewSize.
208 m_backingStore = nullptr;
209 incorporateUpdate(updateInfo);
210 }
211
enterAcceleratedCompositingMode(uint64_t backingStoreStateID,const LayerTreeContext & layerTreeContext)212 void DrawingAreaProxyImpl::enterAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext& layerTreeContext)
213 {
214 ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
215 if (backingStoreStateID < m_currentBackingStoreStateID)
216 return;
217
218 #if USE(ACCELERATED_COMPOSITING)
219 enterAcceleratedCompositingMode(layerTreeContext);
220 #endif
221 }
222
exitAcceleratedCompositingMode(uint64_t backingStoreStateID,const UpdateInfo & updateInfo)223 void DrawingAreaProxyImpl::exitAcceleratedCompositingMode(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
224 {
225 ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
226 if (backingStoreStateID < m_currentBackingStoreStateID)
227 return;
228
229 #if USE(ACCELERATED_COMPOSITING)
230 exitAcceleratedCompositingMode();
231 #endif
232
233 incorporateUpdate(updateInfo);
234 }
235
incorporateUpdate(const UpdateInfo & updateInfo)236 void DrawingAreaProxyImpl::incorporateUpdate(const UpdateInfo& updateInfo)
237 {
238 ASSERT(!isInAcceleratedCompositingMode());
239
240 if (updateInfo.updateRectBounds.isEmpty())
241 return;
242
243 if (!m_backingStore)
244 m_backingStore = BackingStore::create(updateInfo.viewSize, m_webPageProxy);
245
246 m_backingStore->incorporateUpdate(updateInfo);
247
248 bool shouldScroll = !updateInfo.scrollRect.isEmpty();
249
250 if (shouldScroll)
251 m_webPageProxy->scrollView(updateInfo.scrollRect, updateInfo.scrollOffset);
252
253 for (size_t i = 0; i < updateInfo.updateRects.size(); ++i)
254 m_webPageProxy->setViewNeedsDisplay(updateInfo.updateRects[i]);
255
256 if (WebPageProxy::debugPaintFlags() & kWKDebugFlashBackingStoreUpdates)
257 m_webPageProxy->flashBackingStoreUpdates(updateInfo.updateRects);
258
259 if (shouldScroll)
260 m_webPageProxy->displayView();
261 }
262
backingStoreStateDidChange(RespondImmediatelyOrNot respondImmediatelyOrNot)263 void DrawingAreaProxyImpl::backingStoreStateDidChange(RespondImmediatelyOrNot respondImmediatelyOrNot)
264 {
265 ++m_nextBackingStoreStateID;
266 sendUpdateBackingStoreState(respondImmediatelyOrNot);
267 }
268
sendUpdateBackingStoreState(RespondImmediatelyOrNot respondImmediatelyOrNot)269 void DrawingAreaProxyImpl::sendUpdateBackingStoreState(RespondImmediatelyOrNot respondImmediatelyOrNot)
270 {
271 ASSERT(m_currentBackingStoreStateID < m_nextBackingStoreStateID);
272
273 if (!m_webPageProxy->isValid())
274 return;
275
276 if (m_isWaitingForDidUpdateBackingStoreState)
277 return;
278
279 m_isWaitingForDidUpdateBackingStoreState = respondImmediatelyOrNot == RespondImmediately;
280 m_webPageProxy->process()->send(Messages::DrawingArea::UpdateBackingStoreState(m_nextBackingStoreStateID, respondImmediatelyOrNot == RespondImmediately, m_size, m_scrollOffset), m_webPageProxy->pageID());
281 m_scrollOffset = IntSize();
282
283 #if USE(ACCELERATED_COMPOSITING)
284 if (m_isWaitingForDidUpdateBackingStoreState && !m_layerTreeContext.isEmpty()) {
285 // Wait for the DidUpdateBackingStoreState message. Normally we don this in DrawingAreaProxyImpl::paint, but that
286 // function is never called when in accelerated compositing mode.
287 waitForAndDispatchDidUpdateBackingStoreState();
288 }
289 #endif
290 }
291
waitForAndDispatchDidUpdateBackingStoreState()292 void DrawingAreaProxyImpl::waitForAndDispatchDidUpdateBackingStoreState()
293 {
294 ASSERT(m_isWaitingForDidUpdateBackingStoreState);
295
296 if (!m_webPageProxy->isValid())
297 return;
298 if (m_webPageProxy->process()->isLaunching())
299 return;
300
301 #if USE(ACCELERATED_COMPOSITING)
302 // FIXME: waitForAndDispatchImmediately will always return the oldest DidUpdateBackingStoreState message that
303 // hasn't yet been processed. But it might be better to skip ahead to some other DidUpdateBackingStoreState
304 // message, if multiple DidUpdateBackingStoreState messages are waiting to be processed. For instance, we could
305 // choose the most recent one, or the one that is closest to our current size.
306
307 // The timeout, in seconds, we use when waiting for a DidUpdateBackingStoreState message when we're asked to paint.
308 static const double didUpdateBackingStoreStateTimeout = 0.5;
309 m_webPageProxy->process()->connection()->waitForAndDispatchImmediately<Messages::DrawingAreaProxy::DidUpdateBackingStoreState>(m_webPageProxy->pageID(), didUpdateBackingStoreStateTimeout);
310 #endif
311 }
312
313 #if USE(ACCELERATED_COMPOSITING)
enterAcceleratedCompositingMode(const LayerTreeContext & layerTreeContext)314 void DrawingAreaProxyImpl::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
315 {
316 ASSERT(!isInAcceleratedCompositingMode());
317
318 m_backingStore = nullptr;
319 m_layerTreeContext = layerTreeContext;
320 m_webPageProxy->enterAcceleratedCompositingMode(layerTreeContext);
321 }
322
exitAcceleratedCompositingMode()323 void DrawingAreaProxyImpl::exitAcceleratedCompositingMode()
324 {
325 ASSERT(isInAcceleratedCompositingMode());
326
327 m_layerTreeContext = LayerTreeContext();
328 m_webPageProxy->exitAcceleratedCompositingMode();
329 }
330 #endif
331
discardBackingStoreSoon()332 void DrawingAreaProxyImpl::discardBackingStoreSoon()
333 {
334 if (!m_isBackingStoreDiscardable)
335 return;
336
337 // We'll wait this many seconds after the last paint before throwing away our backing store to save memory.
338 // FIXME: It would be smarter to make this delay based on how expensive painting is. See <http://webkit.org/b/55733>.
339 static const double discardBackingStoreDelay = 5;
340
341 m_discardBackingStoreTimer.startOneShot(discardBackingStoreDelay);
342 }
343
discardBackingStore()344 void DrawingAreaProxyImpl::discardBackingStore()
345 {
346 m_backingStore = nullptr;
347 backingStoreStateDidChange(DoNotRespondImmediately);
348 }
349
350 } // namespace WebKit
351