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