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"
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"
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;
39 PluginDataRemover
* PluginDataRemover::Create(BrowserContext
* browser_context
) {
40 return new PluginDataRemoverImpl(browser_context
);
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
) {
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
,
64 public base::RefCountedThreadSafe
<Context
,
65 BrowserThread::DeleteOnIOThread
> {
67 Context(base::Time begin_time
, BrowserContext
* browser_context
)
68 : event_(new base::WaitableEvent(true, false)),
69 begin_time_(begin_time
),
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(
80 base::Bind(&Context::InitOnIOThread
, this, mime_type
));
81 BrowserThread::PostDelayedTask(
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();
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.
106 PepperPluginInfo
* pepper_info
=
107 plugin_service
->GetRegisteredPpapiPluginInfo(plugin_path
);
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);
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
121 LOG_IF(ERROR
, is_removing_
) << "Timed out";
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
{
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.
151 virtual void OnError() OVERRIDE
{
152 LOG(ERROR
) << "Couldn't open plugin channel";
154 // Balancing the AddRef call.
158 // PpapiPluginProcessHost::BrokerClient implementation.
159 virtual void GetPpapiChannelInfo(base::ProcessHandle
* renderer_handle
,
160 int* renderer_id
) OVERRIDE
{
161 *renderer_handle
= base::kNullProcessHandle
;
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.
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()
189 virtual void OnChannelError() OVERRIDE
{
191 NOTREACHED() << "Channel error";
196 base::WaitableEvent
* event() { return event_
.get(); }
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
211 base::FilePath plugin_data_path
=
212 profile_path
.Append(base::FilePath(UTF8ToUTF16(plugin_name_
)));
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.
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";
237 uint64 max_age
= begin_time_
.is_null() ?
238 std::numeric_limits
<uint64
>::max() :
239 (base::Time::Now() - begin_time_
).InSeconds();
243 msg
= CreatePpapiClearSiteDataMsg(max_age
);
245 msg
= new PluginProcessMsg_ClearSiteData(
246 std::string(), kClearAllData
, max_age
);
248 if (!channel_
->Send(msg
)) {
249 NOTREACHED() << "Couldn't send ClearSiteData message";
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_
);
270 // Signals that we are finished with removing data (successful or not). This
271 // method is safe to call multiple times.
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
276 is_removing_
= false;
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_
;
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
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