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