• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <WebCore/Page.h>
38 #include <WebCore/Settings.h>
39 
40 #if !PLATFORM(MAC) && !PLATFORM(WIN)
41 #error "This drawing area is not ready for use by other ports yet."
42 #endif
43 
44 using namespace WebCore;
45 using namespace std;
46 
47 namespace WebKit {
48 
create(WebPage * webPage,const WebPageCreationParameters & parameters)49 PassOwnPtr<DrawingAreaImpl> DrawingAreaImpl::create(WebPage* webPage, const WebPageCreationParameters& parameters)
50 {
51     return adoptPtr(new DrawingAreaImpl(webPage, parameters));
52 }
53 
~DrawingAreaImpl()54 DrawingAreaImpl::~DrawingAreaImpl()
55 {
56     if (m_layerTreeHost)
57         m_layerTreeHost->invalidate();
58 }
59 
DrawingAreaImpl(WebPage * webPage,const WebPageCreationParameters & parameters)60 DrawingAreaImpl::DrawingAreaImpl(WebPage* webPage, const WebPageCreationParameters& parameters)
61     : DrawingArea(DrawingAreaTypeImpl, webPage)
62     , m_backingStoreStateID(0)
63     , m_inUpdateBackingStoreState(false)
64     , m_shouldSendDidUpdateBackingStoreState(false)
65     , m_isWaitingForDidUpdate(false)
66     , m_isPaintingSuspended(!parameters.isVisible)
67     , m_alwaysUseCompositing(false)
68     , m_lastDisplayTime(0)
69     , m_displayTimer(WebProcess::shared().runLoop(), this, &DrawingAreaImpl::displayTimerFired)
70     , m_exitCompositingTimer(WebProcess::shared().runLoop(), this, &DrawingAreaImpl::exitAcceleratedCompositingMode)
71 {
72     if (webPage->corePage()->settings()->acceleratedDrawingEnabled())
73         m_alwaysUseCompositing = true;
74 
75     if (m_alwaysUseCompositing)
76         enterAcceleratedCompositingMode(0);
77 }
78 
setNeedsDisplay(const IntRect & rect)79 void DrawingAreaImpl::setNeedsDisplay(const IntRect& rect)
80 {
81     IntRect dirtyRect = rect;
82     dirtyRect.intersect(m_webPage->bounds());
83 
84     if (dirtyRect.isEmpty())
85         return;
86 
87     if (m_layerTreeHost) {
88         ASSERT(m_dirtyRegion.isEmpty());
89 
90         m_layerTreeHost->setNonCompositedContentsNeedDisplay(dirtyRect);
91         return;
92     }
93 
94     if (m_webPage->mainFrameHasCustomRepresentation())
95         return;
96 
97     m_dirtyRegion.unite(dirtyRect);
98     scheduleDisplay();
99 }
100 
scroll(const IntRect & scrollRect,const IntSize & scrollOffset)101 void DrawingAreaImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
102 {
103     if (m_layerTreeHost) {
104         ASSERT(m_scrollRect.isEmpty());
105         ASSERT(m_scrollOffset.isEmpty());
106         ASSERT(m_dirtyRegion.isEmpty());
107 
108         m_layerTreeHost->scrollNonCompositedContents(scrollRect, scrollOffset);
109         return;
110     }
111 
112     if (m_webPage->mainFrameHasCustomRepresentation())
113         return;
114 
115     if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
116         unsigned scrollArea = scrollRect.width() * scrollRect.height();
117         unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
118 
119         if (currentScrollArea >= scrollArea) {
120             // The rect being scrolled is at least as large as the rect we'd like to scroll.
121             // Go ahead and just invalidate the scroll rect.
122             setNeedsDisplay(scrollRect);
123             return;
124         }
125 
126         // Just repaint the entire current scroll rect, we'll scroll the new rect instead.
127         setNeedsDisplay(m_scrollRect);
128         m_scrollRect = IntRect();
129         m_scrollOffset = IntSize();
130     }
131 
132     // Get the part of the dirty region that is in the scroll rect.
133     Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
134     if (!dirtyRegionInScrollRect.isEmpty()) {
135         // There are parts of the dirty region that are inside the scroll rect.
136         // We need to subtract them from the region, move them and re-add them.
137         m_dirtyRegion.subtract(scrollRect);
138 
139         // Move the dirty parts.
140         Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollOffset), scrollRect);
141 
142         // And add them back.
143         m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
144     }
145 
146     // Compute the scroll repaint region.
147     Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollOffset));
148 
149     m_dirtyRegion.unite(scrollRepaintRegion);
150 
151     m_scrollRect = scrollRect;
152     m_scrollOffset += scrollOffset;
153 }
154 
forceRepaint()155 void DrawingAreaImpl::forceRepaint()
156 {
157     setNeedsDisplay(m_webPage->bounds());
158 
159     m_webPage->layoutIfNeeded();
160 
161     if (m_layerTreeHost) {
162         m_layerTreeHost->forceRepaint();
163         if (!m_layerTreeHost->participatesInDisplay())
164             return;
165     }
166 
167     m_isWaitingForDidUpdate = false;
168     display();
169 }
170 
didInstallPageOverlay()171 void DrawingAreaImpl::didInstallPageOverlay()
172 {
173     if (m_layerTreeHost)
174         m_layerTreeHost->didInstallPageOverlay();
175 }
176 
didUninstallPageOverlay()177 void DrawingAreaImpl::didUninstallPageOverlay()
178 {
179     if (m_layerTreeHost)
180         m_layerTreeHost->didUninstallPageOverlay();
181 
182     setNeedsDisplay(m_webPage->bounds());
183 }
184 
setPageOverlayNeedsDisplay(const IntRect & rect)185 void DrawingAreaImpl::setPageOverlayNeedsDisplay(const IntRect& rect)
186 {
187     if (m_layerTreeHost) {
188         m_layerTreeHost->setPageOverlayNeedsDisplay(rect);
189         return;
190     }
191 
192     setNeedsDisplay(rect);
193 }
194 
setLayerHostNeedsDisplay()195 void DrawingAreaImpl::setLayerHostNeedsDisplay()
196 {
197     ASSERT(m_layerTreeHost);
198     ASSERT(m_layerTreeHost->participatesInDisplay());
199     scheduleDisplay();
200 }
201 
layerHostDidFlushLayers()202 void DrawingAreaImpl::layerHostDidFlushLayers()
203 {
204     ASSERT(m_layerTreeHost);
205 
206     m_layerTreeHost->forceRepaint();
207 
208     if (m_shouldSendDidUpdateBackingStoreState) {
209         sendDidUpdateBackingStoreState();
210         return;
211     }
212 
213     if (!m_layerTreeHost || m_layerTreeHost->participatesInDisplay()) {
214         // When the layer tree host participates in display, we never tell the UI process about
215         // accelerated compositing. From the UI process's point of view, we're still just sending
216         // it a series of bitmaps in Update messages.
217         return;
218     }
219 
220 #if USE(ACCELERATED_COMPOSITING)
221     m_webPage->send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(m_backingStoreStateID, m_layerTreeHost->layerTreeContext()));
222 #endif
223 }
224 
setRootCompositingLayer(GraphicsLayer * graphicsLayer)225 void DrawingAreaImpl::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
226 {
227     if (graphicsLayer) {
228         if (!m_layerTreeHost) {
229             // We're actually entering accelerated compositing mode.
230             enterAcceleratedCompositingMode(graphicsLayer);
231         } else {
232             m_exitCompositingTimer.stop();
233             // We're already in accelerated compositing mode, but the root compositing layer changed.
234             m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
235         }
236     } else {
237         if (m_layerTreeHost) {
238             m_layerTreeHost->setRootCompositingLayer(0);
239             if (!m_alwaysUseCompositing) {
240                 // We'll exit accelerated compositing mode on a timer, to avoid re-entering
241                 // compositing code via display() and layout.
242                 // If we're leaving compositing mode because of a setSize, it is safe to
243                 // exit accelerated compositing mode right away.
244                 if (m_inUpdateBackingStoreState)
245                     exitAcceleratedCompositingMode();
246                 else
247                     exitAcceleratedCompositingModeSoon();
248             }
249         }
250     }
251 }
252 
scheduleCompositingLayerSync()253 void DrawingAreaImpl::scheduleCompositingLayerSync()
254 {
255     if (!m_layerTreeHost)
256         return;
257     m_layerTreeHost->scheduleLayerFlush();
258 }
259 
syncCompositingLayers()260 void DrawingAreaImpl::syncCompositingLayers()
261 {
262 }
263 
didReceiveMessage(CoreIPC::Connection *,CoreIPC::MessageID,CoreIPC::ArgumentDecoder *)264 void DrawingAreaImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
265 {
266 }
267 
updateBackingStoreState(uint64_t stateID,bool respondImmediately,const WebCore::IntSize & size,const WebCore::IntSize & scrollOffset)268 void DrawingAreaImpl::updateBackingStoreState(uint64_t stateID, bool respondImmediately, const WebCore::IntSize& size, const WebCore::IntSize& scrollOffset)
269 {
270     ASSERT(!m_inUpdateBackingStoreState);
271     m_inUpdateBackingStoreState = true;
272 
273     ASSERT_ARG(stateID, stateID >= m_backingStoreStateID);
274     if (stateID != m_backingStoreStateID) {
275         m_backingStoreStateID = stateID;
276         m_shouldSendDidUpdateBackingStoreState = true;
277 
278         m_webPage->setSize(size);
279         m_webPage->layoutIfNeeded();
280         m_webPage->scrollMainFrameIfNotAtMaxScrollPosition(scrollOffset);
281 
282         if (m_layerTreeHost)
283             m_layerTreeHost->sizeDidChange(size);
284         else
285             m_dirtyRegion = m_webPage->bounds();
286     } else {
287         ASSERT(size == m_webPage->size());
288         if (!m_shouldSendDidUpdateBackingStoreState) {
289             // We've already sent a DidUpdateBackingStoreState message for this state. We have nothing more to do.
290             m_inUpdateBackingStoreState = false;
291             return;
292         }
293     }
294 
295     // The UI process has updated to a new backing store state. Any Update messages we sent before
296     // this point will be ignored. We wait to set this to false until after updating the page's
297     // size so that any displays triggered by the relayout will be ignored. If we're supposed to
298     // respond to the UpdateBackingStoreState message immediately, we'll do a display anyway in
299     // sendDidUpdateBackingStoreState; otherwise we shouldn't do one right now.
300     m_isWaitingForDidUpdate = false;
301 
302     if (respondImmediately)
303         sendDidUpdateBackingStoreState();
304 
305     m_inUpdateBackingStoreState = false;
306 }
307 
sendDidUpdateBackingStoreState()308 void DrawingAreaImpl::sendDidUpdateBackingStoreState()
309 {
310     ASSERT(!m_isWaitingForDidUpdate);
311     ASSERT(m_shouldSendDidUpdateBackingStoreState);
312 
313     m_shouldSendDidUpdateBackingStoreState = false;
314 
315     UpdateInfo updateInfo;
316 
317     if (!m_isPaintingSuspended && (!m_layerTreeHost || m_layerTreeHost->participatesInDisplay()))
318         display(updateInfo);
319 
320 #if USE(ACCELERATED_COMPOSITING)
321     LayerTreeContext layerTreeContext;
322 
323     if (m_isPaintingSuspended || (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())) {
324         updateInfo.viewSize = m_webPage->size();
325 
326         if (m_layerTreeHost) {
327             layerTreeContext = m_layerTreeHost->layerTreeContext();
328 
329             // We don't want the layer tree host to notify after the next scheduled
330             // layer flush because that might end up sending an EnterAcceleratedCompositingMode
331             // message back to the UI process, but the updated layer tree context
332             // will be sent back in the DidUpdateBackingStoreState message.
333             m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(false);
334             m_layerTreeHost->forceRepaint();
335         }
336     }
337 
338     m_webPage->send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
339 #endif
340 }
341 
didUpdate()342 void DrawingAreaImpl::didUpdate()
343 {
344     // We might get didUpdate messages from the UI process even after we've
345     // entered accelerated compositing mode. Ignore them.
346     if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())
347         return;
348 
349     m_isWaitingForDidUpdate = false;
350 
351     // Display if needed. We call displayTimerFired here since it will throttle updates to 60fps.
352     displayTimerFired();
353 }
354 
suspendPainting()355 void DrawingAreaImpl::suspendPainting()
356 {
357     ASSERT(!m_isPaintingSuspended);
358 
359     if (m_layerTreeHost)
360         m_layerTreeHost->pauseRendering();
361 
362     m_isPaintingSuspended = true;
363     m_displayTimer.stop();
364 }
365 
resumePainting()366 void DrawingAreaImpl::resumePainting()
367 {
368     if (!m_isPaintingSuspended) {
369         // FIXME: We can get a call to resumePainting when painting is not suspended.
370         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
371         return;
372     }
373 
374     if (m_layerTreeHost)
375         m_layerTreeHost->resumeRendering();
376 
377     m_isPaintingSuspended = false;
378 
379     // FIXME: We shouldn't always repaint everything here.
380     setNeedsDisplay(m_webPage->bounds());
381 }
382 
enterAcceleratedCompositingMode(GraphicsLayer * graphicsLayer)383 void DrawingAreaImpl::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
384 {
385     m_exitCompositingTimer.stop();
386 
387     ASSERT(!m_layerTreeHost);
388 
389     m_layerTreeHost = LayerTreeHost::create(m_webPage);
390     if (!m_inUpdateBackingStoreState)
391         m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(true);
392 
393     m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
394 
395     // Non-composited content will now be handled exclusively by the layer tree host.
396     m_dirtyRegion = Region();
397     m_scrollRect = IntRect();
398     m_scrollOffset = IntSize();
399 
400     if (!m_layerTreeHost->participatesInDisplay()) {
401         m_displayTimer.stop();
402         m_isWaitingForDidUpdate = false;
403     }
404 }
405 
exitAcceleratedCompositingMode()406 void DrawingAreaImpl::exitAcceleratedCompositingMode()
407 {
408     if (m_alwaysUseCompositing)
409         return;
410 
411     m_exitCompositingTimer.stop();
412 
413     ASSERT(m_layerTreeHost);
414 
415     bool wasParticipatingInDisplay = m_layerTreeHost->participatesInDisplay();
416 
417     m_layerTreeHost->invalidate();
418     m_layerTreeHost = nullptr;
419     m_dirtyRegion = m_webPage->bounds();
420 
421     if (m_inUpdateBackingStoreState)
422         return;
423 
424     if (m_shouldSendDidUpdateBackingStoreState) {
425         sendDidUpdateBackingStoreState();
426         return;
427     }
428 
429     UpdateInfo updateInfo;
430     if (m_isPaintingSuspended)
431         updateInfo.viewSize = m_webPage->size();
432     else
433         display(updateInfo);
434 
435 #if USE(ACCELERATED_COMPOSITING)
436     if (wasParticipatingInDisplay) {
437         // When the layer tree host participates in display, we never tell the UI process about
438         // accelerated compositing. From the UI process's point of view, we're still just sending
439         // it a series of bitmaps in Update messages.
440         m_webPage->send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
441     } else {
442         // Send along a complete update of the page so we can paint the contents right after we exit the
443         // accelerated compositing mode, eliminiating flicker.
444         m_webPage->send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode(m_backingStoreStateID, updateInfo));
445     }
446 #endif
447 }
448 
exitAcceleratedCompositingModeSoon()449 void DrawingAreaImpl::exitAcceleratedCompositingModeSoon()
450 {
451     if (m_exitCompositingTimer.isActive())
452         return;
453 
454     m_exitCompositingTimer.startOneShot(0);
455 }
456 
scheduleDisplay()457 void DrawingAreaImpl::scheduleDisplay()
458 {
459     ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
460 
461     if (m_isWaitingForDidUpdate)
462         return;
463 
464     if (m_isPaintingSuspended)
465         return;
466 
467     if (m_layerTreeHost) {
468         if (!m_layerTreeHost->needsDisplay())
469             return;
470     } else if (m_dirtyRegion.isEmpty())
471             return;
472 
473     if (m_displayTimer.isActive())
474         return;
475 
476     m_displayTimer.startOneShot(0);
477 }
478 
displayTimerFired()479 void DrawingAreaImpl::displayTimerFired()
480 {
481     static const double minimumFrameInterval = 1.0 / 60.0;
482 
483     double timeSinceLastDisplay = currentTime() - m_lastDisplayTime;
484     double timeUntilLayerTreeHostNeedsDisplay = m_layerTreeHost && m_layerTreeHost->participatesInDisplay() ? m_layerTreeHost->timeUntilNextDisplay() : 0;
485     double timeUntilNextDisplay = max(minimumFrameInterval - timeSinceLastDisplay, timeUntilLayerTreeHostNeedsDisplay);
486 
487     if (timeUntilNextDisplay > 0) {
488         m_displayTimer.startOneShot(timeUntilNextDisplay);
489         return;
490     }
491 
492     display();
493 }
494 
display()495 void DrawingAreaImpl::display()
496 {
497     ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
498     ASSERT(!m_isWaitingForDidUpdate);
499     ASSERT(!m_inUpdateBackingStoreState);
500 
501     if (m_isPaintingSuspended)
502         return;
503 
504     if (m_layerTreeHost) {
505         if (!m_layerTreeHost->needsDisplay())
506             return;
507     } else if (m_dirtyRegion.isEmpty())
508         return;
509 
510     if (m_shouldSendDidUpdateBackingStoreState) {
511         sendDidUpdateBackingStoreState();
512         return;
513     }
514 
515     UpdateInfo updateInfo;
516     display(updateInfo);
517 
518     if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay()) {
519         // The call to update caused layout which turned on accelerated compositing.
520         // Don't send an Update message in this case.
521         return;
522     }
523 
524     m_webPage->send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
525     m_isWaitingForDidUpdate = true;
526 }
527 
shouldPaintBoundsRect(const IntRect & bounds,const Vector<IntRect> & rects)528 static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
529 {
530     const size_t rectThreshold = 10;
531     const float wastedSpaceThreshold = 0.75f;
532 
533     if (rects.size() <= 1 || rects.size() > rectThreshold)
534         return true;
535 
536     // Attempt to guess whether or not we should use the region bounds rect or the individual rects.
537     // We do this by computing the percentage of "wasted space" in the bounds.  If that wasted space
538     // is too large, then we will do individual rect painting instead.
539     unsigned boundsArea = bounds.width() * bounds.height();
540     unsigned rectsArea = 0;
541     for (size_t i = 0; i < rects.size(); ++i)
542         rectsArea += rects[i].width() * rects[i].height();
543 
544     float wastedSpace = 1 - (rectsArea / boundsArea);
545 
546     return wastedSpace <= wastedSpaceThreshold;
547 }
548 
display(UpdateInfo & updateInfo)549 void DrawingAreaImpl::display(UpdateInfo& updateInfo)
550 {
551     ASSERT(!m_isPaintingSuspended);
552     ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
553     ASSERT(!m_webPage->size().isEmpty());
554 
555     // FIXME: It would be better if we could avoid painting altogether when there is a custom representation.
556     if (m_webPage->mainFrameHasCustomRepresentation()) {
557         // ASSUMPTION: the custom representation will be painting the dirty region for us.
558         m_dirtyRegion = Region();
559         return;
560     }
561 
562     m_webPage->layoutIfNeeded();
563 
564     // The layout may have put the page into accelerated compositing mode. If the LayerTreeHost is
565     // in charge of displaying, we have nothing more to do.
566     if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())
567         return;
568 
569     updateInfo.viewSize = m_webPage->size();
570 
571     if (m_layerTreeHost)
572         m_layerTreeHost->display(updateInfo);
573     else {
574         IntRect bounds = m_dirtyRegion.bounds();
575         ASSERT(m_webPage->bounds().contains(bounds));
576 
577         RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(bounds.size(), ShareableBitmap::SupportsAlpha);
578         if (!bitmap->createHandle(updateInfo.bitmapHandle))
579             return;
580 
581         Vector<IntRect> rects = m_dirtyRegion.rects();
582 
583         if (shouldPaintBoundsRect(bounds, rects)) {
584             rects.clear();
585             rects.append(bounds);
586         }
587 
588         updateInfo.scrollRect = m_scrollRect;
589         updateInfo.scrollOffset = m_scrollOffset;
590 
591         m_dirtyRegion = Region();
592         m_scrollRect = IntRect();
593         m_scrollOffset = IntSize();
594 
595         OwnPtr<GraphicsContext> graphicsContext = bitmap->createGraphicsContext();
596 
597         updateInfo.updateRectBounds = bounds;
598 
599         graphicsContext->translate(-bounds.x(), -bounds.y());
600 
601         for (size_t i = 0; i < rects.size(); ++i) {
602             m_webPage->drawRect(*graphicsContext, rects[i]);
603             if (m_webPage->hasPageOverlay())
604                 m_webPage->drawPageOverlay(*graphicsContext, rects[i]);
605             updateInfo.updateRects.append(rects[i]);
606         }
607     }
608 
609     // Layout can trigger more calls to setNeedsDisplay and we don't want to process them
610     // until the UI process has painted the update, so we stop the timer here.
611     m_displayTimer.stop();
612 
613     m_lastDisplayTime = currentTime();
614 }
615 
616 
617 } // namespace WebKit
618