Home | History | Annotate | Download | only in extensions
      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
    280