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.
6 // Do not warn about use of std::copy with raw pointers.
7 #pragma warning(disable : 4996)
10 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
13 #include <sys/types.h>
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"
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";
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;
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();
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();
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
) {
134 const PPB_UMA_Private
* ptr
= GetUMAInterface();
135 if (ptr
== NULL
) return;
137 ptr
->HistogramCustomTimes(pp::Var(name
).pp_var(),
139 kTimeSmallMin
, kTimeSmallMax
,
143 void HistogramTimeMedium(const std::string
& name
, int64_t ms
) {
146 const PPB_UMA_Private
* ptr
= GetUMAInterface();
147 if (ptr
== NULL
) return;
149 ptr
->HistogramCustomTimes(pp::Var(name
).pp_var(),
151 kTimeMediumMin
, kTimeMediumMax
,
155 void HistogramTimeLarge(const std::string
& name
, int64_t ms
) {
158 const PPB_UMA_Private
* ptr
= GetUMAInterface();
159 if (ptr
== NULL
) return;
161 ptr
->HistogramCustomTimes(pp::Var(name
).pp_var(),
163 kTimeLargeMin
, kTimeLargeMax
,
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(),
175 kSizeKBMin
, kSizeKBMax
,
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.
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
) {
208 NaClOSArch os_arch
= kNaClOSArchMax
;
210 os_arch
= kNaClLinux32
;
212 os_arch
= kNaClMac32
;
214 os_arch
= kNaClWin32
;
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
,
227 HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code
, ERROR_MAX
,
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
,
237 void HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code
,
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
258 // Note: installed files may have "0" for a status code.
259 if (status
< 0 || status
>= 600)
261 HistogramEnumerate(name
, sample
, 7, 6);
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",
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",
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()) {
288 PropertyGetter getter
= property_getters_
[prop_name
];
289 (this->*getter
)(prop_value
);
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)));
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
);
324 // Remember the embed/object argn/argv pairs.
325 argn_
= new char*[argc
];
326 argv_
= new char*[argc
];
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.
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_
) {
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"));
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
,
381 if (pp_error
!= PP_OK
) {
382 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
383 "-- SHOULD NOT HAPPEN\n"));
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
,
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(
409 "sel_ldr init failure " + subprocess
->description());
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
);
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
) {
433 // Now actually load the nexe, which can happen on a background thread.
434 bool nexe_loaded
= new_service_runtime
->LoadNexeAndStart(wrapper
,
437 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (nexe_loaded=%d)\n",
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(),
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
)) {
470 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
471 main_subprocess_
.detailed_description().c_str()));
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.");
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.");
503 } else if (ipc_result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
504 error_info
->SetReport(ERROR_START_PROXY_INSTANCE
,
505 "could not create instance.");
508 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
509 main_subprocess_
.detailed_description().c_str()));
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.");
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(),
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 */,
541 pp::BlockUntilComplete(),
542 pp::BlockUntilComplete())) {
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());
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
)) {
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
{
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
),
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_
; }
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
615 const char* event_type_
;
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.
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
) {
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
)
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
)
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
));
676 // Look for the developer attribute; if it's present, enable 'dev'
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(),
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"));
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"));
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
));
728 Plugin::Plugin(PP_Instance pp_instance
)
729 : pp::InstancePrivate(pp_instance
),
730 scriptable_plugin_(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),
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
);
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()) {
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
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
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_
;
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.
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",
852 struct NaClFileInfo info
= nexe_downloader_
.GetFileInfo();
853 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32
")\n",
855 HistogramHTTPStatusCode(
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
) {
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
);
869 error_info
.SetReport(ERROR_NEXE_LOAD_URL
, "could not load nexe url.");
870 ReportLoadError(error_info
);
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
);
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
);
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
,
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
;
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_
);
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",
952 plugin
->AddToConsole(one_line
);
955 void Plugin::CopyCrashLogToJsConsole() {
956 nacl::string
fatal_msg(main_service_runtime()->GetCrashLogOutput());
960 PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS
" bytes\n",
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",
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"),
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
992 if (nexe_error_reported()) {
993 PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;"
996 if (nacl_ready_state() == DONE
) {
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",
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"));
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
) {
1053 pnacl_coordinator_
->GetCurrentProgress(&loaded
, &total
);
1054 ReportLoadSuccess(LENGTH_IS_COMPUTABLE
, loaded
, total
);
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
) {
1095 error_info
.SetReport(ERROR_MANIFEST_LOAD_URL
,
1096 "could not load manifest url.");
1097 ReportLoadError(error_info
);
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
);
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
);
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(
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
) {
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
);
1148 error_info
.SetReport(ERROR_MANIFEST_LOAD_URL
,
1149 "could not load manifest url.");
1150 ReportLoadError(error_info
);
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
) {
1163 case file_utils::PLUGIN_FILE_SUCCESS
:
1166 case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC
:
1167 error_info
.SetReport(ERROR_MANIFEST_MEMORY_ALLOC
,
1168 "could not allocate manifest memory.");
1170 case file_utils::PLUGIN_FILE_ERROR_OPEN
:
1171 error_info
.SetReport(ERROR_MANIFEST_OPEN
,
1172 "could not open manifest file.");
1174 case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE
:
1175 error_info
.SetReport(ERROR_MANIFEST_TOO_LARGE
,
1176 "manifest file too large.");
1178 case file_utils::PLUGIN_FILE_ERROR_STAT
:
1179 error_info
.SetReport(ERROR_MANIFEST_STAT
,
1180 "could not stat manifest file.");
1182 case file_utils::PLUGIN_FILE_ERROR_READ
:
1183 error_info
.SetReport(ERROR_MANIFEST_READ
,
1184 "could not read manifest file.");
1187 ReportLoadError(error_info
);
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
);
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,
1219 translate_callback
));
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
);
1226 pp::CompletionCallback open_callback
=
1227 callback_factory_
.NewCallback(&Plugin::NexeFileDidOpen
);
1228 // Will always call the callback on success or failure.
1230 nexe_downloader_
.Open(program_url
,
1234 &UpdateDownloadProgress
));
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
);
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
));
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(),
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(),
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
)
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
)) {
1309 manifest_
.reset(json_manifest
.release());
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
);
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()) {
1340 info
.desc
= DUP(info
.desc
);
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\".",
1361 plugin_base_url().c_str()));
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
);
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
,
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
);
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
);
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
);
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
,
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_
;
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
);
1498 return file_downloader
;
1501 void Plugin::EnqueueProgressEvent(const char* event_type
) {
1502 EnqueueProgressEvent(event_type
,
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",
1519 static_cast<int>(length_computable
),
1523 progress_events_
.push(new ProgressEvent(event_type
,
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
),
1542 void Plugin::DispatchProgressEvent(int32_t result
) {
1543 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (result=%"
1544 NACL_PRId32
")\n", result
));
1548 if (progress_events_
.empty()) {
1549 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent: no pending events\n"));
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(),
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, {"
1568 " cancelable: true,"
1569 " lengthComputable: lengthComputable,"
1570 " loaded: loadedBytes,"
1571 " total: totalBytes"
1573 " progress_event.url = url;"
1574 " target.dispatchEvent(progress_event);"
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"));
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"));
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
)
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
1623 if (!DocumentCanRequest(url
))
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(),
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
)
1636 // FileDownloader takes ownership of the file handle.
1637 downloader
->OpenFast(url
, file_handle
, file_token_lo
, file_token_hi
);
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
,
1660 if (scheme
== kChromeExtensionUriScheme
)
1661 return SCHEME_CHROME_EXTENSION
;
1662 if (scheme
== kDataUriScheme
)
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");
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