1 // Copyright 2015 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
5 #include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader.h"
6
7 #include <utility>
8
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/chromeos/launcher_search_provider/error_reporter.h"
11 #include "extensions/common/constants.h"
12 #include "skia/ext/image_operations.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/gfx/geometry/size.h"
15 #include "ui/gfx/image/canvas_image_source.h"
16 #include "ui/gfx/image/image_skia_operations.h"
17
18 namespace {
19
20 const int kTruncatedIconUrlMaxSize = 100;
21 const char kWarningMessagePrefix[] =
22 "[chrome.launcherSearchProvider.setSearchResults]";
23
24 } // namespace
25
26 namespace app_list {
27
LauncherSearchIconImageLoader(const GURL & icon_url,Profile * profile,const extensions::Extension * extension,const int icon_dimension,std::unique_ptr<chromeos::launcher_search_provider::ErrorReporter> error_reporter)28 LauncherSearchIconImageLoader::LauncherSearchIconImageLoader(
29 const GURL& icon_url,
30 Profile* profile,
31 const extensions::Extension* extension,
32 const int icon_dimension,
33 std::unique_ptr<chromeos::launcher_search_provider::ErrorReporter>
34 error_reporter)
35 : profile_(profile),
36 extension_(extension),
37 icon_url_(icon_url),
38 icon_size_(icon_dimension, icon_dimension),
39 error_reporter_(std::move(error_reporter)) {}
40
41 LauncherSearchIconImageLoader::~LauncherSearchIconImageLoader() = default;
42
LoadResources()43 void LauncherSearchIconImageLoader::LoadResources() {
44 DCHECK(custom_icon_image_.isNull());
45
46 // Loads extension icon image and set it as main icon image.
47 extension_icon_image_ = LoadExtensionIcon();
48 CHECK(!extension_icon_image_.isNull());
49 NotifyObserversIconImageChange();
50
51 // If valid icon_url is provided as chrome-extension scheme with the host of
52 // |extension|, load custom icon.
53 if (icon_url_.is_empty()) {
54 return;
55 }
56
57 if (!icon_url_.is_valid() ||
58 !icon_url_.SchemeIs(extensions::kExtensionScheme) ||
59 icon_url_.host() != extension_->id()) {
60 std::vector<std::string> params;
61 params.push_back(kWarningMessagePrefix);
62 params.push_back(GetTruncatedIconUrl(kTruncatedIconUrlMaxSize));
63 params.push_back(extensions::kExtensionScheme);
64 params.push_back(extension_->id());
65 error_reporter_->Warn(base::ReplaceStringPlaceholders(
66 "$1 Invalid icon URL: $2. Must have a valid URL within $3://$4.",
67 params, nullptr));
68 return;
69 }
70
71 LoadIconResourceFromExtension();
72 }
73
AddObserver(Observer * observer)74 void LauncherSearchIconImageLoader::AddObserver(Observer* observer) {
75 observers_.insert(observer);
76 }
77
RemoveObserver(Observer * observer)78 void LauncherSearchIconImageLoader::RemoveObserver(Observer* observer) {
79 observers_.erase(observer);
80 }
81
GetIconImage() const82 const gfx::ImageSkia& LauncherSearchIconImageLoader::GetIconImage() const {
83 // If no custom icon is supplied, return the extension icon.
84 if (custom_icon_image_.isNull())
85 return extension_icon_image_;
86
87 return custom_icon_image_;
88 }
89
GetBadgeIconImage() const90 const gfx::ImageSkia& LauncherSearchIconImageLoader::GetBadgeIconImage() const {
91 // If a custom icon is supplied, badge it with the extension icon.
92 if (!custom_icon_image_.isNull())
93 return extension_icon_image_;
94
95 return custom_icon_image_; // Returns as an empty image.
96 }
97
OnExtensionIconChanged(const gfx::ImageSkia & image)98 void LauncherSearchIconImageLoader::OnExtensionIconChanged(
99 const gfx::ImageSkia& image) {
100 CHECK(!image.isNull());
101
102 extension_icon_image_ = image;
103
104 if (custom_icon_image_.isNull()) {
105 // When |custom_icon_image_| is not set, extension icon image will be shown
106 // as a main icon.
107 NotifyObserversIconImageChange();
108 } else {
109 // When |custom_icon_image_| is set, extension icon image will be shown as a
110 // badge icon.
111 NotifyObserversBadgeIconImageChange();
112 }
113 }
114
OnCustomIconLoaded(const gfx::ImageSkia & image)115 void LauncherSearchIconImageLoader::OnCustomIconLoaded(
116 const gfx::ImageSkia& image) {
117 if (image.isNull()) {
118 std::vector<std::string> params;
119 params.push_back(kWarningMessagePrefix);
120 params.push_back(GetTruncatedIconUrl(kTruncatedIconUrlMaxSize));
121 error_reporter_->Warn(base::ReplaceStringPlaceholders(
122 "$1 Failed to load icon URL: $2", params, nullptr));
123
124 return;
125 }
126
127 const bool previously_unbadged = custom_icon_image_.isNull();
128 custom_icon_image_ = gfx::ImageSkiaOperations::CreateResizedImage(
129 image, skia::ImageOperations::RESIZE_BEST, icon_size_);
130 NotifyObserversIconImageChange();
131
132 // If custom_icon_image_ is not set before, extension icon moves from main
133 // icon to badge icon. We need to notify badge icon image change after we set
134 // custom_icon_image_ to return proper image in GetBadgeIconImage method.
135 if (previously_unbadged)
136 NotifyObserversBadgeIconImageChange();
137 }
138
NotifyObserversIconImageChange()139 void LauncherSearchIconImageLoader::NotifyObserversIconImageChange() {
140 for (auto* observer : observers_) {
141 observer->OnIconImageChanged(this);
142 }
143 }
144
NotifyObserversBadgeIconImageChange()145 void LauncherSearchIconImageLoader::NotifyObserversBadgeIconImageChange() {
146 for (auto* observer : observers_) {
147 observer->OnBadgeIconImageChanged(this);
148 }
149 }
150
GetTruncatedIconUrl(const uint32_t max_size)151 std::string LauncherSearchIconImageLoader::GetTruncatedIconUrl(
152 const uint32_t max_size) {
153 CHECK(max_size > 3);
154
155 if (icon_url_.spec().size() <= max_size)
156 return icon_url_.spec();
157
158 std::string truncated_url;
159 base::TruncateUTF8ToByteSize(icon_url_.spec(), max_size - 3, &truncated_url);
160 truncated_url.append("...");
161 return truncated_url;
162 }
163
164 } // namespace app_list
165