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 38using content::WebContents; 39 40namespace page_actions_keys = extension_page_actions_api_constants; 41 42namespace extensions { 43 44namespace { 45 46const char kBrowserActionStorageKey[] = "browser_action"; 47const char kPopupUrlStorageKey[] = "poupup_url"; 48const char kTitleStorageKey[] = "title"; 49const char kIconStorageKey[] = "icon"; 50const char kBadgeTextStorageKey[] = "badge_text"; 51const char kBadgeBackgroundColorStorageKey[] = "badge_background_color"; 52const char kBadgeTextColorStorageKey[] = "badge_text_color"; 53const 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. 58enum 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. 69const char kBrowserActionVisible[] = "browser_action_visible"; 70 71// Errors. 72const char kNoExtensionActionError[] = 73 "This extension has no action specified."; 74const char kNoTabError[] = "No tab with id: *."; 75const char kNoPageActionError[] = 76 "This extension has no page action specified."; 77const char kUrlNotActiveError[] = "This url is no longer active: *."; 78const char kOpenPopupError[] = 79 "Failed to show popup either because there is an existing popup or another " 80 "error occurred."; 81const char kInternalError[] = "Internal error."; 82 83struct 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 91const IconRepresentationInfo kIconSizes[] = { 92 { "19", ui::SCALE_FACTOR_100P }, 93 { "38", ui::SCALE_FACTOR_200P } 94}; 95 96// Conversion function for reading/writing to storage. 97SkColor 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. 106std::string SkColorToRawString(SkColor color) { 107 return base::Uint64ToString(color); 108} 109 110// Conversion function for reading/writing to storage. 111bool 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. 125std::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|. 141void 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. 201scoped_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 239static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI> > 240 g_factory = LAZY_INSTANCE_INITIALIZER; 241 242ExtensionActionAPI::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 272ExtensionActionAPI::~ExtensionActionAPI() { 273} 274 275// static 276BrowserContextKeyedAPIFactory<ExtensionActionAPI>* 277ExtensionActionAPI::GetFactoryInstance() { 278 return g_factory.Pointer(); 279} 280 281// static 282ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) { 283 return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context); 284} 285 286// static 287bool 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 300void 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 317void 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 325void 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 351void 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 367void 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 388void 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 420ExtensionActionStorageManager::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 431ExtensionActionStorageManager::~ExtensionActionStorageManager() { 432} 433 434void 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 452void 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 466void 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 479void 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 507ExtensionActionFunction::ExtensionActionFunction() 508 : details_(NULL), 509 tab_id_(ExtensionAction::kDefaultTabId), 510 contents_(NULL), 511 extension_action_(NULL) { 512} 513 514ExtensionActionFunction::~ExtensionActionFunction() { 515} 516 517bool 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 563bool 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 610void 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 629void 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 636void ExtensionActionFunction::NotifyLocationBarChange() { 637 LocationBarController::NotifyChange(contents_); 638} 639 640void 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 647bool 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 655TabHelper& ExtensionActionFunction::tab_helper() const { 656 CHECK(contents_); 657 return *TabHelper::FromWebContents(contents_); 658} 659 660bool ExtensionActionShowFunction::RunExtensionAction() { 661 return SetVisible(true); 662} 663 664bool ExtensionActionHideFunction::RunExtensionAction() { 665 return SetVisible(false); 666} 667 668bool 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 702bool 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 711bool 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 725bool 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 734bool 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 763bool ExtensionActionGetTitleFunction::RunExtensionAction() { 764 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_))); 765 return true; 766} 767 768bool ExtensionActionGetPopupFunction::RunExtensionAction() { 769 SetResult( 770 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec())); 771 return true; 772} 773 774bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() { 775 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_))); 776 return true; 777} 778 779bool 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 794BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction() 795 : response_sent_(false) { 796} 797 798bool 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 825void 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 835void 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 859PageActionsFunction::PageActionsFunction() { 860} 861 862PageActionsFunction::~PageActionsFunction() { 863} 864 865bool 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 918bool EnablePageActionsFunction::RunSync() { 919 return SetPageActionEnabled(true); 920} 921 922bool DisablePageActionsFunction::RunSync() { 923 return SetPageActionEnabled(false); 924} 925