2011-02-03 Anders Carlsson <andersca@apple.com>
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / DrawingAreaImpl.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 "DrawingAreaImpl.h"
28
29 #include "DrawingAreaProxyMessages.h"
30 #include "LayerTreeContext.h"
31 #include "ShareableBitmap.h"
32 #include "UpdateInfo.h"
33 #include "WebPage.h"
34 #include "WebPageCreationParameters.h"
35 #include "WebProcess.h"
36 #include <WebCore/GraphicsContext.h>
37
38 #ifndef __APPLE__
39 #error "This drawing area is not ready for use by other ports yet."
40 #endif
41
42 using namespace WebCore;
43
44 namespace WebKit {
45
46 PassRefPtr<DrawingAreaImpl> DrawingAreaImpl::create(WebPage* webPage, const WebPageCreationParameters& parameters)
47 {
48     return adoptRef(new DrawingAreaImpl(webPage, parameters));
49 }
50
51 DrawingAreaImpl::~DrawingAreaImpl()
52 {
53 }
54
55 DrawingAreaImpl::DrawingAreaImpl(WebPage* webPage, const WebPageCreationParameters& parameters)
56     : DrawingArea(DrawingAreaInfo::Impl, parameters.drawingAreaInfo.identifier, webPage)
57     , m_inSetSize(false)
58     , m_isWaitingForDidUpdate(false)
59     , m_isPaintingSuspended(!parameters.isVisible)
60     , m_displayTimer(WebProcess::shared().runLoop(), this, &DrawingAreaImpl::display)
61 {
62 }
63
64 void DrawingAreaImpl::setNeedsDisplay(const IntRect& rect)
65 {
66     if (rect.isEmpty())
67         return;
68
69     if (m_layerTreeHost) {
70         ASSERT(m_dirtyRegion.isEmpty());
71
72         // FIXME: Ask the layer tree host to repaint non-composited content.
73         return;
74     }
75     
76     m_dirtyRegion.unite(rect);
77     scheduleDisplay();
78 }
79
80 void DrawingAreaImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
81 {
82     if (m_layerTreeHost) {
83         ASSERT(m_scrollRect.isEmpty());
84         ASSERT(m_scrollOffset.isEmpty());
85         ASSERT(m_dirtyRegion.isEmpty());
86
87         // FIXME: Ask the layer tree host to do the scroll.
88         return;
89     }
90
91     if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
92         unsigned scrollArea = scrollRect.width() * scrollRect.height();
93         unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
94
95         if (currentScrollArea >= scrollArea) {
96             // The rect being scrolled is at least as large as the rect we'd like to scroll.
97             // Go ahead and just invalidate the scroll rect.
98             setNeedsDisplay(scrollRect);
99             return;
100         }
101
102         // Just repaint the entire current scroll rect, we'll scroll the new rect instead.
103         setNeedsDisplay(m_scrollRect);
104         m_scrollRect = IntRect();
105         m_scrollOffset = IntSize();
106     }
107
108     // Get the part of the dirty region that is in the scroll rect.
109     Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
110     if (!dirtyRegionInScrollRect.isEmpty()) {
111         // There are parts of the dirty region that are inside the scroll rect.
112         // We need to subtract them from the region, move them and re-add them.
113         m_dirtyRegion.subtract(scrollRect);
114
115         // Move the dirty parts.
116         Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollOffset), scrollRect);
117
118         // And add them back.
119         m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
120     } 
121     
122     // Compute the scroll repaint region.
123     Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollOffset));
124
125     m_dirtyRegion.unite(scrollRepaintRegion);
126
127     m_scrollRect = scrollRect;
128     m_scrollOffset += scrollOffset;
129 }
130
131 void DrawingAreaImpl::forceRepaint()
132 {
133     m_isWaitingForDidUpdate = false;
134     display();
135 }
136
137 void DrawingAreaImpl::attachCompositingContext()
138 {
139 }
140
141 void DrawingAreaImpl::detachCompositingContext()
142 {
143 }
144
145 void DrawingAreaImpl::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
146 {
147     if (graphicsLayer)
148         enterAcceleratedCompositingMode(graphicsLayer);
149     else
150         exitAcceleratedCompositingMode();
151 }
152
153 void DrawingAreaImpl::scheduleCompositingLayerSync()
154 {
155     if (!m_layerTreeHost)
156         return;
157     m_layerTreeHost->scheduleLayerFlush();
158 }
159
160 void DrawingAreaImpl::syncCompositingLayers()
161 {
162 }
163
164 void DrawingAreaImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
165 {
166 }
167
168 void DrawingAreaImpl::setSize(const IntSize& size)
169 {
170     ASSERT(!m_inSetSize);
171     m_inSetSize = true;
172
173     // Set this to false since we're about to call display().
174     m_isWaitingForDidUpdate = false;
175
176     m_webPage->setSize(size);
177     m_webPage->layoutIfNeeded();
178
179     UpdateInfo updateInfo;
180
181     if (m_isPaintingSuspended || m_layerTreeHost) {
182         updateInfo.timestamp = currentTime();
183         updateInfo.viewSize = m_webPage->size();
184     } else
185         display(updateInfo);
186
187     m_webPage->send(Messages::DrawingAreaProxy::DidSetSize(updateInfo));
188
189     m_inSetSize = false;
190 }
191
192 void DrawingAreaImpl::didUpdate()
193 {
194     // We might get didUpdate messages from the UI process even after we've
195     // entered accelerated compositing mode. Ignore them.
196     if (m_layerTreeHost)
197         return;
198
199     m_isWaitingForDidUpdate = false;
200
201     // Display if needed.
202     display();
203 }
204
205 void DrawingAreaImpl::suspendPainting()
206 {
207     ASSERT(!m_isPaintingSuspended);
208
209     m_isPaintingSuspended = true;
210     m_displayTimer.stop();
211 }
212
213 void DrawingAreaImpl::resumePainting()
214 {
215     ASSERT(m_isPaintingSuspended);
216
217     m_isPaintingSuspended = false;
218
219     // FIXME: Repaint if needed.
220 }
221
222 void DrawingAreaImpl::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
223 {
224     ASSERT(!m_layerTreeHost);
225
226     m_layerTreeHost = LayerTreeHost::create(m_webPage, graphicsLayer);
227     
228     // Non-composited content will now be handled exclusively by the layer tree host.
229     m_dirtyRegion = Region();
230     m_scrollRect = IntRect();
231     m_scrollOffset = IntSize();
232     m_displayTimer.stop();
233     m_isWaitingForDidUpdate = false;
234
235     if (!m_inSetSize)
236         m_webPage->send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(m_layerTreeHost->layerTreeContext()));
237 }
238
239 void DrawingAreaImpl::exitAcceleratedCompositingMode()
240 {
241     m_layerTreeHost->invalidate();
242     m_layerTreeHost = nullptr;
243     
244     if (!m_inSetSize)
245         m_webPage->send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode());
246 }
247
248 void DrawingAreaImpl::scheduleDisplay()
249 {
250     if (m_isWaitingForDidUpdate)
251         return;
252
253     if (m_isPaintingSuspended)
254         return;
255
256     if (m_dirtyRegion.isEmpty())
257         return;
258
259     if (m_displayTimer.isActive())
260         return;
261
262     m_displayTimer.startOneShot(0);
263 }
264
265 void DrawingAreaImpl::display()
266 {
267     ASSERT(!m_layerTreeHost);
268     ASSERT(!m_isWaitingForDidUpdate);
269
270     if (m_isPaintingSuspended)
271         return;
272
273     if (m_dirtyRegion.isEmpty())
274         return;
275
276     UpdateInfo updateInfo;
277     display(updateInfo);
278
279     m_webPage->send(Messages::DrawingAreaProxy::Update(updateInfo));
280     m_isWaitingForDidUpdate = true;
281 }
282
283 static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
284 {
285     const size_t rectThreshold = 10;
286     const float wastedSpaceThreshold = 0.75f;
287
288     if (rects.size() <= 1 || rects.size() > rectThreshold)
289         return true;
290
291     // Attempt to guess whether or not we should use the region bounds rect or the individual rects.
292     // We do this by computing the percentage of "wasted space" in the bounds.  If that wasted space
293     // is too large, then we will do individual rect painting instead.
294     unsigned boundsArea = bounds.width() * bounds.height();
295     unsigned rectsArea = 0;
296     for (size_t i = 0; i < rects.size(); ++i)
297         rectsArea += rects[i].width() * rects[i].height();
298
299     float wastedSpace = 1 - (rectsArea / boundsArea);
300
301     return wastedSpace <= wastedSpaceThreshold;
302 }
303
304 void DrawingAreaImpl::display(UpdateInfo& updateInfo)
305 {
306     ASSERT(!m_isPaintingSuspended);
307     ASSERT(!m_layerTreeHost);
308
309     // FIXME: It would be better if we could avoid painting altogether when there is a custom representation.
310     if (m_webPage->mainFrameHasCustomRepresentation())
311         return;
312
313     IntRect bounds = m_dirtyRegion.bounds();
314     Vector<IntRect> rects = m_dirtyRegion.rects();
315
316     if (shouldPaintBoundsRect(bounds, rects)) {
317         rects.clear();
318         rects.append(bounds);
319     }
320
321     updateInfo.scrollRect = m_scrollRect;
322     updateInfo.scrollOffset = m_scrollOffset;
323
324     m_dirtyRegion = Region();
325     m_scrollRect = IntRect();
326     m_scrollOffset = IntSize();
327     
328     RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(bounds.size());
329     if (!bitmap->createHandle(updateInfo.bitmapHandle))
330         return;
331
332     OwnPtr<GraphicsContext> graphicsContext = bitmap->createGraphicsContext();
333
334     m_webPage->layoutIfNeeded();
335     
336     updateInfo.timestamp = currentTime();
337     updateInfo.viewSize = m_webPage->size();
338     updateInfo.updateRectBounds = bounds;
339
340     graphicsContext->translate(-bounds.x(), -bounds.y());
341
342     for (size_t i = 0; i < rects.size(); ++i) {
343         m_webPage->drawRect(*graphicsContext, rects[i]);
344         updateInfo.updateRects.append(rects[i]);
345     }
346         
347     // Layout can trigger more calls to setNeedsDisplay and we don't want to process them
348     // until the UI process has painted the update, so we stop the timer here.
349     m_displayTimer.stop();
350 }
351
352
353 } // namespace WebKit