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