blob: 82c9555f5964aade2dc70f5ac2e1271a1264f612 [file] [log] [blame]
// Copyright (c) 2012 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/plugins/plugin_observer.h"
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/debug/crash_logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/plugins/flash_download_interception.h"
#include "chrome/browser/plugins/plugin_finder.h"
#include "chrome/browser/plugins/plugin_infobar_delegates.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/common/features.h"
#include "chrome/common/render_messages.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/component_updater/component_updater_service.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/infobars/core/infobar_delegate.h"
#include "components/infobars/core/simple_alert_infobar_delegate.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/webplugininfo.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/vector_icons_public.h"
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
#include "chrome/browser/plugins/plugin_installer.h"
#include "chrome/browser/plugins/plugin_installer_observer.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
#endif // BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
using content::PluginService;
DEFINE_WEB_CONTENTS_USER_DATA_KEY(PluginObserver);
namespace {
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
// ConfirmInstallDialogDelegate ------------------------------------------------
class ConfirmInstallDialogDelegate : public TabModalConfirmDialogDelegate,
public WeakPluginInstallerObserver {
public:
ConfirmInstallDialogDelegate(content::WebContents* web_contents,
PluginInstaller* installer,
std::unique_ptr<PluginMetadata> plugin_metadata);
// TabModalConfirmDialogDelegate methods:
base::string16 GetTitle() override;
base::string16 GetDialogMessage() override;
base::string16 GetAcceptButtonTitle() override;
void OnAccepted() override;
void OnCanceled() override;
// WeakPluginInstallerObserver methods:
void DownloadStarted() override;
void OnlyWeakObserversLeft() override;
private:
content::WebContents* web_contents_;
std::unique_ptr<PluginMetadata> plugin_metadata_;
};
ConfirmInstallDialogDelegate::ConfirmInstallDialogDelegate(
content::WebContents* web_contents,
PluginInstaller* installer,
std::unique_ptr<PluginMetadata> plugin_metadata)
: TabModalConfirmDialogDelegate(web_contents),
WeakPluginInstallerObserver(installer),
web_contents_(web_contents),
plugin_metadata_(std::move(plugin_metadata)) {}
base::string16 ConfirmInstallDialogDelegate::GetTitle() {
return l10n_util::GetStringFUTF16(
IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE, plugin_metadata_->name());
}
base::string16 ConfirmInstallDialogDelegate::GetDialogMessage() {
return l10n_util::GetStringFUTF16(IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_MSG,
plugin_metadata_->name());
}
base::string16 ConfirmInstallDialogDelegate::GetAcceptButtonTitle() {
return l10n_util::GetStringUTF16(
IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON);
}
void ConfirmInstallDialogDelegate::OnAccepted() {
installer()->StartInstalling(plugin_metadata_->plugin_url(), web_contents_);
}
void ConfirmInstallDialogDelegate::OnCanceled() {
}
void ConfirmInstallDialogDelegate::DownloadStarted() {
Cancel();
}
void ConfirmInstallDialogDelegate::OnlyWeakObserversLeft() {
Cancel();
}
#endif // BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
// ReloadPluginInfoBarDelegate -------------------------------------------------
class ReloadPluginInfoBarDelegate : public ConfirmInfoBarDelegate {
public:
static void Create(InfoBarService* infobar_service,
content::NavigationController* controller,
const base::string16& message);
private:
ReloadPluginInfoBarDelegate(content::NavigationController* controller,
const base::string16& message);
~ReloadPluginInfoBarDelegate() override;
// ConfirmInfobarDelegate:
infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
gfx::VectorIconId GetVectorIconId() const override;
base::string16 GetMessageText() const override;
int GetButtons() const override;
base::string16 GetButtonLabel(InfoBarButton button) const override;
bool Accept() override;
content::NavigationController* controller_;
base::string16 message_;
};
// static
void ReloadPluginInfoBarDelegate::Create(
InfoBarService* infobar_service,
content::NavigationController* controller,
const base::string16& message) {
infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar(
std::unique_ptr<ConfirmInfoBarDelegate>(
new ReloadPluginInfoBarDelegate(controller, message))));
}
ReloadPluginInfoBarDelegate::ReloadPluginInfoBarDelegate(
content::NavigationController* controller,
const base::string16& message)
: controller_(controller),
message_(message) {}
ReloadPluginInfoBarDelegate::~ReloadPluginInfoBarDelegate() {}
infobars::InfoBarDelegate::InfoBarIdentifier
ReloadPluginInfoBarDelegate::GetIdentifier() const {
return RELOAD_PLUGIN_INFOBAR_DELEGATE;
}
gfx::VectorIconId ReloadPluginInfoBarDelegate::GetVectorIconId() const {
return gfx::VectorIconId::EXTENSION_CRASHED;
}
base::string16 ReloadPluginInfoBarDelegate::GetMessageText() const {
return message_;
}
int ReloadPluginInfoBarDelegate::GetButtons() const {
return BUTTON_OK;
}
base::string16 ReloadPluginInfoBarDelegate::GetButtonLabel(
InfoBarButton button) const {
DCHECK_EQ(BUTTON_OK, button);
return l10n_util::GetStringUTF16(IDS_RELOAD_PAGE_WITH_PLUGIN);
}
bool ReloadPluginInfoBarDelegate::Accept() {
controller_->Reload(content::ReloadType::NORMAL, true);
return true;
}
} // namespace
// PluginObserver -------------------------------------------------------------
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
public:
PluginPlaceholderHost(PluginObserver* observer,
int routing_id,
base::string16 plugin_name,
PluginInstaller* installer)
: PluginInstallerObserver(installer),
observer_(observer),
routing_id_(routing_id) {
DCHECK(installer);
switch (installer->state()) {
case PluginInstaller::INSTALLER_STATE_IDLE: {
observer->Send(new ChromeViewMsg_FoundMissingPlugin(routing_id_,
plugin_name));
break;
}
case PluginInstaller::INSTALLER_STATE_DOWNLOADING: {
DownloadStarted();
break;
}
}
}
// PluginInstallerObserver methods:
void DownloadStarted() override {
observer_->Send(new ChromeViewMsg_StartedDownloadingPlugin(routing_id_));
}
void DownloadError(const std::string& msg) override {
observer_->Send(new ChromeViewMsg_ErrorDownloadingPlugin(routing_id_, msg));
}
void DownloadCancelled() override {
observer_->Send(new ChromeViewMsg_CancelledDownloadingPlugin(routing_id_));
}
void DownloadFinished() override {
observer_->Send(new ChromeViewMsg_FinishedDownloadingPlugin(routing_id_));
}
private:
// Weak pointer; owns us.
PluginObserver* observer_;
int routing_id_;
};
#endif // BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
class PluginObserver::ComponentObserver
: public update_client::UpdateClient::Observer {
public:
using Events = update_client::UpdateClient::Observer::Events;
ComponentObserver(PluginObserver* observer,
int routing_id,
const std::string& component_id)
: observer_(observer),
routing_id_(routing_id),
component_id_(component_id) {
g_browser_process->component_updater()->AddObserver(this);
}
~ComponentObserver() override {
g_browser_process->component_updater()->RemoveObserver(this);
}
void OnEvent(Events event, const std::string& id) override {
if (id != component_id_)
return;
switch (event) {
case Events::COMPONENT_UPDATED:
observer_->Send(
new ChromeViewMsg_PluginComponentUpdateSuccess(routing_id_));
observer_->RemoveComponentObserver(routing_id_);
break;
case Events::COMPONENT_UPDATE_FOUND:
observer_->Send(
new ChromeViewMsg_PluginComponentUpdateDownloading(routing_id_));
break;
case Events::COMPONENT_NOT_UPDATED:
observer_->Send(
new ChromeViewMsg_PluginComponentUpdateFailure(routing_id_));
observer_->RemoveComponentObserver(routing_id_);
break;
default:
// No message to send.
break;
}
}
private:
PluginObserver* observer_;
int routing_id_;
std::string component_id_;
DISALLOW_COPY_AND_ASSIGN(ComponentObserver);
};
PluginObserver::PluginObserver(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
weak_ptr_factory_(this) {
}
PluginObserver::~PluginObserver() {
}
void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
base::ProcessId plugin_pid) {
DCHECK(!plugin_path.value().empty());
base::string16 plugin_name =
PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
base::string16 infobar_text;
#if defined(OS_WIN)
// Find out whether the plugin process is still alive.
// Note: Although the chances are slim, it is possible that after the plugin
// process died, |plugin_pid| has been reused by a new process. The
// consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
// than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
base::Process plugin_process =
base::Process::OpenWithAccess(plugin_pid,
PROCESS_QUERY_INFORMATION | SYNCHRONIZE);
bool is_running = false;
if (plugin_process.IsValid()) {
is_running =
base::GetTerminationStatus(plugin_process.Handle(), NULL) ==
base::TERMINATION_STATUS_STILL_RUNNING;
plugin_process.Close();
}
if (is_running) {
infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
plugin_name);
UMA_HISTOGRAM_COUNTS("Plugin.ShowDisconnectedInfobar", 1);
} else {
infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
plugin_name);
UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
}
#else
// Calling the POSIX version of base::GetTerminationStatus() may affect other
// code which is interested in the process termination status. (Please see the
// comment of the function.) Therefore, a better way is needed to distinguish
// disconnections from crashes.
infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
plugin_name);
UMA_HISTOGRAM_COUNTS("Plugin.ShowCrashedInfobar", 1);
#endif
ReloadPluginInfoBarDelegate::Create(
InfoBarService::FromWebContents(web_contents()),
&web_contents()->GetController(),
infobar_text);
}
bool PluginObserver::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
IPC_BEGIN_MESSAGE_MAP(PluginObserver, message)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedOutdatedPlugin,
OnBlockedOutdatedPlugin)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_BlockedComponentUpdatedPlugin,
OnBlockedComponentUpdatedPlugin)
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RemovePluginPlaceholderHost,
OnRemovePluginPlaceholderHost)
#endif
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ShowFlashPermissionBubble,
OnShowFlashPermissionBubble)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CouldNotLoadPlugin,
OnCouldNotLoadPlugin)
IPC_MESSAGE_UNHANDLED(return false)
IPC_END_MESSAGE_MAP()
return true;
}
void PluginObserver::OnBlockedOutdatedPlugin(int placeholder_id,
const std::string& identifier) {
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
PluginFinder* finder = PluginFinder::GetInstance();
// Find plugin to update.
PluginInstaller* installer = NULL;
std::unique_ptr<PluginMetadata> plugin;
if (finder->FindPluginWithIdentifier(identifier, &installer, &plugin)) {
plugin_placeholders_[placeholder_id] =
base::MakeUnique<PluginPlaceholderHost>(this, placeholder_id,
plugin->name(), installer);
OutdatedPluginInfoBarDelegate::Create(
InfoBarService::FromWebContents(web_contents()), installer,
std::move(plugin));
} else {
NOTREACHED();
}
#else
// If we don't support third-party plugin installation, we shouldn't have
// outdated plugins.
NOTREACHED();
#endif // BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
}
void PluginObserver::OnBlockedComponentUpdatedPlugin(
int placeholder_id,
const std::string& identifier) {
component_observers_[placeholder_id] =
base::MakeUnique<ComponentObserver>(this, placeholder_id, identifier);
g_browser_process->component_updater()->GetOnDemandUpdater().OnDemandUpdate(
identifier, component_updater::Callback());
}
void PluginObserver::RemoveComponentObserver(int placeholder_id) {
auto it = component_observers_.find(placeholder_id);
DCHECK(it != component_observers_.end());
component_observers_.erase(it);
}
#if BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
void PluginObserver::OnRemovePluginPlaceholderHost(int placeholder_id) {
auto it = plugin_placeholders_.find(placeholder_id);
if (it == plugin_placeholders_.end()) {
NOTREACHED();
return;
}
plugin_placeholders_.erase(it);
}
#endif // BUILDFLAG(ENABLE_PLUGIN_INSTALLATION)
void PluginObserver::OnShowFlashPermissionBubble() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
FlashDownloadInterception::InterceptFlashDownloadNavigation(
web_contents(), web_contents()->GetLastCommittedURL());
}
void PluginObserver::OnCouldNotLoadPlugin(const base::FilePath& plugin_path) {
g_browser_process->GetMetricsServicesManager()->OnPluginLoadingError(
plugin_path);
base::string16 plugin_name =
PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
SimpleAlertInfoBarDelegate::Create(
InfoBarService::FromWebContents(web_contents()),
infobars::InfoBarDelegate::PLUGIN_OBSERVER, 0,
gfx::VectorIconId::EXTENSION_CRASHED,
l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
plugin_name),
true);
}