// 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/plugin_data_remover.h"
#include "base/command_line.h"
#include "base/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/waitable_event.h"
#include "base/version.h"
#include "chrome/common/chrome_switches.h"
#include "content/browser/browser_thread.h"
#include "content/browser/plugin_service.h"
#include "content/common/plugin_messages.h"
#include "webkit/plugins/npapi/plugin_group.h"
#include "webkit/plugins/npapi/plugin_list.h"
#if defined(OS_POSIX)
#include "ipc/ipc_channel_posix.h"
#endif
namespace {
const char* kFlashMimeType = "application/x-shockwave-flash";
// The minimum Flash Player version that implements NPP_ClearSiteData.
const char* kMinFlashVersion = "10.3";
const int64 kRemovalTimeoutMs = 10000;
const uint64 kClearAllData = 0;
} // namespace
PluginDataRemover::PluginDataRemover()
: mime_type_(kFlashMimeType),
is_removing_(false),
event_(new base::WaitableEvent(true, false)),
channel_(NULL) {
}
PluginDataRemover::~PluginDataRemover() {
DCHECK(!is_removing_);
if (channel_)
BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
}
base::WaitableEvent* PluginDataRemover::StartRemoving(base::Time begin_time) {
DCHECK(!is_removing_);
remove_start_time_ = base::Time::Now();
begin_time_ = begin_time;
is_removing_ = true;
// Balanced in OnChannelOpened or OnError. Exactly one them will eventually be
// called, so we need to keep this object around until then.
AddRef();
PluginService::GetInstance()->OpenChannelToNpapiPlugin(
0, 0, GURL(), mime_type_, this);
BrowserThread::PostDelayedTask(
BrowserThread::IO,
FROM_HERE,
NewRunnableMethod(this, &PluginDataRemover::OnTimeout),
kRemovalTimeoutMs);
return event_.get();
}
void PluginDataRemover::Wait() {
base::Time start_time(base::Time::Now());
bool result = true;
if (is_removing_)
result = event_->Wait();
UMA_HISTOGRAM_TIMES("ClearPluginData.wait_at_shutdown",
base::Time::Now() - start_time);
UMA_HISTOGRAM_TIMES("ClearPluginData.time_at_shutdown",
base::Time::Now() - remove_start_time_);
DCHECK(result) << "Error waiting for plugin process";
}
int PluginDataRemover::ID() {
// Generate a unique identifier for this PluginProcessHostClient.
return ChildProcessInfo::GenerateChildProcessUniqueId();
}
bool PluginDataRemover::OffTheRecord() {
return false;
}
void PluginDataRemover::SetPluginInfo(
const webkit::npapi::WebPluginInfo& info) {
}
void PluginDataRemover::OnChannelOpened(const IPC::ChannelHandle& handle) {
ConnectToChannel(handle);
// Balancing the AddRef call in StartRemoving.
Release();
}
void PluginDataRemover::ConnectToChannel(const IPC::ChannelHandle& handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// If we timed out, don't bother connecting.
if (!is_removing_)
return;
DCHECK(!channel_);
channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
if (!channel_->Connect()) {
NOTREACHED() << "Couldn't connect to plugin";
SignalDone();
return;
}
if (!channel_->Send(new PluginMsg_ClearSiteData(std::string(),
kClearAllData,
begin_time_))) {
NOTREACHED() << "Couldn't send ClearSiteData message";
SignalDone();
return;
}
}
void PluginDataRemover::OnError() {
LOG(DFATAL) << "Couldn't open plugin channel";
SignalDone();
// Balancing the AddRef call in StartRemoving.
Release();
}
void PluginDataRemover::OnClearSiteDataResult(bool success) {
LOG_IF(DFATAL, !success) << "ClearSiteData returned error";
UMA_HISTOGRAM_TIMES("ClearPluginData.time",
base::Time::Now() - remove_start_time_);
SignalDone();
}
void PluginDataRemover::OnTimeout() {
LOG_IF(DFATAL, is_removing_) << "Timed out";
SignalDone();
}
bool PluginDataRemover::OnMessageReceived(const IPC::Message& msg) {
IPC_BEGIN_MESSAGE_MAP(PluginDataRemover, msg)
IPC_MESSAGE_HANDLER(PluginHostMsg_ClearSiteDataResult,
OnClearSiteDataResult)
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()
return true;
}
void PluginDataRemover::OnChannelError() {
if (is_removing_) {
NOTREACHED() << "Channel error";
SignalDone();
}
}
void PluginDataRemover::SignalDone() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!is_removing_)
return;
is_removing_ = false;
event_->Signal();
}
// static
bool PluginDataRemover::IsSupported() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
bool allow_wildcard = false;
webkit::npapi::WebPluginInfo plugin;
std::string mime_type;
if (!webkit::npapi::PluginList::Singleton()->GetPluginInfo(
GURL(), kFlashMimeType, allow_wildcard, &plugin, &mime_type)) {
return false;
}
scoped_ptr<Version> version(
webkit::npapi::PluginGroup::CreateVersionFromString(plugin.version));
scoped_ptr<Version> min_version(Version::GetVersionFromString(
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kMinClearSiteDataFlashVersion)));
if (!min_version.get())
min_version.reset(Version::GetVersionFromString(kMinFlashVersion));
return webkit::npapi::IsPluginEnabled(plugin) &&
version.get() &&
min_version->CompareTo(*version) == -1;
}