Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_action_storage_manager.cc
bloba7de1e6a51eb3674fdcc7152105db9fc9d63b7fc
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"
8 #include "base/bind.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 {
25 namespace {
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.
41 INVISIBLE = 0,
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.
46 ACTIVE = 2,
49 // Conversion function for reading/writing to storage.
50 SkColor RawStringToSkColor(const std::string& str) {
51 uint64 value = 0;
52 base::StringToUint64(str, &value);
53 SkColor color = static_cast<SkColor>(value);
54 DCHECK(value == color); // ensure value fits into color's 32 bits
55 return color;
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.
67 std::string raw_str;
68 if (!base::Base64Decode(str, &raw_str))
69 return false;
71 bool success = gfx::PNGCodec::Decode(
72 reinterpret_cast<unsigned const char*>(raw_str.data()), raw_str.size(),
73 bitmap);
74 return success;
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);
83 if (!success)
84 return std::string();
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);
90 return 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;
98 int int_value;
99 SkBitmap bitmap;
100 gfx::ImageSkia icon;
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)) {
126 switch (int_value) {
127 case INVISIBLE:
128 case OBSOLETE_WANTS_ATTENTION:
129 action->SetIsVisible(kDefaultTabId, false);
130 break;
131 case ACTIVE:
132 action->SetIsVisible(kDefaultTabId, true);
133 break;
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
155 // disk.
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));
164 dict->SetString(
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());
187 return dict.Pass();
190 } // namespace
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();
202 if (store)
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(
213 *extension))
214 return;
216 StateStore* store = GetStateStore();
217 if (store) {
218 store->GetExtensionValue(
219 extension->id(),
220 kBrowserActionStorageKey,
221 base::Bind(&ExtensionActionStorageManager::ReadFromStorage,
222 weak_factory_.GetWeakPtr(),
223 extension->id()));
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();
243 if (store) {
244 scoped_ptr<base::DictionaryValue> defaults =
245 DefaultsToValue(extension_action);
246 store->SetExtensionValue(extension_action->extension_id(),
247 kBrowserActionStorageKey,
248 defaults.Pass());
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);
256 if (!extension)
257 return;
259 ExtensionAction* browser_action =
260 ExtensionActionManager::Get(browser_context_)->GetBrowserAction(
261 *extension);
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
266 return;
269 const base::DictionaryValue* dict = NULL;
270 if (!value.get() || !value->GetAsDictionary(&dict))
271 return;
273 SetDefaultsFromValue(dict, browser_action);
276 StateStore* ExtensionActionStorageManager::GetStateStore() {
277 return ExtensionSystem::Get(browser_context_)->state_store();
280 } // namespace extensions