Rename Mutex to DeprecatedMutex
[WebKit-https.git] / Source / WebCore / platform / ios / LegacyTileCache.mm
1 /*
2  * Copyright (C) 2009, 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 "LegacyTileCache.h"
28
29 #if PLATFORM(IOS)
30
31 #include "FontAntialiasingStateSaver.h"
32 #include "LegacyTileGrid.h"
33 #include "LegacyTileGridTile.h"
34 #include "LegacyTileLayer.h"
35 #include "LegacyTileLayerPool.h"
36 #include "Logging.h"
37 #include "MemoryPressureHandler.h"
38 #include "QuartzCoreSPI.h"
39 #include "SystemMemory.h"
40 #include "WAKWindow.h"
41 #include "WKGraphics.h"
42 #include "WebCoreSystemInterface.h"
43 #include "WebCoreThreadRun.h"
44 #include <wtf/CurrentTime.h>
45 #include <wtf/RAMSize.h>
46
47 @interface WAKView (WebViewExtras)
48 - (void)_dispatchTileDidDraw:(CALayer*)tile;
49 - (void)_willStartScrollingOrZooming;
50 - (void)_didFinishScrollingOrZooming;
51 - (void)_dispatchTileDidDraw;
52 - (void)_scheduleLayerFlushForPendingTileCacheRepaint;
53 @end
54
55 @interface LegacyTileCacheTombstone : NSObject {
56     BOOL dead;
57 }
58 @property(getter=isDead) BOOL dead;
59
60 @end
61
62 @implementation LegacyTileCacheTombstone
63
64 @synthesize dead;
65
66 @end
67
68 namespace WebCore {
69
70 LegacyTileCache::LegacyTileCache(WAKWindow* window)
71     : m_window(window)
72     , m_keepsZoomedOutTiles(false)
73     , m_hasPendingLayoutTiles(false)
74     , m_hasPendingUpdateTilingMode(false)
75     , m_tombstone(adoptNS([[LegacyTileCacheTombstone alloc] init]))
76     , m_tilingMode(Normal)
77     , m_tilingDirection(TilingDirectionDown)
78     , m_tileSize(512, 512)
79     , m_tilesOpaque(true)
80     , m_tileBordersVisible(false)
81     , m_tilePaintCountersVisible(false)
82     , m_acceleratedDrawingEnabled(false)
83     , m_isSpeculativeTileCreationEnabled(true)
84     , m_didCallWillStartScrollingOrZooming(false)
85     , m_zoomedOutTileGrid(std::make_unique<LegacyTileGrid>(*this, m_tileSize))
86     , m_tileCreationTimer(*this, &LegacyTileCache::tileCreationTimerFired)
87     , m_currentScale(1.f)
88     , m_pendingScale(0)
89     , m_pendingZoomedOutScale(0)
90     , m_tileControllerShouldUseLowScaleTiles(false)
91 {
92     [hostLayer() insertSublayer:m_zoomedOutTileGrid->tileHostLayer() atIndex:0];
93     hostLayerSizeChanged();
94 }
95
96 LegacyTileCache::~LegacyTileCache()
97 {
98     [m_tombstone.get() setDead:true];
99 }
100
101 CGFloat LegacyTileCache::screenScale() const
102 {
103     return [m_window screenScale];
104 }
105
106 CALayer* LegacyTileCache::hostLayer() const
107 {
108     return [m_window hostLayer];
109 }
110
111 FloatRect LegacyTileCache::visibleRectInLayer(CALayer *layer) const
112 {
113     if (m_overrideVisibleRect)
114         return [layer convertRect:m_overrideVisibleRect.value() fromLayer:hostLayer()];
115
116     return [layer convertRect:[m_window extendedVisibleRect] fromLayer:hostLayer()];
117 }
118
119 void LegacyTileCache::setOverrideVisibleRect(Optional<FloatRect> rect)
120 {
121     m_overrideVisibleRect = rect;
122 }
123
124 bool LegacyTileCache::tilesOpaque() const
125 {
126     return m_tilesOpaque;
127 }
128     
129 LegacyTileGrid* LegacyTileCache::activeTileGrid() const
130 {
131     if (!m_keepsZoomedOutTiles)
132         return m_zoomedOutTileGrid.get();
133     if (m_tilingMode == Zooming)
134         return m_zoomedOutTileGrid.get();
135     if (m_zoomedInTileGrid && m_currentScale == m_zoomedInTileGrid->scale())
136         return m_zoomedInTileGrid.get();
137     return m_zoomedOutTileGrid.get();
138 }
139
140 LegacyTileGrid* LegacyTileCache::inactiveTileGrid() const
141 {
142     return activeTileGrid() == m_zoomedOutTileGrid.get() ? m_zoomedInTileGrid.get() : m_zoomedOutTileGrid.get();
143 }
144
145 void LegacyTileCache::setTilesOpaque(bool opaque)
146 {
147     if (m_tilesOpaque == opaque)
148         return;
149
150     DeprecatedMutexLocker locker(m_tileMutex);
151
152     m_tilesOpaque = opaque;
153     m_zoomedOutTileGrid->updateTileOpacity();
154     if (m_zoomedInTileGrid)
155         m_zoomedInTileGrid->updateTileOpacity();
156 }
157
158 void LegacyTileCache::doLayoutTiles()
159 {
160     if (isTileCreationSuspended())
161         return;
162
163     DeprecatedMutexLocker locker(m_tileMutex);
164     LegacyTileGrid* activeGrid = activeTileGrid();
165     // Even though we aren't actually creating tiles in the inactive grid, we
166     // still need to drop invalid tiles in response to a layout.
167     // See <rdar://problem/9839867>.
168     if (LegacyTileGrid* inactiveGrid = inactiveTileGrid())
169         inactiveGrid->dropInvalidTiles();
170     if (activeGrid->checkDoSingleTileLayout())
171         return;
172     createTilesInActiveGrid(CoverVisibleOnly);
173 }
174
175 void LegacyTileCache::hostLayerSizeChanged()
176 {
177     m_zoomedOutTileGrid->updateHostLayerSize();
178     if (m_zoomedInTileGrid)
179         m_zoomedInTileGrid->updateHostLayerSize();
180 }
181
182 void LegacyTileCache::setKeepsZoomedOutTiles(bool keep)
183 {
184     m_keepsZoomedOutTiles = keep;
185 }
186
187 void LegacyTileCache::setCurrentScale(float scale)
188 {
189     ASSERT(scale > 0);
190
191     if (currentScale() == scale) {
192         m_pendingScale = 0;
193         return;
194     }
195     m_pendingScale = scale;
196     if (m_tilingMode == Disabled)
197         return;
198     commitScaleChange();
199
200     if (!keepsZoomedOutTiles() && !isTileInvalidationSuspended()) {
201         // Tile invalidation is normally suspended during zooming by UIKit but some applications
202         // using custom scrollviews may zoom without triggering the callbacks. Invalidate the tiles explicitly.
203         DeprecatedMutexLocker locker(m_tileMutex);
204         activeTileGrid()->dropAllTiles();
205         activeTileGrid()->createTiles(CoverVisibleOnly);
206     }
207 }
208
209 void LegacyTileCache::setZoomedOutScale(float scale)
210 {
211     ASSERT(scale > 0);
212
213     if (zoomedOutScale() == scale) {
214         m_pendingZoomedOutScale = 0;
215         return;
216     }
217     m_pendingZoomedOutScale = scale;
218     if (m_tilingMode == Disabled)
219         return;
220     commitScaleChange();
221 }
222     
223 void LegacyTileCache::commitScaleChange()
224 {
225     ASSERT(m_pendingZoomedOutScale || m_pendingScale);
226     ASSERT(m_tilingMode != Disabled);
227     
228     DeprecatedMutexLocker locker(m_tileMutex);
229
230     if (m_pendingZoomedOutScale) {
231         m_zoomedOutTileGrid->setScale(m_pendingZoomedOutScale);
232         m_pendingZoomedOutScale = 0;
233     }
234     
235     if (!m_keepsZoomedOutTiles) {
236         ASSERT(activeTileGrid() == m_zoomedOutTileGrid.get());
237         if (m_pendingScale) {
238             m_currentScale = m_pendingScale;
239             m_zoomedOutTileGrid->setScale(m_currentScale);
240         }
241         m_pendingScale = 0;
242         return;
243     }
244
245     if (m_pendingScale) {
246         m_currentScale = m_pendingScale;
247         m_pendingScale = 0;
248     }
249
250     if (m_currentScale != m_zoomedOutTileGrid->scale()) {
251         if (!m_zoomedInTileGrid) {
252             m_zoomedInTileGrid = std::make_unique<LegacyTileGrid>(*this, m_tileSize);
253             [hostLayer() addSublayer:m_zoomedInTileGrid->tileHostLayer()];
254             hostLayerSizeChanged();
255         }
256         m_zoomedInTileGrid->setScale(m_currentScale);
257     }
258
259     // Keep the current ordering during zooming.
260     if (m_tilingMode != Zooming)
261         bringActiveTileGridToFront();
262
263     adjustTileGridTransforms();
264     layoutTiles();
265 }
266
267 void LegacyTileCache::bringActiveTileGridToFront()
268 {
269     LegacyTileGrid* activeGrid = activeTileGrid();
270     LegacyTileGrid* otherGrid = inactiveTileGrid();
271     if (!otherGrid)
272         return;
273     CALayer* frontLayer = activeGrid->tileHostLayer();
274     CALayer* otherLayer = otherGrid->tileHostLayer();
275     [hostLayer() insertSublayer:frontLayer above:otherLayer];
276 }
277     
278 void LegacyTileCache::adjustTileGridTransforms()
279 {
280     CALayer* zoomedOutHostLayer = m_zoomedOutTileGrid->tileHostLayer();
281     float transformScale = currentScale() / zoomedOutScale();
282     [zoomedOutHostLayer setTransform:CATransform3DMakeScale(transformScale, transformScale, 1.0f)];
283     m_zoomedOutTileGrid->updateHostLayerSize();
284 }
285
286 void LegacyTileCache::layoutTiles()
287 {
288     if (m_hasPendingLayoutTiles)
289         return;
290     m_hasPendingLayoutTiles = true;
291
292     LegacyTileCacheTombstone *tombstone = m_tombstone.get();
293     WebThreadRun(^{
294         if ([tombstone isDead])
295             return;
296         m_hasPendingLayoutTiles = false;
297         doLayoutTiles();
298     });
299 }
300
301 void LegacyTileCache::layoutTilesNow()
302 {
303     ASSERT(WebThreadIsLockedOrDisabled());
304
305     // layoutTilesNow() is called after a zoom, while the tile mode is still set to Zooming.
306     // If we checked for isTileCreationSuspended here, that would cause <rdar://problem/8434112> (Page flashes after zooming in/out).
307     if (m_tilingMode == Disabled)
308         return;
309     
310     // FIXME: layoutTilesNow should be called after state has been set to non-zooming and the active grid is the final one. 
311     // Fix this in UIKit side (perhaps also getting rid of this call) and remove this code afterwards.
312     // <rdar://problem/9672993>
313     TilingMode savedTilingMode = m_tilingMode;
314     if (m_tilingMode == Zooming)
315         m_tilingMode = Minimal;
316
317     DeprecatedMutexLocker locker(m_tileMutex);
318     LegacyTileGrid* activeGrid = activeTileGrid();
319     if (activeGrid->checkDoSingleTileLayout()) {
320         m_tilingMode = savedTilingMode;
321         return;
322     }
323     createTilesInActiveGrid(CoverVisibleOnly);
324     m_tilingMode = savedTilingMode;
325 }
326
327 void LegacyTileCache::layoutTilesNowForRect(const IntRect& rect)
328 {
329     ASSERT(WebThreadIsLockedOrDisabled());
330     DeprecatedMutexLocker locker(m_tileMutex);
331
332     activeTileGrid()->addTilesCoveringRect(rect);
333 }
334
335 void LegacyTileCache::removeAllNonVisibleTiles()
336 {
337     DeprecatedMutexLocker locker(m_tileMutex);
338     removeAllNonVisibleTilesInternal();
339 }
340
341 void LegacyTileCache::removeAllNonVisibleTilesInternal()
342 {
343     LegacyTileGrid* activeGrid = activeTileGrid();
344     if (keepsZoomedOutTiles() && activeGrid == m_zoomedInTileGrid.get() && activeGrid->hasTiles())
345         m_zoomedOutTileGrid->dropAllTiles();
346
347     IntRect activeTileBounds = activeGrid->bounds();
348     if (activeTileBounds.width() <= m_tileSize.width() && activeTileBounds.height() <= m_tileSize.height()) {
349         // If the view is smaller than a tile, keep the tile even if it is not visible.
350         activeGrid->dropTilesOutsideRect(activeTileBounds);
351         return;
352     }
353
354     activeGrid->dropTilesOutsideRect(activeGrid->visibleRect());
355 }
356
357 void LegacyTileCache::removeAllTiles()
358 {
359     DeprecatedMutexLocker locker(m_tileMutex);
360     m_zoomedOutTileGrid->dropAllTiles();
361     if (m_zoomedInTileGrid)
362         m_zoomedInTileGrid->dropAllTiles();
363 }
364
365 void LegacyTileCache::removeForegroundTiles()
366 {
367     DeprecatedMutexLocker locker(m_tileMutex);
368     if (!keepsZoomedOutTiles())
369         m_zoomedOutTileGrid->dropAllTiles();
370     if (m_zoomedInTileGrid)
371         m_zoomedInTileGrid->dropAllTiles();
372 }
373
374 void LegacyTileCache::setContentReplacementImage(RetainPtr<CGImageRef> contentReplacementImage)
375 {
376     DeprecatedMutexLocker locker(m_contentReplacementImageMutex);
377     m_contentReplacementImage = contentReplacementImage;
378 }
379
380 RetainPtr<CGImageRef> LegacyTileCache::contentReplacementImage() const
381 {
382     DeprecatedMutexLocker locker(m_contentReplacementImageMutex);
383     return m_contentReplacementImage;
384 }
385
386 void LegacyTileCache::setTileBordersVisible(bool flag)
387 {
388     if (flag == m_tileBordersVisible)
389         return;
390
391     m_tileBordersVisible = flag;
392     m_zoomedOutTileGrid->updateTileBorderVisibility();
393     if (m_zoomedInTileGrid)
394         m_zoomedInTileGrid->updateTileBorderVisibility();
395 }
396
397 void LegacyTileCache::setTilePaintCountersVisible(bool flag)
398 {
399     m_tilePaintCountersVisible = flag;
400     // The numbers will show up the next time the tiles paint.
401 }
402
403 void LegacyTileCache::finishedCreatingTiles(bool didCreateTiles, bool createMore)
404 {
405     // We need to ensure that all tiles are showing the same version of the content.
406     if (didCreateTiles && !m_savedDisplayRects.isEmpty())
407         flushSavedDisplayRects();
408
409     if (keepsZoomedOutTiles()) {
410         if (m_zoomedInTileGrid && activeTileGrid() == m_zoomedOutTileGrid.get() && m_tilingMode != Zooming && m_zoomedInTileGrid->hasTiles()) {
411             // This CA transaction will cover the screen with top level tiles.
412             // We can remove zoomed-in tiles without flashing.
413             m_zoomedInTileGrid->dropAllTiles();
414         } else if (activeTileGrid() == m_zoomedInTileGrid.get()) {
415             // Pass the minimum possible distance to consider all tiles, even visible ones.
416             m_zoomedOutTileGrid->dropDistantTiles(0, std::numeric_limits<double>::min());
417         }
418     }
419
420     // Keep creating tiles until the whole coverRect is covered.
421     if (createMore)
422         m_tileCreationTimer.startOneShot(0);
423 }
424
425 void LegacyTileCache::tileCreationTimerFired()
426 {
427     if (isTileCreationSuspended())
428         return;
429     DeprecatedMutexLocker locker(m_tileMutex);
430     createTilesInActiveGrid(CoverSpeculative);
431 }
432
433 void LegacyTileCache::createTilesInActiveGrid(SynchronousTileCreationMode mode)
434 {
435     if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
436         LOG(MemoryPressure, "Under memory pressure at: %s", __PRETTY_FUNCTION__);
437         removeAllNonVisibleTilesInternal();
438     }
439     activeTileGrid()->createTiles(mode);
440 }
441
442 unsigned LegacyTileCache::tileCapacityForGrid(LegacyTileGrid* grid)
443 {
444     static unsigned capacity;
445     if (!capacity) {
446         size_t totalMemory = ramSize() / 1024 / 1024;
447         if (totalMemory >= 1024)
448             capacity = 128 * 1024 * 1024;
449         else if (totalMemory >= 512)
450             capacity = 64 * 1024 * 1024;
451         else
452             capacity = 32 * 1024 * 1024;
453     }
454
455     int gridCapacity;
456
457     int memoryLevel = systemMemoryLevel();
458     if (memoryLevel < 15)
459         gridCapacity = capacity / 4;
460     else if (memoryLevel < 20)
461         gridCapacity = capacity / 2;
462     else if (memoryLevel < 30) 
463         gridCapacity = capacity * 3 / 4;
464     else
465         gridCapacity = capacity;
466
467     if (keepsZoomedOutTiles() && grid == m_zoomedOutTileGrid.get()) {
468         if (activeTileGrid() == m_zoomedOutTileGrid.get())
469             return gridCapacity;
470         return gridCapacity / 4;
471     }
472     return gridCapacity * 3 / 4;
473 }
474
475 Color LegacyTileCache::colorForGridTileBorder(LegacyTileGrid* grid) const
476 {
477     if (grid == m_zoomedOutTileGrid.get())
478         return Color(.3f, .0f, 0.4f, 0.5f);
479
480     return Color(.0f, .0f, 0.4f, 0.5f);
481 }
482
483 static bool shouldRepaintInPieces(const CGRect& dirtyRect, CGSRegionObj dirtyRegion, CGFloat contentsScale)
484 {
485     // Estimate whether or not we should use the unioned rect or the individual rects.
486     // We do this by computing the percentage of "wasted space" in the union. If that wasted space
487     // is too large, then we will do individual rect painting instead.
488     float singlePixels = 0;
489     unsigned rectCount = 0;
490
491     CGSRegionEnumeratorObj enumerator = CGSRegionEnumerator(dirtyRegion);
492     CGRect *subRect;
493     while ((subRect = CGSNextRect(enumerator))) {
494         ++rectCount;
495         singlePixels += subRect->size.width * subRect->size.height;
496     }
497     singlePixels /= (contentsScale * contentsScale);
498     CGSReleaseRegionEnumerator(enumerator);
499
500     const unsigned cRectThreshold = 10;
501     if (rectCount < 2 || rectCount > cRectThreshold)
502         return false;
503
504     const float cWastedSpaceThreshold = 0.50f;
505     float unionPixels = dirtyRect.size.width * dirtyRect.size.height;
506     float wastedSpace = 1.f - (singlePixels / unionPixels);
507     return wastedSpace > cWastedSpaceThreshold;
508 }
509
510 void LegacyTileCache::drawReplacementImage(LegacyTileLayer* layer, CGContextRef context, CGImageRef image)
511 {
512     CGContextSetRGBFillColor(context, 1, 1, 1, 1);
513     CGContextFillRect(context, CGContextGetClipBoundingBox(context));
514
515     CGFloat contentsScale = [layer contentsScale];
516     CGContextScaleCTM(context, 1 / contentsScale, -1 / contentsScale);
517     CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
518     CGContextTranslateCTM(context, 0, -imageRect.size.height);
519     CGContextDrawImage(context, imageRect, image);
520 }
521
522 void LegacyTileCache::drawWindowContent(LegacyTileLayer* layer, CGContextRef context, CGRect dirtyRect)
523 {
524     CGRect frame = [layer frame];
525     FontAntialiasingStateSaver fontAntialiasingState(context, [m_window useOrientationDependentFontAntialiasing] && [layer isOpaque]);
526     fontAntialiasingState.setup([WAKWindow hasLandscapeOrientation]);
527
528     CGSRegionObj drawRegion = (CGSRegionObj)[layer regionBeingDrawn];
529     CGFloat contentsScale = [layer contentsScale];
530     if (drawRegion && shouldRepaintInPieces(dirtyRect, drawRegion, contentsScale)) {
531         // Use fine grained repaint rectangles to minimize the amount of painted pixels.
532         CGSRegionEnumeratorObj enumerator = CGSRegionEnumerator(drawRegion);
533         CGRect *subRect;
534         while ((subRect = CGSNextRect(enumerator))) {
535             CGRect adjustedSubRect = *subRect;
536             adjustedSubRect.origin.x /= contentsScale;
537             adjustedSubRect.origin.y = frame.size.height - (adjustedSubRect.origin.y + adjustedSubRect.size.height) / contentsScale;
538             adjustedSubRect.size.width /= contentsScale;
539             adjustedSubRect.size.height /= contentsScale;
540
541             CGRect subRectInSuper = [hostLayer() convertRect:adjustedSubRect fromLayer:layer];
542             [m_window displayRect:subRectInSuper];
543         }
544         CGSReleaseRegionEnumerator(enumerator);
545     } else {
546         // Simple repaint
547         CGRect dirtyRectInSuper = [hostLayer() convertRect:dirtyRect fromLayer:layer];
548         [m_window displayRect:dirtyRectInSuper];
549     }
550
551     fontAntialiasingState.restore();
552 }
553
554 void LegacyTileCache::drawLayer(LegacyTileLayer* layer, CGContextRef context)
555 {
556     // The web lock unlock observer runs after CA commit observer.
557     if (!WebThreadIsLockedOrDisabled()) {
558         LOG_ERROR("Drawing without holding the web thread lock");
559         ASSERT_NOT_REACHED();
560     }
561
562     WKSetCurrentGraphicsContext(context);
563
564     CGRect dirtyRect = CGContextGetClipBoundingBox(context);
565     CGRect frame = [layer frame];
566     CGContextTranslateCTM(context, -frame.origin.x, -frame.origin.y);
567     CGRect scaledFrame = [hostLayer() convertRect:[layer bounds] fromLayer:layer];
568     CGContextScaleCTM(context, frame.size.width / scaledFrame.size.width, frame.size.height / scaledFrame.size.height);
569
570     if (RetainPtr<CGImage> contentReplacementImage = this->contentReplacementImage())
571         drawReplacementImage(layer, context, contentReplacementImage.get());
572     else
573         drawWindowContent(layer, context, dirtyRect);
574
575     ++layer.paintCount;
576     if (m_tilePaintCountersVisible) {
577         char text[16];
578         snprintf(text, sizeof(text), "%d", layer.paintCount);
579
580         CGContextSaveGState(context);
581
582         CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
583         CGContextSetFillColorWithColor(context, cachedCGColor(colorForGridTileBorder([layer tileGrid]), ColorSpaceDeviceRGB));
584         
585         CGRect labelBounds = [layer bounds];
586         labelBounds.size.width = 10 + 12 * strlen(text);
587         labelBounds.size.height = 25;
588         CGContextFillRect(context, labelBounds);
589
590         if (acceleratedDrawingEnabled())
591             CGContextSetRGBFillColor(context, 1, 0, 0, 0.4f);
592         else
593             CGContextSetRGBFillColor(context, 1, 1, 1, 0.6f);
594 #pragma clang diagnostic push
595 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
596         CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1));
597 #pragma clang diagnostic push
598 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
599         CGContextSelectFont(context, "Helvetica", 25, kCGEncodingMacRoman);
600         CGContextShowTextAtPoint(context, labelBounds.origin.x + 3, labelBounds.origin.y + 20, text, strlen(text));
601 #pragma clang diagnostic pop
602     
603         CGContextRestoreGState(context);        
604     }
605
606     WAKView* view = [m_window contentView];
607     [view performSelector:@selector(_dispatchTileDidDraw:) withObject:layer afterDelay:0.0];
608 }
609
610 void LegacyTileCache::setNeedsDisplay()
611 {
612     setNeedsDisplayInRect(IntRect(0, 0, std::numeric_limits<int>::max(), std::numeric_limits<int>::max()));
613 }
614
615 void LegacyTileCache::scheduleLayerFlushForPendingRepaint()
616 {
617     WAKView* view = [m_window contentView];
618     [view _scheduleLayerFlushForPendingTileCacheRepaint];
619 }
620
621 void LegacyTileCache::setNeedsDisplayInRect(const IntRect& dirtyRect)
622 {
623     DeprecatedMutexLocker locker(m_savedDisplayRectMutex);
624     bool addedFirstRect = m_savedDisplayRects.isEmpty();
625     m_savedDisplayRects.append(dirtyRect);
626     if (!addedFirstRect)
627         return;
628     // Compositing layer flush will call back to doPendingRepaints(). The flush may be throttled and not happen immediately.
629     scheduleLayerFlushForPendingRepaint();
630 }
631
632 void LegacyTileCache::invalidateTiles(const IntRect& dirtyRect)
633 {
634     ASSERT(!m_tileMutex.tryLock());
635
636     LegacyTileGrid* activeGrid = activeTileGrid();
637     if (!keepsZoomedOutTiles()) {
638         activeGrid->invalidateTiles(dirtyRect);
639         return;
640     }
641     FloatRect scaledRect = dirtyRect;
642     scaledRect.scale(zoomedOutScale() / currentScale());
643     IntRect zoomedOutDirtyRect = enclosingIntRect(scaledRect);
644     if (activeGrid == m_zoomedOutTileGrid.get()) {
645         bool dummy;
646         IntRect coverRect = m_zoomedOutTileGrid->calculateCoverRect(m_zoomedOutTileGrid->visibleRect(), dummy);
647         // Instead of repainting a tile outside the cover rect, just remove it.
648         m_zoomedOutTileGrid->dropTilesBetweenRects(zoomedOutDirtyRect, coverRect);
649         m_zoomedOutTileGrid->invalidateTiles(zoomedOutDirtyRect);
650         // We need to invalidate zoomed in tiles as well while zooming, since
651         // we could switch back to the zoomed in grid without dropping its
652         // tiles.  See <rdar://problem/9946759>.
653         if (m_tilingMode == Zooming && m_zoomedInTileGrid)
654             m_zoomedInTileGrid->invalidateTiles(dirtyRect);
655         return;
656     }
657     if (!m_zoomedInTileGrid->hasTiles()) {
658         // If no tiles have been created yet for the zoomed in grid, we can't drop the zoomed out tiles.
659         m_zoomedOutTileGrid->invalidateTiles(zoomedOutDirtyRect);
660         return;
661     }
662     m_zoomedOutTileGrid->dropTilesIntersectingRect(zoomedOutDirtyRect);
663     m_zoomedInTileGrid->invalidateTiles(dirtyRect);
664 }
665     
666 bool LegacyTileCache::isTileCreationSuspended() const 
667 {
668     return (!keepsZoomedOutTiles() && m_tilingMode == Zooming) || m_tilingMode == Disabled;
669 }
670
671 bool LegacyTileCache::isTileInvalidationSuspended() const 
672
673     return m_tilingMode == Zooming || m_tilingMode == Panning || m_tilingMode == ScrollToTop || m_tilingMode == Disabled; 
674 }
675
676 void LegacyTileCache::updateTilingMode()
677 {
678     ASSERT(WebThreadIsCurrent() || !WebThreadIsEnabled());
679
680     WAKView* view = [m_window contentView];
681
682     if (m_tilingMode == Zooming || m_tilingMode == Panning || m_tilingMode == ScrollToTop) {
683         if (!m_didCallWillStartScrollingOrZooming) {
684             [view _willStartScrollingOrZooming];
685             m_didCallWillStartScrollingOrZooming = true;
686         }
687     } else {
688         if (m_didCallWillStartScrollingOrZooming) {
689             [view _didFinishScrollingOrZooming];
690             m_didCallWillStartScrollingOrZooming = false;
691         }
692         if (m_tilingMode == Disabled)
693             return;
694
695         DeprecatedMutexLocker locker(m_tileMutex);
696         createTilesInActiveGrid(CoverVisibleOnly);
697
698         if (!m_savedDisplayRects.isEmpty())
699             scheduleLayerFlushForPendingRepaint();
700     }
701 }
702
703 void LegacyTileCache::setTilingMode(TilingMode tilingMode)
704 {
705     if (tilingMode == m_tilingMode)
706         return;
707     bool wasZooming = (m_tilingMode == Zooming);
708     m_tilingMode = tilingMode;
709
710     if ((m_pendingZoomedOutScale || m_pendingScale) && m_tilingMode != Disabled)
711         commitScaleChange();
712     else if (wasZooming) {
713         DeprecatedMutexLocker locker(m_tileMutex);
714         bringActiveTileGridToFront();
715     }
716
717     if (m_hasPendingUpdateTilingMode)
718         return;
719     m_hasPendingUpdateTilingMode = true;
720
721     LegacyTileCacheTombstone *tombstone = m_tombstone.get();
722     WebThreadRun(^{
723         if ([tombstone isDead])
724             return;
725         m_hasPendingUpdateTilingMode = false;
726         updateTilingMode();
727     });
728 }
729
730 void LegacyTileCache::setTilingDirection(TilingDirection tilingDirection)
731 {
732     m_tilingDirection = tilingDirection;
733 }
734
735 LegacyTileCache::TilingDirection LegacyTileCache::tilingDirection() const
736 {
737     return m_tilingDirection;
738 }
739     
740 float LegacyTileCache::zoomedOutScale() const
741 {
742     return m_zoomedOutTileGrid->scale();
743 }
744
745 float LegacyTileCache::currentScale() const
746 {
747     return m_currentScale;
748 }
749
750 void LegacyTileCache::doPendingRepaints()
751 {
752     if (m_savedDisplayRects.isEmpty())
753         return;
754     if (isTileInvalidationSuspended())
755         return;
756     DeprecatedMutexLocker locker(m_tileMutex);
757     flushSavedDisplayRects();
758 }
759
760 void LegacyTileCache::flushSavedDisplayRects()
761 {
762     ASSERT(!m_tileMutex.tryLock());
763     ASSERT(!m_savedDisplayRects.isEmpty());
764
765     Vector<IntRect> rects;
766     {
767         DeprecatedMutexLocker locker(m_savedDisplayRectMutex);
768         m_savedDisplayRects.swap(rects);
769     }
770     size_t size = rects.size();
771     for (size_t n = 0; n < size; ++n)
772         invalidateTiles(rects[n]);
773 }
774
775 void LegacyTileCache::setSpeculativeTileCreationEnabled(bool enabled)
776 {
777     if (m_isSpeculativeTileCreationEnabled == enabled)
778         return;
779     m_isSpeculativeTileCreationEnabled = enabled;
780     if (m_isSpeculativeTileCreationEnabled)
781         m_tileCreationTimer.startOneShot(0);
782 }
783
784 bool LegacyTileCache::hasPendingDraw() const
785 {
786     return !m_savedDisplayRects.isEmpty();
787 }
788
789 void LegacyTileCache::prepareToDraw()
790 {
791     // This will trigger document relayout if needed.
792     [[m_window contentView] viewWillDraw];
793
794     if (!m_savedDisplayRects.isEmpty()) {
795         DeprecatedMutexLocker locker(m_tileMutex);
796         flushSavedDisplayRects();
797     }
798 }
799
800 void LegacyTileCache::setLayerPoolCapacity(unsigned capacity)
801 {
802     LegacyTileLayerPool::sharedPool()->setCapacity(capacity);
803 }
804
805 void LegacyTileCache::drainLayerPool()
806 {
807     LegacyTileLayerPool::sharedPool()->drain();
808 }
809
810 void LegacyTileCache::dumpTiles()
811 {
812     NSLog(@"=================");
813     NSLog(@"ZOOMED OUT");
814     if (m_zoomedOutTileGrid.get() == activeTileGrid())
815         NSLog(@"<ACTIVE>");
816     m_zoomedOutTileGrid->dumpTiles();
817     NSLog(@"=================");
818     if (m_zoomedInTileGrid) {
819         NSLog(@"ZOOMED IN");
820         if (m_zoomedInTileGrid.get() == activeTileGrid())
821             NSLog(@"<ACTIVE>");
822         m_zoomedInTileGrid->dumpTiles();
823         NSLog(@"=================");
824     }
825 }
826
827 } // namespace WebCore
828
829 #endif // PLATFORM(IOS)