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