1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/plugin_data_remover.h" 6 7#include "base/command_line.h" 8#include "base/message_loop_proxy.h" 9#include "base/metrics/histogram.h" 10#include "base/synchronization/waitable_event.h" 11#include "base/version.h" 12#include "chrome/common/chrome_switches.h" 13#include "content/browser/browser_thread.h" 14#include "content/browser/plugin_service.h" 15#include "content/common/plugin_messages.h" 16#include "webkit/plugins/npapi/plugin_group.h" 17#include "webkit/plugins/npapi/plugin_list.h" 18 19#if defined(OS_POSIX) 20#include "ipc/ipc_channel_posix.h" 21#endif 22 23namespace { 24 25const char* kFlashMimeType = "application/x-shockwave-flash"; 26// The minimum Flash Player version that implements NPP_ClearSiteData. 27const char* kMinFlashVersion = "10.3"; 28const int64 kRemovalTimeoutMs = 10000; 29const uint64 kClearAllData = 0; 30 31} // namespace 32 33PluginDataRemover::PluginDataRemover() 34 : mime_type_(kFlashMimeType), 35 is_removing_(false), 36 event_(new base::WaitableEvent(true, false)), 37 channel_(NULL) { 38} 39 40PluginDataRemover::~PluginDataRemover() { 41 DCHECK(!is_removing_); 42 if (channel_) 43 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_); 44} 45 46base::WaitableEvent* PluginDataRemover::StartRemoving(base::Time begin_time) { 47 DCHECK(!is_removing_); 48 remove_start_time_ = base::Time::Now(); 49 begin_time_ = begin_time; 50 51 is_removing_ = true; 52 53 // Balanced in OnChannelOpened or OnError. Exactly one them will eventually be 54 // called, so we need to keep this object around until then. 55 AddRef(); 56 PluginService::GetInstance()->OpenChannelToNpapiPlugin( 57 0, 0, GURL(), mime_type_, this); 58 59 BrowserThread::PostDelayedTask( 60 BrowserThread::IO, 61 FROM_HERE, 62 NewRunnableMethod(this, &PluginDataRemover::OnTimeout), 63 kRemovalTimeoutMs); 64 65 return event_.get(); 66} 67 68void PluginDataRemover::Wait() { 69 base::Time start_time(base::Time::Now()); 70 bool result = true; 71 if (is_removing_) 72 result = event_->Wait(); 73 UMA_HISTOGRAM_TIMES("ClearPluginData.wait_at_shutdown", 74 base::Time::Now() - start_time); 75 UMA_HISTOGRAM_TIMES("ClearPluginData.time_at_shutdown", 76 base::Time::Now() - remove_start_time_); 77 DCHECK(result) << "Error waiting for plugin process"; 78} 79 80int PluginDataRemover::ID() { 81 // Generate a unique identifier for this PluginProcessHostClient. 82 return ChildProcessInfo::GenerateChildProcessUniqueId(); 83} 84 85bool PluginDataRemover::OffTheRecord() { 86 return false; 87} 88 89void PluginDataRemover::SetPluginInfo( 90 const webkit::npapi::WebPluginInfo& info) { 91} 92 93void PluginDataRemover::OnChannelOpened(const IPC::ChannelHandle& handle) { 94 ConnectToChannel(handle); 95 // Balancing the AddRef call in StartRemoving. 96 Release(); 97} 98 99void PluginDataRemover::ConnectToChannel(const IPC::ChannelHandle& handle) { 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 101 102 // If we timed out, don't bother connecting. 103 if (!is_removing_) 104 return; 105 106 DCHECK(!channel_); 107 channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this); 108 if (!channel_->Connect()) { 109 NOTREACHED() << "Couldn't connect to plugin"; 110 SignalDone(); 111 return; 112 } 113 114 if (!channel_->Send(new PluginMsg_ClearSiteData(std::string(), 115 kClearAllData, 116 begin_time_))) { 117 NOTREACHED() << "Couldn't send ClearSiteData message"; 118 SignalDone(); 119 return; 120 } 121} 122 123void PluginDataRemover::OnError() { 124 LOG(DFATAL) << "Couldn't open plugin channel"; 125 SignalDone(); 126 // Balancing the AddRef call in StartRemoving. 127 Release(); 128} 129 130void PluginDataRemover::OnClearSiteDataResult(bool success) { 131 LOG_IF(DFATAL, !success) << "ClearSiteData returned error"; 132 UMA_HISTOGRAM_TIMES("ClearPluginData.time", 133 base::Time::Now() - remove_start_time_); 134 SignalDone(); 135} 136 137void PluginDataRemover::OnTimeout() { 138 LOG_IF(DFATAL, is_removing_) << "Timed out"; 139 SignalDone(); 140} 141 142bool PluginDataRemover::OnMessageReceived(const IPC::Message& msg) { 143 IPC_BEGIN_MESSAGE_MAP(PluginDataRemover, msg) 144 IPC_MESSAGE_HANDLER(PluginHostMsg_ClearSiteDataResult, 145 OnClearSiteDataResult) 146 IPC_MESSAGE_UNHANDLED_ERROR() 147 IPC_END_MESSAGE_MAP() 148 149 return true; 150} 151 152void PluginDataRemover::OnChannelError() { 153 if (is_removing_) { 154 NOTREACHED() << "Channel error"; 155 SignalDone(); 156 } 157} 158 159void PluginDataRemover::SignalDone() { 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 161 if (!is_removing_) 162 return; 163 is_removing_ = false; 164 event_->Signal(); 165} 166 167// static 168bool PluginDataRemover::IsSupported() { 169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 170 bool allow_wildcard = false; 171 webkit::npapi::WebPluginInfo plugin; 172 std::string mime_type; 173 if (!webkit::npapi::PluginList::Singleton()->GetPluginInfo( 174 GURL(), kFlashMimeType, allow_wildcard, &plugin, &mime_type)) { 175 return false; 176 } 177 scoped_ptr<Version> version( 178 webkit::npapi::PluginGroup::CreateVersionFromString(plugin.version)); 179 scoped_ptr<Version> min_version(Version::GetVersionFromString( 180 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 181 switches::kMinClearSiteDataFlashVersion))); 182 if (!min_version.get()) 183 min_version.reset(Version::GetVersionFromString(kMinFlashVersion)); 184 return webkit::npapi::IsPluginEnabled(plugin) && 185 version.get() && 186 min_version->CompareTo(*version) == -1; 187} 188