plugin_data_remover_impl.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 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 "content/browser/plugin_data_remover_impl.h" 6 7#include <limits> 8 9#include "base/bind.h" 10#include "base/metrics/histogram.h" 11#include "base/sequenced_task_runner_helpers.h" 12#include "base/synchronization/waitable_event.h" 13#include "base/utf_string_conversions.h" 14#include "base/version.h" 15#include "content/browser/plugin_process_host.h" 16#include "content/browser/plugin_service_impl.h" 17#include "content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h" 18#include "content/common/child_process_host_impl.h" 19#include "content/common/plugin_process_messages.h" 20#include "content/public/browser/browser_context.h" 21#include "content/public/browser/browser_thread.h" 22#include "content/public/common/pepper_plugin_info.h" 23#include "ppapi/proxy/ppapi_messages.h" 24#include "webkit/plugins/npapi/plugin_utils.h" 25#include "webkit/plugins/plugin_constants.h" 26 27namespace content { 28 29namespace { 30 31// The minimum Flash Player version that implements NPP_ClearSiteData. 32const char kMinFlashVersion[] = "10.3"; 33const int64 kRemovalTimeoutMs = 10000; 34const uint64 kClearAllData = 0; 35 36} // namespace 37 38// static 39PluginDataRemover* PluginDataRemover::Create(BrowserContext* browser_context) { 40 return new PluginDataRemoverImpl(browser_context); 41} 42 43// static 44void PluginDataRemover::GetSupportedPlugins( 45 std::vector<webkit::WebPluginInfo>* supported_plugins) { 46 bool allow_wildcard = false; 47 std::vector<webkit::WebPluginInfo> plugins; 48 PluginService::GetInstance()->GetPluginInfoArray( 49 GURL(), kFlashPluginSwfMimeType, allow_wildcard, &plugins, NULL); 50 Version min_version(kMinFlashVersion); 51 for (std::vector<webkit::WebPluginInfo>::iterator it = plugins.begin(); 52 it != plugins.end(); ++it) { 53 Version version; 54 webkit::npapi::CreateVersionFromString(it->version, &version); 55 if (version.IsValid() && min_version.CompareTo(version) == -1) 56 supported_plugins->push_back(*it); 57 } 58} 59 60class PluginDataRemoverImpl::Context 61 : public PluginProcessHost::Client, 62 public PpapiPluginProcessHost::BrokerClient, 63 public IPC::Listener, 64 public base::RefCountedThreadSafe<Context, 65 BrowserThread::DeleteOnIOThread> { 66 public: 67 Context(base::Time begin_time, BrowserContext* browser_context) 68 : event_(new base::WaitableEvent(true, false)), 69 begin_time_(begin_time), 70 is_removing_(false), 71 browser_context_path_(browser_context->GetPath()), 72 resource_context_(browser_context->GetResourceContext()), 73 channel_(NULL) { 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 75 } 76 77 void Init(const std::string& mime_type) { 78 BrowserThread::PostTask( 79 BrowserThread::IO, 80 FROM_HERE, 81 base::Bind(&Context::InitOnIOThread, this, mime_type)); 82 BrowserThread::PostDelayedTask( 83 BrowserThread::IO, 84 FROM_HERE, 85 base::Bind(&Context::OnTimeout, this), 86 base::TimeDelta::FromMilliseconds(kRemovalTimeoutMs)); 87 } 88 89 void InitOnIOThread(const std::string& mime_type) { 90 PluginServiceImpl* plugin_service = PluginServiceImpl::GetInstance(); 91 92 // Get the plugin file path. 93 std::vector<webkit::WebPluginInfo> plugins; 94 plugin_service->GetPluginInfoArray( 95 GURL(), mime_type, false, &plugins, NULL); 96 base::FilePath plugin_path; 97 if (!plugins.empty()) // May be empty for some tests. 98 plugin_path = plugins[0].path; 99 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 101 remove_start_time_ = base::Time::Now(); 102 is_removing_ = true; 103 // Balanced in On[Ppapi]ChannelOpened or OnError. Exactly one them will 104 // eventually be called, so we need to keep this object around until then. 105 AddRef(); 106 107 PepperPluginInfo* pepper_info = 108 plugin_service->GetRegisteredPpapiPluginInfo(plugin_path); 109 if (pepper_info) { 110 plugin_name_ = pepper_info->name; 111 // Use the broker since we run this function outside the sandbox. 112 plugin_service->OpenChannelToPpapiBroker(0, plugin_path, this); 113 } else { 114 plugin_service->OpenChannelToNpapiPlugin( 115 0, 0, GURL(), GURL(), mime_type, this); 116 } 117 } 118 119 // Called when a timeout happens in order not to block the client 120 // indefinitely. 121 void OnTimeout() { 122 LOG_IF(ERROR, is_removing_) << "Timed out"; 123 SignalDone(); 124 } 125 126 // PluginProcessHost::Client methods. 127 virtual int ID() OVERRIDE { 128 // Generate a unique identifier for this PluginProcessHostClient. 129 return ChildProcessHostImpl::GenerateChildProcessUniqueId(); 130 } 131 132 virtual bool OffTheRecord() OVERRIDE { 133 return false; 134 } 135 136 virtual ResourceContext* GetResourceContext() OVERRIDE { 137 return resource_context_; 138 } 139 140 virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {} 141 142 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {} 143 144 virtual void OnSentPluginChannelRequest() OVERRIDE {} 145 146 virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE { 147 ConnectToChannel(handle, false); 148 // Balancing the AddRef call. 149 Release(); 150 } 151 152 virtual void OnError() OVERRIDE { 153 LOG(ERROR) << "Couldn't open plugin channel"; 154 SignalDone(); 155 // Balancing the AddRef call. 156 Release(); 157 } 158 159 // PpapiPluginProcessHost::BrokerClient implementation. 160 virtual void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle, 161 int* renderer_id) OVERRIDE { 162 *renderer_handle = base::kNullProcessHandle; 163 *renderer_id = 0; 164 } 165 166 virtual void OnPpapiChannelOpened( 167 const IPC::ChannelHandle& channel_handle, 168 base::ProcessId /* peer_pid */, 169 int /* child_id */) OVERRIDE { 170 if (!channel_handle.name.empty()) 171 ConnectToChannel(channel_handle, true); 172 173 // Balancing the AddRef call. 174 Release(); 175 } 176 177 // IPC::Listener methods. 178 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 179 IPC_BEGIN_MESSAGE_MAP(Context, message) 180 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ClearSiteDataResult, 181 OnClearSiteDataResult) 182 IPC_MESSAGE_HANDLER(PpapiHostMsg_ClearSiteDataResult, 183 OnPpapiClearSiteDataResult) 184 IPC_MESSAGE_UNHANDLED_ERROR() 185 IPC_END_MESSAGE_MAP() 186 187 return true; 188 } 189 190 virtual void OnChannelError() OVERRIDE { 191 if (is_removing_) { 192 NOTREACHED() << "Channel error"; 193 SignalDone(); 194 } 195 } 196 197 base::WaitableEvent* event() { return event_.get(); } 198 199 private: 200 friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>; 201 friend class base::DeleteHelper<Context>; 202 virtual ~Context() {} 203 204 IPC::Message* CreatePpapiClearSiteDataMsg(uint64 max_age) { 205 base::FilePath profile_path = 206 PepperFlashFileMessageFilter::GetDataDirName(browser_context_path_); 207 // TODO(vtl): This "duplicates" logic in webkit/plugins/ppapi/file_path.cc 208 // (which prepends the plugin name to the relative part of the path 209 // instead, with the absolute, profile-dependent part being enforced by 210 // the browser). 211#if defined(OS_WIN) 212 base::FilePath plugin_data_path = 213 profile_path.Append(base::FilePath(UTF8ToUTF16(plugin_name_))); 214#else 215 base::FilePath plugin_data_path = 216 profile_path.Append(base::FilePath(plugin_name_)); 217#endif // defined(OS_WIN) 218 return new PpapiMsg_ClearSiteData(0u, plugin_data_path, std::string(), 219 kClearAllData, max_age); 220 } 221 222 // Connects the client side of a newly opened plug-in channel. 223 void ConnectToChannel(const IPC::ChannelHandle& handle, bool is_ppapi) { 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 225 226 // If we timed out, don't bother connecting. 227 if (!is_removing_) 228 return; 229 230 DCHECK(!channel_.get()); 231 channel_.reset(new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this)); 232 if (!channel_->Connect()) { 233 NOTREACHED() << "Couldn't connect to plugin"; 234 SignalDone(); 235 return; 236 } 237 238 uint64 max_age = begin_time_.is_null() ? 239 std::numeric_limits<uint64>::max() : 240 (base::Time::Now() - begin_time_).InSeconds(); 241 242 IPC::Message* msg; 243 if (is_ppapi) { 244 msg = CreatePpapiClearSiteDataMsg(max_age); 245 } else { 246 msg = new PluginProcessMsg_ClearSiteData( 247 std::string(), kClearAllData, max_age); 248 } 249 if (!channel_->Send(msg)) { 250 NOTREACHED() << "Couldn't send ClearSiteData message"; 251 SignalDone(); 252 return; 253 } 254 } 255 256 // Handles the PpapiHostMsg_ClearSiteDataResult message by delegating to the 257 // PluginProcessHostMsg_ClearSiteDataResult handler. 258 void OnPpapiClearSiteDataResult(uint32 request_id, bool success) { 259 DCHECK_EQ(0u, request_id); 260 OnClearSiteDataResult(success); 261 } 262 263 // Handles the PluginProcessHostMsg_ClearSiteDataResult message. 264 void OnClearSiteDataResult(bool success) { 265 LOG_IF(ERROR, !success) << "ClearSiteData returned error"; 266 UMA_HISTOGRAM_TIMES("ClearPluginData.time", 267 base::Time::Now() - remove_start_time_); 268 SignalDone(); 269 } 270 271 // Signals that we are finished with removing data (successful or not). This 272 // method is safe to call multiple times. 273 void SignalDone() { 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 275 if (!is_removing_) 276 return; 277 is_removing_ = false; 278 event_->Signal(); 279 } 280 281 scoped_ptr<base::WaitableEvent> event_; 282 // The point in time when we start removing data. 283 base::Time remove_start_time_; 284 // The point in time from which on we remove data. 285 base::Time begin_time_; 286 bool is_removing_; 287 288 // Path for the current profile. Must be retrieved on the UI thread from the 289 // browser context when we start so we can use it later on the I/O thread. 290 base::FilePath browser_context_path_; 291 292 // The resource context for the profile. Use only on the I/O thread. 293 ResourceContext* resource_context_; 294 295 // The name of the plugin. Use only on the I/O thread. 296 std::string plugin_name_; 297 298 // The channel is NULL until we have opened a connection to the plug-in 299 // process. 300 scoped_ptr<IPC::Channel> channel_; 301}; 302 303 304PluginDataRemoverImpl::PluginDataRemoverImpl(BrowserContext* browser_context) 305 : mime_type_(kFlashPluginSwfMimeType), 306 browser_context_(browser_context) { 307} 308 309PluginDataRemoverImpl::~PluginDataRemoverImpl() { 310} 311 312base::WaitableEvent* PluginDataRemoverImpl::StartRemoving( 313 base::Time begin_time) { 314 DCHECK(!context_.get()); 315 context_ = new Context(begin_time, browser_context_); 316 context_->Init(mime_type_); 317 return context_->event(); 318} 319 320} // namespace content 321