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/content_constants.h"
23 #include "content/public/common/pepper_plugin_info.h"
24 #include "ppapi/proxy/ppapi_messages.h"
30 // The minimum Flash Player version that implements NPP_ClearSiteData.
31 const char kMinFlashVersion
[] = "10.3";
32 const int64 kRemovalTimeoutMs
= 10000;
33 const uint64 kClearAllData
= 0;
38 PluginDataRemover
* PluginDataRemover::Create(BrowserContext
* browser_context
) {
39 return new PluginDataRemoverImpl(browser_context
);
43 void PluginDataRemover::GetSupportedPlugins(
44 std::vector
<WebPluginInfo
>* supported_plugins
) {
45 bool allow_wildcard
= false;
46 std::vector
<WebPluginInfo
> plugins
;
47 PluginService::GetInstance()->GetPluginInfoArray(
48 GURL(), kFlashPluginSwfMimeType
, allow_wildcard
, &plugins
, NULL
);
49 Version
min_version(kMinFlashVersion
);
50 for (std::vector
<WebPluginInfo
>::iterator it
= plugins
.begin();
51 it
!= plugins
.end(); ++it
) {
53 WebPluginInfo::CreateVersionFromString(it
->version
, &version
);
54 if (version
.IsValid() && min_version
.CompareTo(version
) == -1)
55 supported_plugins
->push_back(*it
);
59 class PluginDataRemoverImpl::Context
60 : public PluginProcessHost::Client
,
61 public PpapiPluginProcessHost::BrokerClient
,
63 public base::RefCountedThreadSafe
<Context
,
64 BrowserThread::DeleteOnIOThread
> {
66 Context(base::Time begin_time
, BrowserContext
* browser_context
)
67 : event_(new base::WaitableEvent(true, false)),
68 begin_time_(begin_time
),
70 browser_context_path_(browser_context
->GetPath()),
71 resource_context_(browser_context
->GetResourceContext()) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
75 void Init(const std::string
& mime_type
) {
76 BrowserThread::PostTask(
79 base::Bind(&Context::InitOnIOThread
, this, mime_type
));
80 BrowserThread::PostDelayedTask(
83 base::Bind(&Context::OnTimeout
, this),
84 base::TimeDelta::FromMilliseconds(kRemovalTimeoutMs
));
87 void InitOnIOThread(const std::string
& mime_type
) {
88 PluginServiceImpl
* plugin_service
= PluginServiceImpl::GetInstance();
90 // Get the plugin file path.
91 std::vector
<WebPluginInfo
> plugins
;
92 plugin_service
->GetPluginInfoArray(
93 GURL(), mime_type
, false, &plugins
, NULL
);
94 base::FilePath plugin_path
;
95 if (!plugins
.empty()) // May be empty for some tests.
96 plugin_path
= plugins
[0].path
;
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
99 remove_start_time_
= base::Time::Now();
101 // Balanced in On[Ppapi]ChannelOpened or OnError. Exactly one them will
102 // eventually be called, so we need to keep this object around until then.
105 PepperPluginInfo
* pepper_info
=
106 plugin_service
->GetRegisteredPpapiPluginInfo(plugin_path
);
108 plugin_name_
= pepper_info
->name
;
109 // Use the broker since we run this function outside the sandbox.
110 plugin_service
->OpenChannelToPpapiBroker(0, plugin_path
, this);
112 plugin_service
->OpenChannelToNpapiPlugin(
113 0, 0, GURL(), GURL(), mime_type
, this);
117 // Called when a timeout happens in order not to block the client
120 LOG_IF(ERROR
, is_removing_
) << "Timed out";
124 // PluginProcessHost::Client methods.
125 virtual int ID() OVERRIDE
{
126 // Generate a unique identifier for this PluginProcessHostClient.
127 return ChildProcessHostImpl::GenerateChildProcessUniqueId();
130 virtual bool OffTheRecord() OVERRIDE
{
134 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
135 return resource_context_
;
138 virtual void SetPluginInfo(const WebPluginInfo
& info
) OVERRIDE
{}
140 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{}
142 virtual void OnSentPluginChannelRequest() OVERRIDE
{}
144 virtual void OnChannelOpened(const IPC::ChannelHandle
& handle
) OVERRIDE
{
145 ConnectToChannel(handle
, false);
146 // Balancing the AddRef call.
150 virtual void OnError() OVERRIDE
{
151 LOG(ERROR
) << "Couldn't open plugin channel";
153 // Balancing the AddRef call.
157 // PpapiPluginProcessHost::BrokerClient implementation.
158 virtual void GetPpapiChannelInfo(base::ProcessHandle
* renderer_handle
,
159 int* renderer_id
) OVERRIDE
{
160 *renderer_handle
= base::kNullProcessHandle
;
164 virtual void OnPpapiChannelOpened(
165 const IPC::ChannelHandle
& channel_handle
,
166 base::ProcessId
/* peer_pid */,
167 int /* child_id */) OVERRIDE
{
168 if (!channel_handle
.name
.empty())
169 ConnectToChannel(channel_handle
, true);
171 // Balancing the AddRef call.
175 // IPC::Listener methods.
176 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
177 IPC_BEGIN_MESSAGE_MAP(Context
, message
)
178 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ClearSiteDataResult
,
179 OnClearSiteDataResult
)
180 IPC_MESSAGE_HANDLER(PpapiHostMsg_ClearSiteDataResult
,
181 OnPpapiClearSiteDataResult
)
182 IPC_MESSAGE_UNHANDLED_ERROR()
183 IPC_END_MESSAGE_MAP()
188 virtual void OnChannelError() OVERRIDE
{
190 NOTREACHED() << "Channel error";
195 base::WaitableEvent
* event() { return event_
.get(); }
198 friend struct BrowserThread::DeleteOnThread
<BrowserThread::IO
>;
199 friend class base::DeleteHelper
<Context
>;
200 virtual ~Context() {}
202 IPC::Message
* CreatePpapiClearSiteDataMsg(uint64 max_age
) {
203 base::FilePath profile_path
=
204 PepperFlashFileMessageFilter::GetDataDirName(browser_context_path_
);
205 // TODO(vtl): This "duplicates" logic in webkit/plugins/ppapi/file_path.cc
206 // (which prepends the plugin name to the relative part of the path
207 // instead, with the absolute, profile-dependent part being enforced by
210 base::FilePath plugin_data_path
=
211 profile_path
.Append(base::FilePath(base::UTF8ToUTF16(plugin_name_
)));
213 base::FilePath plugin_data_path
=
214 profile_path
.Append(base::FilePath(plugin_name_
));
215 #endif // defined(OS_WIN)
216 return new PpapiMsg_ClearSiteData(0u, plugin_data_path
, std::string(),
217 kClearAllData
, max_age
);
220 // Connects the client side of a newly opened plug-in channel.
221 void ConnectToChannel(const IPC::ChannelHandle
& handle
, bool is_ppapi
) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
224 // If we timed out, don't bother connecting.
228 DCHECK(!channel_
.get());
229 channel_
.reset(new IPC::Channel(handle
, IPC::Channel::MODE_CLIENT
, this));
230 if (!channel_
->Connect()) {
231 NOTREACHED() << "Couldn't connect to plugin";
236 uint64 max_age
= begin_time_
.is_null() ?
237 std::numeric_limits
<uint64
>::max() :
238 (base::Time::Now() - begin_time_
).InSeconds();
242 msg
= CreatePpapiClearSiteDataMsg(max_age
);
244 msg
= new PluginProcessMsg_ClearSiteData(
245 std::string(), kClearAllData
, max_age
);
247 if (!channel_
->Send(msg
)) {
248 NOTREACHED() << "Couldn't send ClearSiteData message";
254 // Handles the PpapiHostMsg_ClearSiteDataResult message by delegating to the
255 // PluginProcessHostMsg_ClearSiteDataResult handler.
256 void OnPpapiClearSiteDataResult(uint32 request_id
, bool success
) {
257 DCHECK_EQ(0u, request_id
);
258 OnClearSiteDataResult(success
);
261 // Handles the PluginProcessHostMsg_ClearSiteDataResult message.
262 void OnClearSiteDataResult(bool success
) {
263 LOG_IF(ERROR
, !success
) << "ClearSiteData returned error";
264 UMA_HISTOGRAM_TIMES("ClearPluginData.time",
265 base::Time::Now() - remove_start_time_
);
269 // Signals that we are finished with removing data (successful or not). This
270 // method is safe to call multiple times.
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
275 is_removing_
= false;
279 scoped_ptr
<base::WaitableEvent
> event_
;
280 // The point in time when we start removing data.
281 base::Time remove_start_time_
;
282 // The point in time from which on we remove data.
283 base::Time begin_time_
;
286 // Path for the current profile. Must be retrieved on the UI thread from the
287 // browser context when we start so we can use it later on the I/O thread.
288 base::FilePath browser_context_path_
;
290 // The resource context for the profile. Use only on the I/O thread.
291 ResourceContext
* resource_context_
;
293 // The name of the plugin. Use only on the I/O thread.
294 std::string plugin_name_
;
296 // The channel is NULL until we have opened a connection to the plug-in
298 scoped_ptr
<IPC::Channel
> channel_
;
302 PluginDataRemoverImpl::PluginDataRemoverImpl(BrowserContext
* browser_context
)
303 : mime_type_(kFlashPluginSwfMimeType
),
304 browser_context_(browser_context
) {
307 PluginDataRemoverImpl::~PluginDataRemoverImpl() {
310 base::WaitableEvent
* PluginDataRemoverImpl::StartRemoving(
311 base::Time begin_time
) {
312 DCHECK(!context_
.get());
313 context_
= new Context(begin_time
, browser_context_
);
314 context_
->Init(mime_type_
);
315 return context_
->event();
318 } // namespace content