890bc0f175079520c56c858c521e683b5ad9aebd
[WebKit-https.git] / Source / WebCore / platform / ios / LegacyTileGrid.mm
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "LegacyTileGrid.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #include "LegacyTileGridTile.h"
32 #include "LegacyTileLayer.h"
33 #include "LegacyTileLayerPool.h"
34 #include "SystemMemory.h"
35 #include "WAKWindow.h"
36 #include <algorithm>
37 #include <functional>
38 #include <pal/spi/cg/CoreGraphicsSPI.h>
39 #include <pal/spi/cocoa/QuartzCoreSPI.h>
40 #include <wtf/MemoryPressureHandler.h>
41
42 namespace WebCore {
43
44 LegacyTileGrid::LegacyTileGrid(LegacyTileCache& tileCache, const IntSize& tileSize)
45     : m_tileCache(tileCache)
46     , m_tileHostLayer(adoptNS([[LegacyTileHostLayer alloc] initWithTileGrid:this]))
47     , m_tileSize(tileSize)
48     , m_scale(1)
49     , m_validBounds(0, 0, std::numeric_limits<int>::max(), std::numeric_limits<int>::max()) 
50 {
51 }
52     
53 LegacyTileGrid::~LegacyTileGrid()
54 {
55     [m_tileHostLayer removeFromSuperlayer];
56 }
57
58 IntRect LegacyTileGrid::visibleRect() const
59 {
60     IntRect visibleRect = enclosingIntRect(m_tileCache.visibleRectInLayer(m_tileHostLayer.get()));
61
62     // When fast scrolling to the top, move the visible rect there immediately so we have tiles when the scrolling completes.
63     if (m_tileCache.tilingMode() == LegacyTileCache::ScrollToTop)
64         visibleRect.setY(0);
65
66     return visibleRect;
67 }
68
69 void LegacyTileGrid::dropAllTiles()
70 {
71     m_tiles.clear();
72 }
73
74 void LegacyTileGrid::dropTilesIntersectingRect(const IntRect& dropRect)
75 {
76     dropTilesBetweenRects(dropRect, IntRect());
77 }
78
79 void LegacyTileGrid::dropTilesOutsideRect(const IntRect& keepRect)
80 {
81     dropTilesBetweenRects(IntRect(0, 0, std::numeric_limits<int>::max(), std::numeric_limits<int>::max()), keepRect);
82 }
83
84 void LegacyTileGrid::dropTilesBetweenRects(const IntRect& dropRect, const IntRect& keepRect)
85 {
86     Vector<TileIndex> toRemove;
87     for (const auto& tile : m_tiles) {
88         const TileIndex& index = tile.key;
89         IntRect tileRect = tile.value->rect();
90         if (tileRect.intersects(dropRect) && !tileRect.intersects(keepRect))
91             toRemove.append(index);
92     }
93     unsigned removeCount = toRemove.size();
94     for (unsigned n = 0; n < removeCount; ++n)
95         m_tiles.remove(toRemove[n]);
96 }
97
98 unsigned LegacyTileGrid::tileByteSize() const
99 {
100     IntSize tilePixelSize = m_tileSize;
101     tilePixelSize.scale(m_tileCache.screenScale());
102     return LegacyTileLayerPool::bytesBackingLayerWithPixelSize(tilePixelSize);
103 }
104
105 template <typename T>
106 static bool isFartherAway(const std::pair<double, T>& a, const std::pair<double, T>& b)
107 {
108     return a.first > b.first;
109 }
110
111 bool LegacyTileGrid::dropDistantTiles(unsigned tilesNeeded, double shortestDistance)
112 {
113     unsigned bytesPerTile = tileByteSize();
114     unsigned bytesNeeded = tilesNeeded * bytesPerTile;
115     unsigned bytesUsed = tileCount() * bytesPerTile;
116     unsigned maximumBytes = m_tileCache.tileCapacityForGrid(this);
117
118     int bytesToReclaim = int(bytesUsed) - (int(maximumBytes) - bytesNeeded);
119     if (bytesToReclaim <= 0)
120         return true;
121
122     unsigned tilesToRemoveCount = bytesToReclaim / bytesPerTile;
123
124     IntRect visibleRect = this->visibleRect();
125     Vector<std::pair<double, TileIndex>> toRemove;
126     for (const auto& tile : m_tiles) {
127         const TileIndex& index = tile.key;
128         const IntRect& tileRect = tile.value->rect();
129         double distance = tileDistance2(visibleRect, tileRect);
130         if (distance <= shortestDistance)
131             continue;
132         toRemove.append(std::make_pair(distance, index));
133         ALLOW_DEPRECATED_DECLARATIONS_BEGIN
134         std::push_heap(toRemove.begin(), toRemove.end(), std::ptr_fun(isFartherAway<TileIndex>));
135         if (toRemove.size() > tilesToRemoveCount) {
136             std::pop_heap(toRemove.begin(), toRemove.end(), std::ptr_fun(isFartherAway<TileIndex>));
137             toRemove.removeLast();
138         }
139         ALLOW_DEPRECATED_DECLARATIONS_END
140     }
141     size_t removeCount = toRemove.size();
142     for (size_t n = 0; n < removeCount; ++n)
143         m_tiles.remove(toRemove[n].second);
144
145     if (!shortestDistance)
146         return true;
147
148     return tileCount() * bytesPerTile + bytesNeeded <= maximumBytes;
149 }
150
151 void LegacyTileGrid::addTilesCoveringRect(const IntRect& rectToCover)
152 {
153     // We never draw anything outside of our bounds.
154     IntRect rect(rectToCover);
155     rect.intersect(bounds());
156     if (rect.isEmpty())
157         return;
158
159     TileIndex topLeftIndex = tileIndexForPoint(topLeft(rect));
160     TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(rect));
161     for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
162         for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
163             TileIndex index(xIndex, yIndex);
164             if (!tileForIndex(index))
165                 addTileForIndex(index);
166         }
167     }
168 }
169
170 void LegacyTileGrid::addTileForIndex(const TileIndex& index)
171 {
172     m_tiles.set(index, LegacyTileGridTile::create(this, tileRectForIndex(index)));
173 }
174
175 CALayer* LegacyTileGrid::tileHostLayer() const
176 {
177     return m_tileHostLayer.get();
178 }
179
180 IntRect LegacyTileGrid::bounds() const
181 {
182     return IntRect(IntPoint(), IntSize([tileHostLayer() size]));
183 }
184
185 RefPtr<LegacyTileGridTile> LegacyTileGrid::tileForIndex(const TileIndex& index) const
186 {
187     return m_tiles.get(index);
188 }
189
190 IntRect LegacyTileGrid::tileRectForIndex(const TileIndex& index) const
191 {
192     IntRect rect(index.x() * m_tileSize.width() - (m_origin.x() ? m_tileSize.width() - m_origin.x() : 0),
193                  index.y() * m_tileSize.height() - (m_origin.y() ? m_tileSize.height() - m_origin.y() : 0),
194                  m_tileSize.width(),
195                  m_tileSize.height());
196     rect.intersect(bounds());
197     return rect;
198 }
199
200 LegacyTileGrid::TileIndex LegacyTileGrid::tileIndexForPoint(const IntPoint& point) const
201 {
202     ASSERT(m_origin.x() < m_tileSize.width());
203     ASSERT(m_origin.y() < m_tileSize.height());
204     int x = (point.x() + (m_origin.x() ? m_tileSize.width() - m_origin.x() : 0)) / m_tileSize.width();
205     int y = (point.y() + (m_origin.y() ? m_tileSize.height() - m_origin.y() : 0)) / m_tileSize.height();
206     return TileIndex(std::max(x, 0), std::max(y, 0));
207 }
208
209 void LegacyTileGrid::centerTileGridOrigin(const IntRect& visibleRect)
210 {
211     if (visibleRect.isEmpty())
212         return;
213
214     unsigned minimumHorizontalTiles = 1 + (visibleRect.width() - 1) / m_tileSize.width();
215     unsigned minimumVerticalTiles = 1 + (visibleRect.height() - 1) / m_tileSize.height();
216     TileIndex currentTopLeftIndex = tileIndexForPoint(topLeft(visibleRect));
217     TileIndex currentBottomRightIndex = tileIndexForPoint(bottomRight(visibleRect));
218     unsigned currentHorizontalTiles = currentBottomRightIndex.x() - currentTopLeftIndex.x() + 1;
219     unsigned currentVerticalTiles = currentBottomRightIndex.y() - currentTopLeftIndex.y() + 1;
220
221     // If we have tiles already, only center if we would get benefits from both directions (as we need to throw out existing tiles).
222     if (tileCount() && (currentHorizontalTiles == minimumHorizontalTiles || currentVerticalTiles == minimumVerticalTiles))
223         return;
224
225     IntPoint newOrigin(0, 0);
226     IntSize size = bounds().size();
227     if (size.width() > m_tileSize.width()) {
228         newOrigin.setX((visibleRect.x() - (minimumHorizontalTiles * m_tileSize.width() - visibleRect.width()) / 2) % m_tileSize.width());
229         if (newOrigin.x() < 0)
230             newOrigin.setX(0);
231     }
232     if (size.height() > m_tileSize.height()) {
233         newOrigin.setY((visibleRect.y() - (minimumVerticalTiles * m_tileSize.height() - visibleRect.height()) / 2) % m_tileSize.height());
234         if (newOrigin.y() < 0)
235             newOrigin.setY(0);
236     }
237
238     // Drop all existing tiles if the origin moved.
239     if (newOrigin == m_origin)
240         return;
241     m_tiles.clear();
242     m_origin = newOrigin;
243 }
244
245 RefPtr<LegacyTileGridTile> LegacyTileGrid::tileForPoint(const IntPoint& point) const
246 {
247     return tileForIndex(tileIndexForPoint(point));
248 }
249
250 bool LegacyTileGrid::tilesCover(const IntRect& rect) const
251 {
252     return tileForPoint(rect.location()) && tileForPoint(IntPoint(rect.maxX() - 1, rect.y())) &&
253     tileForPoint(IntPoint(rect.x(), rect.maxY() - 1)) && tileForPoint(IntPoint(rect.maxX() - 1, rect.maxY() - 1));
254 }
255
256 void LegacyTileGrid::updateTileOpacity()
257 {
258     TileMap::iterator end = m_tiles.end();
259     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it)
260         [it->value->tileLayer() setOpaque:m_tileCache.tilesOpaque()];
261 }
262
263 void LegacyTileGrid::updateTileBorderVisibility()
264 {
265     TileMap::iterator end = m_tiles.end();
266     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it)
267         it->value->showBorder(m_tileCache.tileBordersVisible());
268 }
269
270 unsigned LegacyTileGrid::tileCount() const
271 {
272     return m_tiles.size();
273 }
274
275 bool LegacyTileGrid::checkDoSingleTileLayout()
276 {
277     IntSize size = bounds().size();
278     if (size.width() > m_tileSize.width() || size.height() > m_tileSize.height())
279         return false;
280
281     if (m_origin != IntPoint(0, 0)) {
282         m_tiles.clear();
283         m_origin = IntPoint(0, 0);
284     }
285
286     dropInvalidTiles();
287
288     if (size.isEmpty()) {
289         ASSERT(!m_tiles.get(TileIndex(0, 0)));
290         return true;
291     }
292
293     TileIndex originIndex(0, 0);
294     if (!m_tiles.get(originIndex))
295         m_tiles.set(originIndex, LegacyTileGridTile::create(this, tileRectForIndex(originIndex)));
296
297     return true;
298 }
299
300 void LegacyTileGrid::updateHostLayerSize()
301 {
302     CALayer* hostLayer = m_tileCache.hostLayer();
303     CGRect tileHostBounds = [hostLayer convertRect:[hostLayer bounds] toLayer:tileHostLayer()];
304     CGSize transformedSize;
305     transformedSize.width = CGRound(tileHostBounds.size.width);
306     transformedSize.height = CGRound(tileHostBounds.size.height);
307
308     CGRect bounds = [tileHostLayer() bounds];
309     if (CGSizeEqualToSize(bounds.size, transformedSize))
310         return;
311     bounds.size = transformedSize;
312     [tileHostLayer() setBounds:bounds];
313 }
314
315 void LegacyTileGrid::dropInvalidTiles()
316 {
317     IntRect bounds = this->bounds();
318     IntRect dropBounds = intersection(m_validBounds, bounds);
319     Vector<TileIndex> toRemove;
320     for (const auto& tile : m_tiles) {
321         const TileIndex& index = tile.key;
322         const IntRect& tileRect = tile.value->rect();
323         IntRect expectedTileRect = tileRectForIndex(index);
324         if (expectedTileRect != tileRect || !dropBounds.contains(tileRect))
325             toRemove.append(index);
326     }
327     unsigned removeCount = toRemove.size();
328     for (unsigned n = 0; n < removeCount; ++n)
329         m_tiles.remove(toRemove[n]);
330
331     m_validBounds = bounds;
332 }
333
334 void LegacyTileGrid::invalidateTiles(const IntRect& dirtyRect)
335 {
336     if (!hasTiles())
337         return;
338
339     IntRect bounds = this->bounds();
340     if (intersection(bounds, m_validBounds) != m_validBounds) {
341         // The bounds have got smaller. Everything outside will also be considered invalid and will be dropped by dropInvalidTiles().
342         // Due to dirtyRect being limited to current bounds the tiles that are temporarily outside might miss invalidation 
343         // completely othwerwise.
344         m_validBounds = bounds;
345     }
346
347     Vector<TileIndex> invalidatedTiles;
348
349     if (dirtyRect.width() > m_tileSize.width() * 4 || dirtyRect.height() > m_tileSize.height() * 4) {
350         // For large invalidates, iterate over live tiles.
351         TileMap::iterator end = m_tiles.end();
352         for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
353             LegacyTileGridTile* tile = it->value.get();
354             if (!tile->rect().intersects(dirtyRect))
355                continue;
356             tile->invalidateRect(dirtyRect);
357             invalidatedTiles.append(it->key);
358         }
359     } else {
360         TileIndex topLeftIndex = tileIndexForPoint(topLeft(dirtyRect));
361         TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(dirtyRect));
362         for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
363             for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
364                 TileIndex index(xIndex, yIndex);
365                 RefPtr<LegacyTileGridTile> tile = tileForIndex(index);
366                 if (!tile)
367                     continue;
368                 if (!tile->rect().intersects(dirtyRect))
369                     continue;
370                 tile->invalidateRect(dirtyRect);
371                 invalidatedTiles.append(index);
372             }
373         }
374     }
375     if (invalidatedTiles.isEmpty())
376         return;
377     // When using minimal coverage, drop speculative tiles instead of updating them.
378     if (!shouldUseMinimalTileCoverage())
379         return;
380     if (m_tileCache.tilingMode() != LegacyTileCache::Minimal && m_tileCache.tilingMode() != LegacyTileCache::Normal)
381         return;
382     IntRect visibleRect = this->visibleRect();
383     unsigned count = invalidatedTiles.size();
384     for (unsigned i = 0; i < count; ++i) {
385         RefPtr<LegacyTileGridTile> tile = tileForIndex(invalidatedTiles[i]);
386         if (!tile->rect().intersects(visibleRect))
387             m_tiles.remove(invalidatedTiles[i]);
388     }
389 }
390
391 bool LegacyTileGrid::shouldUseMinimalTileCoverage() const
392 {
393     return m_tileCache.tilingMode() == LegacyTileCache::Minimal
394         || !m_tileCache.isSpeculativeTileCreationEnabled()
395         || MemoryPressureHandler::singleton().isUnderMemoryPressure();
396 }
397
398 IntRect LegacyTileGrid::adjustCoverRectForPageBounds(const IntRect& rect) const
399 {
400     // Adjust the rect so that it stays within the bounds and keeps the pixel size.
401     IntRect bounds = this->bounds();
402     IntRect adjustedRect = rect;
403     adjustedRect.move(rect.x() < bounds.x() ? bounds.x() - rect.x() : 0,
404               rect.y() < bounds.y() ? bounds.y() - rect.y() : 0);
405     adjustedRect.move(rect.maxX() > bounds.maxX() ? bounds.maxX() - rect.maxX() : 0,
406               rect.maxY() > bounds.maxY() ? bounds.maxY() - rect.maxY() : 0);
407     adjustedRect = intersection(bounds, adjustedRect);
408     if (adjustedRect == rect || adjustedRect.isEmpty() || shouldUseMinimalTileCoverage())
409         return adjustedRect;
410     int pixels = adjustedRect.width() * adjustedRect.height();
411     if (adjustedRect.width() != rect.width())
412         adjustedRect.inflateY((pixels / adjustedRect.width() - adjustedRect.height()) / 2);
413     else if (adjustedRect.height() != rect.height())
414         adjustedRect.inflateX((pixels / adjustedRect.height() - adjustedRect.width()) / 2);
415     return intersection(adjustedRect, bounds);
416 }
417
418 IntRect LegacyTileGrid::calculateCoverRect(const IntRect& visibleRect, bool& centerGrid)
419 {
420     // Use minimum coverRect if we are under memory pressure.
421     if (shouldUseMinimalTileCoverage()) {
422         centerGrid = true;
423         return visibleRect;
424     }
425     IntRect coverRect = visibleRect;
426     centerGrid = false;
427     coverRect.inflateX(visibleRect.width() / 2);
428     coverRect.inflateY(visibleRect.height());
429     return adjustCoverRectForPageBounds(coverRect);
430 }
431
432 double LegacyTileGrid::tileDistance2(const IntRect& visibleRect, const IntRect& tileRect) const
433 {
434     // The "distance" calculated here is used to pick which tile to cache next. The idea is to create those
435     // closest to the current viewport first so the user is more likely to see already rendered content we she
436     // scrolls. The calculation is weighted to prefer vertical and downward direction.
437     if (visibleRect.intersects(tileRect))
438         return 0;
439     IntPoint visibleCenter = visibleRect.location() + IntSize(visibleRect.width() / 2, visibleRect.height() / 2);
440     IntPoint tileCenter = tileRect.location() + IntSize(tileRect.width() / 2, tileRect.height() / 2);
441     
442     double horizontalBias = 1.0;
443     double leftwardBias = 1.0;
444     double rightwardBias = 1.0;
445
446     double verticalBias = 1.0;
447     double upwardBias = 1.0;
448     double downwardBias = 1.0;
449
450     const double tilingBiasVeryLikely = 0.8;
451     const double tilingBiasLikely = 0.9;
452
453     switch (m_tileCache.tilingDirection()) {
454     case LegacyTileCache::TilingDirectionUp:
455         verticalBias = tilingBiasVeryLikely;
456         upwardBias = tilingBiasLikely;
457         break;
458     case LegacyTileCache::TilingDirectionDown:
459         verticalBias = tilingBiasVeryLikely;
460         downwardBias = tilingBiasLikely;
461         break;
462     case LegacyTileCache::TilingDirectionLeft:
463         horizontalBias = tilingBiasVeryLikely;
464         leftwardBias = tilingBiasLikely;
465         break;
466     case LegacyTileCache::TilingDirectionRight:
467         horizontalBias = tilingBiasVeryLikely;
468         rightwardBias = tilingBiasLikely;
469         break;
470     }
471
472     double xScale = horizontalBias * visibleRect.height() / visibleRect.width() * (tileCenter.x() >= visibleCenter.x() ? rightwardBias : leftwardBias);
473     double yScale = verticalBias * visibleRect.width() / visibleRect.height() * (tileCenter.y() >= visibleCenter.y() ? downwardBias : upwardBias);
474
475     double xDistance = xScale * (tileCenter.x() - visibleCenter.x());
476     double yDistance = yScale * (tileCenter.y() - visibleCenter.y());
477
478     double distance2 = xDistance * xDistance + yDistance * yDistance;
479     return distance2;
480 }
481
482 void LegacyTileGrid::createTiles(LegacyTileCache::SynchronousTileCreationMode creationMode)
483 {
484     IntRect visibleRect = this->visibleRect();
485     if (visibleRect.isEmpty())
486         return;
487
488     // Drop tiles that are wrong size or outside the frame (because the frame has been resized).
489     dropInvalidTiles();
490
491     bool centerGrid;
492     IntRect coverRect = calculateCoverRect(visibleRect, centerGrid);
493
494     // If tile size is bigger than the view, centering minimizes the painting needed to cover the screen.
495     // This is especially useful after zooming 
496     centerGrid = centerGrid || !tileCount();
497     if (centerGrid)
498         centerTileGridOrigin(visibleRect);
499
500     double shortestDistance = std::numeric_limits<double>::infinity();
501     double coveredDistance = 0;
502     Vector<LegacyTileGrid::TileIndex> tilesToCreate;
503     unsigned pendingTileCount = 0;
504
505     LegacyTileGrid::TileIndex topLeftIndex = tileIndexForPoint(topLeft(coverRect));
506     LegacyTileGrid::TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(coverRect));
507     for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
508         for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
509             LegacyTileGrid::TileIndex index(xIndex, yIndex);
510             // Currently visible tiles have distance of 0 and get all created in the same transaction.
511             double distance = tileDistance2(visibleRect, tileRectForIndex(index));
512             if (distance > coveredDistance)
513                 coveredDistance = distance;
514             if (tileForIndex(index))
515                 continue;
516             ++pendingTileCount;
517             if (distance > shortestDistance)
518                 continue;
519             if (distance < shortestDistance) {
520                 tilesToCreate.clear();
521                 shortestDistance = distance;
522             }
523             tilesToCreate.append(index);
524         }
525     }
526
527     size_t tilesToCreateCount = tilesToCreate.size();
528
529     // Tile creation timer will invoke this function again in CoverSpeculative mode.
530     bool candidateTilesAreSpeculative = shortestDistance > 0;
531     if (creationMode == LegacyTileCache::CoverVisibleOnly && candidateTilesAreSpeculative)
532         tilesToCreateCount = 0;
533
534     // Even if we don't create any tiles, we should still drop distant tiles
535     // in case coverRect got smaller.
536     double keepDistance = std::min(shortestDistance, coveredDistance);
537     if (!dropDistantTiles(tilesToCreateCount, keepDistance))
538         return;
539
540     ASSERT(pendingTileCount >= tilesToCreateCount);
541     if (!pendingTileCount)
542         return;
543
544     for (size_t n = 0; n < tilesToCreateCount; ++n)
545         addTileForIndex(tilesToCreate[n]);
546
547     bool didCreateTiles = !!tilesToCreateCount;
548     bool createMoreTiles = pendingTileCount > tilesToCreateCount;
549     m_tileCache.finishedCreatingTiles(didCreateTiles, createMoreTiles);
550 }
551
552 void LegacyTileGrid::dumpTiles()
553 {
554     IntRect visibleRect = this->visibleRect();
555     NSLog(@"transformed visibleRect = [%6d %6d %6d %6d]", visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
556     unsigned i = 0;
557     TileMap::iterator end = m_tiles.end();
558     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
559         TileIndex& index = it->key;
560         IntRect tileRect = it->value->rect();
561         NSLog(@"#%-3d (%3d %3d) - [%6d %6d %6d %6d]%@", ++i, index.x(), index.y(), tileRect.x(), tileRect.y(), tileRect.width(), tileRect.height(), tileRect.intersects(visibleRect) ? @" *" : @"");
562         NSLog(@"     %@", [it->value->tileLayer() contents]);
563     }
564 }
565
566 } // namespace WebCore
567
568 #endif // PLATFORM(IOS_FAMILY)