Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / plugin_data_remover_impl.cc
bloba38f47820c935bfee52d55890f3e2ed462ed9e7d
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.
5 #include "content/browser/plugin_data_remover_impl.h"
7 #include <limits>
9 #include "base/bind.h"
10 #include "base/metrics/histogram.h"
11 #include "base/sequenced_task_runner_helpers.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/synchronization/waitable_event.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"
27 namespace content {
29 namespace {
31 // The minimum Flash Player version that implements NPP_ClearSiteData.
32 const char kMinFlashVersion[] = "10.3";
33 const int64 kRemovalTimeoutMs = 10000;
34 const uint64 kClearAllData = 0;
36 } // namespace
38 // static
39 PluginDataRemover* PluginDataRemover::Create(BrowserContext* browser_context) {
40 return new PluginDataRemoverImpl(browser_context);
43 // static
44 void 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);
60 class 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 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76 void Init(const std::string& mime_type) {
77 BrowserThread::PostTask(
78 BrowserThread::IO,
79 FROM_HERE,
80 base::Bind(&Context::InitOnIOThread, this, mime_type));
81 BrowserThread::PostDelayedTask(
82 BrowserThread::IO,
83 FROM_HERE,
84 base::Bind(&Context::OnTimeout, this),
85 base::TimeDelta::FromMilliseconds(kRemovalTimeoutMs));
88 void InitOnIOThread(const std::string& mime_type) {
89 PluginServiceImpl* plugin_service = PluginServiceImpl::GetInstance();
91 // Get the plugin file path.
92 std::vector<webkit::WebPluginInfo> plugins;
93 plugin_service->GetPluginInfoArray(
94 GURL(), mime_type, false, &plugins, NULL);
95 base::FilePath plugin_path;
96 if (!plugins.empty()) // May be empty for some tests.
97 plugin_path = plugins[0].path;
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
100 remove_start_time_ = base::Time::Now();
101 is_removing_ = true;
102 // Balanced in On[Ppapi]ChannelOpened or OnError. Exactly one them will
103 // eventually be called, so we need to keep this object around until then.
104 AddRef();
106 PepperPluginInfo* pepper_info =
107 plugin_service->GetRegisteredPpapiPluginInfo(plugin_path);
108 if (pepper_info) {
109 plugin_name_ = pepper_info->name;
110 // Use the broker since we run this function outside the sandbox.
111 plugin_service->OpenChannelToPpapiBroker(0, plugin_path, this);
112 } else {
113 plugin_service->OpenChannelToNpapiPlugin(
114 0, 0, GURL(), GURL(), mime_type, this);
118 // Called when a timeout happens in order not to block the client
119 // indefinitely.
120 void OnTimeout() {
121 LOG_IF(ERROR, is_removing_) << "Timed out";
122 SignalDone();
125 // PluginProcessHost::Client methods.
126 virtual int ID() OVERRIDE {
127 // Generate a unique identifier for this PluginProcessHostClient.
128 return ChildProcessHostImpl::GenerateChildProcessUniqueId();
131 virtual bool OffTheRecord() OVERRIDE {
132 return false;
135 virtual ResourceContext* GetResourceContext() OVERRIDE {
136 return resource_context_;
139 virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {}
141 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {}
143 virtual void OnSentPluginChannelRequest() OVERRIDE {}
145 virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
146 ConnectToChannel(handle, false);
147 // Balancing the AddRef call.
148 Release();
151 virtual void OnError() OVERRIDE {
152 LOG(ERROR) << "Couldn't open plugin channel";
153 SignalDone();
154 // Balancing the AddRef call.
155 Release();
158 // PpapiPluginProcessHost::BrokerClient implementation.
159 virtual void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
160 int* renderer_id) OVERRIDE {
161 *renderer_handle = base::kNullProcessHandle;
162 *renderer_id = 0;
165 virtual void OnPpapiChannelOpened(
166 const IPC::ChannelHandle& channel_handle,
167 base::ProcessId /* peer_pid */,
168 int /* child_id */) OVERRIDE {
169 if (!channel_handle.name.empty())
170 ConnectToChannel(channel_handle, true);
172 // Balancing the AddRef call.
173 Release();
176 // IPC::Listener methods.
177 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
178 IPC_BEGIN_MESSAGE_MAP(Context, message)
179 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ClearSiteDataResult,
180 OnClearSiteDataResult)
181 IPC_MESSAGE_HANDLER(PpapiHostMsg_ClearSiteDataResult,
182 OnPpapiClearSiteDataResult)
183 IPC_MESSAGE_UNHANDLED_ERROR()
184 IPC_END_MESSAGE_MAP()
186 return true;
189 virtual void OnChannelError() OVERRIDE {
190 if (is_removing_) {
191 NOTREACHED() << "Channel error";
192 SignalDone();
196 base::WaitableEvent* event() { return event_.get(); }
198 private:
199 friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
200 friend class base::DeleteHelper<Context>;
201 virtual ~Context() {}
203 IPC::Message* CreatePpapiClearSiteDataMsg(uint64 max_age) {
204 base::FilePath profile_path =
205 PepperFlashFileMessageFilter::GetDataDirName(browser_context_path_);
206 // TODO(vtl): This "duplicates" logic in webkit/plugins/ppapi/file_path.cc
207 // (which prepends the plugin name to the relative part of the path
208 // instead, with the absolute, profile-dependent part being enforced by
209 // the browser).
210 #if defined(OS_WIN)
211 base::FilePath plugin_data_path =
212 profile_path.Append(base::FilePath(UTF8ToUTF16(plugin_name_)));
213 #else
214 base::FilePath plugin_data_path =
215 profile_path.Append(base::FilePath(plugin_name_));
216 #endif // defined(OS_WIN)
217 return new PpapiMsg_ClearSiteData(0u, plugin_data_path, std::string(),
218 kClearAllData, max_age);
221 // Connects the client side of a newly opened plug-in channel.
222 void ConnectToChannel(const IPC::ChannelHandle& handle, bool is_ppapi) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225 // If we timed out, don't bother connecting.
226 if (!is_removing_)
227 return;
229 DCHECK(!channel_.get());
230 channel_.reset(new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this));
231 if (!channel_->Connect()) {
232 NOTREACHED() << "Couldn't connect to plugin";
233 SignalDone();
234 return;
237 uint64 max_age = begin_time_.is_null() ?
238 std::numeric_limits<uint64>::max() :
239 (base::Time::Now() - begin_time_).InSeconds();
241 IPC::Message* msg;
242 if (is_ppapi) {
243 msg = CreatePpapiClearSiteDataMsg(max_age);
244 } else {
245 msg = new PluginProcessMsg_ClearSiteData(
246 std::string(), kClearAllData, max_age);
248 if (!channel_->Send(msg)) {
249 NOTREACHED() << "Couldn't send ClearSiteData message";
250 SignalDone();
251 return;
255 // Handles the PpapiHostMsg_ClearSiteDataResult message by delegating to the
256 // PluginProcessHostMsg_ClearSiteDataResult handler.
257 void OnPpapiClearSiteDataResult(uint32 request_id, bool success) {
258 DCHECK_EQ(0u, request_id);
259 OnClearSiteDataResult(success);
262 // Handles the PluginProcessHostMsg_ClearSiteDataResult message.
263 void OnClearSiteDataResult(bool success) {
264 LOG_IF(ERROR, !success) << "ClearSiteData returned error";
265 UMA_HISTOGRAM_TIMES("ClearPluginData.time",
266 base::Time::Now() - remove_start_time_);
267 SignalDone();
270 // Signals that we are finished with removing data (successful or not). This
271 // method is safe to call multiple times.
272 void SignalDone() {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
274 if (!is_removing_)
275 return;
276 is_removing_ = false;
277 event_->Signal();
280 scoped_ptr<base::WaitableEvent> event_;
281 // The point in time when we start removing data.
282 base::Time remove_start_time_;
283 // The point in time from which on we remove data.
284 base::Time begin_time_;
285 bool is_removing_;
287 // Path for the current profile. Must be retrieved on the UI thread from the
288 // browser context when we start so we can use it later on the I/O thread.
289 base::FilePath browser_context_path_;
291 // The resource context for the profile. Use only on the I/O thread.
292 ResourceContext* resource_context_;
294 // The name of the plugin. Use only on the I/O thread.
295 std::string plugin_name_;
297 // The channel is NULL until we have opened a connection to the plug-in
298 // process.
299 scoped_ptr<IPC::Channel> channel_;
303 PluginDataRemoverImpl::PluginDataRemoverImpl(BrowserContext* browser_context)
304 : mime_type_(kFlashPluginSwfMimeType),
305 browser_context_(browser_context) {
308 PluginDataRemoverImpl::~PluginDataRemoverImpl() {
311 base::WaitableEvent* PluginDataRemoverImpl::StartRemoving(
312 base::Time begin_time) {
313 DCHECK(!context_.get());
314 context_ = new Context(begin_time, browser_context_);
315 context_->Init(mime_type_);
316 return context_->event();
319 } // namespace content