blob: 5cbb9d7d5c9e39666cd9d442e0a669cceb7cbe51 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifdef _MSC_VER
6// Do not warn about use of std::copy with raw pointers.
7#pragma warning(disable : 4996)
8#endif
9
Ben Murdochca12bfa2013-07-23 11:17:05 +010010#include "ppapi/native_client/src/trusted/plugin/plugin.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011
Torne (Richard Coles)58218062012-11-14 11:43:16 +000012#include <sys/stat.h>
13#include <sys/types.h>
14
15#include <algorithm>
16#include <deque>
17#include <string>
18#include <vector>
19
20#include "native_client/src/include/nacl_base.h"
21#include "native_client/src/include/nacl_macros.h"
22#include "native_client/src/include/nacl_scoped_ptr.h"
23#include "native_client/src/include/nacl_string.h"
24#include "native_client/src/include/portability.h"
25#include "native_client/src/include/portability_io.h"
26#include "native_client/src/include/portability_string.h"
27#include "native_client/src/shared/platform/nacl_check.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000028#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
29#include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030#include "native_client/src/trusted/service_runtime/nacl_error_code.h"
31
Torne (Richard Coles)58218062012-11-14 11:43:16 +000032#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"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000037#include "ppapi/c/ppb_console.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000038#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"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000047#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"
Ben Murdochbb1529c2013-08-08 10:24:53 +010054#include "ppapi/cpp/text_input_controller.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000055
Ben Murdochca12bfa2013-07-23 11:17:05 +010056#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"
65
Torne (Richard Coles)58218062012-11-14 11:43:16 +000066namespace plugin {
67
68namespace {
69
70const 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).
74const 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.
79const char* const kNaClManifestAttribute = "nacl";
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010080// The pseudo-ISA used to indicate portable native client.
81const char* const kPortableISA = "portable";
Torne (Richard Coles)58218062012-11-14 11:43:16 +000082// 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.
85const size_t kNaClManifestMaxFileBytes = 1024 * 1024;
86
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 '@'.
89const char* const kDevAttribute = "@dev";
90
91// URL schemes that we treat in special ways.
92const char* const kChromeExtensionUriScheme = "chrome-extension";
93const char* const kDataUriScheme = "data";
94
95// The key used to find the dictionary nexe URLs in the manifest file.
96const char* const kNexesKey = "nexes";
97
98// Up to 20 seconds
99const int64_t kTimeSmallMin = 1; // in ms
100const int64_t kTimeSmallMax = 20000; // in ms
101const uint32_t kTimeSmallBuckets = 100;
102
103// Up to 3 minutes, 20 seconds
104const int64_t kTimeMediumMin = 10; // in ms
105const int64_t kTimeMediumMax = 200000; // in ms
106const uint32_t kTimeMediumBuckets = 100;
107
108// Up to 33 minutes.
109const int64_t kTimeLargeMin = 100; // in ms
110const int64_t kTimeLargeMax = 2000000; // in ms
111const uint32_t kTimeLargeBuckets = 100;
112
113const int64_t kSizeKBMin = 1;
114const int64_t kSizeKBMax = 512*1024; // very large .nexe
115const uint32_t kSizeKBBuckets = 100;
116
117const 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));
122}
123
124const 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));
129}
130
131void HistogramTimeSmall(const std::string& name, int64_t ms) {
132 if (ms < 0) return;
133
134 const PPB_UMA_Private* ptr = GetUMAInterface();
135 if (ptr == NULL) return;
136
137 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
138 ms,
139 kTimeSmallMin, kTimeSmallMax,
140 kTimeSmallBuckets);
141}
142
143void HistogramTimeMedium(const std::string& name, int64_t ms) {
144 if (ms < 0) return;
145
146 const PPB_UMA_Private* ptr = GetUMAInterface();
147 if (ptr == NULL) return;
148
149 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
150 ms,
151 kTimeMediumMin, kTimeMediumMax,
152 kTimeMediumBuckets);
153}
154
155void HistogramTimeLarge(const std::string& name, int64_t ms) {
156 if (ms < 0) return;
157
158 const PPB_UMA_Private* ptr = GetUMAInterface();
159 if (ptr == NULL) return;
160
161 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
162 ms,
163 kTimeLargeMin, kTimeLargeMax,
164 kTimeLargeBuckets);
165}
166
167void HistogramSizeKB(const std::string& name, int32_t sample) {
168 if (sample < 0) return;
169
170 const PPB_UMA_Private* ptr = GetUMAInterface();
171 if (ptr == NULL) return;
172
173 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
174 sample,
175 kSizeKBMin, kSizeKBMax,
176 kSizeKBBuckets);
177}
178
179void 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;
188 }
189 const PPB_UMA_Private* ptr = GetUMAInterface();
190 if (ptr == NULL) return;
191 ptr->HistogramEnumeration(pp::Var(name).pp_var(), sample, maximum);
192}
193
194void 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
206 };
207
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
216
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);
221
222 HistogramEnumerate("NaCl.Client.OSArch", os_arch, kNaClOSArchMax, -1);
223}
224
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000225void HistogramEnumerateLoadStatus(PluginErrorCode error_code,
226 bool is_installed) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000227 HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code, ERROR_MAX,
228 ERROR_UNKNOWN);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000229
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);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000235}
236
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000237void HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code,
238 bool is_installed) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000239 HistogramEnumerate("NaCl.LoadStatus.SelLdr", error_code, NACL_ERROR_CODE_MAX,
240 LOAD_STATUS_UNKNOWN);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241
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);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000247}
248
249void HistogramEnumerateManifestIsDataURI(bool is_data_uri) {
250 HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1);
251}
252
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000253void 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);
262}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000263
264} // namespace
265
266static int const kAbiHeaderBuffer = 256; // must be at least EI_ABIVERSION + 1
267
268void 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;
273}
274
275bool 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();
279}
280
281bool Plugin::GetProperty(const nacl::string& prop_name,
282 NaClSrpcArg* prop_value) {
283 PLUGIN_PRINTF(("Plugin::GetProperty (prop_name=%s)\n", prop_name.c_str()));
284
285 if (property_getters_.find(prop_name) == property_getters_.end()) {
286 return false;
287 }
288 PropertyGetter getter = property_getters_[prop_name];
289 (this->*getter)(prop_value);
290 return true;
291}
292
293void 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();
297}
298
299void 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());
303}
304
305void 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();
309}
310
311bool Plugin::Init(int argc, char* argn[], char* argv[]) {
312 PLUGIN_PRINTF(("Plugin::Init (instance=%p)\n", static_cast<void*>(this)));
313
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.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100321 pp::TextInputController(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000322#endif
323
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;
337 }
338 ++argc_;
339 }
340 }
341 // TODO(sehr): this leaks strings if there is a subsequent failure.
342
343 // Set up the factory used to produce DescWrappers.
344 wrapper_factory_ = new nacl::DescWrapperFactory();
345 if (NULL == wrapper_factory_) {
346 return false;
347 }
348 PLUGIN_PRINTF(("Plugin::Init (wrapper_factory=%p)\n",
349 static_cast<void*>(wrapper_factory_)));
350
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);
357
358 PLUGIN_PRINTF(("Plugin::Init (return 1)\n"));
359 // Return success.
360 return true;
361}
362
363void 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()));
368
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();
372
373 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
374 static_cast<void*>(this)));
375}
376
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100377void 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;
386 }
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();
391}
392
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000393bool Plugin::LoadNaClModuleCommon(nacl::DescWrapper* wrapper,
394 NaClSubprocess* subprocess,
395 const Manifest* manifest,
396 bool should_report_uma,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100397 const SelLdrStartParams& params,
398 const pp::CompletionCallback& init_done_cb,
399 const pp::CompletionCallback& crash_cb) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000400 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) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100407 params.error_info->SetReport(
408 ERROR_SEL_LDR_INIT,
409 "sel_ldr init failure " + subprocess->description());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000410 return false;
411 }
412
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100413 // 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();
426 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000427 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime_started=%d)\n",
428 service_runtime_started));
429 if (!service_runtime_started) {
430 return false;
431 }
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100432
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;
441 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000442 return true;
443}
444
445bool Plugin::LoadNaClModule(nacl::DescWrapper* wrapper,
446 ErrorInfo* error_info,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100447 bool enable_dyncode_syscalls,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100448 bool enable_exception_handling,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100449 const pp::CompletionCallback& init_done_cb,
450 const pp::CompletionCallback& crash_cb) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000451 // Before forking a new sel_ldr process, ensure that we do not leak
452 // the ServiceRuntime object for an existing subprocess, and that any
453 // associated listener threads do not go unjoined because if they
454 // outlive the Plugin object, they will not be memory safe.
455 ShutDownSubprocesses();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100456 SelLdrStartParams params(manifest_base_url(),
457 error_info,
458 true /* uses_irt */,
459 true /* uses_ppapi */,
460 enable_dev_interfaces_,
461 enable_dyncode_syscalls,
462 enable_exception_handling);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000463 if (!LoadNaClModuleCommon(wrapper, &main_subprocess_, manifest_.get(),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000464 true /* should_report_uma */,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100465 params, init_done_cb, crash_cb)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000466 return false;
467 }
468 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
469 main_subprocess_.detailed_description().c_str()));
470 return true;
471}
472
473bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) {
474 if (!main_subprocess_.StartSrpcServices()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000475 // The NaCl process probably crashed. On Linux, a crash causes this error,
476 // while on other platforms, the error is detected below, when we attempt to
477 // start the proxy. Report a module initialization error here, to make it
478 // less confusing for developers.
479 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
480 "StartSrpcServices failed\n");
481 error_info->SetReport(ERROR_START_PROXY_MODULE,
482 "could not initialize module.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000483 return false;
484 }
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100485 PP_ExternalPluginResult ipc_result =
486 nacl_interface_->StartPpapiProxy(pp_instance());
487 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000488 // Log the amound of time that has passed between the trusted plugin being
489 // initialized and the untrusted plugin being initialized. This is
490 // (roughly) the cost of using NaCl, in terms of startup time.
491 HistogramStartupTimeMedium(
492 "NaCl.Perf.StartupTime.NaClOverhead",
493 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_)
494 / NACL_MICROS_PER_MILLI);
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100495 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000496 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100497 "Got PP_EXTERNAL_PLUGIN_ERROR_MODULE\n");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000498 error_info->SetReport(ERROR_START_PROXY_MODULE,
499 "could not initialize module.");
500 return false;
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100501 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000502 error_info->SetReport(ERROR_START_PROXY_INSTANCE,
503 "could not create instance.");
504 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000505 }
506 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
507 main_subprocess_.detailed_description().c_str()));
508 return true;
509}
510
511NaClSubprocess* Plugin::LoadHelperNaClModule(nacl::DescWrapper* wrapper,
512 const Manifest* manifest,
513 ErrorInfo* error_info) {
514 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
515 new NaClSubprocess("helper module", NULL, NULL));
516 if (NULL == nacl_subprocess.get()) {
517 error_info->SetReport(ERROR_SEL_LDR_INIT,
518 "unable to allocate helper subprocess.");
519 return NULL;
520 }
521
522 // Do not report UMA stats for translator-related nexes.
523 // TODO(sehr): define new UMA stats for translator related nexe events.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000524 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100525 // done to save on address space and swap space.
526 // TODO(jvoung): See if we still need the uses_ppapi variable, now that
527 // LaunchSelLdr always happens on the main thread.
528 SelLdrStartParams params(manifest_base_url(),
529 error_info,
530 false /* uses_irt */,
531 false /* uses_ppapi */,
532 enable_dev_interfaces_,
533 false /* enable_dyncode_syscalls */,
534 false /* enable_exception_handling */);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000535 if (!LoadNaClModuleCommon(wrapper, nacl_subprocess.get(), manifest,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000536 false /* should_report_uma */,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100537 params,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000538 pp::BlockUntilComplete(),
539 pp::BlockUntilComplete())) {
540 return NULL;
541 }
542 // We need not wait for the init_done callback. We can block
543 // here in StartSrpcServices, since helper NaCl modules
544 // are spawned from a private thread.
545 //
546 // TODO(bsy): if helper module crashes, we should abort.
547 // crash_cb is not used here, so we are relying on crashes
548 // being detected in StartSrpcServices or later.
549 //
550 // NB: More refactoring might be needed, however, if helper
551 // NaCl modules have their own manifest. Currently the
552 // manifest is a per-plugin-instance object, not a per
553 // NaClSubprocess object.
554 if (!nacl_subprocess->StartSrpcServices()) {
555 error_info->SetReport(ERROR_SRPC_CONNECTION_FAIL,
556 "SRPC connection failure for " +
557 nacl_subprocess->description());
558 return NULL;
559 }
560
561 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s)\n",
562 nacl_subprocess.get()->detailed_description().c_str()));
563
564 return nacl_subprocess.release();
565}
566
567char* Plugin::LookupArgument(const char* key) {
568 char** keys = argn();
569 for (int ii = 0, len = argc(); ii < len; ++ii) {
570 if (!strcmp(keys[ii], key)) {
571 return argv()[ii];
572 }
573 }
574 return NULL;
575}
576
577// Suggested names for progress event types, per
578// http://www.w3.org/TR/progress-events/
579const char* const Plugin::kProgressEventLoadStart = "loadstart";
580const char* const Plugin::kProgressEventProgress = "progress";
581const char* const Plugin::kProgressEventError = "error";
582const char* const Plugin::kProgressEventAbort = "abort";
583const char* const Plugin::kProgressEventLoad = "load";
584const char* const Plugin::kProgressEventLoadEnd = "loadend";
585// Define a NaCl specific event type for .nexe crashes.
586const char* const Plugin::kProgressEventCrash = "crash";
587
588class ProgressEvent {
589 public:
590 ProgressEvent(const char* event_type,
591 const nacl::string& url,
592 Plugin::LengthComputable length_computable,
593 uint64_t loaded_bytes,
594 uint64_t total_bytes) :
595 event_type_(event_type),
596 url_(url),
597 length_computable_(length_computable),
598 loaded_bytes_(loaded_bytes),
599 total_bytes_(total_bytes) { }
600 const char* event_type() const { return event_type_; }
601 const char* url() const { return url_.c_str(); }
602 Plugin::LengthComputable length_computable() const {
603 return length_computable_;
604 }
605 uint64_t loaded_bytes() const { return loaded_bytes_; }
606 uint64_t total_bytes() const { return total_bytes_; }
607
608 private:
609 // event_type_ is always passed from a string literal, so ownership is
610 // not taken. Hence it does not need to be deleted when ProgressEvent is
611 // destroyed.
612 const char* event_type_;
613 nacl::string url_;
614 Plugin::LengthComputable length_computable_;
615 uint64_t loaded_bytes_;
616 uint64_t total_bytes_;
617};
618
619const char* const Plugin::kNaClMIMEType = "application/x-nacl";
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100620const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000621
622bool Plugin::NexeIsContentHandler() const {
623 // Tests if the MIME type is not a NaCl MIME type.
624 // If the MIME type is foreign, then this NEXE is being used as a content
625 // type handler rather than directly by an HTML document.
626 return
627 !mime_type().empty() &&
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100628 mime_type() != kNaClMIMEType &&
629 mime_type() != kPnaclMIMEType;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000630}
631
632
633Plugin* Plugin::New(PP_Instance pp_instance) {
Ben Murdochbbcdd452013-07-25 10:06:34 +0100634 PLUGIN_PRINTF(("Plugin::New (pp_instance=%" NACL_PRId32 ")\n", pp_instance));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000635 Plugin* plugin = new Plugin(pp_instance);
636 PLUGIN_PRINTF(("Plugin::New (plugin=%p)\n", static_cast<void*>(plugin)));
637 if (plugin == NULL) {
638 return NULL;
639 }
640 return plugin;
641}
642
643
644// All failures of this function will show up as "Missing Plugin-in", so
645// there is no need to log to JS console that there was an initialization
646// failure. Note that module loading functions will log their own errors.
647bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
Ben Murdochbbcdd452013-07-25 10:06:34 +0100648 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000649 HistogramEnumerateOsArch(GetSandboxISA());
650 init_time_ = NaClGetTimeOfDayMicroseconds();
651
652 ScriptablePlugin* scriptable_plugin = ScriptablePlugin::NewPlugin(this);
653 if (scriptable_plugin == NULL)
654 return false;
655
656 set_scriptable_plugin(scriptable_plugin);
657 PLUGIN_PRINTF(("Plugin::Init (scriptable_handle=%p)\n",
658 static_cast<void*>(scriptable_plugin_)));
659 url_util_ = pp::URLUtil_Dev::Get();
660 if (url_util_ == NULL)
661 return false;
662
663 PLUGIN_PRINTF(("Plugin::Init (url_util_=%p)\n",
664 static_cast<const void*>(url_util_)));
665
666 bool status = Plugin::Init(
667 static_cast<int>(argc),
668 // TODO(polina): Can we change the args on our end to be const to
669 // avoid these ugly casts?
670 const_cast<char**>(argn),
671 const_cast<char**>(argv));
672 if (status) {
673 // Look for the developer attribute; if it's present, enable 'dev'
674 // interfaces.
675 const char* dev_settings = LookupArgument(kDevAttribute);
676 enable_dev_interfaces_ = (dev_settings != NULL);
677
678 const char* type_attr = LookupArgument(kTypeAttribute);
679 if (type_attr != NULL) {
680 mime_type_ = nacl::string(type_attr);
681 std::transform(mime_type_.begin(), mime_type_.end(), mime_type_.begin(),
682 tolower);
683 }
684
685 const char* manifest_url = LookupArgument(kSrcManifestAttribute);
686 if (NexeIsContentHandler()) {
687 // For content handlers 'src' will be the URL for the content
688 // and 'nacl' will be the URL for the manifest.
689 manifest_url = LookupArgument(kNaClManifestAttribute);
690 // For content handlers the NEXE runs in the security context of the
691 // content it is rendering and the NEXE itself appears to be a
692 // cross-origin resource stored in a Chrome extension.
693 }
694 // Use the document URL as the base for resolving relative URLs to find the
695 // manifest. This takes into account the setting of <base> tags that
696 // precede the embed/object.
697 CHECK(url_util_ != NULL);
698 pp::Var base_var = url_util_->GetDocumentURL(this);
699 if (!base_var.is_string()) {
700 PLUGIN_PRINTF(("Plugin::Init (unable to find document url)\n"));
701 return false;
702 }
703 set_plugin_base_url(base_var.AsString());
704 if (manifest_url == NULL) {
705 // TODO(sehr,polina): this should be a hard error when scripting
706 // the src property is no longer allowed.
707 PLUGIN_PRINTF(("Plugin::Init:"
708 " WARNING: no 'src' property, so no manifest loaded.\n"));
709 if (NULL != LookupArgument(kNaClManifestAttribute)) {
710 PLUGIN_PRINTF(("Plugin::Init:"
711 " WARNING: 'nacl' property is incorrect. Use 'src'.\n"));
712 }
713 } else {
714 // Issue a GET for the manifest_url. The manifest file will be parsed to
715 // determine the nexe URL.
716 // Sets src property to full manifest URL.
717 RequestNaClManifest(manifest_url);
718 }
719 }
720
721 PLUGIN_PRINTF(("Plugin::Init (status=%d)\n", status));
722 return status;
723}
724
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000725Plugin::Plugin(PP_Instance pp_instance)
726 : pp::InstancePrivate(pp_instance),
727 scriptable_plugin_(NULL),
728 argc_(-1),
729 argn_(NULL),
730 argv_(NULL),
731 main_subprocess_("main subprocess", NULL, NULL),
732 nacl_ready_state_(UNSENT),
733 nexe_error_reported_(false),
734 wrapper_factory_(NULL),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000735 enable_dev_interfaces_(false),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000736 is_installed_(false),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000737 init_time_(0),
738 ready_time_(0),
739 nexe_size_(0),
740 time_of_last_progress_event_(0),
741 nacl_interface_(NULL) {
742 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100743 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000744 callback_factory_.Initialize(this);
745 nexe_downloader_.Initialize(this);
746 nacl_interface_ = GetNaClInterface();
747 CHECK(nacl_interface_ != NULL);
748}
749
750
751Plugin::~Plugin() {
752 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
753
754 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, scriptable_plugin=%p)\n",
755 static_cast<void*>(this),
756 static_cast<void*>(scriptable_plugin())));
757 // Destroy the coordinator while the rest of the data is still there
758 pnacl_coordinator_.reset(NULL);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000759
760 if (!nexe_error_reported()) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000761 HistogramTimeLarge(
762 "NaCl.ModuleUptime.Normal",
763 (shutdown_start - ready_time_) / NACL_MICROS_PER_MILLI);
764 }
765
766 url_downloaders_.erase(url_downloaders_.begin(), url_downloaders_.end());
767
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000768 ScriptablePlugin* scriptable_plugin_ = scriptable_plugin();
769 ScriptablePlugin::Unref(&scriptable_plugin_);
770
771 // ShutDownSubprocesses shuts down the main subprocess, which shuts
772 // down the main ServiceRuntime object, which kills the subprocess.
773 // As a side effect of the subprocess being killed, the reverse
774 // services thread(s) will get EOF on the reverse channel(s), and
775 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
776 // ReverseService::WaitForServiceThreadsToExit(), so that there will
777 // not be an extent thread(s) hanging around. This means that the
778 // ~Plugin will block until this happens. This is a requirement,
779 // since the renderer should be free to unload the plugin code, and
780 // we cannot have threads running code that gets unloaded before
781 // they exit.
782 //
783 // By waiting for the threads here, we also ensure that the Plugin
784 // object and the subprocess and ServiceRuntime objects is not
785 // (fully) destroyed while the threads are running, so resources
786 // that are destroyed after ShutDownSubprocesses (below) are
787 // guaranteed to be live and valid for access from the service
788 // threads.
789 //
790 // The main_subprocess object, which wraps the main service_runtime
791 // object, is dtor'd implicitly after the explicit code below runs,
792 // so the main service runtime object will not have been dtor'd,
793 // though the Shutdown method may have been called, during the
794 // lifetime of the service threads.
795 ShutDownSubprocesses();
796
797 delete wrapper_factory_;
798 delete[] argv_;
799 delete[] argn_;
800
801 HistogramTimeSmall(
802 "NaCl.Perf.ShutdownTime.Total",
803 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
804 / NACL_MICROS_PER_MILLI);
805
806 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
807 static_cast<void*>(this)));
808}
809
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000810bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
811 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
812 static_cast<void*>(this)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000813 // We don't know if the plugin will handle the document load, but return
814 // true in order to give it a chance to respond once the proxy is started.
815 return true;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000816}
817
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000818pp::Var Plugin::GetInstanceObject() {
819 PLUGIN_PRINTF(("Plugin::GetInstanceObject (this=%p)\n",
820 static_cast<void*>(this)));
821 // The browser will unref when it discards the var for this object.
822 ScriptablePlugin* handle =
823 static_cast<ScriptablePlugin*>(scriptable_plugin()->AddRef());
824 pp::Var* handle_var = handle->var();
825 PLUGIN_PRINTF(("Plugin::GetInstanceObject (handle=%p, handle_var=%p)\n",
826 static_cast<void*>(handle), static_cast<void*>(handle_var)));
827 return *handle_var; // make a copy
828}
829
830void Plugin::HistogramStartupTimeSmall(const std::string& name, float dt) {
831 if (nexe_size_ > 0) {
832 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
833 HistogramTimeSmall(name, static_cast<int64_t>(dt));
834 HistogramTimeSmall(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
835 }
836}
837
838void Plugin::HistogramStartupTimeMedium(const std::string& name, float dt) {
839 if (nexe_size_ > 0) {
840 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
841 HistogramTimeMedium(name, static_cast<int64_t>(dt));
842 HistogramTimeMedium(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
843 }
844}
845
846void Plugin::NexeFileDidOpen(int32_t pp_error) {
Ben Murdochbbcdd452013-07-25 10:06:34 +0100847 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (pp_error=%" NACL_PRId32 ")\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000848 pp_error));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100849 struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
Ben Murdochbbcdd452013-07-25 10:06:34 +0100850 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32 ")\n",
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100851 info.desc));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000852 HistogramHTTPStatusCode(
853 is_installed_ ?
854 "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" :
855 "NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp",
856 nexe_downloader_.status_code());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000857 ErrorInfo error_info;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100858 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000859 if (pp_error == PP_ERROR_ABORTED) {
860 ReportLoadAbort();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000861 } else if (pp_error == PP_ERROR_NOACCESS) {
862 error_info.SetReport(ERROR_NEXE_NOACCESS_URL,
863 "access to nexe url was denied.");
864 ReportLoadError(error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000865 } else {
866 error_info.SetReport(ERROR_NEXE_LOAD_URL, "could not load nexe url.");
867 ReportLoadError(error_info);
868 }
869 return;
870 }
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100871 int32_t file_desc_ok_to_close = DUP(info.desc);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000872 if (file_desc_ok_to_close == NACL_NO_FILE_DESC) {
873 error_info.SetReport(ERROR_NEXE_FH_DUP,
874 "could not duplicate loaded file handle.");
875 ReportLoadError(error_info);
876 return;
877 }
878 struct stat stat_buf;
879 if (0 != fstat(file_desc_ok_to_close, &stat_buf)) {
880 CLOSE(file_desc_ok_to_close);
881 error_info.SetReport(ERROR_NEXE_STAT, "could not stat nexe file.");
882 ReportLoadError(error_info);
883 return;
884 }
885 size_t nexe_bytes_read = static_cast<size_t>(stat_buf.st_size);
886
887 nexe_size_ = nexe_bytes_read;
888 HistogramSizeKB("NaCl.Perf.Size.Nexe",
889 static_cast<int32_t>(nexe_size_ / 1024));
890 HistogramStartupTimeMedium(
891 "NaCl.Perf.StartupTime.NexeDownload",
892 static_cast<float>(nexe_downloader_.TimeSinceOpenMilliseconds()));
893
894 // Inform JavaScript that we successfully downloaded the nacl module.
895 EnqueueProgressEvent(kProgressEventProgress,
896 nexe_downloader_.url_to_open(),
897 LENGTH_IS_COMPUTABLE,
898 nexe_bytes_read,
899 nexe_bytes_read);
900
901 load_start_ = NaClGetTimeOfDayMicroseconds();
902 nacl::scoped_ptr<nacl::DescWrapper>
903 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY));
904 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
905 bool was_successful = LoadNaClModule(
906 wrapper.get(), &error_info,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100907 true, /* enable_dyncode_syscalls */
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100908 true, /* enable_exception_handling */
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000909 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
910 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
911
912 if (!was_successful) {
913 ReportLoadError(error_info);
914 }
915}
916
917void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
918 ErrorInfo error_info;
919 bool was_successful;
920
921 UNREFERENCED_PARAMETER(pp_error);
922 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
923 NaClLog(4, "NexeFileDidOpenContinuation: invoking"
924 " LoadNaClModuleContinuationIntern\n");
925 was_successful = LoadNaClModuleContinuationIntern(&error_info);
926 if (was_successful) {
927 NaClLog(4, "NexeFileDidOpenContinuation: success;"
928 " setting histograms\n");
929 ready_time_ = NaClGetTimeOfDayMicroseconds();
930 HistogramStartupTimeSmall(
931 "NaCl.Perf.StartupTime.LoadModule",
932 static_cast<float>(ready_time_ - load_start_) / NACL_MICROS_PER_MILLI);
933 HistogramStartupTimeMedium(
934 "NaCl.Perf.StartupTime.Total",
935 static_cast<float>(ready_time_ - init_time_) / NACL_MICROS_PER_MILLI);
936
937 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, nexe_size_, nexe_size_);
938 } else {
939 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
940 ReportLoadError(error_info);
941 }
942 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
943}
944
945static void LogLineToConsole(Plugin* plugin, const nacl::string& one_line) {
946 PLUGIN_PRINTF(("LogLineToConsole: %s\n",
947 one_line.c_str()));
948 plugin->AddToConsole(one_line);
949}
950
951void Plugin::CopyCrashLogToJsConsole() {
952 nacl::string fatal_msg(main_service_runtime()->GetCrashLogOutput());
953 size_t ix_start = 0;
954 size_t ix_end;
955
Ben Murdochbbcdd452013-07-25 10:06:34 +0100956 PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS " bytes\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000957 fatal_msg.size()));
958 while (nacl::string::npos != (ix_end = fatal_msg.find('\n', ix_start))) {
959 LogLineToConsole(this, fatal_msg.substr(ix_start, ix_end - ix_start));
960 ix_start = ix_end + 1;
961 }
962 if (ix_start != fatal_msg.size()) {
963 LogLineToConsole(this, fatal_msg.substr(ix_start));
964 }
965}
966
967void Plugin::NexeDidCrash(int32_t pp_error) {
Ben Murdochbbcdd452013-07-25 10:06:34 +0100968 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000969 pp_error));
970 if (pp_error != PP_OK) {
971 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
972 " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
973 }
974 PLUGIN_PRINTF(("Plugin::NexeDidCrash: crash event!\n"));
975 int exit_status = main_subprocess_.service_runtime()->exit_status();
976 if (-1 != exit_status) {
977 // The NaCl module voluntarily exited. However, this is still a
978 // crash from the point of view of Pepper, since PPAPI plugins are
979 // event handlers and should never exit.
980 PLUGIN_PRINTF((("Plugin::NexeDidCrash: nexe exited with status %d"
981 " so this is a \"controlled crash\".\n"),
982 exit_status));
983 }
984 // If the crash occurs during load, we just want to report an error
985 // that fits into our load progress event grammar. If the crash
986 // occurs after loaded/loadend, then we use ReportDeadNexe to send a
987 // "crash" event.
988 if (nexe_error_reported()) {
989 PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;"
990 " suppressing\n"));
991 } else {
992 if (nacl_ready_state() == DONE) {
993 ReportDeadNexe();
994 } else {
995 ErrorInfo error_info;
996 // The error is not quite right. In particular, the crash
997 // reported by this path could be due to NaCl application
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000998 // crashes that occur after the PPAPI proxy has started.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000999 error_info.SetReport(ERROR_START_PROXY_CRASH,
1000 "Nexe crashed during startup");
1001 ReportLoadError(error_info);
1002 }
1003 }
1004
1005 // In all cases, try to grab the crash log. The first error
1006 // reported may have come from the start_module RPC reply indicating
1007 // a validation error or something similar, which wouldn't grab the
1008 // crash log. In the event that this is called twice, the second
1009 // invocation will just be a no-op, since all the crash log will
1010 // have been received and we'll just get an EOF indication.
1011 CopyCrashLogToJsConsole();
1012}
1013
1014void Plugin::BitcodeDidTranslate(int32_t pp_error) {
Ben Murdochbbcdd452013-07-25 10:06:34 +01001015 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001016 pp_error));
1017 if (pp_error != PP_OK) {
1018 // Error should have been reported by pnacl. Just return.
1019 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
1020 return;
1021 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001022
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001023 // Inform JavaScript that we successfully translated the bitcode to a nexe.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001024 nacl::scoped_ptr<nacl::DescWrapper>
1025 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD());
1026 ErrorInfo error_info;
1027 bool was_successful = LoadNaClModule(
1028 wrapper.get(), &error_info,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001029 false, /* enable_dyncode_syscalls */
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01001030 false, /* enable_exception_handling */
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001031 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
1032 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
1033
1034 if (!was_successful) {
1035 ReportLoadError(error_info);
1036 }
1037}
1038
1039void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
1040 ErrorInfo error_info;
1041 bool was_successful = LoadNaClModuleContinuationIntern(&error_info);
1042
1043 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
1044 UNREFERENCED_PARAMETER(pp_error);
1045 if (was_successful) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001046 int64_t loaded;
1047 int64_t total;
1048 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
1049 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, loaded, total);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001050 } else {
1051 ReportLoadError(error_info);
1052 }
1053}
1054
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001055void Plugin::ReportDeadNexe() {
1056 PLUGIN_PRINTF(("Plugin::ReportDeadNexe\n"));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001057
1058 if (nacl_ready_state() == DONE && !nexe_error_reported()) { // After loadEnd.
1059 int64_t crash_time = NaClGetTimeOfDayMicroseconds();
1060 // Crashes will be more likely near startup, so use a medium histogram
1061 // instead of a large one.
1062 HistogramTimeMedium(
1063 "NaCl.ModuleUptime.Crash",
1064 (crash_time - ready_time_) / NACL_MICROS_PER_MILLI);
1065
1066 nacl::string message = nacl::string("NaCl module crashed");
1067 set_last_error_string(message);
1068 AddToConsole(message);
1069
1070 EnqueueProgressEvent(kProgressEventCrash);
1071 set_nexe_error_reported(true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001072 }
1073 // else ReportLoadError() and ReportAbortError() will be used by loading code
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001074 // to provide error handling.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001075 //
1076 // NOTE: not all crashes during load will make it here.
1077 // Those in BrowserPpp::InitializeModule and creation of PPP interfaces
1078 // will just get reported back as PP_ERROR_FAILED.
1079}
1080
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001081void Plugin::NaClManifestBufferReady(int32_t pp_error) {
1082 PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001083 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001084 ErrorInfo error_info;
1085 set_manifest_url(nexe_downloader_.url());
1086 if (pp_error != PP_OK) {
1087 if (pp_error == PP_ERROR_ABORTED) {
1088 ReportLoadAbort();
1089 } else {
1090 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
1091 "could not load manifest url.");
1092 ReportLoadError(error_info);
1093 }
1094 return;
1095 }
1096
1097 const std::deque<char>& buffer = nexe_downloader_.buffer();
1098 size_t buffer_size = buffer.size();
1099 if (buffer_size > kNaClManifestMaxFileBytes) {
1100 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1101 "manifest file too large.");
1102 ReportLoadError(error_info);
1103 return;
1104 }
1105 nacl::scoped_array<char> json_buffer(new char[buffer_size + 1]);
1106 if (json_buffer == NULL) {
1107 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1108 "could not allocate manifest memory.");
1109 ReportLoadError(error_info);
1110 return;
1111 }
1112 std::copy(buffer.begin(), buffer.begin() + buffer_size, &json_buffer[0]);
1113 json_buffer[buffer_size] = '\0';
1114
1115 ProcessNaClManifest(json_buffer.get());
1116}
1117
1118void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
1119 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001120 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001121 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
1122 nexe_downloader_.TimeSinceOpenMilliseconds());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001123 HistogramHTTPStatusCode(
1124 is_installed_ ?
1125 "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" :
1126 "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp",
1127 nexe_downloader_.status_code());
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001128 ErrorInfo error_info;
1129 // The manifest file was successfully opened. Set the src property on the
1130 // plugin now, so that the full url is available to error handlers.
1131 set_manifest_url(nexe_downloader_.url());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001132 struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001133 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001134 NACL_PRId32 ")\n", info.desc));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001135 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001136 if (pp_error == PP_ERROR_ABORTED) {
1137 ReportLoadAbort();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001138 } else if (pp_error == PP_ERROR_NOACCESS) {
1139 error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL,
1140 "access to manifest url was denied.");
1141 ReportLoadError(error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001142 } else {
1143 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
1144 "could not load manifest url.");
1145 ReportLoadError(error_info);
1146 }
1147 return;
1148 }
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001149 // SlurpFile closes the file descriptor after reading (or on error).
1150 // Duplicate our file descriptor since it will be handled by the browser.
1151 int dup_file_desc = DUP(info.desc);
1152 nacl::string json_buffer;
1153 file_utils::StatusCode status = file_utils::SlurpFile(
1154 dup_file_desc, json_buffer, kNaClManifestMaxFileBytes);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001155
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001156 if (status != file_utils::PLUGIN_FILE_SUCCESS) {
1157 switch (status) {
1158 case file_utils::PLUGIN_FILE_SUCCESS:
1159 CHECK(0);
1160 break;
1161 case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC:
1162 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1163 "could not allocate manifest memory.");
1164 break;
1165 case file_utils::PLUGIN_FILE_ERROR_OPEN:
1166 error_info.SetReport(ERROR_MANIFEST_OPEN,
1167 "could not open manifest file.");
1168 break;
1169 case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE:
1170 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1171 "manifest file too large.");
1172 break;
1173 case file_utils::PLUGIN_FILE_ERROR_STAT:
1174 error_info.SetReport(ERROR_MANIFEST_STAT,
1175 "could not stat manifest file.");
1176 break;
1177 case file_utils::PLUGIN_FILE_ERROR_READ:
1178 error_info.SetReport(ERROR_MANIFEST_READ,
1179 "could not read manifest file.");
1180 break;
1181 }
1182 ReportLoadError(error_info);
1183 return;
1184 }
1185
1186 ProcessNaClManifest(json_buffer);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001187}
1188
1189void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) {
1190 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1191 static_cast<int32_t>(manifest_json.length() / 1024));
1192 nacl::string program_url;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001193 PnaclOptions pnacl_options;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001194 ErrorInfo error_info;
1195 if (!SetManifestObject(manifest_json, &error_info)) {
1196 ReportLoadError(error_info);
1197 return;
1198 }
1199
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001200 if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) {
1201 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001202 set_nacl_ready_state(LOADING);
1203 // Inform JavaScript that we found a nexe URL to load.
1204 EnqueueProgressEvent(kProgressEventProgress);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001205 if (pnacl_options.translate()) {
1206 if (this->nacl_interface()->IsPnaclEnabled()) {
1207 pp::CompletionCallback translate_callback =
1208 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
1209 // Will always call the callback on success or failure.
1210 pnacl_coordinator_.reset(
1211 PnaclCoordinator::BitcodeToNative(this,
1212 program_url,
1213 pnacl_options,
1214 translate_callback));
1215 return;
1216 } else {
1217 error_info.SetReport(ERROR_PNACL_NOT_ENABLED,
Ben Murdoch558790d2013-07-30 15:19:42 +01001218 "PNaCl has been disabled (e.g., by setting "
1219 "the --disable-pnacl flag).");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001220 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001221 } else {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001222 // 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));
1235 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001236 return;
1237 }
1238 }
1239 // Failed to select the program and/or the translator.
1240 ReportLoadError(error_info);
1241}
1242
1243void 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;
1259 }
1260 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (resolved url='%s')\n",
1261 nmf_resolved_url.AsString().c_str()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001262 is_installed_ = GetUrlScheme(nmf_resolved_url.AsString()) ==
1263 SCHEME_CHROME_EXTENSION;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001264 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,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001278 false,
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001279 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,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001287 false,
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001288 NULL));
1289 }
1290}
1291
1292
1293bool 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.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01001301 bool is_pnacl = (mime_type() == kPnaclMIMEType);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001302 nacl::scoped_ptr<JsonManifest> json_manifest(
1303 new JsonManifest(url_util_,
1304 manifest_base_url(),
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +01001305 (is_pnacl ? kPortableISA : GetSandboxISA())));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001306 if (!json_manifest->Init(manifest_json, error_info)) {
1307 return false;
1308 }
1309 manifest_.reset(json_manifest.release());
1310 return true;
1311}
1312
1313void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error,
1314 FileDownloader*& url_downloader,
1315 PP_CompletionCallback callback) {
Ben Murdochbbcdd452013-07-25 10:06:34 +01001316 PLUGIN_PRINTF(("Plugin::UrlDidOpen (pp_error=%" NACL_PRId32
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001317 ", 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);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001321 struct NaClFileInfo info = scoped_url_downloader->GetFileInfo();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001322
1323 if (pp_error != PP_OK) {
1324 PP_RunCompletionCallback(&callback, pp_error);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001325 } else if (info.desc > NACL_NO_FILE_DESC) {
1326 url_file_info_map_[url_downloader->url_to_open()] = info;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001327 PP_RunCompletionCallback(&callback, PP_OK);
1328 } else {
1329 PP_RunCompletionCallback(&callback, PP_ERROR_FAILED);
1330 }
1331}
1332
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001333struct 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;
1343 }
1344 return info;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001345}
1346
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001347bool 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);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001353 // 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;
1363 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001364
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;
1369 }
1370
1371 pp::CompletionCallback open_callback = callback_factory_.NewCallback(
1372 &Plugin::UrlDidOpenForStreamAsFile, downloader, callback);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001373 // If true, will always call the callback on success or failure.
1374 return downloader->Open(url,
1375 DOWNLOAD_TO_FILE,
1376 open_callback,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001377 true,
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001378 &UpdateDownloadProgress);
1379}
1380
1381
1382void 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);
1393
1394 // UMA
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001395 HistogramEnumerateLoadStatus(ERROR_LOAD_SUCCESS, is_installed_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001396}
1397
1398
1399// TODO(ncbray): report UMA stats
1400void Plugin::ReportLoadError(const ErrorInfo& error_info) {
1401 PLUGIN_PRINTF(("Plugin::ReportLoadError (error='%s')\n",
1402 error_info.message().c_str()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001403 // 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);
1410 }
1411
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001412 // 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);
Ben Murdochca12bfa2013-07-23 11:17:05 +01001419 AddToConsole(nacl::string("NaCl module load failed: ") +
1420 error_info.console_message());
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001421 // Inform JavaScript that loading encountered an error and is complete.
1422 EnqueueProgressEvent(kProgressEventError);
1423 EnqueueProgressEvent(kProgressEventLoadEnd);
1424
1425 // UMA
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001426 HistogramEnumerateLoadStatus(error_info.error_code(), is_installed_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001427}
1428
1429
1430void 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);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001439 // Inform JavaScript that loading was aborted and is complete.
1440 EnqueueProgressEvent(kProgressEventAbort);
1441 EnqueueProgressEvent(kProgressEventLoadEnd);
1442
1443 // UMA
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001444 HistogramEnumerateLoadStatus(ERROR_LOAD_ABORTED, is_installed_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001445}
1446
1447void 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;
1463
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;
1473
1474 plugin->EnqueueProgressEvent(kProgressEventProgress,
1475 url,
1476 length_computable,
1477 bytes_received,
1478 total_bytes_to_be_received);
1479 }
1480 }
1481}
1482
1483const 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;
1494 }
1495 ++it;
1496 }
1497 }
1498 return file_downloader;
1499}
1500
1501void 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);
1507}
1508
1509void 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, "
Ben Murdochbbcdd452013-07-25 10:06:34 +01001516 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001517 event_type,
1518 url.c_str(),
1519 static_cast<int>(length_computable),
1520 loaded_bytes,
1521 total_bytes));
1522
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);
1535}
1536
1537void Plugin::ReportSelLdrLoadStatus(int status) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001538 HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status),
1539 is_installed_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001540}
1541
1542void Plugin::DispatchProgressEvent(int32_t result) {
1543 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (result=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001544 NACL_PRId32 ")\n", result));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001545 if (result < 0) {
1546 return;
1547 }
1548 if (progress_events_.empty()) {
1549 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent: no pending events\n"));
1550 return;
1551 }
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, "
Ben Murdochbbcdd452013-07-25 10:06:34 +01001556 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001557 event->event_type(),
1558 event->url(),
1559 static_cast<int>(event->length_computable()),
1560 event->loaded_bytes(),
1561 event->total_bytes()));
1562
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 "})";
1576
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;
1586 }
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;
1594 }
1595
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()));
1604
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"));
1611 }
1612}
1613
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001614bool 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;
1625
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001626 uint64_t file_token_lo = 0;
1627 uint64_t file_token_hi = 0;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001628 PP_FileHandle file_handle =
1629 nacl_interface()->OpenNaClExecutable(pp_instance(),
1630 url.c_str(),
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001631 &file_token_lo, &file_token_hi);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001632 // We shouldn't hit this if the file URL is in an installed app.
1633 if (file_handle == PP_kInvalidFileHandle)
1634 return false;
1635
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001636 // FileDownloader takes ownership of the file handle.
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001637 downloader->OpenFast(url, file_handle, file_token_lo, file_token_hi);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001638 return true;
1639}
1640
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001641UrlSchemeType 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);
1646
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;
1651 }
1652
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()));
1657
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;
1665}
1666
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001667bool Plugin::DocumentCanRequest(const std::string& url) {
1668 CHECK(url_util_ != NULL);
1669 return url_util_->DocumentCanRequest(this, pp::Var(url));
1670}
1671
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001672void 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()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001683 const PPB_Console* console_interface =
1684 static_cast<const PPB_Console*>(
1685 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001686 console_interface->LogWithSource(pp_instance(), PP_LOGLEVEL_LOG, prefix, str);
1687 var_interface->Release(prefix);
1688 var_interface->Release(str);
1689}
1690
1691} // namespace plugin