image_loader_unittest.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/browser/image_loader.h"
  5. #include <stddef.h>
  6. #include "base/bind.h"
  7. #include "base/files/file_path.h"
  8. #include "base/json/json_file_value_serializer.h"
  9. #include "base/path_service.h"
  10. #include "base/run_loop.h"
  11. #include "base/strings/string_util.h"
  12. #include "content/public/test/test_browser_context.h"
  13. #include "extensions/browser/extension_registry.h"
  14. #include "extensions/browser/extensions_browser_client.h"
  15. #include "extensions/browser/extensions_test.h"
  16. #include "extensions/browser/unloaded_extension_reason.h"
  17. #include "extensions/common/constants.h"
  18. #include "extensions/common/extension.h"
  19. #include "extensions/common/extension_icon_set.h"
  20. #include "extensions/common/extension_paths.h"
  21. #include "extensions/common/extension_resource.h"
  22. #include "extensions/common/manifest.h"
  23. #include "extensions/common/manifest_handlers/icons_handler.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. #include "third_party/skia/include/core/SkBitmap.h"
  26. #include "ui/gfx/geometry/size.h"
  27. #include "ui/gfx/image/image.h"
  28. #include "ui/gfx/image/image_family.h"
  29. #include "ui/gfx/image/image_skia.h"
  30. #include "ui/gfx/image/image_skia_rep.h"
  31. using extensions::mojom::ManifestLocation;
  32. namespace extensions {
  33. class ImageLoaderTest : public ExtensionsTest {
  34. public:
  35. ImageLoaderTest() : image_loaded_count_(0), quit_in_image_loaded_(false) {}
  36. void OnImageLoaded(const gfx::Image& image) {
  37. image_loaded_count_++;
  38. if (quit_in_image_loaded_)
  39. base::RunLoop::QuitCurrentWhenIdleDeprecated();
  40. image_ = image;
  41. }
  42. void OnImageFamilyLoaded(gfx::ImageFamily image_family) {
  43. image_loaded_count_++;
  44. if (quit_in_image_loaded_)
  45. base::RunLoop::QuitCurrentWhenIdleDeprecated();
  46. image_family_ = std::move(image_family);
  47. }
  48. void WaitForImageLoad() {
  49. quit_in_image_loaded_ = true;
  50. base::RunLoop().Run();
  51. quit_in_image_loaded_ = false;
  52. }
  53. int image_loaded_count() {
  54. int result = image_loaded_count_;
  55. image_loaded_count_ = 0;
  56. return result;
  57. }
  58. scoped_refptr<Extension> CreateExtension(const char* dir_name,
  59. ManifestLocation location) {
  60. // Create and load an extension.
  61. base::FilePath extension_dir;
  62. if (!base::PathService::Get(DIR_TEST_DATA, &extension_dir)) {
  63. EXPECT_FALSE(true);
  64. return nullptr;
  65. }
  66. extension_dir = extension_dir.AppendASCII(dir_name);
  67. int error_code = 0;
  68. std::string error;
  69. JSONFileValueDeserializer deserializer(
  70. extension_dir.AppendASCII("manifest.json"));
  71. std::unique_ptr<base::DictionaryValue> valid_value =
  72. base::DictionaryValue::From(
  73. deserializer.Deserialize(&error_code, &error));
  74. EXPECT_EQ(0, error_code) << error;
  75. if (error_code != 0)
  76. return nullptr;
  77. EXPECT_TRUE(valid_value.get());
  78. if (!valid_value)
  79. return nullptr;
  80. return Extension::Create(
  81. extension_dir, location, *valid_value, Extension::NO_FLAGS, &error);
  82. }
  83. gfx::Image image_;
  84. gfx::ImageFamily image_family_;
  85. private:
  86. int image_loaded_count_;
  87. bool quit_in_image_loaded_;
  88. };
  89. // Tests loading an image works correctly.
  90. TEST_F(ImageLoaderTest, LoadImage) {
  91. scoped_refptr<Extension> extension(
  92. CreateExtension("image_loader", ManifestLocation::kInvalidLocation));
  93. ASSERT_TRUE(extension.get() != nullptr);
  94. ExtensionResource image_resource =
  95. IconsInfo::GetIconResource(extension.get(),
  96. extension_misc::EXTENSION_ICON_SMALLISH,
  97. ExtensionIconSet::MATCH_EXACTLY);
  98. gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
  99. extension_misc::EXTENSION_ICON_SMALLISH);
  100. ImageLoader loader;
  101. loader.LoadImageAsync(
  102. extension.get(), image_resource, max_size,
  103. base::BindOnce(&ImageLoaderTest::OnImageLoaded, base::Unretained(this)));
  104. // The image isn't cached, so we should not have received notification.
  105. EXPECT_EQ(0, image_loaded_count());
  106. WaitForImageLoad();
  107. // We should have gotten the image.
  108. EXPECT_FALSE(image_.IsEmpty());
  109. EXPECT_EQ(1, image_loaded_count());
  110. // Check that the image was loaded.
  111. EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
  112. image_.ToSkBitmap()->width());
  113. }
  114. // Tests deleting an extension while waiting for the image to load doesn't cause
  115. // problems.
  116. TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) {
  117. scoped_refptr<Extension> extension(
  118. CreateExtension("image_loader", ManifestLocation::kInvalidLocation));
  119. ASSERT_TRUE(extension.get() != nullptr);
  120. ExtensionResource image_resource =
  121. IconsInfo::GetIconResource(extension.get(),
  122. extension_misc::EXTENSION_ICON_SMALLISH,
  123. ExtensionIconSet::MATCH_EXACTLY);
  124. gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
  125. extension_misc::EXTENSION_ICON_SMALLISH);
  126. ImageLoader loader;
  127. std::set<int> sizes;
  128. sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH);
  129. loader.LoadImageAsync(
  130. extension.get(), image_resource, max_size,
  131. base::BindOnce(&ImageLoaderTest::OnImageLoaded, base::Unretained(this)));
  132. // The image isn't cached, so we should not have received notification.
  133. EXPECT_EQ(0, image_loaded_count());
  134. // Send out notification the extension was uninstalled.
  135. ExtensionRegistry::Get(browser_context())
  136. ->TriggerOnUnloaded(extension.get(), UnloadedExtensionReason::UNINSTALL);
  137. // Chuck the extension, that way if anyone tries to access it we should crash
  138. // or get valgrind errors.
  139. extension = nullptr;
  140. WaitForImageLoad();
  141. // Even though we deleted the extension, we should still get the image.
  142. // We should still have gotten the image.
  143. EXPECT_EQ(1, image_loaded_count());
  144. // Check that the image was loaded.
  145. EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
  146. image_.ToSkBitmap()->width());
  147. }
  148. // Tests loading multiple dimensions of the same image.
  149. TEST_F(ImageLoaderTest, MultipleImages) {
  150. scoped_refptr<Extension> extension(
  151. CreateExtension("image_loader", ManifestLocation::kInvalidLocation));
  152. ASSERT_TRUE(extension.get() != nullptr);
  153. std::vector<ImageLoader::ImageRepresentation> info_list;
  154. int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
  155. extension_misc::EXTENSION_ICON_SMALLISH, };
  156. for (size_t i = 0; i < std::size(sizes); ++i) {
  157. ExtensionResource resource = IconsInfo::GetIconResource(
  158. extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
  159. info_list.push_back(ImageLoader::ImageRepresentation(
  160. resource, ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER,
  161. gfx::Size(sizes[i], sizes[i]), 1.f));
  162. }
  163. ImageLoader loader;
  164. loader.LoadImagesAsync(
  165. extension.get(), info_list,
  166. base::BindOnce(&ImageLoaderTest::OnImageLoaded, base::Unretained(this)));
  167. // The image isn't cached, so we should not have received notification.
  168. EXPECT_EQ(0, image_loaded_count());
  169. WaitForImageLoad();
  170. // We should have gotten the image.
  171. EXPECT_EQ(1, image_loaded_count());
  172. // Check that all images were loaded.
  173. std::vector<gfx::ImageSkiaRep> image_reps =
  174. image_.ToImageSkia()->image_reps();
  175. ASSERT_EQ(2u, image_reps.size());
  176. const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
  177. const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
  178. EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY,
  179. img_rep1->pixel_width());
  180. EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
  181. img_rep2->pixel_width());
  182. }
  183. // Tests loading multiple dimensions of the same image into an image family.
  184. TEST_F(ImageLoaderTest, LoadImageFamily) {
  185. scoped_refptr<Extension> extension(
  186. CreateExtension("image_loader", ManifestLocation::kInvalidLocation));
  187. ASSERT_TRUE(extension.get() != nullptr);
  188. std::vector<ImageLoader::ImageRepresentation> info_list;
  189. int sizes[] = {extension_misc::EXTENSION_ICON_BITTY,
  190. extension_misc::EXTENSION_ICON_SMALLISH, };
  191. for (size_t i = 0; i < std::size(sizes); ++i) {
  192. ExtensionResource resource = IconsInfo::GetIconResource(
  193. extension.get(), sizes[i], ExtensionIconSet::MATCH_EXACTLY);
  194. info_list.push_back(ImageLoader::ImageRepresentation(
  195. resource, ImageLoader::ImageRepresentation::NEVER_RESIZE,
  196. gfx::Size(sizes[i], sizes[i]), 1.f));
  197. }
  198. // Add a second icon of 200P which should get grouped with the smaller icon's
  199. // ImageSkia.
  200. ExtensionResource resource =
  201. IconsInfo::GetIconResource(extension.get(),
  202. extension_misc::EXTENSION_ICON_SMALLISH,
  203. ExtensionIconSet::MATCH_EXACTLY);
  204. info_list.push_back(ImageLoader::ImageRepresentation(
  205. resource, ImageLoader::ImageRepresentation::NEVER_RESIZE,
  206. gfx::Size(extension_misc::EXTENSION_ICON_BITTY,
  207. extension_misc::EXTENSION_ICON_BITTY),
  208. 2.f));
  209. ImageLoader loader;
  210. loader.LoadImageFamilyAsync(
  211. extension.get(), info_list,
  212. base::BindOnce(&ImageLoaderTest::OnImageFamilyLoaded,
  213. base::Unretained(this)));
  214. // The image isn't cached, so we should not have received notification.
  215. EXPECT_EQ(0, image_loaded_count());
  216. WaitForImageLoad();
  217. // We should have gotten the image.
  218. EXPECT_EQ(1, image_loaded_count());
  219. // Check that all images were loaded.
  220. for (size_t i = 0; i < std::size(sizes); ++i) {
  221. const gfx::Image* image = image_family_.GetBest(sizes[i], sizes[i]);
  222. EXPECT_EQ(sizes[i], image->Width());
  223. }
  224. // Check the smaller image has 2 representations of different scale factors.
  225. std::vector<gfx::ImageSkiaRep> image_reps =
  226. image_family_.GetBest(extension_misc::EXTENSION_ICON_BITTY,
  227. extension_misc::EXTENSION_ICON_BITTY)
  228. ->ToImageSkia()
  229. ->image_reps();
  230. ASSERT_EQ(2u, image_reps.size());
  231. const gfx::ImageSkiaRep* img_rep1 = &image_reps[0];
  232. const gfx::ImageSkiaRep* img_rep2 = &image_reps[1];
  233. EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY, img_rep1->pixel_width());
  234. EXPECT_EQ(1.0f, img_rep1->scale());
  235. EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH, img_rep2->pixel_width());
  236. EXPECT_EQ(2.0f, img_rep2->scale());
  237. }
  238. } // namespace extensions