Split tile grid out from TileController
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Apr 2014 18:16:13 +0000 (18:16 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Apr 2014 18:16:13 +0000 (18:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=131102

Reviewed by Simon Fraser.

Source/WebCore:

Add a TileGrid class that encapsulates tiles, zoom level and the related metadata.
This will make it possible to have multiple grids per TileController later.

Source/WebKit2:

* WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.cpp:
(WebKit::PlatformCALayerRemoteTiledBacking::PlatformCALayerRemoteTiledBacking):
* WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@166654 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/graphics/ca/mac/PlatformCALayerMac.h
Source/WebCore/platform/graphics/ca/mac/PlatformCALayerMac.mm
Source/WebCore/platform/graphics/ca/mac/TileController.h
Source/WebCore/platform/graphics/ca/mac/TileController.mm
Source/WebCore/platform/graphics/ca/mac/TileGrid.h [new file with mode: 0644]
Source/WebCore/platform/graphics/ca/mac/TileGrid.mm [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.cpp
Source/WebKit2/WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.h

index 08cf78a..ed1a910 100644 (file)
@@ -1,3 +1,13 @@
+2014-04-02  Antti Koivisto  <antti@apple.com>
+
+        Split tile grid out from TileController
+        https://bugs.webkit.org/show_bug.cgi?id=131102
+
+        Reviewed by Simon Fraser.
+
+        Add a TileGrid class that encapsulates tiles, zoom level and the related metadata.
+        This will make it possible to have multiple grids per TileController later.
+
 2014-03-31  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Correct media controls for test harness
index 01bef02..8317fdf 100644 (file)
@@ -430,6 +430,7 @@ __ZN7WebCore14StorageTracker7originsERN3WTF6VectorINS1_6RefPtrINS_14SecurityOrig
 __ZN7WebCore14StorageTracker7trackerEv
 __ZN7WebCore14SubframeLoader12allowPluginsENS_28ReasonForCallingAllowPluginsE
 __ZN7WebCore14TileController14setTilesOpaqueEb
+__ZN7WebCore14TileController15containerLayersEv
 __ZN7WebCore14TileController15setNeedsDisplayEv
 __ZN7WebCore14TileController21setAcceleratesDrawingEb
 __ZN7WebCore14TileController21setNeedsDisplayInRectERKNS_7IntRectE
index 30bf395..686338f 100644 (file)
                E4BBED4D14FCDBA1003F0B98 /* StyleRule.h in Headers */ = {isa = PBXBuildFile; fileRef = E4BBED4B14FCDBA1003F0B98 /* StyleRule.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E4C1789A0EE6903800824D69 /* CSSSelectorList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C178950EE6903800824D69 /* CSSSelectorList.cpp */; };
                E4C1789B0EE6903800824D69 /* CSSSelectorList.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C178960EE6903800824D69 /* CSSSelectorList.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E4C274CA18E988EA00602C76 /* TileGrid.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4C274C918E988EA00602C76 /* TileGrid.mm */; };
+               E4C274CC18E988F500602C76 /* TileGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C274CB18E988F500602C76 /* TileGrid.h */; };
                E4C279580CF9741900E97B98 /* RenderMedia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C279560CF9741900E97B98 /* RenderMedia.cpp */; };
                E4C279590CF9741900E97B98 /* RenderMedia.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C279570CF9741900E97B98 /* RenderMedia.h */; };
                E4C3B1FA0F0E4161009693F6 /* LegacyTileCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C3B1F90F0E4161009693F6 /* LegacyTileCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E4BBED4B14FCDBA1003F0B98 /* StyleRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleRule.h; sourceTree = "<group>"; };
                E4C178950EE6903800824D69 /* CSSSelectorList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSSelectorList.cpp; sourceTree = "<group>"; };
                E4C178960EE6903800824D69 /* CSSSelectorList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorList.h; sourceTree = "<group>"; };
+               E4C274C918E988EA00602C76 /* TileGrid.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TileGrid.mm; path = ca/mac/TileGrid.mm; sourceTree = "<group>"; };
+               E4C274CB18E988F500602C76 /* TileGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TileGrid.h; path = ca/mac/TileGrid.h; sourceTree = "<group>"; };
                E4C279560CF9741900E97B98 /* RenderMedia.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMedia.cpp; sourceTree = "<group>"; };
                E4C279570CF9741900E97B98 /* RenderMedia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMedia.h; sourceTree = "<group>"; };
                E4C3B1F90F0E4161009693F6 /* LegacyTileCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LegacyTileCache.h; path = ios/LegacyTileCache.h; sourceTree = "<group>"; };
                                4958782012A57DDF007238AC /* PlatformCALayerMac.mm */,
                                1AA71609149BC4DB0016EC19 /* TileController.h */,
                                1AA71608149BC4DA0016EC19 /* TileController.mm */,
+                               E4C274CB18E988F500602C76 /* TileGrid.h */,
+                               E4C274C918E988EA00602C76 /* TileGrid.mm */,
                                0F580FA11496939100FB5BD8 /* WebTiledBackingLayer.h */,
                                0F580FA21496939100FB5BD8 /* WebTiledBackingLayer.mm */,
                        );
                                C585A68D11D4FB08004C3E4B /* JSIDBKeyRange.h in Headers */,
                                C585A68F11D4FB08004C3E4B /* JSIDBObjectStore.h in Headers */,
                                C585A69111D4FB08004C3E4B /* JSIDBRequest.h in Headers */,
+                               E4C274CC18E988F500602C76 /* TileGrid.h in Headers */,
                                B6566270120B1227006EA85C /* JSIDBTransaction.h in Headers */,
                                269239961505E1AA009E57FC /* JSIDBVersionChangeEvent.h in Headers */,
                                BC6C49F40D7DBA0500FFA558 /* JSImageConstructor.h in Headers */,
                                516BB7940CE91E6800512F79 /* JSTreeWalkerCustom.cpp in Sources */,
                                A86629D009DA2B48009633A5 /* JSUIEvent.cpp in Sources */,
                                AAA4FAD3175D5CB300743873 /* JSUIRequestEvent.cpp in Sources */,
+                               E4C274CA18E988EA00602C76 /* TileGrid.mm in Sources */,
                                15C77094100D3CA8005BA267 /* JSValidityState.cpp in Sources */,
                                CDE83DB6183D352A0031EAA3 /* JSVideoPlaybackQuality.cpp in Sources */,
                                07C59B5F17F4B208000FBCBB /* JSVideoStreamTrack.cpp in Sources */,
index 29c14f4..fdd8fe1 100644 (file)
@@ -158,7 +158,7 @@ private:
     bool requiresCustomAppearanceUpdateOnBoundsChange() const;
 
     RetainPtr<NSObject> m_delegate;
-    OwnPtr<PlatformCALayerList> m_customSublayers;
+    std::unique_ptr<PlatformCALayerList> m_customSublayers;
     GraphicsLayer::CustomAppearance m_customAppearance;
     GraphicsLayer::CustomBehavior m_customBehavior;
 };
index cf8e30a..ee87848 100644 (file)
@@ -230,9 +230,7 @@ void PlatformCALayerMac::commonInit()
         WebTiledBackingLayer* tiledBackingLayer = static_cast<WebTiledBackingLayer*>(m_layer.get());
         TileController* tileController = [tiledBackingLayer createTileController:this];
 
-        m_customSublayers = adoptPtr(new PlatformCALayerList(1));
-        PlatformCALayer* tileCacheTileContainerLayer = tileController->tileContainerLayer();
-        (*m_customSublayers)[0] = tileCacheTileContainerLayer;
+        m_customSublayers = std::make_unique<PlatformCALayerList>(tileController->containerLayers());
     }
 
     END_BLOCK_OBJC_EXCEPTIONS
index 418f386..f9d3184 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +27,6 @@
 #define TileController_h
 
 #include "FloatRect.h"
-#include "IntPointHash.h"
 #include "IntRect.h"
 #include "PlatformCALayer.h"
 #include "PlatformCALayerClient.h"
@@ -44,10 +43,11 @@ namespace WebCore {
 class FloatRect;
 class IntPoint;
 class IntRect;
+class TileGrid;
 
 typedef Vector<RetainPtr<PlatformLayer>> PlatformLayerList;
 
-class TileController : public TiledBacking, public PlatformCALayerClient {
+class TileController final : public TiledBacking, public PlatformCALayerClient {
     WTF_MAKE_NONCOPYABLE(TileController);
 
 public:
@@ -60,7 +60,7 @@ public:
     void setNeedsDisplayInRect(const IntRect&);
 
     void setScale(float);
-    float scale() const { return m_scale; }
+    float scale() const;
 
     bool acceleratesDrawing() const { return m_acceleratesDrawing; }
     void setAcceleratesDrawing(bool);
@@ -68,7 +68,7 @@ public:
     void setTilesOpaque(bool);
     bool tilesAreOpaque() const { return m_tilesAreOpaque; }
 
-    PlatformCALayer *tileContainerLayer() const { return m_tileContainerLayer.get(); }
+    PlatformCALayer& rootLayer() { return *m_tileCacheLayer; }
 
     void setTileDebugBorderWidth(float);
     void setTileDebugBorderColor(Color);
@@ -79,31 +79,57 @@ public:
     static unsigned blankPixelCountForTiles(const PlatformLayerList&, const FloatRect&, const IntPoint&);
 
 #if PLATFORM(IOS)
-    unsigned numberOfUnparentedTiles() const { return m_cohortList.size(); }
+    unsigned numberOfUnparentedTiles() const;
     void removeUnparentedTilesNow();
 #endif
 
 public:
-    // Only public for inline methods in the implementation file.
-    typedef IntPoint TileIndex;
-    typedef unsigned TileCohort;
-    static const TileCohort VisibleTileCohort = UINT_MAX;
+    // Public for TileGrid
+    bool isInWindow() const { return m_isInWindow; }
+
+    float deviceScaleFactor() const { return m_deviceScaleFactor; }
+    FloatRect exposedRect() const { return m_exposedRect; }
+
+    Color tileDebugBorderColor() const { return m_tileDebugBorderColor; }
+    float tileDebugBorderWidth() const { return m_tileDebugBorderWidth; }
+
+    virtual IntSize tileSize() const override { return m_tileSize; }
+    virtual IntRect bounds() const override;
+    virtual bool hasMargins() const override;
+    virtual int topMarginHeight() const override;
+    virtual int bottomMarginHeight() const override;
+    virtual int leftMarginWidth() const override;
+    virtual int rightMarginWidth() const override;
+    virtual TileCoverage tileCoverage() const override { return m_tileCoverage; }
+    virtual bool unparentsOffscreenTiles() const override { return m_unparentsOffscreenTiles; }
+
+    IntRect boundsWithoutMargin() const;
+
+    FloatRect computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect) const;
+
+    IntRect boundsAtLastRevalidate() const { return m_boundsAtLastRevalidate; }
+    IntRect boundsAtLastRevalidateWithoutMargin() const;
+    FloatRect visibleRectAtLastRevalidate() const { return m_visibleRectAtLastRevalidate; }
+    void didRevalidateTiles();
+
+    bool shouldAggressivelyRetainTiles() const;
+    bool shouldTemporarilyRetainTileCohorts() const;
+
     typedef HashMap<PlatformCALayer*, int> RepaintCountMap;
+    RepaintCountMap& repaintCountMap() { return m_tileRepaintCounts; }
+
+    void updateTileCoverageMap();
 
-    struct TileInfo {
-        RefPtr<PlatformCALayer> layer;
-        TileCohort cohort; // VisibleTileCohort is visible.
-        bool hasStaleContent;
-        
-        TileInfo()
-            : cohort(VisibleTileCohort)
-            , hasStaleContent(false)
-        { }
-    };
+    RefPtr<PlatformCALayer> createTileLayer(const IntRect&);
+
+    Vector<RefPtr<PlatformCALayer>> containerLayers();
 
 private:
     TileController(PlatformCALayer*);
 
+    TileGrid& tileGrid() { return *m_tileGrid; }
+    const TileGrid& tileGrid() const { return *m_tileGrid; }
+
     // TiledBacking member functions.
     virtual void setVisibleRect(const FloatRect&) override;
     virtual bool tilesWouldChangeForVisibleRect(const FloatRect&) const override;
@@ -111,25 +137,17 @@ private:
     virtual void prepopulateRect(const FloatRect&) override;
     virtual void setIsInWindow(bool) override;
     virtual void setTileCoverage(TileCoverage) override;
-    virtual TileCoverage tileCoverage() const override { return m_tileCoverage; }
     virtual void revalidateTiles() override;
     virtual void forceRepaint() override;
-    virtual IntSize tileSize() const override { return m_tileSize; }
     virtual IntRect tileGridExtent() const override;
     virtual void setScrollingPerformanceLoggingEnabled(bool flag) override { m_scrollingPerformanceLoggingEnabled = flag; }
     virtual bool scrollingPerformanceLoggingEnabled() const override { return m_scrollingPerformanceLoggingEnabled; }
     virtual void setUnparentsOffscreenTiles(bool flag) override { m_unparentsOffscreenTiles = flag; }
-    virtual bool unparentsOffscreenTiles() const override { return m_unparentsOffscreenTiles; }
     virtual double retainedTileBackingStoreMemory() const override;
     virtual IntRect tileCoverageRect() const override;
     virtual PlatformCALayer* tiledScrollingIndicatorLayer() override;
     virtual void setScrollingModeIndication(ScrollingModeIndication) override;
     virtual void setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight) override;
-    virtual bool hasMargins() const override;
-    virtual int topMarginHeight() const override;
-    virtual int bottomMarginHeight() const override;
-    virtual int leftMarginWidth() const override;
-    virtual int rightMarginWidth() const override;
 
     // PlatformCALayerClient
     virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*) override { }
@@ -148,86 +166,31 @@ private:
     virtual void platformCALayerSetNeedsToRevalidateTiles() override { }
     virtual float platformCALayerDeviceScaleFactor() const override;
 
-    virtual IntRect bounds() const override;
-    IntRect boundsWithoutMargin() const;
-    IntRect boundsAtLastRevalidateWithoutMargin() const;
-
-    IntRect rectForTileIndex(const TileIndex&) const;
-    void adjustRectAtTileIndexForMargin(const TileIndex&, IntRect&) const;
-    void getTileIndexRangeForRect(const IntRect&, TileIndex& topLeft, TileIndex& bottomRight) const;
-
-    FloatRect computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect) const;
-
     void scheduleTileRevalidation(double interval);
     void tileRevalidationTimerFired(Timer<TileController>*);
 
-    void scheduleCohortRemoval();
-    void cohortRemovalTimerFired(Timer<TileController>*);
-    
-    typedef unsigned TileValidationPolicyFlags;
-
     void setNeedsRevalidateTiles();
-    void revalidateTiles(TileValidationPolicyFlags foregroundValidationPolicy, TileValidationPolicyFlags backgroundValidationPolicy);
-    enum class CoverageType { PrimaryTiles, SecondaryTiles };
-
-    // Returns the bounds of the covered tiles.
-    IntRect ensureTilesForRect(const FloatRect&, CoverageType);
-    void updateTileCoverageMap();
-
-    void removeAllTiles();
-    void removeAllSecondaryTiles();
-    void removeTilesInCohort(TileCohort);
-
-    TileCohort nextTileCohort() const;
-    void startedNewCohort(TileCohort);
-    
-    TileCohort newestTileCohort() const;
-    TileCohort oldestTileCohort() const;
-
-    void setTileNeedsDisplayInRect(const TileIndex&, TileInfo&, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords);
-
-    RefPtr<PlatformCALayer> createTileLayer(const IntRect&);
 
     void drawTileMapContents(CGContextRef, CGRect);
 
     PlatformCALayerClient* owningGraphicsLayer() const { return m_tileCacheLayer->owner(); }
 
-    FloatRect scaledExposedRect() const;
-
     PlatformCALayer* m_tileCacheLayer;
-    RefPtr<PlatformCALayer> m_tileContainerLayer;
     RefPtr<PlatformCALayer> m_tiledScrollingIndicatorLayer; // Used for coverage visualization.
     RefPtr<PlatformCALayer> m_visibleRectIndicatorLayer;
 
+    std::unique_ptr<TileGrid> m_tileGrid;
+
     IntSize m_tileSize;
     FloatRect m_visibleRect;
     FloatRect m_visibleRectAtLastRevalidate;
     FloatRect m_exposedRect; // The exposed area of containing platform views.
     IntRect m_boundsAtLastRevalidate;
-    
-    Vector<FloatRect> m_secondaryTileCoverageRects;
 
-    typedef HashMap<TileIndex, TileInfo> TileMap;
-    TileMap m_tiles;
     Timer<TileController> m_tileRevalidationTimer;
-    Timer<TileController> m_cohortRemovalTimer;
 
     RepaintCountMap m_tileRepaintCounts;
 
-    struct TileCohortInfo {
-        TileCohort cohort;
-        double creationTime; // in monotonicallyIncreasingTime().
-        TileCohortInfo(TileCohort inCohort, double inTime)
-            : cohort(inCohort)
-            , creationTime(inTime)
-        { }
-    };
-    typedef Deque<TileCohortInfo> TileCohortList;
-    TileCohortList m_cohortList;
-    
-    IntRect m_primaryTileCoverageRect; // In tile coords.
-
-    float m_scale;
     float m_deviceScaleFactor;
 
     TileCoverage m_tileCoverage;
index 1a9f555..e9fb37a 100644 (file)
@@ -30,6 +30,7 @@
 #import "IntRect.h"
 #import "PlatformCALayer.h"
 #import "Region.h"
+#import "TileGrid.h"
 #if !PLATFORM(IOS)
 #import "LayerPool.h"
 #endif
@@ -55,11 +56,10 @@ PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformL
 
 TileController::TileController(PlatformCALayer* rootPlatformLayer)
     : m_tileCacheLayer(rootPlatformLayer)
+    , m_tileGrid(std::make_unique<TileGrid>(*this))
     , m_tileSize(defaultTileWidth, defaultTileHeight)
     , m_exposedRect(FloatRect::infiniteRect())
     , m_tileRevalidationTimer(this, &TileController::tileRevalidationTimerFired)
-    , m_cohortRemovalTimer(this, &TileController::cohortRemovalTimerFired)
-    , m_scale(1)
     , m_deviceScaleFactor(1)
     , m_tileCoverage(CoverageForVisibleArea)
     , m_marginTop(0)
@@ -75,10 +75,6 @@ TileController::TileController(PlatformCALayer* rootPlatformLayer)
     , m_tileDebugBorderWidth(0)
     , m_indicatorMode(AsyncScrollingIndication)
 {
-    m_tileContainerLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr);
-#ifndef NDEBUG
-    m_tileContainerLayer->setName("TileController Container Layer");
-#endif
 }
 
 TileController::~TileController()
@@ -89,9 +85,6 @@ TileController::~TileController()
     tileControllerMemoryHandler().removeTileController(this);
 #endif
 
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-        it->value.layer->setOwner(nullptr);
-
     if (m_tiledScrollingIndicatorLayer)
         m_tiledScrollingIndicatorLayer->setOwner(nullptr);
 }
@@ -104,71 +97,12 @@ void TileController::tileCacheLayerBoundsChanged()
 
 void TileController::setNeedsDisplay()
 {
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        TileInfo& tileInfo = it->value;
-        IntRect tileRect = rectForTileIndex(it->key);
-
-        if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer())
-            tileInfo.layer->setNeedsDisplay();
-        else
-            tileInfo.hasStaleContent = true;
-    }
+    tileGrid().setNeedsDisplay();
 }
 
 void TileController::setNeedsDisplayInRect(const IntRect& rect)
 {
-    if (m_tiles.isEmpty())
-        return;
-
-    FloatRect scaledRect(rect);
-    scaledRect.scale(m_scale);
-    IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect));
-
-    // For small invalidations, lookup the covered tiles.
-    if (repaintRectInTileCoords.height() < 2 * m_tileSize.height() && repaintRectInTileCoords.width() < 2 * m_tileSize.width()) {
-        TileIndex topLeft;
-        TileIndex bottomRight;
-        getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight);
-
-        for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
-            for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
-                TileIndex tileIndex(x, y);
-                
-                TileMap::iterator it = m_tiles.find(tileIndex);
-                if (it != m_tiles.end())
-                    setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
-            }
-        }
-        return;
-    }
-
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-        setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
-}
-
-void TileController::setTileNeedsDisplayInRect(const TileIndex& tileIndex, TileInfo& tileInfo, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords)
-{
-    PlatformCALayer* tileLayer = tileInfo.layer.get();
-
-    IntRect tileRect = rectForTileIndex(tileIndex);
-    FloatRect tileRepaintRect = tileRect;
-    tileRepaintRect.intersect(repaintRectInTileCoords);
-    if (tileRepaintRect.isEmpty())
-        return;
-
-    tileRepaintRect.moveBy(-tileRect.location());
-    
-    // We could test for intersection with the visible rect. This would reduce painting yet more,
-    // but may make scrolling stale tiles into view more frequent.
-    if (tileRect.intersects(coverageRectInTileCoords) && tileLayer->superlayer()) {
-        tileLayer->setNeedsDisplay(&tileRepaintRect);
-
-        if (owningGraphicsLayer()->platformCALayerShowRepaintCounter(0)) {
-            FloatRect indicatorRect(0, 0, 52, 27);
-            tileLayer->setNeedsDisplay(&indicatorRect);
-        }
-    } else
-        tileInfo.hasStaleContent = true;
+    tileGrid().setNeedsDisplayInRect(rect);
 }
 
 void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&)
@@ -179,7 +113,7 @@ void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALay
 #endif
 
     if (platformCALayer == m_tiledScrollingIndicatorLayer.get()) {
-        drawTileMapContents(context.platformContext(), m_tiledScrollingIndicatorLayer->bounds());
+        tileGrid().drawTileMapContents(context.platformContext(), m_tiledScrollingIndicatorLayer->bounds());
         return;
     }
 
@@ -188,7 +122,7 @@ void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALay
 
         FloatPoint3D layerOrigin = platformCALayer->position();
         context.translate(-layerOrigin.x(), -layerOrigin.y());
-        context.scale(FloatSize(m_scale, m_scale));
+        context.scale(FloatSize(tileGrid().scale(), tileGrid().scale()));
 
         RepaintRectList dirtyRects = collectRectsToPaint(context.platformContext(), platformCALayer);
         drawLayerContents(context.platformContext(), m_tileCacheLayer, dirtyRects);
@@ -222,58 +156,46 @@ bool TileController::platformCALayerShowRepaintCounter(PlatformCALayer*) const
     return owningGraphicsLayer()->platformCALayerShowRepaintCounter(0);
 }
 
+float TileController::scale() const
+{
+    return tileGrid().scale();
+}
+
 void TileController::setScale(float scale)
 {
     ASSERT(owningGraphicsLayer()->isCommittingChanges());
 
-    float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
+    float deviceScaleFactor = platformCALayerDeviceScaleFactor();
 
     // The scale we get is the product of the page scale factor and device scale factor.
     // Divide by the device scale factor so we'll get the page scale factor.
     scale /= deviceScaleFactor;
 
-    if (m_scale == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
+    if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
         return;
 
     m_hasTilesWithTemporaryScaleFactor = false;
     m_deviceScaleFactor = deviceScaleFactor;
-    m_scale = scale;
 
-    TransformationMatrix transform;
-    transform.scale(1 / m_scale);
-    m_tileContainerLayer->setTransform(transform);
-
-    // FIXME: we may revalidateTiles twice in this commit.
-    revalidateTiles(PruneSecondaryTiles, PruneSecondaryTiles);
-
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-        it->value.layer->setContentsScale(deviceScaleFactor);
+    tileGrid().setScale(scale);
 }
 
 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
 {
     if (m_acceleratesDrawing == acceleratesDrawing)
         return;
-
     m_acceleratesDrawing = acceleratesDrawing;
 
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        tileInfo.layer->setAcceleratesDrawing(m_acceleratesDrawing);
-    }
+    tileGrid().updateTilerLayerProperties();
 }
 
 void TileController::setTilesOpaque(bool opaque)
 {
     if (opaque == m_tilesAreOpaque)
         return;
-
     m_tilesAreOpaque = opaque;
 
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        tileInfo.layer->setOpaque(opaque);
-    }
+    tileGrid().updateTilerLayerProperties();
 }
 
 void TileController::setVisibleRect(const FloatRect& visibleRect)
@@ -288,24 +210,9 @@ void TileController::setVisibleRect(const FloatRect& visibleRect)
 
 bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const
 {
-    FloatRect visibleRect = newVisibleRect;
-    visibleRect.intersect(scaledExposedRect());
-
-    if (visibleRect.isEmpty() || bounds().isEmpty())
+    if (bounds().isEmpty())
         return false;
-        
-    FloatRect currentTileCoverageRect = computeTileCoverageRect(m_visibleRect, newVisibleRect);
-    FloatRect scaledRect(currentTileCoverageRect);
-    scaledRect.scale(m_scale);
-    IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect));
-
-    TileIndex topLeft;
-    TileIndex bottomRight;
-    getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight);
-
-    IntRect coverageRect = rectForTileIndex(topLeft);
-    coverageRect.unite(rectForTileIndex(bottomRight));
-    return coverageRect != m_primaryTileCoverageRect;
+    return tileGrid().tilesWouldChangeForVisibleRect(newVisibleRect, m_visibleRect);
 }
 
 void TileController::setExposedRect(const FloatRect& exposedRect)
@@ -317,24 +224,10 @@ void TileController::setExposedRect(const FloatRect& exposedRect)
     setNeedsRevalidateTiles();
 }
 
-FloatRect TileController::scaledExposedRect() const
-{
-    FloatRect scaledExposedRect = m_exposedRect;
-    scaledExposedRect.scale(1 / m_scale);
-    return scaledExposedRect;
-}
-
 void TileController::prepopulateRect(const FloatRect& rect)
 {
-    FloatRect scaledRect(rect);
-    scaledRect.scale(m_scale);
-    IntRect rectInTileCoords(enclosingIntRect(scaledRect));
-
-    if (m_primaryTileCoverageRect.contains(rectInTileCoords))
-        return;
-    
-    m_secondaryTileCoverageRects.append(rect);
-    setNeedsRevalidateTiles();
+    if (tileGrid().prepopulateRect(rect))
+        setNeedsRevalidateTiles();
 }
 
 void TileController::setIsInWindow(bool isInWindow)
@@ -364,7 +257,8 @@ void TileController::setTileCoverage(TileCoverage coverage)
 void TileController::revalidateTiles()
 {
     ASSERT(owningGraphicsLayer()->isCommittingChanges());
-    revalidateTiles(0, 0);
+    tileGrid().revalidateTiles(0);
+    m_visibleRectAtLastRevalidate = m_visibleRect;
 }
 
 void TileController::forceRepaint()
@@ -376,22 +270,18 @@ void TileController::setTileDebugBorderWidth(float borderWidth)
 {
     if (m_tileDebugBorderWidth == borderWidth)
         return;
-
     m_tileDebugBorderWidth = borderWidth;
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        tileInfo.layer->setBorderWidth(m_tileDebugBorderWidth);
-    }
+
+    tileGrid().updateTilerLayerProperties();
 }
 
 void TileController::setTileDebugBorderColor(Color borderColor)
 {
     if (m_tileDebugBorderColor == borderColor)
         return;
-
     m_tileDebugBorderColor = borderColor;
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-        it->value.layer->setBorderColor(borderColor);
+
+    tileGrid().updateTilerLayerProperties();
 }
 
 IntRect TileController::bounds() const
@@ -415,80 +305,8 @@ IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
     return boundsWithoutMargin;
 }
 
-void TileController::adjustRectAtTileIndexForMargin(const TileIndex& tileIndex, IntRect& rect) const
-{
-    if (!hasMargins())
-        return;
-
-    // This is a tile in the top margin.
-    if (m_marginTop && tileIndex.y() < 0) {
-        rect.setY(tileIndex.y() * topMarginHeight());
-        rect.setHeight(topMarginHeight());
-    }
-
-    // This is a tile in the left margin.
-    if (m_marginLeft && tileIndex.x() < 0) {
-        rect.setX(tileIndex.x() * leftMarginWidth());
-        rect.setWidth(leftMarginWidth());
-    }
-
-    TileIndex contentTopLeft;
-    TileIndex contentBottomRight;
-    getTileIndexRangeForRect(boundsWithoutMargin(), contentTopLeft, contentBottomRight);
-
-    // This is a tile in the bottom margin.
-    if (m_marginBottom && tileIndex.y() > contentBottomRight.y())
-        rect.setHeight(bottomMarginHeight());
-
-    // This is a tile in the right margin.
-    if (m_marginRight && tileIndex.x() > contentBottomRight.x())
-        rect.setWidth(rightMarginWidth());
-}
-
-IntRect TileController::rectForTileIndex(const TileIndex& tileIndex) const
+FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& visibleRect) const
 {
-    IntRect rect(tileIndex.x() * m_tileSize.width(), tileIndex.y() * m_tileSize.height(), m_tileSize.width(), m_tileSize.height());
-    IntRect scaledBounds(bounds());
-    scaledBounds.scale(m_scale);
-
-    rect.intersect(scaledBounds);
-
-    // These rect computations assume m_tileSize is the correct size to use. However, a tile in the margin area
-    // might be a different size depending on the size of the margins. So adjustRectAtTileIndexForMargin() will
-    // fix the rect we've computed to match the margin sizes if this tile is in the margins.
-    adjustRectAtTileIndexForMargin(tileIndex, rect);
-
-    return rect;
-}
-
-void TileController::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const
-{
-    IntRect clampedRect = bounds();
-    clampedRect.scale(m_scale);
-    clampedRect.intersect(rect);
-
-    if (clampedRect.x() >= 0)
-        topLeft.setX(clampedRect.x() / m_tileSize.width());
-    else
-        topLeft.setX(floorf((float)clampedRect.x() / leftMarginWidth()));
-
-    if (clampedRect.y() >= 0)
-        topLeft.setY(clampedRect.y() / m_tileSize.height());
-    else
-        topLeft.setY(floorf((float)clampedRect.y() / topMarginHeight()));
-
-    int bottomXRatio = ceil((float)clampedRect.maxX() / m_tileSize.width());
-    bottomRight.setX(std::max(bottomXRatio - 1, 0));
-
-    int bottomYRatio = ceil((float)clampedRect.maxY() / m_tileSize.height());
-    bottomRight.setY(std::max(bottomYRatio - 1, 0));
-}
-
-FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect) const
-{
-    FloatRect visibleRect = currentVisibleRect;
-    visibleRect.intersect(scaledExposedRect());
-
     // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect.
     if (!m_isInWindow)
         return visibleRect;
@@ -536,6 +354,16 @@ void TileController::scheduleTileRevalidation(double interval)
     m_tileRevalidationTimer.startOneShot(interval);
 }
 
+bool TileController::shouldAggressivelyRetainTiles() const
+{
+    return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer);
+}
+
+bool TileController::shouldTemporarilyRetainTileCohorts() const
+{
+    return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer);
+}
+
 void TileController::tileRevalidationTimerFired(Timer<TileController>*)
 {
     if (m_isInWindow) {
@@ -543,22 +371,22 @@ void TileController::tileRevalidationTimerFired(Timer<TileController>*)
         return;
     }
 
-    TileValidationPolicyFlags foregroundValidationPolicy = owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer) ? 0 : PruneSecondaryTiles;
-    TileValidationPolicyFlags backgroundValidationPolicy = foregroundValidationPolicy | UnparentAllTiles;
+    TileGrid::TileValidationPolicyFlags validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : PruneSecondaryTiles) | UnparentAllTiles;
 
-    revalidateTiles(foregroundValidationPolicy, backgroundValidationPolicy);
+    tileGrid().revalidateTiles(validationPolicy);
 }
 
-unsigned TileController::blankPixelCount() const
+void TileController::didRevalidateTiles()
 {
-    PlatformLayerList tiles(m_tiles.size());
+    m_visibleRectAtLastRevalidate = visibleRect();
+    m_boundsAtLastRevalidate = bounds();
 
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        if (PlatformLayer *layer = it->value.layer->platformLayer())
-            tiles.append(layer);
-    }
+    updateTileCoverageMap();
+}
 
-    return blankPixelCountForTiles(tiles, m_visibleRect, IntPoint(0,0));
+unsigned TileController::blankPixelCount() const
+{
+    return tileGrid().blankPixelCount();
 }
 
 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
@@ -581,324 +409,19 @@ unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles,
     return uncoveredRegion.totalArea();
 }
 
-static inline void queueTileForRemoval(const TileController::TileIndex& tileIndex, const TileController::TileInfo& tileInfo, Vector<TileController::TileIndex>& tilesToRemove, TileController::RepaintCountMap& repaintCounts)
-{
-    tileInfo.layer->removeFromSuperlayer();
-    repaintCounts.remove(tileInfo.layer.get());
-    tilesToRemove.append(tileIndex);
-}
-
-void TileController::removeAllTiles()
-{
-    Vector<TileIndex> tilesToRemove;
-
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-        queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
-
-    for (size_t i = 0; i < tilesToRemove.size(); ++i) {
-        TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
-#if !PLATFORM(IOS)
-        LayerPool::sharedPool()->addLayer(tileInfo.layer);
-#endif
-    }
-}
-
-void TileController::removeAllSecondaryTiles()
-{
-    Vector<TileIndex> tilesToRemove;
-
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        if (tileInfo.cohort == VisibleTileCohort)
-            continue;
-
-        queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
-    }
-
-    for (size_t i = 0; i < tilesToRemove.size(); ++i) {
-        TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
-#if !PLATFORM(IOS)
-        LayerPool::sharedPool()->addLayer(tileInfo.layer);
-#endif
-    }
-}
-
-void TileController::removeTilesInCohort(TileCohort cohort)
-{
-    ASSERT(cohort != VisibleTileCohort);
-    Vector<TileIndex> tilesToRemove;
-
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        if (tileInfo.cohort != cohort)
-            continue;
-
-        queueTileForRemoval(it->key, it->value, tilesToRemove, m_tileRepaintCounts);
-    }
-
-    for (size_t i = 0; i < tilesToRemove.size(); ++i) {
-        TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
-#if !PLATFORM(IOS)
-        LayerPool::sharedPool()->addLayer(tileInfo.layer);
-#endif
-    }
-}
-
 void TileController::setNeedsRevalidateTiles()
 {
     owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
 }
 
-void TileController::revalidateTiles(TileValidationPolicyFlags foregroundValidationPolicy, TileValidationPolicyFlags backgroundValidationPolicy)
-{
-    FloatRect visibleRect = m_visibleRect;
-    IntRect bounds = this->bounds();
-
-    visibleRect.intersect(scaledExposedRect());
-
-    if (visibleRect.isEmpty() || bounds.isEmpty())
-        return;
-    
-    TileValidationPolicyFlags validationPolicy = m_isInWindow ? foregroundValidationPolicy : backgroundValidationPolicy;
-    
-    FloatRect tileCoverageRect = computeTileCoverageRect(m_visibleRectAtLastRevalidate, m_visibleRect);
-    FloatRect scaledRect(tileCoverageRect);
-    scaledRect.scale(m_scale);
-    IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect));
-
-    TileCohort currCohort = nextTileCohort();
-    unsigned tilesInCohort = 0;
-
-    // Move tiles newly outside the coverage rect into the cohort map.
-    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        TileInfo& tileInfo = it->value;
-        TileIndex tileIndex = it->key;
-
-        PlatformCALayer* tileLayer = tileInfo.layer.get();
-        IntRect tileRect = rectForTileIndex(tileIndex);
-        if (tileRect.intersects(coverageRectInTileCoords)) {
-            tileInfo.cohort = VisibleTileCohort;
-            if (tileInfo.hasStaleContent) {
-                // FIXME: store a dirty region per layer?
-                tileLayer->setNeedsDisplay();
-                tileInfo.hasStaleContent = false;
-            }
-        } else {
-            // Add to the currentCohort if not already in one.
-            if (tileInfo.cohort == VisibleTileCohort) {
-                tileInfo.cohort = currCohort;
-                ++tilesInCohort;
-
-                if (m_unparentsOffscreenTiles)
-                    tileLayer->removeFromSuperlayer();
-            }
-        }
-    }
-
-    if (tilesInCohort)
-        startedNewCohort(currCohort);
-
-    if (!owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer)) {
-        if (owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer))
-            scheduleCohortRemoval();
-        else if (tilesInCohort)
-            removeTilesInCohort(currCohort);
-    }
-
-    // Ensure primary tile coverage tiles.
-    m_primaryTileCoverageRect = ensureTilesForRect(tileCoverageRect, CoverageType::PrimaryTiles);
-
-    if (validationPolicy & PruneSecondaryTiles) {
-        removeAllSecondaryTiles();
-        m_cohortList.clear();
-    } else {
-        for (size_t i = 0; i < m_secondaryTileCoverageRects.size(); ++i)
-            ensureTilesForRect(m_secondaryTileCoverageRects[i], CoverageType::SecondaryTiles);
-        m_secondaryTileCoverageRects.clear();
-    }
-
-    if (m_unparentsOffscreenTiles && (validationPolicy & UnparentAllTiles)) {
-        for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
-            it->value.layer->removeFromSuperlayer();
-    }
-
-    if (m_boundsAtLastRevalidate != bounds) {
-        // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to
-        // be bottom or right margin tiles need to be invalidated.
-        if (hasMargins()) {
-            if (bounds.width() > m_boundsAtLastRevalidate.width() || bounds.height() > m_boundsAtLastRevalidate.height()) {
-                IntRect boundsWithoutMargin = this->boundsWithoutMargin();
-                IntRect oldBoundsWithoutMargin = boundsAtLastRevalidateWithoutMargin();
-
-                if (bounds.height() > m_boundsAtLastRevalidate.height()) {
-                    IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(),
-                        oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height());
-                    setNeedsDisplayInRect(formerBottomMarginRect);
-                }
-
-                if (bounds.width() > m_boundsAtLastRevalidate.width()) {
-                    IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(),
-                        boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height());
-                    setNeedsDisplayInRect(formerRightMarginRect);
-                }
-            }
-        }
-
-        FloatRect scaledBounds(bounds);
-        scaledBounds.scale(m_scale);
-        IntRect boundsInTileCoords(enclosingIntRect(scaledBounds));
-
-        TileIndex topLeftForBounds;
-        TileIndex bottomRightForBounds;
-        getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds);
-
-        Vector<TileIndex> tilesToRemove;
-        for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-            const TileIndex& index = it->key;
-            if (index.y() < topLeftForBounds.y()
-                || index.y() > bottomRightForBounds.y()
-                || index.x() < topLeftForBounds.x()
-                || index.x() > bottomRightForBounds.x())
-                queueTileForRemoval(index, it->value, tilesToRemove, m_tileRepaintCounts);
-        }
-
-        for (size_t i = 0, size = tilesToRemove.size(); i < size; ++i) {
-            TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
-#if !PLATFORM(IOS)
-            LayerPool::sharedPool()->addLayer(tileInfo.layer);
-#endif
-        }
-    }
-
-    if (m_tiledScrollingIndicatorLayer)
-        updateTileCoverageMap();
-
-    m_visibleRectAtLastRevalidate = visibleRect;
-    m_boundsAtLastRevalidate = bounds;
-}
-
-TileController::TileCohort TileController::nextTileCohort() const
-{
-    if (!m_cohortList.isEmpty())
-        return m_cohortList.last().cohort + 1;
-
-    return 1;
-}
-
-void TileController::startedNewCohort(TileCohort cohort)
-{
-    m_cohortList.append(TileCohortInfo(cohort, monotonicallyIncreasingTime()));
-#if PLATFORM(IOS)
-    if (!m_isInWindow)
-        tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(this);
-#endif
-}
-
-TileController::TileCohort TileController::newestTileCohort() const
-{
-    return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort;
-}
-
-TileController::TileCohort TileController::oldestTileCohort() const
-{
-    return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort;
-}
-
-void TileController::scheduleCohortRemoval()
-{
-    const double cohortRemovalTimerSeconds = 1;
-
-    // Start the timer, or reschedule the timer from now if it's already active.
-    if (!m_cohortRemovalTimer.isActive())
-        m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds);
-}
-
-void TileController::cohortRemovalTimerFired(Timer<TileController>*)
-{
-    if (m_cohortList.isEmpty()) {
-        m_cohortRemovalTimer.stop();
-        return;
-    }
-
-    double cohortLifeTimeSeconds = 2;
-    double timeThreshold = monotonicallyIncreasingTime() - cohortLifeTimeSeconds;
-
-    while (!m_cohortList.isEmpty() && m_cohortList.first().creationTime < timeThreshold) {
-        TileCohortInfo firstCohort = m_cohortList.takeFirst();
-        removeTilesInCohort(firstCohort.cohort);
-    }
-
-    if (m_tiledScrollingIndicatorLayer)
-        updateTileCoverageMap();
-}
-
-IntRect TileController::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType)
-{
-    if (m_unparentsOffscreenTiles && !m_isInWindow)
-        return IntRect();
-
-    FloatRect scaledRect(rect);
-    scaledRect.scale(m_scale);
-    IntRect rectInTileCoords(enclosingIntRect(scaledRect));
-
-    TileIndex topLeft;
-    TileIndex bottomRight;
-    getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight);
-
-    TileCohort currCohort = nextTileCohort();
-    unsigned tilesInCohort = 0;
-
-    IntRect coverageRect;
-
-    for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
-        for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
-            TileIndex tileIndex(x, y);
-
-            IntRect tileRect = rectForTileIndex(tileIndex);
-            TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value;
-
-            coverageRect.unite(tileRect);
-
-            bool shouldChangeTileLayerFrame = false;
-
-            if (!tileInfo.layer)
-                tileInfo.layer = createTileLayer(tileRect);
-            else {
-                // We already have a layer for this tile. Ensure that its size is correct.
-                FloatSize tileLayerSize(tileInfo.layer->bounds().size());
-                shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size());
-
-                if (shouldChangeTileLayerFrame) {
-                    tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
-                    tileInfo.layer->setPosition(tileRect.location());
-                    tileInfo.layer->setNeedsDisplay();
-                }
-            }
-
-            if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) {
-                tileInfo.cohort = currCohort;
-                ++tilesInCohort;
-            }
-
-            bool shouldParentTileLayer = (!m_unparentsOffscreenTiles || m_isInWindow) && !tileInfo.layer->superlayer();
-
-            if (shouldParentTileLayer)
-                m_tileContainerLayer->appendSublayer(tileInfo.layer.get());
-        }
-    }
-    
-    if (tilesInCohort)
-        startedNewCohort(currCohort);
-
-    return coverageRect;
-}
-
 void TileController::updateTileCoverageMap()
 {
+    if (!m_tiledScrollingIndicatorLayer)
+        return;
     FloatRect containerBounds = bounds();
     FloatRect visibleRect = this->visibleRect();
 
-    visibleRect.intersect(scaledExposedRect());
+    visibleRect.intersect(tileGrid().scaledExposedRect());
     visibleRect.contract(4, 4); // Layer is positioned 2px from top and left edges.
 
     float widthScale = 1;
@@ -908,7 +431,7 @@ void TileController::updateTileCoverageMap()
         scale = std::min(widthScale, visibleRect.height() / containerBounds.height());
     }
     
-    float indicatorScale = scale * m_scale;
+    float indicatorScale = scale * tileGrid().scale();
     FloatRect mapBounds = containerBounds;
     mapBounds.scale(indicatorScale, indicatorScale);
 
@@ -943,36 +466,18 @@ void TileController::updateTileCoverageMap()
 
 IntRect TileController::tileGridExtent() const
 {
-    TileIndex topLeft;
-    TileIndex bottomRight;
-    getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight);
-
-    // Return index of top, left tile and the number of tiles across and down.
-    return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1);
+    return tileGrid().extent();
 }
 
 double TileController::retainedTileBackingStoreMemory() const
 {
-    double totalBytes = 0;
-    
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        if (tileInfo.layer->superlayer()) {
-            FloatRect bounds = tileInfo.layer->bounds();
-            double contentsScale = tileInfo.layer->contentsScale();
-            totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale;
-        }
-    }
-
-    return totalBytes;
+    return tileGrid().retainedTileBackingStoreMemory();
 }
 
 // Return the rect in layer coords, not tile coords.
 IntRect TileController::tileCoverageRect() const
 {
-    IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect);
-    coverageRectInLayerCoords.scale(1 / m_scale);
-    return coverageRectInLayerCoords;
+    return tileGrid().tileCoverageRect();
 }
 
 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
@@ -1005,8 +510,7 @@ void TileController::setScrollingModeIndication(ScrollingModeIndication scrollin
 
     m_indicatorMode = scrollingMode;
 
-    if (m_tiledScrollingIndicatorLayer)
-        updateTileCoverageMap();
+    updateTileCoverageMap();
 }
 
 void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight)
@@ -1092,62 +596,24 @@ int TileController::platformCALayerIncrementRepaintCount(PlatformCALayer* platfo
     return repaintCount;
 }
 
-void TileController::drawTileMapContents(CGContextRef context, CGRect layerBounds)
+Vector<RefPtr<PlatformCALayer>> TileController::containerLayers()
 {
-    CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1);
-    CGContextFillRect(context, layerBounds);
-
-    CGFloat scaleFactor = layerBounds.size.width / bounds().width();
-
-    CGFloat contextScale = scaleFactor / scale();
-    CGContextScaleCTM(context, contextScale, contextScale);
-    
-    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
-        const TileInfo& tileInfo = it->value;
-        PlatformCALayer* tileLayer = tileInfo.layer.get();
-
-        CGFloat red = 1;
-        CGFloat green = 1;
-        CGFloat blue = 1;
-        if (tileInfo.hasStaleContent) {
-            red = 0.25;
-            green = 0.125;
-            blue = 0;
-        }
-
-        TileCohort newestCohort = newestTileCohort();
-        TileCohort oldestCohort = oldestTileCohort();
-
-        if (!owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer) && tileInfo.cohort != VisibleTileCohort && newestCohort > oldestCohort) {
-            float cohortProportion = static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort);
-            CGContextSetRGBFillColor(context, red, green, blue, 1 - cohortProportion);
-        } else
-            CGContextSetRGBFillColor(context, red, green, blue, 1);
-
-        if (tileLayer->superlayer()) {
-            CGContextSetLineWidth(context, 0.5 / contextScale);
-            CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
-        } else {
-            CGContextSetLineWidth(context, 1 / contextScale);
-            CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1);
-        }
-
-        CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height());
-        CGContextFillRect(context, frame);
-        CGContextStrokeRect(context, frame);
-    }
+    Vector<RefPtr<PlatformCALayer>> layerList(1);
+    layerList[0] = &tileGrid().containerLayer();
+    return layerList;
 }
     
 #if PLATFORM(IOS)
+unsigned TileController::numberOfUnparentedTiles() const
+{
+    return tileGrid().numberOfUnparentedTiles();
+}
+
 void TileController::removeUnparentedTilesNow()
 {
-    while (!m_cohortList.isEmpty()) {
-        TileCohortInfo firstCohort = m_cohortList.takeFirst();
-        removeTilesInCohort(firstCohort.cohort);
-    }
+    tileGrid().removeUnparentedTilesNow();
 
-    if (m_tiledScrollingIndicatorLayer)
-        updateTileCoverageMap();
+    updateTileCoverageMap();
 }
 #endif
 
diff --git a/Source/WebCore/platform/graphics/ca/mac/TileGrid.h b/Source/WebCore/platform/graphics/ca/mac/TileGrid.h
new file mode 100644 (file)
index 0000000..59b259d
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011-2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TileGrid_h
+#define TileGrid_h
+
+#include "IntPointHash.h"
+#include "IntRect.h"
+#include "Timer.h"
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Ref.h>
+
+namespace WebCore {
+
+class PlatformCALayer;
+class TileController;
+
+class TileGrid {
+    WTF_MAKE_NONCOPYABLE(TileGrid);
+public:
+    TileGrid(TileController&);
+    ~TileGrid();
+
+    PlatformCALayer& containerLayer() { return m_containerLayer.get(); }
+
+    void setScale(float);
+    float scale() const { return m_scale; }
+
+    void setNeedsDisplay();
+    void setNeedsDisplayInRect(const IntRect&);
+
+    void updateTilerLayerProperties();
+
+    bool prepopulateRect(const FloatRect&);
+
+    typedef unsigned TileValidationPolicyFlags;
+    void revalidateTiles(TileValidationPolicyFlags);
+    bool tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect, const FloatRect& oldVisibleRect) const;
+
+    FloatRect scaledExposedRect() const;
+
+    IntRect tileCoverageRect() const;
+    IntRect extent() const;
+
+    double retainedTileBackingStoreMemory() const;
+    unsigned blankPixelCount() const;
+
+    void drawTileMapContents(CGContextRef, CGRect layerBounds);
+
+#if PLATFORM(IOS)
+    unsigned numberOfUnparentedTiles() const { return m_cohortList.size(); }
+    void removeUnparentedTilesNow();
+#endif
+
+    typedef IntPoint TileIndex;
+    typedef unsigned TileCohort;
+    static const TileCohort VisibleTileCohort = UINT_MAX;
+
+    struct TileInfo {
+        RefPtr<PlatformCALayer> layer;
+        TileCohort cohort; // VisibleTileCohort is visible.
+        bool hasStaleContent;
+
+        TileInfo()
+            : cohort(VisibleTileCohort)
+            , hasStaleContent(false)
+        { }
+    };
+
+private:
+    void setTileNeedsDisplayInRect(const TileIndex&, TileInfo&, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords);
+
+    IntRect rectForTileIndex(const TileIndex&) const;
+    void adjustRectAtTileIndexForMargin(const TileIndex&, IntRect&) const;
+    void getTileIndexRangeForRect(const IntRect&, TileIndex& topLeft, TileIndex& bottomRight) const;
+
+    enum class CoverageType { PrimaryTiles, SecondaryTiles };
+    IntRect ensureTilesForRect(const FloatRect&, CoverageType);
+
+    void removeAllSecondaryTiles();
+    void removeTilesInCohort(TileCohort);
+
+    void scheduleCohortRemoval();
+    void cohortRemovalTimerFired(Timer<TileGrid>*);
+    TileCohort nextTileCohort() const;
+    void startedNewCohort(TileCohort);
+    TileCohort newestTileCohort() const;
+    TileCohort oldestTileCohort() const;
+
+    TileController& m_controller;
+    Ref<PlatformCALayer> m_containerLayer;
+
+    typedef HashMap<TileIndex, TileInfo> TileMap;
+    TileMap m_tiles;
+
+    IntRect m_primaryTileCoverageRect;
+    Vector<FloatRect> m_secondaryTileCoverageRects;
+
+    float m_scale;
+
+    struct TileCohortInfo {
+        TileCohort cohort;
+        double creationTime; // in monotonicallyIncreasingTime().
+        TileCohortInfo(TileCohort inCohort, double inTime)
+            : cohort(inCohort)
+            , creationTime(inTime)
+        { }
+    };
+    typedef Deque<TileCohortInfo> TileCohortList;
+    TileCohortList m_cohortList;
+
+    Timer<TileGrid> m_cohortRemovalTimer;
+};
+
+}
+#endif
diff --git a/Source/WebCore/platform/graphics/ca/mac/TileGrid.mm b/Source/WebCore/platform/graphics/ca/mac/TileGrid.mm
new file mode 100644 (file)
index 0000000..4df470f
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2011-2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "TileGrid.h"
+
+#import "LayerPool.h"
+#import "PlatformCALayer.h"
+#import "TileController.h"
+#import <wtf/MainThread.h>
+
+#if PLATFORM(IOS)
+#import "TileControllerMemoryHandlerIOS.h"
+#endif
+
+namespace WebCore {
+
+enum TileValidationPolicyFlag {
+    PruneSecondaryTiles = 1 << 0,
+    UnparentAllTiles = 1 << 1
+};
+
+TileGrid::TileGrid(TileController& controller)
+    : m_controller(controller)
+    , m_containerLayer(*controller.rootLayer().createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr))
+    , m_cohortRemovalTimer(this, &TileGrid::cohortRemovalTimerFired)
+{
+#ifndef NDEBUG
+    m_containerLayer.get().setName("TileGrid Container Layer");
+#endif
+}
+
+TileGrid::~TileGrid()
+{
+    ASSERT(isMainThread());
+
+    for (auto& tile : m_tiles.values())
+        tile.layer->setOwner(nullptr);
+}
+
+void TileGrid::setScale(float scale)
+{
+    m_scale = scale;
+
+    TransformationMatrix transform;
+    transform.scale(1 / m_scale);
+    m_containerLayer->setTransform(transform);
+
+    // FIXME: we may revalidateTiles twice in this commit.
+    revalidateTiles(PruneSecondaryTiles);
+
+    for (auto& tile : m_tiles.values())
+        tile.layer->setContentsScale(m_controller.deviceScaleFactor());
+}
+
+void TileGrid::setNeedsDisplay()
+{
+    for (auto& entry : m_tiles) {
+        TileInfo& tileInfo = entry.value;
+        IntRect tileRect = rectForTileIndex(entry.key);
+
+        if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer())
+            tileInfo.layer->setNeedsDisplay();
+        else
+            tileInfo.hasStaleContent = true;
+    }
+}
+
+void TileGrid::setNeedsDisplayInRect(const IntRect& rect)
+{
+    if (m_tiles.isEmpty())
+        return;
+
+    FloatRect scaledRect(rect);
+    scaledRect.scale(m_scale);
+    IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect));
+
+    IntSize tileSize = m_controller.tileSize();
+
+    // For small invalidations, lookup the covered tiles.
+    if (repaintRectInTileCoords.height() < 2 * tileSize.height() && repaintRectInTileCoords.width() < 2 * tileSize.width()) {
+        TileIndex topLeft;
+        TileIndex bottomRight;
+        getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight);
+
+        for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
+            for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
+                TileIndex tileIndex(x, y);
+                
+                TileMap::iterator it = m_tiles.find(tileIndex);
+                if (it != m_tiles.end())
+                    setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
+            }
+        }
+        return;
+    }
+
+    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
+        setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
+}
+
+void TileGrid::setTileNeedsDisplayInRect(const TileIndex& tileIndex, TileInfo& tileInfo, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords)
+{
+    PlatformCALayer* tileLayer = tileInfo.layer.get();
+
+    IntRect tileRect = rectForTileIndex(tileIndex);
+    FloatRect tileRepaintRect = tileRect;
+    tileRepaintRect.intersect(repaintRectInTileCoords);
+    if (tileRepaintRect.isEmpty())
+        return;
+
+    tileRepaintRect.moveBy(-tileRect.location());
+    
+    // We could test for intersection with the visible rect. This would reduce painting yet more,
+    // but may make scrolling stale tiles into view more frequent.
+    if (tileRect.intersects(coverageRectInTileCoords) && tileLayer->superlayer()) {
+        tileLayer->setNeedsDisplay(&tileRepaintRect);
+
+        if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0)) {
+            FloatRect indicatorRect(0, 0, 52, 27);
+            tileLayer->setNeedsDisplay(&indicatorRect);
+        }
+    } else
+        tileInfo.hasStaleContent = true;
+}
+
+void TileGrid::updateTilerLayerProperties()
+{
+    bool acceleratesDrawing = m_controller.acceleratesDrawing();
+    bool opaque = m_controller.tilesAreOpaque();
+    Color tileDebugBorderColor = m_controller.tileDebugBorderColor();
+    float tileDebugBorderWidth = m_controller.tileDebugBorderWidth();
+
+    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        const TileInfo& tileInfo = it->value;
+        tileInfo.layer->setAcceleratesDrawing(acceleratesDrawing);
+        tileInfo.layer->setOpaque(opaque);
+        tileInfo.layer->setBorderColor(tileDebugBorderColor);
+        tileInfo.layer->setBorderWidth(tileDebugBorderWidth);
+    }
+}
+
+bool TileGrid::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect, const FloatRect& oldVisibleRect) const
+{
+    FloatRect visibleRect = newVisibleRect;
+    visibleRect.intersect(scaledExposedRect());
+
+    if (visibleRect.isEmpty())
+        return false;
+        
+    FloatRect currentTileCoverageRect = m_controller.computeTileCoverageRect(oldVisibleRect, newVisibleRect);
+    FloatRect scaledRect(currentTileCoverageRect);
+    scaledRect.scale(m_scale);
+    IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect));
+
+    TileIndex topLeft;
+    TileIndex bottomRight;
+    getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight);
+
+    IntRect coverageRect = rectForTileIndex(topLeft);
+    coverageRect.unite(rectForTileIndex(bottomRight));
+    return coverageRect != m_primaryTileCoverageRect;
+}
+
+FloatRect TileGrid::scaledExposedRect() const
+{
+    FloatRect scaledExposedRect = m_controller.exposedRect();
+    scaledExposedRect.scale(1 / m_scale);
+    return scaledExposedRect;
+}
+
+bool TileGrid::prepopulateRect(const FloatRect& rect)
+{
+    FloatRect scaledRect(rect);
+    scaledRect.scale(m_scale);
+    IntRect rectInTileCoords(enclosingIntRect(scaledRect));
+
+    if (m_primaryTileCoverageRect.contains(rectInTileCoords))
+        return false;
+    
+    m_secondaryTileCoverageRects.append(rect);
+    return true;
+}
+
+void TileGrid::adjustRectAtTileIndexForMargin(const TileIndex& tileIndex, IntRect& rect) const
+{
+    if (!m_controller.hasMargins())
+        return;
+
+    // This is a tile in the top margin.
+    if (m_controller.topMarginHeight() && tileIndex.y() < 0) {
+        rect.setY(tileIndex.y() * m_controller.topMarginHeight());
+        rect.setHeight(m_controller.topMarginHeight());
+    }
+
+    // This is a tile in the left margin.
+    if (m_controller.leftMarginWidth() && tileIndex.x() < 0) {
+        rect.setX(tileIndex.x() * m_controller.leftMarginWidth());
+        rect.setWidth(m_controller.leftMarginWidth());
+    }
+
+    TileIndex contentTopLeft;
+    TileIndex contentBottomRight;
+    getTileIndexRangeForRect(m_controller.boundsWithoutMargin(), contentTopLeft, contentBottomRight);
+
+    // This is a tile in the bottom margin.
+    if (m_controller.bottomMarginHeight() && tileIndex.y() > contentBottomRight.y())
+        rect.setHeight(m_controller.bottomMarginHeight());
+
+    // This is a tile in the right margin.
+    if (m_controller.rightMarginWidth()  && tileIndex.x() > contentBottomRight.x())
+        rect.setWidth(m_controller.rightMarginWidth());
+}
+
+IntRect TileGrid::rectForTileIndex(const TileIndex& tileIndex) const
+{
+    IntSize tileSize = m_controller.tileSize();
+    IntRect rect(tileIndex.x() * tileSize.width(), tileIndex.y() * tileSize.height(), tileSize.width(), tileSize.height());
+    IntRect scaledBounds(m_controller.bounds());
+    scaledBounds.scale(m_scale);
+
+    rect.intersect(scaledBounds);
+
+    // These rect computations assume m_tileSize is the correct size to use. However, a tile in the margin area
+    // might be a different size depending on the size of the margins. So adjustRectAtTileIndexForMargin() will
+    // fix the rect we've computed to match the margin sizes if this tile is in the margins.
+    adjustRectAtTileIndexForMargin(tileIndex, rect);
+
+    return rect;
+}
+
+void TileGrid::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const
+{
+    IntRect clampedRect = m_controller.bounds();
+    clampedRect.scale(m_scale);
+    clampedRect.intersect(rect);
+
+    auto tileSize = m_controller.tileSize();
+    if (clampedRect.x() >= 0)
+        topLeft.setX(clampedRect.x() / tileSize.width());
+    else
+        topLeft.setX(floorf((float)clampedRect.x() / m_controller.leftMarginWidth()));
+
+    if (clampedRect.y() >= 0)
+        topLeft.setY(clampedRect.y() / tileSize.height());
+    else
+        topLeft.setY(floorf((float)clampedRect.y() / m_controller.topMarginHeight()));
+
+    int bottomXRatio = ceil((float)clampedRect.maxX() / tileSize.width());
+    bottomRight.setX(std::max(bottomXRatio - 1, 0));
+
+    int bottomYRatio = ceil((float)clampedRect.maxY() / tileSize.height());
+    bottomRight.setY(std::max(bottomYRatio - 1, 0));
+}
+
+unsigned TileGrid::blankPixelCount() const
+{
+    PlatformLayerList tiles(m_tiles.size());
+
+    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        if (PlatformLayer *layer = it->value.layer->platformLayer())
+            tiles.append(layer);
+    }
+
+    return TileController::blankPixelCountForTiles(tiles, m_controller.visibleRect(), IntPoint(0,0));
+}
+
+static inline void queueTileForRemoval(const TileGrid::TileIndex& tileIndex, const TileGrid::TileInfo& tileInfo, Vector<TileGrid::TileIndex>& tilesToRemove, TileController::RepaintCountMap& repaintCounts)
+{
+    tileInfo.layer->removeFromSuperlayer();
+    repaintCounts.remove(tileInfo.layer.get());
+    tilesToRemove.append(tileIndex);
+}
+
+void TileGrid::removeAllSecondaryTiles()
+{
+    Vector<TileIndex> tilesToRemove;
+
+    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        const TileInfo& tileInfo = it->value;
+        if (tileInfo.cohort == VisibleTileCohort)
+            continue;
+
+        queueTileForRemoval(it->key, it->value, tilesToRemove, m_controller.repaintCountMap());
+    }
+
+    for (size_t i = 0; i < tilesToRemove.size(); ++i) {
+        TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
+#if !PLATFORM(IOS)
+        LayerPool::sharedPool()->addLayer(tileInfo.layer);
+#endif
+    }
+}
+
+void TileGrid::removeTilesInCohort(TileCohort cohort)
+{
+    ASSERT(cohort != VisibleTileCohort);
+    Vector<TileIndex> tilesToRemove;
+
+    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        const TileInfo& tileInfo = it->value;
+        if (tileInfo.cohort != cohort)
+            continue;
+
+        queueTileForRemoval(it->key, it->value, tilesToRemove, m_controller.repaintCountMap());
+    }
+
+    for (size_t i = 0; i < tilesToRemove.size(); ++i) {
+        TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
+#if !PLATFORM(IOS)
+        LayerPool::sharedPool()->addLayer(tileInfo.layer);
+#endif
+    }
+}
+
+void TileGrid::revalidateTiles(TileValidationPolicyFlags validationPolicy)
+{
+    FloatRect visibleRect = m_controller.visibleRect();
+    IntRect bounds = m_controller.bounds();
+
+    visibleRect.intersect(scaledExposedRect());
+
+    if (visibleRect.isEmpty() || bounds.isEmpty())
+        return;
+
+    FloatRect tileCoverageRect = m_controller.computeTileCoverageRect(m_controller.visibleRectAtLastRevalidate(), visibleRect);
+    FloatRect scaledRect(tileCoverageRect);
+    scaledRect.scale(m_scale);
+    IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect));
+
+    TileCohort currCohort = nextTileCohort();
+    unsigned tilesInCohort = 0;
+
+    // Move tiles newly outside the coverage rect into the cohort map.
+    for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        TileInfo& tileInfo = it->value;
+        TileIndex tileIndex = it->key;
+
+        PlatformCALayer* tileLayer = tileInfo.layer.get();
+        IntRect tileRect = rectForTileIndex(tileIndex);
+        if (tileRect.intersects(coverageRectInTileCoords)) {
+            tileInfo.cohort = VisibleTileCohort;
+            if (tileInfo.hasStaleContent) {
+                // FIXME: store a dirty region per layer?
+                tileLayer->setNeedsDisplay();
+                tileInfo.hasStaleContent = false;
+            }
+        } else {
+            // Add to the currentCohort if not already in one.
+            if (tileInfo.cohort == VisibleTileCohort) {
+                tileInfo.cohort = currCohort;
+                ++tilesInCohort;
+
+                if (m_controller.unparentsOffscreenTiles())
+                    tileLayer->removeFromSuperlayer();
+            }
+        }
+    }
+
+    if (tilesInCohort)
+        startedNewCohort(currCohort);
+
+    if (!m_controller.shouldAggressivelyRetainTiles()) {
+        if (m_controller.shouldTemporarilyRetainTileCohorts())
+            scheduleCohortRemoval();
+        else if (tilesInCohort)
+            removeTilesInCohort(currCohort);
+    }
+
+    // Ensure primary tile coverage tiles.
+    m_primaryTileCoverageRect = ensureTilesForRect(tileCoverageRect, CoverageType::PrimaryTiles);
+
+    if (validationPolicy & PruneSecondaryTiles) {
+        removeAllSecondaryTiles();
+        m_cohortList.clear();
+    } else {
+        for (size_t i = 0; i < m_secondaryTileCoverageRects.size(); ++i)
+            ensureTilesForRect(m_secondaryTileCoverageRects[i], CoverageType::SecondaryTiles);
+        m_secondaryTileCoverageRects.clear();
+    }
+
+    if (m_controller.unparentsOffscreenTiles() && (validationPolicy & UnparentAllTiles)) {
+        for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it)
+            it->value.layer->removeFromSuperlayer();
+    }
+
+    auto boundsAtLastRevalidate = m_controller.boundsAtLastRevalidate();
+    if (boundsAtLastRevalidate != bounds) {
+        // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to
+        // be bottom or right margin tiles need to be invalidated.
+        if (m_controller.hasMargins()) {
+            if (bounds.width() > boundsAtLastRevalidate.width() || bounds.height() > boundsAtLastRevalidate.height()) {
+                IntRect boundsWithoutMargin = m_controller.boundsWithoutMargin();
+                IntRect oldBoundsWithoutMargin = m_controller.boundsAtLastRevalidateWithoutMargin();
+
+                if (bounds.height() > boundsAtLastRevalidate.height()) {
+                    IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(),
+                        oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height());
+                    setNeedsDisplayInRect(formerBottomMarginRect);
+                }
+
+                if (bounds.width() > boundsAtLastRevalidate.width()) {
+                    IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(),
+                        boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height());
+                    setNeedsDisplayInRect(formerRightMarginRect);
+                }
+            }
+        }
+
+        FloatRect scaledBounds(bounds);
+        scaledBounds.scale(m_scale);
+        IntRect boundsInTileCoords(enclosingIntRect(scaledBounds));
+
+        TileIndex topLeftForBounds;
+        TileIndex bottomRightForBounds;
+        getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds);
+
+        Vector<TileIndex> tilesToRemove;
+        for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+            const TileIndex& index = it->key;
+            if (index.y() < topLeftForBounds.y()
+                || index.y() > bottomRightForBounds.y()
+                || index.x() < topLeftForBounds.x()
+                || index.x() > bottomRightForBounds.x())
+                queueTileForRemoval(index, it->value, tilesToRemove, m_controller.repaintCountMap());
+        }
+
+        for (size_t i = 0, size = tilesToRemove.size(); i < size; ++i) {
+            TileInfo tileInfo = m_tiles.take(tilesToRemove[i]);
+#if !PLATFORM(IOS)
+            LayerPool::sharedPool()->addLayer(tileInfo.layer);
+#endif
+        }
+    }
+
+    m_controller.didRevalidateTiles();
+}
+
+TileGrid::TileCohort TileGrid::nextTileCohort() const
+{
+    if (!m_cohortList.isEmpty())
+        return m_cohortList.last().cohort + 1;
+
+    return 1;
+}
+
+void TileGrid::startedNewCohort(TileCohort cohort)
+{
+    m_cohortList.append(TileCohortInfo(cohort, monotonicallyIncreasingTime()));
+#if PLATFORM(IOS)
+    if (!m_controller.isInWindow())
+        tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(&m_controller);
+#endif
+}
+
+TileGrid::TileCohort TileGrid::newestTileCohort() const
+{
+    return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort;
+}
+
+TileGrid::TileCohort TileGrid::oldestTileCohort() const
+{
+    return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort;
+}
+
+void TileGrid::scheduleCohortRemoval()
+{
+    const double cohortRemovalTimerSeconds = 1;
+
+    // Start the timer, or reschedule the timer from now if it's already active.
+    if (!m_cohortRemovalTimer.isActive())
+        m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds);
+}
+
+void TileGrid::cohortRemovalTimerFired(Timer<TileGrid>*)
+{
+    if (m_cohortList.isEmpty()) {
+        m_cohortRemovalTimer.stop();
+        return;
+    }
+
+    double cohortLifeTimeSeconds = 2;
+    double timeThreshold = monotonicallyIncreasingTime() - cohortLifeTimeSeconds;
+
+    while (!m_cohortList.isEmpty() && m_cohortList.first().creationTime < timeThreshold) {
+        TileCohortInfo firstCohort = m_cohortList.takeFirst();
+        removeTilesInCohort(firstCohort.cohort);
+    }
+
+    m_controller.updateTileCoverageMap();
+}
+
+IntRect TileGrid::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType)
+{
+    if (m_controller.unparentsOffscreenTiles() && !m_controller.isInWindow())
+        return IntRect();
+
+    FloatRect scaledRect(rect);
+    scaledRect.scale(m_scale);
+    IntRect rectInTileCoords(enclosingIntRect(scaledRect));
+
+    TileIndex topLeft;
+    TileIndex bottomRight;
+    getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight);
+
+    TileCohort currCohort = nextTileCohort();
+    unsigned tilesInCohort = 0;
+
+    IntRect coverageRect;
+
+    for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
+        for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
+            TileIndex tileIndex(x, y);
+
+            IntRect tileRect = rectForTileIndex(tileIndex);
+            TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value;
+
+            coverageRect.unite(tileRect);
+
+            bool shouldChangeTileLayerFrame = false;
+
+            if (!tileInfo.layer)
+                tileInfo.layer = m_controller.createTileLayer(tileRect);
+            else {
+                // We already have a layer for this tile. Ensure that its size is correct.
+                FloatSize tileLayerSize(tileInfo.layer->bounds().size());
+                shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size());
+
+                if (shouldChangeTileLayerFrame) {
+                    tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
+                    tileInfo.layer->setPosition(tileRect.location());
+                    tileInfo.layer->setNeedsDisplay();
+                }
+            }
+
+            if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) {
+                tileInfo.cohort = currCohort;
+                ++tilesInCohort;
+            }
+
+            bool shouldParentTileLayer = (!m_controller.unparentsOffscreenTiles() || m_controller.isInWindow()) && !tileInfo.layer->superlayer();
+
+            if (shouldParentTileLayer)
+                m_containerLayer.get().appendSublayer(tileInfo.layer.get());
+        }
+    }
+    
+    if (tilesInCohort)
+        startedNewCohort(currCohort);
+
+    return coverageRect;
+}
+
+IntRect TileGrid::extent() const
+{
+    TileIndex topLeft;
+    TileIndex bottomRight;
+    getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight);
+
+    // Return index of top, left tile and the number of tiles across and down.
+    return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1);
+}
+
+double TileGrid::retainedTileBackingStoreMemory() const
+{
+    double totalBytes = 0;
+    
+    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        const TileInfo& tileInfo = it->value;
+        if (tileInfo.layer->superlayer()) {
+            FloatRect bounds = tileInfo.layer->bounds();
+            double contentsScale = tileInfo.layer->contentsScale();
+            totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale;
+        }
+    }
+
+    return totalBytes;
+}
+
+// Return the rect in layer coords, not tile coords.
+IntRect TileGrid::tileCoverageRect() const
+{
+    IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect);
+    coverageRectInLayerCoords.scale(1 / m_scale);
+    return coverageRectInLayerCoords;
+}
+
+void TileGrid::drawTileMapContents(CGContextRef context, CGRect layerBounds)
+{
+    CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1);
+    CGContextFillRect(context, layerBounds);
+
+    CGFloat scaleFactor = layerBounds.size.width / m_controller.bounds().width();
+
+    CGFloat contextScale = scaleFactor / m_scale;
+    CGContextScaleCTM(context, contextScale, contextScale);
+    
+    for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+        const TileInfo& tileInfo = it->value;
+        PlatformCALayer* tileLayer = tileInfo.layer.get();
+
+        CGFloat red = 1;
+        CGFloat green = 1;
+        CGFloat blue = 1;
+        if (tileInfo.hasStaleContent) {
+            red = 0.25;
+            green = 0.125;
+            blue = 0;
+        }
+
+        TileCohort newestCohort = newestTileCohort();
+        TileCohort oldestCohort = oldestTileCohort();
+
+        if (!m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != VisibleTileCohort && newestCohort > oldestCohort) {
+            float cohortProportion = static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort);
+            CGContextSetRGBFillColor(context, red, green, blue, 1 - cohortProportion);
+        } else
+            CGContextSetRGBFillColor(context, red, green, blue, 1);
+
+        if (tileLayer->superlayer()) {
+            CGContextSetLineWidth(context, 0.5 / contextScale);
+            CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
+        } else {
+            CGContextSetLineWidth(context, 1 / contextScale);
+            CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1);
+        }
+
+        CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height());
+        CGContextFillRect(context, frame);
+        CGContextStrokeRect(context, frame);
+    }
+}
+
+#if PLATFORM(IOS)
+void TileGrid::removeUnparentedTilesNow()
+{
+    while (!m_cohortList.isEmpty()) {
+        TileCohortInfo firstCohort = m_cohortList.takeFirst();
+        removeTilesInCohort(firstCohort.cohort);
+    }
+}
+#endif
+
+} // namespace WebCore
index a8682ee..8745956 100644 (file)
@@ -1,3 +1,14 @@
+2014-04-02  Antti Koivisto  <antti@apple.com>
+
+        Split tile grid out from TileController
+        https://bugs.webkit.org/show_bug.cgi?id=131102
+
+        Reviewed by Simon Fraser.
+
+        * WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.cpp:
+        (WebKit::PlatformCALayerRemoteTiledBacking::PlatformCALayerRemoteTiledBacking):
+        * WebProcess/WebPage/mac/PlatformCALayerRemoteTiledBacking.h:
+
 2014-04-02  Anders Carlsson  <andersca@apple.com>
 
         Try a workaround for a crash
index 9f40c5c..cb007b1 100644 (file)
@@ -40,10 +40,7 @@ PlatformCALayerRemoteTiledBacking::PlatformCALayerRemoteTiledBacking(LayerType l
     : PlatformCALayerRemote(layerType, owner, context)
 {
     m_tileController = TileController::create(this);
-
-    m_customSublayers = adoptPtr(new PlatformCALayerList(1));
-    PlatformCALayer* tileCacheTileContainerLayer = m_tileController->tileContainerLayer();
-    (*m_customSublayers)[0] = tileCacheTileContainerLayer;
+    m_customSublayers = std::make_unique<PlatformCALayerList>(m_tileController->containerLayers());
 }
 
 PlatformCALayerRemoteTiledBacking::~PlatformCALayerRemoteTiledBacking()
index 827e830..d679360 100644 (file)
@@ -54,7 +54,7 @@ private:
     virtual void setBorderColor(const WebCore::Color&) override;
 
     OwnPtr<WebCore::TileController> m_tileController;
-    OwnPtr<WebCore::PlatformCALayerList> m_customSublayers;
+    std::unique_ptr<WebCore::PlatformCALayerList> m_customSublayers;
 };
 
 } // namespace WebKit