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