Have fixedPositionCreatesStackingContext be on by default everywhere
[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 "LayerTreeHost.h"
31 #include "ShareableBitmap.h"
32 #include "UpdateInfo.h"
33 #include "WebPage.h"
34 #include "WebPageCreationParameters.h"
35 #include "WebPreferencesKeys.h"
36 #include <WebCore/GraphicsContext.h>
37 #include <WebCore/Page.h>
38 #include <WebCore/Settings.h>
39
40 using namespace WebCore;
41
42 namespace WebKit {
43
44 DrawingAreaImpl::~DrawingAreaImpl()
45 {
46 }
47
48 DrawingAreaImpl::DrawingAreaImpl(WebPage& webPage, const WebPageCreationParameters& parameters)
49     : AcceleratedDrawingArea(webPage, parameters)
50     , m_displayTimer(RunLoop::main(), this, &DrawingAreaImpl::displayTimerFired)
51 {
52 }
53
54 void DrawingAreaImpl::setNeedsDisplay()
55 {
56     if (m_layerTreeHost) {
57         ASSERT(m_dirtyRegion.isEmpty());
58         AcceleratedDrawingArea::setNeedsDisplay();
59         return;
60     }
61
62     setNeedsDisplayInRect(m_webPage.bounds());
63 }
64
65 void DrawingAreaImpl::setNeedsDisplayInRect(const IntRect& rect)
66 {
67     if (m_layerTreeHost) {
68         ASSERT(m_dirtyRegion.isEmpty());
69         AcceleratedDrawingArea::setNeedsDisplayInRect(rect);
70         return;
71     }
72
73     if (!m_isPaintingEnabled)
74         return;
75
76     IntRect dirtyRect = rect;
77     dirtyRect.intersect(m_webPage.bounds());
78     if (dirtyRect.isEmpty())
79         return;
80
81     m_dirtyRegion.unite(dirtyRect);
82     scheduleDisplay();
83 }
84
85 void DrawingAreaImpl::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
86 {
87     if (m_layerTreeHost) {
88         ASSERT(m_scrollRect.isEmpty());
89         ASSERT(m_scrollOffset.isEmpty());
90         ASSERT(m_dirtyRegion.isEmpty());
91         AcceleratedDrawingArea::scroll(scrollRect, scrollDelta);
92         return;
93     }
94
95     if (!m_isPaintingEnabled)
96         return;
97
98     if (scrollRect.isEmpty())
99         return;
100
101     if (m_previousLayerTreeHost)
102         m_previousLayerTreeHost->scrollNonCompositedContents(scrollRect);
103
104     if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
105         unsigned scrollArea = scrollRect.width() * scrollRect.height();
106         unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
107
108         if (currentScrollArea >= scrollArea) {
109             // The rect being scrolled is at least as large as the rect we'd like to scroll.
110             // Go ahead and just invalidate the scroll rect.
111             setNeedsDisplayInRect(scrollRect);
112             return;
113         }
114
115         // Just repaint the entire current scroll rect, we'll scroll the new rect instead.
116         setNeedsDisplayInRect(m_scrollRect);
117         m_scrollRect = IntRect();
118         m_scrollOffset = IntSize();
119     }
120
121     // Get the part of the dirty region that is in the scroll rect.
122     Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
123     if (!dirtyRegionInScrollRect.isEmpty()) {
124         // There are parts of the dirty region that are inside the scroll rect.
125         // We need to subtract them from the region, move them and re-add them.
126         m_dirtyRegion.subtract(scrollRect);
127
128         // Move the dirty parts.
129         Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollDelta), scrollRect);
130
131         // And add them back.
132         m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
133     }
134
135     // Compute the scroll repaint region.
136     Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollDelta));
137
138     m_dirtyRegion.unite(scrollRepaintRegion);
139     scheduleDisplay();
140
141     m_scrollRect = scrollRect;
142     m_scrollOffset += scrollDelta;
143 }
144
145 void DrawingAreaImpl::forceRepaint()
146 {
147     if (m_inUpdateBackingStoreState) {
148         m_forceRepaintAfterBackingStoreStateUpdate = true;
149         return;
150     }
151     m_forceRepaintAfterBackingStoreStateUpdate = false;
152
153     if (m_layerTreeHost) {
154         AcceleratedDrawingArea::forceRepaint();
155         return;
156     }
157
158     m_isWaitingForDidUpdate = false;
159     if (m_isPaintingEnabled) {
160         m_dirtyRegion = m_webPage.bounds();
161         display();
162     }
163 }
164
165 void DrawingAreaImpl::mainFrameContentSizeChanged(const WebCore::IntSize& newSize)
166 {
167 #if USE(COORDINATED_GRAPHICS_THREADED)
168     if (m_layerTreeHost)
169         m_layerTreeHost->contentsSizeChanged(newSize);
170     else if (m_previousLayerTreeHost)
171         m_previousLayerTreeHost->contentsSizeChanged(newSize);
172 #else
173     UNUSED_PARAM(newSize);
174 #endif
175 }
176
177 void DrawingAreaImpl::updatePreferences(const WebPreferencesStore& store)
178 {
179     Settings& settings = m_webPage.corePage()->settings();
180     settings.setForceCompositingMode(store.getBoolValueForKey(WebPreferencesKey::forceCompositingModeKey()));
181
182 #if USE(COORDINATED_GRAPHICS_THREADED)
183     // Fixed position elements need to be composited and create stacking contexts
184     // in order to be scrolled by the ScrollingCoordinator.
185     settings.setAcceleratedCompositingForFixedPositionEnabled(settings.acceleratedCompositingEnabled());
186 #endif
187
188     m_alwaysUseCompositing = settings.acceleratedCompositingEnabled() && settings.forceCompositingMode();
189     if (m_alwaysUseCompositing && !m_layerTreeHost)
190         enterAcceleratedCompositingMode(nullptr);
191 }
192
193 void DrawingAreaImpl::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
194 {
195     if (m_layerTreeHost) {
196         AcceleratedDrawingArea::setRootCompositingLayer(graphicsLayer);
197
198         if (!graphicsLayer && !m_alwaysUseCompositing) {
199             // We'll exit accelerated compositing mode on a timer, to avoid re-entering
200             // compositing code via display() and layout.
201             // If we're leaving compositing mode because of a setSize, it is safe to
202             // exit accelerated compositing mode right away.
203             if (m_inUpdateBackingStoreState)
204                 exitAcceleratedCompositingMode();
205             else
206                 exitAcceleratedCompositingModeSoon();
207         }
208         return;
209     }
210
211     if (!graphicsLayer)
212         return;
213
214     // We're actually entering accelerated compositing mode.
215     enterAcceleratedCompositingMode(graphicsLayer);
216 }
217
218 void DrawingAreaImpl::updateBackingStoreState(uint64_t stateID, bool respondImmediately, float deviceScaleFactor, const WebCore::IntSize& size, const WebCore::IntSize& scrollOffset)
219 {
220     if (stateID != m_backingStoreStateID && !m_layerTreeHost)
221         m_dirtyRegion = IntRect(IntPoint(), size);
222
223     AcceleratedDrawingArea::updateBackingStoreState(stateID, respondImmediately, deviceScaleFactor, size, scrollOffset);
224
225     if (m_forceRepaintAfterBackingStoreStateUpdate)
226         forceRepaint();
227 }
228
229 void DrawingAreaImpl::didUpdateBackingStoreState()
230 {
231     // The UI process has updated to a new backing store state. Any Update messages we sent before
232     // this point will be ignored. We wait to set this to false until after updating the page's
233     // size so that any displays triggered by the relayout will be ignored. If we're supposed to
234     // respond to the UpdateBackingStoreState message immediately, we'll do a display anyway in
235     // sendDidUpdateBackingStoreState; otherwise we shouldn't do one right now.
236     m_isWaitingForDidUpdate = false;
237 }
238
239 void DrawingAreaImpl::sendDidUpdateBackingStoreState()
240 {
241     ASSERT(!m_isWaitingForDidUpdate);
242     ASSERT(m_shouldSendDidUpdateBackingStoreState);
243
244     if (!m_isPaintingSuspended && !m_layerTreeHost) {
245         UpdateInfo updateInfo;
246         display(updateInfo);
247         if (!m_layerTreeHost) {
248             m_shouldSendDidUpdateBackingStoreState = false;
249
250             LayerTreeContext layerTreeContext;
251             m_webPage.send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
252             m_compositingAccordingToProxyMessages = false;
253             return;
254         }
255     }
256
257     AcceleratedDrawingArea::sendDidUpdateBackingStoreState();
258 }
259
260 void DrawingAreaImpl::didUpdate()
261 {
262     // We might get didUpdate messages from the UI process even after we've
263     // entered accelerated compositing mode. Ignore them.
264     if (m_layerTreeHost)
265         return;
266
267     m_isWaitingForDidUpdate = false;
268
269     // Display if needed. We call displayTimerFired here since it will throttle updates to 60fps.
270     displayTimerFired();
271 }
272
273 void DrawingAreaImpl::suspendPainting()
274 {
275     AcceleratedDrawingArea::suspendPainting();
276     m_displayTimer.stop();
277 }
278
279 void DrawingAreaImpl::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
280 {
281     AcceleratedDrawingArea::enterAcceleratedCompositingMode(graphicsLayer);
282
283     // Non-composited content will now be handled exclusively by the layer tree host.
284     m_dirtyRegion = Region();
285     m_scrollRect = IntRect();
286     m_scrollOffset = IntSize();
287     m_displayTimer.stop();
288     m_isWaitingForDidUpdate = false;
289 }
290
291 void DrawingAreaImpl::exitAcceleratedCompositingMode()
292 {
293     if (m_alwaysUseCompositing)
294         return;
295
296     AcceleratedDrawingArea::exitAcceleratedCompositingModeNow();
297     m_dirtyRegion = m_webPage.bounds();
298
299     if (m_inUpdateBackingStoreState)
300         return;
301
302     if (m_shouldSendDidUpdateBackingStoreState) {
303         sendDidUpdateBackingStoreState();
304         return;
305     }
306
307     UpdateInfo updateInfo;
308     if (m_isPaintingSuspended) {
309         updateInfo.viewSize = m_webPage.size();
310         updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
311     } else
312         display(updateInfo);
313
314     // Send along a complete update of the page so we can paint the contents right after we exit the
315     // accelerated compositing mode, eliminiating flicker.
316     if (m_compositingAccordingToProxyMessages) {
317         m_webPage.send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode(m_backingStoreStateID, updateInfo));
318         m_compositingAccordingToProxyMessages = false;
319     } else {
320         // If we left accelerated compositing mode before we sent an EnterAcceleratedCompositingMode message to the
321         // UI process, we still need to let it know about the new contents, so send an Update message.
322         m_webPage.send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
323     }
324 }
325
326 void DrawingAreaImpl::scheduleDisplay()
327 {
328     ASSERT(!m_layerTreeHost);
329
330     if (m_isWaitingForDidUpdate)
331         return;
332
333     if (m_isPaintingSuspended)
334         return;
335
336     if (m_dirtyRegion.isEmpty())
337         return;
338
339     if (m_displayTimer.isActive())
340         return;
341
342     m_displayTimer.startOneShot(0);
343 }
344
345 void DrawingAreaImpl::displayTimerFired()
346 {
347     display();
348 }
349
350 void DrawingAreaImpl::display()
351 {
352     ASSERT(!m_layerTreeHost);
353     ASSERT(!m_isWaitingForDidUpdate);
354     ASSERT(!m_inUpdateBackingStoreState);
355
356     if (m_isPaintingSuspended)
357         return;
358
359     if (m_dirtyRegion.isEmpty())
360         return;
361
362     if (m_shouldSendDidUpdateBackingStoreState) {
363         sendDidUpdateBackingStoreState();
364         return;
365     }
366
367     UpdateInfo updateInfo;
368     display(updateInfo);
369
370     if (m_layerTreeHost) {
371         // The call to update caused layout which turned on accelerated compositing.
372         // Don't send an Update message in this case.
373         return;
374     }
375
376     m_webPage.send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
377     m_isWaitingForDidUpdate = true;
378 }
379
380 static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
381 {
382     const size_t rectThreshold = 10;
383     const double wastedSpaceThreshold = 0.75;
384
385     if (rects.size() <= 1 || rects.size() > rectThreshold)
386         return true;
387
388     // Attempt to guess whether or not we should use the region bounds rect or the individual rects.
389     // We do this by computing the percentage of "wasted space" in the bounds.  If that wasted space
390     // is too large, then we will do individual rect painting instead.
391     unsigned boundsArea = bounds.width() * bounds.height();
392     unsigned rectsArea = 0;
393     for (size_t i = 0; i < rects.size(); ++i)
394         rectsArea += rects[i].width() * rects[i].height();
395
396     double wastedSpace = 1 - (static_cast<double>(rectsArea) / boundsArea);
397
398     return wastedSpace <= wastedSpaceThreshold;
399 }
400
401 void DrawingAreaImpl::display(UpdateInfo& updateInfo)
402 {
403     ASSERT(!m_isPaintingSuspended);
404     ASSERT(!m_layerTreeHost);
405     ASSERT(!m_webPage.size().isEmpty());
406
407     m_webPage.layoutIfNeeded();
408
409     // The layout may have put the page into accelerated compositing mode. If the LayerTreeHost is
410     // in charge of displaying, we have nothing more to do.
411     if (m_layerTreeHost)
412         return;
413
414     updateInfo.viewSize = m_webPage.size();
415     updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
416
417     // Always render the whole page when we don't render the background.
418     IntRect bounds = m_webPage.drawsBackground() ? m_dirtyRegion.bounds() : m_webPage.bounds();
419     ASSERT(m_webPage.bounds().contains(bounds));
420
421     IntSize bitmapSize = bounds.size();
422     float deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
423     bitmapSize.scale(deviceScaleFactor);
424     RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(bitmapSize, ShareableBitmap::SupportsAlpha);
425     if (!bitmap)
426         return;
427
428     if (!bitmap->createHandle(updateInfo.bitmapHandle))
429         return;
430
431     Vector<IntRect> rects;
432     if (m_webPage.drawsBackground()) {
433         rects = m_dirtyRegion.rects();
434
435         if (shouldPaintBoundsRect(bounds, rects)) {
436             rects.clear();
437             rects.append(bounds);
438         }
439     } else
440         rects.append(bounds);
441
442     updateInfo.scrollRect = m_scrollRect;
443     updateInfo.scrollOffset = m_scrollOffset;
444
445     m_dirtyRegion = Region();
446     m_scrollRect = IntRect();
447     m_scrollOffset = IntSize();
448
449     auto graphicsContext = bitmap->createGraphicsContext();
450     graphicsContext->applyDeviceScaleFactor(deviceScaleFactor);
451     
452     updateInfo.updateRectBounds = bounds;
453
454     graphicsContext->translate(-bounds.x(), -bounds.y());
455
456     for (const auto& rect : rects) {
457         m_webPage.drawRect(*graphicsContext, rect);
458         updateInfo.updateRects.append(rect);
459     }
460
461     // Layout can trigger more calls to setNeedsDisplay and we don't want to process them
462     // until the UI process has painted the update, so we stop the timer here.
463     m_displayTimer.stop();
464 }
465
466 } // namespace WebKit