Home | History | Annotate | Download | only in plugin
      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 
     67 namespace plugin {
     68 
     69 namespace {
     70 
     71 const 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).
     75 const 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.
     80 const char* const kNaClManifestAttribute = "nacl";
     81 // The pseudo-ISA used to indicate portable native client.
     82 const 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.
     86 const 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 '@'.
     90 const char* const kDevAttribute = "@dev";
     91 
     92 // URL schemes that we treat in special ways.
     93 const char* const kChromeExtensionUriScheme = "chrome-extension";
     94 const char* const kDataUriScheme = "data";
     95 
     96 // The key used to find the dictionary nexe URLs in the manifest file.
     97 const char* const kNexesKey = "nexes";
     98 
     99 // Up to 20 seconds
    100 const int64_t kTimeSmallMin = 1;         // in ms
    101 const int64_t kTimeSmallMax = 20000;     // in ms
    102 const uint32_t kTimeSmallBuckets = 100;
    103 
    104 // Up to 3 minutes, 20 seconds
    105 const int64_t kTimeMediumMin = 10;         // in ms
    106 const int64_t kTimeMediumMax = 200000;     // in ms
    107 const uint32_t kTimeMediumBuckets = 100;
    108 
    109 // Up to 33 minutes.
    110 const int64_t kTimeLargeMin = 100;         // in ms
    111 const int64_t kTimeLargeMax = 2000000;     // in ms
    112 const uint32_t kTimeLargeBuckets = 100;
    113 
    114 const int64_t kSizeKBMin = 1;
    115 const int64_t kSizeKBMax = 512*1024;     // very large .nexe
    116 const uint32_t kSizeKBBuckets = 100;
    117 
    118 const 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 
    125 const 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 
    132 void 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 
    144 void 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 
    156 void 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 
    168 void 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 
    180 void 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 
    195 void 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 
    226 void 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 
    238 void 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 
    250 void HistogramEnumerateManifestIsDataURI(bool is_data_uri) {
    251   HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1);
    252 }
    253 
    254 void 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 
    267 static int const kAbiHeaderBuffer = 256;  // must be at least EI_ABIVERSION + 1
    268 
    269 void 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 
    276 bool 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 
    282 bool 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 
    294 void 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 
    300 void 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 
    306 void 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 
    312 bool 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 
    364 void 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 
    378 void 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 
    394 bool 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 
    446 bool 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 
    474 bool 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 
    512 NaClSubprocess* 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 
    568 char* 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/
    580 const char* const Plugin::kProgressEventLoadStart = "loadstart";
    581 const char* const Plugin::kProgressEventProgress =  "progress";
    582 const char* const Plugin::kProgressEventError =     "error";
    583 const char* const Plugin::kProgressEventAbort =     "abort";
    584 const char* const Plugin::kProgressEventLoad =      "load";
    585 const char* const Plugin::kProgressEventLoadEnd =   "loadend";
    586 // Define a NaCl specific event type for .nexe crashes.
    587 const char* const Plugin::kProgressEventCrash =     "crash";
    588 
    589 class 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 
    620 const char* const Plugin::kNaClMIMEType = "application/x-nacl";
    621 const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
    622 
    623 bool 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 
    634 Plugin* 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.
    648 bool 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 
    726 Plugin::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 
    752 Plugin::~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 
    811 bool 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 
    819 pp::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 
    831 void 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 
    839 void 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 
    847 void 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 
    918 void 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 
    946 static 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 
    952 void 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 
    968 void 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 
   1019 void 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 
   1044 void 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 
   1060 void 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 
   1086 void Plugin::NaClManifestBufferReady(int32_t pp_error) {
   1087   PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%"
   1088                  NACL_PRId32 ")\n", pp_error));
   1089   ErrorInfo error_info;
   1090   set_manifest_url(nexe_downloader_.url());
   1091   if (pp_error != PP_OK) {
   1092     if (pp_error == PP_ERROR_ABORTED) {
   1093       ReportLoadAbort();
   1094     } else {
   1095       error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
   1096                            "could not load manifest url.");
   1097       ReportLoadError(error_info);
   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 
   1123 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
   1124   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
   1125                  NACL_PRId32 ")\n", pp_error));
   1126   HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
   1127                      nexe_downloader_.TimeSinceOpenMilliseconds());
   1128   HistogramHTTPStatusCode(
   1129       is_installed_ ?
   1130           "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" :
   1131           "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp",
   1132       nexe_downloader_.status_code());
   1133   ErrorInfo error_info;
   1134   // The manifest file was successfully opened.  Set the src property on the
   1135   // plugin now, so that the full url is available to error handlers.
   1136   set_manifest_url(nexe_downloader_.url());
   1137   struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
   1138   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%"
   1139                  NACL_PRId32 ")\n", info.desc));
   1140   if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
   1141     if (pp_error == PP_ERROR_ABORTED) {
   1142       ReportLoadAbort();
   1143     } else if (pp_error == PP_ERROR_NOACCESS) {
   1144       error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL,
   1145                            "access to manifest url was denied.");
   1146       ReportLoadError(error_info);
   1147     } else {
   1148       error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
   1149                            "could not load manifest url.");
   1150       ReportLoadError(error_info);
   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 
   1194 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) {
   1195   HistogramSizeKB("NaCl.Perf.Size.Manifest",
   1196                   static_cast<int32_t>(manifest_json.length() / 1024));
   1197   nacl::string program_url;
   1198   PnaclOptions pnacl_options;
   1199   ErrorInfo error_info;
   1200   if (!SetManifestObject(manifest_json, &error_info)) {
   1201     ReportLoadError(error_info);
   1202     return;
   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 
   1258 void 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 
   1308 bool 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 
   1328 void 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 
   1348 struct 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 
   1362 bool 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 
   1397 void 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
   1415 void 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 
   1445 void 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 
   1462 void 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 
   1498 const 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 
   1516 void 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 
   1524 void 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 
   1552 void Plugin::ReportSelLdrLoadStatus(int status) {
   1553   HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status),
   1554                                      is_installed_);
   1555 }
   1556 
   1557 void 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 
   1629 bool 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 
   1656 UrlSchemeType 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 
   1682 bool Plugin::DocumentCanRequest(const std::string& url) {
   1683   CHECK(url_util_ != NULL);
   1684   return url_util_->DocumentCanRequest(this, pp::Var(url));
   1685 }
   1686 
   1687 void 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