• 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 "ppapi/native_client/src/trusted/plugin/plugin.h"
6 
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 
10 #include <string>
11 
12 #include "native_client/src/include/nacl_base.h"
13 #include "native_client/src/include/nacl_macros.h"
14 #include "native_client/src/include/nacl_scoped_ptr.h"
15 #include "native_client/src/include/nacl_string.h"
16 #include "native_client/src/include/portability.h"
17 #include "native_client/src/include/portability_io.h"
18 #include "native_client/src/include/portability_string.h"
19 #include "native_client/src/public/nacl_file_info.h"
20 #include "native_client/src/shared/platform/nacl_check.h"
21 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
22 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
23 #include "native_client/src/trusted/service_runtime/nacl_error_code.h"
24 
25 #include "ppapi/c/pp_errors.h"
26 #include "ppapi/c/private/ppb_nacl_private.h"
27 #include "ppapi/cpp/dev/url_util_dev.h"
28 #include "ppapi/cpp/module.h"
29 
30 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
31 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
32 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
33 #include "ppapi/native_client/src/trusted/plugin/utility.h"
34 
35 namespace plugin {
36 
37 namespace {
38 
39 // Up to 20 seconds
40 const int64_t kTimeSmallMin = 1;         // in ms
41 const int64_t kTimeSmallMax = 20000;     // in ms
42 const uint32_t kTimeSmallBuckets = 100;
43 
44 }  // namespace
45 
ShutDownSubprocesses()46 void Plugin::ShutDownSubprocesses() {
47   PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
48                  static_cast<void*>(this)));
49   PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
50                  main_subprocess_.detailed_description().c_str()));
51 
52   // Shut down service runtime. This must be done before all other calls so
53   // they don't block forever when waiting for the upcall thread to exit.
54   main_subprocess_.Shutdown();
55 
56   PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
57                  static_cast<void*>(this)));
58 }
59 
HistogramTimeSmall(const std::string & name,int64_t ms)60 void Plugin::HistogramTimeSmall(const std::string& name,
61                                 int64_t ms) {
62   if (ms < 0) return;
63   uma_interface_.HistogramCustomTimes(name,
64                                       ms,
65                                       kTimeSmallMin, kTimeSmallMax,
66                                       kTimeSmallBuckets);
67 }
68 
LoadHelperNaClModule(PP_FileHandle file_handle,NaClSubprocess * subprocess,const SelLdrStartParams & params)69 bool Plugin::LoadHelperNaClModule(PP_FileHandle file_handle,
70                                   NaClSubprocess* subprocess,
71                                   const SelLdrStartParams& params) {
72   CHECK(!pp::Module::Get()->core()->IsMainThread());
73   ServiceRuntime* service_runtime =
74       new ServiceRuntime(this,
75                          false,  // No main_service_runtime.
76                          false,  // No non-SFI mode (i.e. in SFI-mode).
77                          pp::BlockUntilComplete(), pp::BlockUntilComplete());
78   subprocess->set_service_runtime(service_runtime);
79   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
80                  "(service_runtime=%p)\n",
81                  static_cast<void*>(service_runtime)));
82 
83   // Now start the SelLdr instance.  This must be created on the main thread.
84   bool service_runtime_started = false;
85   pp::CompletionCallback sel_ldr_callback =
86       callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
87                                     &service_runtime_started,
88                                     service_runtime);
89   pp::CompletionCallback callback =
90       callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
91                                     service_runtime, params,
92                                     sel_ldr_callback);
93   pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
94   if (!service_runtime->WaitForSelLdrStart()) {
95     PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
96                    "WaitForSelLdrStart timed out!\n"));
97     return false;
98   }
99   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
100                  service_runtime_started));
101   if (!service_runtime_started)
102     return false;
103 
104   PP_NaClFileInfo info;
105   info.handle = file_handle;
106   info.token_lo = 0;
107   info.token_hi = 0;
108 
109   // Now actually load the nexe, which can happen on a background thread.
110   //
111   // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
112   // have to roll our own blocking logic, similar to WaitForSelLdrStart()
113   // above, except without timeout logic.
114   bool nexe_started = false;
115   pp::CompletionCallback nexe_started_callback = callback_factory_.NewCallback(
116       &Plugin::SignalNexeStarted, &nexe_started, service_runtime);
117   pp::Module::Get()->core()->CallOnMainThread(
118       0,
119       callback_factory_.NewCallback(
120           &Plugin::LoadNexeAndStart,
121           service_runtime, info, nexe_started_callback));
122   service_runtime->WaitForNexeStart();
123 
124   return nexe_started;
125 }
126 
StartSelLdrOnMainThread(int32_t pp_error,ServiceRuntime * service_runtime,const SelLdrStartParams & params,pp::CompletionCallback callback)127 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
128                                      ServiceRuntime* service_runtime,
129                                      const SelLdrStartParams& params,
130                                      pp::CompletionCallback callback) {
131   if (pp_error != PP_OK) {
132     PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
133                    "-- SHOULD NOT HAPPEN\n"));
134     pp::Module::Get()->core()->CallOnMainThread(0, callback, pp_error);
135     return;
136   }
137   service_runtime->StartSelLdr(params, callback);
138 }
139 
SignalStartSelLdrDone(int32_t pp_error,bool * started,ServiceRuntime * service_runtime)140 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
141                                    bool* started,
142                                    ServiceRuntime* service_runtime) {
143   *started = (pp_error == PP_OK);
144   service_runtime->SignalStartSelLdrDone();
145 }
146 
SignalNexeStarted(int32_t pp_error,bool * started,ServiceRuntime * service_runtime)147 void Plugin::SignalNexeStarted(int32_t pp_error,
148                                bool* started,
149                                ServiceRuntime* service_runtime) {
150   *started = (pp_error == PP_OK);
151   service_runtime->SignalNexeStarted();
152 }
153 
LoadNaClModule(PP_NaClFileInfo file_info,bool uses_nonsfi_mode,bool enable_dyncode_syscalls,bool enable_exception_handling,bool enable_crash_throttling,const pp::CompletionCallback & init_done_cb,const pp::CompletionCallback & crash_cb)154 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
155                             bool uses_nonsfi_mode,
156                             bool enable_dyncode_syscalls,
157                             bool enable_exception_handling,
158                             bool enable_crash_throttling,
159                             const pp::CompletionCallback& init_done_cb,
160                             const pp::CompletionCallback& crash_cb) {
161   CHECK(pp::Module::Get()->core()->IsMainThread());
162   // Before forking a new sel_ldr process, ensure that we do not leak
163   // the ServiceRuntime object for an existing subprocess, and that any
164   // associated listener threads do not go unjoined because if they
165   // outlive the Plugin object, they will not be memory safe.
166   ShutDownSubprocesses();
167   pp::Var manifest_base_url =
168       pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
169   std::string manifest_base_url_str = manifest_base_url.AsString();
170   SelLdrStartParams params(manifest_base_url_str,
171                            true /* uses_irt */,
172                            true /* uses_ppapi */,
173                            enable_dyncode_syscalls,
174                            enable_exception_handling,
175                            enable_crash_throttling);
176   ErrorInfo error_info;
177   ServiceRuntime* service_runtime = new ServiceRuntime(
178       this, true, uses_nonsfi_mode, init_done_cb, crash_cb);
179   main_subprocess_.set_service_runtime(service_runtime);
180   PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n",
181                  static_cast<void*>(service_runtime)));
182   if (NULL == service_runtime) {
183     error_info.SetReport(
184         PP_NACL_ERROR_SEL_LDR_INIT,
185         "sel_ldr init failure " + main_subprocess_.description());
186     ReportLoadError(error_info);
187     return;
188   }
189 
190   // We don't take any action once nexe loading has completed, so pass an empty
191   // callback here for |callback|.
192   pp::CompletionCallback callback = callback_factory_.NewCallback(
193       &Plugin::LoadNexeAndStart,
194       service_runtime, file_info, pp::CompletionCallback());
195   StartSelLdrOnMainThread(
196       static_cast<int32_t>(PP_OK), service_runtime, params, callback);
197 }
198 
LoadNexeAndStart(int32_t pp_error,ServiceRuntime * service_runtime,PP_NaClFileInfo file_info,const pp::CompletionCallback & callback)199 void Plugin::LoadNexeAndStart(int32_t pp_error,
200                               ServiceRuntime* service_runtime,
201                               PP_NaClFileInfo file_info,
202                               const pp::CompletionCallback& callback) {
203   CHECK(pp::Module::Get()->core()->IsMainThread());
204   if (pp_error != PP_OK)
205     return;
206   service_runtime->LoadNexeAndStart(file_info, callback);
207 }
208 
LoadNaClModuleContinuationIntern()209 bool Plugin::LoadNaClModuleContinuationIntern() {
210   ErrorInfo error_info;
211   if (!uses_nonsfi_mode_) {
212     if (!main_subprocess_.StartSrpcServices()) {
213       // The NaCl process probably crashed. On Linux, a crash causes this
214       // error, while on other platforms, the error is detected below, when we
215       // attempt to start the proxy. Report a module initialization error here,
216       // to make it less confusing for developers.
217       NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
218               "StartSrpcServices failed\n");
219       error_info.SetReport(PP_NACL_ERROR_START_PROXY_MODULE,
220                            "could not initialize module.");
221       ReportLoadError(error_info);
222       return false;
223     }
224   }
225 
226   bool result = PP_ToBool(nacl_interface_->StartPpapiProxy(pp_instance()));
227   if (result) {
228     PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
229                    main_subprocess_.detailed_description().c_str()));
230   }
231   return result;
232 }
233 
LoadHelperNaClModule(const nacl::string & helper_url,PP_FileHandle file_handle,ErrorInfo * error_info)234 NaClSubprocess* Plugin::LoadHelperNaClModule(const nacl::string& helper_url,
235                                              PP_FileHandle file_handle,
236                                              ErrorInfo* error_info) {
237   nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
238       new NaClSubprocess("helper module", NULL, NULL));
239   if (NULL == nacl_subprocess.get()) {
240     error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
241                           "unable to allocate helper subprocess.");
242     return NULL;
243   }
244 
245   // Do not report UMA stats for translator-related nexes.
246   // TODO(sehr): define new UMA stats for translator related nexe events.
247   // NOTE: The PNaCl translator nexes are not built to use the IRT.  This is
248   // done to save on address space and swap space.
249   SelLdrStartParams params(helper_url,
250                            false /* uses_irt */,
251                            false /* uses_ppapi */,
252                            false /* enable_dyncode_syscalls */,
253                            false /* enable_exception_handling */,
254                            true /* enable_crash_throttling */);
255 
256   // Helper NaCl modules always use the PNaCl manifest, as there is no
257   // corresponding NMF.
258   if (!LoadHelperNaClModule(file_handle, nacl_subprocess.get(), params)) {
259     return NULL;
260   }
261   // We need not wait for the init_done callback.  We can block
262   // here in StartSrpcServices, since helper NaCl modules
263   // are spawned from a private thread.
264   //
265   // TODO(bsy): if helper module crashes, we should abort.
266   // crash_cb is not used here, so we are relying on crashes
267   // being detected in StartSrpcServices or later.
268   //
269   // NB: More refactoring might be needed, however, if helper
270   // NaCl modules have their own manifest.  Currently the
271   // manifest is a per-plugin-instance object, not a per
272   // NaClSubprocess object.
273   if (!nacl_subprocess->StartSrpcServices()) {
274     error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
275                           "SRPC connection failure for " +
276                           nacl_subprocess->description());
277     return NULL;
278   }
279 
280   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
281                  helper_url.c_str(),
282                  nacl_subprocess.get()->detailed_description().c_str()));
283 
284   return nacl_subprocess.release();
285 }
286 
287 // All failures of this function will show up as "Missing Plugin-in", so
288 // there is no need to log to JS console that there was an initialization
289 // failure. Note that module loading functions will log their own errors.
Init(uint32_t argc,const char * argn[],const char * argv[])290 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
291   PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
292   nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
293   wrapper_factory_ = new nacl::DescWrapperFactory();
294   pp::CompletionCallback open_cb =
295       callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
296   nacl_interface_->RequestNaClManifest(pp_instance(),
297                                        open_cb.pp_completion_callback());
298   return true;
299 }
300 
Plugin(PP_Instance pp_instance)301 Plugin::Plugin(PP_Instance pp_instance)
302     : pp::Instance(pp_instance),
303       main_subprocess_("main subprocess", NULL, NULL),
304       uses_nonsfi_mode_(false),
305       wrapper_factory_(NULL),
306       nacl_interface_(NULL),
307       uma_interface_(this) {
308   PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
309                  NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
310   callback_factory_.Initialize(this);
311   nacl_interface_ = GetNaClInterface();
312   CHECK(nacl_interface_ != NULL);
313 
314   // Notify PPB_NaCl_Private that the instance is created before altering any
315   // state that it tracks.
316   nacl_interface_->InstanceCreated(pp_instance);
317   // We call set_exit_status() here to ensure that the 'exitStatus' property is
318   // set. This can only be called when nacl_interface_ is not NULL.
319   set_exit_status(-1);
320   nexe_file_info_.handle = PP_kInvalidFileHandle;
321   nexe_file_info_.token_lo = 0;
322   nexe_file_info_.token_hi = 0;
323 }
324 
325 
~Plugin()326 Plugin::~Plugin() {
327   int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
328 
329   PLUGIN_PRINTF(("Plugin::~Plugin (this=%p)\n",
330                  static_cast<void*>(this)));
331   // Destroy the coordinator while the rest of the data is still there
332   pnacl_coordinator_.reset(NULL);
333 
334   nacl_interface_->InstanceDestroyed(pp_instance());
335 
336   // ShutDownSubprocesses shuts down the main subprocess, which shuts
337   // down the main ServiceRuntime object, which kills the subprocess.
338   // As a side effect of the subprocess being killed, the reverse
339   // services thread(s) will get EOF on the reverse channel(s), and
340   // the thread(s) will exit.  In ServiceRuntime::Shutdown, we invoke
341   // ReverseService::WaitForServiceThreadsToExit(), so that there will
342   // not be an extent thread(s) hanging around.  This means that the
343   // ~Plugin will block until this happens.  This is a requirement,
344   // since the renderer should be free to unload the plugin code, and
345   // we cannot have threads running code that gets unloaded before
346   // they exit.
347   //
348   // By waiting for the threads here, we also ensure that the Plugin
349   // object and the subprocess and ServiceRuntime objects is not
350   // (fully) destroyed while the threads are running, so resources
351   // that are destroyed after ShutDownSubprocesses (below) are
352   // guaranteed to be live and valid for access from the service
353   // threads.
354   //
355   // The main_subprocess object, which wraps the main service_runtime
356   // object, is dtor'd implicitly after the explicit code below runs,
357   // so the main service runtime object will not have been dtor'd,
358   // though the Shutdown method may have been called, during the
359   // lifetime of the service threads.
360   ShutDownSubprocesses();
361 
362   delete wrapper_factory_;
363 
364   HistogramTimeSmall(
365       "NaCl.Perf.ShutdownTime.Total",
366       (NaClGetTimeOfDayMicroseconds() - shutdown_start)
367           / NACL_MICROS_PER_MILLI);
368 
369   PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
370                  static_cast<void*>(this)));
371 }
372 
HandleDocumentLoad(const pp::URLLoader & url_loader)373 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
374   PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
375                  static_cast<void*>(this)));
376   // We don't know if the plugin will handle the document load, but return
377   // true in order to give it a chance to respond once the proxy is started.
378   return true;
379 }
380 
NexeFileDidOpen(int32_t pp_error)381 void Plugin::NexeFileDidOpen(int32_t pp_error) {
382   if (pp_error != PP_OK)
383     return;
384 
385   NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
386   LoadNaClModule(
387       nexe_file_info_,
388       uses_nonsfi_mode_,
389       true, /* enable_dyncode_syscalls */
390       true, /* enable_exception_handling */
391       false, /* enable_crash_throttling */
392       callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
393       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
394 }
395 
NexeFileDidOpenContinuation(int32_t pp_error)396 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
397   UNREFERENCED_PARAMETER(pp_error);
398   NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
399   if (LoadNaClModuleContinuationIntern()) {
400     NaClLog(4, "NexeFileDidOpenContinuation: success;"
401             " setting histograms\n");
402     int64_t nexe_size = nacl_interface_->GetNexeSize(pp_instance());
403     nacl_interface_->ReportLoadSuccess(
404         pp_instance(), program_url_.c_str(), nexe_size, nexe_size);
405   } else {
406     NaClLog(4, "NexeFileDidOpenContinuation: failed.");
407   }
408   NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
409 }
410 
NexeDidCrash(int32_t pp_error)411 void Plugin::NexeDidCrash(int32_t pp_error) {
412   PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
413                  pp_error));
414   if (pp_error != PP_OK) {
415     PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
416                    " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
417   }
418 
419   std::string crash_log =
420       main_subprocess_.service_runtime()->GetCrashLogOutput();
421   nacl_interface_->NexeDidCrash(pp_instance(), crash_log.c_str());
422 }
423 
BitcodeDidTranslate(int32_t pp_error)424 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
425   PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
426                  pp_error));
427   if (pp_error != PP_OK) {
428     // Error should have been reported by pnacl. Just return.
429     PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
430     return;
431   }
432 
433   // Inform JavaScript that we successfully translated the bitcode to a nexe.
434   PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
435 
436   PP_NaClFileInfo info;
437   info.handle = handle;
438   info.token_lo = 0;
439   info.token_hi = 0;
440   LoadNaClModule(
441       info,
442       false, /* uses_nonsfi_mode */
443       false, /* enable_dyncode_syscalls */
444       false, /* enable_exception_handling */
445       true, /* enable_crash_throttling */
446       callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
447       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
448 }
449 
BitcodeDidTranslateContinuation(int32_t pp_error)450 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
451   NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
452   UNREFERENCED_PARAMETER(pp_error);
453   if (LoadNaClModuleContinuationIntern()) {
454     int64_t loaded;
455     int64_t total;
456     // TODO(teravest): Tighten this up so we can get rid of
457     // GetCurrentProgress(). loaded should always equal total.
458     pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
459     nacl_interface_->ReportLoadSuccess(
460         pp_instance(), program_url_.c_str(), loaded, total);
461   }
462 }
463 
NaClManifestFileDidOpen(int32_t pp_error)464 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
465   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
466                  NACL_PRId32 ")\n", pp_error));
467   if (pp_error != PP_OK)
468     return;
469 
470   PP_Var pp_program_url;
471   PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2};
472   PP_Bool uses_nonsfi_mode;
473   if (nacl_interface_->GetManifestProgramURL(
474           pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
475     program_url_ = pp::Var(pp::PASS_REF, pp_program_url).AsString();
476     // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
477     // this function.
478     nacl_interface_->ProcessNaClManifest(pp_instance(), program_url_.c_str());
479     uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
480     if (pnacl_options.translate) {
481       pp::CompletionCallback translate_callback =
482           callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
483       pnacl_coordinator_.reset(
484           PnaclCoordinator::BitcodeToNative(this,
485                                             program_url_,
486                                             pnacl_options,
487                                             translate_callback));
488       return;
489     } else {
490       pp::CompletionCallback open_callback =
491           callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
492       // Will always call the callback on success or failure.
493       nacl_interface_->DownloadNexe(pp_instance(),
494                                     program_url_.c_str(),
495                                     &nexe_file_info_,
496                                     open_callback.pp_completion_callback());
497       return;
498     }
499   }
500 }
501 
ReportLoadError(const ErrorInfo & error_info)502 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
503   nacl_interface_->ReportLoadError(pp_instance(),
504                                    error_info.error_code(),
505                                    error_info.message().c_str());
506 }
507 
DocumentCanRequest(const std::string & url)508 bool Plugin::DocumentCanRequest(const std::string& url) {
509   CHECK(pp::Module::Get()->core()->IsMainThread());
510   CHECK(pp::URLUtil_Dev::Get() != NULL);
511   return pp::URLUtil_Dev::Get()->DocumentCanRequest(this, pp::Var(url));
512 }
513 
set_exit_status(int exit_status)514 void Plugin::set_exit_status(int exit_status) {
515   pp::Core* core = pp::Module::Get()->core();
516   if (core->IsMainThread()) {
517     SetExitStatusOnMainThread(PP_OK, exit_status);
518   } else {
519     pp::CompletionCallback callback =
520         callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
521                                       exit_status);
522     core->CallOnMainThread(0, callback, 0);
523   }
524 }
525 
SetExitStatusOnMainThread(int32_t pp_error,int exit_status)526 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
527                                        int exit_status) {
528   DCHECK(pp::Module::Get()->core()->IsMainThread());
529   DCHECK(nacl_interface_);
530   nacl_interface_->SetExitStatus(pp_instance(), exit_status);
531 }
532 
533 
534 }  // namespace plugin
535