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