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