Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Side by Side Diff: chrome/browser/extensions/extension_action.cc

Issue 1492073003: Handle more scale factors for extension Browser Action icons (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: test catches real bug! Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/extension_action.h" 5 #include "chrome/browser/extensions/extension_action.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/base64.h" 9 #include "base/base64.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
11 #include "extensions/browser/extension_icon_image.h" 12 #include "extensions/browser/extension_icon_image.h"
12 #include "extensions/browser/extension_icon_placeholder.h" 13 #include "extensions/browser/extension_icon_placeholder.h"
13 #include "extensions/common/constants.h" 14 #include "extensions/common/constants.h"
14 #include "extensions/common/extension_icon_set.h" 15 #include "extensions/common/extension_icon_set.h"
15 #include "extensions/common/feature_switch.h" 16 #include "extensions/common/feature_switch.h"
16 #include "extensions/common/manifest_handlers/icons_handler.h" 17 #include "extensions/common/manifest_handlers/icons_handler.h"
17 #include "grit/theme_resources.h" 18 #include "grit/theme_resources.h"
18 #include "grit/ui_resources.h" 19 #include "grit/ui_resources.h"
19 #include "ipc/ipc_message.h" 20 #include "ipc/ipc_message.h"
20 #include "ipc/ipc_message_utils.h" 21 #include "ipc/ipc_message_utils.h"
21 #include "third_party/skia/include/core/SkBitmap.h" 22 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "third_party/skia/include/core/SkCanvas.h" 23 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkPaint.h" 24 #include "third_party/skia/include/core/SkPaint.h"
24 #include "third_party/skia/include/effects/SkGradientShader.h" 25 #include "third_party/skia/include/effects/SkGradientShader.h"
26 #include "ui/base/resource/material_design/material_design_controller.h"
25 #include "ui/base/resource/resource_bundle.h" 27 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/animation/animation_delegate.h" 28 #include "ui/gfx/animation/animation_delegate.h"
27 #include "ui/gfx/canvas.h" 29 #include "ui/gfx/canvas.h"
28 #include "ui/gfx/color_utils.h" 30 #include "ui/gfx/color_utils.h"
29 #include "ui/gfx/geometry/rect.h" 31 #include "ui/gfx/geometry/rect.h"
30 #include "ui/gfx/geometry/size.h" 32 #include "ui/gfx/geometry/size.h"
31 #include "ui/gfx/image/image.h" 33 #include "ui/gfx/image/image.h"
32 #include "ui/gfx/image/image_skia.h" 34 #include "ui/gfx/image/image_skia.h"
33 #include "ui/gfx/image/image_skia_source.h" 35 #include "ui/gfx/image/image_skia_source.h"
34 #include "ui/gfx/ipc/gfx_param_traits.h" 36 #include "ui/gfx/ipc/gfx_param_traits.h"
35 #include "ui/gfx/skbitmap_operations.h" 37 #include "ui/gfx/skbitmap_operations.h"
36 #include "url/gurl.h" 38 #include "url/gurl.h"
37 39
38 namespace { 40 namespace {
39 41
40 // Returns the default icon image for extensions. 42 // Returns the default icon image for extensions.
41 gfx::Image GetDefaultIcon() { 43 gfx::Image GetDefaultIcon() {
42 return ui::ResourceBundle::GetSharedInstance().GetImageNamed( 44 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
43 IDR_EXTENSIONS_FAVICON); 45 IDR_EXTENSIONS_FAVICON);
44 } 46 }
45 47
46 // Given the extension action type, returns the size the extension action icon
47 // should have. The icon should be square, so only one dimension is
48 // returned.
49 int GetIconSizeForType(extensions::ActionInfo::Type type) {
50 switch (type) {
51 case extensions::ActionInfo::TYPE_BROWSER:
52 case extensions::ActionInfo::TYPE_PAGE:
53 case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
54 // TODO(dewittj) Report the actual icon size of the system
55 // indicator.
56 return extension_misc::EXTENSION_ICON_ACTION;
57 default:
58 NOTREACHED();
59 return 0;
60 }
61 }
62
63 class GetAttentionImageSource : public gfx::ImageSkiaSource { 48 class GetAttentionImageSource : public gfx::ImageSkiaSource {
64 public: 49 public:
65 explicit GetAttentionImageSource(const gfx::ImageSkia& icon) 50 explicit GetAttentionImageSource(const gfx::ImageSkia& icon)
66 : icon_(icon) {} 51 : icon_(icon) {}
67 52
68 // gfx::ImageSkiaSource overrides: 53 // gfx::ImageSkiaSource overrides:
69 gfx::ImageSkiaRep GetImageForScale(float scale) override { 54 gfx::ImageSkiaRep GetImageForScale(float scale) override {
70 gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale); 55 gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
71 color_utils::HSL shift = {-1, 0, 0.5}; 56 color_utils::HSL shift = {-1, 0, 0.5};
72 return gfx::ImageSkiaRep( 57 return gfx::ImageSkiaRep(
73 SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift), 58 SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift),
74 icon_rep.scale()); 59 icon_rep.scale());
75 } 60 }
76 61
77 private: 62 private:
78 const gfx::ImageSkia icon_; 63 const gfx::ImageSkia icon_;
79 }; 64 };
80 65
81 struct IconRepresentationInfo { 66 struct IconRepresentationInfo {
82 // Size as a string that will be used to retrieve a representation value from 67 // Size as a string that will be used to retrieve a representation value from
83 // SetIcon function arguments. 68 // SetIcon function arguments.
84 const char* size_string; 69 const char* size_string;
85 // Scale factor for which the represantion should be used. 70 // Scale factor for which the represantion should be used.
86 ui::ScaleFactor scale; 71 ui::ScaleFactor scale;
87 }; 72 };
88 73
89 const IconRepresentationInfo kIconSizes[] = {{"19", ui::SCALE_FACTOR_100P},
90 {"38", ui::SCALE_FACTOR_200P}};
91
92 template <class T> 74 template <class T>
93 bool HasValue(const std::map<int, T>& map, int tab_id) { 75 bool HasValue(const std::map<int, T>& map, int tab_id) {
94 return map.find(tab_id) != map.end(); 76 return map.find(tab_id) != map.end();
95 } 77 }
96 78
97 } // namespace 79 } // namespace
98 80
81 extension_misc::ExtensionIcons ExtensionAction::ActionIconSize() {
82 return ui::MaterialDesignController::IsModeMaterial()
83 ? extension_misc::EXTENSION_ICON_BITTY
84 : extension_misc::EXTENSION_ICON_ACTION;
85 }
86
99 const int ExtensionAction::kDefaultTabId = -1; 87 const int ExtensionAction::kDefaultTabId = -1;
100 const int ExtensionAction::kPageActionIconMaxSize =
101 extension_misc::EXTENSION_ICON_ACTION;
102 88
103 ExtensionAction::ExtensionAction(const extensions::Extension& extension, 89 ExtensionAction::ExtensionAction(const extensions::Extension& extension,
104 extensions::ActionInfo::Type action_type, 90 extensions::ActionInfo::Type action_type,
105 const extensions::ActionInfo& manifest_data) 91 const extensions::ActionInfo& manifest_data)
106 : extension_id_(extension.id()), 92 : extension_id_(extension.id()),
107 extension_name_(extension.name()), 93 extension_name_(extension.name()),
108 action_type_(action_type) { 94 action_type_(action_type) {
109 // Page/script actions are hidden/disabled by default, and browser actions are 95 // Page/script actions are hidden/disabled by default, and browser actions are
110 // visible/enabled by default. 96 // visible/enabled by default.
111 SetIsVisible(kDefaultTabId, 97 SetIsVisible(kDefaultTabId,
(...skipping 21 matching lines...) Expand all
133 return GetValue(&popup_url_, tab_id); 119 return GetValue(&popup_url_, tab_id);
134 } 120 }
135 121
136 void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) { 122 void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) {
137 SetValue(&icon_, tab_id, image); 123 SetValue(&icon_, tab_id, image);
138 } 124 }
139 125
140 bool ExtensionAction::ParseIconFromCanvasDictionary( 126 bool ExtensionAction::ParseIconFromCanvasDictionary(
141 const base::DictionaryValue& dict, 127 const base::DictionaryValue& dict,
142 gfx::ImageSkia* icon) { 128 gfx::ImageSkia* icon) {
143 // Try to extract an icon for each known scale. 129 for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd();
144 for (size_t i = 0; i < arraysize(kIconSizes); i++) { 130 iter.Advance()) {
131 int icon_size = 0;
132 if (!base::StringToInt(iter.key(), &icon_size))
133 continue;
134
145 const base::BinaryValue* image_data; 135 const base::BinaryValue* image_data;
146 std::string binary_string64; 136 std::string binary_string64;
147 IPC::Message pickle; 137 IPC::Message pickle;
148 if (dict.GetBinary(kIconSizes[i].size_string, &image_data)) { 138 if (iter.value().GetAsBinary(&image_data)) {
149 pickle = IPC::Message(image_data->GetBuffer(), image_data->GetSize()); 139 pickle = IPC::Message(image_data->GetBuffer(), image_data->GetSize());
150 } else if (dict.GetString(kIconSizes[i].size_string, &binary_string64)) { 140 } else if (iter.value().GetAsString(&binary_string64)) {
151 std::string binary_string; 141 std::string binary_string;
152 if (!base::Base64Decode(binary_string64, &binary_string)) 142 if (!base::Base64Decode(binary_string64, &binary_string))
153 return false; 143 return false;
154 pickle = IPC::Message(binary_string.c_str(), binary_string.length()); 144 pickle = IPC::Message(binary_string.c_str(), binary_string.length());
155 } else { 145 } else {
156 continue; 146 continue;
157 } 147 }
158 base::PickleIterator iter(pickle); 148 base::PickleIterator pickle_iter(pickle);
159 SkBitmap bitmap; 149 SkBitmap bitmap;
160 if (!IPC::ReadParam(&pickle, &iter, &bitmap)) 150 if (!IPC::ReadParam(&pickle, &pickle_iter, &bitmap))
161 return false; 151 return false;
162 CHECK(!bitmap.isNull()); 152 CHECK(!bitmap.isNull());
163 float scale = ui::GetScaleForScaleFactor(kIconSizes[i].scale); 153 float scale =
154 static_cast<float>(icon_size) / ExtensionAction::ActionIconSize();
164 icon->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale)); 155 icon->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
165 } 156 }
166 return true; 157 return true;
167 } 158 }
168 159
169 gfx::Image ExtensionAction::GetExplicitlySetIcon(int tab_id) const { 160 gfx::Image ExtensionAction::GetExplicitlySetIcon(int tab_id) const {
170 return GetValue(&icon_, tab_id); 161 return GetValue(&icon_, tab_id);
171 } 162 }
172 163
173 bool ExtensionAction::SetIsVisible(int tab_id, bool new_visibility) { 164 bool ExtensionAction::SetIsVisible(int tab_id, bool new_visibility) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 // when the tab's closed. There's a race between the 224 // when the tab's closed. There's a race between the
234 // LocationBarController and the ContentRulesRegistry on navigation, 225 // LocationBarController and the ContentRulesRegistry on navigation,
235 // which prevents me from cleaning everything up now. 226 // which prevents me from cleaning everything up now.
236 } 227 }
237 228
238 extensions::IconImage* ExtensionAction::LoadDefaultIconImage( 229 extensions::IconImage* ExtensionAction::LoadDefaultIconImage(
239 const extensions::Extension& extension, 230 const extensions::Extension& extension,
240 content::BrowserContext* browser_context) { 231 content::BrowserContext* browser_context) {
241 if (default_icon_ && !default_icon_image_) { 232 if (default_icon_ && !default_icon_image_) {
242 default_icon_image_.reset(new extensions::IconImage( 233 default_icon_image_.reset(new extensions::IconImage(
243 browser_context, 234 browser_context, &extension, *default_icon(), ActionIconSize(),
244 &extension, 235 *GetDefaultIcon().ToImageSkia(), nullptr));
245 *default_icon(),
246 GetIconSizeForType(action_type_),
247 *GetDefaultIcon().ToImageSkia(),
248 nullptr));
249 } 236 }
250 return default_icon_image_.get(); 237 return default_icon_image_.get();
251 } 238 }
252 239
253 gfx::Image ExtensionAction::GetDefaultIconImage() const { 240 gfx::Image ExtensionAction::GetDefaultIconImage() const {
254 // If we have a default icon, it should be loaded before trying to use it. 241 // If we have a default icon, it should be loaded before trying to use it.
255 DCHECK(!default_icon_image_ == !default_icon_); 242 DCHECK(!default_icon_image_ == !default_icon_);
256 if (default_icon_image_) 243 if (default_icon_image_)
257 return default_icon_image_->image(); 244 return default_icon_image_->image();
258 245
259 if (placeholder_icon_image_.IsEmpty()) { 246 if (placeholder_icon_image_.IsEmpty()) {
260 // If the extension action redesign is enabled, we use a special placeholder 247 // If the extension action redesign is enabled, we use a special placeholder
261 // icon (with the first letter of the extension name) rather than the 248 // icon (with the first letter of the extension name) rather than the
262 // default (puzzle piece). 249 // default (puzzle piece).
263 if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) { 250 if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) {
264 placeholder_icon_image_ = 251 placeholder_icon_image_ =
265 extensions::ExtensionIconPlaceholder::CreateImage( 252 extensions::ExtensionIconPlaceholder::CreateImage(ActionIconSize(),
266 extension_misc::EXTENSION_ICON_ACTION, extension_name_); 253 extension_name_);
267 } else { 254 } else {
268 placeholder_icon_image_ = GetDefaultIcon(); 255 placeholder_icon_image_ = GetDefaultIcon();
269 } 256 }
270 } 257 }
271 258
272 return placeholder_icon_image_; 259 return placeholder_icon_image_;
273 } 260 }
274 261
275 bool ExtensionAction::HasPopupUrl(int tab_id) const { 262 bool ExtensionAction::HasPopupUrl(int tab_id) const {
276 return HasValue(popup_url_, tab_id); 263 return HasValue(popup_url_, tab_id);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 const extensions::ActionInfo& manifest_data) { 296 const extensions::ActionInfo& manifest_data) {
310 // If the manifest doesn't specify a title, set it to |extension|'s name. 297 // If the manifest doesn't specify a title, set it to |extension|'s name.
311 const std::string& title = 298 const std::string& title =
312 !manifest_data.default_title.empty() ? manifest_data.default_title : 299 !manifest_data.default_title.empty() ? manifest_data.default_title :
313 extension.name(); 300 extension.name();
314 SetTitle(kDefaultTabId, title); 301 SetTitle(kDefaultTabId, title);
315 SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url); 302 SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
316 set_id(manifest_data.id); 303 set_id(manifest_data.id);
317 304
318 // Initialize the specified icon set. 305 // Initialize the specified icon set.
319 if (!manifest_data.default_icon.empty()) 306 if (!manifest_data.default_icon.empty()) {
320 default_icon_.reset(new ExtensionIconSet(manifest_data.default_icon)); 307 default_icon_.reset(new ExtensionIconSet(manifest_data.default_icon));
321 308 } else {
322 const ExtensionIconSet& extension_icons = 309 // Fall back to the product icons if no action icon exists.
323 extensions::IconsInfo::GetIcons(&extension); 310 const ExtensionIconSet& product_icons =
324 // Look for any other icons. 311 extensions::IconsInfo::GetIcons(&extension);
325 std::string largest_icon = extension_icons.Get( 312 if (!product_icons.empty())
326 extension_misc::EXTENSION_ICON_GIGANTOR, ExtensionIconSet::MATCH_SMALLER); 313 default_icon_.reset(new ExtensionIconSet(product_icons));
327
328 if (!largest_icon.empty()) {
329 // We found an icon to use, so create an icon set if one doesn't exist.
330 if (!default_icon_)
331 default_icon_.reset(new ExtensionIconSet());
332 int largest_icon_size = extension_icons.GetIconSizeFromPath(largest_icon);
333 // Replace any missing extension action icons with the largest icon
334 // retrieved from |extension|'s manifest so long as the largest icon is
335 // larger than the current key.
336 for (int i = extension_misc::kNumExtensionActionIconSizes - 1; i >= 0;
337 --i) {
338 int size = extension_misc::kExtensionActionIconSizes[i].size;
339 if (default_icon_->Get(size, ExtensionIconSet::MATCH_BIGGER).empty() &&
340 largest_icon_size > size) {
341 default_icon_->Add(size, largest_icon);
342 break;
343 }
344 }
345 } 314 }
346 } 315 }
347 316
348 // Determines which icon would be returned by |GetIcon|, and returns its width. 317 // Determines which icon would be returned by |GetIcon|, and returns its width.
349 int ExtensionAction::GetIconWidth(int tab_id) const { 318 int ExtensionAction::GetIconWidth(int tab_id) const {
350 // If icon has been set, return its width. 319 // If icon has been set, return its width.
351 gfx::Image icon = GetValue(&icon_, tab_id); 320 gfx::Image icon = GetValue(&icon_, tab_id);
352 if (!icon.IsEmpty()) 321 if (!icon.IsEmpty())
353 return icon.Width(); 322 return icon.Width();
354 // If there is a default icon, the icon width will be set depending on our 323 // If there is a default icon, the icon width will be set depending on our
355 // action type. 324 // action type.
356 if (default_icon_) 325 if (default_icon_)
357 return GetIconSizeForType(action_type()); 326 return ActionIconSize();
358 327
359 // If no icon has been set and there is no default icon, we need favicon 328 // If no icon has been set and there is no default icon, we need favicon
360 // width. 329 // width.
361 return ui::ResourceBundle::GetSharedInstance().GetImageNamed( 330 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
362 IDR_EXTENSIONS_FAVICON).Width(); 331 IDR_EXTENSIONS_FAVICON).Width();
363 } 332 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_action.h ('k') | chrome/browser/extensions/extension_action_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698