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