1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" 6 7 #include "base/base64.h" 8 #include "base/lazy_instance.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_util.h" 11 #include "base/values.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h" 14 #include "chrome/browser/extensions/extension_action.h" 15 #include "chrome/browser/extensions/extension_action_manager.h" 16 #include "chrome/browser/extensions/extension_service.h" 17 #include "chrome/browser/extensions/extension_tab_util.h" 18 #include "chrome/browser/extensions/extension_toolbar_model.h" 19 #include "chrome/browser/extensions/location_bar_controller.h" 20 #include "chrome/browser/extensions/state_store.h" 21 #include "chrome/browser/extensions/tab_helper.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/common/extensions/api/extension_action/action_info.h" 24 #include "chrome/common/render_messages.h" 25 #include "content/public/browser/navigation_entry.h" 26 #include "content/public/browser/notification_service.h" 27 #include "extensions/browser/event_router.h" 28 #include "extensions/browser/extension_function_registry.h" 29 #include "extensions/browser/extension_host.h" 30 #include "extensions/browser/extension_registry.h" 31 #include "extensions/browser/extension_system.h" 32 #include "extensions/browser/image_util.h" 33 #include "extensions/common/error_utils.h" 34 #include "ui/gfx/codec/png_codec.h" 35 #include "ui/gfx/image/image.h" 36 #include "ui/gfx/image/image_skia.h" 37 38 using content::WebContents; 39 40 namespace page_actions_keys = extension_page_actions_api_constants; 41 42 namespace extensions { 43 44 namespace { 45 46 const char kBrowserActionStorageKey[] = "browser_action"; 47 const char kPopupUrlStorageKey[] = "poupup_url"; 48 const char kTitleStorageKey[] = "title"; 49 const char kIconStorageKey[] = "icon"; 50 const char kBadgeTextStorageKey[] = "badge_text"; 51 const char kBadgeBackgroundColorStorageKey[] = "badge_background_color"; 52 const char kBadgeTextColorStorageKey[] = "badge_text_color"; 53 const char kAppearanceStorageKey[] = "appearance"; 54 55 // Only add values to the end of this enum, since it's stored in the user's 56 // Extension State, under the kAppearanceStorageKey. It represents the 57 // ExtensionAction's default visibility. 58 enum StoredAppearance { 59 // The action icon is hidden. 60 INVISIBLE = 0, 61 // The action is trying to get the user's attention but isn't yet 62 // running on the page. Was only used for script badges. 63 OBSOLETE_WANTS_ATTENTION = 1, 64 // The action icon is visible with its normal appearance. 65 ACTIVE = 2, 66 }; 67 68 // Whether the browser action is visible in the toolbar. 69 const char kBrowserActionVisible[] = "browser_action_visible"; 70 71 // Errors. 72 const char kNoExtensionActionError[] = 73 "This extension has no action specified."; 74 const char kNoTabError[] = "No tab with id: *."; 75 const char kNoPageActionError[] = 76 "This extension has no page action specified."; 77 const char kUrlNotActiveError[] = "This url is no longer active: *."; 78 const char kOpenPopupError[] = 79 "Failed to show popup either because there is an existing popup or another " 80 "error occurred."; 81 const char kInternalError[] = "Internal error."; 82 83 struct IconRepresentationInfo { 84 // Size as a string that will be used to retrieve representation value from 85 // SetIcon function arguments. 86 const char* size_string; 87 // Scale factor for which the represantion should be used. 88 ui::ScaleFactor scale; 89 }; 90 91 const IconRepresentationInfo kIconSizes[] = { 92 { "19", ui::SCALE_FACTOR_100P }, 93 { "38", ui::SCALE_FACTOR_200P } 94 }; 95 96 // Conversion function for reading/writing to storage. 97 SkColor RawStringToSkColor(const std::string& str) { 98 uint64 value = 0; 99 base::StringToUint64(str, &value); 100 SkColor color = static_cast<SkColor>(value); 101 DCHECK(value == color); // ensure value fits into color's 32 bits 102 return color; 103 } 104 105 // Conversion function for reading/writing to storage. 106 std::string SkColorToRawString(SkColor color) { 107 return base::Uint64ToString(color); 108 } 109 110 // Conversion function for reading/writing to storage. 111 bool StringToSkBitmap(const std::string& str, SkBitmap* bitmap) { 112 // TODO(mpcomplete): Remove the base64 encode/decode step when 113 // http://crbug.com/140546 is fixed. 114 std::string raw_str; 115 if (!base::Base64Decode(str, &raw_str)) 116 return false; 117 118 bool success = gfx::PNGCodec::Decode( 119 reinterpret_cast<unsigned const char*>(raw_str.data()), raw_str.size(), 120 bitmap); 121 return success; 122 } 123 124 // Conversion function for reading/writing to storage. 125 std::string RepresentationToString(const gfx::ImageSkia& image, float scale) { 126 SkBitmap bitmap = image.GetRepresentation(scale).sk_bitmap(); 127 SkAutoLockPixels lock_image(bitmap); 128 std::vector<unsigned char> data; 129 bool success = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data); 130 if (!success) 131 return std::string(); 132 133 base::StringPiece raw_str( 134 reinterpret_cast<const char*>(&data[0]), data.size()); 135 std::string base64_str; 136 base::Base64Encode(raw_str, &base64_str); 137 return base64_str; 138 } 139 140 // Set |action|'s default values to those specified in |dict|. 141 void SetDefaultsFromValue(const base::DictionaryValue* dict, 142 ExtensionAction* action) { 143 const int kDefaultTabId = ExtensionAction::kDefaultTabId; 144 std::string str_value; 145 int int_value; 146 SkBitmap bitmap; 147 gfx::ImageSkia icon; 148 149 // For each value, don't set it if it has been modified already. 150 if (dict->GetString(kPopupUrlStorageKey, &str_value) && 151 !action->HasPopupUrl(kDefaultTabId)) { 152 action->SetPopupUrl(kDefaultTabId, GURL(str_value)); 153 } 154 if (dict->GetString(kTitleStorageKey, &str_value) && 155 !action->HasTitle(kDefaultTabId)) { 156 action->SetTitle(kDefaultTabId, str_value); 157 } 158 if (dict->GetString(kBadgeTextStorageKey, &str_value) && 159 !action->HasBadgeText(kDefaultTabId)) { 160 action->SetBadgeText(kDefaultTabId, str_value); 161 } 162 if (dict->GetString(kBadgeBackgroundColorStorageKey, &str_value) && 163 !action->HasBadgeBackgroundColor(kDefaultTabId)) { 164 action->SetBadgeBackgroundColor(kDefaultTabId, 165 RawStringToSkColor(str_value)); 166 } 167 if (dict->GetString(kBadgeTextColorStorageKey, &str_value) && 168 !action->HasBadgeTextColor(kDefaultTabId)) { 169 action->SetBadgeTextColor(kDefaultTabId, RawStringToSkColor(str_value)); 170 } 171 if (dict->GetInteger(kAppearanceStorageKey, &int_value) && 172 !action->HasIsVisible(kDefaultTabId)) { 173 switch (int_value) { 174 case INVISIBLE: 175 case OBSOLETE_WANTS_ATTENTION: 176 action->SetIsVisible(kDefaultTabId, false); 177 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. 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 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 272 ExtensionActionAPI::~ExtensionActionAPI() { 273 } 274 275 // static 276 BrowserContextKeyedAPIFactory<ExtensionActionAPI>* 277 ExtensionActionAPI::GetFactoryInstance() { 278 return g_factory.Pointer(); 279 } 280 281 // static 282 ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) { 283 return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context); 284 } 285 286 // static 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 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 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 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 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 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 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 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 431 ExtensionActionStorageManager::~ExtensionActionStorageManager() { 432 } 433 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 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 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 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 507 ExtensionActionFunction::ExtensionActionFunction() 508 : details_(NULL), 509 tab_id_(ExtensionAction::kDefaultTabId), 510 contents_(NULL), 511 extension_action_(NULL) { 512 } 513 514 ExtensionActionFunction::~ExtensionActionFunction() { 515 } 516 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 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 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 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 636 void ExtensionActionFunction::NotifyLocationBarChange() { 637 LocationBarController::NotifyChange(contents_); 638 } 639 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 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 655 TabHelper& ExtensionActionFunction::tab_helper() const { 656 CHECK(contents_); 657 return *TabHelper::FromWebContents(contents_); 658 } 659 660 bool ExtensionActionShowFunction::RunExtensionAction() { 661 return SetVisible(true); 662 } 663 664 bool ExtensionActionHideFunction::RunExtensionAction() { 665 return SetVisible(false); 666 } 667 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 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 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 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 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 763 bool ExtensionActionGetTitleFunction::RunExtensionAction() { 764 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_))); 765 return true; 766 } 767 768 bool ExtensionActionGetPopupFunction::RunExtensionAction() { 769 SetResult( 770 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec())); 771 return true; 772 } 773 774 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() { 775 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_))); 776 return true; 777 } 778 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 794 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction() 795 : response_sent_(false) { 796 } 797 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 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 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 859 PageActionsFunction::PageActionsFunction() { 860 } 861 862 PageActionsFunction::~PageActionsFunction() { 863 } 864 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 918 bool EnablePageActionsFunction::RunSync() { 919 return SetPageActionEnabled(true); 920 } 921 922 bool DisablePageActionsFunction::RunSync() { 923 return SetPageActionEnabled(false); 924 } 925