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