Index: chrome/browser/extensions/extension_action_storage_manager.cc |
diff --git a/chrome/browser/extensions/extension_action_storage_manager.cc b/chrome/browser/extensions/extension_action_storage_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..56ff5c4983678e09346a7b7870edc7ef07afc785 |
--- /dev/null |
+++ b/chrome/browser/extensions/extension_action_storage_manager.cc |
@@ -0,0 +1,279 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/extensions/extension_action_storage_manager.h" |
+ |
+#include "base/base64.h" |
+#include "base/bind.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/values.h" |
+#include "chrome/browser/extensions/extension_action.h" |
+#include "chrome/browser/extensions/extension_action_manager.h" |
+#include "extensions/browser/extension_registry.h" |
+#include "extensions/browser/extension_system.h" |
+#include "extensions/browser/state_store.h" |
+#include "extensions/common/constants.h" |
+#include "ui/base/layout.h" |
+#include "ui/gfx/codec/png_codec.h" |
+#include "ui/gfx/image/image.h" |
+#include "ui/gfx/image/image_skia.h" |
+ |
+namespace extensions { |
+ |
+namespace { |
+ |
+const char kBrowserActionStorageKey[] = "browser_action"; |
+const char kPopupUrlStorageKey[] = "poupup_url"; |
+const char kTitleStorageKey[] = "title"; |
+const char kIconStorageKey[] = "icon"; |
+const char kBadgeTextStorageKey[] = "badge_text"; |
+const char kBadgeBackgroundColorStorageKey[] = "badge_background_color"; |
+const char kBadgeTextColorStorageKey[] = "badge_text_color"; |
+const char kAppearanceStorageKey[] = "appearance"; |
+ |
+// Only add values to the end of this enum, since it's stored in the user's |
+// Extension State, under the kAppearanceStorageKey. It represents the |
+// ExtensionAction's default visibility. |
+enum StoredAppearance { |
+ // The action icon is hidden. |
+ INVISIBLE = 0, |
+ // The action is trying to get the user's attention but isn't yet |
+ // running on the page. Was only used for script badges. |
+ OBSOLETE_WANTS_ATTENTION = 1, |
+ // The action icon is visible with its normal appearance. |
+ ACTIVE = 2, |
+}; |
+ |
+// Conversion function for reading/writing to storage. |
+SkColor RawStringToSkColor(const std::string& str) { |
+ uint64 value = 0; |
+ base::StringToUint64(str, &value); |
+ SkColor color = static_cast<SkColor>(value); |
+ DCHECK(value == color); // ensure value fits into color's 32 bits |
+ return color; |
+} |
+ |
+// Conversion function for reading/writing to storage. |
+std::string SkColorToRawString(SkColor color) { |
+ return base::Uint64ToString(color); |
+} |
+ |
+// Conversion function for reading/writing to storage. |
+bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) { |
+ // TODO(mpcomplete): Remove the base64 encode/decode step when |
+ // http://crbug.com/140546 is fixed. |
+ std::string raw_str; |
+ if (!base::Base64Decode(str, &raw_str)) |
+ return false; |
+ |
+ bool success = gfx::PNGCodec::Decode( |
+ reinterpret_cast<unsigned const char*>(raw_str.data()), raw_str.size(), |
+ bitmap); |
+ return success; |
+} |
+ |
+// Conversion function for reading/writing to storage. |
+std::string RepresentationToString(const gfx::ImageSkia& image, float scale) { |
+ SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap(); |
+ SkAutoLockPixels lock_image(bitmap); |
+ std::vector<unsigned char> data; |
+ bool success = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data); |
+ if (!success) |
+ return std::string(); |
+ |
+ base::StringPiece raw_str( |
+ reinterpret_cast<const char*>(&data[0]), data.size()); |
+ std::string base64_str; |
+ base::Base64Encode(raw_str, &base64_str); |
+ return base64_str; |
+} |
+ |
+// Set |action|'s default values to those specified in |dict|. |
+void SetDefaultsFromValue(const base::DictionaryValue* dict, |
+ ExtensionAction* action) { |
+ const int kDefaultTabId = ExtensionAction::kDefaultTabId; |
+ std::string str_value; |
+ int int_value; |
+ SkBitmap bitmap; |
+ gfx::ImageSkia icon; |
+ |
+ // For each value, don't set it if it has been modified already. |
+ if (dict->GetString(kPopupUrlStorageKey, &str_value) && |
+ !action->HasPopupUrl(kDefaultTabId)) { |
+ action->SetPopupUrl(kDefaultTabId, GURL(str_value)); |
+ } |
+ if (dict->GetString(kTitleStorageKey, &str_value) && |
+ !action->HasTitle(kDefaultTabId)) { |
+ action->SetTitle(kDefaultTabId, str_value); |
+ } |
+ if (dict->GetString(kBadgeTextStorageKey, &str_value) && |
+ !action->HasBadgeText(kDefaultTabId)) { |
+ action->SetBadgeText(kDefaultTabId, str_value); |
+ } |
+ if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value) && |
+ !action->HasBadgeBackgroundColor(kDefaultTabId)) { |
+ action->SetBadgeBackgroundColor(kDefaultTabId, |
+ RawStringToSkColor(str_value)); |
+ } |
+ if (dict->GetString(kBadgeTextColorStorageKey, &str_value) && |
+ !action->HasBadgeTextColor(kDefaultTabId)) { |
+ action->SetBadgeTextColor(kDefaultTabId, RawStringToSkColor(str_value)); |
+ } |
+ if (dict->GetInteger(kAppearanceStorageKey, &int_value) && |
+ !action->HasIsVisible(kDefaultTabId)) { |
+ switch (int_value) { |
+ case INVISIBLE: |
+ case OBSOLETE_WANTS_ATTENTION: |
+ action->SetIsVisible(kDefaultTabId, false); |
+ break; |
+ case ACTIVE: |
+ action->SetIsVisible(kDefaultTabId, true); |
+ break; |
+ } |
+ } |
+ |
+ const base::DictionaryValue* icon_value = NULL; |
+ if (dict->GetDictionary(kIconStorageKey, &icon_value) && |
+ !action->HasIcon(kDefaultTabId)) { |
+ for (size_t i = 0; i < extension_misc::kNumExtensionActionIconSizes; i++) { |
+ const extension_misc::IconRepresentationInfo& icon_info = |
+ extension_misc::kExtensionActionIconSizes[i]; |
+ if (icon_value->GetString(icon_info.size_string, &str_value) && |
+ StringToSkBitmap(str_value, &bitmap)) { |
+ CHECK(!bitmap.isNull()); |
+ float scale = ui::GetScaleForScaleFactor(icon_info.scale); |
+ icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale)); |
+ } |
+ } |
+ action->SetIcon(kDefaultTabId, gfx::Image(icon)); |
+ } |
+} |
+ |
+// Store |action|'s default values in a DictionaryValue for use in storing to |
+// disk. |
+scoped_ptr<base::DictionaryValue> DefaultsToValue(ExtensionAction* action) { |
+ const int kDefaultTabId = ExtensionAction::kDefaultTabId; |
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
+ |
+ dict->SetString(kPopupUrlStorageKey, |
+ action->GetPopupUrl(kDefaultTabId).spec()); |
+ dict->SetString(kTitleStorageKey, action->GetTitle(kDefaultTabId)); |
+ dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kDefaultTabId)); |
+ dict->SetString( |
+ kBadgeBackgroundColorStorageKey, |
+ SkColorToRawString(action->GetBadgeBackgroundColor(kDefaultTabId))); |
+ dict->SetString(kBadgeTextColorStorageKey, |
+ SkColorToRawString(action->GetBadgeTextColor(kDefaultTabId))); |
+ dict->SetInteger(kAppearanceStorageKey, |
+ action->GetIsVisible(kDefaultTabId) ? ACTIVE : INVISIBLE); |
+ |
+ gfx::ImageSkia icon = action->GetExplicitlySetIcon(kDefaultTabId); |
+ if (!icon.isNull()) { |
+ scoped_ptr<base::DictionaryValue> icon_value(new base::DictionaryValue()); |
+ for (size_t i = 0; i < extension_misc::kNumExtensionActionIconSizes; i++) { |
+ const extension_misc::IconRepresentationInfo& icon_info = |
+ extension_misc::kExtensionActionIconSizes[i]; |
+ float scale = ui::GetScaleForScaleFactor(icon_info.scale); |
+ if (icon.HasRepresentation(scale)) { |
+ icon_value->SetString(icon_info.size_string, |
+ RepresentationToString(icon, scale)); |
+ } |
+ } |
+ dict->Set(kIconStorageKey, icon_value.release()); |
+ } |
+ return dict.Pass(); |
+} |
+ |
+} // namespace |
+ |
+ExtensionActionStorageManager::ExtensionActionStorageManager( |
+ content::BrowserContext* context) |
+ : browser_context_(context), |
+ extension_action_observer_(this), |
+ extension_registry_observer_(this), |
+ weak_factory_(this) { |
+ extension_action_observer_.Add(ExtensionActionAPI::Get(browser_context_)); |
+ extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
+ |
+ StateStore* store = GetStateStore(); |
+ if (store) |
+ store->RegisterKey(kBrowserActionStorageKey); |
+} |
+ |
+ExtensionActionStorageManager::~ExtensionActionStorageManager() { |
+} |
+ |
+void ExtensionActionStorageManager::OnExtensionLoaded( |
+ content::BrowserContext* browser_context, |
+ const Extension* extension) { |
+ if (!ExtensionActionManager::Get(browser_context_)->GetBrowserAction( |
+ *extension)) |
+ return; |
+ |
+ StateStore* store = GetStateStore(); |
+ if (store) { |
+ store->GetExtensionValue( |
+ extension->id(), |
+ kBrowserActionStorageKey, |
+ base::Bind(&ExtensionActionStorageManager::ReadFromStorage, |
+ weak_factory_.GetWeakPtr(), |
+ extension->id())); |
+ } |
+} |
+ |
+void ExtensionActionStorageManager::OnExtensionActionUpdated( |
+ ExtensionAction* extension_action, |
+ content::WebContents* web_contents, |
+ content::BrowserContext* browser_context) { |
+ if (browser_context_ == browser_context && |
+ extension_action->action_type() == ActionInfo::TYPE_BROWSER) |
+ WriteToStorage(extension_action); |
+} |
+ |
+void ExtensionActionStorageManager::OnExtensionActionAPIShuttingDown() { |
+ extension_action_observer_.RemoveAll(); |
+} |
+ |
+void ExtensionActionStorageManager::WriteToStorage( |
+ ExtensionAction* extension_action) { |
+ StateStore* store = GetStateStore(); |
+ if (store) { |
+ scoped_ptr<base::DictionaryValue> defaults = |
+ DefaultsToValue(extension_action); |
+ store->SetExtensionValue(extension_action->extension_id(), |
+ kBrowserActionStorageKey, |
+ defaults.PassAs<base::Value>()); |
+ } |
+} |
+ |
+void ExtensionActionStorageManager::ReadFromStorage( |
+ const std::string& extension_id, scoped_ptr<base::Value> value) { |
+ const Extension* extension = ExtensionRegistry::Get(browser_context_)-> |
+ enabled_extensions().GetByID(extension_id); |
+ if (!extension) |
+ return; |
+ |
+ ExtensionAction* browser_action = |
+ ExtensionActionManager::Get(browser_context_)->GetBrowserAction( |
+ *extension); |
+ if (!browser_action) { |
+ // This can happen if the extension is updated between startup and when the |
+ // storage read comes back, and the update removes the browser action. |
+ // http://crbug.com/349371 |
+ return; |
+ } |
+ |
+ const base::DictionaryValue* dict = NULL; |
+ if (!value.get() || !value->GetAsDictionary(&dict)) |
+ return; |
+ |
+ SetDefaultsFromValue(dict, browser_action); |
+} |
+ |
+StateStore* ExtensionActionStorageManager::GetStateStore() { |
+ return ExtensionSystem::Get(browser_context_)->state_store(); |
+} |
+ |
+} // namespace extensions |