Home | History | Annotate | Download | only in extensions
      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