Home | History | Annotate | Download | only in extension_action
      1 // Copyright (c) 2012 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/api/extension_action/extension_action_api.h"
      6 
      7 #include <string>
      8 
      9 #include "base/base64.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/chrome_notification_types.h"
     15 #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h"
     16 #include "chrome/browser/extensions/extension_action.h"
     17 #include "chrome/browser/extensions/extension_action_manager.h"
     18 #include "chrome/browser/extensions/extension_function_registry.h"
     19 #include "chrome/browser/extensions/extension_service.h"
     20 #include "chrome/browser/extensions/extension_system.h"
     21 #include "chrome/browser/extensions/extension_tab_util.h"
     22 #include "chrome/browser/extensions/location_bar_controller.h"
     23 #include "chrome/browser/extensions/state_store.h"
     24 #include "chrome/browser/extensions/tab_helper.h"
     25 #include "chrome/browser/profiles/profile.h"
     26 #include "chrome/common/extensions/api/extension_action/action_info.h"
     27 #include "chrome/common/render_messages.h"
     28 #include "content/public/browser/navigation_entry.h"
     29 #include "content/public/browser/notification_service.h"
     30 #include "extensions/common/error_utils.h"
     31 #include "ui/gfx/image/image.h"
     32 #include "ui/gfx/image/image_skia.h"
     33 
     34 namespace extensions {
     35 
     36 namespace {
     37 
     38 const char kBrowserActionStorageKey[] = "browser_action";
     39 const char kPopupUrlStorageKey[] = "poupup_url";
     40 const char kTitleStorageKey[] = "title";
     41 const char kIconStorageKey[] = "icon";
     42 const char kBadgeTextStorageKey[] = "badge_text";
     43 const char kBadgeBackgroundColorStorageKey[] = "badge_background_color";
     44 const char kBadgeTextColorStorageKey[] = "badge_text_color";
     45 const char kAppearanceStorageKey[] = "appearance";
     46 
     47 // Whether the browser action is visible in the toolbar.
     48 const char kBrowserActionVisible[] = "browser_action_visible";
     49 
     50 // Errors.
     51 const char kNoExtensionActionError[] =
     52     "This extension has no action specified.";
     53 const char kNoTabError[] = "No tab with id: *.";
     54 const char kNoPageActionError[] =
     55     "This extension has no page action specified.";
     56 const char kUrlNotActiveError[] = "This url is no longer active: *.";
     57 
     58 struct IconRepresentationInfo {
     59   // Size as a string that will be used to retrieve representation value from
     60   // SetIcon function arguments.
     61   const char* size_string;
     62   // Scale factor for which the represantion should be used.
     63   ui::ScaleFactor scale;
     64 };
     65 
     66 const IconRepresentationInfo kIconSizes[] = {
     67     { "19", ui::SCALE_FACTOR_100P },
     68     { "38", ui::SCALE_FACTOR_200P }
     69 };
     70 
     71 // Conversion function for reading/writing to storage.
     72 SkColor RawStringToSkColor(const std::string& str) {
     73   uint64 value = 0;
     74   base::StringToUint64(str, &value);
     75   SkColor color = static_cast<SkColor>(value);
     76   DCHECK(value == color);  // ensure value fits into color's 32 bits
     77   return color;
     78 }
     79 
     80 // Conversion function for reading/writing to storage.
     81 std::string SkColorToRawString(SkColor color) {
     82   return base::Uint64ToString(color);
     83 }
     84 
     85 // Conversion function for reading/writing to storage.
     86 bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) {
     87   // TODO(mpcomplete): Remove the base64 encode/decode step when
     88   // http://crbug.com/140546 is fixed.
     89   std::string raw_str;
     90   if (!base::Base64Decode(str, &raw_str))
     91     return false;
     92   IPC::Message bitmap_pickle(raw_str.data(), raw_str.size());
     93   PickleIterator iter(bitmap_pickle);
     94   return IPC::ReadParam(&bitmap_pickle, &iter, bitmap);
     95 }
     96 
     97 // Conversion function for reading/writing to storage.
     98 std::string RepresentationToString(const gfx::ImageSkia& image,
     99                                    ui::ScaleFactor scale) {
    100   SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap();
    101   IPC::Message bitmap_pickle;
    102   // Clear the header values so they don't vary in serialization.
    103   bitmap_pickle.SetHeaderValues(0, 0, 0);
    104   IPC::WriteParam(&bitmap_pickle, bitmap);
    105   std::string raw_str(static_cast<const char*>(bitmap_pickle.data()),
    106                       bitmap_pickle.size());
    107   std::string base64_str;
    108   if (!base::Base64Encode(raw_str, &base64_str))
    109     return std::string();
    110   return base64_str;
    111 }
    112 
    113 // Set |action|'s default values to those specified in |dict|.
    114 void SetDefaultsFromValue(const base::DictionaryValue* dict,
    115                           ExtensionAction* action) {
    116   const int kTabId = ExtensionAction::kDefaultTabId;
    117   std::string str_value;
    118   int int_value;
    119   SkBitmap bitmap;
    120   gfx::ImageSkia icon;
    121 
    122   if (dict->GetString(kPopupUrlStorageKey, &str_value))
    123     action->SetPopupUrl(kTabId, GURL(str_value));
    124   if (dict->GetString(kTitleStorageKey, &str_value))
    125     action->SetTitle(kTabId, str_value);
    126   if (dict->GetString(kBadgeTextStorageKey, &str_value))
    127     action->SetBadgeText(kTabId, str_value);
    128   if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value))
    129     action->SetBadgeBackgroundColor(kTabId, RawStringToSkColor(str_value));
    130   if (dict->GetString(kBadgeTextColorStorageKey, &str_value))
    131     action->SetBadgeTextColor(kTabId, RawStringToSkColor(str_value));
    132   if (dict->GetInteger(kAppearanceStorageKey, &int_value))
    133     action->SetAppearance(kTabId,
    134                           static_cast<ExtensionAction::Appearance>(int_value));
    135 
    136   const base::DictionaryValue* icon_value = NULL;
    137   if (dict->GetDictionary(kIconStorageKey, &icon_value)) {
    138     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
    139       if (icon_value->GetString(kIconSizes[i].size_string, &str_value) &&
    140           StringToSkBitmap(str_value, &bitmap)) {
    141         CHECK(!bitmap.isNull());
    142         icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, kIconSizes[i].scale));
    143       }
    144     }
    145     action->SetIcon(kTabId, gfx::Image(icon));
    146   }
    147 }
    148 
    149 // Store |action|'s default values in a DictionaryValue for use in storing to
    150 // disk.
    151 scoped_ptr<base::DictionaryValue> DefaultsToValue(ExtensionAction* action) {
    152   const int kTabId = ExtensionAction::kDefaultTabId;
    153   scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
    154 
    155   dict->SetString(kPopupUrlStorageKey, action->GetPopupUrl(kTabId).spec());
    156   dict->SetString(kTitleStorageKey, action->GetTitle(kTabId));
    157   dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kTabId));
    158   dict->SetString(kBadgeBackgroundColorStorageKey,
    159                   SkColorToRawString(action->GetBadgeBackgroundColor(kTabId)));
    160   dict->SetString(kBadgeTextColorStorageKey,
    161                   SkColorToRawString(action->GetBadgeTextColor(kTabId)));
    162   dict->SetInteger(kAppearanceStorageKey,
    163                    action->GetIsVisible(kTabId) ?
    164                        ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
    165 
    166   gfx::ImageSkia icon = action->GetExplicitlySetIcon(kTabId);
    167   if (!icon.isNull()) {
    168     base::DictionaryValue* icon_value = new base::DictionaryValue();
    169     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
    170       if (icon.HasRepresentation(kIconSizes[i].scale)) {
    171         icon_value->SetString(
    172             kIconSizes[i].size_string,
    173             RepresentationToString(icon, kIconSizes[i].scale));
    174       }
    175     }
    176     dict->Set(kIconStorageKey, icon_value);
    177   }
    178   return dict.Pass();
    179 }
    180 
    181 }  // namespace
    182 
    183 //
    184 // ExtensionActionAPI
    185 //
    186 
    187 static base::LazyInstance<ProfileKeyedAPIFactory<ExtensionActionAPI> >
    188     g_factory = LAZY_INSTANCE_INITIALIZER;
    189 
    190 ExtensionActionAPI::ExtensionActionAPI(Profile* profile) {
    191   ExtensionFunctionRegistry* registry =
    192       ExtensionFunctionRegistry::GetInstance();
    193 
    194   // Browser Actions
    195   registry->RegisterFunction<BrowserActionSetIconFunction>();
    196   registry->RegisterFunction<BrowserActionSetTitleFunction>();
    197   registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
    198   registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
    199   registry->RegisterFunction<BrowserActionSetPopupFunction>();
    200   registry->RegisterFunction<BrowserActionGetTitleFunction>();
    201   registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
    202   registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
    203   registry->RegisterFunction<BrowserActionGetPopupFunction>();
    204   registry->RegisterFunction<BrowserActionEnableFunction>();
    205   registry->RegisterFunction<BrowserActionDisableFunction>();
    206 
    207   // Page Actions
    208   registry->RegisterFunction<EnablePageActionsFunction>();
    209   registry->RegisterFunction<DisablePageActionsFunction>();
    210   registry->RegisterFunction<PageActionShowFunction>();
    211   registry->RegisterFunction<PageActionHideFunction>();
    212   registry->RegisterFunction<PageActionSetIconFunction>();
    213   registry->RegisterFunction<PageActionSetTitleFunction>();
    214   registry->RegisterFunction<PageActionSetPopupFunction>();
    215   registry->RegisterFunction<PageActionGetTitleFunction>();
    216   registry->RegisterFunction<PageActionGetPopupFunction>();
    217 
    218   // Script Badges
    219   registry->RegisterFunction<ScriptBadgeGetAttentionFunction>();
    220   registry->RegisterFunction<ScriptBadgeGetPopupFunction>();
    221   registry->RegisterFunction<ScriptBadgeSetPopupFunction>();
    222 }
    223 
    224 ExtensionActionAPI::~ExtensionActionAPI() {
    225 }
    226 
    227 // static
    228 ProfileKeyedAPIFactory<ExtensionActionAPI>*
    229 ExtensionActionAPI::GetFactoryInstance() {
    230   return &g_factory.Get();
    231 }
    232 
    233 // static
    234 bool ExtensionActionAPI::GetBrowserActionVisibility(
    235     const ExtensionPrefs* prefs,
    236     const std::string& extension_id) {
    237   bool visible = false;
    238   if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
    239                                           kBrowserActionVisible,
    240                                           &visible)) {
    241     return true;
    242   }
    243   return visible;
    244 }
    245 
    246 // static
    247 void ExtensionActionAPI::SetBrowserActionVisibility(
    248     ExtensionPrefs* prefs,
    249     const std::string& extension_id,
    250     bool visible) {
    251   if (GetBrowserActionVisibility(prefs, extension_id) == visible)
    252     return;
    253 
    254   prefs->UpdateExtensionPref(extension_id,
    255                              kBrowserActionVisible,
    256                              Value::CreateBooleanValue(visible));
    257   content::NotificationService::current()->Notify(
    258       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
    259       content::Source<ExtensionPrefs>(prefs),
    260       content::Details<const std::string>(&extension_id));
    261 }
    262 
    263 //
    264 // ExtensionActionStorageManager
    265 //
    266 
    267 ExtensionActionStorageManager::ExtensionActionStorageManager(Profile* profile)
    268     : profile_(profile) {
    269   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
    270                  content::Source<Profile>(profile_));
    271   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
    272                  content::NotificationService::AllBrowserContextsAndSources());
    273 
    274   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
    275   if (storage)
    276     storage->RegisterKey(kBrowserActionStorageKey);
    277 }
    278 
    279 ExtensionActionStorageManager::~ExtensionActionStorageManager() {
    280 }
    281 
    282 void ExtensionActionStorageManager::Observe(
    283     int type,
    284     const content::NotificationSource& source,
    285     const content::NotificationDetails& details) {
    286   switch (type) {
    287     case chrome::NOTIFICATION_EXTENSION_LOADED: {
    288       const Extension* extension =
    289           content::Details<const Extension>(details).ptr();
    290       if (!ExtensionActionManager::Get(profile_)->
    291           GetBrowserAction(*extension)) {
    292         break;
    293       }
    294 
    295       StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
    296       if (storage) {
    297         storage->GetExtensionValue(extension->id(), kBrowserActionStorageKey,
    298             base::Bind(&ExtensionActionStorageManager::ReadFromStorage,
    299                        AsWeakPtr(), extension->id()));
    300       }
    301       break;
    302     }
    303     case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED: {
    304       ExtensionAction* extension_action =
    305           content::Source<ExtensionAction>(source).ptr();
    306       Profile* profile = content::Details<Profile>(details).ptr();
    307       if (profile != profile_)
    308         break;
    309 
    310       extension_action->set_has_changed(true);
    311       WriteToStorage(extension_action);
    312       break;
    313     }
    314     default:
    315       NOTREACHED();
    316       break;
    317   }
    318 }
    319 
    320 void ExtensionActionStorageManager::WriteToStorage(
    321     ExtensionAction* extension_action) {
    322   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
    323   if (!storage)
    324     return;
    325 
    326   scoped_ptr<base::DictionaryValue> defaults =
    327       DefaultsToValue(extension_action);
    328   storage->SetExtensionValue(extension_action->extension_id(),
    329                              kBrowserActionStorageKey,
    330                              defaults.PassAs<base::Value>());
    331 }
    332 
    333 void ExtensionActionStorageManager::ReadFromStorage(
    334     const std::string& extension_id, scoped_ptr<base::Value> value) {
    335   const Extension* extension =
    336       ExtensionSystem::Get(profile_)->extension_service()->
    337       extensions()->GetByID(extension_id);
    338   if (!extension)
    339     return;
    340 
    341   ExtensionAction* browser_action =
    342       ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
    343   CHECK(browser_action);
    344 
    345   // Don't load values from storage if the extension has updated a value
    346   // already. The extension may have only updated some of the values, but
    347   // this is a good first approximation. If the extension is doing stuff
    348   // to the browser action, we can assume it is ready to take over.
    349   if (browser_action->has_changed())
    350     return;
    351 
    352   const base::DictionaryValue* dict = NULL;
    353   if (!value.get() || !value->GetAsDictionary(&dict))
    354     return;
    355 
    356   SetDefaultsFromValue(dict, browser_action);
    357 }
    358 
    359 //
    360 // ExtensionActionFunction
    361 //
    362 
    363 ExtensionActionFunction::ExtensionActionFunction()
    364     : details_(NULL),
    365       tab_id_(ExtensionAction::kDefaultTabId),
    366       contents_(NULL),
    367       extension_action_(NULL) {
    368 }
    369 
    370 ExtensionActionFunction::~ExtensionActionFunction() {
    371 }
    372 
    373 bool ExtensionActionFunction::RunImpl() {
    374   ExtensionActionManager* manager = ExtensionActionManager::Get(profile_);
    375   const Extension* extension = GetExtension();
    376   if (StartsWithASCII(name(), "scriptBadge.", false)) {
    377     extension_action_ = manager->GetScriptBadge(*extension);
    378   } else if (StartsWithASCII(name(), "systemIndicator.", false)) {
    379     extension_action_ = manager->GetSystemIndicator(*extension);
    380   } else {
    381     extension_action_ = manager->GetBrowserAction(*extension);
    382     if (!extension_action_) {
    383       extension_action_ = manager->GetPageAction(*extension);
    384     }
    385   }
    386   if (!extension_action_) {
    387     // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
    388     // exist for extensions that don't have one declared. This should come as
    389     // part of the Feature system.
    390     error_ = kNoExtensionActionError;
    391     return false;
    392   }
    393 
    394   // Populates the tab_id_ and details_ members.
    395   EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
    396 
    397   // Find the WebContents that contains this tab id if one is required.
    398   if (tab_id_ != ExtensionAction::kDefaultTabId) {
    399     ExtensionTabUtil::GetTabById(
    400         tab_id_, profile(), include_incognito(), NULL, NULL, &contents_, NULL);
    401     if (!contents_) {
    402       error_ = ErrorUtils::FormatErrorMessage(
    403           kNoTabError, base::IntToString(tab_id_));
    404       return false;
    405     }
    406   } else {
    407     // Only browser actions and system indicators have a default tabId.
    408     ActionInfo::Type action_type = extension_action_->action_type();
    409     EXTENSION_FUNCTION_VALIDATE(
    410         action_type == ActionInfo::TYPE_BROWSER ||
    411         action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
    412   }
    413   return RunExtensionAction();
    414 }
    415 
    416 bool ExtensionActionFunction::ExtractDataFromArguments() {
    417   // There may or may not be details (depends on the function).
    418   // The tabId might appear in details (if it exists), as the first
    419   // argument besides the action type (depends on the function), or be omitted
    420   // entirely.
    421   base::Value* first_arg = NULL;
    422   if (!args_->Get(0, &first_arg))
    423     return true;
    424 
    425   switch (first_arg->GetType()) {
    426     case Value::TYPE_INTEGER:
    427       CHECK(first_arg->GetAsInteger(&tab_id_));
    428       break;
    429 
    430     case Value::TYPE_DICTIONARY: {
    431       // Found the details argument.
    432       details_ = static_cast<base::DictionaryValue*>(first_arg);
    433       // Still need to check for the tabId within details.
    434       base::Value* tab_id_value = NULL;
    435       if (details_->Get("tabId", &tab_id_value)) {
    436         switch (tab_id_value->GetType()) {
    437           case Value::TYPE_NULL:
    438             // OK; tabId is optional, leave it default.
    439             return true;
    440           case Value::TYPE_INTEGER:
    441             CHECK(tab_id_value->GetAsInteger(&tab_id_));
    442             return true;
    443           default:
    444             // Boom.
    445             return false;
    446         }
    447       }
    448       // Not found; tabId is optional, leave it default.
    449       break;
    450     }
    451 
    452     case Value::TYPE_NULL:
    453       // The tabId might be an optional argument.
    454       break;
    455 
    456     default:
    457       return false;
    458   }
    459 
    460   return true;
    461 }
    462 
    463 void ExtensionActionFunction::NotifyChange() {
    464   switch (extension_action_->action_type()) {
    465     case ActionInfo::TYPE_BROWSER:
    466     case ActionInfo::TYPE_PAGE:
    467       if (ExtensionActionManager::Get(profile_)
    468               ->GetBrowserAction(*extension_.get())) {
    469         NotifyBrowserActionChange();
    470       } else if (ExtensionActionManager::Get(profile_)
    471                      ->GetPageAction(*extension_.get())) {
    472         NotifyLocationBarChange();
    473       }
    474       return;
    475     case ActionInfo::TYPE_SCRIPT_BADGE:
    476       NotifyLocationBarChange();
    477       return;
    478     case ActionInfo::TYPE_SYSTEM_INDICATOR:
    479       NotifySystemIndicatorChange();
    480       return;
    481   }
    482   NOTREACHED();
    483 }
    484 
    485 void ExtensionActionFunction::NotifyBrowserActionChange() {
    486   content::NotificationService::current()->Notify(
    487       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
    488       content::Source<ExtensionAction>(extension_action_),
    489       content::Details<Profile>(profile()));
    490 }
    491 
    492 void ExtensionActionFunction::NotifyLocationBarChange() {
    493   TabHelper::FromWebContents(contents_)->
    494       location_bar_controller()->NotifyChange();
    495 }
    496 
    497 void ExtensionActionFunction::NotifySystemIndicatorChange() {
    498   content::NotificationService::current()->Notify(
    499       chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
    500       content::Source<Profile>(profile()),
    501       content::Details<ExtensionAction>(extension_action_));
    502 }
    503 
    504 // static
    505 bool ExtensionActionFunction::ParseCSSColorString(
    506     const std::string& color_string,
    507     SkColor* result) {
    508   std::string formatted_color = "#";
    509   // Check the string for incorrect formatting.
    510   if (color_string[0] != '#')
    511     return false;
    512 
    513   // Convert the string from #FFF format to #FFFFFF format.
    514   if (color_string.length() == 4) {
    515     for (size_t i = 1; i < color_string.length(); i++) {
    516       formatted_color += color_string[i];
    517       formatted_color += color_string[i];
    518     }
    519   } else {
    520     formatted_color = color_string;
    521   }
    522 
    523   if (formatted_color.length() != 7)
    524     return false;
    525 
    526   // Convert the string to an integer and make sure it is in the correct value
    527   // range.
    528   int color_ints[3] = {0};
    529   for (int i = 0; i < 3; i++) {
    530     if (!base::HexStringToInt(formatted_color.substr(1 + (2 * i), 2),
    531                               color_ints + i))
    532       return false;
    533     if (color_ints[i] > 255 || color_ints[i] < 0)
    534       return false;
    535   }
    536 
    537   *result = SkColorSetARGB(255, color_ints[0], color_ints[1], color_ints[2]);
    538   return true;
    539 }
    540 
    541 bool ExtensionActionFunction::SetVisible(bool visible) {
    542   if (extension_action_->GetIsVisible(tab_id_) == visible)
    543     return true;
    544   extension_action_->SetAppearance(
    545       tab_id_, visible ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
    546   NotifyChange();
    547   return true;
    548 }
    549 
    550 TabHelper& ExtensionActionFunction::tab_helper() const {
    551   CHECK(contents_);
    552   return *TabHelper::FromWebContents(contents_);
    553 }
    554 
    555 bool ExtensionActionShowFunction::RunExtensionAction() {
    556   return SetVisible(true);
    557 }
    558 
    559 bool ExtensionActionHideFunction::RunExtensionAction() {
    560   return SetVisible(false);
    561 }
    562 
    563 bool ExtensionActionSetIconFunction::RunExtensionAction() {
    564   EXTENSION_FUNCTION_VALIDATE(details_);
    565 
    566   // setIcon can take a variant argument: either a dictionary of canvas
    567   // ImageData, or an icon index.
    568   base::DictionaryValue* canvas_set = NULL;
    569   int icon_index;
    570   if (details_->GetDictionary("imageData", &canvas_set)) {
    571     gfx::ImageSkia icon;
    572     // Extract icon representations from the ImageDataSet dictionary.
    573     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
    574       base::BinaryValue* binary;
    575       if (canvas_set->GetBinary(kIconSizes[i].size_string, &binary)) {
    576         IPC::Message pickle(binary->GetBuffer(), binary->GetSize());
    577         PickleIterator iter(pickle);
    578         SkBitmap bitmap;
    579         EXTENSION_FUNCTION_VALIDATE(IPC::ReadParam(&pickle, &iter, &bitmap));
    580         CHECK(!bitmap.isNull());
    581         icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, kIconSizes[i].scale));
    582       }
    583     }
    584 
    585     extension_action_->SetIcon(tab_id_, gfx::Image(icon));
    586   } else if (details_->GetInteger("iconIndex", &icon_index)) {
    587     // Obsolete argument: ignore it.
    588     return true;
    589   } else {
    590     EXTENSION_FUNCTION_VALIDATE(false);
    591   }
    592   NotifyChange();
    593   return true;
    594 }
    595 
    596 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
    597   EXTENSION_FUNCTION_VALIDATE(details_);
    598   std::string title;
    599   EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
    600   extension_action_->SetTitle(tab_id_, title);
    601   NotifyChange();
    602   return true;
    603 }
    604 
    605 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
    606   EXTENSION_FUNCTION_VALIDATE(details_);
    607   std::string popup_string;
    608   EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
    609 
    610   GURL popup_url;
    611   if (!popup_string.empty())
    612     popup_url = GetExtension()->GetResourceURL(popup_string);
    613 
    614   extension_action_->SetPopupUrl(tab_id_, popup_url);
    615   NotifyChange();
    616   return true;
    617 }
    618 
    619 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
    620   EXTENSION_FUNCTION_VALIDATE(details_);
    621   std::string badge_text;
    622   EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
    623   extension_action_->SetBadgeText(tab_id_, badge_text);
    624   NotifyChange();
    625   return true;
    626 }
    627 
    628 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
    629   EXTENSION_FUNCTION_VALIDATE(details_);
    630   Value* color_value = NULL;
    631   EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
    632   SkColor color = 0;
    633   if (color_value->IsType(Value::TYPE_LIST)) {
    634     base::ListValue* list = NULL;
    635     EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
    636     EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
    637 
    638     int color_array[4] = {0};
    639     for (size_t i = 0; i < arraysize(color_array); ++i) {
    640       EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
    641     }
    642 
    643     color = SkColorSetARGB(color_array[3], color_array[0],
    644                            color_array[1], color_array[2]);
    645   } else if (color_value->IsType(Value::TYPE_STRING)) {
    646     std::string color_string;
    647     EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
    648     if (!ParseCSSColorString(color_string, &color))
    649       return false;
    650   }
    651 
    652   extension_action_->SetBadgeBackgroundColor(tab_id_, color);
    653   NotifyChange();
    654   return true;
    655 }
    656 
    657 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
    658   SetResult(Value::CreateStringValue(extension_action_->GetTitle(tab_id_)));
    659   return true;
    660 }
    661 
    662 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
    663   SetResult(
    664       Value::CreateStringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
    665   return true;
    666 }
    667 
    668 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
    669   SetResult(Value::CreateStringValue(extension_action_->GetBadgeText(tab_id_)));
    670   return true;
    671 }
    672 
    673 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
    674   base::ListValue* list = new base::ListValue();
    675   SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
    676   list->Append(Value::CreateIntegerValue(SkColorGetR(color)));
    677   list->Append(Value::CreateIntegerValue(SkColorGetG(color)));
    678   list->Append(Value::CreateIntegerValue(SkColorGetB(color)));
    679   list->Append(Value::CreateIntegerValue(SkColorGetA(color)));
    680   SetResult(list);
    681   return true;
    682 }
    683 
    684 //
    685 // ScriptBadgeGetAttentionFunction
    686 //
    687 
    688 ScriptBadgeGetAttentionFunction::~ScriptBadgeGetAttentionFunction() {}
    689 
    690 bool ScriptBadgeGetAttentionFunction::RunExtensionAction() {
    691   tab_helper().location_bar_controller()->GetAttentionFor(extension_id());
    692   return true;
    693 }
    694 
    695 }  // namespace extensions
    696 
    697 //
    698 // PageActionsFunction (deprecated)
    699 //
    700 
    701 namespace keys = extension_page_actions_api_constants;
    702 
    703 PageActionsFunction::PageActionsFunction() {
    704 }
    705 
    706 PageActionsFunction::~PageActionsFunction() {
    707 }
    708 
    709 bool PageActionsFunction::SetPageActionEnabled(bool enable) {
    710   std::string extension_action_id;
    711   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_action_id));
    712   DictionaryValue* action = NULL;
    713   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &action));
    714 
    715   int tab_id;
    716   EXTENSION_FUNCTION_VALIDATE(action->GetInteger(keys::kTabIdKey, &tab_id));
    717   std::string url;
    718   EXTENSION_FUNCTION_VALIDATE(action->GetString(keys::kUrlKey, &url));
    719 
    720   std::string title;
    721   if (enable) {
    722     if (action->HasKey(keys::kTitleKey))
    723       EXTENSION_FUNCTION_VALIDATE(action->GetString(keys::kTitleKey, &title));
    724   }
    725 
    726   ExtensionAction* page_action =
    727       extensions::ExtensionActionManager::Get(profile())->
    728       GetPageAction(*GetExtension());
    729   if (!page_action) {
    730     error_ = extensions::kNoPageActionError;
    731     return false;
    732   }
    733 
    734   // Find the WebContents that contains this tab id.
    735   content::WebContents* contents = NULL;
    736   bool result = ExtensionTabUtil::GetTabById(
    737       tab_id, profile(), include_incognito(), NULL, NULL, &contents, NULL);
    738   if (!result || !contents) {
    739     error_ = extensions::ErrorUtils::FormatErrorMessage(
    740         extensions::kNoTabError, base::IntToString(tab_id));
    741     return false;
    742   }
    743 
    744   // Make sure the URL hasn't changed.
    745   content::NavigationEntry* entry = contents->GetController().GetActiveEntry();
    746   if (!entry || url != entry->GetURL().spec()) {
    747     error_ = extensions::ErrorUtils::FormatErrorMessage(
    748         extensions::kUrlNotActiveError, url);
    749     return false;
    750   }
    751 
    752   // Set visibility and broadcast notifications that the UI should be updated.
    753   page_action->SetAppearance(
    754       tab_id, enable ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
    755   page_action->SetTitle(tab_id, title);
    756   extensions::TabHelper::FromWebContents(contents)->
    757       location_bar_controller()->NotifyChange();
    758 
    759   return true;
    760 }
    761 
    762 bool EnablePageActionsFunction::RunImpl() {
    763   return SetPageActionEnabled(true);
    764 }
    765 
    766 bool DisablePageActionsFunction::RunImpl() {
    767   return SetPageActionEnabled(false);
    768 }
    769