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