| // 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; |
| } |