1 // -*- c++ -*- 2 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 6 // The portable representation of an instance and root scriptable object. 7 // The PPAPI version of the plugin instantiates a subclass of this class. 8 9 #ifndef NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ 10 #define NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ 11 12 #include <stdio.h> 13 14 #include <map> 15 #include <queue> 16 #include <set> 17 #include <string> 18 19 #include "native_client/src/include/nacl_macros.h" 20 #include "native_client/src/include/nacl_scoped_ptr.h" 21 #include "native_client/src/include/nacl_string.h" 22 #include "native_client/src/trusted/validator/nacl_file_info.h" 23 24 #include "ppapi/c/private/ppb_nacl_private.h" 25 #include "ppapi/cpp/private/var_private.h" 26 // for pp::VarPrivate 27 #include "ppapi/cpp/private/instance_private.h" 28 #include "ppapi/cpp/rect.h" 29 #include "ppapi/cpp/url_loader.h" 30 #include "ppapi/cpp/var.h" 31 #include "ppapi/cpp/view.h" 32 33 #include "ppapi/native_client/src/trusted/plugin/file_downloader.h" 34 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h" 35 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h" 36 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" 37 #include "ppapi/native_client/src/trusted/plugin/utility.h" 38 39 struct NaClSrpcChannel; 40 41 namespace nacl { 42 class DescWrapper; 43 class DescWrapperFactory; 44 } // namespace nacl 45 46 namespace pp { 47 class Find_Dev; 48 class MouseLock; 49 class Printing_Dev; 50 class Selection_Dev; 51 class URLLoader; 52 class URLUtil_Dev; 53 class Zoom_Dev; 54 } 55 56 namespace ppapi_proxy { 57 class BrowserPpp; 58 } 59 60 namespace plugin { 61 62 class ErrorInfo; 63 class Manifest; 64 class ProgressEvent; 65 class ScriptablePlugin; 66 67 class Plugin : public pp::InstancePrivate { 68 public: 69 // Factory method for creation. 70 static Plugin* New(PP_Instance instance); 71 72 // ----- Methods inherited from pp::Instance: 73 74 // Initializes this plugin with <embed/object ...> tag attribute count |argc|, 75 // names |argn| and values |argn|. Returns false on failure. 76 // Gets called by the browser right after New(). 77 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); 78 79 // Handles document load, when the plugin is a MIME type handler. 80 virtual bool HandleDocumentLoad(const pp::URLLoader& url_loader); 81 82 // Returns a scriptable reference to this plugin element. 83 // Called by JavaScript document.getElementById(plugin_id). 84 virtual pp::Var GetInstanceObject(); 85 86 // ----- Plugin interface support. 87 88 // Load support. 89 // NaCl module can be loaded given a DescWrapper. 90 // 91 // Starts NaCl module but does not wait until low-level 92 // initialization (e.g., ld.so dynamic loading of manifest files) is 93 // done. The module will become ready later, asynchronously. Other 94 // event handlers should block until the module is ready before 95 // trying to communicate with it, i.e., until nacl_ready_state is 96 // DONE. 97 // 98 // NB: currently we do not time out, so if the untrusted code 99 // does not signal that it is ready, then we will deadlock the main 100 // thread of the renderer on this subsequent event delivery. We 101 // should include a time-out at which point we declare the 102 // nacl_ready_state to be done, and let the normal crash detection 103 // mechanism(s) take over. 104 // 105 // Updates nacl_module_origin() and nacl_module_url(). 106 bool LoadNaClModule(nacl::DescWrapper* wrapper, ErrorInfo* error_info, 107 bool enable_dyncode_syscalls, 108 bool enable_exception_handling, 109 const pp::CompletionCallback& init_done_cb, 110 const pp::CompletionCallback& crash_cb); 111 112 // Finish hooking interfaces up, after low-level initialization is 113 // complete. 114 bool LoadNaClModuleContinuationIntern(ErrorInfo* error_info); 115 116 // Continuation for starting SRPC/JSProxy services as appropriate. 117 // This is invoked as a callback when the NaCl module makes the 118 // init_done reverse RPC to tell us that low-level initialization 119 // such as ld.so processing is done. That initialization requires 120 // that the main thread be free in order to do Pepper 121 // main-thread-only operations such as file processing. 122 bool LoadNaClModuleContinuation(int32_t pp_error); 123 124 // Load support. 125 // A helper SRPC NaCl module can be loaded given a DescWrapper. 126 // Blocks until the helper module signals initialization is done. 127 // Does not update nacl_module_origin(). 128 // Returns NULL or the NaClSubprocess of the new helper NaCl module. 129 NaClSubprocess* LoadHelperNaClModule(nacl::DescWrapper* wrapper, 130 const Manifest* manifest, 131 ErrorInfo* error_info); 132 133 // Returns the argument value for the specified key, or NULL if not found. 134 // The callee retains ownership of the result. 135 char* LookupArgument(const char* key); 136 137 enum LengthComputable { 138 LENGTH_IS_NOT_COMPUTABLE = 0, 139 LENGTH_IS_COMPUTABLE = 1 140 }; 141 // Report successful loading of a module. 142 void ReportLoadSuccess(LengthComputable length_computable, 143 uint64_t loaded_bytes, 144 uint64_t total_bytes); 145 // Report an error that was encountered while loading a module. 146 void ReportLoadError(const ErrorInfo& error_info); 147 // Report loading a module was aborted, typically due to user action. 148 void ReportLoadAbort(); 149 150 // Write a text string on the JavaScript console. 151 void AddToConsole(const nacl::string& text); 152 153 // Dispatch a JavaScript event to indicate a key step in loading. 154 // |event_type| is a character string indicating which type of progress 155 // event (loadstart, progress, error, abort, load, loadend). Events are 156 // enqueued on the JavaScript event loop, which then calls back through 157 // DispatchProgressEvent. 158 void EnqueueProgressEvent(const char* event_type); 159 void EnqueueProgressEvent(const char* event_type, 160 const nacl::string& url, 161 LengthComputable length_computable, 162 uint64_t loaded_bytes, 163 uint64_t total_bytes); 164 165 // Progress event types. 166 static const char* const kProgressEventLoadStart; 167 static const char* const kProgressEventProgress; 168 static const char* const kProgressEventError; 169 static const char* const kProgressEventAbort; 170 static const char* const kProgressEventLoad; 171 static const char* const kProgressEventLoadEnd; 172 static const char* const kProgressEventCrash; 173 174 // Report the error code that sel_ldr produces when starting a nexe. 175 void ReportSelLdrLoadStatus(int status); 176 177 // Report nexe death after load to JS and shut down the proxy. 178 void ReportDeadNexe(); 179 180 // The embed/object tag argument list. 181 int argc() const { return argc_; } 182 char** argn() const { return argn_; } 183 char** argv() const { return argv_; } 184 185 Plugin* plugin() const { return const_cast<Plugin*>(this); } 186 187 // URL resolution support. 188 // plugin_base_url is the URL used for resolving relative URLs used in 189 // src="...". 190 nacl::string plugin_base_url() const { return plugin_base_url_; } 191 void set_plugin_base_url(const nacl::string& url) { plugin_base_url_ = url; } 192 // manifest_base_url is the URL used for resolving relative URLs mentioned 193 // in manifest files. If the manifest is a data URI, this is an empty string. 194 nacl::string manifest_base_url() const { return manifest_base_url_; } 195 void set_manifest_base_url(const nacl::string& url) { 196 manifest_base_url_ = url; 197 } 198 199 // The URL of the manifest file as set by the "src" attribute. 200 // It is not the fully resolved URL if it was set as relative. 201 const nacl::string& manifest_url() const { return manifest_url_; } 202 void set_manifest_url(const nacl::string& manifest_url) { 203 manifest_url_ = manifest_url; 204 } 205 206 // The state of readiness of the plugin. 207 enum ReadyState { 208 // The trusted plugin begins in this ready state. 209 UNSENT = 0, 210 // The manifest file has been requested, but not yet received. 211 OPENED = 1, 212 // This state is unused. 213 HEADERS_RECEIVED = 2, 214 // The manifest file has been received and the nexe successfully requested. 215 LOADING = 3, 216 // The nexe has been loaded and the proxy started, so it is ready for 217 // interaction with the page. 218 DONE = 4 219 }; 220 ReadyState nacl_ready_state() const { return nacl_ready_state_; } 221 void set_nacl_ready_state(ReadyState nacl_ready_state) { 222 nacl_ready_state_ = nacl_ready_state; 223 } 224 bool nexe_error_reported() const { return nexe_error_reported_; } 225 void set_nexe_error_reported(bool val) { 226 nexe_error_reported_ = val; 227 } 228 229 nacl::DescWrapperFactory* wrapper_factory() const { return wrapper_factory_; } 230 231 // Requests a NaCl manifest download from a |url| relative to the page origin. 232 void RequestNaClManifest(const nacl::string& url); 233 234 // Support for property getting. 235 typedef void (Plugin::* PropertyGetter)(NaClSrpcArg* prop_value); 236 void AddPropertyGet(const nacl::string& prop_name, PropertyGetter getter); 237 bool HasProperty(const nacl::string& prop_name); 238 bool GetProperty(const nacl::string& prop_name, NaClSrpcArg* prop_value); 239 // The supported property getters. 240 void GetExitStatus(NaClSrpcArg* prop_value); 241 void GetLastError(NaClSrpcArg* prop_value); 242 void GetReadyStateProperty(NaClSrpcArg* prop_value); 243 244 // The size returned when a file download operation is unable to determine 245 // the size of the file to load. W3C ProgressEvents specify that unknown 246 // sizes return 0. 247 static const uint64_t kUnknownBytes = 0; 248 249 // Called back by CallOnMainThread. Dispatches the first enqueued progress 250 // event. 251 void DispatchProgressEvent(int32_t result); 252 253 // Requests a URL asynchronously resulting in a call to pp_callback with 254 // a PP_Error indicating status. On success an open file descriptor 255 // corresponding to the url body is recorded for further lookup. 256 bool StreamAsFile(const nacl::string& url, 257 PP_CompletionCallback pp_callback); 258 259 // Returns rich information for a file retrieved by StreamAsFile(). This info 260 // contains a file descriptor. The caller must take ownership of this 261 // descriptor. 262 struct NaClFileInfo GetFileInfo(const nacl::string& url); 263 264 // A helper function that gets the scheme type for |url|. Uses URLUtil_Dev 265 // interface which this class has as a member. 266 UrlSchemeType GetUrlScheme(const std::string& url); 267 268 // A helper function that indicates if |url| can be requested by the document 269 // under the same-origin policy. Strictly speaking, it may be possible for the 270 // document to request the URL using CORS even if this function returns false. 271 bool DocumentCanRequest(const std::string& url); 272 273 // Get the text description of the last error reported by the plugin. 274 const nacl::string& last_error_string() const { return last_error_string_; } 275 void set_last_error_string(const nacl::string& error) { 276 last_error_string_ = error; 277 } 278 279 // The MIME type used to instantiate this instance of the NaCl plugin. 280 // Typically, the MIME type will be application/x-nacl. However, if the NEXE 281 // is being used as a content type handler for another content type (such as 282 // PDF), then this function will return that type. 283 const nacl::string& mime_type() const { return mime_type_; } 284 // The default MIME type for the NaCl plugin. 285 static const char* const kNaClMIMEType; 286 // The MIME type for the plugin when using PNaCl. 287 static const char* const kPnaclMIMEType; 288 // Returns true if PPAPI Dev interfaces should be allowed. 289 bool enable_dev_interfaces() { return enable_dev_interfaces_; } 290 291 Manifest const* manifest() const { return manifest_.get(); } 292 const pp::URLUtil_Dev* url_util() const { return url_util_; } 293 294 // Extracts the exit status from the (main) service runtime. 295 int exit_status() const { 296 if (NULL == main_service_runtime()) { 297 return -1; 298 } 299 return main_service_runtime()->exit_status(); 300 } 301 302 const PPB_NaCl_Private* nacl_interface() const { return nacl_interface_; } 303 304 private: 305 NACL_DISALLOW_COPY_AND_ASSIGN(Plugin); 306 // Prevent construction and destruction from outside the class: 307 // must use factory New() method instead. 308 explicit Plugin(PP_Instance instance); 309 // The browser will invoke the destructor via the pp::Instance 310 // pointer to this object, not from base's Delete(). 311 ~Plugin(); 312 313 bool Init(int argc, char* argn[], char* argv[]); 314 // Shuts down socket connection, service runtime, and receive thread, 315 // in this order, for the main nacl subprocess. 316 void ShutDownSubprocesses(); 317 318 ScriptablePlugin* scriptable_plugin() const { return scriptable_plugin_; } 319 void set_scriptable_plugin(ScriptablePlugin* scriptable_plugin) { 320 scriptable_plugin_ = scriptable_plugin; 321 } 322 323 // Access the service runtime for the main NaCl subprocess. 324 ServiceRuntime* main_service_runtime() const { 325 return main_subprocess_.service_runtime(); 326 } 327 328 // Help load a nacl module, from the file specified in wrapper. 329 // This will fully initialize the |subprocess| if the load was successful. 330 bool LoadNaClModuleCommon(nacl::DescWrapper* wrapper, 331 NaClSubprocess* subprocess, 332 const Manifest* manifest, 333 bool should_report_uma, 334 const SelLdrStartParams& params, 335 const pp::CompletionCallback& init_done_cb, 336 const pp::CompletionCallback& crash_cb); 337 338 // Start sel_ldr from the main thread, given the start params. 339 // Sets |success| to true on success. 340 // |pp_error| is set by CallOnMainThread (should be PP_OK). 341 void StartSelLdrOnMainThread(int32_t pp_error, 342 ServiceRuntime* service_runtime, 343 const SelLdrStartParams& params, 344 bool* success); 345 346 // Callback used when getting the URL for the .nexe file. If the URL loading 347 // is successful, the file descriptor is opened and can be passed to sel_ldr 348 // with the sandbox on. 349 void NexeFileDidOpen(int32_t pp_error); 350 void NexeFileDidOpenContinuation(int32_t pp_error); 351 352 // Callback used when the reverse channel closes. This is an 353 // asynchronous event that might turn into a JavaScript error or 354 // crash event -- this is controlled by the two state variables 355 // nacl_ready_state_ and nexe_error_reported_: If an error or crash 356 // had already been reported, no additional crash event is 357 // generated. If no error has been reported but nacl_ready_state_ 358 // is not DONE, then the loadend event has not been reported, and we 359 // enqueue an error event followed by loadend. If nacl_ready_state_ 360 // is DONE, then we are in the post-loadend (we need temporal 361 // predicate symbols), and we enqueue a crash event. 362 void NexeDidCrash(int32_t pp_error); 363 364 // Callback used when a .nexe is translated from bitcode. If the translation 365 // is successful, the file descriptor is opened and can be passed to sel_ldr 366 // with the sandbox on. 367 void BitcodeDidTranslate(int32_t pp_error); 368 void BitcodeDidTranslateContinuation(int32_t pp_error); 369 370 // NaCl ISA selection manifest file support. The manifest file is specified 371 // using the "nacl" attribute in the <embed> tag. First, the manifest URL (or 372 // data: URI) is fetched, then the JSON is parsed. Once a valid .nexe is 373 // chosen for the sandbox ISA, any current service runtime is shut down, the 374 // .nexe is loaded and run. 375 376 // Callback used when getting the manifest file as a buffer (e.g., data URIs) 377 void NaClManifestBufferReady(int32_t pp_error); 378 379 // Callback used when getting the manifest file as a local file descriptor. 380 void NaClManifestFileDidOpen(int32_t pp_error); 381 382 // Processes the JSON manifest string and starts loading the nexe. 383 void ProcessNaClManifest(const nacl::string& manifest_json); 384 385 // Parses the JSON in |manifest_json| and retains a Manifest in 386 // |manifest_| for use by subsequent resource lookups. 387 // On success, |true| is returned and |manifest_| is updated to 388 // contain a Manifest that is used by SelectNexeURLFromManifest. 389 // On failure, |false| is returned, and |manifest_| is unchanged. 390 bool SetManifestObject(const nacl::string& manifest_json, 391 ErrorInfo* error_info); 392 393 // Logs timing information to a UMA histogram, and also logs the same timing 394 // information divided by the size of the nexe to another histogram. 395 void HistogramStartupTimeSmall(const std::string& name, float dt); 396 void HistogramStartupTimeMedium(const std::string& name, float dt); 397 398 // This NEXE is being used as a content type handler rather than directly by 399 // an HTML document. 400 bool NexeIsContentHandler() const; 401 402 // Callback used when loading a URL for SRPC-based StreamAsFile(). 403 void UrlDidOpenForStreamAsFile(int32_t pp_error, 404 FileDownloader*& url_downloader, 405 PP_CompletionCallback pp_callback); 406 407 // Copy the main service runtime's most recent NaClLog output to the 408 // JavaScript console. Valid to use only after a crash, e.g., via a 409 // detail level LOG_FATAL NaClLog entry. If the crash was not due 410 // to a LOG_FATAL this method will do nothing. 411 void CopyCrashLogToJsConsole(); 412 413 // Open an app file by requesting a file descriptor from the browser. This 414 // method first checks that the url is for an installed file before making the 415 // request so it won't slow down non-installed file downloads. 416 bool OpenURLFast(const nacl::string& url, FileDownloader* downloader); 417 418 ScriptablePlugin* scriptable_plugin_; 419 420 int argc_; 421 char** argn_; 422 char** argv_; 423 424 // Keep track of the NaCl module subprocess that was spun up in the plugin. 425 NaClSubprocess main_subprocess_; 426 427 nacl::string plugin_base_url_; 428 nacl::string manifest_base_url_; 429 nacl::string manifest_url_; 430 ReadyState nacl_ready_state_; 431 bool nexe_error_reported_; // error or crash reported 432 433 nacl::DescWrapperFactory* wrapper_factory_; 434 435 std::map<nacl::string, PropertyGetter> property_getters_; 436 437 // File download support. |nexe_downloader_| can be opened with a specific 438 // callback to run when the file has been downloaded and is opened for 439 // reading. We use one downloader for all URL downloads to prevent issuing 440 // multiple GETs that might arrive out of order. For example, this will 441 // prevent a GET of a NaCl manifest while a .nexe GET is pending. Note that 442 // this will also prevent simultaneous handling of multiple .nexes on a page. 443 FileDownloader nexe_downloader_; 444 pp::CompletionCallbackFactory<Plugin> callback_factory_; 445 446 nacl::scoped_ptr<PnaclCoordinator> pnacl_coordinator_; 447 448 // The manifest dictionary. Used for looking up resources to be loaded. 449 nacl::scoped_ptr<Manifest> manifest_; 450 // URL processing interface for use in looking up resources in manifests. 451 const pp::URLUtil_Dev* url_util_; 452 453 // A string containing the text description of the last error 454 // produced by this plugin. 455 nacl::string last_error_string_; 456 457 // PPAPI Dev interfaces are disabled by default. 458 bool enable_dev_interfaces_; 459 460 // A flag indicating if the NaCl executable is being loaded from an installed 461 // application. This flag is used to bucket UMA statistics more precisely to 462 // help determine whether nexe loading problems are caused by networking 463 // issues. (Installed applications will be loaded from disk.) 464 // Unfortunately, the definition of what it means to be part of an installed 465 // application is a little murky - for example an installed application can 466 // register a mime handler that loads NaCl executables into an arbitrary web 467 // page. As such, the flag actually means "our best guess, based on the URLs 468 // for NaCl resources that we have seen so far". 469 bool is_installed_; 470 471 // If we get a DidChangeView event before the nexe is loaded, we store it and 472 // replay it to nexe after it's loaded. We need to replay when this View 473 // resource is non-is_null(). 474 pp::View view_to_replay_; 475 476 // If we get a HandleDocumentLoad event before the nexe is loaded, we store 477 // it and replay it to nexe after it's loaded. We need to replay when this 478 // URLLoader resource is non-is_null(). 479 pp::URLLoader document_load_to_replay_; 480 481 nacl::string mime_type_; 482 483 // Keep track of the FileDownloaders created to fetch urls. 484 std::set<FileDownloader*> url_downloaders_; 485 // Keep track of file descriptors opened by StreamAsFile(). 486 // These are owned by the browser. 487 std::map<nacl::string, struct NaClFileInfo> url_file_info_map_; 488 489 // Pending progress events. 490 std::queue<ProgressEvent*> progress_events_; 491 492 // Used for NexeFileDidOpenContinuation 493 int64_t load_start_; 494 495 int64_t init_time_; 496 int64_t ready_time_; 497 size_t nexe_size_; 498 499 // Callback to receive .nexe and .dso download progress notifications. 500 static void UpdateDownloadProgress( 501 PP_Instance pp_instance, 502 PP_Resource pp_resource, 503 int64_t bytes_sent, 504 int64_t total_bytes_to_be_sent, 505 int64_t bytes_received, 506 int64_t total_bytes_to_be_received); 507 508 // Finds the file downloader which owns the given URL loader. This is used 509 // in UpdateDownloadProgress to map a url loader back to the URL being 510 // downloaded. 511 const FileDownloader* FindFileDownloader(PP_Resource url_loader) const; 512 513 int64_t time_of_last_progress_event_; 514 515 const PPB_NaCl_Private* nacl_interface_; 516 }; 517 518 } // namespace plugin 519 520 #endif // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_ 521