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.
5 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
7 #include "base/base64.h"
8 #include "base/lazy_instance.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h"
14 #include "chrome/browser/extensions/event_router.h"
15 #include "chrome/browser/extensions/extension_action.h"
16 #include "chrome/browser/extensions/extension_action_manager.h"
17 #include "chrome/browser/extensions/extension_function_registry.h"
18 #include "chrome/browser/extensions/extension_host.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/extension_system.h"
21 #include "chrome/browser/extensions/extension_tab_util.h"
22 #include "chrome/browser/extensions/location_bar_controller.h"
23 #include "chrome/browser/extensions/state_store.h"
24 #include "chrome/browser/extensions/tab_helper.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/extensions/api/extension_action/action_info.h"
27 #include "chrome/common/render_messages.h"
28 #include "content/public/browser/navigation_entry.h"
29 #include "content/public/browser/notification_service.h"
30 #include "extensions/common/error_utils.h"
31 #include "ui/gfx/image/image.h"
32 #include "ui/gfx/image/image_skia.h"
34 using content::WebContents;
36 namespace page_actions_keys = extension_page_actions_api_constants;
38 namespace extensions {
42 const char kBrowserActionStorageKey[] = "browser_action";
43 const char kPopupUrlStorageKey[] = "poupup_url";
44 const char kTitleStorageKey[] = "title";
45 const char kIconStorageKey[] = "icon";
46 const char kBadgeTextStorageKey[] = "badge_text";
47 const char kBadgeBackgroundColorStorageKey[] = "badge_background_color";
48 const char kBadgeTextColorStorageKey[] = "badge_text_color";
49 const char kAppearanceStorageKey[] = "appearance";
51 // Whether the browser action is visible in the toolbar.
52 const char kBrowserActionVisible[] = "browser_action_visible";
55 const char kNoExtensionActionError[] =
56 "This extension has no action specified.";
57 const char kNoTabError[] = "No tab with id: *.";
58 const char kNoPageActionError[] =
59 "This extension has no page action specified.";
60 const char kUrlNotActiveError[] = "This url is no longer active: *.";
61 const char kOpenPopupError[] =
62 "Failed to show popup either because there is an existing popup or another "
64 const char kInternalError[] = "Internal error.";
66 struct IconRepresentationInfo {
67 // Size as a string that will be used to retrieve representation value from
68 // SetIcon function arguments.
69 const char* size_string;
70 // Scale factor for which the represantion should be used.
71 ui::ScaleFactor scale;
74 const IconRepresentationInfo kIconSizes[] = {
75 { "19", ui::SCALE_FACTOR_100P },
76 { "38", ui::SCALE_FACTOR_200P }
79 // Conversion function for reading/writing to storage.
80 SkColor RawStringToSkColor(const std::string& str) {
82 base::StringToUint64(str, &value);
83 SkColor color = static_cast<SkColor>(value);
84 DCHECK(value == color); // ensure value fits into color's 32 bits
88 // Conversion function for reading/writing to storage.
89 std::string SkColorToRawString(SkColor color) {
90 return base::Uint64ToString(color);
93 // Conversion function for reading/writing to storage.
94 bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) {
95 // TODO(mpcomplete): Remove the base64 encode/decode step when
96 // http://crbug.com/140546 is fixed.
98 if (!base::Base64Decode(str, &raw_str))
100 IPC::Message bitmap_pickle(raw_str.data(), raw_str.size());
101 PickleIterator iter(bitmap_pickle);
102 return IPC::ReadParam(&bitmap_pickle, &iter, bitmap);
105 // Conversion function for reading/writing to storage.
106 std::string RepresentationToString(const gfx::ImageSkia& image, float scale) {
107 SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap();
108 IPC::Message bitmap_pickle;
109 // Clear the header values so they don't vary in serialization.
110 bitmap_pickle.SetHeaderValues(0, 0, 0);
111 IPC::WriteParam(&bitmap_pickle, bitmap);
112 std::string raw_str(static_cast<const char*>(bitmap_pickle.data()),
113 bitmap_pickle.size());
114 std::string base64_str;
115 if (!base::Base64Encode(raw_str, &base64_str))
116 return std::string();
120 // Set |action|'s default values to those specified in |dict|.
121 void SetDefaultsFromValue(const base::DictionaryValue* dict,
122 ExtensionAction* action) {
123 const int kTabId = ExtensionAction::kDefaultTabId;
124 std::string str_value;
129 if (dict->GetString(kPopupUrlStorageKey, &str_value))
130 action->SetPopupUrl(kTabId, GURL(str_value));
131 if (dict->GetString(kTitleStorageKey, &str_value))
132 action->SetTitle(kTabId, str_value);
133 if (dict->GetString(kBadgeTextStorageKey, &str_value))
134 action->SetBadgeText(kTabId, str_value);
135 if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value))
136 action->SetBadgeBackgroundColor(kTabId, RawStringToSkColor(str_value));
137 if (dict->GetString(kBadgeTextColorStorageKey, &str_value))
138 action->SetBadgeTextColor(kTabId, RawStringToSkColor(str_value));
139 if (dict->GetInteger(kAppearanceStorageKey, &int_value))
140 action->SetAppearance(kTabId,
141 static_cast<ExtensionAction::Appearance>(int_value));
143 const base::DictionaryValue* icon_value = NULL;
144 if (dict->GetDictionary(kIconStorageKey, &icon_value)) {
145 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
146 if (icon_value->GetString(kIconSizes[i].size_string, &str_value) &&
147 StringToSkBitmap(str_value, &bitmap)) {
148 CHECK(!bitmap.isNull());
149 float scale = ui::GetImageScale(kIconSizes[i].scale);
150 icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
153 action->SetIcon(kTabId, gfx::Image(icon));
157 // Store |action|'s default values in a DictionaryValue for use in storing to
159 scoped_ptr<base::DictionaryValue> DefaultsToValue(ExtensionAction* action) {
160 const int kTabId = ExtensionAction::kDefaultTabId;
161 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
163 dict->SetString(kPopupUrlStorageKey, action->GetPopupUrl(kTabId).spec());
164 dict->SetString(kTitleStorageKey, action->GetTitle(kTabId));
165 dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kTabId));
166 dict->SetString(kBadgeBackgroundColorStorageKey,
167 SkColorToRawString(action->GetBadgeBackgroundColor(kTabId)));
168 dict->SetString(kBadgeTextColorStorageKey,
169 SkColorToRawString(action->GetBadgeTextColor(kTabId)));
170 dict->SetInteger(kAppearanceStorageKey,
171 action->GetIsVisible(kTabId) ?
172 ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
174 gfx::ImageSkia icon = action->GetExplicitlySetIcon(kTabId);
175 if (!icon.isNull()) {
176 base::DictionaryValue* icon_value = new base::DictionaryValue();
177 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
178 float scale = ui::GetImageScale(kIconSizes[i].scale);
179 if (icon.HasRepresentation(scale)) {
180 icon_value->SetString(
181 kIconSizes[i].size_string,
182 RepresentationToString(icon, scale));
185 dict->Set(kIconStorageKey, icon_value);
193 // ExtensionActionAPI
196 static base::LazyInstance<ProfileKeyedAPIFactory<ExtensionActionAPI> >
197 g_factory = LAZY_INSTANCE_INITIALIZER;
199 ExtensionActionAPI::ExtensionActionAPI(Profile* profile) {
200 ExtensionFunctionRegistry* registry =
201 ExtensionFunctionRegistry::GetInstance();
204 registry->RegisterFunction<BrowserActionSetIconFunction>();
205 registry->RegisterFunction<BrowserActionSetTitleFunction>();
206 registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
207 registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
208 registry->RegisterFunction<BrowserActionSetPopupFunction>();
209 registry->RegisterFunction<BrowserActionGetTitleFunction>();
210 registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
211 registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
212 registry->RegisterFunction<BrowserActionGetPopupFunction>();
213 registry->RegisterFunction<BrowserActionEnableFunction>();
214 registry->RegisterFunction<BrowserActionDisableFunction>();
215 registry->RegisterFunction<BrowserActionOpenPopupFunction>();
218 registry->RegisterFunction<EnablePageActionsFunction>();
219 registry->RegisterFunction<DisablePageActionsFunction>();
220 registry->RegisterFunction<PageActionShowFunction>();
221 registry->RegisterFunction<PageActionHideFunction>();
222 registry->RegisterFunction<PageActionSetIconFunction>();
223 registry->RegisterFunction<PageActionSetTitleFunction>();
224 registry->RegisterFunction<PageActionSetPopupFunction>();
225 registry->RegisterFunction<PageActionGetTitleFunction>();
226 registry->RegisterFunction<PageActionGetPopupFunction>();
229 registry->RegisterFunction<ScriptBadgeGetAttentionFunction>();
230 registry->RegisterFunction<ScriptBadgeGetPopupFunction>();
231 registry->RegisterFunction<ScriptBadgeSetPopupFunction>();
234 ExtensionActionAPI::~ExtensionActionAPI() {
238 ProfileKeyedAPIFactory<ExtensionActionAPI>*
239 ExtensionActionAPI::GetFactoryInstance() {
240 return &g_factory.Get();
244 ExtensionActionAPI* ExtensionActionAPI::Get(Profile* profile) {
245 return ProfileKeyedAPIFactory<ExtensionActionAPI>::GetForProfile(profile);
249 bool ExtensionActionAPI::GetBrowserActionVisibility(
250 const ExtensionPrefs* prefs,
251 const std::string& extension_id) {
252 bool visible = false;
253 if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
254 kBrowserActionVisible,
262 void ExtensionActionAPI::SetBrowserActionVisibility(
263 ExtensionPrefs* prefs,
264 const std::string& extension_id,
266 if (GetBrowserActionVisibility(prefs, extension_id) == visible)
269 prefs->UpdateExtensionPref(extension_id,
270 kBrowserActionVisible,
271 new base::FundamentalValue(visible));
272 content::NotificationService::current()->Notify(
273 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
274 content::Source<ExtensionPrefs>(prefs),
275 content::Details<const std::string>(&extension_id));
279 void ExtensionActionAPI::BrowserActionExecuted(
281 const ExtensionAction& browser_action,
282 WebContents* web_contents) {
283 ExtensionActionExecuted(profile, browser_action, web_contents);
287 void ExtensionActionAPI::PageActionExecuted(Profile* profile,
288 const ExtensionAction& page_action,
290 const std::string& url,
292 DispatchOldPageActionEvent(profile, page_action.extension_id(),
293 page_action.id(), tab_id, url, button);
294 WebContents* web_contents = NULL;
295 if (!ExtensionTabUtil::GetTabById(tab_id, profile, profile->IsOffTheRecord(),
296 NULL, NULL, &web_contents, NULL)) {
299 ExtensionActionExecuted(profile, page_action, web_contents);
303 void ExtensionActionAPI::ScriptBadgeExecuted(
305 const ExtensionAction& script_badge,
307 WebContents* web_contents = NULL;
308 if (!ExtensionTabUtil::GetTabById(tab_id, profile, profile->IsOffTheRecord(),
309 NULL, NULL, &web_contents, NULL)) {
312 ExtensionActionExecuted(profile, script_badge, web_contents);
316 void ExtensionActionAPI::DispatchEventToExtension(
318 const std::string& extension_id,
319 const std::string& event_name,
320 scoped_ptr<base::ListValue> event_args) {
321 if (!extensions::ExtensionSystem::Get(profile)->event_router())
324 scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
325 event->restrict_to_profile = profile;
326 event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
327 ExtensionSystem::Get(profile)->event_router()->
328 DispatchEventToExtension(extension_id, event.Pass());
332 void ExtensionActionAPI::DispatchOldPageActionEvent(
334 const std::string& extension_id,
335 const std::string& page_action_id,
337 const std::string& url,
339 scoped_ptr<base::ListValue> args(new base::ListValue());
340 args->Append(new base::StringValue(page_action_id));
342 DictionaryValue* data = new DictionaryValue();
343 data->Set(page_actions_keys::kTabIdKey, new base::FundamentalValue(tab_id));
344 data->Set(page_actions_keys::kTabUrlKey, new base::StringValue(url));
345 data->Set(page_actions_keys::kButtonKey,
346 new base::FundamentalValue(button));
349 DispatchEventToExtension(profile, extension_id, "pageActions", args.Pass());
353 void ExtensionActionAPI::ExtensionActionExecuted(
355 const ExtensionAction& extension_action,
356 WebContents* web_contents) {
357 const char* event_name = NULL;
358 switch (extension_action.action_type()) {
359 case ActionInfo::TYPE_BROWSER:
360 event_name = "browserAction.onClicked";
362 case ActionInfo::TYPE_PAGE:
363 event_name = "pageAction.onClicked";
365 case ActionInfo::TYPE_SCRIPT_BADGE:
366 event_name = "scriptBadge.onClicked";
368 case ActionInfo::TYPE_SYSTEM_INDICATOR:
369 // The System Indicator handles its own clicks.
374 scoped_ptr<base::ListValue> args(new base::ListValue());
375 DictionaryValue* tab_value = ExtensionTabUtil::CreateTabValue(
377 args->Append(tab_value);
379 DispatchEventToExtension(profile,
380 extension_action.extension_id(),
387 // ExtensionActionStorageManager
390 ExtensionActionStorageManager::ExtensionActionStorageManager(Profile* profile)
391 : profile_(profile) {
392 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
393 content::Source<Profile>(profile_));
394 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
395 content::NotificationService::AllBrowserContextsAndSources());
397 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
399 storage->RegisterKey(kBrowserActionStorageKey);
402 ExtensionActionStorageManager::~ExtensionActionStorageManager() {
405 void ExtensionActionStorageManager::Observe(
407 const content::NotificationSource& source,
408 const content::NotificationDetails& details) {
410 case chrome::NOTIFICATION_EXTENSION_LOADED: {
411 const Extension* extension =
412 content::Details<const Extension>(details).ptr();
413 if (!ExtensionActionManager::Get(profile_)->
414 GetBrowserAction(*extension)) {
418 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
420 storage->GetExtensionValue(extension->id(), kBrowserActionStorageKey,
421 base::Bind(&ExtensionActionStorageManager::ReadFromStorage,
422 AsWeakPtr(), extension->id()));
426 case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED: {
427 ExtensionAction* extension_action =
428 content::Source<ExtensionAction>(source).ptr();
429 Profile* profile = content::Details<Profile>(details).ptr();
430 if (profile != profile_)
433 extension_action->set_has_changed(true);
434 WriteToStorage(extension_action);
443 void ExtensionActionStorageManager::WriteToStorage(
444 ExtensionAction* extension_action) {
445 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
449 scoped_ptr<base::DictionaryValue> defaults =
450 DefaultsToValue(extension_action);
451 storage->SetExtensionValue(extension_action->extension_id(),
452 kBrowserActionStorageKey,
453 defaults.PassAs<base::Value>());
456 void ExtensionActionStorageManager::ReadFromStorage(
457 const std::string& extension_id, scoped_ptr<base::Value> value) {
458 const Extension* extension =
459 ExtensionSystem::Get(profile_)->extension_service()->
460 extensions()->GetByID(extension_id);
464 ExtensionAction* browser_action =
465 ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
466 CHECK(browser_action);
468 // Don't load values from storage if the extension has updated a value
469 // already. The extension may have only updated some of the values, but
470 // this is a good first approximation. If the extension is doing stuff
471 // to the browser action, we can assume it is ready to take over.
472 if (browser_action->has_changed())
475 const base::DictionaryValue* dict = NULL;
476 if (!value.get() || !value->GetAsDictionary(&dict))
479 SetDefaultsFromValue(dict, browser_action);
483 // ExtensionActionFunction
486 ExtensionActionFunction::ExtensionActionFunction()
488 tab_id_(ExtensionAction::kDefaultTabId),
490 extension_action_(NULL) {
493 ExtensionActionFunction::~ExtensionActionFunction() {
496 bool ExtensionActionFunction::RunImpl() {
497 ExtensionActionManager* manager = ExtensionActionManager::Get(GetProfile());
498 const Extension* extension = GetExtension();
499 if (StartsWithASCII(name(), "scriptBadge.", false)) {
500 extension_action_ = manager->GetScriptBadge(*extension);
501 } else if (StartsWithASCII(name(), "systemIndicator.", false)) {
502 extension_action_ = manager->GetSystemIndicator(*extension);
504 extension_action_ = manager->GetBrowserAction(*extension);
505 if (!extension_action_) {
506 extension_action_ = manager->GetPageAction(*extension);
509 if (!extension_action_) {
510 // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
511 // exist for extensions that don't have one declared. This should come as
512 // part of the Feature system.
513 error_ = kNoExtensionActionError;
517 // Populates the tab_id_ and details_ members.
518 EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
520 // Find the WebContents that contains this tab id if one is required.
521 if (tab_id_ != ExtensionAction::kDefaultTabId) {
522 ExtensionTabUtil::GetTabById(tab_id_,
530 error_ = ErrorUtils::FormatErrorMessage(
531 kNoTabError, base::IntToString(tab_id_));
535 // Only browser actions and system indicators have a default tabId.
536 ActionInfo::Type action_type = extension_action_->action_type();
537 EXTENSION_FUNCTION_VALIDATE(
538 action_type == ActionInfo::TYPE_BROWSER ||
539 action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
541 return RunExtensionAction();
544 bool ExtensionActionFunction::ExtractDataFromArguments() {
545 // There may or may not be details (depends on the function).
546 // The tabId might appear in details (if it exists), as the first
547 // argument besides the action type (depends on the function), or be omitted
549 base::Value* first_arg = NULL;
550 if (!args_->Get(0, &first_arg))
553 switch (first_arg->GetType()) {
554 case Value::TYPE_INTEGER:
555 CHECK(first_arg->GetAsInteger(&tab_id_));
558 case Value::TYPE_DICTIONARY: {
559 // Found the details argument.
560 details_ = static_cast<base::DictionaryValue*>(first_arg);
561 // Still need to check for the tabId within details.
562 base::Value* tab_id_value = NULL;
563 if (details_->Get("tabId", &tab_id_value)) {
564 switch (tab_id_value->GetType()) {
565 case Value::TYPE_NULL:
566 // OK; tabId is optional, leave it default.
568 case Value::TYPE_INTEGER:
569 CHECK(tab_id_value->GetAsInteger(&tab_id_));
576 // Not found; tabId is optional, leave it default.
580 case Value::TYPE_NULL:
581 // The tabId might be an optional argument.
591 void ExtensionActionFunction::NotifyChange() {
592 switch (extension_action_->action_type()) {
593 case ActionInfo::TYPE_BROWSER:
594 case ActionInfo::TYPE_PAGE:
595 if (ExtensionActionManager::Get(GetProfile())
596 ->GetBrowserAction(*extension_.get())) {
597 NotifyBrowserActionChange();
598 } else if (ExtensionActionManager::Get(GetProfile())
599 ->GetPageAction(*extension_.get())) {
600 NotifyLocationBarChange();
603 case ActionInfo::TYPE_SCRIPT_BADGE:
604 NotifyLocationBarChange();
606 case ActionInfo::TYPE_SYSTEM_INDICATOR:
607 NotifySystemIndicatorChange();
613 void ExtensionActionFunction::NotifyBrowserActionChange() {
614 content::NotificationService::current()->Notify(
615 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
616 content::Source<ExtensionAction>(extension_action_),
617 content::Details<Profile>(GetProfile()));
620 void ExtensionActionFunction::NotifyLocationBarChange() {
621 TabHelper::FromWebContents(contents_)->
622 location_bar_controller()->NotifyChange();
625 void ExtensionActionFunction::NotifySystemIndicatorChange() {
626 content::NotificationService::current()->Notify(
627 chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
628 content::Source<Profile>(GetProfile()),
629 content::Details<ExtensionAction>(extension_action_));
633 bool ExtensionActionFunction::ParseCSSColorString(
634 const std::string& color_string,
636 std::string formatted_color = "#";
637 // Check the string for incorrect formatting.
638 if (color_string[0] != '#')
641 // Convert the string from #FFF format to #FFFFFF format.
642 if (color_string.length() == 4) {
643 for (size_t i = 1; i < color_string.length(); i++) {
644 formatted_color += color_string[i];
645 formatted_color += color_string[i];
648 formatted_color = color_string;
651 if (formatted_color.length() != 7)
654 // Convert the string to an integer and make sure it is in the correct value
656 int color_ints[3] = {0};
657 for (int i = 0; i < 3; i++) {
658 if (!base::HexStringToInt(formatted_color.substr(1 + (2 * i), 2),
661 if (color_ints[i] > 255 || color_ints[i] < 0)
665 *result = SkColorSetARGB(255, color_ints[0], color_ints[1], color_ints[2]);
669 bool ExtensionActionFunction::SetVisible(bool visible) {
670 if (extension_action_->GetIsVisible(tab_id_) == visible)
672 extension_action_->SetAppearance(
673 tab_id_, visible ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
678 TabHelper& ExtensionActionFunction::tab_helper() const {
680 return *TabHelper::FromWebContents(contents_);
683 bool ExtensionActionShowFunction::RunExtensionAction() {
684 return SetVisible(true);
687 bool ExtensionActionHideFunction::RunExtensionAction() {
688 return SetVisible(false);
691 bool ExtensionActionSetIconFunction::RunExtensionAction() {
692 EXTENSION_FUNCTION_VALIDATE(details_);
694 // setIcon can take a variant argument: either a dictionary of canvas
695 // ImageData, or an icon index.
696 base::DictionaryValue* canvas_set = NULL;
698 if (details_->GetDictionary("imageData", &canvas_set)) {
700 // Extract icon representations from the ImageDataSet dictionary.
701 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
702 base::BinaryValue* binary;
703 if (canvas_set->GetBinary(kIconSizes[i].size_string, &binary)) {
704 IPC::Message pickle(binary->GetBuffer(), binary->GetSize());
705 PickleIterator iter(pickle);
707 EXTENSION_FUNCTION_VALIDATE(IPC::ReadParam(&pickle, &iter, &bitmap));
708 CHECK(!bitmap.isNull());
709 float scale = ui::GetImageScale(kIconSizes[i].scale);
710 icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
714 extension_action_->SetIcon(tab_id_, gfx::Image(icon));
715 } else if (details_->GetInteger("iconIndex", &icon_index)) {
716 // Obsolete argument: ignore it.
719 EXTENSION_FUNCTION_VALIDATE(false);
725 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
726 EXTENSION_FUNCTION_VALIDATE(details_);
728 EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
729 extension_action_->SetTitle(tab_id_, title);
734 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
735 EXTENSION_FUNCTION_VALIDATE(details_);
736 std::string popup_string;
737 EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
740 if (!popup_string.empty())
741 popup_url = GetExtension()->GetResourceURL(popup_string);
743 extension_action_->SetPopupUrl(tab_id_, popup_url);
748 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
749 EXTENSION_FUNCTION_VALIDATE(details_);
750 std::string badge_text;
751 EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
752 extension_action_->SetBadgeText(tab_id_, badge_text);
757 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
758 EXTENSION_FUNCTION_VALIDATE(details_);
759 Value* color_value = NULL;
760 EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
762 if (color_value->IsType(Value::TYPE_LIST)) {
763 base::ListValue* list = NULL;
764 EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
765 EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
767 int color_array[4] = {0};
768 for (size_t i = 0; i < arraysize(color_array); ++i) {
769 EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
772 color = SkColorSetARGB(color_array[3], color_array[0],
773 color_array[1], color_array[2]);
774 } else if (color_value->IsType(Value::TYPE_STRING)) {
775 std::string color_string;
776 EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
777 if (!ParseCSSColorString(color_string, &color))
781 extension_action_->SetBadgeBackgroundColor(tab_id_, color);
786 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
787 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_)));
791 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
793 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
797 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
798 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_)));
802 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
803 base::ListValue* list = new base::ListValue();
804 SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
806 new base::FundamentalValue(static_cast<int>(SkColorGetR(color))));
808 new base::FundamentalValue(static_cast<int>(SkColorGetG(color))));
810 new base::FundamentalValue(static_cast<int>(SkColorGetB(color))));
812 new base::FundamentalValue(static_cast<int>(SkColorGetA(color))));
817 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction()
818 : response_sent_(false) {
821 bool BrowserActionOpenPopupFunction::RunImpl() {
822 ExtensionToolbarModel* model = extensions::ExtensionSystem::Get(GetProfile())
823 ->extension_service()
826 error_ = kInternalError;
830 if (!model->ShowBrowserActionPopup(extension_)) {
831 error_ = kOpenPopupError;
836 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
837 content::Source<Profile>(GetProfile()));
839 // Set a timeout for waiting for the notification that the popup is loaded.
840 // Waiting is required so that the popup view can be retrieved by the custom
841 // bindings for the response callback. It's also needed to keep this function
842 // instance around until a notification is observed.
843 base::MessageLoopForUI::current()->PostDelayedTask(
845 base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this),
846 base::TimeDelta::FromSeconds(10));
850 void BrowserActionOpenPopupFunction::OpenPopupTimedOut() {
854 DVLOG(1) << "chrome.browserAction.openPopup did not show a popup.";
855 error_ = kOpenPopupError;
857 response_sent_ = true;
860 void BrowserActionOpenPopupFunction::Observe(
862 const content::NotificationSource& source,
863 const content::NotificationDetails& details) {
864 DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, type);
868 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
869 if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP ||
870 host->extension()->id() != extension_->id())
874 response_sent_ = true;
875 registrar_.RemoveAll();
879 // ScriptBadgeGetAttentionFunction
882 ScriptBadgeGetAttentionFunction::~ScriptBadgeGetAttentionFunction() {}
884 bool ScriptBadgeGetAttentionFunction::RunExtensionAction() {
885 tab_helper().location_bar_controller()->GetAttentionFor(extension_id());
889 } // namespace extensions
892 // PageActionsFunction (deprecated)
895 PageActionsFunction::PageActionsFunction() {
898 PageActionsFunction::~PageActionsFunction() {
901 bool PageActionsFunction::SetPageActionEnabled(bool enable) {
902 std::string extension_action_id;
903 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_action_id));
904 DictionaryValue* action = NULL;
905 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &action));
908 EXTENSION_FUNCTION_VALIDATE(action->GetInteger(
909 page_actions_keys::kTabIdKey, &tab_id));
911 EXTENSION_FUNCTION_VALIDATE(action->GetString(
912 page_actions_keys::kUrlKey, &url));
916 if (action->HasKey(page_actions_keys::kTitleKey))
917 EXTENSION_FUNCTION_VALIDATE(action->GetString(
918 page_actions_keys::kTitleKey, &title));
921 ExtensionAction* page_action = extensions::ExtensionActionManager::Get(
922 GetProfile())->GetPageAction(*GetExtension());
924 error_ = extensions::kNoPageActionError;
928 // Find the WebContents that contains this tab id.
929 WebContents* contents = NULL;
930 bool result = ExtensionTabUtil::GetTabById(
931 tab_id, GetProfile(), include_incognito(), NULL, NULL, &contents, NULL);
932 if (!result || !contents) {
933 error_ = extensions::ErrorUtils::FormatErrorMessage(
934 extensions::kNoTabError, base::IntToString(tab_id));
938 // Make sure the URL hasn't changed.
939 content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
940 if (!entry || url != entry->GetURL().spec()) {
941 error_ = extensions::ErrorUtils::FormatErrorMessage(
942 extensions::kUrlNotActiveError, url);
946 // Set visibility and broadcast notifications that the UI should be updated.
947 page_action->SetAppearance(
948 tab_id, enable ? ExtensionAction::ACTIVE : ExtensionAction::INVISIBLE);
949 page_action->SetTitle(tab_id, title);
950 extensions::TabHelper::FromWebContents(contents)->
951 location_bar_controller()->NotifyChange();
956 bool EnablePageActionsFunction::RunImpl() {
957 return SetPageActionEnabled(true);
960 bool DisablePageActionsFunction::RunImpl() {
961 return SetPageActionEnabled(false);