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