Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / nacl / renderer / plugin / plugin.cc
blob89c5db7556d4270866085c26e8750051e411717c
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.
5 #include "components/nacl/renderer/plugin/plugin.h"
7 #include <sys/stat.h>
8 #include <sys/types.h>
10 #include <string>
12 #include "components/nacl/renderer/plugin/nacl_subprocess.h"
13 #include "components/nacl/renderer/plugin/plugin_error.h"
14 #include "components/nacl/renderer/plugin/service_runtime.h"
15 #include "components/nacl/renderer/plugin/utility.h"
16 #include "components/nacl/renderer/ppb_nacl_private.h"
17 #include "native_client/src/include/nacl_base.h"
18 #include "native_client/src/include/nacl_macros.h"
19 #include "native_client/src/include/nacl_scoped_ptr.h"
20 #include "native_client/src/include/portability.h"
21 #include "native_client/src/include/portability_io.h"
22 #include "native_client/src/shared/platform/nacl_check.h"
23 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
24 #include "ppapi/c/pp_errors.h"
25 #include "ppapi/cpp/module.h"
27 namespace plugin {
29 void Plugin::ShutDownSubprocesses() {
30 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
31 static_cast<void*>(this)));
33 // Shut down service runtime. This must be done before all other calls so
34 // they don't block forever when waiting for the upcall thread to exit.
35 main_subprocess_.Shutdown();
37 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
38 static_cast<void*>(this)));
41 bool Plugin::LoadHelperNaClModuleInternal(NaClSubprocess* subprocess,
42 const SelLdrStartParams& params) {
43 CHECK(!pp::Module::Get()->core()->IsMainThread());
44 ServiceRuntime* service_runtime =
45 new ServiceRuntime(this,
46 pp_instance(),
47 false, // No main_service_runtime.
48 false); // No non-SFI mode (i.e. in SFI-mode).
50 // Now start the SelLdr instance. This must be created on the main thread.
51 bool service_runtime_started = false;
52 pp::CompletionCallback sel_ldr_callback =
53 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
54 &service_runtime_started,
55 service_runtime);
56 pp::CompletionCallback callback =
57 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
58 service_runtime, params,
59 sel_ldr_callback);
60 pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
61 if (!service_runtime->WaitForSelLdrStart()) {
62 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
63 "WaitForSelLdrStart timed out!\n"));
64 service_runtime->Shutdown();
65 // Don't delete service_runtime here; it could still be used by the pending
66 // SignalStartSelLdrDone callback.
67 return false;
69 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
70 service_runtime_started));
71 if (!service_runtime_started) {
72 service_runtime->Shutdown();
73 delete service_runtime;
74 return false;
77 // Now actually start the nexe.
79 // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
80 // have to roll our own blocking logic, similar to WaitForSelLdrStart()
81 // above, except without timeout logic.
82 pp::Module::Get()->core()->CallOnMainThread(
84 callback_factory_.NewCallback(&Plugin::StartNexe, service_runtime));
85 if (!service_runtime->WaitForNexeStart()) {
86 service_runtime->Shutdown();
87 delete service_runtime;
88 return false;
90 subprocess->set_service_runtime(service_runtime);
91 return true;
94 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
95 ServiceRuntime* service_runtime,
96 const SelLdrStartParams& params,
97 pp::CompletionCallback callback) {
98 CHECK(pp_error == PP_OK);
99 service_runtime->StartSelLdr(params, callback);
102 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
103 bool* started,
104 ServiceRuntime* service_runtime) {
105 if (service_runtime->SelLdrWaitTimedOut()) {
106 delete service_runtime;
107 } else {
108 *started = (pp_error == PP_OK);
109 service_runtime->SignalStartSelLdrDone();
113 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
114 bool uses_nonsfi_mode,
115 PP_NaClAppProcessType process_type) {
116 CHECK(pp::Module::Get()->core()->IsMainThread());
117 // Before forking a new sel_ldr process, ensure that we do not leak
118 // the ServiceRuntime object for an existing subprocess, and that any
119 // associated listener threads do not go unjoined because if they
120 // outlive the Plugin object, they will not be memory safe.
121 ShutDownSubprocesses();
122 pp::Var manifest_base_url =
123 pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
124 std::string manifest_base_url_str = manifest_base_url.AsString();
126 SelLdrStartParams params(manifest_base_url_str,
127 file_info,
128 process_type);
129 ErrorInfo error_info;
130 ServiceRuntime* service_runtime = new ServiceRuntime(
131 this, pp_instance(), true, uses_nonsfi_mode);
132 main_subprocess_.set_service_runtime(service_runtime);
133 if (NULL == service_runtime) {
134 error_info.SetReport(
135 PP_NACL_ERROR_SEL_LDR_INIT,
136 "sel_ldr init failure " + main_subprocess_.description());
137 ReportLoadError(error_info);
138 return;
141 // We don't take any action once nexe loading has completed, so pass an empty
142 // callback here for |callback|.
143 pp::CompletionCallback callback = callback_factory_.NewCallback(
144 &Plugin::StartNexe, service_runtime);
145 StartSelLdrOnMainThread(
146 static_cast<int32_t>(PP_OK), service_runtime, params, callback);
149 void Plugin::StartNexe(int32_t pp_error, ServiceRuntime* service_runtime) {
150 CHECK(pp::Module::Get()->core()->IsMainThread());
151 if (pp_error != PP_OK)
152 return;
153 service_runtime->StartNexe();
156 NaClSubprocess* Plugin::LoadHelperNaClModule(const std::string& helper_url,
157 PP_NaClFileInfo file_info,
158 ErrorInfo* error_info) {
159 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
160 new NaClSubprocess("helper module", NULL, NULL));
161 if (NULL == nacl_subprocess.get()) {
162 error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
163 "unable to allocate helper subprocess.");
164 return NULL;
167 // Do not report UMA stats for translator-related nexes.
168 // TODO(sehr): define new UMA stats for translator related nexe events.
169 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
170 // done to save on address space and swap space.
171 SelLdrStartParams params(helper_url,
172 file_info,
173 PP_PNACL_TRANSLATOR_PROCESS_TYPE);
175 // Helper NaCl modules always use the PNaCl manifest, as there is no
176 // corresponding NMF.
177 if (!LoadHelperNaClModuleInternal(nacl_subprocess.get(), params))
178 return NULL;
180 // We can block here in StartSrpcServices, since helper NaCl
181 // modules are spawned from a private thread.
183 // TODO(bsy): if helper module crashes, we should abort.
184 // crash_cb is not used here, so we are relying on crashes
185 // being detected in StartSrpcServices or later.
187 // NB: More refactoring might be needed, however, if helper
188 // NaCl modules have their own manifest. Currently the
189 // manifest is a per-plugin-instance object, not a per
190 // NaClSubprocess object.
191 if (!nacl_subprocess->StartSrpcServices()) {
192 error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
193 "SRPC connection failure for " +
194 nacl_subprocess->description());
195 return NULL;
198 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
199 helper_url.c_str(),
200 nacl_subprocess.get()->detailed_description().c_str()));
202 return nacl_subprocess.release();
205 // All failures of this function will show up as "Missing Plugin-in", so
206 // there is no need to log to JS console that there was an initialization
207 // failure. Note that module loading functions will log their own errors.
208 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
209 nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
210 wrapper_factory_ = new nacl::DescWrapperFactory();
211 pp::CompletionCallback open_cb =
212 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
213 nacl_interface_->RequestNaClManifest(pp_instance(),
214 open_cb.pp_completion_callback());
215 return true;
218 Plugin::Plugin(PP_Instance pp_instance)
219 : pp::Instance(pp_instance),
220 main_subprocess_("main subprocess", NULL, NULL),
221 uses_nonsfi_mode_(false),
222 wrapper_factory_(NULL),
223 nacl_interface_(NULL),
224 uma_interface_(this) {
225 callback_factory_.Initialize(this);
226 nacl_interface_ = GetNaClInterface();
227 CHECK(nacl_interface_ != NULL);
229 // Notify PPB_NaCl_Private that the instance is created before altering any
230 // state that it tracks.
231 nacl_interface_->InstanceCreated(pp_instance);
232 nexe_file_info_ = kInvalidNaClFileInfo;
235 Plugin::~Plugin() {
236 // Destroy the coordinator while the rest of the data is still there
237 pnacl_coordinator_.reset(NULL);
239 nacl_interface_->InstanceDestroyed(pp_instance());
241 // ShutDownSubprocesses shuts down the main subprocess, which shuts
242 // down the main ServiceRuntime object, which kills the subprocess.
243 // As a side effect of the subprocess being killed, the reverse
244 // services thread(s) will get EOF on the reverse channel(s), and
245 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
246 // ReverseService::WaitForServiceThreadsToExit(), so that there will
247 // not be an extent thread(s) hanging around. This means that the
248 // ~Plugin will block until this happens. This is a requirement,
249 // since the renderer should be free to unload the plugin code, and
250 // we cannot have threads running code that gets unloaded before
251 // they exit.
253 // By waiting for the threads here, we also ensure that the Plugin
254 // object and the subprocess and ServiceRuntime objects is not
255 // (fully) destroyed while the threads are running, so resources
256 // that are destroyed after ShutDownSubprocesses (below) are
257 // guaranteed to be live and valid for access from the service
258 // threads.
260 // The main_subprocess object, which wraps the main service_runtime
261 // object, is dtor'd implicitly after the explicit code below runs,
262 // so the main service runtime object will not have been dtor'd,
263 // though the Shutdown method may have been called, during the
264 // lifetime of the service threads.
265 ShutDownSubprocesses();
267 delete wrapper_factory_;
270 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
271 // We don't know if the plugin will handle the document load, but return
272 // true in order to give it a chance to respond once the proxy is started.
273 return true;
276 void Plugin::NexeFileDidOpen(int32_t pp_error) {
277 if (pp_error != PP_OK)
278 return;
279 LoadNaClModule(
280 nexe_file_info_,
281 uses_nonsfi_mode_,
282 PP_NATIVE_NACL_PROCESS_TYPE);
285 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
286 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
287 pp_error));
288 if (pp_error != PP_OK) {
289 // Error should have been reported by pnacl. Just return.
290 return;
293 // Inform JavaScript that we successfully translated the bitcode to a nexe.
294 PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
296 PP_NaClFileInfo info;
297 info.handle = handle;
298 info.token_lo = 0;
299 info.token_hi = 0;
300 LoadNaClModule(
301 info,
302 false, /* uses_nonsfi_mode */
303 PP_PNACL_PROCESS_TYPE);
306 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
307 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
308 NACL_PRId32 ")\n", pp_error));
309 if (pp_error != PP_OK)
310 return;
312 PP_Var pp_program_url;
313 PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, PP_FALSE, 2};
314 PP_Bool uses_nonsfi_mode;
315 if (nacl_interface_->GetManifestProgramURL(
316 pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
317 std::string program_url = pp::Var(pp::PASS_REF, pp_program_url).AsString();
318 // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
319 // this function.
320 nacl_interface_->ProcessNaClManifest(pp_instance(), program_url.c_str());
321 uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
322 if (pnacl_options.translate) {
323 pp::CompletionCallback translate_callback =
324 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
325 pnacl_coordinator_.reset(
326 PnaclCoordinator::BitcodeToNative(this,
327 program_url,
328 pnacl_options,
329 translate_callback));
330 return;
331 } else {
332 pp::CompletionCallback open_callback =
333 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
334 // Will always call the callback on success or failure.
335 nacl_interface_->DownloadNexe(pp_instance(),
336 program_url.c_str(),
337 &nexe_file_info_,
338 open_callback.pp_completion_callback());
339 return;
344 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
345 nacl_interface_->ReportLoadError(pp_instance(),
346 error_info.error_code(),
347 error_info.message().c_str());
350 } // namespace plugin