// 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( webkit::npapi::PluginGroup::CreateVersionFromString(plugin.version)); scoped_ptr 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; }