- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / extension_action / extension_action_api.cc
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 "base/base64.h"
8 #include "base/lazy_instance.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h"
14 #include "chrome/browser/extensions/event_router.h"
15 #include "chrome/browser/extensions/extension_action.h"
16 #include "chrome/browser/extensions/extension_action_manager.h"
17 #include "chrome/browser/extensions/extension_function_registry.h"
18 #include "chrome/browser/extensions/extension_host.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 using content::WebContents;
35
36 namespace page_actions_keys = extension_page_actions_api_constants;
37
38 namespace extensions {
39
40 namespace {
41
42 const char kBrowserActionStorageKey[] = "browser_action";
43 const char kPopupUrlStorageKey[] = "poupup_url";
44 const char kTitleStorageKey[] = "title";
45 const char kIconStorageKey[] = "icon";
46 const char kBadgeTextStorageKey[] = "badge_text";
47 const char kBadgeBackgroundColorStorageKey[] = "badge_background_color";
48 const char kBadgeTextColorStorageKey[] = "badge_text_color";
49 const char kAppearanceStorageKey[] = "appearance";
50
51 // Whether the browser action is visible in the toolbar.
52 const char kBrowserActionVisible[] = "browser_action_visible";
53
54 // Errors.
55 const char kNoExtensionActionError[] =
56     "This extension has no action specified.";
57 const char kNoTabError[] = "No tab with id: *.";
58 const char kNoPageActionError[] =
59     "This extension has no page action specified.";
60 const char kUrlNotActiveError[] = "This url is no longer active: *.";
61 const char kOpenPopupError[] =
62     "Failed to show popup either because there is an existing popup or another "
63     "error occurred.";
64 const char kInternalError[] = "Internal error.";
65
66 struct IconRepresentationInfo {
67   // Size as a string that will be used to retrieve representation value from
68   // SetIcon function arguments.
69   const char* size_string;
70   // Scale factor for which the represantion should be used.
71   ui::ScaleFactor scale;
72 };
73
74 const IconRepresentationInfo kIconSizes[] = {
75     { "19", ui::SCALE_FACTOR_100P },
76     { "38", ui::SCALE_FACTOR_200P }
77 };
78
79 // Conversion function for reading/writing to storage.
80 SkColor RawStringToSkColor(const std::string& str) {
81   uint64 value = 0;
82   base::StringToUint64(str, &value);
83   SkColor color = static_cast<SkColor>(value);
84   DCHECK(value == color);  // ensure value fits into color's 32 bits
85   return color;
86 }
87
88 // Conversion function for reading/writing to storage.
89 std::string SkColorToRawString(SkColor color) {
90   return base::Uint64ToString(color);
91 }
92
93 // Conversion function for reading/writing to storage.
94 bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) {
95   // TODO(mpcomplete): Remove the base64 encode/decode step when
96   // http://crbug.com/140546 is fixed.
97   std::string raw_str;
98   if (!base::Base64Decode(str, &raw_str))
99     return false;
100   IPC::Message bitmap_pickle(raw_str.data(), raw_str.size());
101   PickleIterator iter(bitmap_pickle);
102   return IPC::ReadParam(&bitmap_pickle, &iter, bitmap);
103 }
104
105 // Conversion function for reading/writing to storage.
106 std::string RepresentationToString(const gfx::ImageSkia& image, float scale) {
107   SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap();
108   IPC::Message bitmap_pickle;
109   // Clear the header values so they don't vary in serialization.
110   bitmap_pickle.SetHeaderValues(0, 0, 0);
111   IPC::WriteParam(&bitmap_pickle, bitmap);
112   std::string raw_str(static_cast<const char*>(bitmap_pickle.data()),
113                       bitmap_pickle.size());
114   std::string base64_str;
115   if (!base::Base64Encode(raw_str, &base64_str))
116     return std::string();
117   return base64_str;
118 }
119
120 // Set |action|'s default values to those specified in |dict|.
121 void SetDefaultsFromValue(const base::DictionaryValue* dict,
122                           ExtensionAction* action) {
123   const int kTabId = ExtensionAction::kDefaultTabId;
124   std::string str_value;
125   int int_value;
126   SkBitmap bitmap;
127   gfx::ImageSkia icon;
128
129   if (dict->GetString(kPopupUrlStorageKey, &str_value))
130     action->SetPopupUrl(kTabId, GURL(str_value));
131   if (dict->GetString(kTitleStorageKey, &str_value))
132     action->SetTitle(kTabId, str_value);
133   if (dict->GetString(kBadgeTextStorageKey, &str_value))
134     action->SetBadgeText(kTabId, str_value);
135   if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value))
136     action->SetBadgeBackgroundColor(kTabId, RawStringToSkColor(str_value));
137   if (dict->GetString(kBadgeTextColorStorageKey, &str_value))
138     action->SetBadgeTextColor(kTabId, RawStringToSkColor(str_value));
139   if (dict->GetInteger(kAppearanceStorageKey, &int_value))
140     action->SetAppearance(kTabId,
141                           static_cast<ExtensionAction::Appearance>(int_value));
142
143   const base::DictionaryValue* icon_value = NULL;
144   if (dict->GetDictionary(kIconStorageKey, &icon_value)) {
145     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
146       if (icon_value->GetString(kIconSizes[i].size_string, &str_value) &&
147           StringToSkBitmap(str_value, &bitmap)) {
148         CHECK(!bitmap.isNull());
149         float scale = ui::GetImageScale(kIconSizes[i].scale);
150         icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
151       }
152     }
153     action->SetIcon(kTabId, gfx::Image(icon));
154   }
155 }
156
157 // Store |action|'s default values in a DictionaryValue for use in storing to
158 // disk.
159 scoped_ptr<base::DictionaryValue> DefaultsToValue(ExtensionAction* action) {
160   const int kTabId = ExtensionAction::kDefaultTabId;
161   scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
162
163   dict->SetString(kPopupUrlStorageKey, action->GetPopupUrl(kTabId).spec());
164   dict->SetString(kTitleStorageKey, action->GetTitle(kTabId));
165   dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kTabId));
166   dict->SetString(kBadgeBackgroundColorStorageKey,
167                   SkColorToRawString(action->GetBadgeBackgroundColor(kTabId)));
168   dict->SetString(kBadgeTextColorStorageKey,
169                   SkColorToRawString(action->GetBadgeTextColor(kTabId)));
170   dict->SetInteger(kAppearanceStorageKey,
171                    action->GetIsVisible(kTabId) ?
172                        ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
173
174   gfx::ImageSkia icon = action->GetExplicitlySetIcon(kTabId);
175   if (!icon.isNull()) {
176     base::DictionaryValue* icon_value = new base::DictionaryValue();
177     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
178       float scale = ui::GetImageScale(kIconSizes[i].scale);
179       if (icon.HasRepresentation(scale)) {
180         icon_value->SetString(
181             kIconSizes[i].size_string,
182             RepresentationToString(icon, scale));
183       }
184     }
185     dict->Set(kIconStorageKey, icon_value);
186   }
187   return dict.Pass();
188 }
189
190 }  // namespace
191
192 //
193 // ExtensionActionAPI
194 //
195
196 static base::LazyInstance<ProfileKeyedAPIFactory<ExtensionActionAPI> >
197     g_factory = LAZY_INSTANCE_INITIALIZER;
198
199 ExtensionActionAPI::ExtensionActionAPI(Profile* profile) {
200   ExtensionFunctionRegistry* registry =
201       ExtensionFunctionRegistry::GetInstance();
202
203   // Browser Actions
204   registry->RegisterFunction<BrowserActionSetIconFunction>();
205   registry->RegisterFunction<BrowserActionSetTitleFunction>();
206   registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
207   registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
208   registry->RegisterFunction<BrowserActionSetPopupFunction>();
209   registry->RegisterFunction<BrowserActionGetTitleFunction>();
210   registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
211   registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
212   registry->RegisterFunction<BrowserActionGetPopupFunction>();
213   registry->RegisterFunction<BrowserActionEnableFunction>();
214   registry->RegisterFunction<BrowserActionDisableFunction>();
215   registry->RegisterFunction<BrowserActionOpenPopupFunction>();
216
217   // Page Actions
218   registry->RegisterFunction<EnablePageActionsFunction>();
219   registry->RegisterFunction<DisablePageActionsFunction>();
220   registry->RegisterFunction<PageActionShowFunction>();
221   registry->RegisterFunction<PageActionHideFunction>();
222   registry->RegisterFunction<PageActionSetIconFunction>();
223   registry->RegisterFunction<PageActionSetTitleFunction>();
224   registry->RegisterFunction<PageActionSetPopupFunction>();
225   registry->RegisterFunction<PageActionGetTitleFunction>();
226   registry->RegisterFunction<PageActionGetPopupFunction>();
227
228   // Script Badges
229   registry->RegisterFunction<ScriptBadgeGetAttentionFunction>();
230   registry->RegisterFunction<ScriptBadgeGetPopupFunction>();
231   registry->RegisterFunction<ScriptBadgeSetPopupFunction>();
232 }
233
234 ExtensionActionAPI::~ExtensionActionAPI() {
235 }
236
237 // static
238 ProfileKeyedAPIFactory<ExtensionActionAPI>*
239 ExtensionActionAPI::GetFactoryInstance() {
240   return &g_factory.Get();
241 }
242
243 // static
244 ExtensionActionAPI* ExtensionActionAPI::Get(Profile* profile) {
245   return ProfileKeyedAPIFactory<ExtensionActionAPI>::GetForProfile(profile);
246 }
247
248 // static
249 bool ExtensionActionAPI::GetBrowserActionVisibility(
250     const ExtensionPrefs* prefs,
251     const std::string& extension_id) {
252   bool visible = false;
253   if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
254                                           kBrowserActionVisible,
255                                           &visible)) {
256     return true;
257   }
258   return visible;
259 }
260
261 // static
262 void ExtensionActionAPI::SetBrowserActionVisibility(
263     ExtensionPrefs* prefs,
264     const std::string& extension_id,
265     bool visible) {
266   if (GetBrowserActionVisibility(prefs, extension_id) == visible)
267     return;
268
269   prefs->UpdateExtensionPref(extension_id,
270                              kBrowserActionVisible,
271                              new base::FundamentalValue(visible));
272   content::NotificationService::current()->Notify(
273       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
274       content::Source<ExtensionPrefs>(prefs),
275       content::Details<const std::string>(&extension_id));
276 }
277
278 // static
279 void ExtensionActionAPI::BrowserActionExecuted(
280     Profile* profile,
281     const ExtensionAction& browser_action,
282     WebContents* web_contents) {
283   ExtensionActionExecuted(profile, browser_action, web_contents);
284 }
285
286 // static
287 void ExtensionActionAPI::PageActionExecuted(Profile* profile,
288                                             const ExtensionAction& page_action,
289                                             int tab_id,
290                                             const std::string& url,
291                                             int button) {
292   DispatchOldPageActionEvent(profile, page_action.extension_id(),
293                              page_action.id(), tab_id, url, button);
294   WebContents* web_contents = NULL;
295   if (!ExtensionTabUtil::GetTabById(tab_id, profile, profile->IsOffTheRecord(),
296                                     NULL, NULL, &web_contents, NULL)) {
297     return;
298   }
299   ExtensionActionExecuted(profile, page_action, web_contents);
300 }
301
302 // static
303 void ExtensionActionAPI::ScriptBadgeExecuted(
304     Profile* profile,
305     const ExtensionAction& script_badge,
306     int tab_id) {
307   WebContents* web_contents = NULL;
308   if (!ExtensionTabUtil::GetTabById(tab_id, profile, profile->IsOffTheRecord(),
309                                     NULL, NULL, &web_contents, NULL)) {
310     return;
311   }
312   ExtensionActionExecuted(profile, script_badge, web_contents);
313 }
314
315 // static
316 void ExtensionActionAPI::DispatchEventToExtension(
317     Profile* profile,
318     const std::string& extension_id,
319     const std::string& event_name,
320     scoped_ptr<base::ListValue> event_args) {
321   if (!extensions::ExtensionSystem::Get(profile)->event_router())
322     return;
323
324   scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
325   event->restrict_to_profile = profile;
326   event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
327   ExtensionSystem::Get(profile)->event_router()->
328       DispatchEventToExtension(extension_id, event.Pass());
329 }
330
331 // static
332 void ExtensionActionAPI::DispatchOldPageActionEvent(
333     Profile* profile,
334     const std::string& extension_id,
335     const std::string& page_action_id,
336     int tab_id,
337     const std::string& url,
338     int button) {
339   scoped_ptr<base::ListValue> args(new base::ListValue());
340   args->Append(new base::StringValue(page_action_id));
341
342   DictionaryValue* data = new DictionaryValue();
343   data->Set(page_actions_keys::kTabIdKey, new base::FundamentalValue(tab_id));
344   data->Set(page_actions_keys::kTabUrlKey, new base::StringValue(url));
345   data->Set(page_actions_keys::kButtonKey,
346             new base::FundamentalValue(button));
347   args->Append(data);
348
349   DispatchEventToExtension(profile, extension_id, "pageActions", args.Pass());
350 }
351
352 // static
353 void ExtensionActionAPI::ExtensionActionExecuted(
354     Profile* profile,
355     const ExtensionAction& extension_action,
356     WebContents* web_contents) {
357   const char* event_name = NULL;
358   switch (extension_action.action_type()) {
359     case ActionInfo::TYPE_BROWSER:
360       event_name = "browserAction.onClicked";
361       break;
362     case ActionInfo::TYPE_PAGE:
363       event_name = "pageAction.onClicked";
364       break;
365     case ActionInfo::TYPE_SCRIPT_BADGE:
366       event_name = "scriptBadge.onClicked";
367       break;
368     case ActionInfo::TYPE_SYSTEM_INDICATOR:
369       // The System Indicator handles its own clicks.
370       break;
371   }
372
373   if (event_name) {
374     scoped_ptr<base::ListValue> args(new base::ListValue());
375     DictionaryValue* tab_value = ExtensionTabUtil::CreateTabValue(
376         web_contents);
377     args->Append(tab_value);
378
379     DispatchEventToExtension(profile,
380                              extension_action.extension_id(),
381                              event_name,
382                              args.Pass());
383   }
384 }
385
386 //
387 // ExtensionActionStorageManager
388 //
389
390 ExtensionActionStorageManager::ExtensionActionStorageManager(Profile* profile)
391     : profile_(profile) {
392   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
393                  content::Source<Profile>(profile_));
394   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
395                  content::NotificationService::AllBrowserContextsAndSources());
396
397   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
398   if (storage)
399     storage->RegisterKey(kBrowserActionStorageKey);
400 }
401
402 ExtensionActionStorageManager::~ExtensionActionStorageManager() {
403 }
404
405 void ExtensionActionStorageManager::Observe(
406     int type,
407     const content::NotificationSource& source,
408     const content::NotificationDetails& details) {
409   switch (type) {
410     case chrome::NOTIFICATION_EXTENSION_LOADED: {
411       const Extension* extension =
412           content::Details<const Extension>(details).ptr();
413       if (!ExtensionActionManager::Get(profile_)->
414           GetBrowserAction(*extension)) {
415         break;
416       }
417
418       StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
419       if (storage) {
420         storage->GetExtensionValue(extension->id(), kBrowserActionStorageKey,
421             base::Bind(&ExtensionActionStorageManager::ReadFromStorage,
422                        AsWeakPtr(), extension->id()));
423       }
424       break;
425     }
426     case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED: {
427       ExtensionAction* extension_action =
428           content::Source<ExtensionAction>(source).ptr();
429       Profile* profile = content::Details<Profile>(details).ptr();
430       if (profile != profile_)
431         break;
432
433       extension_action->set_has_changed(true);
434       WriteToStorage(extension_action);
435       break;
436     }
437     default:
438       NOTREACHED();
439       break;
440   }
441 }
442
443 void ExtensionActionStorageManager::WriteToStorage(
444     ExtensionAction* extension_action) {
445   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
446   if (!storage)
447     return;
448
449   scoped_ptr<base::DictionaryValue> defaults =
450       DefaultsToValue(extension_action);
451   storage->SetExtensionValue(extension_action->extension_id(),
452                              kBrowserActionStorageKey,
453                              defaults.PassAs<base::Value>());
454 }
455
456 void ExtensionActionStorageManager::ReadFromStorage(
457     const std::string& extension_id, scoped_ptr<base::Value> value) {
458   const Extension* extension =
459       ExtensionSystem::Get(profile_)->extension_service()->
460       extensions()->GetByID(extension_id);
461   if (!extension)
462     return;
463
464   ExtensionAction* browser_action =
465       ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
466   CHECK(browser_action);
467
468   // Don't load values from storage if the extension has updated a value
469   // already. The extension may have only updated some of the values, but
470   // this is a good first approximation. If the extension is doing stuff
471   // to the browser action, we can assume it is ready to take over.
472   if (browser_action->has_changed())
473     return;
474
475   const base::DictionaryValue* dict = NULL;
476   if (!value.get() || !value->GetAsDictionary(&dict))
477     return;
478
479   SetDefaultsFromValue(dict, browser_action);
480 }
481
482 //
483 // ExtensionActionFunction
484 //
485
486 ExtensionActionFunction::ExtensionActionFunction()
487     : details_(NULL),
488       tab_id_(ExtensionAction::kDefaultTabId),
489       contents_(NULL),
490       extension_action_(NULL) {
491 }
492
493 ExtensionActionFunction::~ExtensionActionFunction() {
494 }
495
496 bool ExtensionActionFunction::RunImpl() {
497   ExtensionActionManager* manager = ExtensionActionManager::Get(GetProfile());
498   const Extension* extension = GetExtension();
499   if (StartsWithASCII(name(), "scriptBadge.", false)) {
500     extension_action_ = manager->GetScriptBadge(*extension);
501   } else if (StartsWithASCII(name(), "systemIndicator.", false)) {
502     extension_action_ = manager->GetSystemIndicator(*extension);
503   } else {
504     extension_action_ = manager->GetBrowserAction(*extension);
505     if (!extension_action_) {
506       extension_action_ = manager->GetPageAction(*extension);
507     }
508   }
509   if (!extension_action_) {
510     // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
511     // exist for extensions that don't have one declared. This should come as
512     // part of the Feature system.
513     error_ = kNoExtensionActionError;
514     return false;
515   }
516
517   // Populates the tab_id_ and details_ members.
518   EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
519
520   // Find the WebContents that contains this tab id if one is required.
521   if (tab_id_ != ExtensionAction::kDefaultTabId) {
522     ExtensionTabUtil::GetTabById(tab_id_,
523                                  GetProfile(),
524                                  include_incognito(),
525                                  NULL,
526                                  NULL,
527                                  &contents_,
528                                  NULL);
529     if (!contents_) {
530       error_ = ErrorUtils::FormatErrorMessage(
531           kNoTabError, base::IntToString(tab_id_));
532       return false;
533     }
534   } else {
535     // Only browser actions and system indicators have a default tabId.
536     ActionInfo::Type action_type = extension_action_->action_type();
537     EXTENSION_FUNCTION_VALIDATE(
538         action_type == ActionInfo::TYPE_BROWSER ||
539         action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
540   }
541   return RunExtensionAction();
542 }
543
544 bool ExtensionActionFunction::ExtractDataFromArguments() {
545   // There may or may not be details (depends on the function).
546   // The tabId might appear in details (if it exists), as the first
547   // argument besides the action type (depends on the function), or be omitted
548   // entirely.
549   base::Value* first_arg = NULL;
550   if (!args_->Get(0, &first_arg))
551     return true;
552
553   switch (first_arg->GetType()) {
554     case Value::TYPE_INTEGER:
555       CHECK(first_arg->GetAsInteger(&tab_id_));
556       break;
557
558     case Value::TYPE_DICTIONARY: {
559       // Found the details argument.
560       details_ = static_cast<base::DictionaryValue*>(first_arg);
561       // Still need to check for the tabId within details.
562       base::Value* tab_id_value = NULL;
563       if (details_->Get("tabId", &tab_id_value)) {
564         switch (tab_id_value->GetType()) {
565           case Value::TYPE_NULL:
566             // OK; tabId is optional, leave it default.
567             return true;
568           case Value::TYPE_INTEGER:
569             CHECK(tab_id_value->GetAsInteger(&tab_id_));
570             return true;
571           default:
572             // Boom.
573             return false;
574         }
575       }
576       // Not found; tabId is optional, leave it default.
577       break;
578     }
579
580     case Value::TYPE_NULL:
581       // The tabId might be an optional argument.
582       break;
583
584     default:
585       return false;
586   }
587
588   return true;
589 }
590
591 void ExtensionActionFunction::NotifyChange() {
592   switch (extension_action_->action_type()) {
593     case ActionInfo::TYPE_BROWSER:
594     case ActionInfo::TYPE_PAGE:
595       if (ExtensionActionManager::Get(GetProfile())
596               ->GetBrowserAction(*extension_.get())) {
597         NotifyBrowserActionChange();
598       } else if (ExtensionActionManager::Get(GetProfile())
599                      ->GetPageAction(*extension_.get())) {
600         NotifyLocationBarChange();
601       }
602       return;
603     case ActionInfo::TYPE_SCRIPT_BADGE:
604       NotifyLocationBarChange();
605       return;
606     case ActionInfo::TYPE_SYSTEM_INDICATOR:
607       NotifySystemIndicatorChange();
608       return;
609   }
610   NOTREACHED();
611 }
612
613 void ExtensionActionFunction::NotifyBrowserActionChange() {
614   content::NotificationService::current()->Notify(
615       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
616       content::Source<ExtensionAction>(extension_action_),
617       content::Details<Profile>(GetProfile()));
618 }
619
620 void ExtensionActionFunction::NotifyLocationBarChange() {
621   TabHelper::FromWebContents(contents_)->
622       location_bar_controller()->NotifyChange();
623 }
624
625 void ExtensionActionFunction::NotifySystemIndicatorChange() {
626   content::NotificationService::current()->Notify(
627       chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
628       content::Source<Profile>(GetProfile()),
629       content::Details<ExtensionAction>(extension_action_));
630 }
631
632 // static
633 bool ExtensionActionFunction::ParseCSSColorString(
634     const std::string& color_string,
635     SkColor* result) {
636   std::string formatted_color = "#";
637   // Check the string for incorrect formatting.
638   if (color_string[0] != '#')
639     return false;
640
641   // Convert the string from #FFF format to #FFFFFF format.
642   if (color_string.length() == 4) {
643     for (size_t i = 1; i < color_string.length(); i++) {
644       formatted_color += color_string[i];
645       formatted_color += color_string[i];
646     }
647   } else {
648     formatted_color = color_string;
649   }
650
651   if (formatted_color.length() != 7)
652     return false;
653
654   // Convert the string to an integer and make sure it is in the correct value
655   // range.
656   int color_ints[3] = {0};
657   for (int i = 0; i < 3; i++) {
658     if (!base::HexStringToInt(formatted_color.substr(1 + (2 * i), 2),
659                               color_ints + i))
660       return false;
661     if (color_ints[i] > 255 || color_ints[i] < 0)
662       return false;
663   }
664
665   *result = SkColorSetARGB(255, color_ints[0], color_ints[1], color_ints[2]);
666   return true;
667 }
668
669 bool ExtensionActionFunction::SetVisible(bool visible) {
670   if (extension_action_->GetIsVisible(tab_id_) == visible)
671     return true;
672   extension_action_->SetAppearance(
673       tab_id_, visible ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
674   NotifyChange();
675   return true;
676 }
677
678 TabHelper& ExtensionActionFunction::tab_helper() const {
679   CHECK(contents_);
680   return *TabHelper::FromWebContents(contents_);
681 }
682
683 bool ExtensionActionShowFunction::RunExtensionAction() {
684   return SetVisible(true);
685 }
686
687 bool ExtensionActionHideFunction::RunExtensionAction() {
688   return SetVisible(false);
689 }
690
691 bool ExtensionActionSetIconFunction::RunExtensionAction() {
692   EXTENSION_FUNCTION_VALIDATE(details_);
693
694   // setIcon can take a variant argument: either a dictionary of canvas
695   // ImageData, or an icon index.
696   base::DictionaryValue* canvas_set = NULL;
697   int icon_index;
698   if (details_->GetDictionary("imageData", &canvas_set)) {
699     gfx::ImageSkia icon;
700     // Extract icon representations from the ImageDataSet dictionary.
701     for (size_t i = 0; i < arraysize(kIconSizes); i++) {
702       base::BinaryValue* binary;
703       if (canvas_set->GetBinary(kIconSizes[i].size_string, &binary)) {
704         IPC::Message pickle(binary->GetBuffer(), binary->GetSize());
705         PickleIterator iter(pickle);
706         SkBitmap bitmap;
707         EXTENSION_FUNCTION_VALIDATE(IPC::ReadParam(&pickle, &iter, &bitmap));
708         CHECK(!bitmap.isNull());
709         float scale = ui::GetImageScale(kIconSizes[i].scale);
710         icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
711       }
712     }
713
714     extension_action_->SetIcon(tab_id_, gfx::Image(icon));
715   } else if (details_->GetInteger("iconIndex", &icon_index)) {
716     // Obsolete argument: ignore it.
717     return true;
718   } else {
719     EXTENSION_FUNCTION_VALIDATE(false);
720   }
721   NotifyChange();
722   return true;
723 }
724
725 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
726   EXTENSION_FUNCTION_VALIDATE(details_);
727   std::string title;
728   EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
729   extension_action_->SetTitle(tab_id_, title);
730   NotifyChange();
731   return true;
732 }
733
734 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
735   EXTENSION_FUNCTION_VALIDATE(details_);
736   std::string popup_string;
737   EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
738
739   GURL popup_url;
740   if (!popup_string.empty())
741     popup_url = GetExtension()->GetResourceURL(popup_string);
742
743   extension_action_->SetPopupUrl(tab_id_, popup_url);
744   NotifyChange();
745   return true;
746 }
747
748 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
749   EXTENSION_FUNCTION_VALIDATE(details_);
750   std::string badge_text;
751   EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
752   extension_action_->SetBadgeText(tab_id_, badge_text);
753   NotifyChange();
754   return true;
755 }
756
757 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
758   EXTENSION_FUNCTION_VALIDATE(details_);
759   Value* color_value = NULL;
760   EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
761   SkColor color = 0;
762   if (color_value->IsType(Value::TYPE_LIST)) {
763     base::ListValue* list = NULL;
764     EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
765     EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
766
767     int color_array[4] = {0};
768     for (size_t i = 0; i < arraysize(color_array); ++i) {
769       EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
770     }
771
772     color = SkColorSetARGB(color_array[3], color_array[0],
773                            color_array[1], color_array[2]);
774   } else if (color_value->IsType(Value::TYPE_STRING)) {
775     std::string color_string;
776     EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
777     if (!ParseCSSColorString(color_string, &color))
778       return false;
779   }
780
781   extension_action_->SetBadgeBackgroundColor(tab_id_, color);
782   NotifyChange();
783   return true;
784 }
785
786 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
787   SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_)));
788   return true;
789 }
790
791 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
792   SetResult(
793       new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
794   return true;
795 }
796
797 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
798   SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_)));
799   return true;
800 }
801
802 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
803   base::ListValue* list = new base::ListValue();
804   SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
805   list->Append(
806       new base::FundamentalValue(static_cast<int>(SkColorGetR(color))));
807   list->Append(
808       new base::FundamentalValue(static_cast<int>(SkColorGetG(color))));
809   list->Append(
810       new base::FundamentalValue(static_cast<int>(SkColorGetB(color))));
811   list->Append(
812       new base::FundamentalValue(static_cast<int>(SkColorGetA(color))));
813   SetResult(list);
814   return true;
815 }
816
817 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction()
818     : response_sent_(false) {
819 }
820
821 bool BrowserActionOpenPopupFunction::RunImpl() {
822   ExtensionToolbarModel* model = extensions::ExtensionSystem::Get(GetProfile())
823                                      ->extension_service()
824                                      ->toolbar_model();
825   if (!model) {
826     error_ = kInternalError;
827     return false;
828   }
829
830   if (!model->ShowBrowserActionPopup(extension_)) {
831     error_ = kOpenPopupError;
832     return false;
833   }
834
835   registrar_.Add(this,
836                  chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
837                  content::Source<Profile>(GetProfile()));
838
839   // Set a timeout for waiting for the notification that the popup is loaded.
840   // Waiting is required so that the popup view can be retrieved by the custom
841   // bindings for the response callback. It's also needed to keep this function
842   // instance around until a notification is observed.
843   base::MessageLoopForUI::current()->PostDelayedTask(
844       FROM_HERE,
845       base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this),
846       base::TimeDelta::FromSeconds(10));
847   return true;
848 }
849
850 void BrowserActionOpenPopupFunction::OpenPopupTimedOut() {
851   if (response_sent_)
852     return;
853
854   DVLOG(1) << "chrome.browserAction.openPopup did not show a popup.";
855   error_ = kOpenPopupError;
856   SendResponse(false);
857   response_sent_ = true;
858 }
859
860 void BrowserActionOpenPopupFunction::Observe(
861     int type,
862     const content::NotificationSource& source,
863     const content::NotificationDetails& details) {
864   DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, type);
865   if (response_sent_)
866     return;
867
868   ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
869   if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP ||
870       host->extension()->id() != extension_->id())
871     return;
872
873   SendResponse(true);
874   response_sent_ = true;
875   registrar_.RemoveAll();
876 }
877
878 //
879 // ScriptBadgeGetAttentionFunction
880 //
881
882 ScriptBadgeGetAttentionFunction::~ScriptBadgeGetAttentionFunction() {}
883
884 bool ScriptBadgeGetAttentionFunction::RunExtensionAction() {
885   tab_helper().location_bar_controller()->GetAttentionFor(extension_id());
886   return true;
887 }
888
889 }  // namespace extensions
890
891 //
892 // PageActionsFunction (deprecated)
893 //
894
895 PageActionsFunction::PageActionsFunction() {
896 }
897
898 PageActionsFunction::~PageActionsFunction() {
899 }
900
901 bool PageActionsFunction::SetPageActionEnabled(bool enable) {
902   std::string extension_action_id;
903   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_action_id));
904   DictionaryValue* action = NULL;
905   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &action));
906
907   int tab_id;
908   EXTENSION_FUNCTION_VALIDATE(action->GetInteger(
909       page_actions_keys::kTabIdKey, &tab_id));
910   std::string url;
911   EXTENSION_FUNCTION_VALIDATE(action->GetString(
912       page_actions_keys::kUrlKey, &url));
913
914   std::string title;
915   if (enable) {
916     if (action->HasKey(page_actions_keys::kTitleKey))
917       EXTENSION_FUNCTION_VALIDATE(action->GetString(
918           page_actions_keys::kTitleKey, &title));
919   }
920
921   ExtensionAction* page_action = extensions::ExtensionActionManager::Get(
922       GetProfile())->GetPageAction(*GetExtension());
923   if (!page_action) {
924     error_ = extensions::kNoPageActionError;
925     return false;
926   }
927
928   // Find the WebContents that contains this tab id.
929   WebContents* contents = NULL;
930   bool result = ExtensionTabUtil::GetTabById(
931       tab_id, GetProfile(), include_incognito(), NULL, NULL, &contents, NULL);
932   if (!result || !contents) {
933     error_ = extensions::ErrorUtils::FormatErrorMessage(
934         extensions::kNoTabError, base::IntToString(tab_id));
935     return false;
936   }
937
938   // Make sure the URL hasn't changed.
939   content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
940   if (!entry || url != entry->GetURL().spec()) {
941     error_ = extensions::ErrorUtils::FormatErrorMessage(
942         extensions::kUrlNotActiveError, url);
943     return false;
944   }
945
946   // Set visibility and broadcast notifications that the UI should be updated.
947   page_action->SetAppearance(
948       tab_id, enable ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
949   page_action->SetTitle(tab_id, title);
950   extensions::TabHelper::FromWebContents(contents)->
951       location_bar_controller()->NotifyChange();
952
953   return true;
954 }
955
956 bool EnablePageActionsFunction::RunImpl() {
957   return SetPageActionEnabled(true);
958 }
959
960 bool DisablePageActionsFunction::RunImpl() {
961   return SetPageActionEnabled(false);
962 }