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