ebb817896a37d90b981b706813a85d6da98fe110
[WebKit-https.git] / Source / WebCore / platform / graphics / ca / TileGrid.cpp
1 /*
2  * Copyright (C) 2011-2014 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 "TileGrid.h"
28
29 #include "GraphicsContext.h"
30 #include "LayerPool.h"
31 #include "Logging.h"
32 #include "PlatformCALayer.h"
33 #include "TextStream.h"
34 #include "TileController.h"
35 #include <wtf/MainThread.h>
36 #include <wtf/text/CString.h>
37
38 #if PLATFORM(IOS)
39 #include "TileControllerMemoryHandlerIOS.h"
40 #endif
41
42 namespace WebCore {
43
44 TileGrid::TileGrid(TileController& controller)
45     : m_controller(controller)
46     , m_containerLayer(*controller.rootLayer().createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr))
47     , m_cohortRemovalTimer(*this, &TileGrid::cohortRemovalTimerFired)
48     , m_tileSize(kDefaultTileSize, kDefaultTileSize)
49 {
50     m_containerLayer.get().setName(TileController::tileGridContainerLayerName());
51 }
52
53 TileGrid::~TileGrid()
54 {
55     ASSERT(isMainThread());
56
57     for (auto& tile : m_tiles.values())
58         tile.layer->setOwner(nullptr);
59 }
60
61 void TileGrid::setIsZoomedOutTileGrid(bool isZoomedOutGrid)
62 {
63     if (isZoomedOutGrid)
64         m_containerLayer.get().setName(TileController::zoomedOutTileGridContainerLayerName());
65     else
66         m_containerLayer.get().setName(TileController::tileGridContainerLayerName());
67 }
68
69 void TileGrid::setScale(float scale)
70 {
71     m_scale = scale;
72
73     TransformationMatrix transform;
74     transform.scale(1 / m_scale);
75     m_containerLayer->setTransform(transform);
76
77     // FIXME: we may revalidateTiles twice in this commit.
78     revalidateTiles(PruneSecondaryTiles);
79
80     for (auto& tile : m_tiles.values())
81         tile.layer->setContentsScale(m_controller.deviceScaleFactor());
82 }
83
84 void TileGrid::setNeedsDisplay()
85 {
86     for (auto& entry : m_tiles) {
87         TileInfo& tileInfo = entry.value;
88         IntRect tileRect = rectForTileIndex(entry.key);
89
90         if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer())
91             tileInfo.layer->setNeedsDisplay();
92         else
93             tileInfo.hasStaleContent = true;
94     }
95 }
96
97 void TileGrid::setNeedsDisplayInRect(const IntRect& rect)
98 {
99     if (m_tiles.isEmpty())
100         return;
101
102     FloatRect scaledRect(rect);
103     scaledRect.scale(m_scale);
104     IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect));
105
106     IntSize tileSize = m_tileSize;
107
108     // For small invalidations, lookup the covered tiles.
109     if (repaintRectInTileCoords.height() < 2 * tileSize.height() && repaintRectInTileCoords.width() < 2 * tileSize.width()) {
110         TileIndex topLeft;
111         TileIndex bottomRight;
112         getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight);
113
114         for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
115             for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
116                 TileIndex tileIndex(x, y);
117                 
118                 TileMap::iterator it = m_tiles.find(tileIndex);
119                 if (it != m_tiles.end())
120                     setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
121             }
122         }
123         return;
124     }
125
126     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
127         setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
128 }
129
130 void TileGrid::dropTilesInRect(const IntRect& rect)
131 {
132     if (m_tiles.isEmpty())
133         return;
134
135     FloatRect scaledRect(rect);
136     scaledRect.scale(m_scale);
137     IntRect dropRectInTileCoords(enclosingIntRect(scaledRect));
138
139     Vector<TileIndex> tilesToRemove;
140
141     for (auto& index : m_tiles.keys()) {
142         if (rectForTileIndex(index).intersects(dropRectInTileCoords))
143             tilesToRemove.append(index);
144     }
145
146     removeTiles(tilesToRemove);
147 }
148
149 void TileGrid::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->setNeedsDisplayInRect(tileRepaintRect);
165
166         if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0)) {
167             FloatRect indicatorRect(0, 0, 52, 27);
168             tileLayer->setNeedsDisplayInRect(indicatorRect);
169         }
170     } else
171         tileInfo.hasStaleContent = true;
172 }
173
174 void TileGrid::updateTileLayerProperties()
175 {
176     bool acceleratesDrawing = m_controller.acceleratesDrawing();
177     bool opaque = m_controller.tilesAreOpaque();
178     Color tileDebugBorderColor = m_controller.tileDebugBorderColor();
179     float tileDebugBorderWidth = m_controller.tileDebugBorderWidth();
180
181     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
182         const TileInfo& tileInfo = it->value;
183         tileInfo.layer->setAcceleratesDrawing(acceleratesDrawing);
184         tileInfo.layer->setOpaque(opaque);
185         tileInfo.layer->setBorderColor(tileDebugBorderColor);
186         tileInfo.layer->setBorderWidth(tileDebugBorderWidth);
187     }
188 }
189
190 bool TileGrid::tilesWouldChangeForCoverageRect(const FloatRect& coverageRect) const
191 {
192     if (coverageRect.isEmpty())
193         return false;
194
195     FloatRect scaledRect(coverageRect);
196     scaledRect.scale(m_scale);
197     IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect));
198
199     TileIndex topLeft;
200     TileIndex bottomRight;
201     getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight);
202
203     IntRect tileCoverageRect = rectForTileIndex(topLeft);
204     tileCoverageRect.unite(rectForTileIndex(bottomRight));
205     return tileCoverageRect != m_primaryTileCoverageRect;
206 }
207
208 bool TileGrid::prepopulateRect(const FloatRect& rect)
209 {
210     IntRect enclosingCoverageRect = enclosingIntRect(rect);
211     if (m_primaryTileCoverageRect.contains(enclosingCoverageRect))
212         return false;
213
214     m_secondaryTileCoverageRects.append(enclosingCoverageRect);
215     return true;
216 }
217
218 IntRect TileGrid::rectForTileIndex(const TileIndex& tileIndex) const
219 {
220     // FIXME: calculating the scaled size here should match with the rest of calculated sizes where we use the combination of
221     // enclosingIntRect, expandedIntSize (floor vs ceil).
222     // However enclosing this size could reveal gap on root layer's background. see RenderView::backgroundRect()
223     IntSize tileSize = m_tileSize;
224     IntRect rect(tileIndex.x() * tileSize.width(), tileIndex.y() * tileSize.height(), tileSize.width(), tileSize.height());
225     IntRect scaledBounds(m_controller.bounds());
226     scaledBounds.scale(m_scale);
227     rect.intersect(scaledBounds);
228     return rect;
229 }
230
231 void TileGrid::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const
232 {
233     IntRect clampedRect = m_controller.bounds();
234     clampedRect.scale(m_scale);
235     clampedRect.intersect(rect);
236
237     auto tileSize = m_tileSize;
238     if (clampedRect.x() >= 0)
239         topLeft.setX(clampedRect.x() / tileSize.width());
240     else
241         topLeft.setX(floorf((float)clampedRect.x() / tileSize.width()));
242
243     if (clampedRect.y() >= 0)
244         topLeft.setY(clampedRect.y() / tileSize.height());
245     else
246         topLeft.setY(floorf((float)clampedRect.y() / tileSize.height()));
247
248     int bottomXRatio = ceil((float)clampedRect.maxX() / tileSize.width());
249     bottomRight.setX(std::max(bottomXRatio - 1, 0));
250
251     int bottomYRatio = ceil((float)clampedRect.maxY() / tileSize.height());
252     bottomRight.setY(std::max(bottomYRatio - 1, 0));
253 }
254
255 unsigned TileGrid::blankPixelCount() const
256 {
257     PlatformLayerList tiles(m_tiles.size());
258
259     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
260         if (PlatformLayer *layer = it->value.layer->platformLayer())
261             tiles.append(layer);
262     }
263
264     return TileController::blankPixelCountForTiles(tiles, m_controller.visibleRect(), IntPoint(0, 0));
265 }
266
267 void TileGrid::removeTiles(Vector<TileGrid::TileIndex>& toRemove)
268 {
269     for (size_t i = 0; i < toRemove.size(); ++i) {
270         TileInfo tileInfo = m_tiles.take(toRemove[i]);
271         tileInfo.layer->removeFromSuperlayer();
272         m_tileRepaintCounts.remove(tileInfo.layer.get());
273         tileInfo.layer->moveToLayerPool();
274     }
275 }
276
277 void TileGrid::removeAllTiles()
278 {
279     Vector<TileIndex> tilesToRemove;
280     tilesToRemove.reserveInitialCapacity(m_tiles.size());
281
282     for (auto& entry : m_tiles)
283         tilesToRemove.uncheckedAppend(entry.key);
284
285     removeTiles(tilesToRemove);
286 }
287
288 void TileGrid::removeAllSecondaryTiles()
289 {
290     Vector<TileIndex> tilesToRemove;
291
292     for (auto& entry : m_tiles) {
293         const TileInfo& tileInfo = entry.value;
294         if (tileInfo.cohort == VisibleTileCohort)
295             continue;
296         tilesToRemove.append(entry.key);
297     }
298
299     removeTiles(tilesToRemove);
300 }
301
302 void TileGrid::removeTilesInCohort(TileCohort cohort)
303 {
304     ASSERT(cohort != VisibleTileCohort);
305     Vector<TileIndex> tilesToRemove;
306
307     for (auto& entry : m_tiles) {
308         const TileInfo& tileInfo = entry.value;
309         if (tileInfo.cohort != cohort)
310             continue;
311         tilesToRemove.append(entry.key);
312     }
313
314     removeTiles(tilesToRemove);
315 }
316
317 void TileGrid::revalidateTiles(TileValidationPolicy validationPolicy)
318 {
319     FloatRect coverageRect = m_controller.coverageRect();
320     IntRect bounds = m_controller.bounds();
321
322     if (coverageRect.isEmpty() || bounds.isEmpty())
323         return;
324
325     FloatRect scaledRect(coverageRect);
326     scaledRect.scale(m_scale);
327     IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect));
328
329     TileCohort currCohort = nextTileCohort();
330     unsigned tilesInCohort = 0;
331
332     double minimumRevalidationTimerDuration = std::numeric_limits<double>::max();
333     bool needsTileRevalidation = false;
334     
335     auto tileSize = m_controller.tileSize();
336     if (tileSize != m_tileSize) {
337         removeAllTiles();
338         m_tileSize = tileSize;
339     }
340
341     // Move tiles newly outside the coverage rect into the cohort map.
342     for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
343         TileInfo& tileInfo = it->value;
344         TileIndex tileIndex = it->key;
345
346         PlatformCALayer* tileLayer = tileInfo.layer.get();
347         IntRect tileRect = rectForTileIndex(tileIndex);
348         if (tileRect.intersects(coverageRectInTileCoords)) {
349             tileInfo.cohort = VisibleTileCohort;
350             if (tileInfo.hasStaleContent) {
351                 // FIXME: store a dirty region per layer?
352                 tileLayer->setNeedsDisplay();
353                 tileInfo.hasStaleContent = false;
354             }
355         } else {
356             // Add to the currentCohort if not already in one.
357             if (tileInfo.cohort == VisibleTileCohort) {
358                 tileInfo.cohort = currCohort;
359                 ++tilesInCohort;
360
361                 if (m_controller.unparentsOffscreenTiles())
362                     tileLayer->removeFromSuperlayer();
363             } else if (m_controller.unparentsOffscreenTiles() && m_controller.shouldAggressivelyRetainTiles() && tileLayer->superlayer()) {
364                 // Aggressive tile retention means we'll never remove cohorts, but we need to make sure they're unparented.
365                 // We can't immediately unparent cohorts comprised of secondary tiles that never touch the primary coverage rect,
366                 // because that would defeat the usefulness of prepopulateRect(); instead, age prepopulated tiles out as if they were being removed.
367                 for (auto& cohort : m_cohortList) {
368                     if (cohort.cohort != tileInfo.cohort)
369                         continue;
370                     double timeUntilCohortExpires = cohort.timeUntilExpiration();
371                     if (timeUntilCohortExpires > 0) {
372                         minimumRevalidationTimerDuration = std::min(minimumRevalidationTimerDuration, timeUntilCohortExpires);
373                         needsTileRevalidation = true;
374                     } else
375                         tileLayer->removeFromSuperlayer();
376                     break;
377                 }
378             }
379         }
380     }
381
382     if (needsTileRevalidation)
383         m_controller.scheduleTileRevalidation(minimumRevalidationTimerDuration);
384
385     if (tilesInCohort)
386         startedNewCohort(currCohort);
387
388     if (!m_controller.shouldAggressivelyRetainTiles()) {
389         if (m_controller.shouldTemporarilyRetainTileCohorts())
390             scheduleCohortRemoval();
391         else if (tilesInCohort)
392             removeTilesInCohort(currCohort);
393     }
394
395     // Ensure primary tile coverage tiles.
396     m_primaryTileCoverageRect = ensureTilesForRect(coverageRect, CoverageType::PrimaryTiles);
397
398     if (validationPolicy & PruneSecondaryTiles) {
399         removeAllSecondaryTiles();
400         m_cohortList.clear();
401     } else {
402         for (auto& secondaryCoverageRect : m_secondaryTileCoverageRects) {
403             FloatRect secondaryRectInLayerCoordinates(secondaryCoverageRect);
404             secondaryRectInLayerCoordinates.scale(1 / m_scale);
405             ensureTilesForRect(secondaryRectInLayerCoordinates, CoverageType::SecondaryTiles);
406         }
407         m_secondaryTileCoverageRects.clear();
408     }
409
410     if (m_controller.unparentsOffscreenTiles() && (validationPolicy & UnparentAllTiles)) {
411         for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
412             it->value.layer->removeFromSuperlayer();
413     }
414
415     auto boundsAtLastRevalidate = m_controller.boundsAtLastRevalidate();
416     if (boundsAtLastRevalidate != bounds) {
417         // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to
418         // be bottom or right margin tiles need to be invalidated.
419         if (m_controller.hasMargins()) {
420             if (bounds.width() > boundsAtLastRevalidate.width() || bounds.height() > boundsAtLastRevalidate.height()) {
421                 IntRect boundsWithoutMargin = m_controller.boundsWithoutMargin();
422                 IntRect oldBoundsWithoutMargin = m_controller.boundsAtLastRevalidateWithoutMargin();
423
424                 if (bounds.height() > boundsAtLastRevalidate.height()) {
425                     IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(),
426                         oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height());
427                     setNeedsDisplayInRect(formerBottomMarginRect);
428                 }
429
430                 if (bounds.width() > boundsAtLastRevalidate.width()) {
431                     IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(),
432                         boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height());
433                     setNeedsDisplayInRect(formerRightMarginRect);
434                 }
435             }
436         }
437
438         FloatRect scaledBounds(bounds);
439         scaledBounds.scale(m_scale);
440         IntRect boundsInTileCoords(enclosingIntRect(scaledBounds));
441
442         TileIndex topLeftForBounds;
443         TileIndex bottomRightForBounds;
444         getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds);
445
446         Vector<TileIndex> tilesToRemove;
447         for (auto& index : m_tiles.keys()) {
448             if (index.y() < topLeftForBounds.y() || index.y() > bottomRightForBounds.y() || index.x() < topLeftForBounds.x() || index.x() > bottomRightForBounds.x())
449                 tilesToRemove.append(index);
450         }
451         removeTiles(tilesToRemove);
452     }
453
454     m_controller.didRevalidateTiles();
455 }
456
457 TileGrid::TileCohort TileGrid::nextTileCohort() const
458 {
459     if (!m_cohortList.isEmpty())
460         return m_cohortList.last().cohort + 1;
461
462     return 1;
463 }
464
465 void TileGrid::startedNewCohort(TileCohort cohort)
466 {
467     m_cohortList.append(TileCohortInfo(cohort, monotonicallyIncreasingTime()));
468 #if PLATFORM(IOS)
469     if (!m_controller.isInWindow())
470         tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(&m_controller);
471 #endif
472 }
473
474 TileGrid::TileCohort TileGrid::newestTileCohort() const
475 {
476     return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort;
477 }
478
479 TileGrid::TileCohort TileGrid::oldestTileCohort() const
480 {
481     return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort;
482 }
483
484 void TileGrid::scheduleCohortRemoval()
485 {
486     const double cohortRemovalTimerSeconds = 1;
487
488     // Start the timer, or reschedule the timer from now if it's already active.
489     if (!m_cohortRemovalTimer.isActive())
490         m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds);
491 }
492
493 double TileGrid::TileCohortInfo::timeUntilExpiration()
494 {
495     double cohortLifeTimeSeconds = 2;
496     double timeThreshold = monotonicallyIncreasingTime() - cohortLifeTimeSeconds;
497     return creationTime - timeThreshold;
498 }
499
500 void TileGrid::cohortRemovalTimerFired()
501 {
502     if (m_cohortList.isEmpty()) {
503         m_cohortRemovalTimer.stop();
504         return;
505     }
506
507     while (!m_cohortList.isEmpty() && m_cohortList.first().timeUntilExpiration() < 0) {
508         TileCohortInfo firstCohort = m_cohortList.takeFirst();
509         removeTilesInCohort(firstCohort.cohort);
510     }
511
512     m_controller.updateTileCoverageMap();
513 }
514
515 IntRect TileGrid::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType)
516 {
517     if (m_controller.unparentsOffscreenTiles() && !m_controller.isInWindow())
518         return IntRect();
519
520     FloatRect scaledRect(rect);
521     scaledRect.scale(m_scale);
522     IntRect rectInTileCoords(enclosingIntRect(scaledRect));
523
524     TileIndex topLeft;
525     TileIndex bottomRight;
526     getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight);
527
528     TileCohort currCohort = nextTileCohort();
529     unsigned tilesInCohort = 0;
530
531     IntRect coverageRect;
532
533     for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
534         for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
535             TileIndex tileIndex(x, y);
536
537             IntRect tileRect = rectForTileIndex(tileIndex);
538             TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value;
539
540             coverageRect.unite(tileRect);
541
542             bool shouldChangeTileLayerFrame = false;
543
544             if (!tileInfo.layer) {
545                 tileInfo.layer = m_controller.createTileLayer(tileRect, *this);
546                 ASSERT(!m_tileRepaintCounts.contains(tileInfo.layer.get()));
547             } else {
548                 // We already have a layer for this tile. Ensure that its size is correct.
549                 FloatSize tileLayerSize(tileInfo.layer->bounds().size());
550                 shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size());
551
552                 if (shouldChangeTileLayerFrame) {
553                     tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
554                     tileInfo.layer->setPosition(tileRect.location());
555                     tileInfo.layer->setNeedsDisplay();
556                 }
557             }
558
559             if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) {
560                 tileInfo.cohort = currCohort;
561                 ++tilesInCohort;
562             }
563
564             bool shouldParentTileLayer = (!m_controller.unparentsOffscreenTiles() || m_controller.isInWindow()) && !tileInfo.layer->superlayer();
565
566             if (shouldParentTileLayer)
567                 m_containerLayer.get().appendSublayer(*tileInfo.layer);
568         }
569     }
570     
571     if (tilesInCohort)
572         startedNewCohort(currCohort);
573
574     return coverageRect;
575 }
576
577 IntRect TileGrid::extent() const
578 {
579     TileIndex topLeft;
580     TileIndex bottomRight;
581     getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight);
582
583     // Return index of top, left tile and the number of tiles across and down.
584     return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1);
585 }
586
587 double TileGrid::retainedTileBackingStoreMemory() const
588 {
589     double totalBytes = 0;
590     
591     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
592         const TileInfo& tileInfo = it->value;
593         if (tileInfo.layer->superlayer()) {
594             FloatRect bounds = tileInfo.layer->bounds();
595             double contentsScale = tileInfo.layer->contentsScale();
596             totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale;
597         }
598     }
599
600     return totalBytes;
601 }
602
603 // Return the rect in layer coords, not tile coords.
604 IntRect TileGrid::tileCoverageRect() const
605 {
606     IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect);
607     coverageRectInLayerCoords.scale(1 / m_scale);
608     return coverageRectInLayerCoords;
609 }
610
611 void TileGrid::drawTileMapContents(CGContextRef context, CGRect layerBounds) const
612 {
613     CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1);
614     CGContextFillRect(context, layerBounds);
615
616     CGFloat scaleFactor = layerBounds.size.width / m_controller.bounds().width();
617
618     CGFloat contextScale = scaleFactor / m_scale;
619     CGContextScaleCTM(context, contextScale, contextScale);
620     
621     for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
622         const TileInfo& tileInfo = it->value;
623         PlatformCALayer* tileLayer = tileInfo.layer.get();
624
625         CGFloat red = 1;
626         CGFloat green = 1;
627         CGFloat blue = 1;
628         CGFloat alpha = 1;
629         if (tileInfo.hasStaleContent) {
630             red = 0.25;
631             green = 0.125;
632             blue = 0;
633         } else if (m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != VisibleTileCohort) {
634             red = 0.8;
635             green = 0.8;
636             blue = 0.8;
637         }
638
639         TileCohort newestCohort = newestTileCohort();
640         TileCohort oldestCohort = oldestTileCohort();
641
642         if (!m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != VisibleTileCohort && newestCohort > oldestCohort)
643             alpha = 1 - (static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort));
644
645         CGContextSetRGBFillColor(context, red, green, blue, alpha);
646
647         if (tileLayer->superlayer()) {
648             CGContextSetLineWidth(context, 0.5 / contextScale);
649             CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
650         } else {
651             CGContextSetLineWidth(context, 1 / contextScale);
652             CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1);
653         }
654
655         CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height());
656         CGContextFillRect(context, frame);
657         CGContextStrokeRect(context, frame);
658
659         CGContextSetRGBFillColor(context, 0, 0, 0, 0.5);
660
661         String repaintCount = String::number(m_tileRepaintCounts.get(tileLayer));
662
663         CGContextSaveGState(context);
664
665         tileLayer->drawTextAtPoint(context, frame.origin.x + 64, frame.origin.y + 192, CGSizeMake(3, -3), 58,
666             repaintCount.ascii().data(), repaintCount.length());
667
668         CGContextRestoreGState(context);
669     }
670 }
671
672 void TileGrid::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&)
673 {
674 #if PLATFORM(IOS)
675     if (pthread_main_np())
676         WebThreadLock();
677 #endif
678
679     {
680         GraphicsContextStateSaver stateSaver(context);
681
682         FloatPoint3D layerOrigin = platformCALayer->position();
683         context.translate(-layerOrigin.x(), -layerOrigin.y());
684         context.scale(FloatSize(m_scale, m_scale));
685
686         PlatformCALayer::RepaintRectList dirtyRects = PlatformCALayer::collectRectsToPaint(context.platformContext(), platformCALayer);
687         PlatformCALayer::drawLayerContents(context.platformContext(), &m_controller.rootLayer(), dirtyRects);
688     }
689
690     int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer);
691     if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0))
692         PlatformCALayer::drawRepaintIndicator(context.platformContext(), platformCALayer, repaintCount, cachedCGColor(m_controller.tileDebugBorderColor()));
693
694     if (m_controller.scrollingPerformanceLoggingEnabled()) {
695         FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height());
696         visiblePart.intersect(m_controller.visibleRect());
697
698         if (repaintCount == 1 && !visiblePart.isEmpty())
699             WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), blankPixelCount());
700     }
701 }
702
703 float TileGrid::platformCALayerDeviceScaleFactor() const
704 {
705     return m_controller.rootLayer().owner()->platformCALayerDeviceScaleFactor();
706 }
707
708 bool TileGrid::platformCALayerShowDebugBorders() const
709 {
710     return m_controller.rootLayer().owner()->platformCALayerShowDebugBorders();
711 }
712
713 bool TileGrid::platformCALayerShowRepaintCounter(PlatformCALayer*) const
714 {
715     return m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(nullptr);
716 }
717
718 bool TileGrid::isUsingDisplayListDrawing(PlatformCALayer*) const
719 {
720     return m_controller.rootLayer().owner()->isUsingDisplayListDrawing(nullptr);
721 }
722
723 bool TileGrid::platformCALayerContentsOpaque() const
724 {
725     return m_controller.tilesAreOpaque();
726 }
727
728 int TileGrid::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer)
729 {
730     int repaintCount = 0;
731
732     if (m_tileRepaintCounts.contains(platformCALayer))
733         repaintCount = m_tileRepaintCounts.get(platformCALayer);
734
735     m_tileRepaintCounts.set(platformCALayer, ++repaintCount);
736
737     return repaintCount;
738 }
739
740 #if PLATFORM(IOS)
741 void TileGrid::removeUnparentedTilesNow()
742 {
743     while (!m_cohortList.isEmpty()) {
744         TileCohortInfo firstCohort = m_cohortList.takeFirst();
745         removeTilesInCohort(firstCohort.cohort);
746     }
747 }
748 #endif
749
750 } // namespace WebCore