Should get rid of TileController's CoverageForSlowScrolling mode
[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 #if !PLATFORM(IOS)
33 #import "LayerPool.h"
34 #endif
35 #import "WebLayer.h"
36 #import <wtf/MainThread.h>
37 #import <utility>
38
39 #if PLATFORM(IOS)
40 #import "TileControllerMemoryHandlerIOS.h"
41 #endif
42
43 namespace WebCore {
44
45 enum TileValidationPolicyFlag {
46     PruneSecondaryTiles = 1 << 0,
47     UnparentAllTiles = 1 << 1
48 };
49
50 PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformLayer)
51 {
52     return adoptPtr(new TileController(rootPlatformLayer));
53 }
54
55 TileController::TileController(PlatformCALayer* rootPlatformLayer)
56     : m_tileCacheLayer(rootPlatformLayer)
57     , m_tileSize(defaultTileWidth, defaultTileHeight)
58     , m_exposedRect(FloatRect::infiniteRect())
59     , m_tileRevalidationTimer(this, &TileController::tileRevalidationTimerFired)
60     , m_cohortRemovalTimer(this, &TileController::cohortRemovalTimerFired)
61     , m_scale(1)
62     , m_deviceScaleFactor(1)
63     , m_tileCoverage(CoverageForVisibleArea)
64     , m_marginTop(0)
65     , m_marginBottom(0)
66     , m_marginLeft(0)
67     , m_marginRight(0)
68     , m_isInWindow(false)
69     , m_scrollingPerformanceLoggingEnabled(false)
70     , m_aggressivelyRetainsTiles(false)
71     , m_unparentsOffscreenTiles(false)
72     , m_acceleratesDrawing(false)
73     , m_tilesAreOpaque(false)
74     , m_hasTilesWithTemporaryScaleFactor(false)
75     , m_tileDebugBorderWidth(0)
76     , m_indicatorMode(AsyncScrollingIndication)
77 {
78     m_tileContainerLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr);
79 #ifndef NDEBUG
80     m_tileContainerLayer->setName("TileController Container Layer");
81 #endif
82 }
83
84 TileController::~TileController()
85 {
86     ASSERT(isMainThread());
87
88 #if PLATFORM(IOS)
89     tileControllerMemoryHandler().removeTileController(this);
90 #endif
91
92     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
93         it->value.layer->setOwner(nullptr);
94
95     if (m_tiledScrollingIndicatorLayer)
96         m_tiledScrollingIndicatorLayer->setOwner(nullptr);
97 }
98
99 void TileController::tileCacheLayerBoundsChanged()
100 {
101     ASSERT(owningGraphicsLayer()->isCommittingChanges());
102     setNeedsRevalidateTiles();
103 }
104
105 void TileController::setNeedsDisplay()
106 {
107     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
108         TileInfo& tileInfo = it->value;
109         IntRect tileRect = rectForTileIndex(it->key);
110
111         if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer())
112             tileInfo.layer->setNeedsDisplay();
113         else
114             tileInfo.hasStaleContent = true;
115     }
116 }
117
118 void TileController::setNeedsDisplayInRect(const IntRect& rect)
119 {
120     if (m_tiles.isEmpty())
121         return;
122
123     FloatRect scaledRect(rect);
124     scaledRect.scale(m_scale);
125     IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect));
126
127     // For small invalidations, lookup the covered tiles.
128     if (repaintRectInTileCoords.height() < 2 * m_tileSize.height() && repaintRectInTileCoords.width() < 2 * m_tileSize.width()) {
129         TileIndex topLeft;
130         TileIndex bottomRight;
131         getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight);
132
133         for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
134             for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
135                 TileIndex tileIndex(x, y);
136                 
137                 TileMap::iterator it = m_tiles.find(tileIndex);
138                 if (it != m_tiles.end())
139                     setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
140             }
141         }
142         return;
143     }
144
145     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
146         setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
147 }
148
149 void TileController::setTileNeedsDisplayInRect(const TileIndex& tileIndex, TileInfo& tileInfo, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords)
150 {
151     PlatformCALayer* tileLayer = tileInfo.layer.get();
152
153     IntRect tileRect = rectForTileIndex(tileIndex);
154     FloatRect tileRepaintRect = tileRect;
155     tileRepaintRect.intersect(repaintRectInTileCoords);
156     if (tileRepaintRect.isEmpty())
157         return;
158
159     tileRepaintRect.moveBy(-tileRect.location());
160     
161     // We could test for intersection with the visible rect. This would reduce painting yet more,
162     // but may make scrolling stale tiles into view more frequent.
163     if (tileRect.intersects(coverageRectInTileCoords) && tileLayer->superlayer()) {
164         tileLayer->setNeedsDisplay(&tileRepaintRect);
165
166         if (owningGraphicsLayer()->platformCALayerShowRepaintCounter(0)) {
167             FloatRect indicatorRect(0, 0, 52, 27);
168             tileLayer->setNeedsDisplay(&indicatorRect);
169         }
170     } else
171         tileInfo.hasStaleContent = true;
172 }
173
174 void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const IntRect&)
175 {
176 #if PLATFORM(IOS)
177     if (pthread_main_np())
178         WebThreadLock();
179 #endif
180
181     if (platformCALayer == m_tiledScrollingIndicatorLayer.get()) {
182         drawTileMapContents(context.platformContext(), m_tiledScrollingIndicatorLayer->bounds());
183         return;
184     }
185
186     {
187         GraphicsContextStateSaver stateSaver(context);
188
189         FloatPoint3D layerOrigin = platformCALayer->position();
190         context.translate(-layerOrigin.x(), -layerOrigin.y());
191         context.scale(FloatSize(m_scale, m_scale));
192
193         RepaintRectList dirtyRects = collectRectsToPaint(context.platformContext(), platformCALayer);
194         drawLayerContents(context.platformContext(), m_tileCacheLayer, dirtyRects);
195     }
196
197     int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer);
198     if (owningGraphicsLayer()->platformCALayerShowRepaintCounter(0))
199         drawRepaintIndicator(context.platformContext(), platformCALayer, repaintCount, cachedCGColor(m_tileDebugBorderColor, ColorSpaceDeviceRGB));
200
201     if (scrollingPerformanceLoggingEnabled()) {
202         FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height());
203         visiblePart.intersect(visibleRect());
204
205         if (repaintCount == 1 && !visiblePart.isEmpty())
206             WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), blankPixelCount());
207     }
208 }
209
210 float TileController::platformCALayerDeviceScaleFactor() const
211 {
212     return owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
213 }
214
215 bool TileController::platformCALayerShowDebugBorders() const
216 {
217     return owningGraphicsLayer()->platformCALayerShowDebugBorders();
218 }
219
220 bool TileController::platformCALayerShowRepaintCounter(PlatformCALayer*) const
221 {
222     return owningGraphicsLayer()->platformCALayerShowRepaintCounter(0);
223 }
224
225 void TileController::setScale(float scale)
226 {
227     ASSERT(owningGraphicsLayer()->isCommittingChanges());
228
229     float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
230
231     // The scale we get is the product of the page scale factor and device scale factor.
232     // Divide by the device scale factor so we'll get the page scale factor.
233     scale /= deviceScaleFactor;
234
235     if (m_scale == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
236         return;
237
238     m_hasTilesWithTemporaryScaleFactor = false;
239     m_deviceScaleFactor = deviceScaleFactor;
240     m_scale = scale;
241
242     TransformationMatrix transform;
243     transform.scale(1 / m_scale);
244     m_tileContainerLayer->setTransform(transform);
245
246     // FIXME: we may revalidateTiles twice in this commit.
247     revalidateTiles(PruneSecondaryTiles, PruneSecondaryTiles);
248
249     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
250         it->value.layer->setContentsScale(deviceScaleFactor);
251 }
252
253 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
254 {
255     if (m_acceleratesDrawing == acceleratesDrawing)
256         return;
257
258     m_acceleratesDrawing = acceleratesDrawing;
259
260     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
261         const TileInfo& tileInfo = it->value;
262         tileInfo.layer->setAcceleratesDrawing(m_acceleratesDrawing);
263     }
264 }
265
266 void TileController::setTilesOpaque(bool opaque)
267 {
268     if (opaque == m_tilesAreOpaque)
269         return;
270
271     m_tilesAreOpaque = opaque;
272
273     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
274         const TileInfo& tileInfo = it->value;
275         tileInfo.layer->setOpaque(opaque);
276     }
277 }
278
279 void TileController::setVisibleRect(const FloatRect& visibleRect)
280 {
281     ASSERT(owningGraphicsLayer()->isCommittingChanges());
282     if (m_visibleRect == visibleRect)
283         return;
284
285     m_visibleRect = visibleRect;
286     setNeedsRevalidateTiles();
287 }
288
289 bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const
290 {
291     FloatRect visibleRect = newVisibleRect;
292     visibleRect.intersect(scaledExposedRect());
293
294     if (visibleRect.isEmpty() || bounds().isEmpty())
295         return false;
296         
297     FloatRect currentTileCoverageRect = computeTileCoverageRect(m_visibleRect, newVisibleRect);
298     FloatRect scaledRect(currentTileCoverageRect);
299     scaledRect.scale(m_scale);
300     IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect));
301
302     TileIndex topLeft;
303     TileIndex bottomRight;
304     getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight);
305
306     IntRect coverageRect = rectForTileIndex(topLeft);
307     coverageRect.unite(rectForTileIndex(bottomRight));
308     return coverageRect != m_primaryTileCoverageRect;
309 }
310
311 void TileController::setExposedRect(const FloatRect& exposedRect)
312 {
313     if (m_exposedRect == exposedRect)
314         return;
315
316     m_exposedRect = exposedRect;
317     setNeedsRevalidateTiles();
318 }
319
320 FloatRect TileController::scaledExposedRect() const
321 {
322     // Since the exposedRect is in FrameView-relative coordinates, we need to scale into document space.
323     FloatRect scaledExposedRect = m_exposedRect;
324     scaledExposedRect.scale(1 / m_scale);
325     return scaledExposedRect;
326 }
327
328 void TileController::prepopulateRect(const FloatRect& rect)
329 {
330     FloatRect scaledRect(rect);
331     scaledRect.scale(m_scale);
332     IntRect rectInTileCoords(enclosingIntRect(scaledRect));
333
334     if (m_primaryTileCoverageRect.contains(rectInTileCoords))
335         return;
336     
337     m_secondaryTileCoverageRects.append(rect);
338     setNeedsRevalidateTiles();
339 }
340
341 void TileController::setIsInWindow(bool isInWindow)
342 {
343     if (m_isInWindow == isInWindow)
344         return;
345
346     m_isInWindow = isInWindow;
347
348     if (m_isInWindow)
349         setNeedsRevalidateTiles();
350     else {
351         const double tileRevalidationTimeout = 4;
352         scheduleTileRevalidation(tileRevalidationTimeout);
353     }
354 }
355
356 void TileController::setTileCoverage(TileCoverage coverage)
357 {
358     if (coverage == m_tileCoverage)
359         return;
360
361     m_tileCoverage = coverage;
362     setNeedsRevalidateTiles();
363 }
364
365 void TileController::revalidateTiles()
366 {
367     ASSERT(owningGraphicsLayer()->isCommittingChanges());
368     revalidateTiles(0, 0);
369 }
370
371 void TileController::forceRepaint()
372 {
373     setNeedsDisplay();
374 }
375
376 void TileController::setTileDebugBorderWidth(float borderWidth)
377 {
378     if (m_tileDebugBorderWidth == borderWidth)
379         return;
380
381     m_tileDebugBorderWidth = borderWidth;
382     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
383         const TileInfo& tileInfo = it->value;
384         tileInfo.layer->setBorderWidth(m_tileDebugBorderWidth);
385     }
386 }
387
388 void TileController::setTileDebugBorderColor(Color borderColor)
389 {
390     if (m_tileDebugBorderColor == borderColor)
391         return;
392
393     m_tileDebugBorderColor = borderColor;
394     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
395         it->value.layer->setBorderColor(borderColor);
396 }
397
398 IntRect TileController::bounds() const
399 {
400     IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight());
401     IntSize boundsSizeIncludingMargin = expandedIntSize(m_tileCacheLayer->bounds().size());
402     boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight());
403
404     return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin);
405 }
406
407 IntRect TileController::boundsWithoutMargin() const
408 {
409     return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size()));
410 }
411
412 IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
413 {
414     IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size());
415     boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()));
416     return boundsWithoutMargin;
417 }
418
419 void TileController::adjustRectAtTileIndexForMargin(const TileIndex& tileIndex, IntRect& rect) const
420 {
421     if (!hasMargins())
422         return;
423
424     // This is a tile in the top margin.
425     if (m_marginTop && tileIndex.y() < 0) {
426         rect.setY(tileIndex.y() * topMarginHeight());
427         rect.setHeight(topMarginHeight());
428     }
429
430     // This is a tile in the left margin.
431     if (m_marginLeft && tileIndex.x() < 0) {
432         rect.setX(tileIndex.x() * leftMarginWidth());
433         rect.setWidth(leftMarginWidth());
434     }
435
436     TileIndex contentTopLeft;
437     TileIndex contentBottomRight;
438     getTileIndexRangeForRect(boundsWithoutMargin(), contentTopLeft, contentBottomRight);
439
440     // This is a tile in the bottom margin.
441     if (m_marginBottom && tileIndex.y() > contentBottomRight.y())
442         rect.setHeight(bottomMarginHeight());
443
444     // This is a tile in the right margin.
445     if (m_marginRight && tileIndex.x() > contentBottomRight.x())
446         rect.setWidth(rightMarginWidth());
447 }
448
449 IntRect TileController::rectForTileIndex(const TileIndex& tileIndex) const
450 {
451     IntRect rect(tileIndex.x() * m_tileSize.width(), tileIndex.y() * m_tileSize.height(), m_tileSize.width(), m_tileSize.height());
452     IntRect scaledBounds(bounds());
453     scaledBounds.scale(m_scale);
454
455     rect.intersect(scaledBounds);
456
457     // These rect computations assume m_tileSize is the correct size to use. However, a tile in the margin area
458     // might be a different size depending on the size of the margins. So adjustRectAtTileIndexForMargin() will
459     // fix the rect we've computed to match the margin sizes if this tile is in the margins.
460     adjustRectAtTileIndexForMargin(tileIndex, rect);
461
462     return rect;
463 }
464
465 void TileController::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const
466 {
467     IntRect clampedRect = bounds();
468     clampedRect.scale(m_scale);
469     clampedRect.intersect(rect);
470
471     if (clampedRect.x() >= 0)
472         topLeft.setX(clampedRect.x() / m_tileSize.width());
473     else
474         topLeft.setX(clampedRect.x() / leftMarginWidth());
475
476     if (clampedRect.y() >= 0)
477         topLeft.setY(clampedRect.y() / m_tileSize.height());
478     else
479         topLeft.setY(clampedRect.y() / topMarginHeight());
480
481     int bottomXRatio = ceil((float)clampedRect.maxX() / m_tileSize.width());
482     bottomRight.setX(std::max(bottomXRatio - 1, 0));
483
484     int bottomYRatio = ceil((float)clampedRect.maxY() / m_tileSize.height());
485     bottomRight.setY(std::max(bottomYRatio - 1, 0));
486 }
487
488 FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect) const
489 {
490     FloatRect visibleRect = currentVisibleRect;
491     visibleRect.intersect(scaledExposedRect());
492
493     // 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.
494     if (!m_isInWindow)
495         return visibleRect;
496
497     bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect);
498     
499     // FIXME: look at how far the document can scroll in each dimension.
500     float coverageHorizontalSize = visibleRect.width();
501     float coverageVerticalSize = visibleRect.height();
502     
503     // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
504     // These values were chosen because it's more common to have tall pages and to scroll vertically,
505     // so we keep more tiles above and below the current area.
506     if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange)
507         coverageHorizontalSize *= 2;
508
509     if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange)
510         coverageVerticalSize *= 3;
511     
512     coverageVerticalSize += topMarginHeight() + bottomMarginHeight();
513     coverageHorizontalSize += leftMarginWidth() + rightMarginWidth();
514
515     FloatRect coverageBounds = bounds();
516     float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2;
517     coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize);
518     coverageLeft = std::max(coverageLeft, coverageBounds.x());
519
520     float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2;
521     coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize);
522     coverageTop = std::max(coverageTop, coverageBounds.y());
523
524     return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize);
525 }
526
527 void TileController::scheduleTileRevalidation(double interval)
528 {
529     if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval)
530         return;
531
532     m_tileRevalidationTimer.startOneShot(interval);
533 }
534
535 void TileController::tileRevalidationTimerFired(Timer<TileController>*)
536 {
537     if (m_isInWindow) {
538         setNeedsRevalidateTiles();
539         return;
540     }
541
542     TileValidationPolicyFlags foregroundValidationPolicy = m_aggressivelyRetainsTiles ? 0 : PruneSecondaryTiles;
543     TileValidationPolicyFlags backgroundValidationPolicy = foregroundValidationPolicy | UnparentAllTiles;
544
545     revalidateTiles(foregroundValidationPolicy, backgroundValidationPolicy);
546 }
547
548 unsigned TileController::blankPixelCount() const
549 {
550     PlatformLayerList tiles(m_tiles.size());
551
552     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
553         if (PlatformLayer *layer = it->value.layer->platformLayer())
554             tiles.append(layer);
555     }
556
557     return blankPixelCountForTiles(tiles, m_visibleRect, IntPoint(0,0));
558 }
559
560 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
561 {
562     Region paintedVisibleTiles;
563
564     for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) {
565         const PlatformLayer* tileLayer = it->get();
566
567         FloatRect visiblePart(CGRectOffset([tileLayer frame], tileTranslation.x(), tileTranslation.y()));
568         visiblePart.intersect(visibleRect);
569
570         if (!visiblePart.isEmpty())
571             paintedVisibleTiles.unite(enclosingIntRect(visiblePart));
572     }
573
574     Region uncoveredRegion(enclosingIntRect(visibleRect));
575     uncoveredRegion.subtract(paintedVisibleTiles);
576
577     return uncoveredRegion.totalArea();
578 }
579
580 static inline void queueTileForRemoval(const TileController::TileIndex& tileIndex, const TileController::TileInfo& tileInfo, Vector<TileController::TileIndex>& tilesToRemove, TileController::RepaintCountMap& repaintCounts)
581 {
582     tileInfo.layer->removeFromSuperlayer();
583     repaintCounts.remove(tileInfo.layer.get());
584     tilesToRemove.append(tileIndex);
585 }
586
587 void TileController::removeAllTiles()
588 {
589     Vector<TileIndex> tilesToRemove;
590
591     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
592         queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
593
594     for (size_t i = 0; i < tilesToRemove.size(); ++i) {
595         TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
596 #if !PLATFORM(IOS)
597         LayerPool::sharedPool()->addLayer(tileInfo.layer);
598 #endif
599     }
600 }
601
602 void TileController::removeAllSecondaryTiles()
603 {
604     Vector<TileIndex> tilesToRemove;
605
606     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
607         const TileInfo& tileInfo = it->value;
608         if (tileInfo.cohort == VisibleTileCohort)
609             continue;
610
611         queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
612     }
613
614     for (size_t i = 0; i < tilesToRemove.size(); ++i) {
615         TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
616 #if !PLATFORM(IOS)
617         LayerPool::sharedPool()->addLayer(tileInfo.layer);
618 #endif
619     }
620 }
621
622 void TileController::removeTilesInCohort(TileCohort cohort)
623 {
624     ASSERT(cohort != VisibleTileCohort);
625     Vector<TileIndex> tilesToRemove;
626
627     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
628         const TileInfo& tileInfo = it->value;
629         if (tileInfo.cohort != cohort)
630             continue;
631
632         queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
633     }
634
635     for (size_t i = 0; i < tilesToRemove.size(); ++i) {
636         TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
637 #if !PLATFORM(IOS)
638         LayerPool::sharedPool()->addLayer(tileInfo.layer);
639 #endif
640     }
641 }
642
643 void TileController::setNeedsRevalidateTiles()
644 {
645     owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
646 }
647
648 void TileController::revalidateTiles(TileValidationPolicyFlags foregroundValidationPolicy, TileValidationPolicyFlags backgroundValidationPolicy)
649 {
650     FloatRect visibleRect = m_visibleRect;
651     IntRect bounds = this->bounds();
652
653     visibleRect.intersect(scaledExposedRect());
654
655     if (visibleRect.isEmpty() || bounds.isEmpty())
656         return;
657     
658     TileValidationPolicyFlags validationPolicy = m_isInWindow ? foregroundValidationPolicy : backgroundValidationPolicy;
659     
660     FloatRect tileCoverageRect = computeTileCoverageRect(m_visibleRectAtLastRevalidate, m_visibleRect);
661     FloatRect scaledRect(tileCoverageRect);
662     scaledRect.scale(m_scale);
663     IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect));
664
665     TileCohort currCohort = nextTileCohort();
666     unsigned tilesInCohort = 0;
667
668     // Move tiles newly outside the coverage rect into the cohort map.
669     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
670         TileInfo& tileInfo = it->value;
671         TileIndex tileIndex = it->key;
672
673         PlatformCALayer* tileLayer = tileInfo.layer.get();
674         IntRect tileRect = rectForTileIndex(tileIndex);
675         if (tileRect.intersects(coverageRectInTileCoords)) {
676             tileInfo.cohort = VisibleTileCohort;
677             if (tileInfo.hasStaleContent) {
678                 // FIXME: store a dirty region per layer?
679                 tileLayer->setNeedsDisplay();
680                 tileInfo.hasStaleContent = false;
681             }
682         } else {
683             // Add to the currentCohort if not already in one.
684             if (tileInfo.cohort == VisibleTileCohort) {
685                 tileInfo.cohort = currCohort;
686                 ++tilesInCohort;
687
688                 if (m_unparentsOffscreenTiles)
689                     tileLayer->removeFromSuperlayer();
690             }
691         }
692     }
693
694     if (tilesInCohort)
695         startedNewCohort(currCohort);
696
697     if (!m_aggressivelyRetainsTiles)
698         scheduleCohortRemoval();
699
700     // Ensure primary tile coverage tiles.
701     m_primaryTileCoverageRect = ensureTilesForRect(tileCoverageRect, CoverageType::PrimaryTiles);
702
703     if (validationPolicy & PruneSecondaryTiles) {
704         removeAllSecondaryTiles();
705         m_cohortList.clear();
706     } else {
707         for (size_t i = 0; i < m_secondaryTileCoverageRects.size(); ++i)
708             ensureTilesForRect(m_secondaryTileCoverageRects[i], CoverageType::SecondaryTiles);
709         m_secondaryTileCoverageRects.clear();
710     }
711
712     if (m_unparentsOffscreenTiles && (validationPolicy & UnparentAllTiles)) {
713         for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
714             it->value.layer->removeFromSuperlayer();
715     }
716
717     if (m_boundsAtLastRevalidate != bounds) {
718         // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to
719         // be bottom or right margin tiles need to be invalidated.
720         if (hasMargins()) {
721             if (bounds.width() > m_boundsAtLastRevalidate.width() || bounds.height() > m_boundsAtLastRevalidate.height()) {
722                 IntRect boundsWithoutMargin = this->boundsWithoutMargin();
723                 IntRect oldBoundsWithoutMargin = boundsAtLastRevalidateWithoutMargin();
724
725                 if (bounds.height() > m_boundsAtLastRevalidate.height()) {
726                     IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(),
727                         oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height());
728                     setNeedsDisplayInRect(formerBottomMarginRect);
729                 }
730
731                 if (bounds.width() > m_boundsAtLastRevalidate.width()) {
732                     IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(),
733                         boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height());
734                     setNeedsDisplayInRect(formerRightMarginRect);
735                 }
736             }
737         }
738
739         FloatRect scaledBounds(bounds);
740         scaledBounds.scale(m_scale);
741         IntRect boundsInTileCoords(enclosingIntRect(scaledBounds));
742
743         TileIndex topLeftForBounds;
744         TileIndex bottomRightForBounds;
745         getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds);
746
747         Vector<TileIndex> tilesToRemove;
748         for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
749             const TileIndex& index = it->key;
750             if (index.y() < topLeftForBounds.y()
751                 || index.y() > bottomRightForBounds.y()
752                 || index.x() < topLeftForBounds.x()
753                 || index.x() > bottomRightForBounds.x())
754                 queueTileForRemoval(index, it->value, tilesToRemove, m_tileRepaintCounts);
755         }
756
757         for (size_t i = 0, size = tilesToRemove.size(); i < size; ++i) {
758             TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
759 #if !PLATFORM(IOS)
760             LayerPool::sharedPool()->addLayer(tileInfo.layer);
761 #endif
762         }
763     }
764
765     if (m_tiledScrollingIndicatorLayer)
766         updateTileCoverageMap();
767
768     m_visibleRectAtLastRevalidate = visibleRect;
769     m_boundsAtLastRevalidate = bounds;
770 }
771
772 TileController::TileCohort TileController::nextTileCohort() const
773 {
774     if (!m_cohortList.isEmpty())
775         return m_cohortList.last().cohort + 1;
776
777     return 1;
778 }
779
780 void TileController::startedNewCohort(TileCohort cohort)
781 {
782     m_cohortList.append(TileCohortInfo(cohort, monotonicallyIncreasingTime()));
783 #if PLATFORM(IOS)
784     if (!m_isInWindow)
785         tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(this);
786 #endif
787 }
788
789 TileController::TileCohort TileController::newestTileCohort() const
790 {
791     return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort;
792 }
793
794 TileController::TileCohort TileController::oldestTileCohort() const
795 {
796     return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort;
797 }
798
799 void TileController::scheduleCohortRemoval()
800 {
801     const double cohortRemovalTimerSeconds = 1;
802
803     // Start the timer, or reschedule the timer from now if it's already active.
804     if (!m_cohortRemovalTimer.isActive())
805         m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds);
806 }
807
808 void TileController::cohortRemovalTimerFired(Timer<TileController>*)
809 {
810     if (m_cohortList.isEmpty()) {
811         m_cohortRemovalTimer.stop();
812         return;
813     }
814
815     double cohortLifeTimeSeconds = 2;
816     double timeThreshold = monotonicallyIncreasingTime() - cohortLifeTimeSeconds;
817
818     while (!m_cohortList.isEmpty() && m_cohortList.first().creationTime < timeThreshold) {
819         TileCohortInfo firstCohort = m_cohortList.takeFirst();
820         removeTilesInCohort(firstCohort.cohort);
821     }
822
823     if (m_tiledScrollingIndicatorLayer)
824         updateTileCoverageMap();
825 }
826
827 IntRect TileController::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType)
828 {
829     if (m_unparentsOffscreenTiles && !m_isInWindow)
830         return IntRect();
831
832     FloatRect scaledRect(rect);
833     scaledRect.scale(m_scale);
834     IntRect rectInTileCoords(enclosingIntRect(scaledRect));
835
836     TileIndex topLeft;
837     TileIndex bottomRight;
838     getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight);
839
840     TileCohort currCohort = nextTileCohort();
841     unsigned tilesInCohort = 0;
842
843     IntRect coverageRect;
844
845     for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
846         for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
847             TileIndex tileIndex(x, y);
848
849             IntRect tileRect = rectForTileIndex(tileIndex);
850             TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value;
851
852             coverageRect.unite(tileRect);
853
854             bool shouldChangeTileLayerFrame = false;
855
856             if (!tileInfo.layer)
857                 tileInfo.layer = createTileLayer(tileRect);
858             else {
859                 // We already have a layer for this tile. Ensure that its size is correct.
860                 FloatSize tileLayerSize(tileInfo.layer->bounds().size());
861                 shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size());
862
863                 if (shouldChangeTileLayerFrame) {
864                     tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
865                     tileInfo.layer->setPosition(tileRect.location());
866                     tileInfo.layer->setNeedsDisplay();
867                 }
868             }
869
870             if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) {
871                 tileInfo.cohort = currCohort;
872                 ++tilesInCohort;
873             }
874
875             bool shouldParentTileLayer = (!m_unparentsOffscreenTiles || m_isInWindow) && !tileInfo.layer->superlayer();
876
877             if (shouldParentTileLayer)
878                 m_tileContainerLayer->appendSublayer(tileInfo.layer.get());
879         }
880     }
881     
882     if (tilesInCohort)
883         startedNewCohort(currCohort);
884
885     return coverageRect;
886 }
887
888 void TileController::updateTileCoverageMap()
889 {
890     FloatRect containerBounds = bounds();
891     FloatRect visibleRect = this->visibleRect();
892
893     visibleRect.intersect(scaledExposedRect());
894     visibleRect.contract(4, 4); // Layer is positioned 2px from top and left edges.
895
896     float widthScale = 1;
897     float scale = 1;
898     if (!containerBounds.isEmpty()) {
899         widthScale = std::min<float>(visibleRect.width() / containerBounds.width(), 0.1);
900         scale = std::min(widthScale, visibleRect.height() / containerBounds.height());
901     }
902     
903     float indicatorScale = scale * m_scale;
904     FloatRect mapBounds = containerBounds;
905     mapBounds.scale(indicatorScale, indicatorScale);
906
907     if (!m_exposedRect.isInfinite())
908         m_tiledScrollingIndicatorLayer->setPosition(m_exposedRect.location() + FloatPoint(2, 2));
909     else
910         m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
911
912     m_tiledScrollingIndicatorLayer->setBounds(mapBounds);
913     m_tiledScrollingIndicatorLayer->setNeedsDisplay();
914
915     visibleRect.scale(indicatorScale, indicatorScale);
916     visibleRect.expand(2, 2);
917     m_visibleRectIndicatorLayer->setPosition(visibleRect.location());
918     m_visibleRectIndicatorLayer->setBounds(FloatRect(FloatPoint(), visibleRect.size()));
919
920     Color visibleRectIndicatorColor;
921     switch (m_indicatorMode) {
922     case SynchronousScrollingBecauseOfStyleIndication:
923         visibleRectIndicatorColor = Color(255, 0, 0);
924         break;
925     case SynchronousScrollingBecauseOfEventHandlersIndication:
926         visibleRectIndicatorColor = Color(255, 255, 0);
927         break;
928     case AsyncScrollingIndication:
929         visibleRectIndicatorColor = Color(0, 200, 0);
930         break;
931     }
932
933     m_visibleRectIndicatorLayer->setBorderColor(visibleRectIndicatorColor);
934 }
935
936 IntRect TileController::tileGridExtent() const
937 {
938     TileIndex topLeft;
939     TileIndex bottomRight;
940     getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight);
941
942     // Return index of top, left tile and the number of tiles across and down.
943     return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1);
944 }
945
946 double TileController::retainedTileBackingStoreMemory() const
947 {
948     double totalBytes = 0;
949     
950     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
951         const TileInfo& tileInfo = it->value;
952         if (tileInfo.layer->superlayer()) {
953             FloatRect bounds = tileInfo.layer->bounds();
954             double contentsScale = tileInfo.layer->contentsScale();
955             totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale;
956         }
957     }
958
959     return totalBytes;
960 }
961
962 // Return the rect in layer coords, not tile coords.
963 IntRect TileController::tileCoverageRect() const
964 {
965     IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect);
966     coverageRectInLayerCoords.scale(1 / m_scale);
967     return coverageRectInLayerCoords;
968 }
969
970 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
971 {
972     if (!m_tiledScrollingIndicatorLayer) {
973         m_tiledScrollingIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeSimpleLayer, this);
974         m_tiledScrollingIndicatorLayer->setOpacity(0.75);
975         m_tiledScrollingIndicatorLayer->setAnchorPoint(FloatPoint3D());
976         m_tiledScrollingIndicatorLayer->setBorderColor(Color::black);
977         m_tiledScrollingIndicatorLayer->setBorderWidth(1);
978         m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
979
980         m_visibleRectIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr);
981         m_visibleRectIndicatorLayer->setBorderWidth(2);
982         m_visibleRectIndicatorLayer->setAnchorPoint(FloatPoint3D());
983         m_visibleRectIndicatorLayer->setBorderColor(Color(255, 0, 0));
984
985         m_tiledScrollingIndicatorLayer->appendSublayer(m_visibleRectIndicatorLayer.get());
986
987         updateTileCoverageMap();
988     }
989
990     return m_tiledScrollingIndicatorLayer.get();
991 }
992
993 void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode)
994 {
995     if (scrollingMode == m_indicatorMode)
996         return;
997
998     m_indicatorMode = scrollingMode;
999
1000     if (m_tiledScrollingIndicatorLayer)
1001         updateTileCoverageMap();
1002 }
1003
1004 void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight)
1005 {
1006     m_marginTop = marginTop;
1007     m_marginBottom = marginBottom;
1008     m_marginLeft = marginLeft;
1009     m_marginRight = marginRight;
1010
1011     setNeedsRevalidateTiles();
1012 }
1013
1014 bool TileController::hasMargins() const
1015 {
1016     return m_marginTop || m_marginBottom || m_marginLeft || m_marginRight;
1017 }
1018
1019 int TileController::topMarginHeight() const
1020 {
1021     return m_marginTop;
1022 }
1023
1024 int TileController::bottomMarginHeight() const
1025 {
1026     return m_marginBottom;
1027 }
1028
1029 int TileController::leftMarginWidth() const
1030 {
1031     return m_marginLeft;
1032 }
1033
1034 int TileController::rightMarginWidth() const
1035 {
1036     return m_marginRight;
1037 }
1038
1039 RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect)
1040 {
1041 #if PLATFORM(IOS)
1042     RefPtr<PlatformCALayer> layer;
1043 #else
1044     RefPtr<PlatformCALayer> layer = LayerPool::sharedPool()->takeLayerWithSize(tileRect.size());
1045 #endif
1046
1047     if (layer) {
1048         m_tileRepaintCounts.remove(layer.get());
1049         layer->setOwner(this);
1050     } else
1051         layer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeTiledBackingTileLayer, this);
1052
1053     layer->setAnchorPoint(FloatPoint3D());
1054     layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
1055     layer->setPosition(tileRect.location());
1056     layer->setBorderColor(m_tileDebugBorderColor);
1057     layer->setBorderWidth(m_tileDebugBorderWidth);
1058     layer->setEdgeAntialiasingMask(0);
1059     layer->setOpaque(m_tilesAreOpaque);
1060 #ifndef NDEBUG
1061     layer->setName("Tile");
1062 #endif
1063
1064     float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer);
1065     m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1;
1066
1067     layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor);
1068     layer->setAcceleratesDrawing(m_acceleratesDrawing);
1069
1070     layer->setNeedsDisplay();
1071
1072     return layer;
1073 }
1074
1075 int TileController::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer)
1076 {
1077     int repaintCount = 0;
1078
1079     if (m_tileRepaintCounts.contains(platformCALayer))
1080         repaintCount = m_tileRepaintCounts.get(platformCALayer);
1081
1082     m_tileRepaintCounts.set(platformCALayer, ++repaintCount);
1083
1084     return repaintCount;
1085 }
1086
1087 void TileController::drawTileMapContents(CGContextRef context, CGRect layerBounds)
1088 {
1089     CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1);
1090     CGContextFillRect(context, layerBounds);
1091
1092     CGFloat scaleFactor = layerBounds.size.width / bounds().width();
1093
1094     CGFloat contextScale = scaleFactor / scale();
1095     CGContextScaleCTM(context, contextScale, contextScale);
1096     
1097     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
1098         const TileInfo& tileInfo = it->value;
1099         PlatformCALayer* tileLayer = tileInfo.layer.get();
1100
1101         CGFloat red = 1;
1102         CGFloat green = 1;
1103         CGFloat blue = 1;
1104         if (tileInfo.hasStaleContent) {
1105             red = 0.25;
1106             green = 0.125;
1107             blue = 0;
1108         }
1109
1110         TileCohort newestCohort = newestTileCohort();
1111         TileCohort oldestCohort = oldestTileCohort();
1112
1113         if (!m_aggressivelyRetainsTiles && tileInfo.cohort != VisibleTileCohort && newestCohort > oldestCohort) {
1114             float cohortProportion = static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort);
1115             CGContextSetRGBFillColor(context, red, green, blue, 1 - cohortProportion);
1116         } else
1117             CGContextSetRGBFillColor(context, red, green, blue, 1);
1118
1119         if (tileLayer->superlayer()) {
1120             CGContextSetLineWidth(context, 0.5 / contextScale);
1121             CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
1122         } else {
1123             CGContextSetLineWidth(context, 1 / contextScale);
1124             CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1);
1125         }
1126
1127         CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height());
1128         CGContextFillRect(context, frame);
1129         CGContextStrokeRect(context, frame);
1130     }
1131 }
1132     
1133 #if PLATFORM(IOS)
1134 void TileController::removeUnparentedTilesNow()
1135 {
1136     while (!m_cohortList.isEmpty()) {
1137         TileCohortInfo firstCohort = m_cohortList.takeFirst();
1138         removeTilesInCohort(firstCohort.cohort);
1139     }
1140
1141     if (m_tiledScrollingIndicatorLayer)
1142         updateTileCoverageMap();
1143 }
1144 #endif
1145
1146 } // namespace WebCore