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