adding support for DiscardablePixelRef for caching lazily decoded images
https://bugs.webkit.org/show_bug.cgi?id=106842

Patch by Min Qin <qinmin@chromium.org> on 2013-01-28
Reviewed by Stephen White.

Source/WebCore:

This change allows using discardable memory in the deferred image decoding path.
Fully decoded images are unpinned and stored in ImageDecodingStore.
Partially decoded images are pinned and stored in ImageDecodingStore.
Discardable memory allocation could fail. Fall back to heap allocation in that case.
There is a separate size limit for heap entries and no limit on discardable entries.
New tests are added to ImageDecodingStoreTests

* WebCore.gypi:
* platform/graphics/chromium/DiscardablePixelRef.cpp: Added.
  Added implementation of the DiscardablePixelRef object that is backed by discardable memory.
  Memory allocated to the DiscardablePixelRef can be purged when it is unlocked.
(WebCore::DiscardablePixelRefAllocator::allocPixelRef):
(WebCore):
(WebCore::DiscardablePixelRef::DiscardablePixelRef):
(WebCore::DiscardablePixelRef::~DiscardablePixelRef):
(WebCore::DiscardablePixelRef::allocAndLockDiscardableMemory):
(WebCore::DiscardablePixelRef::onLockPixels):
(WebCore::DiscardablePixelRef::onUnlockPixels):
(WebCore::DiscardablePixelRef::isDiscardable):
* platform/graphics/chromium/DiscardablePixelRef.h: Added.
  Added class definition of the DiscardablePixelRef.
(WebCore):
(DiscardablePixelRefAllocator):
(DiscardablePixelRef):
* platform/graphics/chromium/ImageDecodingStore.cpp:
  Added new cache replacement strategy for DiscardablePixelRef.
(WebCore::ImageDecodingStore::lockCache):
(WebCore::ImageDecodingStore::overwriteAndLockCache):
(WebCore::ImageDecodingStore::prune):
(WebCore::ImageDecodingStore::insertCacheInternal):
(WebCore::ImageDecodingStore::removeFromCacheInternal):
* platform/graphics/chromium/ImageDecodingStore.h:
  Added isDiscardable() calls to check if a cache entry is discardable.
(WebCore::ImageDecodingStore::CacheEntry::CacheEntry):
(WebCore::ImageDecodingStore::CacheEntry::overwriteCachedImage):
(WebCore::ImageDecodingStore::CacheEntry::isDiscardable):
(CacheEntry):
* platform/graphics/chromium/ImageFrameGenerator.cpp:
  Added some code to pass DiscardableMemoryAllocator to the image decoder.
(WebCore::ImageFrameGenerator::tryToScale):
(WebCore::ImageFrameGenerator::decode):
* platform/graphics/chromium/ImageFrameGenerator.h:
  Added a new member variable of type DiscardableMemoryAllocator.
(ImageFrameGenerator):
* platform/image-decoders/ImageDecoder.h:
  Added methods to pass Allocator to ImageFrame.
(ImageFrame):
(WebCore::ImageFrame::setMemoryAllocator):
(WebCore::ImageFrame::allocator):
(ImageDecoder):
(WebCore::ImageDecoder::setMemoryAllocator):
* platform/image-decoders/skia/ImageDecoderSkia.cpp:
  Added code to allocate pixel memory using the allocator passed from the caller.
(WebCore::ImageFrame::ImageFrame):
(WebCore::ImageFrame::operator=):
(WebCore::ImageFrame::setSize):

Source/WebKit/chromium:

Adding new tests for ImageDecodingStore

* WebKit.gypi:
* tests/ImageDecodingStoreTest.cpp:
(WebCore::ImageDecodingStoreTest::createCompleteImage):
(WebCore::ImageDecodingStoreTest::createIncompleteImage):
(WebCore::TEST_F):
(WebCore):
* tests/MockDiscardablePixelRef.h: Added.
(WebCore):
(MockDiscardablePixelRef):
(WebCore::MockDiscardablePixelRef::MockDiscardablePixelRef):
(WebCore::MockDiscardablePixelRef::~MockDiscardablePixelRef):
(WebCore::MockDiscardablePixelRef::discard):
(WebCore::MockDiscardablePixelRef::onLockPixels):
(WebCore::MockDiscardablePixelRef::onUnlockPixels):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@141020 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 7dab97b..99d8d39 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,67 @@
+2013-01-28  Min Qin  <qinmin@chromium.org>
+
+        adding support for DiscardablePixelRef for caching lazily decoded images
+        https://bugs.webkit.org/show_bug.cgi?id=106842
+
+        Reviewed by Stephen White.
+
+        This change allows using discardable memory in the deferred image decoding path.
+        Fully decoded images are unpinned and stored in ImageDecodingStore.
+        Partially decoded images are pinned and stored in ImageDecodingStore.
+        Discardable memory allocation could fail. Fall back to heap allocation in that case.
+        There is a separate size limit for heap entries and no limit on discardable entries.
+        New tests are added to ImageDecodingStoreTests
+
+        * WebCore.gypi:
+        * platform/graphics/chromium/DiscardablePixelRef.cpp: Added.
+          Added implementation of the DiscardablePixelRef object that is backed by discardable memory.
+          Memory allocated to the DiscardablePixelRef can be purged when it is unlocked.
+        (WebCore::DiscardablePixelRefAllocator::allocPixelRef):
+        (WebCore):
+        (WebCore::DiscardablePixelRef::DiscardablePixelRef):
+        (WebCore::DiscardablePixelRef::~DiscardablePixelRef):
+        (WebCore::DiscardablePixelRef::allocAndLockDiscardableMemory):
+        (WebCore::DiscardablePixelRef::onLockPixels):
+        (WebCore::DiscardablePixelRef::onUnlockPixels):
+        (WebCore::DiscardablePixelRef::isDiscardable):
+        * platform/graphics/chromium/DiscardablePixelRef.h: Added.
+          Added class definition of the DiscardablePixelRef.
+        (WebCore):
+        (DiscardablePixelRefAllocator):
+        (DiscardablePixelRef):
+        * platform/graphics/chromium/ImageDecodingStore.cpp:
+          Added new cache replacement strategy for DiscardablePixelRef.
+        (WebCore::ImageDecodingStore::lockCache):
+        (WebCore::ImageDecodingStore::overwriteAndLockCache):
+        (WebCore::ImageDecodingStore::prune):
+        (WebCore::ImageDecodingStore::insertCacheInternal):
+        (WebCore::ImageDecodingStore::removeFromCacheInternal):
+        * platform/graphics/chromium/ImageDecodingStore.h:
+          Added isDiscardable() calls to check if a cache entry is discardable.
+        (WebCore::ImageDecodingStore::CacheEntry::CacheEntry):
+        (WebCore::ImageDecodingStore::CacheEntry::overwriteCachedImage):
+        (WebCore::ImageDecodingStore::CacheEntry::isDiscardable):
+        (CacheEntry):
+        * platform/graphics/chromium/ImageFrameGenerator.cpp:
+          Added some code to pass DiscardableMemoryAllocator to the image decoder.
+        (WebCore::ImageFrameGenerator::tryToScale):
+        (WebCore::ImageFrameGenerator::decode):
+        * platform/graphics/chromium/ImageFrameGenerator.h:
+          Added a new member variable of type DiscardableMemoryAllocator.
+        (ImageFrameGenerator):
+        * platform/image-decoders/ImageDecoder.h:
+          Added methods to pass Allocator to ImageFrame.
+        (ImageFrame):
+        (WebCore::ImageFrame::setMemoryAllocator):
+        (WebCore::ImageFrame::allocator):
+        (ImageDecoder):
+        (WebCore::ImageDecoder::setMemoryAllocator):
+        * platform/image-decoders/skia/ImageDecoderSkia.cpp:
+          Added code to allocate pixel memory using the allocator passed from the caller.
+        (WebCore::ImageFrame::ImageFrame):
+        (WebCore::ImageFrame::operator=):
+        (WebCore::ImageFrame::setSize):
+
 2013-01-28  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r140869.
diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi
index 62c3abe..fa350ea 100644
--- a/Source/WebCore/WebCore.gypi
+++ b/Source/WebCore/WebCore.gypi
@@ -3867,6 +3867,8 @@
             'platform/graphics/chromium/CrossProcessFontLoading.mm',
             'platform/graphics/chromium/DeferredImageDecoder.cpp',
             'platform/graphics/chromium/DeferredImageDecoder.h',
+            'platform/graphics/chromium/DiscardablePixelRef.cpp',
+            'platform/graphics/chromium/DiscardablePixelRef.h',
             'platform/graphics/chromium/DrawingBufferChromium.cpp',
             'platform/graphics/chromium/Extensions3DChromium.h',
             'platform/graphics/chromium/FontCacheAndroid.cpp',
diff --git a/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.cpp b/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.cpp
new file mode 100644
index 0000000..aeacf91
--- /dev/null
+++ b/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013 Google 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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.
+ */
+
+#include "config.h"
+#include "DiscardablePixelRef.h"
+
+#include <public/Platform.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+namespace {
+
+// URI label for a discardable SkPixelRef.
+const char labelDiscardable[] = "discardable";
+
+} // namespace
+
+
+bool DiscardablePixelRefAllocator::allocPixelRef(SkBitmap* dst, SkColorTable* ctable)
+{
+    Sk64 size = dst->getSize64();
+    if (size.isNeg() || !size.is32())
+        return false;
+
+    SkAutoTUnref<DiscardablePixelRef> pixelRef(new DiscardablePixelRef(ctable, adoptPtr(new SkMutex())));
+    if (pixelRef->allocAndLockDiscardableMemory(size.get32())) {
+        pixelRef->setURI(labelDiscardable);
+        dst->setPixelRef(pixelRef.get());
+        // This method is only called when a DiscardablePixelRef is created to back a SkBitmap.
+        // It is necessary to lock this SkBitmap to have a valid pointer to pixels. Otherwise,
+        // this SkBitmap could be assigned to another SkBitmap and locking/unlocking the other
+        // SkBitmap will make this one losing its pixels.
+        dst->lockPixels();
+        return true;
+    }
+
+    // Fallback to heap allocator if discardable memory is not available.
+    return dst->allocPixels(ctable);
+}
+
+DiscardablePixelRef::DiscardablePixelRef(SkColorTable* ctable, PassOwnPtr<SkMutex> mutex)
+    : SkPixelRef(mutex.get())
+    , m_colorTable(ctable)
+    , m_lockedMemory(0)
+    , m_mutex(mutex)
+{
+}
+
+DiscardablePixelRef::~DiscardablePixelRef()
+{
+    SkSafeUnref(m_colorTable);
+}
+
+bool DiscardablePixelRef::allocAndLockDiscardableMemory(size_t bytes)
+{
+    m_discardable = adoptPtr(WebKit::Platform::current()->allocateAndLockDiscardableMemory(bytes));
+    if (m_discardable) {
+        m_lockedMemory = m_discardable->data();
+        return true;
+    }
+    return false;
+}
+
+void* DiscardablePixelRef::onLockPixels(SkColorTable** ctable)
+{
+    if (!m_lockedMemory && m_discardable->lock())
+        m_lockedMemory = m_discardable->data();
+
+    *ctable = m_colorTable;
+    return m_lockedMemory;
+}
+
+void DiscardablePixelRef::onUnlockPixels()
+{
+    if (m_lockedMemory)
+        m_discardable->unlock();
+    m_lockedMemory = 0;
+}
+
+bool DiscardablePixelRef::isDiscardable(SkPixelRef* pixelRef)
+{
+    // FIXME: DEFINE_STATIC_LOCAL is not thread safe.
+    // ImageDecodingStore provides the synchronization for this.
+    DEFINE_STATIC_LOCAL(const SkString, discardable, (labelDiscardable));
+    return pixelRef && pixelRef->getURI() && discardable.equals(pixelRef->getURI());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.h b/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.h
new file mode 100644
index 0000000..79f1c6d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/chromium/DiscardablePixelRef.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Google 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 DiscardablePixelRef_h
+#define DiscardablePixelRef_h
+
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+
+#include <public/WebDiscardableMemory.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+// Class for allocating the DiscardablePixelRef object.
+class DiscardablePixelRefAllocator : public SkBitmap::Allocator {
+    // SkBitmap::Allocator implementation. The discardable memory allocated
+    // after this call is locked and will not be purged until next
+    // onUnlockPixels().
+    virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
+};
+
+// PixelRef object whose memory can be discarded when pixels are unlocked.
+class DiscardablePixelRef : public SkPixelRef {
+public:
+    DiscardablePixelRef(SkColorTable*, PassOwnPtr<SkMutex>);
+    ~DiscardablePixelRef();
+
+    static bool isDiscardable(SkPixelRef*);
+    bool allocAndLockDiscardableMemory(size_t);
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+protected:
+    // SkPixelRef implementation.
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+private:
+    SkColorTable* m_colorTable;
+    void* m_lockedMemory;
+    OwnPtr<WebKit::WebDiscardableMemory> m_discardable;
+    OwnPtr<SkMutex> m_mutex;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp b/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp
index 501cc55..fc9beb2 100644
--- a/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp
+++ b/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.cpp
@@ -82,28 +82,37 @@
     ASSERT(cachedImage);
 
     CacheEntry* cacheEntry = 0;
+    Vector<OwnPtr<CacheEntry> > cacheEntriesToDelete;
     {
         MutexLocker lock(m_mutex);
         CacheMap::iterator iter = m_cacheMap.find(std::make_pair(generator, scaledSize));
         if (iter == m_cacheMap.end())
             return false;
         cacheEntry = iter->value.get();
-        if (condition == CacheMustBeComplete && !cacheEntry->cachedImage()->isComplete())
+        ScaledImageFragment* image = cacheEntry->cachedImage();
+        if (condition == CacheMustBeComplete && !image->isComplete())
             return false;
 
         // Incomplete cache entry cannot be used more than once.
-        ASSERT(cacheEntry->cachedImage()->isComplete() || !cacheEntry->useCount());
+        ASSERT(image->isComplete() || !cacheEntry->useCount());
 
-        // Increment use count such that it doesn't get evicted.
-        cacheEntry->incrementUseCount();
+        image->bitmap().lockPixels();
+        if (image->bitmap().getPixels()) {
+            // Increment use count such that it doesn't get evicted.
+            cacheEntry->incrementUseCount();
 
-        // Complete cache entry doesn't have a decoder.
-        ASSERT(!cacheEntry->cachedImage()->isComplete() || !cacheEntry->cachedDecoder());
+            // Complete cache entry doesn't have a decoder.
+            ASSERT(!image->isComplete() || !cacheEntry->cachedDecoder());
 
-        if (decoder)
-            *decoder = cacheEntry->cachedDecoder();
-        *cachedImage = cacheEntry->cachedImage();
-        (*cachedImage)->bitmap().lockPixels();
+            if (decoder)
+                *decoder = cacheEntry->cachedDecoder();
+            *cachedImage = image;
+        } else {
+            image->bitmap().unlockPixels();
+            removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
+            removeFromCacheListInternal(cacheEntriesToDelete);
+            return false;
+        }
     }
 
     return true;
@@ -160,6 +169,11 @@
         ASSERT(cacheEntry->useCount() == 1);
         ASSERT(!cacheEntry->cachedImage()->isComplete());
 
+        bool isNewImageDiscardable = DiscardablePixelRef::isDiscardable(newImage->bitmap().pixelRef());
+        if (cacheEntry->isDiscardable() && !isNewImageDiscardable)
+            incrementMemoryUsage(cacheEntry->memoryUsageInBytes());
+        else if (!cacheEntry->isDiscardable() && isNewImageDiscardable)
+            decrementMemoryUsage(cacheEntry->memoryUsageInBytes());
         trash = cacheEntry->overwriteCachedImage(newImage);
         newCachedImage = cacheEntry->cachedImage();
         // Lock the underlying SkBitmap to prevent it from being purged.
@@ -230,7 +244,7 @@
 
         // Walk the list of cache entries starting from the least recently used
         // and then keep them for deletion later.
-        while (cacheEntry && m_memoryUsageInBytes > m_cacheLimitInBytes) {
+        while (cacheEntry && (m_memoryUsageInBytes > m_cacheLimitInBytes || !m_cacheLimitInBytes)) {
             // Cache is not used; Remove it.
             if (!cacheEntry->useCount())
                 removeFromCacheInternal(cacheEntry, &cacheEntriesToDelete);
@@ -244,7 +258,8 @@
 
 void ImageDecodingStore::insertCacheInternal(PassOwnPtr<CacheEntry> cacheEntry)
 {
-    incrementMemoryUsage(cacheEntry->memoryUsageInBytes());
+    if (!cacheEntry->isDiscardable())
+        incrementMemoryUsage(cacheEntry->memoryUsageInBytes());
 
     // m_orderedCacheList is used to support LRU operations to reorder cache
     // entries quickly.
@@ -262,7 +277,8 @@
 
 void ImageDecodingStore::removeFromCacheInternal(const CacheEntry* cacheEntry, Vector<OwnPtr<CacheEntry> >* deletionList)
 {
-    decrementMemoryUsage(cacheEntry->memoryUsageInBytes());
+    if (!cacheEntry->isDiscardable())
+        decrementMemoryUsage(cacheEntry->memoryUsageInBytes());
 
     // Remove from m_cacheMap.
     CacheIdentifier key = cacheEntry->cacheKey();
diff --git a/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h b/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h
index eaac43a..4c90c7d 100644
--- a/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h
+++ b/Source/WebCore/platform/graphics/chromium/ImageDecodingStore.h
@@ -26,6 +26,7 @@
 #ifndef ImageDecodingStore_h
 #define ImageDecodingStore_h
 
+#include "DiscardablePixelRef.h"
 #include "ImageDecoder.h"
 #include "ScaledImageFragment.h"
 #include "SkTypes.h"
@@ -92,6 +93,7 @@
             , m_cachedImage(image)
             , m_cachedDecoder(decoder)
             , m_useCount(count)
+            , m_isDiscardable(DiscardablePixelRef::isDiscardable(m_cachedImage->bitmap().pixelRef()))
         {
         }
 
@@ -106,6 +108,7 @@
         ImageDecoder* cachedDecoder() const { return m_cachedDecoder.get(); }
         PassOwnPtr<ImageDecoder> overwriteCachedImage(PassOwnPtr<ScaledImageFragment> image)
         {
+            m_isDiscardable = DiscardablePixelRef::isDiscardable(image->bitmap().pixelRef());
             m_cachedImage = image;
             if (m_cachedImage->isComplete())
                 return m_cachedDecoder.release();
@@ -114,6 +117,7 @@
         int useCount() const { return m_useCount; }
         void incrementUseCount() { ++m_useCount; }
         void decrementUseCount() { --m_useCount; ASSERT(m_useCount >= 0); }
+        bool isDiscardable() const { return m_isDiscardable; }
 
         // FIXME: getSafeSize() returns size in bytes truncated to a 32-bits integer.
         //        Find a way to get the size in 64-bits.
@@ -126,6 +130,7 @@
         OwnPtr<ScaledImageFragment> m_cachedImage;
         OwnPtr<ImageDecoder> m_cachedDecoder;
         int m_useCount;
+        bool m_isDiscardable;
     };
 
     ImageDecodingStore();
diff --git a/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp b/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp
index 3c98df7..93f95ef 100644
--- a/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp
+++ b/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.cpp
@@ -112,8 +112,13 @@
     if (!fullSizeImage && !ImageDecodingStore::instance()->lockCache(this, m_fullSize, ImageDecodingStore::CacheMustBeComplete, &fullSizeImage))
         return 0;
 
-    SkBitmap scaledBitmap = skia::ImageOperations::Resize(
-        fullSizeImage->bitmap(), resizeMethod(), scaledSize.width(), scaledSize.height());
+    DiscardablePixelRefAllocator allocator;
+    // This call allocates the DiscardablePixelRef and lock/unlocks it
+    // afterwards. So the memory allocated to the scaledBitmap can be
+    // discarded after this call. Need to lock the scaledBitmap and
+    // check the pixels before using it next time.
+    SkBitmap scaledBitmap = skia::ImageOperations::Resize(fullSizeImage->bitmap(), resizeMethod(), scaledSize.width(), scaledSize.height(), &allocator);
+
     OwnPtr<ScaledImageFragment> scaledImage = ScaledImageFragment::create(scaledSize, scaledBitmap, fullSizeImage->isComplete());
 
     ImageDecodingStore::instance()->unlockCache(this, fullSizeImage);
@@ -189,7 +194,15 @@
             return nullptr;
     }
 
+    // TODO: this is very ugly. We need to refactor the way how we can pass a
+    // memory allocator to image decoders.
+    (*decoder)->setMemoryAllocator(&m_allocator);
     (*decoder)->setData(data, allDataReceived);
+    // If this call returns a newly allocated DiscardablePixelRef, then
+    // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked.
+    // They will be unlocked when ImageDecoder is destroyed since ImageDecoder
+    // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the
+    // ImageDecodingStore while locked.
     ImageFrame* frame = (*decoder)->frameBufferAtIndex(0);
     (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.
 
diff --git a/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.h b/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.h
index 1c0037c..550a070 100644
--- a/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.h
+++ b/Source/WebCore/platform/graphics/chromium/ImageFrameGenerator.h
@@ -26,6 +26,7 @@
 #ifndef ImageFrameGenerator_h
 #define ImageFrameGenerator_h
 
+#include "DiscardablePixelRef.h"
 #include "SkTypes.h"
 #include "SkBitmap.h"
 #include "SkSize.h"
@@ -79,6 +80,7 @@
     SkISize m_fullSize;
     ThreadSafeDataTransport m_data;
     bool m_decodeFailedAndEmpty;
+    DiscardablePixelRefAllocator m_allocator;
 
     OwnPtr<ImageDecoderFactory> m_imageDecoderFactory;
 
diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.h b/Source/WebCore/platform/image-decoders/ImageDecoder.h
index 4c1c3de..2e0b57f 100644
--- a/Source/WebCore/platform/image-decoders/ImageDecoder.h
+++ b/Source/WebCore/platform/image-decoders/ImageDecoder.h
@@ -157,6 +157,13 @@
         {
             return m_bitmap.bitmap();
         }
+
+        void setMemoryAllocator(SkBitmap::Allocator* allocator)
+        {
+            m_allocator = allocator;
+        }
+
+        SkBitmap::Allocator* allocator() const { return m_allocator; }
 #endif
 
         // Use fix point multiplier instead of integer division or floating point math.
@@ -212,6 +219,7 @@
 
 #if USE(SKIA)
         NativeImageSkia m_bitmap;
+        SkBitmap::Allocator* m_allocator;
 #else
         Vector<PixelData> m_backingStore;
         PixelData* m_bytes; // The memory is backed by m_backingStore.
@@ -406,6 +414,15 @@
 
         virtual void reportMemoryUsage(MemoryObjectInfo*) const;
 
+#if USE(SKIA)
+        virtual void setMemoryAllocator(SkBitmap::Allocator* allocator)
+        {
+            // FIXME: this doesn't work for images with multiple frames.
+            if (m_frameBufferCache.isEmpty())
+                m_frameBufferCache.resize(1);
+            m_frameBufferCache[0].setMemoryAllocator(allocator);
+        }
+#endif
     protected:
         void prepareScaleDataIfNecessary();
         int upperBoundScaledX(int origX, int searchStart = 0);
diff --git a/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp
index b238216..774701b 100644
--- a/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp
+++ b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp
@@ -32,7 +32,8 @@
 namespace WebCore {
 
 ImageFrame::ImageFrame()
-    : m_hasAlpha(false)
+    : m_allocator(0)
+    , m_hasAlpha(false)
     , m_status(FrameEmpty)
     , m_duration(0)
     , m_disposalMethod(DisposeNotSpecified)
@@ -49,6 +50,7 @@
     // Keep the pixels locked since we will be writing directly into the
     // bitmap throughout this object's lifetime.
     m_bitmap.bitmap().lockPixels();
+    setMemoryAllocator(other.allocator());
     setOriginalFrameRect(other.originalFrameRect());
     setStatus(other.status());
     setDuration(other.duration());
@@ -93,7 +95,7 @@
     ASSERT(!width() && !height());
 
     m_bitmap.bitmap().setConfig(SkBitmap::kARGB_8888_Config, newWidth, newHeight);
-    if (!m_bitmap.bitmap().allocPixels())
+    if (!m_bitmap.bitmap().allocPixels(m_allocator, 0))
         return false;
 
     zeroFillPixelData();
diff --git a/Source/WebKit/chromium/ChangeLog b/Source/WebKit/chromium/ChangeLog
index 66407a1..ae6c290 100644
--- a/Source/WebKit/chromium/ChangeLog
+++ b/Source/WebKit/chromium/ChangeLog
@@ -1,3 +1,35 @@
+2013-01-28  Min Qin  <qinmin@chromium.org>
+
+        adding support for DiscardablePixelRef for caching lazily decoded images
+        https://bugs.webkit.org/show_bug.cgi?id=106842
+
+        Reviewed by Stephen White.
+
+        Adding new tests for ImageDecodingStore
+
+        * WebKit.gypi:
+        * tests/ImageDecodingStoreTest.cpp:
+        (WebCore::ImageDecodingStoreTest::createCompleteImage):
+        (WebCore::ImageDecodingStoreTest::createIncompleteImage):
+        (WebCore::TEST_F):
+        (WebCore):
+        * tests/MockDiscardablePixelRef.h: Added.
+        (WebCore):
+        (MockDiscardablePixelRef):
+        (WebCore::MockDiscardablePixelRef::MockDiscardablePixelRef):
+        (WebCore::MockDiscardablePixelRef::~MockDiscardablePixelRef):
+        (WebCore::MockDiscardablePixelRef::discard):
+        (WebCore::MockDiscardablePixelRef::onLockPixels):
+        (WebCore::MockDiscardablePixelRef::onUnlockPixels):
+
+2013-01-28  Stephen Chenney  <schenney@chromium.org>
+
+        [Chromium] Fix the build.
+
+        Unreviewed build fix.
+
+        * src/AssertMatchingEnums.cpp: Move the include to the right place and remove the bad directory prefix.
+
 2013-01-28  Dan Alcantara  <dfalcantara@chromium.org>
 
         [Chromium, Mobile] Do not show disambiguation pop up in mobile sites
diff --git a/Source/WebKit/chromium/WebKit.gypi b/Source/WebKit/chromium/WebKit.gypi
index 2349367..d581128 100644
--- a/Source/WebKit/chromium/WebKit.gypi
+++ b/Source/WebKit/chromium/WebKit.gypi
@@ -83,6 +83,7 @@
             'tests/ImageDecodingStoreTest.cpp',
             'tests/ImageFrameGeneratorTest.cpp',
             'tests/ImageLayerChromiumTest.cpp',
+            'tests/MockDiscardablePixelRef.h',
             'tests/MockImageDecoder.h',
             'tests/KeyboardTest.cpp',
             'tests/KURLTest.cpp',
diff --git a/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp b/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp
index 83ea842..3a41103 100644
--- a/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp
+++ b/Source/WebKit/chromium/tests/ImageDecodingStoreTest.cpp
@@ -28,6 +28,7 @@
 #include "ImageDecodingStore.h"
 
 #include "ImageFrameGenerator.h"
+#include "MockDiscardablePixelRef.h"
 #include "MockImageDecoder.h"
 #include "SharedBuffer.h"
 #include <gtest/gtest.h>
@@ -68,19 +69,25 @@
     }
 
 protected:
-    PassOwnPtr<ScaledImageFragment> createCompleteImage(const SkISize& size)
+    PassOwnPtr<ScaledImageFragment> createCompleteImage(const SkISize& size, bool discardable = false)
     {
         SkBitmap bitmap;
         bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
-        bitmap.allocPixels();
+        if (!discardable)
+            bitmap.allocPixels();
+        else
+            bitmap.setPixelRef(new MockDiscardablePixelRef())->unref();
         return ScaledImageFragment::create(size, bitmap, true);
     }
 
-    PassOwnPtr<ScaledImageFragment> createIncompleteImage(const SkISize& size)
+    PassOwnPtr<ScaledImageFragment> createIncompleteImage(const SkISize& size, bool discardable = false)
     {
         SkBitmap bitmap;
         bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
-        bitmap.allocPixels();
+        if (!discardable)
+            bitmap.allocPixels();
+        else
+            bitmap.setPixelRef(new MockDiscardablePixelRef())->unref();
         return ScaledImageFragment::create(size, bitmap, false);
     }
 
@@ -281,4 +288,59 @@
     ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
 }
 
+TEST_F(ImageDecodingStoreTest, lockCacheFailedAfterMemoryDiscarded)
+{
+    const ScaledImageFragment* cachedImage = ImageDecodingStore::instance()->insertAndLockCache(
+        m_generator.get(), createCompleteImage(SkISize::Make(1, 1), true), MockImageDecoder::create(this));
+    unlockCache(cachedImage);
+    MockDiscardablePixelRef* pixelRef = static_cast<MockDiscardablePixelRef*>(cachedImage->bitmap().pixelRef());
+    pixelRef->discard();
+    EXPECT_EQ(0, lockCache(SkISize::Make(1, 1)));
+    EXPECT_EQ(0u, ImageDecodingStore::instance()->cacheEntries());
+}
+
+TEST_F(ImageDecodingStoreTest, overwriteNonDiscardableCacheWithDiscardable)
+{
+
+    const SkISize size = SkISize::Make(1, 1);
+    const ScaledImageFragment* cachedImage = ImageDecodingStore::instance()->insertAndLockCache(
+        m_generator.get(), createIncompleteImage(size), MockImageDecoder::create(this));
+    ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+    EXPECT_LT(0u, ImageDecodingStore::instance()->memoryUsageInBytes());
+
+    ImageDecoder* decoder = 0;
+    EXPECT_TRUE(ImageDecodingStore::instance()->lockCache(m_generator.get(), size, ImageDecodingStore::CacheCanBeIncomplete, &cachedImage, &decoder));
+    EXPECT_FALSE(cachedImage->isComplete());
+
+    cachedImage = ImageDecodingStore::instance()->overwriteAndLockCache(
+        m_generator.get(), cachedImage, createCompleteImage(size, true));
+    EXPECT_TRUE(cachedImage->isComplete());
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+    EXPECT_EQ(0u, ImageDecodingStore::instance()->memoryUsageInBytes());
+    ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+}
+
+TEST_F(ImageDecodingStoreTest, overwriteDiscardableCacheWithNonDiscardable)
+{
+
+    const SkISize size = SkISize::Make(1, 1);
+    const ScaledImageFragment* cachedImage = ImageDecodingStore::instance()->insertAndLockCache(
+        m_generator.get(), createIncompleteImage(size, true), MockImageDecoder::create(this));
+    ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+    EXPECT_EQ(0u, ImageDecodingStore::instance()->memoryUsageInBytes());
+
+    ImageDecoder* decoder = 0;
+    EXPECT_TRUE(ImageDecodingStore::instance()->lockCache(m_generator.get(), size, ImageDecodingStore::CacheCanBeIncomplete, &cachedImage, &decoder));
+    EXPECT_FALSE(cachedImage->isComplete());
+
+    cachedImage = ImageDecodingStore::instance()->overwriteAndLockCache(
+        m_generator.get(), cachedImage, createCompleteImage(size));
+    EXPECT_TRUE(cachedImage->isComplete());
+    EXPECT_EQ(1u, ImageDecodingStore::instance()->cacheEntries());
+    EXPECT_LT(0u, ImageDecodingStore::instance()->memoryUsageInBytes());
+    ImageDecodingStore::instance()->unlockCache(m_generator.get(), cachedImage);
+}
+
 } // namespace
diff --git a/Source/WebKit/chromium/tests/MockDiscardablePixelRef.h b/Source/WebKit/chromium/tests/MockDiscardablePixelRef.h
new file mode 100644
index 0000000..c1bd7fe
--- /dev/null
+++ b/Source/WebKit/chromium/tests/MockDiscardablePixelRef.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Google 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 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 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 MockDiscardablePixelRef_h
+
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+
+namespace WebCore {
+
+class MockDiscardablePixelRef : public SkPixelRef {
+public:
+    MockDiscardablePixelRef() : discarded(false) { setURI("discardable"); }
+    ~MockDiscardablePixelRef() { }
+
+    void discard()
+    {
+        ASSERT(!m_lockedMemory);
+        discarded = true;
+    }
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+protected:
+    // SkPixelRef implementation.
+    virtual void* onLockPixels(SkColorTable**)
+    {
+        if (discarded)
+            return 0;
+        m_lockedMemory = &discarded;
+        return m_lockedMemory;
+    }
+
+    virtual void onUnlockPixels()
+    {
+        m_lockedMemory = 0;
+    }
+
+private:
+    void* m_lockedMemory;
+    bool discarded;
+};
+
+} // namespace WebCore
+
+#endif // MockDiscardablePixelRef_h