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