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