1 // Copyright (c) 2011 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/extensions_ui.h" 6 7 #include <algorithm> 8 9 #include "base/base64.h" 10 #include "base/callback.h" 11 #include "base/file_util.h" 12 #include "base/memory/singleton.h" 13 #include "base/string_number_conversions.h" 14 #include "base/string_util.h" 15 #include "base/threading/thread.h" 16 #include "base/utf_string_conversions.h" 17 #include "base/version.h" 18 #include "chrome/browser/debugger/devtools_manager.h" 19 #include "chrome/browser/debugger/devtools_toggle_action.h" 20 #include "chrome/browser/extensions/crx_installer.h" 21 #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h" 22 #include "chrome/browser/extensions/extension_error_reporter.h" 23 #include "chrome/browser/extensions/extension_function_dispatcher.h" 24 #include "chrome/browser/extensions/extension_host.h" 25 #include "chrome/browser/extensions/extension_message_service.h" 26 #include "chrome/browser/extensions/extension_service.h" 27 #include "chrome/browser/extensions/extension_updater.h" 28 #include "chrome/browser/google/google_util.h" 29 #include "chrome/browser/prefs/pref_service.h" 30 #include "chrome/browser/profiles/profile.h" 31 #include "chrome/browser/tab_contents/background_contents.h" 32 #include "chrome/browser/ui/browser_list.h" 33 #include "chrome/browser/ui/webui/extension_icon_source.h" 34 #include "chrome/common/extensions/extension.h" 35 #include "chrome/common/extensions/extension_icon_set.h" 36 #include "chrome/common/extensions/url_pattern.h" 37 #include "chrome/common/extensions/user_script.h" 38 #include "chrome/common/jstemplate_builder.h" 39 #include "chrome/common/pref_names.h" 40 #include "chrome/common/url_constants.h" 41 #include "content/browser/renderer_host/render_process_host.h" 42 #include "content/browser/renderer_host/render_view_host.h" 43 #include "content/browser/renderer_host/render_widget_host.h" 44 #include "content/browser/tab_contents/tab_contents.h" 45 #include "content/browser/tab_contents/tab_contents_view.h" 46 #include "content/common/notification_service.h" 47 #include "content/common/notification_type.h" 48 #include "googleurl/src/gurl.h" 49 #include "grit/browser_resources.h" 50 #include "grit/chromium_strings.h" 51 #include "grit/generated_resources.h" 52 #include "grit/theme_resources.h" 53 #include "net/base/net_util.h" 54 #include "ui/base/l10n/l10n_util.h" 55 #include "ui/base/resource/resource_bundle.h" 56 57 namespace { 58 59 bool ShouldShowExtension(const Extension* extension) { 60 // Don't show themes since this page's UI isn't really useful for themes. 61 if (extension->is_theme()) 62 return false; 63 64 // Don't show component extensions because they are only extensions as an 65 // implementation detail of Chrome. 66 if (extension->location() == Extension::COMPONENT) 67 return false; 68 69 // Always show unpacked extensions and apps. 70 if (extension->location() == Extension::LOAD) 71 return true; 72 73 // Unless they are unpacked, never show hosted apps. 74 if (extension->is_hosted_app()) 75 return false; 76 77 return true; 78 } 79 80 } // namespace 81 82 //////////////////////////////////////////////////////////////////////////////// 83 // 84 // ExtensionsHTMLSource 85 // 86 //////////////////////////////////////////////////////////////////////////////// 87 88 ExtensionsUIHTMLSource::ExtensionsUIHTMLSource() 89 : DataSource(chrome::kChromeUIExtensionsHost, MessageLoop::current()) { 90 } 91 92 void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path, 93 bool is_incognito, int request_id) { 94 DictionaryValue localized_strings; 95 localized_strings.SetString("title", 96 l10n_util::GetStringUTF16(IDS_EXTENSIONS_TITLE)); 97 localized_strings.SetString("devModeLink", 98 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK)); 99 localized_strings.SetString("devModePrefix", 100 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_PREFIX)); 101 localized_strings.SetString("loadUnpackedButton", 102 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON)); 103 localized_strings.SetString("packButton", 104 l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON)); 105 localized_strings.SetString("updateButton", 106 l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON)); 107 localized_strings.SetString("noExtensions", 108 l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED)); 109 localized_strings.SetString("suggestGallery", 110 l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY, 111 ASCIIToUTF16("<a href='") + 112 ASCIIToUTF16(google_util::AppendGoogleLocaleParam( 113 GURL(Extension::ChromeStoreLaunchURL())).spec()) + 114 ASCIIToUTF16("'>"), 115 ASCIIToUTF16("</a>"))); 116 localized_strings.SetString("getMoreExtensions", 117 ASCIIToUTF16("<a href='") + 118 ASCIIToUTF16(google_util::AppendGoogleLocaleParam( 119 GURL(Extension::ChromeStoreLaunchURL())).spec()) + 120 ASCIIToUTF16("'>") + 121 l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS) + 122 ASCIIToUTF16("</a>")); 123 localized_strings.SetString("extensionCrashed", 124 l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION)); 125 localized_strings.SetString("extensionDisabled", 126 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLED_EXTENSION)); 127 localized_strings.SetString("inDevelopment", 128 l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT)); 129 localized_strings.SetString("viewIncognito", 130 l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO)); 131 localized_strings.SetString("extensionId", 132 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); 133 localized_strings.SetString("extensionPath", 134 l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH)); 135 localized_strings.SetString("extensionVersion", 136 l10n_util::GetStringUTF16(IDS_EXTENSIONS_VERSION)); 137 localized_strings.SetString("inspectViews", 138 l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS)); 139 localized_strings.SetString("inspectPopupsInstructions", 140 l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_POPUPS_INSTRUCTIONS)); 141 localized_strings.SetString("disable", 142 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE)); 143 localized_strings.SetString("enable", 144 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE)); 145 localized_strings.SetString("enableIncognito", 146 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO)); 147 localized_strings.SetString("allowFileAccess", 148 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS)); 149 localized_strings.SetString("incognitoWarning", 150 l10n_util::GetStringFUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING, 151 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 152 localized_strings.SetString("reload", 153 l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD)); 154 localized_strings.SetString("uninstall", 155 l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL)); 156 localized_strings.SetString("options", 157 l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS)); 158 localized_strings.SetString("policyControlled", 159 l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED)); 160 localized_strings.SetString("packDialogTitle", 161 l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE)); 162 localized_strings.SetString("packDialogHeading", 163 l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING)); 164 localized_strings.SetString("rootDirectoryLabel", 165 l10n_util::GetStringUTF16( 166 IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL)); 167 localized_strings.SetString("packDialogBrowse", 168 l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE)); 169 localized_strings.SetString("privateKeyLabel", 170 l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL)); 171 localized_strings.SetString("okButton", 172 l10n_util::GetStringUTF16(IDS_OK)); 173 localized_strings.SetString("cancelButton", 174 l10n_util::GetStringUTF16(IDS_CANCEL)); 175 localized_strings.SetString("showButton", 176 l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON)); 177 178 SetFontAndTextDirection(&localized_strings); 179 180 static const base::StringPiece extensions_html( 181 ResourceBundle::GetSharedInstance().GetRawDataResource( 182 IDR_EXTENSIONS_UI_HTML)); 183 std::string full_html(extensions_html.data(), extensions_html.size()); 184 jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); 185 jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); 186 jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); 187 jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); 188 189 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); 190 html_bytes->data.resize(full_html.size()); 191 std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); 192 193 SendResponse(request_id, html_bytes); 194 } 195 196 std::string ExtensionsUIHTMLSource::GetMimeType(const std::string&) const { 197 return "text/html"; 198 } 199 200 /////////////////////////////////////////////////////////////////////////////// 201 // 202 // ExtensionsDOMHandler 203 // 204 /////////////////////////////////////////////////////////////////////////////// 205 206 ExtensionsDOMHandler::ExtensionsDOMHandler(ExtensionService* extension_service) 207 : extensions_service_(extension_service), 208 ignore_notifications_(false), 209 deleting_rvh_(NULL) { 210 RegisterForNotifications(); 211 } 212 213 void ExtensionsDOMHandler::RegisterMessages() { 214 web_ui_->RegisterMessageCallback("requestExtensionsData", 215 NewCallback(this, &ExtensionsDOMHandler::HandleRequestExtensionsData)); 216 web_ui_->RegisterMessageCallback("toggleDeveloperMode", 217 NewCallback(this, &ExtensionsDOMHandler::HandleToggleDeveloperMode)); 218 web_ui_->RegisterMessageCallback("inspect", 219 NewCallback(this, &ExtensionsDOMHandler::HandleInspectMessage)); 220 web_ui_->RegisterMessageCallback("reload", 221 NewCallback(this, &ExtensionsDOMHandler::HandleReloadMessage)); 222 web_ui_->RegisterMessageCallback("enable", 223 NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage)); 224 web_ui_->RegisterMessageCallback("enableIncognito", 225 NewCallback(this, &ExtensionsDOMHandler::HandleEnableIncognitoMessage)); 226 web_ui_->RegisterMessageCallback("allowFileAccess", 227 NewCallback(this, &ExtensionsDOMHandler::HandleAllowFileAccessMessage)); 228 web_ui_->RegisterMessageCallback("uninstall", 229 NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage)); 230 web_ui_->RegisterMessageCallback("options", 231 NewCallback(this, &ExtensionsDOMHandler::HandleOptionsMessage)); 232 web_ui_->RegisterMessageCallback("showButton", 233 NewCallback(this, &ExtensionsDOMHandler::HandleShowButtonMessage)); 234 web_ui_->RegisterMessageCallback("load", 235 NewCallback(this, &ExtensionsDOMHandler::HandleLoadMessage)); 236 web_ui_->RegisterMessageCallback("pack", 237 NewCallback(this, &ExtensionsDOMHandler::HandlePackMessage)); 238 web_ui_->RegisterMessageCallback("autoupdate", 239 NewCallback(this, &ExtensionsDOMHandler::HandleAutoUpdateMessage)); 240 web_ui_->RegisterMessageCallback("selectFilePath", 241 NewCallback(this, &ExtensionsDOMHandler::HandleSelectFilePathMessage)); 242 } 243 244 void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { 245 DictionaryValue results; 246 247 // Add the extensions to the results structure. 248 ListValue* extensions_list = new ListValue(); 249 250 const ExtensionList* extensions = extensions_service_->extensions(); 251 for (ExtensionList::const_iterator extension = extensions->begin(); 252 extension != extensions->end(); ++extension) { 253 if (ShouldShowExtension(*extension)) { 254 extensions_list->Append(CreateExtensionDetailValue( 255 extensions_service_.get(), 256 *extension, 257 GetActivePagesForExtension(*extension), 258 true, false)); // enabled, terminated 259 } 260 } 261 extensions = extensions_service_->disabled_extensions(); 262 for (ExtensionList::const_iterator extension = extensions->begin(); 263 extension != extensions->end(); ++extension) { 264 if (ShouldShowExtension(*extension)) { 265 extensions_list->Append(CreateExtensionDetailValue( 266 extensions_service_.get(), 267 *extension, 268 GetActivePagesForExtension(*extension), 269 false, false)); // enabled, terminated 270 } 271 } 272 extensions = extensions_service_->terminated_extensions(); 273 std::vector<ExtensionPage> empty_pages; 274 for (ExtensionList::const_iterator extension = extensions->begin(); 275 extension != extensions->end(); ++extension) { 276 if (ShouldShowExtension(*extension)) { 277 extensions_list->Append(CreateExtensionDetailValue( 278 extensions_service_.get(), 279 *extension, 280 empty_pages, // Terminated process has no active pages. 281 false, true)); // enabled, terminated 282 } 283 } 284 results.Set("extensions", extensions_list); 285 286 bool developer_mode = web_ui_->GetProfile()->GetPrefs() 287 ->GetBoolean(prefs::kExtensionsUIDeveloperMode); 288 results.SetBoolean("developerMode", developer_mode); 289 290 web_ui_->CallJavascriptFunction("returnExtensionsData", results); 291 } 292 293 void ExtensionsDOMHandler::RegisterForNotifications() { 294 // Register for notifications that we need to reload the page. 295 registrar_.Add(this, NotificationType::EXTENSION_LOADED, 296 NotificationService::AllSources()); 297 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_CREATED, 298 NotificationService::AllSources()); 299 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, 300 NotificationService::AllSources()); 301 registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED, 302 NotificationService::AllSources()); 303 registrar_.Add(this, NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED, 304 NotificationService::AllSources()); 305 registrar_.Add(this, 306 NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED, 307 NotificationService::AllSources()); 308 registrar_.Add(this, 309 NotificationType::NAV_ENTRY_COMMITTED, 310 NotificationService::AllSources()); 311 registrar_.Add(this, 312 NotificationType::RENDER_VIEW_HOST_DELETED, 313 NotificationService::AllSources()); 314 registrar_.Add(this, 315 NotificationType::BACKGROUND_CONTENTS_NAVIGATED, 316 NotificationService::AllSources()); 317 registrar_.Add(this, 318 NotificationType::BACKGROUND_CONTENTS_DELETED, 319 NotificationService::AllSources()); 320 registrar_.Add(this, 321 NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, 322 NotificationService::AllSources()); 323 } 324 325 ExtensionUninstallDialog* ExtensionsDOMHandler::GetExtensionUninstallDialog() { 326 if (!extension_uninstall_dialog_.get()) { 327 extension_uninstall_dialog_.reset( 328 new ExtensionUninstallDialog(web_ui_->GetProfile())); 329 } 330 return extension_uninstall_dialog_.get(); 331 } 332 333 void ExtensionsDOMHandler::HandleToggleDeveloperMode(const ListValue* args) { 334 bool developer_mode = web_ui_->GetProfile()->GetPrefs() 335 ->GetBoolean(prefs::kExtensionsUIDeveloperMode); 336 web_ui_->GetProfile()->GetPrefs()->SetBoolean( 337 prefs::kExtensionsUIDeveloperMode, !developer_mode); 338 } 339 340 void ExtensionsDOMHandler::HandleInspectMessage(const ListValue* args) { 341 std::string render_process_id_str; 342 std::string render_view_id_str; 343 int render_process_id; 344 int render_view_id; 345 CHECK(args->GetSize() == 2); 346 CHECK(args->GetString(0, &render_process_id_str)); 347 CHECK(args->GetString(1, &render_view_id_str)); 348 CHECK(base::StringToInt(render_process_id_str, &render_process_id)); 349 CHECK(base::StringToInt(render_view_id_str, &render_view_id)); 350 RenderViewHost* host = RenderViewHost::FromID(render_process_id, 351 render_view_id); 352 if (!host) { 353 // This can happen if the host has gone away since the page was displayed. 354 return; 355 } 356 357 DevToolsManager::GetInstance()->OpenDevToolsWindow(host); 358 } 359 360 void ExtensionsDOMHandler::HandleReloadMessage(const ListValue* args) { 361 std::string extension_id = UTF16ToASCII(ExtractStringValue(args)); 362 CHECK(!extension_id.empty()); 363 extensions_service_->ReloadExtension(extension_id); 364 } 365 366 void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) { 367 CHECK(args->GetSize() == 2); 368 std::string extension_id, enable_str; 369 CHECK(args->GetString(0, &extension_id)); 370 CHECK(args->GetString(1, &enable_str)); 371 const Extension* extension = 372 extensions_service_->GetExtensionById(extension_id, true); 373 DCHECK(extension); 374 if (!Extension::UserMayDisable(extension->location())) { 375 LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was" 376 << "made. Extension id: " << extension->id(); 377 return; 378 } 379 380 if (enable_str == "true") { 381 ExtensionPrefs* prefs = extensions_service_->extension_prefs(); 382 if (prefs->DidExtensionEscalatePermissions(extension_id)) { 383 ShowExtensionDisabledDialog(extensions_service_, 384 web_ui_->GetProfile(), extension); 385 } else { 386 extensions_service_->EnableExtension(extension_id); 387 } 388 } else { 389 extensions_service_->DisableExtension(extension_id); 390 } 391 } 392 393 void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) { 394 CHECK(args->GetSize() == 2); 395 std::string extension_id, enable_str; 396 CHECK(args->GetString(0, &extension_id)); 397 CHECK(args->GetString(1, &enable_str)); 398 const Extension* extension = 399 extensions_service_->GetExtensionById(extension_id, true); 400 DCHECK(extension); 401 402 // Flipping the incognito bit will generate unload/load notifications for the 403 // extension, but we don't want to reload the page, because a) we've already 404 // updated the UI to reflect the change, and b) we want the yellow warning 405 // text to stay until the user has left the page. 406 // 407 // TODO(aa): This creates crapiness in some cases. For example, in a main 408 // window, when toggling this, the browser action will flicker because it gets 409 // unloaded, then reloaded. It would be better to have a dedicated 410 // notification for this case. 411 // 412 // Bug: http://crbug.com/41384 413 ignore_notifications_ = true; 414 extensions_service_->SetIsIncognitoEnabled(extension_id, 415 enable_str == "true"); 416 ignore_notifications_ = false; 417 } 418 419 void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) { 420 CHECK(args->GetSize() == 2); 421 std::string extension_id, allow_str; 422 CHECK(args->GetString(0, &extension_id)); 423 CHECK(args->GetString(1, &allow_str)); 424 const Extension* extension = 425 extensions_service_->GetExtensionById(extension_id, true); 426 DCHECK(extension); 427 if (!Extension::UserMayDisable(extension->location())) { 428 LOG(ERROR) << "Attempt to change allow file access of an extension that is " 429 << "non-usermanagable was made. Extension id : " 430 << extension->id(); 431 return; 432 } 433 434 extensions_service_->SetAllowFileAccess(extension, allow_str == "true"); 435 } 436 437 void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { 438 std::string extension_id = UTF16ToASCII(ExtractStringValue(args)); 439 CHECK(!extension_id.empty()); 440 const Extension* extension = 441 extensions_service_->GetExtensionById(extension_id, true); 442 if (!extension) 443 extension = extensions_service_->GetTerminatedExtension(extension_id); 444 if (!extension) 445 return; 446 447 if (!Extension::UserMayDisable(extension->location())) { 448 LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable " 449 << "was made. Extension id : " << extension->id(); 450 return; 451 } 452 453 if (!extension_id_prompting_.empty()) 454 return; // Only one prompt at a time. 455 456 extension_id_prompting_ = extension_id; 457 458 GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); 459 } 460 461 void ExtensionsDOMHandler::ExtensionDialogAccepted() { 462 DCHECK(!extension_id_prompting_.empty()); 463 464 bool was_terminated = false; 465 466 // The extension can be uninstalled in another window while the UI was 467 // showing. Do nothing in that case. 468 const Extension* extension = 469 extensions_service_->GetExtensionById(extension_id_prompting_, true); 470 if (!extension) { 471 extension = extensions_service_->GetTerminatedExtension( 472 extension_id_prompting_); 473 was_terminated = true; 474 } 475 if (!extension) 476 return; 477 478 extensions_service_->UninstallExtension(extension_id_prompting_, 479 false /* external_uninstall */, NULL); 480 extension_id_prompting_ = ""; 481 482 // There will be no EXTENSION_UNLOADED notification for terminated 483 // extensions as they were already unloaded. 484 if (was_terminated) 485 HandleRequestExtensionsData(NULL); 486 } 487 488 void ExtensionsDOMHandler::ExtensionDialogCanceled() { 489 extension_id_prompting_ = ""; 490 } 491 492 void ExtensionsDOMHandler::HandleOptionsMessage(const ListValue* args) { 493 const Extension* extension = GetExtension(args); 494 if (!extension || extension->options_url().is_empty()) 495 return; 496 web_ui_->GetProfile()->GetExtensionProcessManager()->OpenOptionsPage( 497 extension, NULL); 498 } 499 500 void ExtensionsDOMHandler::HandleShowButtonMessage(const ListValue* args) { 501 const Extension* extension = GetExtension(args); 502 extensions_service_->SetBrowserActionVisibility(extension, true); 503 } 504 505 void ExtensionsDOMHandler::HandleLoadMessage(const ListValue* args) { 506 FilePath::StringType string_path; 507 CHECK(args->GetSize() == 1) << args->GetSize(); 508 CHECK(args->GetString(0, &string_path)); 509 extensions_service_->LoadExtension(FilePath(string_path)); 510 } 511 512 void ExtensionsDOMHandler::ShowAlert(const std::string& message) { 513 ListValue arguments; 514 arguments.Append(Value::CreateStringValue(message)); 515 web_ui_->CallJavascriptFunction("alert", arguments); 516 } 517 518 void ExtensionsDOMHandler::HandlePackMessage(const ListValue* args) { 519 std::string extension_path; 520 std::string private_key_path; 521 CHECK(args->GetSize() == 2); 522 CHECK(args->GetString(0, &extension_path)); 523 CHECK(args->GetString(1, &private_key_path)); 524 525 FilePath root_directory = 526 FilePath::FromWStringHack(UTF8ToWide(extension_path)); 527 FilePath key_file = FilePath::FromWStringHack(UTF8ToWide(private_key_path)); 528 529 if (root_directory.empty()) { 530 if (extension_path.empty()) { 531 ShowAlert(l10n_util::GetStringUTF8( 532 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED)); 533 } else { 534 ShowAlert(l10n_util::GetStringUTF8( 535 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID)); 536 } 537 538 return; 539 } 540 541 if (!private_key_path.empty() && key_file.empty()) { 542 ShowAlert(l10n_util::GetStringUTF8( 543 IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID)); 544 return; 545 } 546 547 pack_job_ = new PackExtensionJob(this, root_directory, key_file); 548 pack_job_->Start(); 549 } 550 551 void ExtensionsDOMHandler::OnPackSuccess(const FilePath& crx_file, 552 const FilePath& pem_file) { 553 ShowAlert(UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(crx_file, 554 pem_file))); 555 556 ListValue results; 557 web_ui_->CallJavascriptFunction("hidePackDialog", results); 558 } 559 560 void ExtensionsDOMHandler::OnPackFailure(const std::string& error) { 561 ShowAlert(error); 562 } 563 564 void ExtensionsDOMHandler::HandleAutoUpdateMessage(const ListValue* args) { 565 ExtensionUpdater* updater = extensions_service_->updater(); 566 if (updater) 567 updater->CheckNow(); 568 } 569 570 void ExtensionsDOMHandler::HandleSelectFilePathMessage(const ListValue* args) { 571 std::string select_type; 572 std::string operation; 573 CHECK(args->GetSize() == 2); 574 CHECK(args->GetString(0, &select_type)); 575 CHECK(args->GetString(1, &operation)); 576 577 SelectFileDialog::Type type = SelectFileDialog::SELECT_FOLDER; 578 static SelectFileDialog::FileTypeInfo info; 579 int file_type_index = 0; 580 if (select_type == "file") 581 type = SelectFileDialog::SELECT_OPEN_FILE; 582 583 string16 select_title; 584 if (operation == "load") { 585 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY); 586 } else if (operation == "packRoot") { 587 select_title = l10n_util::GetStringUTF16( 588 IDS_EXTENSION_PACK_DIALOG_SELECT_ROOT); 589 } else if (operation == "pem") { 590 select_title = l10n_util::GetStringUTF16( 591 IDS_EXTENSION_PACK_DIALOG_SELECT_KEY); 592 info.extensions.push_back(std::vector<FilePath::StringType>()); 593 info.extensions.front().push_back(FILE_PATH_LITERAL("pem")); 594 info.extension_description_overrides.push_back( 595 l10n_util::GetStringUTF16( 596 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION)); 597 info.include_all_files = true; 598 file_type_index = 1; 599 } else { 600 NOTREACHED(); 601 return; 602 } 603 604 load_extension_dialog_ = SelectFileDialog::Create(this); 605 load_extension_dialog_->SelectFile(type, select_title, FilePath(), &info, 606 file_type_index, FILE_PATH_LITERAL(""), web_ui_->tab_contents(), 607 web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); 608 } 609 610 611 void ExtensionsDOMHandler::FileSelected(const FilePath& path, int index, 612 void* params) { 613 // Add the extensions to the results structure. 614 ListValue results; 615 results.Append(Value::CreateStringValue(path.value())); 616 web_ui_->CallJavascriptFunction("window.handleFilePathSelected", results); 617 } 618 619 void ExtensionsDOMHandler::MultiFilesSelected( 620 const std::vector<FilePath>& files, void* params) { 621 NOTREACHED(); 622 } 623 624 void ExtensionsDOMHandler::Observe(NotificationType type, 625 const NotificationSource& source, 626 const NotificationDetails& details) { 627 switch (type.value) { 628 // We listen for notifications that will result in the page being 629 // repopulated with data twice for the same event in certain cases. 630 // For instance, EXTENSION_LOADED & EXTENSION_PROCESS_CREATED because 631 // we don't know about the views for an extension at EXTENSION_LOADED, but 632 // if we only listen to EXTENSION_PROCESS_CREATED, we'll miss extensions 633 // that don't have a process at startup. Similarly, NAV_ENTRY_COMMITTED & 634 // EXTENSION_FUNCTION_DISPATCHER_CREATED because we want to handle both 635 // the case of live app pages (which don't have an EFD) and 636 // chrome-extension:// urls which are served in a TabContents. 637 // 638 // Doing it this way gets everything but causes the page to be rendered 639 // more than we need. It doesn't seem to result in any noticeable flicker. 640 case NotificationType::RENDER_VIEW_HOST_DELETED: 641 deleting_rvh_ = Source<RenderViewHost>(source).ptr(); 642 MaybeUpdateAfterNotification(); 643 break; 644 case NotificationType::BACKGROUND_CONTENTS_DELETED: 645 deleting_rvh_ = Details<BackgroundContents>(details)->render_view_host(); 646 MaybeUpdateAfterNotification(); 647 break; 648 case NotificationType::EXTENSION_LOADED: 649 case NotificationType::EXTENSION_PROCESS_CREATED: 650 case NotificationType::EXTENSION_UNLOADED: 651 case NotificationType::EXTENSION_UPDATE_DISABLED: 652 case NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED: 653 case NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED: 654 case NotificationType::NAV_ENTRY_COMMITTED: 655 case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: 656 case NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED: 657 MaybeUpdateAfterNotification(); 658 break; 659 default: 660 NOTREACHED(); 661 } 662 } 663 664 const Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { 665 std::string extension_id = UTF16ToASCII(ExtractStringValue(args)); 666 CHECK(!extension_id.empty()); 667 return extensions_service_->GetExtensionById(extension_id, true); 668 } 669 670 void ExtensionsDOMHandler::MaybeUpdateAfterNotification() { 671 if (!ignore_notifications_ && 672 web_ui_->tab_contents() && 673 web_ui_->tab_contents()->render_view_host()) { 674 HandleRequestExtensionsData(NULL); 675 } 676 deleting_rvh_ = NULL; 677 } 678 679 // Static 680 DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( 681 ExtensionService* service, const Extension* extension, 682 const std::vector<ExtensionPage>& pages, bool enabled, bool terminated) { 683 DictionaryValue* extension_data = new DictionaryValue(); 684 GURL icon = 685 ExtensionIconSource::GetIconURL(extension, 686 Extension::EXTENSION_ICON_MEDIUM, 687 ExtensionIconSet::MATCH_BIGGER, 688 !enabled); 689 extension_data->SetString("id", extension->id()); 690 extension_data->SetString("name", extension->name()); 691 extension_data->SetString("description", extension->description()); 692 if (extension->location() == Extension::LOAD) 693 extension_data->SetString("path", extension->path().value()); 694 extension_data->SetString("version", extension->version()->GetString()); 695 extension_data->SetString("icon", icon.spec()); 696 extension_data->SetBoolean("isUnpacked", 697 extension->location() == Extension::LOAD); 698 extension_data->SetBoolean("mayDisable", 699 Extension::UserMayDisable(extension->location())); 700 extension_data->SetBoolean("enabled", enabled); 701 extension_data->SetBoolean("terminated", terminated); 702 extension_data->SetBoolean("enabledIncognito", 703 service ? service->IsIncognitoEnabled(extension->id()) : false); 704 extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access()); 705 extension_data->SetBoolean("allowFileAccess", 706 service ? service->AllowFileAccess(extension) : false); 707 extension_data->SetBoolean("allow_reload", 708 extension->location() == Extension::LOAD); 709 extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app()); 710 711 // Determine the sort order: Extensions loaded through --load-extensions show 712 // up at the top. Disabled extensions show up at the bottom. 713 if (extension->location() == Extension::LOAD) 714 extension_data->SetInteger("order", 1); 715 else 716 extension_data->SetInteger("order", 2); 717 718 if (!extension->options_url().is_empty()) 719 extension_data->SetString("options_url", extension->options_url().spec()); 720 721 if (service && !service->GetBrowserActionVisibility(extension)) 722 extension_data->SetBoolean("enable_show_button", true); 723 724 // Add views 725 ListValue* views = new ListValue; 726 for (std::vector<ExtensionPage>::const_iterator iter = pages.begin(); 727 iter != pages.end(); ++iter) { 728 DictionaryValue* view_value = new DictionaryValue; 729 if (iter->url.scheme() == chrome::kExtensionScheme) { 730 // No leading slash. 731 view_value->SetString("path", iter->url.path().substr(1)); 732 } else { 733 // For live pages, use the full URL. 734 view_value->SetString("path", iter->url.spec()); 735 } 736 view_value->SetInteger("renderViewId", iter->render_view_id); 737 view_value->SetInteger("renderProcessId", iter->render_process_id); 738 view_value->SetBoolean("incognito", iter->incognito); 739 views->Append(view_value); 740 } 741 extension_data->Set("views", views); 742 extension_data->SetBoolean("hasPopupAction", 743 extension->browser_action() || extension->page_action()); 744 extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec()); 745 746 return extension_data; 747 } 748 749 std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( 750 const Extension* extension) { 751 std::vector<ExtensionPage> result; 752 753 // Get the extension process's active views. 754 ExtensionProcessManager* process_manager = 755 extensions_service_->profile()->GetExtensionProcessManager(); 756 GetActivePagesForExtensionProcess( 757 process_manager->GetExtensionProcess(extension->url()), 758 extension, &result); 759 760 // Repeat for the incognito process, if applicable. 761 if (extensions_service_->profile()->HasOffTheRecordProfile() && 762 extension->incognito_split_mode()) { 763 ExtensionProcessManager* process_manager = 764 extensions_service_->profile()->GetOffTheRecordProfile()-> 765 GetExtensionProcessManager(); 766 GetActivePagesForExtensionProcess( 767 process_manager->GetExtensionProcess(extension->url()), 768 extension, &result); 769 } 770 771 return result; 772 } 773 774 void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( 775 RenderProcessHost* process, 776 const Extension* extension, 777 std::vector<ExtensionPage> *result) { 778 if (!process) 779 return; 780 781 RenderProcessHost::listeners_iterator iter = process->ListenersIterator(); 782 for (; !iter.IsAtEnd(); iter.Advance()) { 783 const RenderWidgetHost* widget = 784 static_cast<const RenderWidgetHost*>(iter.GetCurrentValue()); 785 DCHECK(widget); 786 if (!widget || !widget->IsRenderView()) 787 continue; 788 const RenderViewHost* host = static_cast<const RenderViewHost*>(widget); 789 if (host == deleting_rvh_ || 790 ViewType::EXTENSION_POPUP == host->delegate()->GetRenderViewType()) 791 continue; 792 793 GURL url = host->delegate()->GetURL(); 794 if (url.SchemeIs(chrome::kExtensionScheme)) { 795 if (url.host() != extension->id()) 796 continue; 797 } else if (!extension->web_extent().ContainsURL(url)) { 798 continue; 799 } 800 801 result->push_back(ExtensionPage(url, process->id(), host->routing_id(), 802 process->profile()->IsOffTheRecord())); 803 } 804 } 805 806 ExtensionsDOMHandler::~ExtensionsDOMHandler() { 807 // There may be pending file dialogs, we need to tell them that we've gone 808 // away so they don't try and call back to us. 809 if (load_extension_dialog_.get()) 810 load_extension_dialog_->ListenerDestroyed(); 811 812 if (pack_job_.get()) 813 pack_job_->ClearClient(); 814 815 registrar_.RemoveAll(); 816 } 817 818 // ExtensionsDOMHandler, public: ----------------------------------------------- 819 820 ExtensionsUI::ExtensionsUI(TabContents* contents) : WebUI(contents) { 821 ExtensionService *exstension_service = 822 GetProfile()->GetOriginalProfile()->GetExtensionService(); 823 824 ExtensionsDOMHandler* handler = new ExtensionsDOMHandler(exstension_service); 825 AddMessageHandler(handler); 826 handler->Attach(this); 827 828 ExtensionsUIHTMLSource* html_source = new ExtensionsUIHTMLSource(); 829 830 // Set up the chrome://extensions/ source. 831 contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); 832 } 833 834 // static 835 RefCountedMemory* ExtensionsUI::GetFaviconResourceBytes() { 836 return ResourceBundle::GetSharedInstance(). 837 LoadDataResourceBytes(IDR_PLUGIN); 838 } 839 840 // static 841 void ExtensionsUI::RegisterUserPrefs(PrefService* prefs) { 842 prefs->RegisterBooleanPref(prefs::kExtensionsUIDeveloperMode, false); 843 } 844