1/* 2 * Copyright (c) 2013, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/frame/ImageBitmap.h" 33 34#include "SkPixelRef.h" // FIXME: qualify this skia header file. 35#include "core/dom/Document.h" 36#include "core/fetch/ImageResource.h" 37#include "core/fetch/MemoryCache.h" 38#include "core/fetch/MockImageResourceClient.h" 39#include "core/fetch/ResourcePtr.h" 40#include "core/html/HTMLCanvasElement.h" 41#include "core/html/HTMLImageElement.h" 42#include "core/html/canvas/CanvasRenderingContext2D.h" 43#include "platform/graphics/BitmapImage.h" 44#include "platform/graphics/skia/NativeImageSkia.h" 45#include "platform/heap/Handle.h" 46#include "platform/network/ResourceRequest.h" 47#include "wtf/OwnPtr.h" 48 49#include <gtest/gtest.h> 50 51namespace blink { 52 53class ImageBitmapTest : public ::testing::Test { 54protected: 55 virtual void SetUp() 56 { 57 m_bitmap.allocN32Pixels(10, 10); 58 m_bitmap.eraseColor(0xFFFFFFFF); 59 60 m_bitmap2.allocN32Pixels(5, 5); 61 m_bitmap2.eraseColor(0xAAAAAAAA); 62 63 // Save the global memory cache to restore it upon teardown. 64 m_globalMemoryCache = replaceMemoryCacheForTesting(MemoryCache::create()); 65 } 66 virtual void TearDown() 67 { 68 // Garbage collection is required prior to switching out the 69 // test's memory cache; image resources are released, evicting 70 // them from the cache. 71 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); 72 73 replaceMemoryCacheForTesting(m_globalMemoryCache.release()); 74 } 75 76 SkBitmap m_bitmap, m_bitmap2; 77 OwnPtrWillBePersistent<MemoryCache> m_globalMemoryCache; 78}; 79 80// Verifies that the image resource held by an ImageBitmap is the same as the 81// one held by the HTMLImageElement. 82TEST_F(ImageBitmapTest, ImageResourceConsistency) 83{ 84 RefPtrWillBeRawPtr<HTMLImageElement> imageElement = HTMLImageElement::create(*Document::create().get()); 85 imageElement->setImageResource(new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get())); 86 87 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageElement.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 88 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageElement.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width() / 2, m_bitmap.height() / 2)); 89 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageElement.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 90 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageElement.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); 91 92 ASSERT_EQ(imageBitmapNoCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 93 ASSERT_EQ(imageBitmapInteriorCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 94 ASSERT_EQ(imageBitmapExteriorCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 95 96 RefPtr<Image> emptyImage = imageBitmapOutsideCrop->bitmapImage(); 97 ASSERT_NE(emptyImage.get(), imageElement->cachedImage()->image()); 98} 99 100// Verifies that HTMLImageElements are given an elevated CacheLiveResourcePriority when used to construct an ImageBitmap. 101// ImageBitmaps that have crop rects outside of the bounds of the HTMLImageElement do not have elevated CacheLiveResourcePriority. 102TEST_F(ImageBitmapTest, ImageBitmapLiveResourcePriority) 103{ 104 RefPtrWillBePersistent<HTMLImageElement> imageNoCrop = HTMLImageElement::create(*Document::create().get()); 105 ResourcePtr<ImageResource> cachedImageNoCrop = new ImageResource(ResourceRequest("http://foo.com/1"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 106 imageNoCrop->setImageResource(cachedImageNoCrop.get()); 107 108 RefPtrWillBePersistent<HTMLImageElement> imageInteriorCrop = HTMLImageElement::create(*Document::create().get()); 109 ResourcePtr<ImageResource> cachedImageInteriorCrop = new ImageResource(ResourceRequest("http://foo.com/2"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 110 imageInteriorCrop->setImageResource(cachedImageInteriorCrop.get()); 111 112 RefPtrWillBePersistent<HTMLImageElement> imageExteriorCrop = HTMLImageElement::create(*Document::create().get()); 113 ResourcePtr<ImageResource> cachedImageExteriorCrop = new ImageResource(ResourceRequest("http://foo.com/3"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 114 imageExteriorCrop->setImageResource(cachedImageExteriorCrop.get()); 115 116 RefPtrWillBePersistent<HTMLImageElement> imageOutsideCrop = HTMLImageElement::create(*Document::create().get()); 117 ResourcePtr<ImageResource> cachedImageOutsideCrop = new ImageResource(ResourceRequest("http://foo.com/4"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 118 imageOutsideCrop->setImageResource(cachedImageOutsideCrop.get()); 119 120 MockImageResourceClient mockClient1, mockClient2, mockClient3, mockClient4; 121 cachedImageNoCrop->addClient(&mockClient1); 122 cachedImageInteriorCrop->addClient(&mockClient2); 123 cachedImageExteriorCrop->addClient(&mockClient3); 124 cachedImageOutsideCrop->addClient(&mockClient4); 125 126 memoryCache()->add(cachedImageNoCrop.get()); 127 memoryCache()->add(cachedImageInteriorCrop.get()); 128 memoryCache()->add(cachedImageExteriorCrop.get()); 129 memoryCache()->add(cachedImageOutsideCrop.get()); 130 memoryCache()->updateDecodedResource(cachedImageNoCrop.get(), UpdateForPropertyChange); 131 memoryCache()->updateDecodedResource(cachedImageInteriorCrop.get(), UpdateForPropertyChange); 132 memoryCache()->updateDecodedResource(cachedImageExteriorCrop.get(), UpdateForPropertyChange); 133 memoryCache()->updateDecodedResource(cachedImageOutsideCrop.get(), UpdateForPropertyChange); 134 135 // HTMLImageElements should default to CacheLiveResourcePriorityLow. 136 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 137 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 138 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 139 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 140 141 RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 142 { 143 RefPtrWillBePersistent<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageNoCrop.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 144 RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop2 = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 145 RefPtrWillBePersistent<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageExteriorCrop.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 146 RefPtrWillBePersistent<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageOutsideCrop.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); 147 148 // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh. 149 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 150 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 151 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 152 153 // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority. 154 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 155 } 156 // Force a garbage collection to sweep out the local ImageBitmaps. 157 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); 158 159 // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image. 160 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 161 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 162 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 163 164 // There is still an ImageBitmap that references this image. 165 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 166 imageBitmapInteriorCrop = nullptr; 167 168 cachedImageNoCrop->removeClient(&mockClient1); 169 cachedImageInteriorCrop->removeClient(&mockClient2); 170 cachedImageExteriorCrop->removeClient(&mockClient3); 171 cachedImageOutsideCrop->removeClient(&mockClient4); 172} 173 174// Verifies that ImageBitmaps constructed from HTMLImageElements hold a reference to the original Image if the HTMLImageElement src is changed. 175TEST_F(ImageBitmapTest, ImageBitmapSourceChanged) 176{ 177 RefPtrWillBeRawPtr<HTMLImageElement> image = HTMLImageElement::create(*Document::create().get()); 178 ResourcePtr<ImageResource> originalImageResource = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 179 image->setImageResource(originalImageResource.get()); 180 181 RefPtrWillBeRawPtr<ImageBitmap> imageBitmap = ImageBitmap::create(image.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 182 ASSERT_EQ(imageBitmap->bitmapImage().get(), originalImageResource->image()); 183 184 ResourcePtr<ImageResource> newImageResource = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap2)).get()); 185 image->setImageResource(newImageResource.get()); 186 187 // The ImageBitmap should contain the same data as the original cached image but should no longer hold a reference. 188 ASSERT_NE(imageBitmap->bitmapImage().get(), originalImageResource->image()); 189 ASSERT_EQ(imageBitmap->bitmapImage()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels(), 190 originalImageResource->image()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels()); 191 192 ASSERT_NE(imageBitmap->bitmapImage().get(), newImageResource->image()); 193 ASSERT_NE(imageBitmap->bitmapImage()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels(), 194 newImageResource->image()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels()); 195} 196 197// Verifies that ImageBitmaps constructed from ImageBitmaps hold onto their own Image. 198TEST_F(ImageBitmapTest, ImageResourceLifetime) 199{ 200 RefPtrWillBeRawPtr<HTMLCanvasElement> canvasElement = HTMLCanvasElement::create(*Document::create().get()); 201 canvasElement->setHeight(40); 202 canvasElement->setWidth(40); 203 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapDerived = nullptr; 204 { 205 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapFromCanvas = ImageBitmap::create(canvasElement.get(), IntRect(0, 0, canvasElement->width(), canvasElement->height())); 206 imageBitmapDerived = ImageBitmap::create(imageBitmapFromCanvas.get(), IntRect(0, 0, 20, 20)); 207 } 208 CanvasRenderingContext* context = canvasElement->getContext("2d"); 209 TrackExceptionState exceptionState; 210 toCanvasRenderingContext2D(context)->drawImage(imageBitmapDerived.get(), 0, 0, exceptionState); 211} 212 213} // namespace 214