OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "chrome/browser/extensions/extension_action_storage_manager.h" |
| 6 |
| 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" |
| 22 |
| 23 namespace extensions { |
| 24 |
| 25 namespace { |
| 26 |
| 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"; |
| 35 |
| 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, |
| 47 }; |
| 48 |
| 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; |
| 56 } |
| 57 |
| 58 // Conversion function for reading/writing to storage. |
| 59 std::string SkColorToRawString(SkColor color) { |
| 60 return base::Uint64ToString(color); |
| 61 } |
| 62 |
| 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; |
| 70 |
| 71 bool success = gfx::PNGCodec::Decode( |
| 72 reinterpret_cast<unsigned const char*>(raw_str.data()), raw_str.size(), |
| 73 bitmap); |
| 74 return success; |
| 75 } |
| 76 |
| 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(); |
| 85 |
| 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; |
| 91 } |
| 92 |
| 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; |
| 101 |
| 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)); |
| 106 } |
| 107 if (dict->GetString(kTitleStorageKey, &str_value) && |
| 108 !action->HasTitle(kDefaultTabId)) { |
| 109 action->SetTitle(kDefaultTabId, str_value); |
| 110 } |
| 111 if (dict->GetString(kBadgeTextStorageKey, &str_value) && |
| 112 !action->HasBadgeText(kDefaultTabId)) { |
| 113 action->SetBadgeText(kDefaultTabId, str_value); |
| 114 } |
| 115 if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value) && |
| 116 !action->HasBadgeBackgroundColor(kDefaultTabId)) { |
| 117 action->SetBadgeBackgroundColor(kDefaultTabId, |
| 118 RawStringToSkColor(str_value)); |
| 119 } |
| 120 if (dict->GetString(kBadgeTextColorStorageKey, &str_value) && |
| 121 !action->HasBadgeTextColor(kDefaultTabId)) { |
| 122 action->SetBadgeTextColor(kDefaultTabId, RawStringToSkColor(str_value)); |
| 123 } |
| 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; |
| 134 } |
| 135 } |
| 136 |
| 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)); |
| 148 } |
| 149 } |
| 150 action->SetIcon(kDefaultTabId, gfx::Image(icon)); |
| 151 } |
| 152 } |
| 153 |
| 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()); |
| 159 |
| 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); |
| 171 |
| 172 gfx::ImageSkia icon = action->GetExplicitlySetIcon(kDefaultTabId); |
| 173 if (!icon.isNull()) { |
| 174 scoped_ptr<base::DictionaryValue> icon_value(new base::DictionaryValue()); |
| 175 for (size_t i = 0; i < extension_misc::kNumExtensionActionIconSizes; i++) { |
| 176 const extension_misc::IconRepresentationInfo& icon_info = |
| 177 extension_misc::kExtensionActionIconSizes[i]; |
| 178 float scale = ui::GetScaleForScaleFactor(icon_info.scale); |
| 179 if (icon.HasRepresentation(scale)) { |
| 180 icon_value->SetString(icon_info.size_string, |
| 181 RepresentationToString(icon, scale)); |
| 182 } |
| 183 } |
| 184 dict->Set(kIconStorageKey, icon_value.release()); |
| 185 } |
| 186 return dict.Pass(); |
| 187 } |
| 188 |
| 189 } // namespace |
| 190 |
| 191 ExtensionActionStorageManager::ExtensionActionStorageManager( |
| 192 content::BrowserContext* context) |
| 193 : browser_context_(context), |
| 194 extension_action_observer_(this), |
| 195 extension_registry_observer_(this), |
| 196 weak_factory_(this) { |
| 197 extension_action_observer_.Add(ExtensionActionAPI::Get(browser_context_)); |
| 198 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
| 199 |
| 200 StateStore* store = GetStateStore(); |
| 201 if (store) |
| 202 store->RegisterKey(kBrowserActionStorageKey); |
| 203 } |
| 204 |
| 205 ExtensionActionStorageManager::~ExtensionActionStorageManager() { |
| 206 } |
| 207 |
| 208 void ExtensionActionStorageManager::OnExtensionLoaded( |
| 209 content::BrowserContext* browser_context, |
| 210 const Extension* extension) { |
| 211 if (!ExtensionActionManager::Get(browser_context_)->GetBrowserAction( |
| 212 *extension)) |
| 213 return; |
| 214 |
| 215 StateStore* store = GetStateStore(); |
| 216 if (store) { |
| 217 store->GetExtensionValue( |
| 218 extension->id(), |
| 219 kBrowserActionStorageKey, |
| 220 base::Bind(&ExtensionActionStorageManager::ReadFromStorage, |
| 221 weak_factory_.GetWeakPtr(), |
| 222 extension->id())); |
| 223 } |
| 224 } |
| 225 |
| 226 void ExtensionActionStorageManager::OnExtensionActionUpdated( |
| 227 ExtensionAction* extension_action, |
| 228 content::WebContents* web_contents, |
| 229 content::BrowserContext* browser_context) { |
| 230 if (browser_context_ == browser_context && |
| 231 extension_action->action_type() == ActionInfo::TYPE_BROWSER) |
| 232 WriteToStorage(extension_action); |
| 233 } |
| 234 |
| 235 void ExtensionActionStorageManager::OnExtensionActionAPIShuttingDown() { |
| 236 extension_action_observer_.RemoveAll(); |
| 237 } |
| 238 |
| 239 void ExtensionActionStorageManager::WriteToStorage( |
| 240 ExtensionAction* extension_action) { |
| 241 StateStore* store = GetStateStore(); |
| 242 if (store) { |
| 243 scoped_ptr<base::DictionaryValue> defaults = |
| 244 DefaultsToValue(extension_action); |
| 245 store->SetExtensionValue(extension_action->extension_id(), |
| 246 kBrowserActionStorageKey, |
| 247 defaults.PassAs<base::Value>()); |
| 248 } |
| 249 } |
| 250 |
| 251 void ExtensionActionStorageManager::ReadFromStorage( |
| 252 const std::string& extension_id, scoped_ptr<base::Value> value) { |
| 253 const Extension* extension = ExtensionRegistry::Get(browser_context_)-> |
| 254 enabled_extensions().GetByID(extension_id); |
| 255 if (!extension) |
| 256 return; |
| 257 |
| 258 ExtensionAction* browser_action = |
| 259 ExtensionActionManager::Get(browser_context_)->GetBrowserAction( |
| 260 *extension); |
| 261 if (!browser_action) { |
| 262 // This can happen if the extension is updated between startup and when the |
| 263 // storage read comes back, and the update removes the browser action. |
| 264 // http://crbug.com/349371 |
| 265 return; |
| 266 } |
| 267 |
| 268 const base::DictionaryValue* dict = NULL; |
| 269 if (!value.get() || !value->GetAsDictionary(&dict)) |
| 270 return; |
| 271 |
| 272 SetDefaultsFromValue(dict, browser_action); |
| 273 } |
| 274 |
| 275 StateStore* ExtensionActionStorageManager::GetStateStore() { |
| 276 return ExtensionSystem::Get(browser_context_)->state_store(); |
| 277 } |
| 278 |
| 279 } // namespace extensions |
OLD | NEW |