• 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_loader_posix.h"
6 
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/metrics/histogram.h"
11 #include "content/browser/utility_process_host_impl.h"
12 #include "content/common/child_process_host_impl.h"
13 #include "content/common/plugin_list.h"
14 #include "content/common/utility_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/plugin_service.h"
17 #include "content/public/browser/user_metrics.h"
18 
19 namespace content {
20 
PluginLoaderPosix()21 PluginLoaderPosix::PluginLoaderPosix()
22     : next_load_index_(0) {
23 }
24 
GetPlugins(const PluginService::GetPluginsCallback & callback)25 void PluginLoaderPosix::GetPlugins(
26     const PluginService::GetPluginsCallback& callback) {
27   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
28 
29   std::vector<WebPluginInfo> cached_plugins;
30   if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
31     // Can't assume the caller is reentrant.
32     base::MessageLoop::current()->PostTask(FROM_HERE,
33         base::Bind(callback, cached_plugins));
34     return;
35   }
36 
37   if (callbacks_.empty()) {
38     callbacks_.push_back(callback);
39 
40     PluginList::Singleton()->PrepareForPluginLoading();
41 
42     BrowserThread::PostTask(BrowserThread::FILE,
43                             FROM_HERE,
44                             base::Bind(&PluginLoaderPosix::GetPluginsToLoad,
45                                        make_scoped_refptr(this)));
46   } else {
47     // If we are currently loading plugins, the plugin list might have been
48     // invalidated in the mean time, or might get invalidated before we finish.
49     // We'll wait until we have finished the current run, then try to get them
50     // again from the plugin list. If it has indeed been invalidated, it will
51     // restart plugin loading, otherwise it will immediately run the callback.
52     callbacks_.push_back(base::Bind(&PluginLoaderPosix::GetPluginsWrapper,
53                                     make_scoped_refptr(this), callback));
54   }
55 }
56 
OnMessageReceived(const IPC::Message & message)57 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) {
58   bool handled = true;
59   IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message)
60     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded)
61     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed)
62     IPC_MESSAGE_UNHANDLED(handled = false)
63   IPC_END_MESSAGE_MAP()
64   return handled;
65 }
66 
OnProcessCrashed(int exit_code)67 void PluginLoaderPosix::OnProcessCrashed(int exit_code) {
68   RecordAction(
69       base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed"));
70 
71   if (next_load_index_ == canonical_list_.size()) {
72     // How this case occurs is unknown. See crbug.com/111935.
73     canonical_list_.clear();
74   } else {
75     canonical_list_.erase(canonical_list_.begin(),
76                           canonical_list_.begin() + next_load_index_ + 1);
77   }
78 
79   next_load_index_ = 0;
80 
81   LoadPluginsInternal();
82 }
83 
Send(IPC::Message * message)84 bool PluginLoaderPosix::Send(IPC::Message* message) {
85   if (process_host_.get())
86     return process_host_->Send(message);
87   return false;
88 }
89 
~PluginLoaderPosix()90 PluginLoaderPosix::~PluginLoaderPosix() {
91 }
92 
GetPluginsToLoad()93 void PluginLoaderPosix::GetPluginsToLoad() {
94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
95 
96   base::TimeTicks start_time(base::TimeTicks::Now());
97 
98   loaded_plugins_.clear();
99   next_load_index_ = 0;
100 
101   canonical_list_.clear();
102   PluginList::Singleton()->GetPluginPathsToLoad(
103       &canonical_list_,
104       PluginService::GetInstance()->NPAPIPluginsSupported());
105 
106   internal_plugins_.clear();
107   PluginList::Singleton()->GetInternalPlugins(&internal_plugins_);
108 
109   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
110       base::Bind(&PluginLoaderPosix::LoadPluginsInternal,
111                  make_scoped_refptr(this)));
112 
113   HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList",
114                   (base::TimeTicks::Now() - start_time) *
115                       base::Time::kMicrosecondsPerMillisecond);
116 }
117 
LoadPluginsInternal()118 void PluginLoaderPosix::LoadPluginsInternal() {
119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120 
121   // Check if the list is empty or all plugins have already been loaded before
122   // forking.
123   if (MaybeRunPendingCallbacks())
124     return;
125 
126   RecordAction(
127       base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
128 
129   if (load_start_time_.is_null())
130     load_start_time_ = base::TimeTicks::Now();
131 
132   UtilityProcessHostImpl* host = new UtilityProcessHostImpl(
133       this,
134       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());
135   process_host_ = host->AsWeakPtr();
136   process_host_->DisableSandbox();
137 #if defined(OS_MACOSX)
138   host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION);
139 #endif
140 
141   process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_));
142 }
143 
GetPluginsWrapper(const PluginService::GetPluginsCallback & callback,const std::vector<WebPluginInfo> & plugins_unused)144 void PluginLoaderPosix::GetPluginsWrapper(
145     const PluginService::GetPluginsCallback& callback,
146     const std::vector<WebPluginInfo>& plugins_unused) {
147   // We are being called after plugin loading has finished, but we don't know
148   // whether the plugin list has been invalidated in the mean time
149   // (and therefore |plugins| might already be stale). So we simply ignore it
150   // and call regular GetPlugins() instead.
151   GetPlugins(callback);
152 }
153 
OnPluginLoaded(uint32 index,const WebPluginInfo & plugin)154 void PluginLoaderPosix::OnPluginLoaded(uint32 index,
155                                        const WebPluginInfo& plugin) {
156   if (index != next_load_index_) {
157     LOG(ERROR) << "Received unexpected plugin load message for "
158                << plugin.path.value() << "; index=" << index;
159     return;
160   }
161 
162   if (!MaybeAddInternalPlugin(plugin.path))
163     loaded_plugins_.push_back(plugin);
164 
165   ++next_load_index_;
166 
167   MaybeRunPendingCallbacks();
168 }
169 
OnPluginLoadFailed(uint32 index,const base::FilePath & plugin_path)170 void PluginLoaderPosix::OnPluginLoadFailed(uint32 index,
171                                            const base::FilePath& plugin_path) {
172   if (index != next_load_index_) {
173     LOG(ERROR) << "Received unexpected plugin load failure message for "
174                << plugin_path.value() << "; index=" << index;
175     return;
176   }
177 
178   ++next_load_index_;
179 
180   MaybeAddInternalPlugin(plugin_path);
181   MaybeRunPendingCallbacks();
182 }
183 
MaybeAddInternalPlugin(const base::FilePath & plugin_path)184 bool PluginLoaderPosix::MaybeAddInternalPlugin(
185     const base::FilePath& plugin_path) {
186   for (std::vector<WebPluginInfo>::iterator it = internal_plugins_.begin();
187        it != internal_plugins_.end();
188        ++it) {
189     if (it->path == plugin_path) {
190       loaded_plugins_.push_back(*it);
191       internal_plugins_.erase(it);
192       return true;
193     }
194   }
195   return false;
196 }
197 
MaybeRunPendingCallbacks()198 bool PluginLoaderPosix::MaybeRunPendingCallbacks() {
199   if (next_load_index_ < canonical_list_.size())
200     return false;
201 
202   PluginList::Singleton()->SetPlugins(loaded_plugins_);
203 
204   for (std::vector<PluginService::GetPluginsCallback>::iterator it =
205            callbacks_.begin();
206        it != callbacks_.end(); ++it) {
207     base::MessageLoop::current()->PostTask(FROM_HERE,
208                                            base::Bind(*it, loaded_plugins_));
209   }
210   callbacks_.clear();
211 
212   HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone",
213                   (base::TimeTicks::Now() - load_start_time_)
214                       * base::Time::kMicrosecondsPerMillisecond);
215   load_start_time_ = base::TimeTicks();
216 
217   return true;
218 }
219 
220 }  // namespace content
221