Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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 "chrome/browser/plugin_data_remover.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/message_loop_proxy.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/synchronization/waitable_event.h"
     11 #include "base/version.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "content/browser/plugin_service.h"
     15 #include "content/common/plugin_messages.h"
     16 #include "webkit/plugins/npapi/plugin_group.h"
     17 #include "webkit/plugins/npapi/plugin_list.h"
     18 
     19 #if defined(OS_POSIX)
     20 #include "ipc/ipc_channel_posix.h"
     21 #endif
     22 
     23 namespace {
     24 
     25 const char* kFlashMimeType = "application/x-shockwave-flash";
     26 // The minimum Flash Player version that implements NPP_ClearSiteData.
     27 const char* kMinFlashVersion = "10.3";
     28 const int64 kRemovalTimeoutMs = 10000;
     29 const uint64 kClearAllData = 0;
     30 
     31 }  // namespace
     32 
     33 PluginDataRemover::PluginDataRemover()
     34     : mime_type_(kFlashMimeType),
     35       is_removing_(false),
     36       event_(new base::WaitableEvent(true, false)),
     37       channel_(NULL) {
     38 }
     39 
     40 PluginDataRemover::~PluginDataRemover() {
     41   DCHECK(!is_removing_);
     42   if (channel_)
     43     BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
     44 }
     45 
     46 base::WaitableEvent* PluginDataRemover::StartRemoving(base::Time begin_time) {
     47   DCHECK(!is_removing_);
     48   remove_start_time_ = base::Time::Now();
     49   begin_time_ = begin_time;
     50 
     51   is_removing_ = true;
     52 
     53   // Balanced in OnChannelOpened or OnError. Exactly one them will eventually be
     54   // called, so we need to keep this object around until then.
     55   AddRef();
     56   PluginService::GetInstance()->OpenChannelToNpapiPlugin(
     57       0, 0, GURL(), mime_type_, this);
     58 
     59   BrowserThread::PostDelayedTask(
     60       BrowserThread::IO,
     61       FROM_HERE,
     62       NewRunnableMethod(this, &PluginDataRemover::OnTimeout),
     63       kRemovalTimeoutMs);
     64 
     65   return event_.get();
     66 }
     67 
     68 void PluginDataRemover::Wait() {
     69   base::Time start_time(base::Time::Now());
     70   bool result = true;
     71   if (is_removing_)
     72     result = event_->Wait();
     73   UMA_HISTOGRAM_TIMES("ClearPluginData.wait_at_shutdown",
     74                       base::Time::Now() - start_time);
     75   UMA_HISTOGRAM_TIMES("ClearPluginData.time_at_shutdown",
     76                       base::Time::Now() - remove_start_time_);
     77   DCHECK(result) << "Error waiting for plugin process";
     78 }
     79 
     80 int PluginDataRemover::ID() {
     81   // Generate a unique identifier for this PluginProcessHostClient.
     82   return ChildProcessInfo::GenerateChildProcessUniqueId();
     83 }
     84 
     85 bool PluginDataRemover::OffTheRecord() {
     86   return false;
     87 }
     88 
     89 void PluginDataRemover::SetPluginInfo(
     90     const webkit::npapi::WebPluginInfo& info) {
     91 }
     92 
     93 void PluginDataRemover::OnChannelOpened(const IPC::ChannelHandle& handle) {
     94   ConnectToChannel(handle);
     95   // Balancing the AddRef call in StartRemoving.
     96   Release();
     97 }
     98 
     99 void PluginDataRemover::ConnectToChannel(const IPC::ChannelHandle& handle) {
    100   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    101 
    102   // If we timed out, don't bother connecting.
    103   if (!is_removing_)
    104     return;
    105 
    106   DCHECK(!channel_);
    107   channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
    108   if (!channel_->Connect()) {
    109     NOTREACHED() << "Couldn't connect to plugin";
    110     SignalDone();
    111     return;
    112   }
    113 
    114   if (!channel_->Send(new PluginMsg_ClearSiteData(std::string(),
    115                                                   kClearAllData,
    116                                                   begin_time_))) {
    117     NOTREACHED() << "Couldn't send ClearSiteData message";
    118     SignalDone();
    119     return;
    120   }
    121 }
    122 
    123 void PluginDataRemover::OnError() {
    124   LOG(DFATAL) << "Couldn't open plugin channel";
    125   SignalDone();
    126   // Balancing the AddRef call in StartRemoving.
    127   Release();
    128 }
    129 
    130 void PluginDataRemover::OnClearSiteDataResult(bool success) {
    131   LOG_IF(DFATAL, !success) << "ClearSiteData returned error";
    132   UMA_HISTOGRAM_TIMES("ClearPluginData.time",
    133                       base::Time::Now() - remove_start_time_);
    134   SignalDone();
    135 }
    136 
    137 void PluginDataRemover::OnTimeout() {
    138   LOG_IF(DFATAL, is_removing_) << "Timed out";
    139   SignalDone();
    140 }
    141 
    142 bool PluginDataRemover::OnMessageReceived(const IPC::Message& msg) {
    143   IPC_BEGIN_MESSAGE_MAP(PluginDataRemover, msg)
    144     IPC_MESSAGE_HANDLER(PluginHostMsg_ClearSiteDataResult,
    145                         OnClearSiteDataResult)
    146     IPC_MESSAGE_UNHANDLED_ERROR()
    147   IPC_END_MESSAGE_MAP()
    148 
    149   return true;
    150 }
    151 
    152 void PluginDataRemover::OnChannelError() {
    153   if (is_removing_) {
    154     NOTREACHED() << "Channel error";
    155     SignalDone();
    156   }
    157 }
    158 
    159 void PluginDataRemover::SignalDone() {
    160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    161   if (!is_removing_)
    162     return;
    163   is_removing_ = false;
    164   event_->Signal();
    165 }
    166 
    167 // static
    168 bool PluginDataRemover::IsSupported() {
    169   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    170   bool allow_wildcard = false;
    171   webkit::npapi::WebPluginInfo plugin;
    172   std::string mime_type;
    173   if (!webkit::npapi::PluginList::Singleton()->GetPluginInfo(
    174           GURL(), kFlashMimeType, allow_wildcard, &plugin, &mime_type)) {
    175     return false;
    176   }
    177   scoped_ptr<Version> version(
    178       webkit::npapi::PluginGroup::CreateVersionFromString(plugin.version));
    179   scoped_ptr<Version> min_version(Version::GetVersionFromString(
    180       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    181           switches::kMinClearSiteDataFlashVersion)));
    182   if (!min_version.get())
    183     min_version.reset(Version::GetVersionFromString(kMinFlashVersion));
    184   return webkit::npapi::IsPluginEnabled(plugin) &&
    185          version.get() &&
    186          min_version->CompareTo(*version) == -1;
    187 }
    188