Tiled scrolling indicator needs to take topContentInset into account
[WebKit-https.git] / Source / WebCore / platform / graphics / ca / mac / TileController.mm
1 /*
2  * Copyright (C) 2011, 2012, 2013 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 #import "config.h"
27 #import "TileController.h"
28
29 #import "IntRect.h"
30 #import "PlatformCALayer.h"
31 #import "Region.h"
32 #import "TileCoverageMap.h"
33 #import "TileGrid.h"
34 #import "WebLayer.h"
35 #if !PLATFORM(IOS)
36 #import "LayerPool.h"
37 #endif
38 #import <wtf/MainThread.h>
39 #import <utility>
40
41 #if PLATFORM(IOS)
42 #import "TileControllerMemoryHandlerIOS.h"
43 #endif
44
45 namespace WebCore {
46
47 PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformLayer)
48 {
49     return adoptPtr(new TileController(rootPlatformLayer));
50 }
51
52 TileController::TileController(PlatformCALayer* rootPlatformLayer)
53     : m_tileCacheLayer(rootPlatformLayer)
54     , m_tileGrid(std::make_unique<TileGrid>(*this))
55     , m_tileSize(defaultTileWidth, defaultTileHeight)
56     , m_tileRevalidationTimer(this, &TileController::tileRevalidationTimerFired)
57     , m_zoomedOutContentsScale(0)
58     , m_deviceScaleFactor(owningGraphicsLayer()->platformCALayerDeviceScaleFactor())
59     , m_tileCoverage(CoverageForVisibleArea)
60     , m_marginTop(0)
61     , m_marginBottom(0)
62     , m_marginLeft(0)
63     , m_marginRight(0)
64     , m_isInWindow(false)
65     , m_scrollingPerformanceLoggingEnabled(false)
66     , m_unparentsOffscreenTiles(false)
67     , m_acceleratesDrawing(false)
68     , m_tilesAreOpaque(false)
69     , m_hasTilesWithTemporaryScaleFactor(false)
70     , m_tileDebugBorderWidth(0)
71     , m_indicatorMode(AsyncScrollingIndication)
72     , m_topContentInset(0)
73 {
74 }
75
76 TileController::~TileController()
77 {
78     ASSERT(isMainThread());
79
80 #if PLATFORM(IOS)
81     tileControllerMemoryHandler().removeTileController(this);
82 #endif
83 }
84
85 void TileController::tileCacheLayerBoundsChanged()
86 {
87     ASSERT(owningGraphicsLayer()->isCommittingChanges());
88     setNeedsRevalidateTiles();
89 }
90
91 void TileController::setNeedsDisplay()
92 {
93     tileGrid().setNeedsDisplay();
94     m_zoomedOutTileGrid = nullptr;
95 }
96
97 void TileController::setNeedsDisplayInRect(const IntRect& rect)
98 {
99     tileGrid().setNeedsDisplayInRect(rect);
100     if (m_zoomedOutTileGrid)
101         m_zoomedOutTileGrid->dropTilesInRect(rect);
102 }
103
104 void TileController::setContentsScale(float scale)
105 {
106     ASSERT(owningGraphicsLayer()->isCommittingChanges());
107
108     float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
109     // The scale we get is the product of the page scale factor and device scale factor.
110     // Divide by the device scale factor so we'll get the page scale factor.
111     scale /= deviceScaleFactor;
112
113     if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
114         return;
115
116     m_hasTilesWithTemporaryScaleFactor = false;
117     m_deviceScaleFactor = deviceScaleFactor;
118
119     if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() == scale) {
120         m_tileGrid = std::move(m_zoomedOutTileGrid);
121         m_tileGrid->revalidateTiles(0);
122         return;
123     }
124
125     if (m_zoomedOutContentsScale && m_zoomedOutContentsScale == tileGrid().scale() && tileGrid().scale() != scale && !m_hasTilesWithTemporaryScaleFactor) {
126         m_zoomedOutTileGrid = std::move(m_tileGrid);
127         m_tileGrid = std::make_unique<TileGrid>(*this);
128     }
129
130     tileGrid().setScale(scale);
131     tileGrid().setNeedsDisplay();
132 }
133
134 float TileController::contentsScale() const
135 {
136     return tileGrid().scale() * m_deviceScaleFactor;
137 }
138
139 float TileController::zoomedOutContentsScale() const
140 {
141     return m_zoomedOutContentsScale * m_deviceScaleFactor;
142 }
143
144 void TileController::setZoomedOutContentsScale(float scale)
145 {
146     ASSERT(owningGraphicsLayer()->isCommittingChanges());
147
148     float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
149     scale /= deviceScaleFactor;
150
151     if (m_zoomedOutContentsScale == scale)
152         return;
153     m_zoomedOutContentsScale = scale;
154
155     if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() != m_zoomedOutContentsScale)
156         m_zoomedOutTileGrid = nullptr;
157 }
158
159 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
160 {
161     if (m_acceleratesDrawing == acceleratesDrawing)
162         return;
163     m_acceleratesDrawing = acceleratesDrawing;
164
165     tileGrid().updateTilerLayerProperties();
166 }
167
168 void TileController::setTilesOpaque(bool opaque)
169 {
170     if (opaque == m_tilesAreOpaque)
171         return;
172     m_tilesAreOpaque = opaque;
173
174     tileGrid().updateTilerLayerProperties();
175 }
176
177 void TileController::setVisibleRect(const FloatRect& visibleRect)
178 {
179     ASSERT(owningGraphicsLayer()->isCommittingChanges());
180     if (m_visibleRect == visibleRect)
181         return;
182
183     m_visibleRect = visibleRect;
184     setNeedsRevalidateTiles();
185 }
186
187 bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const
188 {
189     if (bounds().isEmpty())
190         return false;
191     return tileGrid().tilesWouldChangeForVisibleRect(newVisibleRect, m_visibleRect);
192 }
193
194 void TileController::setTopContentInset(float topContentInset)
195 {
196     m_topContentInset = topContentInset;
197     setTiledScrollingIndicatorPosition(FloatPoint(0, m_topContentInset));
198 }
199
200 void TileController::setTiledScrollingIndicatorPosition(const FloatPoint& position)
201 {
202     if (!m_coverageMap)
203         return;
204     m_coverageMap->setPosition(position);
205     m_coverageMap->update();
206 }
207
208 void TileController::prepopulateRect(const FloatRect& rect)
209 {
210     if (tileGrid().prepopulateRect(rect))
211         setNeedsRevalidateTiles();
212 }
213
214 void TileController::setIsInWindow(bool isInWindow)
215 {
216     if (m_isInWindow == isInWindow)
217         return;
218
219     m_isInWindow = isInWindow;
220
221     if (m_isInWindow)
222         setNeedsRevalidateTiles();
223     else {
224         const double tileRevalidationTimeout = 4;
225         scheduleTileRevalidation(tileRevalidationTimeout);
226     }
227 }
228
229 void TileController::setTileCoverage(TileCoverage coverage)
230 {
231     if (coverage == m_tileCoverage)
232         return;
233
234     m_tileCoverage = coverage;
235     setNeedsRevalidateTiles();
236 }
237
238 void TileController::revalidateTiles()
239 {
240     ASSERT(owningGraphicsLayer()->isCommittingChanges());
241     tileGrid().revalidateTiles(0);
242     m_visibleRectAtLastRevalidate = m_visibleRect;
243 }
244
245 void TileController::forceRepaint()
246 {
247     setNeedsDisplay();
248 }
249
250 void TileController::setTileDebugBorderWidth(float borderWidth)
251 {
252     if (m_tileDebugBorderWidth == borderWidth)
253         return;
254     m_tileDebugBorderWidth = borderWidth;
255
256     tileGrid().updateTilerLayerProperties();
257 }
258
259 void TileController::setTileDebugBorderColor(Color borderColor)
260 {
261     if (m_tileDebugBorderColor == borderColor)
262         return;
263     m_tileDebugBorderColor = borderColor;
264
265     tileGrid().updateTilerLayerProperties();
266 }
267
268 IntRect TileController::bounds() const
269 {
270     IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight());
271     IntSize boundsSizeIncludingMargin = expandedIntSize(m_tileCacheLayer->bounds().size());
272     boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight());
273
274     return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin);
275 }
276
277 IntRect TileController::boundsWithoutMargin() const
278 {
279     return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size()));
280 }
281
282 IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
283 {
284     IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size());
285     boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()));
286     return boundsWithoutMargin;
287 }
288
289 FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& visibleRect) const
290 {
291     // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect.
292     if (!m_isInWindow)
293         return visibleRect;
294
295     // FIXME: look at how far the document can scroll in each dimension.
296     float coverageHorizontalSize = visibleRect.width();
297     float coverageVerticalSize = visibleRect.height();
298
299 #if PLATFORM(IOS)
300     UNUSED_PARAM(previousVisibleRect);
301     return visibleRect;
302 #else
303     bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect);
304
305     // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
306     // These values were chosen because it's more common to have tall pages and to scroll vertically,
307     // so we keep more tiles above and below the current area.
308
309     if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange)
310         coverageHorizontalSize *= 2;
311
312     if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange)
313         coverageVerticalSize *= 3;
314 #endif
315     coverageVerticalSize += topMarginHeight() + bottomMarginHeight();
316     coverageHorizontalSize += leftMarginWidth() + rightMarginWidth();
317
318     FloatRect coverageBounds = bounds();
319     float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2;
320     coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize);
321     coverageLeft = std::max(coverageLeft, coverageBounds.x());
322
323     float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2;
324     coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize);
325     coverageTop = std::max(coverageTop, coverageBounds.y());
326
327     return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize);
328 }
329
330 void TileController::scheduleTileRevalidation(double interval)
331 {
332     if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval)
333         return;
334
335     m_tileRevalidationTimer.startOneShot(interval);
336 }
337
338 bool TileController::shouldAggressivelyRetainTiles() const
339 {
340     return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer);
341 }
342
343 bool TileController::shouldTemporarilyRetainTileCohorts() const
344 {
345     return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer);
346 }
347
348 void TileController::tileRevalidationTimerFired(Timer<TileController>*)
349 {
350     if (!owningGraphicsLayer())
351         return;
352
353     if (m_isInWindow) {
354         setNeedsRevalidateTiles();
355         return;
356     }
357     // If we are not visible get rid of the zoomed-out tiles.
358     m_zoomedOutTileGrid = nullptr;
359
360     unsigned validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : TileGrid::PruneSecondaryTiles) | TileGrid::UnparentAllTiles;
361
362     tileGrid().revalidateTiles(validationPolicy);
363 }
364
365 void TileController::didRevalidateTiles()
366 {
367     m_visibleRectAtLastRevalidate = visibleRect();
368     m_boundsAtLastRevalidate = bounds();
369
370     updateTileCoverageMap();
371 }
372
373 unsigned TileController::blankPixelCount() const
374 {
375     return tileGrid().blankPixelCount();
376 }
377
378 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
379 {
380     Region paintedVisibleTiles;
381
382     for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) {
383         const PlatformLayer* tileLayer = it->get();
384
385         FloatRect visiblePart(CGRectOffset([tileLayer frame], tileTranslation.x(), tileTranslation.y()));
386         visiblePart.intersect(visibleRect);
387
388         if (!visiblePart.isEmpty())
389             paintedVisibleTiles.unite(enclosingIntRect(visiblePart));
390     }
391
392     Region uncoveredRegion(enclosingIntRect(visibleRect));
393     uncoveredRegion.subtract(paintedVisibleTiles);
394
395     return uncoveredRegion.totalArea();
396 }
397
398 void TileController::setNeedsRevalidateTiles()
399 {
400     owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
401 }
402
403 void TileController::updateTileCoverageMap()
404 {
405     if (m_coverageMap)
406         m_coverageMap->update();
407 }
408
409 IntRect TileController::tileGridExtent() const
410 {
411     return tileGrid().extent();
412 }
413
414 double TileController::retainedTileBackingStoreMemory() const
415 {
416     double bytes = tileGrid().retainedTileBackingStoreMemory();
417     if (m_zoomedOutTileGrid)
418         bytes += m_zoomedOutTileGrid->retainedTileBackingStoreMemory();
419     return bytes;
420 }
421
422 // Return the rect in layer coords, not tile coords.
423 IntRect TileController::tileCoverageRect() const
424 {
425     return tileGrid().tileCoverageRect();
426 }
427
428 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
429 {
430     if (!m_coverageMap)
431         m_coverageMap = std::make_unique<TileCoverageMap>(*this);
432
433     return &m_coverageMap->layer();
434 }
435
436 void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode)
437 {
438     if (scrollingMode == m_indicatorMode)
439         return;
440
441     m_indicatorMode = scrollingMode;
442
443     updateTileCoverageMap();
444 }
445
446 void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight)
447 {
448     m_marginTop = marginTop;
449     m_marginBottom = marginBottom;
450     m_marginLeft = marginLeft;
451     m_marginRight = marginRight;
452
453     setNeedsRevalidateTiles();
454 }
455
456 bool TileController::hasMargins() const
457 {
458     return m_marginTop || m_marginBottom || m_marginLeft || m_marginRight;
459 }
460
461 bool TileController::hasHorizontalMargins() const
462 {
463     return m_marginLeft || m_marginRight;
464 }
465
466 bool TileController::hasVerticalMargins() const
467 {
468     return m_marginTop || m_marginBottom;
469 }
470
471 int TileController::topMarginHeight() const
472 {
473     return m_marginTop / tileGrid().scale();
474 }
475
476 int TileController::bottomMarginHeight() const
477 {
478     return m_marginBottom / tileGrid().scale();
479 }
480
481 int TileController::leftMarginWidth() const
482 {
483     return m_marginLeft / tileGrid().scale();
484 }
485
486 int TileController::rightMarginWidth() const
487 {
488     return m_marginRight / tileGrid().scale();
489 }
490
491 RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect, TileGrid& grid)
492 {
493 #if PLATFORM(IOS)
494     RefPtr<PlatformCALayer> layer;
495 #else
496     RefPtr<PlatformCALayer> layer = LayerPool::sharedPool()->takeLayerWithSize(tileRect.size());
497 #endif
498
499     if (layer)
500         layer->setOwner(&grid);
501     else
502         layer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeTiledBackingTileLayer, &grid);
503
504     layer->setAnchorPoint(FloatPoint3D());
505     layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
506     layer->setPosition(tileRect.location());
507     layer->setBorderColor(m_tileDebugBorderColor);
508     layer->setBorderWidth(m_tileDebugBorderWidth);
509     layer->setEdgeAntialiasingMask(0);
510     layer->setOpaque(m_tilesAreOpaque);
511 #ifndef NDEBUG
512     layer->setName("Tile");
513 #endif
514
515     float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer);
516     m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1;
517
518     layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor);
519     layer->setAcceleratesDrawing(m_acceleratesDrawing);
520
521     layer->setNeedsDisplay();
522
523     return layer;
524 }
525
526 Vector<RefPtr<PlatformCALayer>> TileController::containerLayers()
527 {
528     Vector<RefPtr<PlatformCALayer>> layerList;
529     if (m_zoomedOutTileGrid)
530         layerList.append(&m_zoomedOutTileGrid->containerLayer());
531     layerList.append(&tileGrid().containerLayer());
532     return layerList;
533 }
534
535 #if PLATFORM(IOS)
536 unsigned TileController::numberOfUnparentedTiles() const
537 {
538     unsigned count = tileGrid().numberOfUnparentedTiles();
539     if (m_zoomedOutTileGrid)
540         count += m_zoomedOutTileGrid->numberOfUnparentedTiles();
541     return count;
542 }
543
544 void TileController::removeUnparentedTilesNow()
545 {
546     tileGrid().removeUnparentedTilesNow();
547     if (m_zoomedOutTileGrid)
548         m_zoomedOutTileGrid->removeUnparentedTilesNow();
549
550     updateTileCoverageMap();
551 }
552 #endif
553
554 } // namespace WebCore