Home | History | Annotate | Download | only in browser
      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
     38 PluginDataRemover* PluginDataRemover::Create(BrowserContext* browser_context) {
     39   return new PluginDataRemoverImpl(browser_context);
     40 }
     41 
     42 // static
     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:
     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 
     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 
     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.
    119   void OnTimeout() {
    120     LOG_IF(ERROR, is_removing_) << "Timed out";
    121     SignalDone();
    122   }
    123 
    124   // PluginProcessHost::Client methods.
    125   virtual int ID() OVERRIDE {
    126     // Generate a unique identifier for this PluginProcessHostClient.
    127     return ChildProcessHostImpl::GenerateChildProcessUniqueId();
    128   }
    129 
    130   virtual bool OffTheRecord() OVERRIDE {
    131     return false;
    132   }
    133 
    134   virtual ResourceContext* GetResourceContext() OVERRIDE {
    135     return resource_context_;
    136   }
    137 
    138   virtual void SetPluginInfo(const WebPluginInfo& info) OVERRIDE {}
    139 
    140   virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {}
    141 
    142   virtual void OnSentPluginChannelRequest() OVERRIDE {}
    143 
    144   virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
    145     ConnectToChannel(handle, false);
    146     // Balancing the AddRef call.
    147     Release();
    148   }
    149 
    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.
    158   virtual void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
    159                                    int* renderer_id) OVERRIDE {
    160     *renderer_handle = base::kNullProcessHandle;
    161     *renderer_id = 0;
    162   }
    163 
    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.
    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 
    188   virtual void OnChannelError() OVERRIDE {
    189     if (is_removing_) {
    190       NOTREACHED() << "Channel error";
    191       SignalDone();
    192     }
    193   }
    194 
    195   base::WaitableEvent* event() { return event_.get(); }
    196 
    197  private:
    198   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
    199   friend class base::DeleteHelper<Context>;
    200   virtual ~Context() {}
    201 
    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(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.
    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_.reset(new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, 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.
    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.
    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.
    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 
    302 PluginDataRemoverImpl::PluginDataRemoverImpl(BrowserContext* browser_context)
    303     : mime_type_(kFlashPluginSwfMimeType),
    304       browser_context_(browser_context) {
    305 }
    306 
    307 PluginDataRemoverImpl::~PluginDataRemoverImpl() {
    308 }
    309 
    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