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