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