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.
5 #include "chrome/browser/extensions/extension_action_storage_manager.h"
7 #include "base/base64.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/values.h"
12 #include "chrome/browser/extensions/extension_action.h"
13 #include "chrome/browser/extensions/extension_action_manager.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/browser/extension_system.h"
16 #include "extensions/browser/state_store.h"
17 #include "extensions/common/constants.h"
18 #include "ui/base/layout.h"
19 #include "ui/gfx/codec/png_codec.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/image/image_skia.h"
23 namespace extensions
{
27 const char kBrowserActionStorageKey
[] = "browser_action";
28 const char kPopupUrlStorageKey
[] = "poupup_url";
29 const char kTitleStorageKey
[] = "title";
30 const char kIconStorageKey
[] = "icon";
31 const char kBadgeTextStorageKey
[] = "badge_text";
32 const char kBadgeBackgroundColorStorageKey
[] = "badge_background_color";
33 const char kBadgeTextColorStorageKey
[] = "badge_text_color";
34 const char kAppearanceStorageKey
[] = "appearance";
36 // Only add values to the end of this enum, since it's stored in the user's
37 // Extension State, under the kAppearanceStorageKey. It represents the
38 // ExtensionAction's default visibility.
39 enum StoredAppearance
{
40 // The action icon is hidden.
42 // The action is trying to get the user's attention but isn't yet
43 // running on the page. Was only used for script badges.
44 OBSOLETE_WANTS_ATTENTION
= 1,
45 // The action icon is visible with its normal appearance.
49 // Conversion function for reading/writing to storage.
50 SkColor
RawStringToSkColor(const std::string
& str
) {
52 base::StringToUint64(str
, &value
);
53 SkColor color
= static_cast<SkColor
>(value
);
54 DCHECK(value
== color
); // ensure value fits into color's 32 bits
58 // Conversion function for reading/writing to storage.
59 std::string
SkColorToRawString(SkColor color
) {
60 return base::Uint64ToString(color
);
63 // Conversion function for reading/writing to storage.
64 bool StringToSkBitmap(const std::string
& str
, SkBitmap
* bitmap
) {
65 // TODO(mpcomplete): Remove the base64 encode/decode step when
66 // http://crbug.com/140546 is fixed.
68 if (!base::Base64Decode(str
, &raw_str
))
71 bool success
= gfx::PNGCodec::Decode(
72 reinterpret_cast<unsigned const char*>(raw_str
.data()), raw_str
.size(),
77 // Conversion function for reading/writing to storage.
78 std::string
RepresentationToString(const gfx::ImageSkia
& image
, float scale
) {
79 SkBitmap bitmap
= image
.GetRepresentation(scale
).sk_bitmap();
80 SkAutoLockPixels
lock_image(bitmap
);
81 std::vector
<unsigned char> data
;
82 bool success
= gfx::PNGCodec::EncodeBGRASkBitmap(bitmap
, false, &data
);
86 base::StringPiece
raw_str(
87 reinterpret_cast<const char*>(&data
[0]), data
.size());
88 std::string base64_str
;
89 base::Base64Encode(raw_str
, &base64_str
);
93 // Set |action|'s default values to those specified in |dict|.
94 void SetDefaultsFromValue(const base::DictionaryValue
* dict
,
95 ExtensionAction
* action
) {
96 const int kDefaultTabId
= ExtensionAction::kDefaultTabId
;
97 std::string str_value
;
102 // For each value, don't set it if it has been modified already.
103 if (dict
->GetString(kPopupUrlStorageKey
, &str_value
) &&
104 !action
->HasPopupUrl(kDefaultTabId
)) {
105 action
->SetPopupUrl(kDefaultTabId
, GURL(str_value
));
107 if (dict
->GetString(kTitleStorageKey
, &str_value
) &&
108 !action
->HasTitle(kDefaultTabId
)) {
109 action
->SetTitle(kDefaultTabId
, str_value
);
111 if (dict
->GetString(kBadgeTextStorageKey
, &str_value
) &&
112 !action
->HasBadgeText(kDefaultTabId
)) {
113 action
->SetBadgeText(kDefaultTabId
, str_value
);
115 if (dict
->GetString(kBadgeBackgroundColorStorageKey
, &str_value
) &&
116 !action
->HasBadgeBackgroundColor(kDefaultTabId
)) {
117 action
->SetBadgeBackgroundColor(kDefaultTabId
,
118 RawStringToSkColor(str_value
));
120 if (dict
->GetString(kBadgeTextColorStorageKey
, &str_value
) &&
121 !action
->HasBadgeTextColor(kDefaultTabId
)) {
122 action
->SetBadgeTextColor(kDefaultTabId
, RawStringToSkColor(str_value
));
124 if (dict
->GetInteger(kAppearanceStorageKey
, &int_value
) &&
125 !action
->HasIsVisible(kDefaultTabId
)) {
128 case OBSOLETE_WANTS_ATTENTION
:
129 action
->SetIsVisible(kDefaultTabId
, false);
132 action
->SetIsVisible(kDefaultTabId
, true);
137 const base::DictionaryValue
* icon_value
= NULL
;
138 if (dict
->GetDictionary(kIconStorageKey
, &icon_value
) &&
139 !action
->HasIcon(kDefaultTabId
)) {
140 for (size_t i
= 0; i
< extension_misc::kNumExtensionActionIconSizes
; i
++) {
141 const extension_misc::IconRepresentationInfo
& icon_info
=
142 extension_misc::kExtensionActionIconSizes
[i
];
143 if (icon_value
->GetString(icon_info
.size_string
, &str_value
) &&
144 StringToSkBitmap(str_value
, &bitmap
)) {
145 CHECK(!bitmap
.isNull());
146 float scale
= ui::GetScaleForScaleFactor(icon_info
.scale
);
147 icon
.AddRepresentation(gfx::ImageSkiaRep(bitmap
, scale
));
150 action
->SetIcon(kDefaultTabId
, gfx::Image(icon
));
154 // Store |action|'s default values in a DictionaryValue for use in storing to
156 scoped_ptr
<base::DictionaryValue
> DefaultsToValue(ExtensionAction
* action
) {
157 const int kDefaultTabId
= ExtensionAction::kDefaultTabId
;
158 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
160 dict
->SetString(kPopupUrlStorageKey
,
161 action
->GetPopupUrl(kDefaultTabId
).spec());
162 dict
->SetString(kTitleStorageKey
, action
->GetTitle(kDefaultTabId
));
163 dict
->SetString(kBadgeTextStorageKey
, action
->GetBadgeText(kDefaultTabId
));
165 kBadgeBackgroundColorStorageKey
,
166 SkColorToRawString(action
->GetBadgeBackgroundColor(kDefaultTabId
)));
167 dict
->SetString(kBadgeTextColorStorageKey
,
168 SkColorToRawString(action
->GetBadgeTextColor(kDefaultTabId
)));
169 dict
->SetInteger(kAppearanceStorageKey
,
170 action
->GetIsVisible(kDefaultTabId
) ? ACTIVE
: INVISIBLE
);
172 gfx::ImageSkia icon
=
173 action
->GetExplicitlySetIcon(kDefaultTabId
).AsImageSkia();
174 if (!icon
.isNull()) {
175 scoped_ptr
<base::DictionaryValue
> icon_value(new base::DictionaryValue());
176 for (size_t i
= 0; i
< extension_misc::kNumExtensionActionIconSizes
; i
++) {
177 const extension_misc::IconRepresentationInfo
& icon_info
=
178 extension_misc::kExtensionActionIconSizes
[i
];
179 float scale
= ui::GetScaleForScaleFactor(icon_info
.scale
);
180 if (icon
.HasRepresentation(scale
)) {
181 icon_value
->SetString(icon_info
.size_string
,
182 RepresentationToString(icon
, scale
));
185 dict
->Set(kIconStorageKey
, icon_value
.release());
192 ExtensionActionStorageManager::ExtensionActionStorageManager(
193 content::BrowserContext
* context
)
194 : browser_context_(context
),
195 extension_action_observer_(this),
196 extension_registry_observer_(this),
197 weak_factory_(this) {
198 extension_action_observer_
.Add(ExtensionActionAPI::Get(browser_context_
));
199 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
201 StateStore
* store
= GetStateStore();
203 store
->RegisterKey(kBrowserActionStorageKey
);
206 ExtensionActionStorageManager::~ExtensionActionStorageManager() {
209 void ExtensionActionStorageManager::OnExtensionLoaded(
210 content::BrowserContext
* browser_context
,
211 const Extension
* extension
) {
212 if (!ExtensionActionManager::Get(browser_context_
)->GetBrowserAction(
216 StateStore
* store
= GetStateStore();
218 store
->GetExtensionValue(
220 kBrowserActionStorageKey
,
221 base::Bind(&ExtensionActionStorageManager::ReadFromStorage
,
222 weak_factory_
.GetWeakPtr(),
227 void ExtensionActionStorageManager::OnExtensionActionUpdated(
228 ExtensionAction
* extension_action
,
229 content::WebContents
* web_contents
,
230 content::BrowserContext
* browser_context
) {
231 if (browser_context_
== browser_context
&&
232 extension_action
->action_type() == ActionInfo::TYPE_BROWSER
)
233 WriteToStorage(extension_action
);
236 void ExtensionActionStorageManager::OnExtensionActionAPIShuttingDown() {
237 extension_action_observer_
.RemoveAll();
240 void ExtensionActionStorageManager::WriteToStorage(
241 ExtensionAction
* extension_action
) {
242 StateStore
* store
= GetStateStore();
244 scoped_ptr
<base::DictionaryValue
> defaults
=
245 DefaultsToValue(extension_action
);
246 store
->SetExtensionValue(extension_action
->extension_id(),
247 kBrowserActionStorageKey
,
252 void ExtensionActionStorageManager::ReadFromStorage(
253 const std::string
& extension_id
, scoped_ptr
<base::Value
> value
) {
254 const Extension
* extension
= ExtensionRegistry::Get(browser_context_
)->
255 enabled_extensions().GetByID(extension_id
);
259 ExtensionAction
* browser_action
=
260 ExtensionActionManager::Get(browser_context_
)->GetBrowserAction(
262 if (!browser_action
) {
263 // This can happen if the extension is updated between startup and when the
264 // storage read comes back, and the update removes the browser action.
265 // http://crbug.com/349371
269 const base::DictionaryValue
* dict
= NULL
;
270 if (!value
.get() || !value
->GetAsDictionary(&dict
))
273 SetDefaultsFromValue(dict
, browser_action
);
276 StateStore
* ExtensionActionStorageManager::GetStateStore() {
277 return ExtensionSystem::Get(browser_context_
)->state_store();
280 } // namespace extensions