3309d65ecd6cacf757ce1594a9ec5f5536408587
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / plugin.cc
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 #ifdef _MSC_VER
6 // Do not warn about use of std::copy with raw pointers.
7 #pragma warning(disable : 4996)
8 #endif
9
10 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
11
12 #include <sys/stat.h>
13 #include <sys/types.h>
14
15 #include <algorithm>
16 #include <deque>
17 #include <string>
18 #include <vector>
19
20 #include "native_client/src/include/nacl_base.h"
21 #include "native_client/src/include/nacl_macros.h"
22 #include "native_client/src/include/nacl_scoped_ptr.h"
23 #include "native_client/src/include/nacl_string.h"
24 #include "native_client/src/include/portability.h"
25 #include "native_client/src/include/portability_io.h"
26 #include "native_client/src/include/portability_string.h"
27 #include "native_client/src/shared/platform/nacl_check.h"
28 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
29 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
30 #include "native_client/src/trusted/service_runtime/nacl_error_code.h"
31
32 #include "ppapi/c/pp_errors.h"
33 #include "ppapi/c/ppb_console.h"
34 #include "ppapi/c/ppb_var.h"
35 #include "ppapi/c/ppp_instance.h"
36 #include "ppapi/c/private/ppb_nacl_private.h"
37 #include "ppapi/cpp/dev/url_util_dev.h"
38 #include "ppapi/cpp/module.h"
39 #include "ppapi/cpp/text_input_controller.h"
40
41 #include "ppapi/native_client/src/trusted/plugin/file_utils.h"
42 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h"
43 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
44 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
45 #include "ppapi/native_client/src/trusted/plugin/nexe_arch.h"
46 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
47 #include "ppapi/native_client/src/trusted/plugin/scriptable_plugin.h"
48 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
49 #include "ppapi/native_client/src/trusted/plugin/utility.h"
50
51 namespace plugin {
52
53 namespace {
54
55 const char* const kTypeAttribute = "type";
56 // The "src" attribute of the <embed> tag.  The value is expected to be either
57 // a URL or URI pointing to the manifest file (which is expected to contain
58 // JSON matching ISAs with .nexe URLs).
59 const char* const kSrcManifestAttribute = "src";
60 // The "nacl" attribute of the <embed> tag.  We use the value of this attribute
61 // to find the manifest file when NaCl is registered as a plug-in for another
62 // MIME type because the "src" attribute is used to supply us with the resource
63 // of that MIME type that we're supposed to display.
64 const char* const kNaClManifestAttribute = "nacl";
65 // The pseudo-ISA used to indicate portable native client.
66 const char* const kPortableISA = "portable";
67 // This is a pretty arbitrary limit on the byte size of the NaCl manfest file.
68 // Note that the resulting string object has to have at least one byte extra
69 // for the null termination character.
70 const size_t kNaClManifestMaxFileBytes = 1024 * 1024;
71
72 // Define an argument name to enable 'dev' interfaces. To make sure it doesn't
73 // collide with any user-defined HTML attribute, make the first character '@'.
74 const char* const kDevAttribute = "@dev";
75
76 // URL schemes that we treat in special ways.
77 const char* const kChromeExtensionUriScheme = "chrome-extension";
78 const char* const kDataUriScheme = "data";
79
80 // Up to 20 seconds
81 const int64_t kTimeSmallMin = 1;         // in ms
82 const int64_t kTimeSmallMax = 20000;     // in ms
83 const uint32_t kTimeSmallBuckets = 100;
84
85 // Up to 3 minutes, 20 seconds
86 const int64_t kTimeMediumMin = 10;         // in ms
87 const int64_t kTimeMediumMax = 200000;     // in ms
88 const uint32_t kTimeMediumBuckets = 100;
89
90 // Up to 33 minutes.
91 const int64_t kTimeLargeMin = 100;         // in ms
92 const int64_t kTimeLargeMax = 2000000;     // in ms
93 const uint32_t kTimeLargeBuckets = 100;
94
95 const int64_t kSizeKBMin = 1;
96 const int64_t kSizeKBMax = 512*1024;     // very large .nexe
97 const uint32_t kSizeKBBuckets = 100;
98
99 const PPB_NaCl_Private* GetNaClInterface() {
100   pp::Module *module = pp::Module::Get();
101   CHECK(module);
102   return static_cast<const PPB_NaCl_Private*>(
103       module->GetBrowserInterface(PPB_NACL_PRIVATE_INTERFACE));
104 }
105
106 }  // namespace
107
108 bool Plugin::EarlyInit(int argc, const char* argn[], const char* argv[]) {
109   PLUGIN_PRINTF(("Plugin::EarlyInit (instance=%p)\n",
110                  static_cast<void*>(this)));
111
112 #ifdef NACL_OSX
113   // TODO(kochi): For crbug.com/102808, this is a stopgap solution for Lion
114   // until we expose IME API to .nexe. This disables any IME interference
115   // against key inputs, so you cannot use off-the-spot IME input for NaCl apps.
116   // This makes discrepancy among platforms and therefore we should remove
117   // this hack when IME API is made available.
118   // The default for non-Mac platforms is still off-the-spot IME mode.
119   pp::TextInputController(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
120 #endif
121
122   // Remember the embed/object argn/argv pairs.
123   argn_ = new char*[argc];
124   argv_ = new char*[argc];
125   argc_ = 0;
126   for (int i = 0; i < argc; ++i) {
127     if (NULL != argn_ && NULL != argv_) {
128       argn_[argc_] = strdup(argn[i]);
129       argv_[argc_] = strdup(argv[i]);
130       if (NULL == argn_[argc_] || NULL == argv_[argc_]) {
131         // Give up on passing arguments.
132         free(argn_[argc_]);
133         free(argv_[argc_]);
134         continue;
135       }
136       ++argc_;
137     }
138   }
139   // TODO(sehr): this leaks strings if there is a subsequent failure.
140
141   // Set up the factory used to produce DescWrappers.
142   wrapper_factory_ = new nacl::DescWrapperFactory();
143   if (NULL == wrapper_factory_) {
144     return false;
145   }
146   PLUGIN_PRINTF(("Plugin::Init (wrapper_factory=%p)\n",
147                  static_cast<void*>(wrapper_factory_)));
148
149   PLUGIN_PRINTF(("Plugin::Init (return 1)\n"));
150   // Return success.
151   return true;
152 }
153
154 void Plugin::ShutDownSubprocesses() {
155   PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
156                  static_cast<void*>(this)));
157   PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
158                  main_subprocess_.detailed_description().c_str()));
159
160   // Shut down service runtime. This must be done before all other calls so
161   // they don't block forever when waiting for the upcall thread to exit.
162   main_subprocess_.Shutdown();
163
164   PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
165                  static_cast<void*>(this)));
166 }
167
168 void Plugin::HistogramTimeSmall(const std::string& name,
169                                 int64_t ms) {
170   if (ms < 0) return;
171   uma_interface_.HistogramCustomTimes(name,
172                                       ms,
173                                       kTimeSmallMin, kTimeSmallMax,
174                                       kTimeSmallBuckets);
175 }
176
177 void Plugin::HistogramTimeMedium(const std::string& name,
178                                  int64_t ms) {
179   if (ms < 0) return;
180   uma_interface_.HistogramCustomTimes(name,
181                                       ms,
182                                       kTimeMediumMin, kTimeMediumMax,
183                                       kTimeMediumBuckets);
184 }
185
186 void Plugin::HistogramTimeLarge(const std::string& name,
187                                 int64_t ms) {
188   if (ms < 0) return;
189   uma_interface_.HistogramCustomTimes(name,
190                                       ms,
191                                       kTimeLargeMin, kTimeLargeMax,
192                                       kTimeLargeBuckets);
193 }
194
195 void Plugin::HistogramSizeKB(const std::string& name,
196                              int32_t sample) {
197   if (sample < 0) return;
198   uma_interface_.HistogramCustomCounts(name,
199                                        sample,
200                                        kSizeKBMin, kSizeKBMax,
201                                        kSizeKBBuckets);
202 }
203
204 void Plugin::HistogramEnumerate(const std::string& name,
205                                 int sample,
206                                 int maximum,
207                                 int out_of_range_replacement) {
208   if (sample < 0 || sample >= maximum) {
209     if (out_of_range_replacement < 0)
210       // No replacement for bad input, abort.
211       return;
212     else
213       // Use a specific value to signal a bad input.
214       sample = out_of_range_replacement;
215   }
216   uma_interface_.HistogramEnumeration(name, sample, maximum);
217 }
218
219 void Plugin::HistogramEnumerateOsArch(const std::string& sandbox_isa) {
220   enum NaClOSArch {
221     kNaClLinux32 = 0,
222     kNaClLinux64,
223     kNaClLinuxArm,
224     kNaClMac32,
225     kNaClMac64,
226     kNaClMacArm,
227     kNaClWin32,
228     kNaClWin64,
229     kNaClWinArm,
230     kNaClOSArchMax
231   };
232
233   NaClOSArch os_arch = kNaClOSArchMax;
234 #if NACL_LINUX
235   os_arch = kNaClLinux32;
236 #elif NACL_OSX
237   os_arch = kNaClMac32;
238 #elif NACL_WINDOWS
239   os_arch = kNaClWin32;
240 #endif
241
242   if (sandbox_isa == "x86-64")
243     os_arch = static_cast<NaClOSArch>(os_arch + 1);
244   if (sandbox_isa == "arm")
245     os_arch = static_cast<NaClOSArch>(os_arch + 2);
246
247   HistogramEnumerate("NaCl.Client.OSArch", os_arch, kNaClOSArchMax, -1);
248 }
249
250 void Plugin::HistogramEnumerateLoadStatus(PluginErrorCode error_code,
251                                           bool is_installed) {
252   HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code, ERROR_MAX,
253                      ERROR_UNKNOWN);
254
255   // Gather data to see if being installed changes load outcomes.
256   const char* name = is_installed ? "NaCl.LoadStatus.Plugin.InstalledApp" :
257       "NaCl.LoadStatus.Plugin.NotInstalledApp";
258   HistogramEnumerate(name, error_code, ERROR_MAX, ERROR_UNKNOWN);
259 }
260
261 void Plugin::HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code,
262                                                 bool is_installed) {
263   HistogramEnumerate("NaCl.LoadStatus.SelLdr", error_code,
264                      NACL_ERROR_CODE_MAX, LOAD_STATUS_UNKNOWN);
265
266   // Gather data to see if being installed changes load outcomes.
267   const char* name = is_installed ? "NaCl.LoadStatus.SelLdr.InstalledApp" :
268       "NaCl.LoadStatus.SelLdr.NotInstalledApp";
269   HistogramEnumerate(name, error_code, NACL_ERROR_CODE_MAX,
270                      LOAD_STATUS_UNKNOWN);
271 }
272
273 void Plugin::HistogramEnumerateManifestIsDataURI(bool is_data_uri) {
274   HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1);
275 }
276
277 void Plugin::HistogramHTTPStatusCode(const std::string& name,
278                                      int status) {
279   // Log the status codes in rough buckets - 1XX, 2XX, etc.
280   int sample = status / 100;
281   // HTTP status codes only go up to 5XX, using "6" to indicate an internal
282   // error.
283   // Note: installed files may have "0" for a status code.
284   if (status < 0 || status >= 600)
285     sample = 6;
286   HistogramEnumerate(name, sample, 7, 6);
287 }
288
289 bool Plugin::LoadNaClModuleCommon(nacl::DescWrapper* wrapper,
290                                   NaClSubprocess* subprocess,
291                                   const Manifest* manifest,
292                                   bool should_report_uma,
293                                   const SelLdrStartParams& params,
294                                   const pp::CompletionCallback& init_done_cb,
295                                   const pp::CompletionCallback& crash_cb) {
296   ServiceRuntime* new_service_runtime =
297       new ServiceRuntime(this, manifest, should_report_uma, init_done_cb,
298                          crash_cb);
299   subprocess->set_service_runtime(new_service_runtime);
300   PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime=%p)\n",
301                  static_cast<void*>(new_service_runtime)));
302   if (NULL == new_service_runtime) {
303     params.error_info->SetReport(
304         ERROR_SEL_LDR_INIT,
305         "sel_ldr init failure " + subprocess->description());
306     return false;
307   }
308
309   // Now start the SelLdr instance.  This must be created on the main thread.
310   pp::Core* core = pp::Module::Get()->core();
311   bool service_runtime_started;
312   if (core->IsMainThread()) {
313     StartSelLdrOnMainThread(PP_OK, new_service_runtime, params,
314                             &service_runtime_started);
315   } else {
316     pp::CompletionCallback callback =
317         callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
318                                       new_service_runtime, params,
319                                       &service_runtime_started);
320     core->CallOnMainThread(0, callback, 0);
321     new_service_runtime->WaitForSelLdrStart();
322   }
323   PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime_started=%d)\n",
324                  service_runtime_started));
325   if (!service_runtime_started) {
326     return false;
327   }
328
329   // Now actually load the nexe, which can happen on a background thread.
330   bool nexe_loaded = new_service_runtime->LoadNexeAndStart(wrapper,
331                                                            params.error_info,
332                                                            crash_cb);
333   PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (nexe_loaded=%d)\n",
334                  nexe_loaded));
335   if (!nexe_loaded) {
336     return false;
337   }
338   return true;
339 }
340
341 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
342                                      ServiceRuntime* service_runtime,
343                                      const SelLdrStartParams& params,
344                                      bool* success) {
345   if (pp_error != PP_OK) {
346     PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
347                    "-- SHOULD NOT HAPPEN\n"));
348     *success = false;
349     return;
350   }
351   *success = service_runtime->StartSelLdr(params);
352   // Signal outside of StartSelLdr here, so that the write to *success
353   // is done before signaling.
354   service_runtime->SignalStartSelLdrDone();
355 }
356
357 bool Plugin::LoadNaClModule(nacl::DescWrapper* wrapper,
358                             ErrorInfo* error_info,
359                             bool enable_dyncode_syscalls,
360                             bool enable_exception_handling,
361                             bool enable_crash_throttling,
362                             const pp::CompletionCallback& init_done_cb,
363                             const pp::CompletionCallback& crash_cb) {
364   // Before forking a new sel_ldr process, ensure that we do not leak
365   // the ServiceRuntime object for an existing subprocess, and that any
366   // associated listener threads do not go unjoined because if they
367   // outlive the Plugin object, they will not be memory safe.
368   ShutDownSubprocesses();
369   SelLdrStartParams params(manifest_base_url(),
370                            error_info,
371                            true /* uses_irt */,
372                            true /* uses_ppapi */,
373                            enable_dev_interfaces_,
374                            enable_dyncode_syscalls,
375                            enable_exception_handling,
376                            enable_crash_throttling);
377   if (!LoadNaClModuleCommon(wrapper, &main_subprocess_, manifest_.get(),
378                             true /* should_report_uma */,
379                             params, init_done_cb, crash_cb)) {
380     return false;
381   }
382   PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
383                  main_subprocess_.detailed_description().c_str()));
384   return true;
385 }
386
387 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) {
388   if (!main_subprocess_.StartSrpcServices()) {
389     // The NaCl process probably crashed. On Linux, a crash causes this error,
390     // while on other platforms, the error is detected below, when we attempt to
391     // start the proxy. Report a module initialization error here, to make it
392     // less confusing for developers.
393     NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
394             "StartSrpcServices failed\n");
395     error_info->SetReport(ERROR_START_PROXY_MODULE,
396                           "could not initialize module.");
397     return false;
398   }
399   PP_ExternalPluginResult ipc_result =
400       nacl_interface_->StartPpapiProxy(pp_instance());
401   if (ipc_result == PP_EXTERNAL_PLUGIN_OK) {
402     // Log the amound of time that has passed between the trusted plugin being
403     // initialized and the untrusted plugin being initialized.  This is
404     // (roughly) the cost of using NaCl, in terms of startup time.
405     HistogramStartupTimeMedium(
406         "NaCl.Perf.StartupTime.NaClOverhead",
407         static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_)
408             / NACL_MICROS_PER_MILLI);
409   } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
410     NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
411             "Got PP_EXTERNAL_PLUGIN_ERROR_MODULE\n");
412     error_info->SetReport(ERROR_START_PROXY_MODULE,
413                           "could not initialize module.");
414     return false;
415   } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
416     error_info->SetReport(ERROR_START_PROXY_INSTANCE,
417                           "could not create instance.");
418     return false;
419   }
420   PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
421                  main_subprocess_.detailed_description().c_str()));
422   return true;
423 }
424
425 NaClSubprocess* Plugin::LoadHelperNaClModule(nacl::DescWrapper* wrapper,
426                                              const Manifest* manifest,
427                                              ErrorInfo* error_info) {
428   nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
429       new NaClSubprocess("helper module", NULL, NULL));
430   if (NULL == nacl_subprocess.get()) {
431     error_info->SetReport(ERROR_SEL_LDR_INIT,
432                           "unable to allocate helper subprocess.");
433     return NULL;
434   }
435
436   // Do not report UMA stats for translator-related nexes.
437   // TODO(sehr): define new UMA stats for translator related nexe events.
438   // NOTE: The PNaCl translator nexes are not built to use the IRT.  This is
439   // done to save on address space and swap space.
440   // TODO(jvoung): See if we still need the uses_ppapi variable, now that
441   // LaunchSelLdr always happens on the main thread.
442   SelLdrStartParams params(manifest_base_url(),
443                            error_info,
444                            false /* uses_irt */,
445                            false /* uses_ppapi */,
446                            enable_dev_interfaces_,
447                            false /* enable_dyncode_syscalls */,
448                            false /* enable_exception_handling */,
449                            true /* enable_crash_throttling */);
450   if (!LoadNaClModuleCommon(wrapper, nacl_subprocess.get(), manifest,
451                             false /* should_report_uma */,
452                             params,
453                             pp::BlockUntilComplete(),
454                             pp::BlockUntilComplete())) {
455     return NULL;
456   }
457   // We need not wait for the init_done callback.  We can block
458   // here in StartSrpcServices, since helper NaCl modules
459   // are spawned from a private thread.
460   //
461   // TODO(bsy): if helper module crashes, we should abort.
462   // crash_cb is not used here, so we are relying on crashes
463   // being detected in StartSrpcServices or later.
464   //
465   // NB: More refactoring might be needed, however, if helper
466   // NaCl modules have their own manifest.  Currently the
467   // manifest is a per-plugin-instance object, not a per
468   // NaClSubprocess object.
469   if (!nacl_subprocess->StartSrpcServices()) {
470     error_info->SetReport(ERROR_SRPC_CONNECTION_FAIL,
471                           "SRPC connection failure for " +
472                           nacl_subprocess->description());
473     return NULL;
474   }
475
476   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s)\n",
477                  nacl_subprocess.get()->detailed_description().c_str()));
478
479   return nacl_subprocess.release();
480 }
481
482 char* Plugin::LookupArgument(const char* key) {
483   char** keys = argn_;
484   for (int ii = 0, len = argc_; ii < len; ++ii) {
485     if (!strcmp(keys[ii], key)) {
486       return argv_[ii];
487     }
488   }
489   return NULL;
490 }
491
492 class ProgressEvent {
493  public:
494   ProgressEvent(PP_NaClEventType event_type,
495                 const nacl::string& url,
496                 Plugin::LengthComputable length_computable,
497                 uint64_t loaded_bytes,
498                 uint64_t total_bytes) :
499     event_type_(event_type),
500     url_(url),
501     length_computable_(length_computable),
502     loaded_bytes_(loaded_bytes),
503     total_bytes_(total_bytes) { }
504   PP_NaClEventType event_type() const { return event_type_; }
505   const char* url() const { return url_.c_str(); }
506   Plugin::LengthComputable length_computable() const {
507     return length_computable_;
508   }
509   uint64_t loaded_bytes() const { return loaded_bytes_; }
510   uint64_t total_bytes() const { return total_bytes_; }
511
512  private:
513   PP_NaClEventType event_type_;
514   nacl::string url_;
515   Plugin::LengthComputable length_computable_;
516   uint64_t loaded_bytes_;
517   uint64_t total_bytes_;
518 };
519
520 const char* const Plugin::kNaClMIMEType = "application/x-nacl";
521 const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
522
523 bool Plugin::NexeIsContentHandler() const {
524   // Tests if the MIME type is not a NaCl MIME type.
525   // If the MIME type is foreign, then this NEXE is being used as a content
526   // type handler rather than directly by an HTML document.
527   return
528       !mime_type().empty() &&
529       mime_type() != kNaClMIMEType &&
530       mime_type() != kPnaclMIMEType;
531 }
532
533
534 Plugin* Plugin::New(PP_Instance pp_instance) {
535   PLUGIN_PRINTF(("Plugin::New (pp_instance=%" NACL_PRId32 ")\n", pp_instance));
536   Plugin* plugin = new Plugin(pp_instance);
537   PLUGIN_PRINTF(("Plugin::New (plugin=%p)\n", static_cast<void*>(plugin)));
538   return plugin;
539 }
540
541
542 // All failures of this function will show up as "Missing Plugin-in", so
543 // there is no need to log to JS console that there was an initialization
544 // failure. Note that module loading functions will log their own errors.
545 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
546   PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
547   HistogramEnumerateOsArch(GetSandboxISA());
548   init_time_ = NaClGetTimeOfDayMicroseconds();
549
550   ScriptablePlugin* scriptable_plugin = ScriptablePlugin::NewPlugin(this);
551   if (scriptable_plugin == NULL)
552     return false;
553
554   set_scriptable_plugin(scriptable_plugin);
555   PLUGIN_PRINTF(("Plugin::Init (scriptable_handle=%p)\n",
556                  static_cast<void*>(scriptable_plugin_)));
557   url_util_ = pp::URLUtil_Dev::Get();
558   if (url_util_ == NULL)
559     return false;
560
561   PLUGIN_PRINTF(("Plugin::Init (url_util_=%p)\n",
562                  static_cast<const void*>(url_util_)));
563
564   bool status = EarlyInit(static_cast<int>(argc), argn, argv);
565   if (status) {
566     // Look for the developer attribute; if it's present, enable 'dev'
567     // interfaces.
568     const char* dev_settings = LookupArgument(kDevAttribute);
569     enable_dev_interfaces_ = (dev_settings != NULL);
570
571     const char* type_attr = LookupArgument(kTypeAttribute);
572     if (type_attr != NULL) {
573       mime_type_ = nacl::string(type_attr);
574       std::transform(mime_type_.begin(), mime_type_.end(), mime_type_.begin(),
575                      tolower);
576     }
577
578     const char* manifest_url = LookupArgument(kSrcManifestAttribute);
579     if (NexeIsContentHandler()) {
580       // For content handlers 'src' will be the URL for the content
581       // and 'nacl' will be the URL for the manifest.
582       manifest_url = LookupArgument(kNaClManifestAttribute);
583       // For content handlers the NEXE runs in the security context of the
584       // content it is rendering and the NEXE itself appears to be a
585       // cross-origin resource stored in a Chrome extension.
586     }
587     // Use the document URL as the base for resolving relative URLs to find the
588     // manifest.  This takes into account the setting of <base> tags that
589     // precede the embed/object.
590     CHECK(url_util_ != NULL);
591     pp::Var base_var = url_util_->GetDocumentURL(this);
592     if (!base_var.is_string()) {
593       PLUGIN_PRINTF(("Plugin::Init (unable to find document url)\n"));
594       return false;
595     }
596     set_plugin_base_url(base_var.AsString());
597     if (manifest_url == NULL) {
598       // TODO(sehr,polina): this should be a hard error when scripting
599       // the src property is no longer allowed.
600       PLUGIN_PRINTF(("Plugin::Init:"
601                      " WARNING: no 'src' property, so no manifest loaded.\n"));
602       if (NULL != LookupArgument(kNaClManifestAttribute)) {
603         PLUGIN_PRINTF(("Plugin::Init:"
604                        " WARNING: 'nacl' property is incorrect. Use 'src'.\n"));
605       }
606     } else {
607       // Issue a GET for the manifest_url.  The manifest file will be parsed to
608       // determine the nexe URL.
609       // Sets src property to full manifest URL.
610       RequestNaClManifest(manifest_url);
611     }
612   }
613
614   PLUGIN_PRINTF(("Plugin::Init (status=%d)\n", status));
615   return status;
616 }
617
618 Plugin::Plugin(PP_Instance pp_instance)
619     : pp::InstancePrivate(pp_instance),
620       scriptable_plugin_(NULL),
621       argc_(-1),
622       argn_(NULL),
623       argv_(NULL),
624       main_subprocess_("main subprocess", NULL, NULL),
625       nexe_error_reported_(false),
626       wrapper_factory_(NULL),
627       enable_dev_interfaces_(false),
628       is_installed_(false),
629       init_time_(0),
630       ready_time_(0),
631       nexe_size_(0),
632       time_of_last_progress_event_(0),
633       exit_status_(-1),
634       nacl_interface_(NULL),
635       uma_interface_(this) {
636   PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
637                  NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
638   callback_factory_.Initialize(this);
639   nexe_downloader_.Initialize(this);
640   nacl_interface_ = GetNaClInterface();
641   CHECK(nacl_interface_ != NULL);
642   set_nacl_ready_state(UNSENT);
643   set_last_error_string("");
644   // We call set_exit_status() here to ensure that the 'exitStatus' property is
645   // set. This can only be called when nacl_interface_ is not NULL.
646   set_exit_status(-1);
647 }
648
649
650 Plugin::~Plugin() {
651   int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
652
653   PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, scriptable_plugin=%p)\n",
654                  static_cast<void*>(this),
655                  static_cast<void*>(scriptable_plugin())));
656   // Destroy the coordinator while the rest of the data is still there
657   pnacl_coordinator_.reset(NULL);
658
659   if (!nexe_error_reported()) {
660     HistogramTimeLarge(
661         "NaCl.ModuleUptime.Normal",
662         (shutdown_start - ready_time_) / NACL_MICROS_PER_MILLI);
663   }
664
665   url_downloaders_.erase(url_downloaders_.begin(), url_downloaders_.end());
666
667   ScriptablePlugin* scriptable_plugin_ = scriptable_plugin();
668   ScriptablePlugin::Unref(&scriptable_plugin_);
669
670   // ShutDownSubprocesses shuts down the main subprocess, which shuts
671   // down the main ServiceRuntime object, which kills the subprocess.
672   // As a side effect of the subprocess being killed, the reverse
673   // services thread(s) will get EOF on the reverse channel(s), and
674   // the thread(s) will exit.  In ServiceRuntime::Shutdown, we invoke
675   // ReverseService::WaitForServiceThreadsToExit(), so that there will
676   // not be an extent thread(s) hanging around.  This means that the
677   // ~Plugin will block until this happens.  This is a requirement,
678   // since the renderer should be free to unload the plugin code, and
679   // we cannot have threads running code that gets unloaded before
680   // they exit.
681   //
682   // By waiting for the threads here, we also ensure that the Plugin
683   // object and the subprocess and ServiceRuntime objects is not
684   // (fully) destroyed while the threads are running, so resources
685   // that are destroyed after ShutDownSubprocesses (below) are
686   // guaranteed to be live and valid for access from the service
687   // threads.
688   //
689   // The main_subprocess object, which wraps the main service_runtime
690   // object, is dtor'd implicitly after the explicit code below runs,
691   // so the main service runtime object will not have been dtor'd,
692   // though the Shutdown method may have been called, during the
693   // lifetime of the service threads.
694   ShutDownSubprocesses();
695
696   delete wrapper_factory_;
697   delete[] argv_;
698   delete[] argn_;
699
700   HistogramTimeSmall(
701       "NaCl.Perf.ShutdownTime.Total",
702       (NaClGetTimeOfDayMicroseconds() - shutdown_start)
703           / NACL_MICROS_PER_MILLI);
704
705   PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
706                  static_cast<void*>(this)));
707 }
708
709 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
710   PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
711                  static_cast<void*>(this)));
712   // We don't know if the plugin will handle the document load, but return
713   // true in order to give it a chance to respond once the proxy is started.
714   return true;
715 }
716
717 pp::Var Plugin::GetInstanceObject() {
718   PLUGIN_PRINTF(("Plugin::GetInstanceObject (this=%p)\n",
719                  static_cast<void*>(this)));
720   // The browser will unref when it discards the var for this object.
721   ScriptablePlugin* handle =
722       static_cast<ScriptablePlugin*>(scriptable_plugin()->AddRef());
723   pp::Var* handle_var = handle->var();
724   PLUGIN_PRINTF(("Plugin::GetInstanceObject (handle=%p, handle_var=%p)\n",
725                  static_cast<void*>(handle), static_cast<void*>(handle_var)));
726   return *handle_var;  // make a copy
727 }
728
729 void Plugin::HistogramStartupTimeSmall(const std::string& name, float dt) {
730   if (nexe_size_ > 0) {
731     float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
732     HistogramTimeSmall(name, static_cast<int64_t>(dt));
733     HistogramTimeSmall(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
734   }
735 }
736
737 void Plugin::HistogramStartupTimeMedium(const std::string& name, float dt) {
738   if (nexe_size_ > 0) {
739     float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
740     HistogramTimeMedium(name, static_cast<int64_t>(dt));
741     HistogramTimeMedium(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
742   }
743 }
744
745 void Plugin::NexeFileDidOpen(int32_t pp_error) {
746   PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (pp_error=%" NACL_PRId32 ")\n",
747                  pp_error));
748   struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
749   PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32 ")\n",
750                  info.desc));
751   HistogramHTTPStatusCode(
752       is_installed_ ?
753           "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" :
754           "NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp",
755       nexe_downloader_.status_code());
756   ErrorInfo error_info;
757   if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
758     if (pp_error == PP_ERROR_ABORTED) {
759       ReportLoadAbort();
760     } else if (pp_error == PP_ERROR_NOACCESS) {
761       error_info.SetReport(ERROR_NEXE_NOACCESS_URL,
762                            "access to nexe url was denied.");
763       ReportLoadError(error_info);
764     } else {
765       error_info.SetReport(ERROR_NEXE_LOAD_URL, "could not load nexe url.");
766       ReportLoadError(error_info);
767     }
768     return;
769   }
770   int32_t file_desc_ok_to_close = DUP(info.desc);
771   if (file_desc_ok_to_close == NACL_NO_FILE_DESC) {
772     error_info.SetReport(ERROR_NEXE_FH_DUP,
773                          "could not duplicate loaded file handle.");
774     ReportLoadError(error_info);
775     return;
776   }
777   struct stat stat_buf;
778   if (0 != fstat(file_desc_ok_to_close, &stat_buf)) {
779     CLOSE(file_desc_ok_to_close);
780     error_info.SetReport(ERROR_NEXE_STAT, "could not stat nexe file.");
781     ReportLoadError(error_info);
782     return;
783   }
784   size_t nexe_bytes_read = static_cast<size_t>(stat_buf.st_size);
785
786   nexe_size_ = nexe_bytes_read;
787   HistogramSizeKB("NaCl.Perf.Size.Nexe",
788                   static_cast<int32_t>(nexe_size_ / 1024));
789   HistogramStartupTimeMedium(
790       "NaCl.Perf.StartupTime.NexeDownload",
791       static_cast<float>(nexe_downloader_.TimeSinceOpenMilliseconds()));
792
793   // Inform JavaScript that we successfully downloaded the nacl module.
794   EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
795                        nexe_downloader_.url_to_open(),
796                        LENGTH_IS_COMPUTABLE,
797                        nexe_bytes_read,
798                        nexe_bytes_read);
799
800   load_start_ = NaClGetTimeOfDayMicroseconds();
801   nacl::scoped_ptr<nacl::DescWrapper>
802       wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY));
803   NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
804   bool was_successful = LoadNaClModule(
805       wrapper.get(), &error_info,
806       true, /* enable_dyncode_syscalls */
807       true, /* enable_exception_handling */
808       false, /* enable_crash_throttling */
809       callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
810       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
811
812   if (!was_successful) {
813     ReportLoadError(error_info);
814   }
815 }
816
817 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
818   ErrorInfo error_info;
819   bool was_successful;
820
821   UNREFERENCED_PARAMETER(pp_error);
822   NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
823   NaClLog(4, "NexeFileDidOpenContinuation: invoking"
824           " LoadNaClModuleContinuationIntern\n");
825   was_successful = LoadNaClModuleContinuationIntern(&error_info);
826   if (was_successful) {
827     NaClLog(4, "NexeFileDidOpenContinuation: success;"
828             " setting histograms\n");
829     ready_time_ = NaClGetTimeOfDayMicroseconds();
830     HistogramStartupTimeSmall(
831         "NaCl.Perf.StartupTime.LoadModule",
832         static_cast<float>(ready_time_ - load_start_) / NACL_MICROS_PER_MILLI);
833     HistogramStartupTimeMedium(
834         "NaCl.Perf.StartupTime.Total",
835         static_cast<float>(ready_time_ - init_time_) / NACL_MICROS_PER_MILLI);
836
837     ReportLoadSuccess(LENGTH_IS_COMPUTABLE, nexe_size_, nexe_size_);
838   } else {
839     NaClLog(4, "NexeFileDidOpenContinuation: failed.");
840     ReportLoadError(error_info);
841   }
842   NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
843 }
844
845 static void LogLineToConsole(Plugin* plugin, const nacl::string& one_line) {
846   PLUGIN_PRINTF(("LogLineToConsole: %s\n",
847                  one_line.c_str()));
848   plugin->AddToConsole(one_line);
849 }
850
851 void Plugin::CopyCrashLogToJsConsole() {
852   nacl::string fatal_msg(main_service_runtime()->GetCrashLogOutput());
853   size_t ix_start = 0;
854   size_t ix_end;
855
856   PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS " bytes\n",
857                  fatal_msg.size()));
858   while (nacl::string::npos != (ix_end = fatal_msg.find('\n', ix_start))) {
859     LogLineToConsole(this, fatal_msg.substr(ix_start, ix_end - ix_start));
860     ix_start = ix_end + 1;
861   }
862   if (ix_start != fatal_msg.size()) {
863     LogLineToConsole(this, fatal_msg.substr(ix_start));
864   }
865 }
866
867 void Plugin::NexeDidCrash(int32_t pp_error) {
868   PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
869                  pp_error));
870   if (pp_error != PP_OK) {
871     PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
872                    " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
873   }
874   PLUGIN_PRINTF(("Plugin::NexeDidCrash: crash event!\n"));
875   if (-1 != exit_status()) {
876     // The NaCl module voluntarily exited.  However, this is still a
877     // crash from the point of view of Pepper, since PPAPI plugins are
878     // event handlers and should never exit.
879     PLUGIN_PRINTF((("Plugin::NexeDidCrash: nexe exited with status %d"
880                     " so this is a \"controlled crash\".\n"),
881                    exit_status()));
882   }
883   // If the crash occurs during load, we just want to report an error
884   // that fits into our load progress event grammar.  If the crash
885   // occurs after loaded/loadend, then we use ReportDeadNexe to send a
886   // "crash" event.
887   if (nexe_error_reported()) {
888     PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;"
889                    " suppressing\n"));
890   } else {
891     if (nacl_ready_state_ == DONE) {
892       ReportDeadNexe();
893     } else {
894       ErrorInfo error_info;
895       // The error is not quite right.  In particular, the crash
896       // reported by this path could be due to NaCl application
897       // crashes that occur after the PPAPI proxy has started.
898       error_info.SetReport(ERROR_START_PROXY_CRASH,
899                            "Nexe crashed during startup");
900       ReportLoadError(error_info);
901     }
902   }
903
904   // In all cases, try to grab the crash log.  The first error
905   // reported may have come from the start_module RPC reply indicating
906   // a validation error or something similar, which wouldn't grab the
907   // crash log.  In the event that this is called twice, the second
908   // invocation will just be a no-op, since all the crash log will
909   // have been received and we'll just get an EOF indication.
910   CopyCrashLogToJsConsole();
911 }
912
913 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
914   PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
915                  pp_error));
916   if (pp_error != PP_OK) {
917     // Error should have been reported by pnacl. Just return.
918     PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
919     return;
920   }
921
922   // Inform JavaScript that we successfully translated the bitcode to a nexe.
923   nacl::scoped_ptr<nacl::DescWrapper>
924       wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD());
925   ErrorInfo error_info;
926   bool was_successful = LoadNaClModule(
927       wrapper.get(), &error_info,
928       false, /* enable_dyncode_syscalls */
929       false, /* enable_exception_handling */
930       true, /* enable_crash_throttling */
931       callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
932       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
933
934   if (!was_successful) {
935     ReportLoadError(error_info);
936   }
937 }
938
939 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
940   ErrorInfo error_info;
941   bool was_successful = LoadNaClModuleContinuationIntern(&error_info);
942
943   NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
944   UNREFERENCED_PARAMETER(pp_error);
945   if (was_successful) {
946     int64_t loaded;
947     int64_t total;
948     pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
949     ReportLoadSuccess(LENGTH_IS_COMPUTABLE, loaded, total);
950   } else {
951     ReportLoadError(error_info);
952   }
953 }
954
955 void Plugin::ReportDeadNexe() {
956   PLUGIN_PRINTF(("Plugin::ReportDeadNexe\n"));
957
958   if (nacl_ready_state_ == DONE && !nexe_error_reported()) {  // After loadEnd.
959     int64_t crash_time = NaClGetTimeOfDayMicroseconds();
960     // Crashes will be more likely near startup, so use a medium histogram
961     // instead of a large one.
962     HistogramTimeMedium(
963         "NaCl.ModuleUptime.Crash",
964         (crash_time - ready_time_) / NACL_MICROS_PER_MILLI);
965
966     nacl::string message = nacl::string("NaCl module crashed");
967     set_last_error_string(message);
968     AddToConsole(message);
969
970     EnqueueProgressEvent(PP_NACL_EVENT_CRASH);
971     set_nexe_error_reported(true);
972   }
973   // else ReportLoadError() and ReportAbortError() will be used by loading code
974   // to provide error handling.
975   //
976   // NOTE: not all crashes during load will make it here.
977   // Those in BrowserPpp::InitializeModule and creation of PPP interfaces
978   // will just get reported back as PP_ERROR_FAILED.
979 }
980
981 void Plugin::NaClManifestBufferReady(int32_t pp_error) {
982   PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%"
983                  NACL_PRId32 ")\n", pp_error));
984   ErrorInfo error_info;
985   set_manifest_url(nexe_downloader_.url());
986   if (pp_error != PP_OK) {
987     if (pp_error == PP_ERROR_ABORTED) {
988       ReportLoadAbort();
989     } else {
990       error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
991                            "could not load manifest url.");
992       ReportLoadError(error_info);
993     }
994     return;
995   }
996
997   const std::deque<char>& buffer = nexe_downloader_.buffer();
998   size_t buffer_size = buffer.size();
999   if (buffer_size > kNaClManifestMaxFileBytes) {
1000     error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1001                          "manifest file too large.");
1002     ReportLoadError(error_info);
1003     return;
1004   }
1005   nacl::scoped_array<char> json_buffer(new char[buffer_size + 1]);
1006   if (json_buffer == NULL) {
1007     error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1008                          "could not allocate manifest memory.");
1009     ReportLoadError(error_info);
1010     return;
1011   }
1012   std::copy(buffer.begin(), buffer.begin() + buffer_size, &json_buffer[0]);
1013   json_buffer[buffer_size] = '\0';
1014
1015   ProcessNaClManifest(json_buffer.get());
1016 }
1017
1018 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
1019   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
1020                  NACL_PRId32 ")\n", pp_error));
1021   HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
1022                      nexe_downloader_.TimeSinceOpenMilliseconds());
1023   HistogramHTTPStatusCode(
1024       is_installed_ ?
1025           "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" :
1026           "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp",
1027       nexe_downloader_.status_code());
1028   ErrorInfo error_info;
1029   // The manifest file was successfully opened.  Set the src property on the
1030   // plugin now, so that the full url is available to error handlers.
1031   set_manifest_url(nexe_downloader_.url());
1032   struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
1033   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%"
1034                  NACL_PRId32 ")\n", info.desc));
1035   if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
1036     if (pp_error == PP_ERROR_ABORTED) {
1037       ReportLoadAbort();
1038     } else if (pp_error == PP_ERROR_NOACCESS) {
1039       error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL,
1040                            "access to manifest url was denied.");
1041       ReportLoadError(error_info);
1042     } else {
1043       error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
1044                            "could not load manifest url.");
1045       ReportLoadError(error_info);
1046     }
1047     return;
1048   }
1049   // SlurpFile closes the file descriptor after reading (or on error).
1050   // Duplicate our file descriptor since it will be handled by the browser.
1051   int dup_file_desc = DUP(info.desc);
1052   nacl::string json_buffer;
1053   file_utils::StatusCode status = file_utils::SlurpFile(
1054       dup_file_desc, json_buffer, kNaClManifestMaxFileBytes);
1055
1056   if (status != file_utils::PLUGIN_FILE_SUCCESS) {
1057     switch (status) {
1058       case file_utils::PLUGIN_FILE_SUCCESS:
1059         CHECK(0);
1060         break;
1061       case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC:
1062         error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1063                              "could not allocate manifest memory.");
1064         break;
1065       case file_utils::PLUGIN_FILE_ERROR_OPEN:
1066         error_info.SetReport(ERROR_MANIFEST_OPEN,
1067                              "could not open manifest file.");
1068         break;
1069       case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE:
1070         error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1071                              "manifest file too large.");
1072         break;
1073       case file_utils::PLUGIN_FILE_ERROR_STAT:
1074         error_info.SetReport(ERROR_MANIFEST_STAT,
1075                              "could not stat manifest file.");
1076         break;
1077       case file_utils::PLUGIN_FILE_ERROR_READ:
1078         error_info.SetReport(ERROR_MANIFEST_READ,
1079                              "could not read manifest file.");
1080         break;
1081     }
1082     ReportLoadError(error_info);
1083     return;
1084   }
1085
1086   ProcessNaClManifest(json_buffer);
1087 }
1088
1089 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) {
1090   HistogramSizeKB("NaCl.Perf.Size.Manifest",
1091                   static_cast<int32_t>(manifest_json.length() / 1024));
1092   nacl::string program_url;
1093   PnaclOptions pnacl_options;
1094   ErrorInfo error_info;
1095   if (!SetManifestObject(manifest_json, &error_info)) {
1096     ReportLoadError(error_info);
1097     return;
1098   }
1099
1100   if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) {
1101     is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION;
1102     set_nacl_ready_state(LOADING);
1103     // Inform JavaScript that we found a nexe URL to load.
1104     EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS);
1105     if (pnacl_options.translate()) {
1106       pp::CompletionCallback translate_callback =
1107           callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
1108       // Will always call the callback on success or failure.
1109       pnacl_coordinator_.reset(
1110           PnaclCoordinator::BitcodeToNative(this,
1111                                             program_url,
1112                                             pnacl_options,
1113                                             translate_callback));
1114       return;
1115     } else {
1116       // Try the fast path first. This will only block if the file is installed.
1117       if (OpenURLFast(program_url, &nexe_downloader_)) {
1118         NexeFileDidOpen(PP_OK);
1119       } else {
1120         pp::CompletionCallback open_callback =
1121             callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
1122         // Will always call the callback on success or failure.
1123         CHECK(
1124             nexe_downloader_.Open(program_url,
1125                                   DOWNLOAD_TO_FILE,
1126                                   open_callback,
1127                                   true,
1128                                   &UpdateDownloadProgress));
1129       }
1130       return;
1131     }
1132   }
1133   // Failed to select the program and/or the translator.
1134   ReportLoadError(error_info);
1135 }
1136
1137 void Plugin::RequestNaClManifest(const nacl::string& url) {
1138   PLUGIN_PRINTF(("Plugin::RequestNaClManifest (url='%s')\n", url.c_str()));
1139   PLUGIN_PRINTF(("Plugin::RequestNaClManifest (plugin base url='%s')\n",
1140                  plugin_base_url().c_str()));
1141   // The full URL of the manifest file is relative to the base url.
1142   CHECK(url_util_ != NULL);
1143   pp::Var nmf_resolved_url =
1144       url_util_->ResolveRelativeToURL(plugin_base_url(), pp::Var(url));
1145   if (!nmf_resolved_url.is_string()) {
1146     ErrorInfo error_info;
1147     error_info.SetReport(
1148         ERROR_MANIFEST_RESOLVE_URL,
1149         nacl::string("could not resolve URL \"") + url.c_str() +
1150         "\" relative to \"" + plugin_base_url().c_str() + "\".");
1151     ReportLoadError(error_info);
1152     return;
1153   }
1154   PLUGIN_PRINTF(("Plugin::RequestNaClManifest (resolved url='%s')\n",
1155                  nmf_resolved_url.AsString().c_str()));
1156   is_installed_ = GetUrlScheme(nmf_resolved_url.AsString()) ==
1157       SCHEME_CHROME_EXTENSION;
1158   set_manifest_base_url(nmf_resolved_url.AsString());
1159   set_manifest_url(url);
1160   // Inform JavaScript that a load is starting.
1161   set_nacl_ready_state(OPENED);
1162   EnqueueProgressEvent(PP_NACL_EVENT_LOADSTART);
1163   bool is_data_uri = GetUrlScheme(nmf_resolved_url.AsString()) == SCHEME_DATA;
1164   HistogramEnumerateManifestIsDataURI(static_cast<int>(is_data_uri));
1165   if (is_data_uri) {
1166     pp::CompletionCallback open_callback =
1167         callback_factory_.NewCallback(&Plugin::NaClManifestBufferReady);
1168     // Will always call the callback on success or failure.
1169     CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1170                                 DOWNLOAD_TO_BUFFER,
1171                                 open_callback,
1172                                 false,
1173                                 NULL));
1174   } else {
1175     pp::CompletionCallback open_callback =
1176         callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
1177     // Will always call the callback on success or failure.
1178     CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1179                                 DOWNLOAD_TO_FILE,
1180                                 open_callback,
1181                                 false,
1182                                 NULL));
1183   }
1184 }
1185
1186
1187 bool Plugin::SetManifestObject(const nacl::string& manifest_json,
1188                                ErrorInfo* error_info) {
1189   PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n",
1190        manifest_json.c_str()));
1191   if (error_info == NULL)
1192     return false;
1193   // Determine whether lookups should use portable (i.e., pnacl versions)
1194   // rather than platform-specific files.
1195   bool is_pnacl = (mime_type() == kPnaclMIMEType);
1196   nacl::scoped_ptr<JsonManifest> json_manifest(
1197       new JsonManifest(url_util_,
1198                        manifest_base_url(),
1199                        (is_pnacl ? kPortableISA : GetSandboxISA())));
1200   if (!json_manifest->Init(manifest_json, error_info)) {
1201     return false;
1202   }
1203   manifest_.reset(json_manifest.release());
1204   return true;
1205 }
1206
1207 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error,
1208                                        FileDownloader*& url_downloader,
1209                                        PP_CompletionCallback callback) {
1210   PLUGIN_PRINTF(("Plugin::UrlDidOpen (pp_error=%" NACL_PRId32
1211                  ", url_downloader=%p)\n", pp_error,
1212                  static_cast<void*>(url_downloader)));
1213   url_downloaders_.erase(url_downloader);
1214   nacl::scoped_ptr<FileDownloader> scoped_url_downloader(url_downloader);
1215   struct NaClFileInfo info = scoped_url_downloader->GetFileInfo();
1216
1217   if (pp_error != PP_OK) {
1218     PP_RunCompletionCallback(&callback, pp_error);
1219   } else if (info.desc > NACL_NO_FILE_DESC) {
1220     url_file_info_map_[url_downloader->url_to_open()] = info;
1221     PP_RunCompletionCallback(&callback, PP_OK);
1222   } else {
1223     PP_RunCompletionCallback(&callback, PP_ERROR_FAILED);
1224   }
1225 }
1226
1227 struct NaClFileInfo Plugin::GetFileInfo(const nacl::string& url) {
1228   struct NaClFileInfo info;
1229   memset(&info, 0, sizeof(info));
1230   std::map<nacl::string, struct NaClFileInfo>::iterator it =
1231       url_file_info_map_.find(url);
1232   if (it != url_file_info_map_.end()) {
1233     info = it->second;
1234     info.desc = DUP(info.desc);
1235   } else {
1236     info.desc = -1;
1237   }
1238   return info;
1239 }
1240
1241 bool Plugin::StreamAsFile(const nacl::string& url,
1242                           PP_CompletionCallback callback) {
1243   PLUGIN_PRINTF(("Plugin::StreamAsFile (url='%s')\n", url.c_str()));
1244   FileDownloader* downloader = new FileDownloader();
1245   downloader->Initialize(this);
1246   url_downloaders_.insert(downloader);
1247   // Untrusted loads are always relative to the page's origin.
1248   CHECK(url_util_ != NULL);
1249   pp::Var resolved_url =
1250       url_util_->ResolveRelativeToURL(pp::Var(plugin_base_url()), url);
1251   if (!resolved_url.is_string()) {
1252     PLUGIN_PRINTF(("Plugin::StreamAsFile: "
1253                    "could not resolve url \"%s\" relative to plugin \"%s\".",
1254                    url.c_str(),
1255                    plugin_base_url().c_str()));
1256     return false;
1257   }
1258
1259   // Try the fast path first. This will only block if the file is installed.
1260   if (OpenURLFast(url, downloader)) {
1261     UrlDidOpenForStreamAsFile(PP_OK, downloader, callback);
1262     return true;
1263   }
1264
1265   pp::CompletionCallback open_callback = callback_factory_.NewCallback(
1266       &Plugin::UrlDidOpenForStreamAsFile, downloader, callback);
1267   // If true, will always call the callback on success or failure.
1268   return downloader->Open(url,
1269                           DOWNLOAD_TO_FILE,
1270                           open_callback,
1271                           true,
1272                           &UpdateDownloadProgress);
1273 }
1274
1275
1276 void Plugin::ReportLoadSuccess(LengthComputable length_computable,
1277                                uint64_t loaded_bytes,
1278                                uint64_t total_bytes) {
1279   // Set the readyState attribute to indicate loaded.
1280   set_nacl_ready_state(DONE);
1281   // Inform JavaScript that loading was successful and is complete.
1282   const nacl::string& url = nexe_downloader_.url_to_open();
1283   EnqueueProgressEvent(
1284       PP_NACL_EVENT_LOAD, url, length_computable, loaded_bytes, total_bytes);
1285   EnqueueProgressEvent(
1286       PP_NACL_EVENT_LOADEND, url, length_computable, loaded_bytes, total_bytes);
1287
1288   // UMA
1289   HistogramEnumerateLoadStatus(ERROR_LOAD_SUCCESS, is_installed_);
1290 }
1291
1292
1293 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
1294   PLUGIN_PRINTF(("Plugin::ReportLoadError (error='%s')\n",
1295                  error_info.message().c_str()));
1296   // For errors the user (and not just the developer) should know about,
1297   // report them to the renderer so the browser can display a message.
1298   if (error_info.error_code() == ERROR_MANIFEST_PROGRAM_MISSING_ARCH) {
1299     // A special case: the manifest may otherwise be valid but is missing
1300     // a program/file compatible with the user's sandbox.
1301     nacl_interface()->ReportNaClError(pp_instance(),
1302                                       PP_NACL_MANIFEST_MISSING_ARCH);
1303   }
1304
1305   // Set the readyState attribute to indicate we need to start over.
1306   set_nacl_ready_state(DONE);
1307   set_nexe_error_reported(true);
1308   // Report an error in lastError and on the JavaScript console.
1309   nacl::string message = nacl::string("NaCl module load failed: ") +
1310       error_info.message();
1311   set_last_error_string(message);
1312   AddToConsole(nacl::string("NaCl module load failed: ") +
1313                error_info.console_message());
1314   // Inform JavaScript that loading encountered an error and is complete.
1315   EnqueueProgressEvent(PP_NACL_EVENT_ERROR);
1316   EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1317
1318   // UMA
1319   HistogramEnumerateLoadStatus(error_info.error_code(), is_installed_);
1320 }
1321
1322
1323 void Plugin::ReportLoadAbort() {
1324   PLUGIN_PRINTF(("Plugin::ReportLoadAbort\n"));
1325   // Set the readyState attribute to indicate we need to start over.
1326   set_nacl_ready_state(DONE);
1327   set_nexe_error_reported(true);
1328   // Report an error in lastError and on the JavaScript console.
1329   nacl::string error_string("NaCl module load failed: user aborted");
1330   set_last_error_string(error_string);
1331   AddToConsole(error_string);
1332   // Inform JavaScript that loading was aborted and is complete.
1333   EnqueueProgressEvent(PP_NACL_EVENT_ABORT);
1334   EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1335
1336   // UMA
1337   HistogramEnumerateLoadStatus(ERROR_LOAD_ABORTED, is_installed_);
1338 }
1339
1340 void Plugin::UpdateDownloadProgress(
1341     PP_Instance pp_instance,
1342     PP_Resource pp_resource,
1343     int64_t /*bytes_sent*/,
1344     int64_t /*total_bytes_to_be_sent*/,
1345     int64_t bytes_received,
1346     int64_t total_bytes_to_be_received) {
1347   Instance* instance = pp::Module::Get()->InstanceForPPInstance(pp_instance);
1348   if (instance != NULL) {
1349     Plugin* plugin = static_cast<Plugin*>(instance);
1350     // Rate limit progress events to a maximum of 100 per second.
1351     int64_t time = NaClGetTimeOfDayMicroseconds();
1352     int64_t elapsed = time - plugin->time_of_last_progress_event_;
1353     const int64_t kTenMilliseconds = 10000;
1354     if (elapsed > kTenMilliseconds) {
1355       plugin->time_of_last_progress_event_ = time;
1356
1357       // Find the URL loader that sent this notification.
1358       const FileDownloader* file_downloader =
1359           plugin->FindFileDownloader(pp_resource);
1360       // If not a streamed file, it must be the .nexe loader.
1361       if (file_downloader == NULL)
1362         file_downloader = &plugin->nexe_downloader_;
1363       nacl::string url = file_downloader->url_to_open();
1364       LengthComputable length_computable = (total_bytes_to_be_received >= 0) ?
1365           LENGTH_IS_COMPUTABLE : LENGTH_IS_NOT_COMPUTABLE;
1366
1367       plugin->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
1368                                    url,
1369                                    length_computable,
1370                                    bytes_received,
1371                                    total_bytes_to_be_received);
1372     }
1373   }
1374 }
1375
1376 const FileDownloader* Plugin::FindFileDownloader(
1377     PP_Resource url_loader) const {
1378   const FileDownloader* file_downloader = NULL;
1379   if (url_loader == nexe_downloader_.url_loader()) {
1380     file_downloader = &nexe_downloader_;
1381   } else {
1382     std::set<FileDownloader*>::const_iterator it = url_downloaders_.begin();
1383     while (it != url_downloaders_.end()) {
1384       if (url_loader == (*it)->url_loader()) {
1385         file_downloader = (*it);
1386         break;
1387       }
1388       ++it;
1389     }
1390   }
1391   return file_downloader;
1392 }
1393
1394 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type) {
1395   EnqueueProgressEvent(event_type,
1396                        NACL_NO_URL,
1397                        Plugin::LENGTH_IS_NOT_COMPUTABLE,
1398                        Plugin::kUnknownBytes,
1399                        Plugin::kUnknownBytes);
1400 }
1401
1402 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type,
1403                                   const nacl::string& url,
1404                                   LengthComputable length_computable,
1405                                   uint64_t loaded_bytes,
1406                                   uint64_t total_bytes) {
1407   PLUGIN_PRINTF(("Plugin::EnqueueProgressEvent ("
1408                  "event_type='%d', url='%s', length_computable=%d, "
1409                  "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
1410                  static_cast<int>(event_type),
1411                  url.c_str(),
1412                  static_cast<int>(length_computable),
1413                  loaded_bytes,
1414                  total_bytes));
1415
1416   progress_events_.push(new ProgressEvent(event_type,
1417                                           url,
1418                                           length_computable,
1419                                           loaded_bytes,
1420                                           total_bytes));
1421   // Note that using callback_factory_ in this way is not thread safe.
1422   // If/when EnqueueProgressEvent is callable from another thread, this
1423   // will need to change.
1424   pp::CompletionCallback callback =
1425       callback_factory_.NewCallback(&Plugin::DispatchProgressEvent);
1426   pp::Core* core = pp::Module::Get()->core();
1427   core->CallOnMainThread(0, callback, 0);
1428 }
1429
1430 void Plugin::ReportSelLdrLoadStatus(int status) {
1431   HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status),
1432                                      is_installed_);
1433 }
1434
1435 void Plugin::DispatchProgressEvent(int32_t result) {
1436   PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (result=%"
1437                  NACL_PRId32 ")\n", result));
1438   if (result < 0) {
1439     return;
1440   }
1441   if (progress_events_.empty()) {
1442     PLUGIN_PRINTF(("Plugin::DispatchProgressEvent: no pending events\n"));
1443     return;
1444   }
1445   nacl::scoped_ptr<ProgressEvent> event(progress_events_.front());
1446   progress_events_.pop();
1447   PLUGIN_PRINTF(("Plugin::DispatchProgressEvent ("
1448                  "event_type='%d', url='%s', length_computable=%d, "
1449                  "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
1450                  static_cast<int>(event->event_type()),
1451                  event->url(),
1452                  static_cast<int>(event->length_computable()),
1453                  event->loaded_bytes(),
1454                  event->total_bytes()));
1455
1456   nacl_interface_->DispatchEvent(
1457       pp_instance(),
1458       event->event_type(),
1459       pp::Var(event->url()).pp_var(),
1460       event->length_computable() == LENGTH_IS_COMPUTABLE ? PP_TRUE : PP_FALSE,
1461       event->loaded_bytes(),
1462       event->total_bytes());
1463 }
1464
1465 bool Plugin::OpenURLFast(const nacl::string& url,
1466                          FileDownloader* downloader) {
1467   // Fast path only works for installed file URLs.
1468   if (GetUrlScheme(url) != SCHEME_CHROME_EXTENSION)
1469     return false;
1470   // IMPORTANT: Make sure the document can request the given URL. If we don't
1471   // check, a malicious app could probe the extension system. This enforces a
1472   // same-origin policy which prevents the app from requesting resources from
1473   // another app.
1474   if (!DocumentCanRequest(url))
1475     return false;
1476
1477   uint64_t file_token_lo = 0;
1478   uint64_t file_token_hi = 0;
1479   PP_FileHandle file_handle =
1480       nacl_interface()->OpenNaClExecutable(pp_instance(),
1481                                            url.c_str(),
1482                                            &file_token_lo, &file_token_hi);
1483   // We shouldn't hit this if the file URL is in an installed app.
1484   if (file_handle == PP_kInvalidFileHandle)
1485     return false;
1486
1487   // FileDownloader takes ownership of the file handle.
1488   downloader->OpenFast(url, file_handle, file_token_lo, file_token_hi);
1489   return true;
1490 }
1491
1492 UrlSchemeType Plugin::GetUrlScheme(const std::string& url) {
1493   CHECK(url_util_ != NULL);
1494   PP_URLComponents_Dev comps;
1495   pp::Var canonicalized =
1496       url_util_->Canonicalize(pp::Var(url), &comps);
1497
1498   if (canonicalized.is_null() ||
1499       (comps.scheme.begin == 0 && comps.scheme.len == -1)) {
1500     // |url| was an invalid URL or has no scheme.
1501     return SCHEME_OTHER;
1502   }
1503
1504   CHECK(comps.scheme.begin <
1505             static_cast<int>(canonicalized.AsString().size()));
1506   CHECK(comps.scheme.begin + comps.scheme.len <
1507             static_cast<int>(canonicalized.AsString().size()));
1508
1509   std::string scheme = canonicalized.AsString().substr(comps.scheme.begin,
1510                                                        comps.scheme.len);
1511   if (scheme == kChromeExtensionUriScheme)
1512     return SCHEME_CHROME_EXTENSION;
1513   if (scheme == kDataUriScheme)
1514     return SCHEME_DATA;
1515   return SCHEME_OTHER;
1516 }
1517
1518 bool Plugin::DocumentCanRequest(const std::string& url) {
1519   CHECK(url_util_ != NULL);
1520   return url_util_->DocumentCanRequest(this, pp::Var(url));
1521 }
1522
1523 void Plugin::AddToConsole(const nacl::string& text) {
1524   pp::Module* module = pp::Module::Get();
1525   const PPB_Var* var_interface =
1526       static_cast<const PPB_Var*>(
1527           module->GetBrowserInterface(PPB_VAR_INTERFACE));
1528   nacl::string prefix_string("NativeClient");
1529   PP_Var prefix =
1530       var_interface->VarFromUtf8(prefix_string.c_str(),
1531                                  static_cast<uint32_t>(prefix_string.size()));
1532   PP_Var str = var_interface->VarFromUtf8(text.c_str(),
1533                                           static_cast<uint32_t>(text.size()));
1534   const PPB_Console* console_interface =
1535       static_cast<const PPB_Console*>(
1536           module->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
1537   console_interface->LogWithSource(pp_instance(), PP_LOGLEVEL_LOG, prefix, str);
1538   var_interface->Release(prefix);
1539   var_interface->Release(str);
1540 }
1541
1542 void Plugin::set_last_error_string(const nacl::string& error) {
1543   DCHECK(nacl_interface_);
1544   nacl_interface_->SetReadOnlyProperty(pp_instance(),
1545                                        pp::Var("lastError").pp_var(),
1546                                        pp::Var(error).pp_var());
1547 }
1548
1549 void Plugin::set_nacl_ready_state(ReadyState state) {
1550   nacl_ready_state_ = state;
1551   DCHECK(nacl_interface_);
1552   nacl_interface_->SetReadOnlyProperty(pp_instance(),
1553                                        pp::Var("readyState").pp_var(),
1554                                        pp::Var(state).pp_var());
1555 }
1556
1557 void Plugin::set_exit_status(int exit_status) {
1558   pp::Core* core = pp::Module::Get()->core();
1559   if (core->IsMainThread()) {
1560     SetExitStatusOnMainThread(PP_OK, exit_status);
1561   } else {
1562     pp::CompletionCallback callback =
1563         callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
1564                                       exit_status);
1565     core->CallOnMainThread(0, callback, 0);
1566   }
1567 }
1568
1569 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
1570                                        int exit_status) {
1571   DCHECK(pp::Module::Get()->core()->IsMainThread());
1572   DCHECK(nacl_interface_);
1573   exit_status_ = exit_status;
1574   nacl_interface_->SetReadOnlyProperty(pp_instance(),
1575                                        pp::Var("exitStatus").pp_var(),
1576                                        pp::Var(exit_status_).pp_var());
1577 }
1578
1579
1580 }  // namespace plugin