2 * Copyright (c) 2013, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/frame/ImageBitmap.h"
34 #include "SkPixelRef.h" // FIXME: qualify this skia header file.
35 #include "bindings/core/v8/UnionTypesCore.h"
36 #include "core/dom/Document.h"
37 #include "core/fetch/ImageResource.h"
38 #include "core/fetch/MemoryCache.h"
39 #include "core/fetch/MockImageResourceClient.h"
40 #include "core/fetch/ResourcePtr.h"
41 #include "core/html/HTMLCanvasElement.h"
42 #include "core/html/HTMLImageElement.h"
43 #include "core/html/HTMLVideoElement.h"
44 #include "platform/graphics/StaticBitmapImage.h"
45 #include "platform/heap/Handle.h"
46 #include "platform/network/ResourceRequest.h"
47 #include "third_party/skia/include/core/SkImage.h"
48 #include "third_party/skia/include/core/SkSurface.h"
49 #include "wtf/OwnPtr.h"
51 #include <gtest/gtest.h>
55 class ImageBitmapTest
: public ::testing::Test
{
59 RefPtr
<SkSurface
> surface
= adoptRef(SkSurface::NewRasterN32Premul(10, 10));
60 surface
->getCanvas()->clear(0xFFFFFFFF);
61 m_image
= adoptRef(surface
->newImageSnapshot());
63 RefPtr
<SkSurface
> surface2
= adoptRef(SkSurface::NewRasterN32Premul(5, 5));
64 surface2
->getCanvas()->clear(0xAAAAAAAA);
65 m_image2
= adoptRef(surface2
->newImageSnapshot());
67 // Save the global memory cache to restore it upon teardown.
68 m_globalMemoryCache
= replaceMemoryCacheForTesting(MemoryCache::create());
70 virtual void TearDown()
72 // Garbage collection is required prior to switching out the
73 // test's memory cache; image resources are released, evicting
74 // them from the cache.
75 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack
, ThreadState::GCWithSweep
, Heap::ForcedGC
);
77 replaceMemoryCacheForTesting(m_globalMemoryCache
.release());
80 RefPtr
<SkImage
> m_image
, m_image2
;
81 Persistent
<MemoryCache
> m_globalMemoryCache
;
84 // Verifies that the image resource held by an ImageBitmap is the same as the
85 // one held by the HTMLImageElement.
86 TEST_F(ImageBitmapTest
, ImageResourceConsistency
)
88 RefPtrWillBeRawPtr
<HTMLImageElement
> imageElement
= HTMLImageElement::create(*Document::create().get());
89 imageElement
->setImageResource(new ImageResource(StaticBitmapImage::create(m_image
).get()));
91 RefPtrWillBeRawPtr
<ImageBitmap
> imageBitmapNoCrop
= ImageBitmap::create(imageElement
.get(),
92 IntRect(0, 0, m_image
->width(), m_image
->height()));
93 RefPtrWillBeRawPtr
<ImageBitmap
> imageBitmapInteriorCrop
= ImageBitmap::create(imageElement
.get(),
94 IntRect(m_image
->width() / 2, m_image
->height() / 2, m_image
->width() / 2, m_image
->height() / 2));
95 RefPtrWillBeRawPtr
<ImageBitmap
> imageBitmapExteriorCrop
= ImageBitmap::create(imageElement
.get(),
96 IntRect(-m_image
->width() / 2, -m_image
->height() / 2, m_image
->width(), m_image
->height()));
97 RefPtrWillBeRawPtr
<ImageBitmap
> imageBitmapOutsideCrop
= ImageBitmap::create(imageElement
.get(),
98 IntRect(-m_image
->width(), -m_image
->height(), m_image
->width(), m_image
->height()));
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());
104 RefPtr
<Image
> emptyImage
= imageBitmapOutsideCrop
->bitmapImage();
105 ASSERT_NE(emptyImage
.get(), imageElement
->cachedImage()->image());
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
)
112 RefPtrWillBePersistent
<HTMLImageElement
> imageNoCrop
= HTMLImageElement::create(*Document::create().get());
113 ResourcePtr
<ImageResource
> cachedImageNoCrop
= new ImageResource(ResourceRequest("http://foo.com/1"),
114 StaticBitmapImage::create(m_image
).get());
115 imageNoCrop
->setImageResource(cachedImageNoCrop
.get());
117 RefPtrWillBePersistent
<HTMLImageElement
> imageInteriorCrop
= HTMLImageElement::create(*Document::create().get());
118 ResourcePtr
<ImageResource
> cachedImageInteriorCrop
= new ImageResource(ResourceRequest("http://foo.com/2"),
119 StaticBitmapImage::create(m_image
).get());
120 imageInteriorCrop
->setImageResource(cachedImageInteriorCrop
.get());
122 RefPtrWillBePersistent
<HTMLImageElement
> imageExteriorCrop
= HTMLImageElement::create(*Document::create().get());
123 ResourcePtr
<ImageResource
> cachedImageExteriorCrop
= new ImageResource(ResourceRequest("http://foo.com/3"),
124 StaticBitmapImage::create(m_image
).get());
125 imageExteriorCrop
->setImageResource(cachedImageExteriorCrop
.get());
127 RefPtrWillBePersistent
<HTMLImageElement
> imageOutsideCrop
= HTMLImageElement::create(*Document::create().get());
128 ResourcePtr
<ImageResource
> cachedImageOutsideCrop
= new ImageResource(ResourceRequest("http://foo.com/4"),
129 StaticBitmapImage::create(m_image
).get());
130 imageOutsideCrop
->setImageResource(cachedImageOutsideCrop
.get());
132 MockImageResourceClient
mockClient1(cachedImageNoCrop
);
133 MockImageResourceClient
mockClient2(cachedImageInteriorCrop
);
134 MockImageResourceClient
mockClient3(cachedImageExteriorCrop
);
135 MockImageResourceClient
mockClient4(cachedImageOutsideCrop
);
137 memoryCache()->add(cachedImageNoCrop
.get());
138 memoryCache()->add(cachedImageInteriorCrop
.get());
139 memoryCache()->add(cachedImageExteriorCrop
.get());
140 memoryCache()->add(cachedImageOutsideCrop
.get());
141 memoryCache()->updateDecodedResource(cachedImageNoCrop
.get(), UpdateForPropertyChange
);
142 memoryCache()->updateDecodedResource(cachedImageInteriorCrop
.get(), UpdateForPropertyChange
);
143 memoryCache()->updateDecodedResource(cachedImageExteriorCrop
.get(), UpdateForPropertyChange
);
144 memoryCache()->updateDecodedResource(cachedImageOutsideCrop
.get(), UpdateForPropertyChange
);
146 // HTMLImageElements should default to CacheLiveResourcePriorityLow.
147 ASSERT_EQ(memoryCache()->priority(imageNoCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
148 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
149 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
150 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
152 RefPtrWillBePersistent
<ImageBitmap
> imageBitmapInteriorCrop
= ImageBitmap::create(imageInteriorCrop
.get(),
153 IntRect(m_image
->width() / 2, m_image
->height() / 2, m_image
->width(), m_image
->height()));
155 RefPtrWillBePersistent
<ImageBitmap
> imageBitmapNoCrop
= ImageBitmap::create(imageNoCrop
.get(),
156 IntRect(0, 0, m_image
->width(), m_image
->height()));
157 RefPtrWillBePersistent
<ImageBitmap
> imageBitmapInteriorCrop2
= ImageBitmap::create(imageInteriorCrop
.get(),
158 IntRect(m_image
->width() / 2, m_image
->height() / 2, m_image
->width(), m_image
->height()));
159 RefPtrWillBePersistent
<ImageBitmap
> imageBitmapExteriorCrop
= ImageBitmap::create(imageExteriorCrop
.get(),
160 IntRect(-m_image
->width() / 2, -m_image
->height() / 2, m_image
->width(), m_image
->height()));
161 RefPtrWillBePersistent
<ImageBitmap
> imageBitmapOutsideCrop
= ImageBitmap::create(imageOutsideCrop
.get(),
162 IntRect(-m_image
->width(), -m_image
->height(), m_image
->width(), m_image
->height()));
164 // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh.
165 ASSERT_EQ(memoryCache()->priority(imageNoCrop
->cachedImage()), MemoryCacheLiveResourcePriorityHigh
);
166 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityHigh
);
167 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityHigh
);
169 // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority.
170 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
172 // Force a garbage collection to sweep out the local ImageBitmaps.
173 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack
, ThreadState::GCWithSweep
, Heap::ForcedGC
);
175 // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image.
176 ASSERT_EQ(memoryCache()->priority(imageNoCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
177 ASSERT_EQ(memoryCache()->priority(imageExteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
178 ASSERT_EQ(memoryCache()->priority(imageOutsideCrop
->cachedImage()), MemoryCacheLiveResourcePriorityLow
);
180 // There is still an ImageBitmap that references this image.
181 ASSERT_EQ(memoryCache()->priority(imageInteriorCrop
->cachedImage()), MemoryCacheLiveResourcePriorityHigh
);
182 imageBitmapInteriorCrop
= nullptr;
185 // Verifies that ImageBitmaps constructed from HTMLImageElements hold a reference to the original Image if the HTMLImageElement src is changed.
186 TEST_F(ImageBitmapTest
, ImageBitmapSourceChanged
)
188 RefPtrWillBeRawPtr
<HTMLImageElement
> image
= HTMLImageElement::create(*Document::create().get());
189 ResourcePtr
<ImageResource
> originalImageResource
= new ImageResource(
190 StaticBitmapImage::create(m_image
).get());
191 image
->setImageResource(originalImageResource
.get());
193 RefPtrWillBeRawPtr
<ImageBitmap
> imageBitmap
= ImageBitmap::create(image
.get(),
194 IntRect(0, 0, m_image
->width(), m_image
->height()));
195 ASSERT_EQ(imageBitmap
->bitmapImage().get(), originalImageResource
->image());
197 ResourcePtr
<ImageResource
> newImageResource
= new ImageResource(
198 StaticBitmapImage::create(m_image2
).get());
199 image
->setImageResource(newImageResource
.get());
201 // The ImageBitmap should contain the same data as the original cached image but should no longer hold a reference.
203 ASSERT_NE(imageBitmap
->bitmapImage().get(), originalImageResource
->image());
204 RefPtr
<SkImage
> image1
= imageBitmap
->bitmapImage()->imageForCurrentFrame();
205 ASSERT_NE(image1
, nullptr);
206 RefPtr
<SkImage
> image2
= originalImageResource
->image()->imageForCurrentFrame();
207 ASSERT_NE(image2
, nullptr);
208 ASSERT_EQ(image1
, image2
);
212 ASSERT_NE(imageBitmap
->bitmapImage().get(), newImageResource
->image());
213 RefPtr
<SkImage
> image1
= imageBitmap
->bitmapImage()->imageForCurrentFrame();
214 ASSERT_NE(image1
, nullptr);
215 RefPtr
<SkImage
> image2
= newImageResource
->image()->imageForCurrentFrame();
216 ASSERT_NE(image2
, nullptr);
217 ASSERT_NE(image1
, image2
);