普通文本  |  384行  |  14.24 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/plugins_ui.h"

#include <algorithm>
#include <string>
#include <vector>

#include "base/memory/singleton.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/plugin_updater.h"
#include "chrome/browser/prefs/pref_member.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_service.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "webkit/plugins/npapi/plugin_list.h"

namespace {

///////////////////////////////////////////////////////////////////////////////
//
// PluginsHTMLSource
//
///////////////////////////////////////////////////////////////////////////////

class PluginsUIHTMLSource : public ChromeURLDataManager::DataSource {
 public:
  PluginsUIHTMLSource()
      : DataSource(chrome::kChromeUIPluginsHost, MessageLoop::current()) {}

  // Called when the network layer has requested a resource underneath
  // the path we registered.
  virtual void StartDataRequest(const std::string& path,
                                bool is_incognito,
                                int request_id);
  virtual std::string GetMimeType(const std::string&) const {
    return "text/html";
  }

 private:
  ~PluginsUIHTMLSource() {}

  DISALLOW_COPY_AND_ASSIGN(PluginsUIHTMLSource);
};

void PluginsUIHTMLSource::StartDataRequest(const std::string& path,
                                           bool is_incognito,
                                           int request_id) {
  // Strings used in the JsTemplate file.
  DictionaryValue localized_strings;
  localized_strings.SetString("pluginsTitle",
      l10n_util::GetStringUTF16(IDS_PLUGINS_TITLE));
  localized_strings.SetString("pluginsDetailsModeLink",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DETAILS_MODE_LINK));
  localized_strings.SetString("pluginsNoneInstalled",
      l10n_util::GetStringUTF16(IDS_PLUGINS_NONE_INSTALLED));
  localized_strings.SetString("pluginDisabled",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_PLUGIN));
  localized_strings.SetString("pluginDisabledByPolicy",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN));
  localized_strings.SetString("pluginCannotBeEnabledDueToPolicy",
      l10n_util::GetStringUTF16(IDS_PLUGINS_CANNOT_ENABLE_DUE_TO_POLICY));
  localized_strings.SetString("pluginEnabledByPolicy",
      l10n_util::GetStringUTF16(IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN));
  localized_strings.SetString("pluginCannotBeDisabledDueToPolicy",
      l10n_util::GetStringUTF16(IDS_PLUGINS_CANNOT_DISABLE_DUE_TO_POLICY));
  localized_strings.SetString("pluginDownload",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DOWNLOAD));
  localized_strings.SetString("pluginName",
      l10n_util::GetStringUTF16(IDS_PLUGINS_NAME));
  localized_strings.SetString("pluginVersion",
      l10n_util::GetStringUTF16(IDS_PLUGINS_VERSION));
  localized_strings.SetString("pluginDescription",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DESCRIPTION));
  localized_strings.SetString("pluginPath",
      l10n_util::GetStringUTF16(IDS_PLUGINS_PATH));
  localized_strings.SetString("pluginMimeTypes",
      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES));
  localized_strings.SetString("pluginMimeTypesMimeType",
      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_MIME_TYPE));
  localized_strings.SetString("pluginMimeTypesDescription",
      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_DESCRIPTION));
  localized_strings.SetString("pluginMimeTypesFileExtensions",
      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS));
  localized_strings.SetString("disable",
      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLE));
  localized_strings.SetString("enable",
      l10n_util::GetStringUTF16(IDS_PLUGINS_ENABLE));
  localized_strings.SetString("noPlugins",
      l10n_util::GetStringUTF16(IDS_PLUGINS_NO_PLUGINS));

  ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings);

  static const base::StringPiece plugins_html(
      ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_PLUGINS_HTML));
  std::string full_html(plugins_html.data(), plugins_html.size());
  jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
  jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
  jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
  jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);

  scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
  html_bytes->data.resize(full_html.size());
  std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());

  SendResponse(request_id, html_bytes);
}

////////////////////////////////////////////////////////////////////////////////
//
// PluginsDOMHandler
//
////////////////////////////////////////////////////////////////////////////////

// The handler for Javascript messages for the chrome://plugins/ page.
// TODO(viettrungluu): Make plugin list updates notify, and then observe
// changes; maybe replumb plugin list through plugin service?
// <http://crbug.com/39101>
class PluginsDOMHandler : public WebUIMessageHandler,
                          public NotificationObserver {
 public:
  explicit PluginsDOMHandler();
  virtual ~PluginsDOMHandler() {}

  // WebUIMessageHandler implementation.
  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
  virtual void RegisterMessages();

  // Callback for the "requestPluginsData" message.
  void HandleRequestPluginsData(const ListValue* args);

  // Callback for the "enablePlugin" message.
  void HandleEnablePluginMessage(const ListValue* args);

  // Callback for the "showTermsOfService" message. This really just opens a new
  // window with about:terms. Flash can't link directly to about:terms due to
  // the security model.
  void HandleShowTermsOfServiceMessage(const ListValue* args);

  // Callback for the "saveShowDetailsToPrefs" message.
  void HandleSaveShowDetailsToPrefs(const ListValue* args);

  // Calback for the "getShowDetails" message.
  void HandleGetShowDetails(const ListValue* args);

  // NotificationObserver method overrides
  void Observe(NotificationType type,
               const NotificationSource& source,
               const NotificationDetails& details);

 private:
  // This extra wrapper is used to ensure we don't leak the ListValue* pointer
  // if the PluginsDOMHandler object goes away before the task on the UI thread
  // to give it the plugin list runs.
  struct ListWrapper {
    ListValue* list;
  };
  // Loads the plugins on the FILE thread.
  static void LoadPluginsOnFileThread(ListWrapper* wrapper, Task* task);

  // Used in conjunction with ListWrapper to avoid any memory leaks.
  static void EnsureListDeleted(ListWrapper* wrapper);

  // Call this to start getting the plugins on the UI thread.
  void LoadPlugins();

  // Called on the UI thread when the plugin information is ready.
  void PluginsLoaded(ListWrapper* wrapper);

  NotificationRegistrar registrar_;

  ScopedRunnableMethodFactory<PluginsDOMHandler> get_plugins_factory_;

  // This pref guards the value whether about:plugins is in the details mode or
  // not.
  BooleanPrefMember show_details_;

  DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler);
};

PluginsDOMHandler::PluginsDOMHandler()
    : ALLOW_THIS_IN_INITIALIZER_LIST(get_plugins_factory_(this)) {
  registrar_.Add(this,
                 NotificationType::PLUGIN_ENABLE_STATUS_CHANGED,
                 NotificationService::AllSources());
}

WebUIMessageHandler* PluginsDOMHandler::Attach(WebUI* web_ui) {
  PrefService* prefs = web_ui->GetProfile()->GetPrefs();

  show_details_.Init(prefs::kPluginsShowDetails, prefs, this);

  return WebUIMessageHandler::Attach(web_ui);
}

void PluginsDOMHandler::RegisterMessages() {
  web_ui_->RegisterMessageCallback("requestPluginsData",
      NewCallback(this, &PluginsDOMHandler::HandleRequestPluginsData));
  web_ui_->RegisterMessageCallback("enablePlugin",
      NewCallback(this, &PluginsDOMHandler::HandleEnablePluginMessage));
  web_ui_->RegisterMessageCallback("showTermsOfService",
      NewCallback(this, &PluginsDOMHandler::HandleShowTermsOfServiceMessage));
  web_ui_->RegisterMessageCallback("saveShowDetailsToPrefs",
      NewCallback(this, &PluginsDOMHandler::HandleSaveShowDetailsToPrefs));
  web_ui_->RegisterMessageCallback("getShowDetails",
      NewCallback(this, &PluginsDOMHandler::HandleGetShowDetails));
}

void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) {
  LoadPlugins();
}

void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) {
  // Be robust in accepting badness since plug-ins display HTML (hence
  // JavaScript).
  if (args->GetSize() != 3)
    return;

  std::string enable_str;
  std::string is_group_str;
  if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str))
    return;
  bool enable = enable_str == "true";

  PluginUpdater* plugin_updater = PluginUpdater::GetInstance();
  if (is_group_str == "true") {
    string16 group_name;
    if (!args->GetString(0, &group_name))
      return;

    plugin_updater->EnablePluginGroup(enable, group_name);
    if (enable) {
      // See http://crbug.com/50105 for background.
      string16 adobereader = ASCIIToUTF16(
          webkit::npapi::PluginGroup::kAdobeReaderGroupName);
      string16 internalpdf =
          ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName);
      if (group_name == adobereader) {
        plugin_updater->EnablePluginGroup(false, internalpdf);
      } else if (group_name == internalpdf) {
        plugin_updater->EnablePluginGroup(false, adobereader);
      }
    }
  } else {
    FilePath::StringType file_path;
    if (!args->GetString(0, &file_path))
      return;

    plugin_updater->EnablePlugin(enable, file_path);
  }

  // TODO(viettrungluu): We might also want to ensure that the plugins
  // list is always written to prefs even when the user hasn't disabled a
  // plugin. <http://crbug.com/39101>
  plugin_updater->UpdatePreferences(web_ui_->GetProfile(), 0);
}

void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const ListValue* args) {
  // Show it in a new browser window....
  Browser* browser = Browser::Create(web_ui_->GetProfile());
  browser->OpenURL(GURL(chrome::kAboutTermsURL),
                   GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
  browser->window()->Show();
}

void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) {
  std::string details_mode;
  if (!args->GetString(0, &details_mode)) {
    NOTREACHED();
    return;
  }
  show_details_.SetValue(details_mode == "true");
}

void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) {
  FundamentalValue show_details(show_details_.GetValue());
  web_ui_->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
}

void PluginsDOMHandler::Observe(NotificationType type,
                                const NotificationSource& source,
                                const NotificationDetails& details) {
  DCHECK_EQ(NotificationType::PLUGIN_ENABLE_STATUS_CHANGED, type.value);
  LoadPlugins();
}

void PluginsDOMHandler::LoadPluginsOnFileThread(ListWrapper* wrapper,
                                                Task* task) {
  wrapper->list = PluginUpdater::GetInstance()->GetPluginGroupsData();
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableFunction(&PluginsDOMHandler::EnsureListDeleted, wrapper));
}

void PluginsDOMHandler::EnsureListDeleted(ListWrapper* wrapper) {
  delete wrapper->list;
  delete wrapper;
}

void PluginsDOMHandler::LoadPlugins() {
  if (!get_plugins_factory_.empty())
    return;

  ListWrapper* wrapper = new ListWrapper;
  wrapper->list = NULL;
  Task* task = get_plugins_factory_.NewRunnableMethod(
          &PluginsDOMHandler::PluginsLoaded, wrapper);

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableFunction(
          &PluginsDOMHandler::LoadPluginsOnFileThread, wrapper, task));
}

void PluginsDOMHandler::PluginsLoaded(ListWrapper* wrapper) {
  DictionaryValue results;
  results.Set("plugins", wrapper->list);
  wrapper->list = NULL;  // So it doesn't get deleted.
  web_ui_->CallJavascriptFunction("returnPluginsData", results);
}

}  // namespace

///////////////////////////////////////////////////////////////////////////////
//
// PluginsUI
//
///////////////////////////////////////////////////////////////////////////////

PluginsUI::PluginsUI(TabContents* contents) : WebUI(contents) {
  AddMessageHandler((new PluginsDOMHandler())->Attach(this));

  PluginsUIHTMLSource* html_source = new PluginsUIHTMLSource();

  // Set up the chrome://plugins/ source.
  contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
}


// static
RefCountedMemory* PluginsUI::GetFaviconResourceBytes() {
  return ResourceBundle::GetSharedInstance().
      LoadDataResourceBytes(IDR_PLUGIN);
}

// static
void PluginsUI::RegisterUserPrefs(PrefService* prefs) {
  FilePath internal_dir;
  PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir);
  prefs->RegisterFilePathPref(prefs::kPluginsLastInternalDirectory,
                              internal_dir);

  prefs->RegisterListPref(prefs::kPluginsDisabledPlugins);
  prefs->RegisterListPref(prefs::kPluginsDisabledPluginsExceptions);
  prefs->RegisterListPref(prefs::kPluginsEnabledPlugins);
  prefs->RegisterListPref(prefs::kPluginsPluginsList);
  prefs->RegisterBooleanPref(prefs::kPluginsEnabledInternalPDF, false);
  prefs->RegisterBooleanPref(prefs::kPluginsShowDetails, false);
  prefs->RegisterBooleanPref(prefs::kPluginsShowSetReaderDefaultInfobar, true);
}