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