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