Home | History | Annotate | Download | only in WebPage
      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 
     49 PassOwnPtr<DrawingAreaImpl> DrawingAreaImpl::create(WebPage* webPage, const WebPageCreationParameters& parameters)
     50 {
     51     return adoptPtr(new DrawingAreaImpl(webPage, parameters));
     52 }
     53 
     54 DrawingAreaImpl::~DrawingAreaImpl()
     55 {
     56     if (m_layerTreeHost)
     57         m_layerTreeHost->invalidate();
     58 }
     59 
     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 
     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 
    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 
    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 
    171 void DrawingAreaImpl::didInstallPageOverlay()
    172 {
    173     if (m_layerTreeHost)
    174         m_layerTreeHost->didInstallPageOverlay();
    175 }
    176 
    177 void DrawingAreaImpl::didUninstallPageOverlay()
    178 {
    179     if (m_layerTreeHost)
    180         m_layerTreeHost->didUninstallPageOverlay();
    181 
    182     setNeedsDisplay(m_webPage->bounds());
    183 }
    184 
    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 
    195 void DrawingAreaImpl::setLayerHostNeedsDisplay()
    196 {
    197     ASSERT(m_layerTreeHost);
    198     ASSERT(m_layerTreeHost->participatesInDisplay());
    199     scheduleDisplay();
    200 }
    201 
    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 
    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 
    253 void DrawingAreaImpl::scheduleCompositingLayerSync()
    254 {
    255     if (!m_layerTreeHost)
    256         return;
    257     m_layerTreeHost->scheduleLayerFlush();
    258 }
    259 
    260 void DrawingAreaImpl::syncCompositingLayers()
    261 {
    262 }
    263 
    264 void DrawingAreaImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
    265 {
    266 }
    267 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    449 void DrawingAreaImpl::exitAcceleratedCompositingModeSoon()
    450 {
    451     if (m_exitCompositingTimer.isActive())
    452         return;
    453 
    454     m_exitCompositingTimer.startOneShot(0);
    455 }
    456 
    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 
    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 
    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 
    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 
    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