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