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