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 51 namespace WebCore { 52 53 class ImageBitmapTest : public ::testing::Test { 54 protected: 55 virtual void SetUp() 56 { 57 ASSERT_TRUE(m_bitmap.allocN32Pixels(10, 10)); 58 m_bitmap.eraseColor(0xFFFFFFFF); 59 60 ASSERT_TRUE(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 = adoptPtr(memoryCache()); 65 // Create the test memory cache instance and hook it in. 66 m_testingMemoryCache = adoptPtr(new MemoryCache()); 67 setMemoryCacheForTesting(m_testingMemoryCache.leakPtr()); 68 } 69 virtual void TearDown() 70 { 71 // Garbage collection is required prior to switching out the 72 // test's memory cache; image resources are released, evicting 73 // them from the cache. 74 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); 75 76 // Regain the ownership of testing memory cache, so that it will be 77 // destroyed. 78 m_testingMemoryCache = adoptPtr(memoryCache()); 79 // Yield the ownership of the global memory cache back. 80 setMemoryCacheForTesting(m_globalMemoryCache.leakPtr()); 81 } 82 83 SkBitmap m_bitmap, m_bitmap2; 84 OwnPtr<MemoryCache> m_testingMemoryCache; 85 OwnPtr<MemoryCache> m_globalMemoryCache; 86 }; 87 88 // Verifies that the image resource held by an ImageBitmap is the same as the 89 // one held by the HTMLImageElement. 90 TEST_F(ImageBitmapTest, ImageResourceConsistency) 91 { 92 RefPtrWillBeRawPtr<HTMLImageElement> imageElement = HTMLImageElement::create(*Document::create().get()); 93 imageElement->setImageResource(new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get())); 94 95 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageElement.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 96 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageElement.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width() / 2, m_bitmap.height() / 2)); 97 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageElement.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 98 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageElement.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); 99 100 ASSERT_EQ(imageBitmapNoCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 101 ASSERT_EQ(imageBitmapInteriorCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 102 ASSERT_EQ(imageBitmapExteriorCrop->bitmapImage().get(), imageElement->cachedImage()->image()); 103 104 RefPtr<Image> emptyImage = imageBitmapOutsideCrop->bitmapImage(); 105 ASSERT_NE(emptyImage.get(), imageElement->cachedImage()->image()); 106 } 107 108 // Verifies that HTMLImageElements are given an elevated CacheLiveResourcePriority when used to construct an ImageBitmap. 109 // ImageBitmaps that have crop rects outside of the bounds of the HTMLImageElement do not have elevated CacheLiveResourcePriority. 110 TEST_F(ImageBitmapTest, ImageBitmapLiveResourcePriority) 111 { 112 RefPtrWillBePersistent<HTMLImageElement> imageNoCrop = HTMLImageElement::create(*Document::create().get()); 113 ResourcePtr<ImageResource> cachedImageNoCrop = new ImageResource(ResourceRequest("http://foo.com/1"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 114 imageNoCrop->setImageResource(cachedImageNoCrop.get()); 115 116 RefPtrWillBePersistent<HTMLImageElement> imageInteriorCrop = HTMLImageElement::create(*Document::create().get()); 117 ResourcePtr<ImageResource> cachedImageInteriorCrop = new ImageResource(ResourceRequest("http://foo.com/2"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 118 imageInteriorCrop->setImageResource(cachedImageInteriorCrop.get()); 119 120 RefPtrWillBePersistent<HTMLImageElement> imageExteriorCrop = HTMLImageElement::create(*Document::create().get()); 121 ResourcePtr<ImageResource> cachedImageExteriorCrop = new ImageResource(ResourceRequest("http://foo.com/3"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 122 imageExteriorCrop->setImageResource(cachedImageExteriorCrop.get()); 123 124 RefPtrWillBePersistent<HTMLImageElement> imageOutsideCrop = HTMLImageElement::create(*Document::create().get()); 125 ResourcePtr<ImageResource> cachedImageOutsideCrop = new ImageResource(ResourceRequest("http://foo.com/4"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 126 imageOutsideCrop->setImageResource(cachedImageOutsideCrop.get()); 127 128 MockImageResourceClient mockClient1, mockClient2, mockClient3, mockClient4; 129 cachedImageNoCrop->addClient(&mockClient1); 130 cachedImageInteriorCrop->addClient(&mockClient2); 131 cachedImageExteriorCrop->addClient(&mockClient3); 132 cachedImageOutsideCrop->addClient(&mockClient4); 133 134 memoryCache()->add(cachedImageNoCrop.get()); 135 memoryCache()->add(cachedImageInteriorCrop.get()); 136 memoryCache()->add(cachedImageExteriorCrop.get()); 137 memoryCache()->add(cachedImageOutsideCrop.get()); 138 memoryCache()->updateDecodedResource(cachedImageNoCrop.get(), UpdateForPropertyChange); 139 memoryCache()->updateDecodedResource(cachedImageInteriorCrop.get(), UpdateForPropertyChange); 140 memoryCache()->updateDecodedResource(cachedImageExteriorCrop.get(), UpdateForPropertyChange); 141 memoryCache()->updateDecodedResource(cachedImageOutsideCrop.get(), UpdateForPropertyChange); 142 143 // HTMLImageElements should default to CacheLiveResourcePriorityLow. 144 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 145 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 146 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 147 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 148 149 RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 150 { 151 RefPtrWillBePersistent<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageNoCrop.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 152 RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop2 = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 153 RefPtrWillBePersistent<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageExteriorCrop.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); 154 RefPtrWillBePersistent<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageOutsideCrop.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); 155 156 // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh. 157 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 158 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 159 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 160 161 // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority. 162 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 163 } 164 // Force a garbage collection to sweep out the local ImageBitmaps. 165 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); 166 167 // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image. 168 ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 169 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 170 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); 171 172 // There is still an ImageBitmap that references this image. 173 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); 174 imageBitmapInteriorCrop = nullptr; 175 } 176 177 // Verifies that ImageBitmaps constructed from HTMLImageElements hold a reference to the original Image if the HTMLImageElement src is changed. 178 TEST_F(ImageBitmapTest, ImageBitmapSourceChanged) 179 { 180 RefPtrWillBeRawPtr<HTMLImageElement> image = HTMLImageElement::create(*Document::create().get()); 181 ResourcePtr<ImageResource> originalImageResource = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); 182 image->setImageResource(originalImageResource.get()); 183 184 RefPtrWillBeRawPtr<ImageBitmap> imageBitmap = ImageBitmap::create(image.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); 185 ASSERT_EQ(imageBitmap->bitmapImage().get(), originalImageResource->image()); 186 187 ResourcePtr<ImageResource> newImageResource = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap2)).get()); 188 image->setImageResource(newImageResource.get()); 189 190 // The ImageBitmap should contain the same data as the original cached image but should no longer hold a reference. 191 ASSERT_NE(imageBitmap->bitmapImage().get(), originalImageResource->image()); 192 ASSERT_EQ(imageBitmap->bitmapImage()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels(), 193 originalImageResource->image()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels()); 194 195 ASSERT_NE(imageBitmap->bitmapImage().get(), newImageResource->image()); 196 ASSERT_NE(imageBitmap->bitmapImage()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels(), 197 newImageResource->image()->nativeImageForCurrentFrame()->bitmap().pixelRef()->pixels()); 198 } 199 200 // Verifies that ImageBitmaps constructed from ImageBitmaps hold onto their own Image. 201 TEST_F(ImageBitmapTest, ImageResourceLifetime) 202 { 203 RefPtrWillBeRawPtr<HTMLCanvasElement> canvasElement = HTMLCanvasElement::create(*Document::create().get()); 204 canvasElement->setHeight(40); 205 canvasElement->setWidth(40); 206 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapDerived = nullptr; 207 { 208 RefPtrWillBeRawPtr<ImageBitmap> imageBitmapFromCanvas = ImageBitmap::create(canvasElement.get(), IntRect(0, 0, canvasElement->width(), canvasElement->height())); 209 imageBitmapDerived = ImageBitmap::create(imageBitmapFromCanvas.get(), IntRect(0, 0, 20, 20)); 210 } 211 CanvasRenderingContext* context = canvasElement->getContext("2d"); 212 TrackExceptionState exceptionState; 213 toCanvasRenderingContext2D(context)->drawImage(imageBitmapDerived.get(), 0, 0, exceptionState); 214 } 215 216 } // namespace 217