11ea51b69d167807cddbc2d2c272c1ad4695c55a
[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)
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         std::push_heap(toRemove.begin(), toRemove.end(), std::ptr_fun(isFartherAway<TileIndex>));
134         if (toRemove.size() > tilesToRemoveCount) {
135             std::pop_heap(toRemove.begin(), toRemove.end(), std::ptr_fun(isFartherAway<TileIndex>));
136             toRemove.removeLast();
137         }
138     }
139     size_t removeCount = toRemove.size();
140     for (size_t n = 0; n < removeCount; ++n)
141         m_tiles.remove(toRemove[n].second);
142
143     if (!shortestDistance)
144         return true;
145
146     return tileCount() * bytesPerTile + bytesNeeded <= maximumBytes;
147 }
148
149 void LegacyTileGrid::addTilesCoveringRect(const IntRect& rectToCover)
150 {
151     // We never draw anything outside of our bounds.
152     IntRect rect(rectToCover);
153     rect.intersect(bounds());
154     if (rect.isEmpty())
155         return;
156
157     TileIndex topLeftIndex = tileIndexForPoint(topLeft(rect));
158     TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(rect));
159     for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
160         for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
161             TileIndex index(xIndex, yIndex);
162             if (!tileForIndex(index))
163                 addTileForIndex(index);
164         }
165     }
166 }
167
168 void LegacyTileGrid::addTileForIndex(const TileIndex& index)
169 {
170     m_tiles.set(index, LegacyTileGridTile::create(this, tileRectForIndex(index)));
171 }
172
173 CALayer* LegacyTileGrid::tileHostLayer() const
174 {
175     return m_tileHostLayer.get();
176 }
177
178 IntRect LegacyTileGrid::bounds() const
179 {
180     return IntRect(IntPoint(), IntSize([tileHostLayer() size]));
181 }
182
183 RefPtr<LegacyTileGridTile> LegacyTileGrid::tileForIndex(const TileIndex& index) const
184 {
185     return m_tiles.get(index);
186 }
187
188 IntRect LegacyTileGrid::tileRectForIndex(const TileIndex& index) const
189 {
190     IntRect rect(index.x() * m_tileSize.width() - (m_origin.x() ? m_tileSize.width() - m_origin.x() : 0),
191                  index.y() * m_tileSize.height() - (m_origin.y() ? m_tileSize.height() - m_origin.y() : 0),
192                  m_tileSize.width(),
193                  m_tileSize.height());
194     rect.intersect(bounds());
195     return rect;
196 }
197
198 LegacyTileGrid::TileIndex LegacyTileGrid::tileIndexForPoint(const IntPoint& point) const
199 {
200     ASSERT(m_origin.x() < m_tileSize.width());
201     ASSERT(m_origin.y() < m_tileSize.height());
202     int x = (point.x() + (m_origin.x() ? m_tileSize.width() - m_origin.x() : 0)) / m_tileSize.width();
203     int y = (point.y() + (m_origin.y() ? m_tileSize.height() - m_origin.y() : 0)) / m_tileSize.height();
204     return TileIndex(std::max(x, 0), std::max(y, 0));
205 }
206
207 void LegacyTileGrid::centerTileGridOrigin(const IntRect& visibleRect)
208 {
209     if (visibleRect.isEmpty())
210         return;
211
212     unsigned minimumHorizontalTiles = 1 + (visibleRect.width() - 1) / m_tileSize.width();
213     unsigned minimumVerticalTiles = 1 + (visibleRect.height() - 1) / m_tileSize.height();
214     TileIndex currentTopLeftIndex = tileIndexForPoint(topLeft(visibleRect));
215     TileIndex currentBottomRightIndex = tileIndexForPoint(bottomRight(visibleRect));
216     unsigned currentHorizontalTiles = currentBottomRightIndex.x() - currentTopLeftIndex.x() + 1;
217     unsigned currentVerticalTiles = currentBottomRightIndex.y() - currentTopLeftIndex.y() + 1;
218
219     // If we have tiles already, only center if we would get benefits from both directions (as we need to throw out existing tiles).
220     if (tileCount() && (currentHorizontalTiles == minimumHorizontalTiles || currentVerticalTiles == minimumVerticalTiles))
221         return;
222
223     IntPoint newOrigin(0, 0);
224     IntSize size = bounds().size();
225     if (size.width() > m_tileSize.width()) {
226         newOrigin.setX((visibleRect.x() - (minimumHorizontalTiles * m_tileSize.width() - visibleRect.width()) / 2) % m_tileSize.width());
227         if (newOrigin.x() < 0)
228             newOrigin.setX(0);
229     }
230     if (size.height() > m_tileSize.height()) {
231         newOrigin.setY((visibleRect.y() - (minimumVerticalTiles * m_tileSize.height() - visibleRect.height()) / 2) % m_tileSize.height());
232         if (newOrigin.y() < 0)
233             newOrigin.setY(0);
234     }
235
236     // Drop all existing tiles if the origin moved.
237     if (newOrigin == m_origin)
238         return;
239     m_tiles.clear();
240     m_origin = newOrigin;
241 }
242
243 RefPtr<LegacyTileGridTile> LegacyTileGrid::tileForPoint(const IntPoint& point) const
244 {
245     return tileForIndex(tileIndexForPoint(point));
246 }
247
248 bool LegacyTileGrid::tilesCover(const IntRect& rect) const
249 {
250     return tileForPoint(rect.location()) && tileForPoint(IntPoint(rect.maxX() - 1, rect.y())) &&
251     tileForPoint(IntPoint(rect.x(), rect.maxY() - 1)) && tileForPoint(IntPoint(rect.maxX() - 1, rect.maxY() - 1));
252 }
253
254 void LegacyTileGrid::updateTileOpacity()
255 {
256     TileMap::iterator end = m_tiles.end();
257     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it)
258         [it->value->tileLayer() setOpaque:m_tileCache.tilesOpaque()];
259 }
260
261 void LegacyTileGrid::updateTileBorderVisibility()
262 {
263     TileMap::iterator end = m_tiles.end();
264     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it)
265         it->value->showBorder(m_tileCache.tileBordersVisible());
266 }
267
268 unsigned LegacyTileGrid::tileCount() const
269 {
270     return m_tiles.size();
271 }
272
273 bool LegacyTileGrid::checkDoSingleTileLayout()
274 {
275     IntSize size = bounds().size();
276     if (size.width() > m_tileSize.width() || size.height() > m_tileSize.height())
277         return false;
278
279     if (m_origin != IntPoint(0, 0)) {
280         m_tiles.clear();
281         m_origin = IntPoint(0, 0);
282     }
283
284     dropInvalidTiles();
285
286     if (size.isEmpty()) {
287         ASSERT(!m_tiles.get(TileIndex(0, 0)));
288         return true;
289     }
290
291     TileIndex originIndex(0, 0);
292     if (!m_tiles.get(originIndex))
293         m_tiles.set(originIndex, LegacyTileGridTile::create(this, tileRectForIndex(originIndex)));
294
295     return true;
296 }
297
298 void LegacyTileGrid::updateHostLayerSize()
299 {
300     CALayer* hostLayer = m_tileCache.hostLayer();
301     CGRect tileHostBounds = [hostLayer convertRect:[hostLayer bounds] toLayer:tileHostLayer()];
302     CGSize transformedSize;
303     transformedSize.width = CGRound(tileHostBounds.size.width);
304     transformedSize.height = CGRound(tileHostBounds.size.height);
305
306     CGRect bounds = [tileHostLayer() bounds];
307     if (CGSizeEqualToSize(bounds.size, transformedSize))
308         return;
309     bounds.size = transformedSize;
310     [tileHostLayer() setBounds:bounds];
311 }
312
313 void LegacyTileGrid::dropInvalidTiles()
314 {
315     IntRect bounds = this->bounds();
316     IntRect dropBounds = intersection(m_validBounds, bounds);
317     Vector<TileIndex> toRemove;
318     for (const auto& tile : m_tiles) {
319         const TileIndex& index = tile.key;
320         const IntRect& tileRect = tile.value->rect();
321         IntRect expectedTileRect = tileRectForIndex(index);
322         if (expectedTileRect != tileRect || !dropBounds.contains(tileRect))
323             toRemove.append(index);
324     }
325     unsigned removeCount = toRemove.size();
326     for (unsigned n = 0; n < removeCount; ++n)
327         m_tiles.remove(toRemove[n]);
328
329     m_validBounds = bounds;
330 }
331
332 void LegacyTileGrid::invalidateTiles(const IntRect& dirtyRect)
333 {
334     if (!hasTiles())
335         return;
336
337     IntRect bounds = this->bounds();
338     if (intersection(bounds, m_validBounds) != m_validBounds) {
339         // The bounds have got smaller. Everything outside will also be considered invalid and will be dropped by dropInvalidTiles().
340         // Due to dirtyRect being limited to current bounds the tiles that are temporarily outside might miss invalidation 
341         // completely othwerwise.
342         m_validBounds = bounds;
343     }
344
345     Vector<TileIndex> invalidatedTiles;
346
347     if (dirtyRect.width() > m_tileSize.width() * 4 || dirtyRect.height() > m_tileSize.height() * 4) {
348         // For large invalidates, iterate over live tiles.
349         TileMap::iterator end = m_tiles.end();
350         for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
351             LegacyTileGridTile* tile = it->value.get();
352             if (!tile->rect().intersects(dirtyRect))
353                continue;
354             tile->invalidateRect(dirtyRect);
355             invalidatedTiles.append(it->key);
356         }
357     } else {
358         TileIndex topLeftIndex = tileIndexForPoint(topLeft(dirtyRect));
359         TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(dirtyRect));
360         for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
361             for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
362                 TileIndex index(xIndex, yIndex);
363                 RefPtr<LegacyTileGridTile> tile = tileForIndex(index);
364                 if (!tile)
365                     continue;
366                 if (!tile->rect().intersects(dirtyRect))
367                     continue;
368                 tile->invalidateRect(dirtyRect);
369                 invalidatedTiles.append(index);
370             }
371         }
372     }
373     if (invalidatedTiles.isEmpty())
374         return;
375     // When using minimal coverage, drop speculative tiles instead of updating them.
376     if (!shouldUseMinimalTileCoverage())
377         return;
378     if (m_tileCache.tilingMode() != LegacyTileCache::Minimal && m_tileCache.tilingMode() != LegacyTileCache::Normal)
379         return;
380     IntRect visibleRect = this->visibleRect();
381     unsigned count = invalidatedTiles.size();
382     for (unsigned i = 0; i < count; ++i) {
383         RefPtr<LegacyTileGridTile> tile = tileForIndex(invalidatedTiles[i]);
384         if (!tile->rect().intersects(visibleRect))
385             m_tiles.remove(invalidatedTiles[i]);
386     }
387 }
388
389 bool LegacyTileGrid::shouldUseMinimalTileCoverage() const
390 {
391     return m_tileCache.tilingMode() == LegacyTileCache::Minimal
392         || !m_tileCache.isSpeculativeTileCreationEnabled()
393         || MemoryPressureHandler::singleton().isUnderMemoryPressure();
394 }
395
396 IntRect LegacyTileGrid::adjustCoverRectForPageBounds(const IntRect& rect) const
397 {
398     // Adjust the rect so that it stays within the bounds and keeps the pixel size.
399     IntRect bounds = this->bounds();
400     IntRect adjustedRect = rect;
401     adjustedRect.move(rect.x() < bounds.x() ? bounds.x() - rect.x() : 0,
402               rect.y() < bounds.y() ? bounds.y() - rect.y() : 0);
403     adjustedRect.move(rect.maxX() > bounds.maxX() ? bounds.maxX() - rect.maxX() : 0,
404               rect.maxY() > bounds.maxY() ? bounds.maxY() - rect.maxY() : 0);
405     adjustedRect = intersection(bounds, adjustedRect);
406     if (adjustedRect == rect || adjustedRect.isEmpty() || shouldUseMinimalTileCoverage())
407         return adjustedRect;
408     int pixels = adjustedRect.width() * adjustedRect.height();
409     if (adjustedRect.width() != rect.width())
410         adjustedRect.inflateY((pixels / adjustedRect.width() - adjustedRect.height()) / 2);
411     else if (adjustedRect.height() != rect.height())
412         adjustedRect.inflateX((pixels / adjustedRect.height() - adjustedRect.width()) / 2);
413     return intersection(adjustedRect, bounds);
414 }
415
416 IntRect LegacyTileGrid::calculateCoverRect(const IntRect& visibleRect, bool& centerGrid)
417 {
418     // Use minimum coverRect if we are under memory pressure.
419     if (shouldUseMinimalTileCoverage()) {
420         centerGrid = true;
421         return visibleRect;
422     }
423     IntRect coverRect = visibleRect;
424     centerGrid = false;
425     coverRect.inflateX(visibleRect.width() / 2);
426     coverRect.inflateY(visibleRect.height());
427     return adjustCoverRectForPageBounds(coverRect);
428 }
429
430 double LegacyTileGrid::tileDistance2(const IntRect& visibleRect, const IntRect& tileRect) const
431 {
432     // The "distance" calculated here is used to pick which tile to cache next. The idea is to create those
433     // closest to the current viewport first so the user is more likely to see already rendered content we she
434     // scrolls. The calculation is weighted to prefer vertical and downward direction.
435     if (visibleRect.intersects(tileRect))
436         return 0;
437     IntPoint visibleCenter = visibleRect.location() + IntSize(visibleRect.width() / 2, visibleRect.height() / 2);
438     IntPoint tileCenter = tileRect.location() + IntSize(tileRect.width() / 2, tileRect.height() / 2);
439     
440     double horizontalBias = 1.0;
441     double leftwardBias = 1.0;
442     double rightwardBias = 1.0;
443
444     double verticalBias = 1.0;
445     double upwardBias = 1.0;
446     double downwardBias = 1.0;
447
448     const double tilingBiasVeryLikely = 0.8;
449     const double tilingBiasLikely = 0.9;
450
451     switch (m_tileCache.tilingDirection()) {
452     case LegacyTileCache::TilingDirectionUp:
453         verticalBias = tilingBiasVeryLikely;
454         upwardBias = tilingBiasLikely;
455         break;
456     case LegacyTileCache::TilingDirectionDown:
457         verticalBias = tilingBiasVeryLikely;
458         downwardBias = tilingBiasLikely;
459         break;
460     case LegacyTileCache::TilingDirectionLeft:
461         horizontalBias = tilingBiasVeryLikely;
462         leftwardBias = tilingBiasLikely;
463         break;
464     case LegacyTileCache::TilingDirectionRight:
465         horizontalBias = tilingBiasVeryLikely;
466         rightwardBias = tilingBiasLikely;
467         break;
468     }
469
470     double xScale = horizontalBias * visibleRect.height() / visibleRect.width() * (tileCenter.x() >= visibleCenter.x() ? rightwardBias : leftwardBias);
471     double yScale = verticalBias * visibleRect.width() / visibleRect.height() * (tileCenter.y() >= visibleCenter.y() ? downwardBias : upwardBias);
472
473     double xDistance = xScale * (tileCenter.x() - visibleCenter.x());
474     double yDistance = yScale * (tileCenter.y() - visibleCenter.y());
475
476     double distance2 = xDistance * xDistance + yDistance * yDistance;
477     return distance2;
478 }
479
480 void LegacyTileGrid::createTiles(LegacyTileCache::SynchronousTileCreationMode creationMode)
481 {
482     IntRect visibleRect = this->visibleRect();
483     if (visibleRect.isEmpty())
484         return;
485
486     // Drop tiles that are wrong size or outside the frame (because the frame has been resized).
487     dropInvalidTiles();
488
489     bool centerGrid;
490     IntRect coverRect = calculateCoverRect(visibleRect, centerGrid);
491
492     // If tile size is bigger than the view, centering minimizes the painting needed to cover the screen.
493     // This is especially useful after zooming 
494     centerGrid = centerGrid || !tileCount();
495     if (centerGrid)
496         centerTileGridOrigin(visibleRect);
497
498     double shortestDistance = std::numeric_limits<double>::infinity();
499     double coveredDistance = 0;
500     Vector<LegacyTileGrid::TileIndex> tilesToCreate;
501     unsigned pendingTileCount = 0;
502
503     LegacyTileGrid::TileIndex topLeftIndex = tileIndexForPoint(topLeft(coverRect));
504     LegacyTileGrid::TileIndex bottomRightIndex = tileIndexForPoint(bottomRight(coverRect));
505     for (int yIndex = topLeftIndex.y(); yIndex <= bottomRightIndex.y(); ++yIndex) {
506         for (int xIndex = topLeftIndex.x(); xIndex <= bottomRightIndex.x(); ++xIndex) {
507             LegacyTileGrid::TileIndex index(xIndex, yIndex);
508             // Currently visible tiles have distance of 0 and get all created in the same transaction.
509             double distance = tileDistance2(visibleRect, tileRectForIndex(index));
510             if (distance > coveredDistance)
511                 coveredDistance = distance;
512             if (tileForIndex(index))
513                 continue;
514             ++pendingTileCount;
515             if (distance > shortestDistance)
516                 continue;
517             if (distance < shortestDistance) {
518                 tilesToCreate.clear();
519                 shortestDistance = distance;
520             }
521             tilesToCreate.append(index);
522         }
523     }
524
525     size_t tilesToCreateCount = tilesToCreate.size();
526
527     // Tile creation timer will invoke this function again in CoverSpeculative mode.
528     bool candidateTilesAreSpeculative = shortestDistance > 0;
529     if (creationMode == LegacyTileCache::CoverVisibleOnly && candidateTilesAreSpeculative)
530         tilesToCreateCount = 0;
531
532     // Even if we don't create any tiles, we should still drop distant tiles
533     // in case coverRect got smaller.
534     double keepDistance = std::min(shortestDistance, coveredDistance);
535     if (!dropDistantTiles(tilesToCreateCount, keepDistance))
536         return;
537
538     ASSERT(pendingTileCount >= tilesToCreateCount);
539     if (!pendingTileCount)
540         return;
541
542     for (size_t n = 0; n < tilesToCreateCount; ++n)
543         addTileForIndex(tilesToCreate[n]);
544
545     bool didCreateTiles = !!tilesToCreateCount;
546     bool createMoreTiles = pendingTileCount > tilesToCreateCount;
547     m_tileCache.finishedCreatingTiles(didCreateTiles, createMoreTiles);
548 }
549
550 void LegacyTileGrid::dumpTiles()
551 {
552     IntRect visibleRect = this->visibleRect();
553     NSLog(@"transformed visibleRect = [%6d %6d %6d %6d]", visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
554     unsigned i = 0;
555     TileMap::iterator end = m_tiles.end();
556     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
557         TileIndex& index = it->key;
558         IntRect tileRect = it->value->rect();
559         NSLog(@"#%-3d (%3d %3d) - [%6d %6d %6d %6d]%@", ++i, index.x(), index.y(), tileRect.x(), tileRect.y(), tileRect.width(), tileRect.height(), tileRect.intersects(visibleRect) ? @" *" : @"");
560         NSLog(@"     %@", [it->value->tileLayer() contents]);
561     }
562 }
563
564 } // namespace WebCore
565
566 #endif // PLATFORM(IOS)