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/extension_action.h"
15 #include "chrome/browser/extensions/extension_action_manager.h"
16 #include "chrome/browser/extensions/extension_function_registry.h"
17 #include "chrome/browser/extensions/extension_host.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/extension_tab_util.h"
20 #include "chrome/browser/extensions/extension_toolbar_model.h"
21 #include "chrome/browser/extensions/location_bar_controller.h"
22 #include "chrome/browser/extensions/state_store.h"
23 #include "chrome/browser/extensions/tab_helper.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/common/extensions/api/extension_action/action_info.h"
26 #include "chrome/common/render_messages.h"
27 #include "content/public/browser/navigation_entry.h"
28 #include "content/public/browser/notification_service.h"
29 #include "extensions/browser/event_router.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/common/error_utils.h"
32 #include "ui/gfx/codec/png_codec.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_skia.h"
36 using content::WebContents;
38 namespace page_actions_keys = extension_page_actions_api_constants;
40 namespace extensions {
44 const char kBrowserActionStorageKey[] = "browser_action";
45 const char kPopupUrlStorageKey[] = "poupup_url";
46 const char kTitleStorageKey[] = "title";
47 const char kIconStorageKey[] = "icon";
48 const char kBadgeTextStorageKey[] = "badge_text";
49 const char kBadgeBackgroundColorStorageKey[] = "badge_background_color";
50 const char kBadgeTextColorStorageKey[] = "badge_text_color";
51 const char kAppearanceStorageKey[] = "appearance";
53 // Only add values to the end of this enum, since it's stored in the user's
54 // Extension State, under the kAppearanceStorageKey. It represents the
55 // ExtensionAction's default visibility.
56 enum StoredAppearance {
57 // The action icon is hidden.
59 // The action is trying to get the user's attention but isn't yet
60 // running on the page. Was only used for script badges.
61 OBSOLETE_WANTS_ATTENTION = 1,
62 // The action icon is visible with its normal appearance.
66 // Whether the browser action is visible in the toolbar.
67 const char kBrowserActionVisible[] = "browser_action_visible";
70 const char kNoExtensionActionError[] =
71 "This extension has no action specified.";
72 const char kNoTabError[] = "No tab with id: *.";
73 const char kNoPageActionError[] =
74 "This extension has no page action specified.";
75 const char kUrlNotActiveError[] = "This url is no longer active: *.";
76 const char kOpenPopupError[] =
77 "Failed to show popup either because there is an existing popup or another "
79 const char kInternalError[] = "Internal error.";
81 struct IconRepresentationInfo {
82 // Size as a string that will be used to retrieve representation value from
83 // SetIcon function arguments.
84 const char* size_string;
85 // Scale factor for which the represantion should be used.
86 ui::ScaleFactor scale;
89 const IconRepresentationInfo kIconSizes[] = {
90 { "19", ui::SCALE_FACTOR_100P },
91 { "38", ui::SCALE_FACTOR_200P }
94 // Conversion function for reading/writing to storage.
95 SkColor RawStringToSkColor(const std::string& str) {
97 base::StringToUint64(str, &value);
98 SkColor color = static_cast<SkColor>(value);
99 DCHECK(value == color); // ensure value fits into color's 32 bits
103 // Conversion function for reading/writing to storage.
104 std::string SkColorToRawString(SkColor color) {
105 return base::Uint64ToString(color);
108 // Conversion function for reading/writing to storage.
109 bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) {
110 // TODO(mpcomplete): Remove the base64 encode/decode step when
111 // http://crbug.com/140546 is fixed.
113 if (!base::Base64Decode(str, &raw_str))
116 bool success = gfx::PNGCodec::Decode(
117 reinterpret_cast<unsigned const char*>(raw_str.data()), raw_str.size(),
122 // Conversion function for reading/writing to storage.
123 std::string RepresentationToString(const gfx::ImageSkia& image, float scale) {
124 SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap();
125 SkAutoLockPixels lock_image(bitmap);
126 std::vector<unsigned char> data;
127 bool success = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data);
129 return std::string();
131 base::StringPiece raw_str(
132 reinterpret_cast<const char*>(&data[0]), data.size());
133 std::string base64_str;
134 base::Base64Encode(raw_str, &base64_str);
138 // Set |action|'s default values to those specified in |dict|.
139 void SetDefaultsFromValue(const base::DictionaryValue* dict,
140 ExtensionAction* action) {
141 const int kTabId = ExtensionAction::kDefaultTabId;
142 std::string str_value;
147 if (dict->GetString(kPopupUrlStorageKey, &str_value))
148 action->SetPopupUrl(kTabId, GURL(str_value));
149 if (dict->GetString(kTitleStorageKey, &str_value))
150 action->SetTitle(kTabId, str_value);
151 if (dict->GetString(kBadgeTextStorageKey, &str_value))
152 action->SetBadgeText(kTabId, str_value);
153 if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value))
154 action->SetBadgeBackgroundColor(kTabId, RawStringToSkColor(str_value));
155 if (dict->GetString(kBadgeTextColorStorageKey, &str_value))
156 action->SetBadgeTextColor(kTabId, RawStringToSkColor(str_value));
157 if (dict->GetInteger(kAppearanceStorageKey, &int_value)) {
160 case OBSOLETE_WANTS_ATTENTION:
161 action->SetIsVisible(kTabId, false);
163 action->SetIsVisible(kTabId, true);
167 const base::DictionaryValue* icon_value = NULL;
168 if (dict->GetDictionary(kIconStorageKey, &icon_value)) {
169 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
170 if (icon_value->GetString(kIconSizes[i].size_string, &str_value) &&
171 StringToSkBitmap(str_value, &bitmap)) {
172 CHECK(!bitmap.isNull());
173 float scale = ui::GetImageScale(kIconSizes[i].scale);
174 icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
177 action->SetIcon(kTabId, gfx::Image(icon));
181 // Store |action|'s default values in a DictionaryValue for use in storing to
183 scoped_ptr<base::DictionaryValue> DefaultsToValue(ExtensionAction* action) {
184 const int kTabId = ExtensionAction::kDefaultTabId;
185 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
187 dict->SetString(kPopupUrlStorageKey, action->GetPopupUrl(kTabId).spec());
188 dict->SetString(kTitleStorageKey, action->GetTitle(kTabId));
189 dict->SetString(kBadgeTextStorageKey, action->GetBadgeText(kTabId));
190 dict->SetString(kBadgeBackgroundColorStorageKey,
191 SkColorToRawString(action->GetBadgeBackgroundColor(kTabId)));
192 dict->SetString(kBadgeTextColorStorageKey,
193 SkColorToRawString(action->GetBadgeTextColor(kTabId)));
194 dict->SetInteger(kAppearanceStorageKey,
195 action->GetIsVisible(kTabId) ? ACTIVE : INVISIBLE);
197 gfx::ImageSkia icon = action->GetExplicitlySetIcon(kTabId);
198 if (!icon.isNull()) {
199 base::DictionaryValue* icon_value = new base::DictionaryValue();
200 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
201 float scale = ui::GetImageScale(kIconSizes[i].scale);
202 if (icon.HasRepresentation(scale)) {
203 icon_value->SetString(
204 kIconSizes[i].size_string,
205 RepresentationToString(icon, scale));
208 dict->Set(kIconStorageKey, icon_value);
216 // ExtensionActionAPI
219 static base::LazyInstance<ProfileKeyedAPIFactory<ExtensionActionAPI> >
220 g_factory = LAZY_INSTANCE_INITIALIZER;
222 ExtensionActionAPI::ExtensionActionAPI(Profile* profile) {
223 ExtensionFunctionRegistry* registry =
224 ExtensionFunctionRegistry::GetInstance();
227 registry->RegisterFunction<BrowserActionSetIconFunction>();
228 registry->RegisterFunction<BrowserActionSetTitleFunction>();
229 registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
230 registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
231 registry->RegisterFunction<BrowserActionSetPopupFunction>();
232 registry->RegisterFunction<BrowserActionGetTitleFunction>();
233 registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
234 registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
235 registry->RegisterFunction<BrowserActionGetPopupFunction>();
236 registry->RegisterFunction<BrowserActionEnableFunction>();
237 registry->RegisterFunction<BrowserActionDisableFunction>();
238 registry->RegisterFunction<BrowserActionOpenPopupFunction>();
241 registry->RegisterFunction<EnablePageActionsFunction>();
242 registry->RegisterFunction<DisablePageActionsFunction>();
243 registry->RegisterFunction<PageActionShowFunction>();
244 registry->RegisterFunction<PageActionHideFunction>();
245 registry->RegisterFunction<PageActionSetIconFunction>();
246 registry->RegisterFunction<PageActionSetTitleFunction>();
247 registry->RegisterFunction<PageActionSetPopupFunction>();
248 registry->RegisterFunction<PageActionGetTitleFunction>();
249 registry->RegisterFunction<PageActionGetPopupFunction>();
252 ExtensionActionAPI::~ExtensionActionAPI() {
256 ProfileKeyedAPIFactory<ExtensionActionAPI>*
257 ExtensionActionAPI::GetFactoryInstance() {
258 return g_factory.Pointer();
262 ExtensionActionAPI* ExtensionActionAPI::Get(Profile* profile) {
263 return ProfileKeyedAPIFactory<ExtensionActionAPI>::GetForProfile(profile);
267 bool ExtensionActionAPI::GetBrowserActionVisibility(
268 const ExtensionPrefs* prefs,
269 const std::string& extension_id) {
270 bool visible = false;
271 if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
272 kBrowserActionVisible,
280 void ExtensionActionAPI::SetBrowserActionVisibility(
281 ExtensionPrefs* prefs,
282 const std::string& extension_id,
284 if (GetBrowserActionVisibility(prefs, extension_id) == visible)
287 prefs->UpdateExtensionPref(extension_id,
288 kBrowserActionVisible,
289 new base::FundamentalValue(visible));
290 content::NotificationService::current()->Notify(
291 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
292 content::Source<ExtensionPrefs>(prefs),
293 content::Details<const std::string>(&extension_id));
297 void ExtensionActionAPI::BrowserActionExecuted(
299 const ExtensionAction& browser_action,
300 WebContents* web_contents) {
301 ExtensionActionExecuted(profile, browser_action, web_contents);
305 void ExtensionActionAPI::PageActionExecuted(Profile* profile,
306 const ExtensionAction& page_action,
308 const std::string& url,
310 DispatchOldPageActionEvent(profile, page_action.extension_id(),
311 page_action.id(), tab_id, url, button);
312 WebContents* web_contents = NULL;
313 if (!extensions::ExtensionTabUtil::GetTabById(
314 tab_id, profile, profile->IsOffTheRecord(),
315 NULL, NULL, &web_contents, NULL)) {
318 ExtensionActionExecuted(profile, page_action, web_contents);
322 void ExtensionActionAPI::DispatchEventToExtension(
324 const std::string& extension_id,
325 const std::string& event_name,
326 scoped_ptr<base::ListValue> event_args) {
327 if (!extensions::ExtensionSystem::Get(profile)->event_router())
330 scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
331 event->restrict_to_browser_context = profile;
332 event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
333 ExtensionSystem::Get(profile)->event_router()->
334 DispatchEventToExtension(extension_id, event.Pass());
338 void ExtensionActionAPI::DispatchOldPageActionEvent(
340 const std::string& extension_id,
341 const std::string& page_action_id,
343 const std::string& url,
345 scoped_ptr<base::ListValue> args(new base::ListValue());
346 args->Append(new base::StringValue(page_action_id));
348 base::DictionaryValue* data = new base::DictionaryValue();
349 data->Set(page_actions_keys::kTabIdKey, new base::FundamentalValue(tab_id));
350 data->Set(page_actions_keys::kTabUrlKey, new base::StringValue(url));
351 data->Set(page_actions_keys::kButtonKey,
352 new base::FundamentalValue(button));
355 DispatchEventToExtension(profile, extension_id, "pageActions", args.Pass());
359 void ExtensionActionAPI::ExtensionActionExecuted(
361 const ExtensionAction& extension_action,
362 WebContents* web_contents) {
363 const char* event_name = NULL;
364 switch (extension_action.action_type()) {
365 case ActionInfo::TYPE_BROWSER:
366 event_name = "browserAction.onClicked";
368 case ActionInfo::TYPE_PAGE:
369 event_name = "pageAction.onClicked";
371 case ActionInfo::TYPE_SYSTEM_INDICATOR:
372 // The System Indicator handles its own clicks.
377 scoped_ptr<base::ListValue> args(new base::ListValue());
378 base::DictionaryValue* tab_value =
379 extensions::ExtensionTabUtil::CreateTabValue(web_contents);
380 args->Append(tab_value);
382 DispatchEventToExtension(profile,
383 extension_action.extension_id(),
390 // ExtensionActionStorageManager
393 ExtensionActionStorageManager::ExtensionActionStorageManager(Profile* profile)
394 : profile_(profile) {
395 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
396 content::Source<Profile>(profile_));
397 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
398 content::NotificationService::AllBrowserContextsAndSources());
400 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
402 storage->RegisterKey(kBrowserActionStorageKey);
405 ExtensionActionStorageManager::~ExtensionActionStorageManager() {
408 void ExtensionActionStorageManager::Observe(
410 const content::NotificationSource& source,
411 const content::NotificationDetails& details) {
413 case chrome::NOTIFICATION_EXTENSION_LOADED: {
414 const Extension* extension =
415 content::Details<const Extension>(details).ptr();
416 if (!ExtensionActionManager::Get(profile_)->
417 GetBrowserAction(*extension)) {
421 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
423 storage->GetExtensionValue(extension->id(), kBrowserActionStorageKey,
424 base::Bind(&ExtensionActionStorageManager::ReadFromStorage,
425 AsWeakPtr(), extension->id()));
429 case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED: {
430 ExtensionAction* extension_action =
431 content::Source<ExtensionAction>(source).ptr();
432 Profile* profile = content::Details<Profile>(details).ptr();
433 if (profile != profile_)
436 extension_action->set_has_changed(true);
437 WriteToStorage(extension_action);
446 void ExtensionActionStorageManager::WriteToStorage(
447 ExtensionAction* extension_action) {
448 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
452 scoped_ptr<base::DictionaryValue> defaults =
453 DefaultsToValue(extension_action);
454 storage->SetExtensionValue(extension_action->extension_id(),
455 kBrowserActionStorageKey,
456 defaults.PassAs<base::Value>());
459 void ExtensionActionStorageManager::ReadFromStorage(
460 const std::string& extension_id, scoped_ptr<base::Value> value) {
461 const Extension* extension =
462 ExtensionSystem::Get(profile_)->extension_service()->
463 extensions()->GetByID(extension_id);
467 ExtensionAction* browser_action =
468 ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
469 CHECK(browser_action);
471 // Don't load values from storage if the extension has updated a value
472 // already. The extension may have only updated some of the values, but
473 // this is a good first approximation. If the extension is doing stuff
474 // to the browser action, we can assume it is ready to take over.
475 if (browser_action->has_changed())
478 const base::DictionaryValue* dict = NULL;
479 if (!value.get() || !value->GetAsDictionary(&dict))
482 SetDefaultsFromValue(dict, browser_action);
486 // ExtensionActionFunction
489 ExtensionActionFunction::ExtensionActionFunction()
491 tab_id_(ExtensionAction::kDefaultTabId),
493 extension_action_(NULL) {
496 ExtensionActionFunction::~ExtensionActionFunction() {
499 bool ExtensionActionFunction::RunImpl() {
500 ExtensionActionManager* manager = ExtensionActionManager::Get(GetProfile());
501 const Extension* extension = GetExtension();
502 if (StartsWithASCII(name(), "systemIndicator.", false)) {
503 extension_action_ = manager->GetSystemIndicator(*extension);
505 extension_action_ = manager->GetBrowserAction(*extension);
506 if (!extension_action_) {
507 extension_action_ = manager->GetPageAction(*extension);
510 if (!extension_action_) {
511 // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
512 // exist for extensions that don't have one declared. This should come as
513 // part of the Feature system.
514 error_ = kNoExtensionActionError;
518 // Populates the tab_id_ and details_ members.
519 EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
521 // Find the WebContents that contains this tab id if one is required.
522 if (tab_id_ != ExtensionAction::kDefaultTabId) {
523 ExtensionTabUtil::GetTabById(tab_id_,
531 error_ = ErrorUtils::FormatErrorMessage(
532 kNoTabError, base::IntToString(tab_id_));
536 // Only browser actions and system indicators have a default tabId.
537 ActionInfo::Type action_type = extension_action_->action_type();
538 EXTENSION_FUNCTION_VALIDATE(
539 action_type == ActionInfo::TYPE_BROWSER ||
540 action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
542 return RunExtensionAction();
545 bool ExtensionActionFunction::ExtractDataFromArguments() {
546 // There may or may not be details (depends on the function).
547 // The tabId might appear in details (if it exists), as the first
548 // argument besides the action type (depends on the function), or be omitted
550 base::Value* first_arg = NULL;
551 if (!args_->Get(0, &first_arg))
554 switch (first_arg->GetType()) {
555 case base::Value::TYPE_INTEGER:
556 CHECK(first_arg->GetAsInteger(&tab_id_));
559 case base::Value::TYPE_DICTIONARY: {
560 // Found the details argument.
561 details_ = static_cast<base::DictionaryValue*>(first_arg);
562 // Still need to check for the tabId within details.
563 base::Value* tab_id_value = NULL;
564 if (details_->Get("tabId", &tab_id_value)) {
565 switch (tab_id_value->GetType()) {
566 case base::Value::TYPE_NULL:
567 // OK; tabId is optional, leave it default.
569 case base::Value::TYPE_INTEGER:
570 CHECK(tab_id_value->GetAsInteger(&tab_id_));
577 // Not found; tabId is optional, leave it default.
581 case base::Value::TYPE_NULL:
582 // The tabId might be an optional argument.
592 void ExtensionActionFunction::NotifyChange() {
593 switch (extension_action_->action_type()) {
594 case ActionInfo::TYPE_BROWSER:
595 case ActionInfo::TYPE_PAGE:
596 if (ExtensionActionManager::Get(GetProfile())
597 ->GetBrowserAction(*extension_.get())) {
598 NotifyBrowserActionChange();
599 } else if (ExtensionActionManager::Get(GetProfile())
600 ->GetPageAction(*extension_.get())) {
601 NotifyLocationBarChange();
604 case ActionInfo::TYPE_SYSTEM_INDICATOR:
605 NotifySystemIndicatorChange();
611 void ExtensionActionFunction::NotifyBrowserActionChange() {
612 content::NotificationService::current()->Notify(
613 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
614 content::Source<ExtensionAction>(extension_action_),
615 content::Details<Profile>(GetProfile()));
618 void ExtensionActionFunction::NotifyLocationBarChange() {
619 TabHelper::FromWebContents(contents_)->
620 location_bar_controller()->NotifyChange();
623 void ExtensionActionFunction::NotifySystemIndicatorChange() {
624 content::NotificationService::current()->Notify(
625 chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
626 content::Source<Profile>(GetProfile()),
627 content::Details<ExtensionAction>(extension_action_));
631 bool ExtensionActionFunction::ParseCSSColorString(
632 const std::string& color_string,
634 std::string formatted_color = "#";
635 // Check the string for incorrect formatting.
636 if (color_string[0] != '#')
639 // Convert the string from #FFF format to #FFFFFF format.
640 if (color_string.length() == 4) {
641 for (size_t i = 1; i < color_string.length(); i++) {
642 formatted_color += color_string[i];
643 formatted_color += color_string[i];
646 formatted_color = color_string;
649 if (formatted_color.length() != 7)
652 // Convert the string to an integer and make sure it is in the correct value
654 int color_ints[3] = {0};
655 for (int i = 0; i < 3; i++) {
656 if (!base::HexStringToInt(formatted_color.substr(1 + (2 * i), 2),
659 if (color_ints[i] > 255 || color_ints[i] < 0)
663 *result = SkColorSetARGB(255, color_ints[0], color_ints[1], color_ints[2]);
667 bool ExtensionActionFunction::SetVisible(bool visible) {
668 if (extension_action_->GetIsVisible(tab_id_) == visible)
670 extension_action_->SetIsVisible(tab_id_, visible);
675 TabHelper& ExtensionActionFunction::tab_helper() const {
677 return *TabHelper::FromWebContents(contents_);
680 bool ExtensionActionShowFunction::RunExtensionAction() {
681 return SetVisible(true);
684 bool ExtensionActionHideFunction::RunExtensionAction() {
685 return SetVisible(false);
688 bool ExtensionActionSetIconFunction::RunExtensionAction() {
689 EXTENSION_FUNCTION_VALIDATE(details_);
691 // setIcon can take a variant argument: either a dictionary of canvas
692 // ImageData, or an icon index.
693 base::DictionaryValue* canvas_set = NULL;
695 if (details_->GetDictionary("imageData", &canvas_set)) {
697 // Extract icon representations from the ImageDataSet dictionary.
698 for (size_t i = 0; i < arraysize(kIconSizes); i++) {
699 base::BinaryValue* binary;
700 if (canvas_set->GetBinary(kIconSizes[i].size_string, &binary)) {
701 IPC::Message pickle(binary->GetBuffer(), binary->GetSize());
702 PickleIterator iter(pickle);
704 EXTENSION_FUNCTION_VALIDATE(IPC::ReadParam(&pickle, &iter, &bitmap));
705 CHECK(!bitmap.isNull());
706 float scale = ui::GetImageScale(kIconSizes[i].scale);
707 icon.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
711 extension_action_->SetIcon(tab_id_, gfx::Image(icon));
712 } else if (details_->GetInteger("iconIndex", &icon_index)) {
713 // Obsolete argument: ignore it.
716 EXTENSION_FUNCTION_VALIDATE(false);
722 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
723 EXTENSION_FUNCTION_VALIDATE(details_);
725 EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
726 extension_action_->SetTitle(tab_id_, title);
731 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
732 EXTENSION_FUNCTION_VALIDATE(details_);
733 std::string popup_string;
734 EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
737 if (!popup_string.empty())
738 popup_url = GetExtension()->GetResourceURL(popup_string);
740 extension_action_->SetPopupUrl(tab_id_, popup_url);
745 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
746 EXTENSION_FUNCTION_VALIDATE(details_);
747 std::string badge_text;
748 EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
749 extension_action_->SetBadgeText(tab_id_, badge_text);
754 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
755 EXTENSION_FUNCTION_VALIDATE(details_);
756 base::Value* color_value = NULL;
757 EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
759 if (color_value->IsType(base::Value::TYPE_LIST)) {
760 base::ListValue* list = NULL;
761 EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
762 EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
764 int color_array[4] = {0};
765 for (size_t i = 0; i < arraysize(color_array); ++i) {
766 EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
769 color = SkColorSetARGB(color_array[3], color_array[0],
770 color_array[1], color_array[2]);
771 } else if (color_value->IsType(base::Value::TYPE_STRING)) {
772 std::string color_string;
773 EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
774 if (!ParseCSSColorString(color_string, &color))
778 extension_action_->SetBadgeBackgroundColor(tab_id_, color);
783 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
784 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_)));
788 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
790 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
794 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
795 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_)));
799 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
800 base::ListValue* list = new base::ListValue();
801 SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
803 new base::FundamentalValue(static_cast<int>(SkColorGetR(color))));
805 new base::FundamentalValue(static_cast<int>(SkColorGetG(color))));
807 new base::FundamentalValue(static_cast<int>(SkColorGetB(color))));
809 new base::FundamentalValue(static_cast<int>(SkColorGetA(color))));
814 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction()
815 : response_sent_(false) {
818 bool BrowserActionOpenPopupFunction::RunImpl() {
819 ExtensionToolbarModel* model = ExtensionToolbarModel::Get(GetProfile());
821 error_ = kInternalError;
825 if (!model->ShowBrowserActionPopup(extension_)) {
826 error_ = kOpenPopupError;
831 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
832 content::Source<Profile>(GetProfile()));
834 // Set a timeout for waiting for the notification that the popup is loaded.
835 // Waiting is required so that the popup view can be retrieved by the custom
836 // bindings for the response callback. It's also needed to keep this function
837 // instance around until a notification is observed.
838 base::MessageLoopForUI::current()->PostDelayedTask(
840 base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this),
841 base::TimeDelta::FromSeconds(10));
845 void BrowserActionOpenPopupFunction::OpenPopupTimedOut() {
849 DVLOG(1) << "chrome.browserAction.openPopup did not show a popup.";
850 error_ = kOpenPopupError;
852 response_sent_ = true;
855 void BrowserActionOpenPopupFunction::Observe(
857 const content::NotificationSource& source,
858 const content::NotificationDetails& details) {
859 DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, type);
863 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
864 if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP ||
865 host->extension()->id() != extension_->id())
869 response_sent_ = true;
870 registrar_.RemoveAll();
873 } // namespace extensions
876 // PageActionsFunction (deprecated)
879 PageActionsFunction::PageActionsFunction() {
882 PageActionsFunction::~PageActionsFunction() {
885 bool PageActionsFunction::SetPageActionEnabled(bool enable) {
886 std::string extension_action_id;
887 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_action_id));
888 base::DictionaryValue* action = NULL;
889 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &action));
892 EXTENSION_FUNCTION_VALIDATE(action->GetInteger(
893 page_actions_keys::kTabIdKey, &tab_id));
895 EXTENSION_FUNCTION_VALIDATE(action->GetString(
896 page_actions_keys::kUrlKey, &url));
900 if (action->HasKey(page_actions_keys::kTitleKey))
901 EXTENSION_FUNCTION_VALIDATE(action->GetString(
902 page_actions_keys::kTitleKey, &title));
905 ExtensionAction* page_action = extensions::ExtensionActionManager::Get(
906 GetProfile())->GetPageAction(*GetExtension());
908 error_ = extensions::kNoPageActionError;
912 // Find the WebContents that contains this tab id.
913 WebContents* contents = NULL;
914 bool result = extensions::ExtensionTabUtil::GetTabById(
915 tab_id, GetProfile(), include_incognito(), NULL, NULL, &contents, NULL);
916 if (!result || !contents) {
917 error_ = extensions::ErrorUtils::FormatErrorMessage(
918 extensions::kNoTabError, base::IntToString(tab_id));
922 // Make sure the URL hasn't changed.
923 content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
924 if (!entry || url != entry->GetURL().spec()) {
925 error_ = extensions::ErrorUtils::FormatErrorMessage(
926 extensions::kUrlNotActiveError, url);
930 // Set visibility and broadcast notifications that the UI should be updated.
931 page_action->SetIsVisible(tab_id, enable);
932 page_action->SetTitle(tab_id, title);
933 extensions::TabHelper::FromWebContents(contents)->
934 location_bar_controller()->NotifyChange();
939 bool EnablePageActionsFunction::RunImpl() {
940 return SetPageActionEnabled(true);
943 bool DisablePageActionsFunction::RunImpl() {
944 return SetPageActionEnabled(false);