• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
25 
26 namespace content {
27 
28 namespace {
29 
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;
34 
35 }  // namespace
36 
37 // static
Create(BrowserContext * browser_context)38 PluginDataRemover* PluginDataRemover::Create(BrowserContext* browser_context) {
39   return new PluginDataRemoverImpl(browser_context);
40 }
41 
42 // static
GetSupportedPlugins(std::vector<WebPluginInfo> * supported_plugins)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) {
52     Version version;
53     WebPluginInfo::CreateVersionFromString(it->version, &version);
54     if (version.IsValid() && min_version.CompareTo(version) == -1)
55       supported_plugins->push_back(*it);
56   }
57 }
58 
59 class PluginDataRemoverImpl::Context
60     : public PluginProcessHost::Client,
61       public PpapiPluginProcessHost::BrokerClient,
62       public IPC::Listener,
63       public base::RefCountedThreadSafe<Context,
64                                         BrowserThread::DeleteOnIOThread> {
65  public:
Context(base::Time begin_time,BrowserContext * browser_context)66   Context(base::Time begin_time, BrowserContext* browser_context)
67       : event_(new base::WaitableEvent(true, false)),
68         begin_time_(begin_time),
69         is_removing_(false),
70         browser_context_path_(browser_context->GetPath()),
71         resource_context_(browser_context->GetResourceContext()) {
72     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73   }
74 
Init(const std::string & mime_type)75   void Init(const std::string& mime_type) {
76     BrowserThread::PostTask(
77         BrowserThread::IO,
78         FROM_HERE,
79         base::Bind(&Context::InitOnIOThread, this, mime_type));
80     BrowserThread::PostDelayedTask(
81         BrowserThread::IO,
82         FROM_HERE,
83         base::Bind(&Context::OnTimeout, this),
84         base::TimeDelta::FromMilliseconds(kRemovalTimeoutMs));
85   }
86 
InitOnIOThread(const std::string & mime_type)87   void InitOnIOThread(const std::string& mime_type) {
88     PluginServiceImpl* plugin_service = PluginServiceImpl::GetInstance();
89 
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;
97 
98     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
99     remove_start_time_ = base::Time::Now();
100     is_removing_ = true;
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.
103     AddRef();
104 
105     PepperPluginInfo* pepper_info =
106         plugin_service->GetRegisteredPpapiPluginInfo(plugin_path);
107     if (pepper_info) {
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);
111     } else {
112       plugin_service->OpenChannelToNpapiPlugin(
113           0, 0, GURL(), GURL(), mime_type, this);
114     }
115   }
116 
117   // Called when a timeout happens in order not to block the client
118   // indefinitely.
OnTimeout()119   void OnTimeout() {
120     LOG_IF(ERROR, is_removing_) << "Timed out";
121     SignalDone();
122   }
123 
124   // PluginProcessHost::Client methods.
ID()125   virtual int ID() OVERRIDE {
126     // Generate a unique identifier for this PluginProcessHostClient.
127     return ChildProcessHostImpl::GenerateChildProcessUniqueId();
128   }
129 
OffTheRecord()130   virtual bool OffTheRecord() OVERRIDE {
131     return false;
132   }
133 
GetResourceContext()134   virtual ResourceContext* GetResourceContext() OVERRIDE {
135     return resource_context_;
136   }
137 
SetPluginInfo(const WebPluginInfo & info)138   virtual void SetPluginInfo(const WebPluginInfo& info) OVERRIDE {}
139 
OnFoundPluginProcessHost(PluginProcessHost * host)140   virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {}
141 
OnSentPluginChannelRequest()142   virtual void OnSentPluginChannelRequest() OVERRIDE {}
143 
OnChannelOpened(const IPC::ChannelHandle & handle)144   virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
145     ConnectToChannel(handle, false);
146     // Balancing the AddRef call.
147     Release();
148   }
149 
OnError()150   virtual void OnError() OVERRIDE {
151     LOG(ERROR) << "Couldn't open plugin channel";
152     SignalDone();
153     // Balancing the AddRef call.
154     Release();
155   }
156 
157   // PpapiPluginProcessHost::BrokerClient implementation.
GetPpapiChannelInfo(base::ProcessHandle * renderer_handle,int * renderer_id)158   virtual void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
159                                    int* renderer_id) OVERRIDE {
160     *renderer_handle = base::kNullProcessHandle;
161     *renderer_id = 0;
162   }
163 
OnPpapiChannelOpened(const IPC::ChannelHandle & channel_handle,base::ProcessId,int)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);
170 
171     // Balancing the AddRef call.
172     Release();
173   }
174 
175   // IPC::Listener methods.
OnMessageReceived(const IPC::Message & message)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()
184 
185     return true;
186   }
187 
OnChannelError()188   virtual void OnChannelError() OVERRIDE {
189     if (is_removing_) {
190       NOTREACHED() << "Channel error";
191       SignalDone();
192     }
193   }
194 
event()195   base::WaitableEvent* event() { return event_.get(); }
196 
197  private:
198   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
199   friend class base::DeleteHelper<Context>;
~Context()200   virtual ~Context() {}
201 
CreatePpapiClearSiteDataMsg(uint64 max_age)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
208     // the browser).
209 #if defined(OS_WIN)
210     base::FilePath plugin_data_path =
211         profile_path.Append(base::FilePath(base::UTF8ToUTF16(plugin_name_)));
212 #else
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);
218   }
219 
220   // Connects the client side of a newly opened plug-in channel.
ConnectToChannel(const IPC::ChannelHandle & handle,bool is_ppapi)221   void ConnectToChannel(const IPC::ChannelHandle& handle, bool is_ppapi) {
222     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
223 
224     // If we timed out, don't bother connecting.
225     if (!is_removing_)
226       return;
227 
228     DCHECK(!channel_.get());
229     channel_ = IPC::Channel::CreateClient(handle, this);
230     if (!channel_->Connect()) {
231       NOTREACHED() << "Couldn't connect to plugin";
232       SignalDone();
233       return;
234     }
235 
236     uint64 max_age = begin_time_.is_null() ?
237         std::numeric_limits<uint64>::max() :
238         (base::Time::Now() - begin_time_).InSeconds();
239 
240     IPC::Message* msg;
241     if (is_ppapi) {
242       msg = CreatePpapiClearSiteDataMsg(max_age);
243     } else {
244       msg = new PluginProcessMsg_ClearSiteData(
245           std::string(), kClearAllData, max_age);
246     }
247     if (!channel_->Send(msg)) {
248       NOTREACHED() << "Couldn't send ClearSiteData message";
249       SignalDone();
250       return;
251     }
252   }
253 
254   // Handles the PpapiHostMsg_ClearSiteDataResult message by delegating to the
255   // PluginProcessHostMsg_ClearSiteDataResult handler.
OnPpapiClearSiteDataResult(uint32 request_id,bool success)256   void OnPpapiClearSiteDataResult(uint32 request_id, bool success) {
257     DCHECK_EQ(0u, request_id);
258     OnClearSiteDataResult(success);
259   }
260 
261   // Handles the PluginProcessHostMsg_ClearSiteDataResult message.
OnClearSiteDataResult(bool success)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_);
266     SignalDone();
267   }
268 
269   // Signals that we are finished with removing data (successful or not). This
270   // method is safe to call multiple times.
SignalDone()271   void SignalDone() {
272     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273     if (!is_removing_)
274       return;
275     is_removing_ = false;
276     event_->Signal();
277   }
278 
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_;
284   bool is_removing_;
285 
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_;
289 
290   // The resource context for the profile. Use only on the I/O thread.
291   ResourceContext* resource_context_;
292 
293   // The name of the plugin. Use only on the I/O thread.
294   std::string plugin_name_;
295 
296   // The channel is NULL until we have opened a connection to the plug-in
297   // process.
298   scoped_ptr<IPC::Channel> channel_;
299 };
300 
301 
PluginDataRemoverImpl(BrowserContext * browser_context)302 PluginDataRemoverImpl::PluginDataRemoverImpl(BrowserContext* browser_context)
303     : mime_type_(kFlashPluginSwfMimeType),
304       browser_context_(browser_context) {
305 }
306 
~PluginDataRemoverImpl()307 PluginDataRemoverImpl::~PluginDataRemoverImpl() {
308 }
309 
StartRemoving(base::Time begin_time)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();
316 }
317 
318 }  // namespace content
319