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