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