Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / extension_action.cc
blob312633d586034ee11790c527b6cc31e251585271
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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/extension_action.h"
7 #include <algorithm>
9 #include "base/base64.h"
10 #include "base/logging.h"
11 #include "chrome/common/badge_util.h"
12 #include "chrome/common/icon_with_badge_image_source.h"
13 #include "extensions/browser/extension_icon_image.h"
14 #include "extensions/browser/extension_icon_placeholder.h"
15 #include "extensions/common/constants.h"
16 #include "extensions/common/extension_icon_set.h"
17 #include "extensions/common/feature_switch.h"
18 #include "extensions/common/manifest_handlers/icons_handler.h"
19 #include "grit/theme_resources.h"
20 #include "grit/ui_resources.h"
21 #include "ipc/ipc_message.h"
22 #include "ipc/ipc_message_utils.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "third_party/skia/include/core/SkCanvas.h"
25 #include "third_party/skia/include/core/SkPaint.h"
26 #include "third_party/skia/include/effects/SkGradientShader.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/animation/animation_delegate.h"
29 #include "ui/gfx/canvas.h"
30 #include "ui/gfx/color_utils.h"
31 #include "ui/gfx/geometry/rect.h"
32 #include "ui/gfx/geometry/size.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_skia.h"
35 #include "ui/gfx/image/image_skia_source.h"
36 #include "ui/gfx/ipc/gfx_param_traits.h"
37 #include "ui/gfx/skbitmap_operations.h"
38 #include "url/gurl.h"
40 namespace {
42 // Returns the default icon image for extensions.
43 gfx::Image GetDefaultIcon() {
44 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
45 IDR_EXTENSIONS_FAVICON);
48 // Given the extension action type, returns the size the extension action icon
49 // should have. The icon should be square, so only one dimension is
50 // returned.
51 int GetIconSizeForType(extensions::ActionInfo::Type type) {
52 switch (type) {
53 case extensions::ActionInfo::TYPE_BROWSER:
54 case extensions::ActionInfo::TYPE_PAGE:
55 case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
56 // TODO(dewittj) Report the actual icon size of the system
57 // indicator.
58 return extension_misc::EXTENSION_ICON_ACTION;
59 default:
60 NOTREACHED();
61 return 0;
65 class GetAttentionImageSource : public gfx::ImageSkiaSource {
66 public:
67 explicit GetAttentionImageSource(const gfx::ImageSkia& icon)
68 : icon_(icon) {}
70 // gfx::ImageSkiaSource overrides:
71 gfx::ImageSkiaRep GetImageForScale(float scale) override {
72 gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
73 color_utils::HSL shift = {-1, 0, 0.5};
74 return gfx::ImageSkiaRep(
75 SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift),
76 icon_rep.scale());
79 private:
80 const gfx::ImageSkia icon_;
83 struct IconRepresentationInfo {
84 // Size as a string that will be used to retrieve a representation value from
85 // SetIcon function arguments.
86 const char* size_string;
87 // Scale factor for which the represantion should be used.
88 ui::ScaleFactor scale;
91 const IconRepresentationInfo kIconSizes[] = {{"19", ui::SCALE_FACTOR_100P},
92 {"38", ui::SCALE_FACTOR_200P}};
94 template <class T>
95 bool HasValue(const std::map<int, T>& map, int tab_id) {
96 return map.find(tab_id) != map.end();
99 } // namespace
101 const int ExtensionAction::kDefaultTabId = -1;
102 const int ExtensionAction::kPageActionIconMaxSize =
103 extension_misc::EXTENSION_ICON_ACTION;
105 ExtensionAction::ExtensionAction(const extensions::Extension& extension,
106 extensions::ActionInfo::Type action_type,
107 const extensions::ActionInfo& manifest_data)
108 : extension_id_(extension.id()),
109 extension_name_(extension.name()),
110 action_type_(action_type) {
111 // Page/script actions are hidden/disabled by default, and browser actions are
112 // visible/enabled by default.
113 SetIsVisible(kDefaultTabId,
114 action_type == extensions::ActionInfo::TYPE_BROWSER);
115 Populate(extension, manifest_data);
118 ExtensionAction::~ExtensionAction() {
121 void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) {
122 // We store |url| even if it is empty, rather than removing a URL from the
123 // map. If an extension has a default popup, and removes it for a tab via
124 // the API, we must remember that there is no popup for that specific tab.
125 // If we removed the tab's URL, GetPopupURL would incorrectly return the
126 // default URL.
127 SetValue(&popup_url_, tab_id, url);
130 bool ExtensionAction::HasPopup(int tab_id) const {
131 return !GetPopupUrl(tab_id).is_empty();
134 GURL ExtensionAction::GetPopupUrl(int tab_id) const {
135 return GetValue(&popup_url_, tab_id);
138 void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) {
139 SetValue(&icon_, tab_id, image);
142 bool ExtensionAction::ParseIconFromCanvasDictionary(
143 const base::DictionaryValue& dict,
144 gfx::ImageSkia* icon) {
145 // Try to extract an icon for each known scale.
146 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
147 const base::BinaryValue* image_data;
148 std::string binary_string64;
149 IPC::Message pickle;
150 if (dict.GetBinary(kIconSizes[i].size_string, &image_data)) {
151 pickle = IPC::Message(image_data->GetBuffer(), image_data->GetSize());
152 } else if (dict.GetString(kIconSizes[i].size_string, &binary_string64)) {
153 std::string binary_string;
154 if (!base::Base64Decode(binary_string64, &binary_string))
155 return false;
156 pickle = IPC::Message(binary_string.c_str(), binary_string.length());
157 } else {
158 continue;
160 base::PickleIterator iter(pickle);
161 SkBitmap bitmap;
162 if (!IPC::ReadParam(&pickle, &iter, &bitmap))
163 return false;
164 CHECK(!bitmap.isNull());
165 float scale = ui::GetScaleForScaleFactor(kIconSizes[i].scale);
166 icon->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
168 return true;
171 gfx::Image ExtensionAction::GetExplicitlySetIcon(int tab_id) const {
172 return GetValue(&icon_, tab_id);
175 bool ExtensionAction::SetIsVisible(int tab_id, bool new_visibility) {
176 const bool old_visibility = GetValue(&is_visible_, tab_id);
178 if (old_visibility == new_visibility)
179 return false;
181 SetValue(&is_visible_, tab_id, new_visibility);
183 return true;
186 void ExtensionAction::DeclarativeShow(int tab_id) {
187 DCHECK_NE(tab_id, kDefaultTabId);
188 ++declarative_show_count_[tab_id]; // Use default initialization to 0.
191 void ExtensionAction::UndoDeclarativeShow(int tab_id) {
192 int& show_count = declarative_show_count_[tab_id];
193 DCHECK_GT(show_count, 0);
194 if (--show_count == 0)
195 declarative_show_count_.erase(tab_id);
198 void ExtensionAction::DeclarativeSetIcon(int tab_id,
199 int priority,
200 const gfx::Image& icon) {
201 DCHECK_NE(tab_id, kDefaultTabId);
202 declarative_icon_[tab_id][priority].push_back(icon);
205 void ExtensionAction::UndoDeclarativeSetIcon(int tab_id,
206 int priority,
207 const gfx::Image& icon) {
208 std::vector<gfx::Image>& icons = declarative_icon_[tab_id][priority];
209 for (std::vector<gfx::Image>::iterator it = icons.begin(); it != icons.end();
210 ++it) {
211 if (it->AsImageSkia().BackedBySameObjectAs(icon.AsImageSkia())) {
212 icons.erase(it);
213 return;
218 const gfx::Image ExtensionAction::GetDeclarativeIcon(int tab_id) const {
219 if (declarative_icon_.find(tab_id) != declarative_icon_.end() &&
220 !declarative_icon_.find(tab_id)->second.rbegin()->second.empty()) {
221 return declarative_icon_.find(tab_id)->second.rbegin()->second.back();
223 return gfx::Image();
226 void ExtensionAction::ClearAllValuesForTab(int tab_id) {
227 popup_url_.erase(tab_id);
228 title_.erase(tab_id);
229 icon_.erase(tab_id);
230 badge_text_.erase(tab_id);
231 badge_text_color_.erase(tab_id);
232 badge_background_color_.erase(tab_id);
233 is_visible_.erase(tab_id);
234 // TODO(jyasskin): Erase the element from declarative_show_count_
235 // when the tab's closed. There's a race between the
236 // LocationBarController and the ContentRulesRegistry on navigation,
237 // which prevents me from cleaning everything up now.
240 void ExtensionAction::PaintBadge(gfx::Canvas* canvas,
241 const gfx::Rect& bounds,
242 int tab_id) {
243 badge_util::PaintBadge(
244 canvas,
245 bounds,
246 GetBadgeText(tab_id),
247 GetBadgeTextColor(tab_id),
248 GetBadgeBackgroundColor(tab_id),
249 GetIconWidth(tab_id),
250 action_type());
253 gfx::ImageSkia ExtensionAction::GetIconWithBadge(
254 const gfx::ImageSkia& icon,
255 int tab_id,
256 const gfx::Size& spacing) const {
257 if (tab_id < 0)
258 return icon;
260 return gfx::ImageSkia(
261 new IconWithBadgeImageSource(icon,
262 icon.size(),
263 spacing,
264 GetBadgeText(tab_id),
265 GetBadgeTextColor(tab_id),
266 GetBadgeBackgroundColor(tab_id),
267 action_type()),
268 icon.size());
271 extensions::IconImage* ExtensionAction::LoadDefaultIconImage(
272 const extensions::Extension& extension,
273 content::BrowserContext* browser_context) {
274 if (default_icon_ && !default_icon_image_) {
275 default_icon_image_.reset(new extensions::IconImage(
276 browser_context,
277 &extension,
278 *default_icon(),
279 GetIconSizeForType(action_type_),
280 *GetDefaultIcon().ToImageSkia(),
281 nullptr));
283 return default_icon_image_.get();
286 gfx::Image ExtensionAction::GetDefaultIconImage() const {
287 // If we have a default icon, it should be loaded before trying to use it.
288 DCHECK(!default_icon_image_ == !default_icon_);
289 if (default_icon_image_)
290 return default_icon_image_->image();
292 // If the extension action redesign is enabled, we use a special placeholder
293 // icon (with the first letter of the extension name) rather than the default
294 // (puzzle piece).
295 if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) {
296 return extensions::ExtensionIconPlaceholder::CreateImage(
297 extension_misc::EXTENSION_ICON_ACTION, extension_name_);
300 return GetDefaultIcon();
303 bool ExtensionAction::HasPopupUrl(int tab_id) const {
304 return HasValue(popup_url_, tab_id);
307 bool ExtensionAction::HasTitle(int tab_id) const {
308 return HasValue(title_, tab_id);
311 bool ExtensionAction::HasBadgeText(int tab_id) const {
312 return HasValue(badge_text_, tab_id);
315 bool ExtensionAction::HasBadgeBackgroundColor(int tab_id) const {
316 return HasValue(badge_background_color_, tab_id);
319 bool ExtensionAction::HasBadgeTextColor(int tab_id) const {
320 return HasValue(badge_text_color_, tab_id);
323 bool ExtensionAction::HasIsVisible(int tab_id) const {
324 return HasValue(is_visible_, tab_id);
327 bool ExtensionAction::HasIcon(int tab_id) const {
328 return HasValue(icon_, tab_id);
331 void ExtensionAction::SetDefaultIconForTest(
332 scoped_ptr<ExtensionIconSet> default_icon) {
333 default_icon_ = default_icon.Pass();
336 void ExtensionAction::Populate(const extensions::Extension& extension,
337 const extensions::ActionInfo& manifest_data) {
338 // If the manifest doesn't specify a title, set it to |extension|'s name.
339 const std::string& title =
340 !manifest_data.default_title.empty() ? manifest_data.default_title :
341 extension.name();
342 SetTitle(kDefaultTabId, title);
343 SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
344 set_id(manifest_data.id);
346 // Initialize the specified icon set.
347 if (!manifest_data.default_icon.empty())
348 default_icon_.reset(new ExtensionIconSet(manifest_data.default_icon));
350 const ExtensionIconSet& extension_icons =
351 extensions::IconsInfo::GetIcons(&extension);
352 // Look for any other icons.
353 std::string largest_icon = extension_icons.Get(
354 extension_misc::EXTENSION_ICON_GIGANTOR, ExtensionIconSet::MATCH_SMALLER);
356 if (!largest_icon.empty()) {
357 // We found an icon to use, so create an icon set if one doesn't exist.
358 if (!default_icon_)
359 default_icon_.reset(new ExtensionIconSet());
360 int largest_icon_size = extension_icons.GetIconSizeFromPath(largest_icon);
361 // Replace any missing extension action icons with the largest icon
362 // retrieved from |extension|'s manifest so long as the largest icon is
363 // larger than the current key.
364 for (int i = extension_misc::kNumExtensionActionIconSizes - 1; i >= 0;
365 --i) {
366 int size = extension_misc::kExtensionActionIconSizes[i].size;
367 if (default_icon_->Get(size, ExtensionIconSet::MATCH_BIGGER).empty() &&
368 largest_icon_size > size) {
369 default_icon_->Add(size, largest_icon);
370 break;
376 // Determines which icon would be returned by |GetIcon|, and returns its width.
377 int ExtensionAction::GetIconWidth(int tab_id) const {
378 // If icon has been set, return its width.
379 gfx::Image icon = GetValue(&icon_, tab_id);
380 if (!icon.IsEmpty())
381 return icon.Width();
382 // If there is a default icon, the icon width will be set depending on our
383 // action type.
384 if (default_icon_)
385 return GetIconSizeForType(action_type());
387 // If no icon has been set and there is no default icon, we need favicon
388 // width.
389 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
390 IDR_EXTENSIONS_FAVICON).Width();