plugin.h revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/instance.h"
26#include "ppapi/cpp/private/uma_private.h"
27#include "ppapi/cpp/url_loader.h"
28#include "ppapi/cpp/var.h"
29#include "ppapi/cpp/view.h"
30
31#include "ppapi/native_client/src/trusted/plugin/file_downloader.h"
32#include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
33#include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
34#include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
35#include "ppapi/native_client/src/trusted/plugin/utility.h"
36
37namespace nacl {
38class DescWrapper;
39class DescWrapperFactory;
40}  // namespace nacl
41
42namespace pp {
43class CompletionCallback;
44class URLLoader;
45class URLUtil_Dev;
46}
47
48namespace plugin {
49
50class ErrorInfo;
51class Manifest;
52
53class Plugin : public pp::Instance {
54 public:
55  // Factory method for creation.
56  static Plugin* New(PP_Instance instance);
57
58  // ----- Methods inherited from pp::Instance:
59
60  // Initializes this plugin with <embed/object ...> tag attribute count |argc|,
61  // names |argn| and values |argn|. Returns false on failure.
62  // Gets called by the browser right after New().
63  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
64
65  // Handles document load, when the plugin is a MIME type handler.
66  virtual bool HandleDocumentLoad(const pp::URLLoader& url_loader);
67
68  // ----- Plugin interface support.
69
70  // Load support.
71  // NaCl module can be loaded given a DescWrapper.
72  //
73  // Starts NaCl module but does not wait until low-level
74  // initialization (e.g., ld.so dynamic loading of manifest files) is
75  // done.  The module will become ready later, asynchronously.  Other
76  // event handlers should block until the module is ready before
77  // trying to communicate with it, i.e., until nacl_ready_state is
78  // DONE.
79  //
80  // NB: currently we do not time out, so if the untrusted code
81  // does not signal that it is ready, then we will deadlock the main
82  // thread of the renderer on this subsequent event delivery.  We
83  // should include a time-out at which point we declare the
84  // nacl_ready_state to be done, and let the normal crash detection
85  // mechanism(s) take over.
86  //
87  // Updates nacl_module_origin() and nacl_module_url().
88  void LoadNaClModule(nacl::DescWrapper* wrapper,
89                      bool uses_nonsfi_mode,
90                      bool enable_dyncode_syscalls,
91                      bool enable_exception_handling,
92                      bool enable_crash_throttling,
93                      const pp::CompletionCallback& init_done_cb,
94                      const pp::CompletionCallback& crash_cb);
95
96  // Finish hooking interfaces up, after low-level initialization is
97  // complete.
98  bool LoadNaClModuleContinuationIntern();
99
100  // Continuation for starting SRPC/JSProxy services as appropriate.
101  // This is invoked as a callback when the NaCl module makes the
102  // init_done reverse RPC to tell us that low-level initialization
103  // such as ld.so processing is done.  That initialization requires
104  // that the main thread be free in order to do Pepper
105  // main-thread-only operations such as file processing.
106  bool LoadNaClModuleContinuation(int32_t pp_error);
107
108  // Load support.
109  // A helper SRPC NaCl module can be loaded given a DescWrapper.
110  // Blocks until the helper module signals initialization is done.
111  // Does not update nacl_module_origin().
112  // Returns NULL or the NaClSubprocess of the new helper NaCl module.
113  NaClSubprocess* LoadHelperNaClModule(const nacl::string& helper_url,
114                                       nacl::DescWrapper* wrapper,
115                                       const Manifest* manifest,
116                                       ErrorInfo* error_info);
117
118  // Returns the argument value for the specified key, or NULL if not found.
119  std::string LookupArgument(const std::string& key) const;
120
121  enum LengthComputable {
122    LENGTH_IS_NOT_COMPUTABLE = 0,
123    LENGTH_IS_COMPUTABLE = 1
124  };
125  // Report successful loading of a module.
126  void ReportLoadSuccess(uint64_t loaded_bytes, uint64_t total_bytes);
127  // Report an error that was encountered while loading a module.
128  void ReportLoadError(const ErrorInfo& error_info);
129  // Report loading a module was aborted, typically due to user action.
130  void ReportLoadAbort();
131
132  // Dispatch a JavaScript event to indicate a key step in loading.
133  // |event_type| is a character string indicating which type of progress
134  // event (loadstart, progress, error, abort, load, loadend).  Events are
135  // enqueued on the JavaScript event loop, which then calls back through
136  // DispatchProgressEvent.
137  void EnqueueProgressEvent(PP_NaClEventType event_type);
138  void EnqueueProgressEvent(PP_NaClEventType event_type,
139                            const nacl::string& url,
140                            LengthComputable length_computable,
141                            uint64_t loaded_bytes,
142                            uint64_t total_bytes);
143
144  // Report the error code that sel_ldr produces when starting a nexe.
145  void ReportSelLdrLoadStatus(int status);
146
147  // URL resolution support.
148  // plugin_base_url is the URL used for resolving relative URLs used in
149  // src="...".
150  nacl::string plugin_base_url() const { return plugin_base_url_; }
151  void set_plugin_base_url(const nacl::string& url) { plugin_base_url_ = url; }
152  // manifest_base_url is the URL used for resolving relative URLs mentioned
153  // in manifest files.  If the manifest is a data URI, this is an empty string.
154  nacl::string manifest_base_url() const { return manifest_base_url_; }
155  void set_manifest_base_url(const nacl::string& url) {
156    manifest_base_url_ = url;
157  }
158
159  nacl::DescWrapperFactory* wrapper_factory() const { return wrapper_factory_; }
160
161  // Requests a NaCl manifest download from a |url| relative to the page origin.
162  void RequestNaClManifest(const nacl::string& url);
163
164  // The size returned when a file download operation is unable to determine
165  // the size of the file to load.  W3C ProgressEvents specify that unknown
166  // sizes return 0.
167  static const uint64_t kUnknownBytes = 0;
168
169  // Called back by CallOnMainThread.  Dispatches the first enqueued progress
170  // event.
171  void DispatchProgressEvent(int32_t result);
172
173  // Requests a URL asynchronously resulting in a call to pp_callback with
174  // a PP_Error indicating status. On success an open file descriptor
175  // corresponding to the url body is recorded for further lookup.
176  bool StreamAsFile(const nacl::string& url,
177                    const pp::CompletionCallback& callback);
178
179  // Returns rich information for a file retrieved by StreamAsFile(). This info
180  // contains a file descriptor. The caller must take ownership of this
181  // descriptor.
182  struct NaClFileInfo GetFileInfo(const nacl::string& url);
183
184  // A helper function that indicates if |url| can be requested by the document
185  // under the same-origin policy. Strictly speaking, it may be possible for the
186  // document to request the URL using CORS even if this function returns false.
187  bool DocumentCanRequest(const std::string& url);
188
189  // The MIME type used to instantiate this instance of the NaCl plugin.
190  // Typically, the MIME type will be application/x-nacl.  However, if the NEXE
191  // is being used as a content type handler for another content type (such as
192  // PDF), then this function will return that type.
193  const nacl::string& mime_type() const { return mime_type_; }
194  // The default MIME type for the NaCl plugin.
195  static const char* const kNaClMIMEType;
196  // The MIME type for the plugin when using PNaCl.
197  static const char* const kPnaclMIMEType;
198  // Returns true if PPAPI Dev interfaces should be allowed.
199  bool enable_dev_interfaces() { return enable_dev_interfaces_; }
200
201  Manifest const* manifest() const { return manifest_.get(); }
202  const pp::URLUtil_Dev* url_util() const { return url_util_; }
203
204  // set_exit_status may be called off the main thread.
205  void set_exit_status(int exit_status);
206
207  const PPB_NaCl_Private* nacl_interface() const { return nacl_interface_; }
208  pp::UMAPrivate& uma_interface() { return uma_interface_; }
209
210 private:
211  NACL_DISALLOW_COPY_AND_ASSIGN(Plugin);
212  // Prevent construction and destruction from outside the class:
213  // must use factory New() method instead.
214  explicit Plugin(PP_Instance instance);
215  // The browser will invoke the destructor via the pp::Instance
216  // pointer to this object, not from base's Delete().
217  ~Plugin();
218
219  bool EarlyInit(int argc, const char* argn[], const char* argv[]);
220  // Shuts down socket connection, service runtime, and receive thread,
221  // in this order, for the main nacl subprocess.
222  void ShutDownSubprocesses();
223
224  // Access the service runtime for the main NaCl subprocess.
225  ServiceRuntime* main_service_runtime() const {
226    return main_subprocess_.service_runtime();
227  }
228
229  // Histogram helper functions, internal to Plugin so they can use
230  // uma_interface_ normally.
231  void HistogramTimeSmall(const std::string& name, int64_t ms);
232  void HistogramTimeMedium(const std::string& name, int64_t ms);
233  void HistogramTimeLarge(const std::string& name, int64_t ms);
234  void HistogramSizeKB(const std::string& name, int32_t sample);
235  void HistogramEnumerate(const std::string& name,
236                          int sample,
237                          int maximum,
238                          int out_of_range_replacement);
239  void HistogramEnumerateOsArch(const std::string& sandbox_isa);
240  void HistogramEnumerateLoadStatus(PP_NaClError error_code);
241  void HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code);
242  void HistogramEnumerateManifestIsDataURI(bool is_data_uri);
243  void HistogramHTTPStatusCode(const std::string& name, int status);
244
245  // Load a nacl module from the file specified in wrapper.
246  // Only to be used from a background (non-main) thread.
247  // This will fully initialize the |subprocess| if the load was successful.
248  bool LoadNaClModuleFromBackgroundThread(nacl::DescWrapper* wrapper,
249                                          NaClSubprocess* subprocess,
250                                          const Manifest* manifest,
251                                          const SelLdrStartParams& params);
252
253  // Start sel_ldr from the main thread, given the start params.
254  // |pp_error| is set by CallOnMainThread (should be PP_OK).
255  void StartSelLdrOnMainThread(int32_t pp_error,
256                               ServiceRuntime* service_runtime,
257                               const SelLdrStartParams& params,
258                               pp::CompletionCallback callback);
259
260  // Signals that StartSelLdr has finished.
261  void SignalStartSelLdrDone(int32_t pp_error,
262                             bool* started,
263                             ServiceRuntime* service_runtime);
264
265  void LoadNexeAndStart(int32_t pp_error,
266                        nacl::DescWrapper* wrapper,
267                        ServiceRuntime* service_runtime,
268                        const pp::CompletionCallback& crash_cb);
269
270  // Callback used when getting the URL for the .nexe file.  If the URL loading
271  // is successful, the file descriptor is opened and can be passed to sel_ldr
272  // with the sandbox on.
273  void NexeFileDidOpen(int32_t pp_error);
274  void NexeFileDidOpenContinuation(int32_t pp_error);
275
276  // Callback used when the reverse channel closes.  This is an
277  // asynchronous event that might turn into a JavaScript error or
278  // crash event -- this is controlled by the two state variables
279  // nacl_ready_state_ and nexe_error_reported_: If an error or crash
280  // had already been reported, no additional crash event is
281  // generated.  If no error has been reported but nacl_ready_state_
282  // is not DONE, then the loadend event has not been reported, and we
283  // enqueue an error event followed by loadend.  If nacl_ready_state_
284  // is DONE, then we are in the post-loadend (we need temporal
285  // predicate symbols), and we enqueue a crash event.
286  void NexeDidCrash(int32_t pp_error);
287
288  // Callback used when a .nexe is translated from bitcode.  If the translation
289  // is successful, the file descriptor is opened and can be passed to sel_ldr
290  // with the sandbox on.
291  void BitcodeDidTranslate(int32_t pp_error);
292  void BitcodeDidTranslateContinuation(int32_t pp_error);
293
294  // NaCl ISA selection manifest file support.  The manifest file is specified
295  // using the "nacl" attribute in the <embed> tag.  First, the manifest URL (or
296  // data: URI) is fetched, then the JSON is parsed.  Once a valid .nexe is
297  // chosen for the sandbox ISA, any current service runtime is shut down, the
298  // .nexe is loaded and run.
299
300  // Callback used when getting the manifest file as a buffer (e.g., data URIs)
301  void NaClManifestBufferReady(int32_t pp_error);
302
303  // Callback used when getting the manifest file as a local file descriptor.
304  void NaClManifestFileDidOpen(int32_t pp_error);
305
306  // Processes the JSON manifest string and starts loading the nexe.
307  void ProcessNaClManifest(const nacl::string& manifest_json);
308
309  // Parses the JSON in |manifest_json| and retains a Manifest in
310  // |manifest_| for use by subsequent resource lookups.
311  // On success, |true| is returned and |manifest_| is updated to
312  // contain a Manifest that is used by SelectNexeURLFromManifest.
313  // On failure, |false| is returned, and |manifest_| is unchanged.
314  bool SetManifestObject(const nacl::string& manifest_json,
315                         ErrorInfo* error_info);
316
317  // Logs timing information to a UMA histogram, and also logs the same timing
318  // information divided by the size of the nexe to another histogram.
319  void HistogramStartupTimeSmall(const std::string& name, float dt);
320  void HistogramStartupTimeMedium(const std::string& name, float dt);
321
322  // This NEXE is being used as a content type handler rather than directly by
323  // an HTML document.
324  bool NexeIsContentHandler() const;
325
326  // Callback used when loading a URL for SRPC-based StreamAsFile().
327  void UrlDidOpenForStreamAsFile(int32_t pp_error,
328                                 FileDownloader* url_downloader,
329                                 pp::CompletionCallback pp_callback);
330
331  // Open an app file by requesting a file descriptor from the browser. This
332  // method first checks that the url is for an installed file before making the
333  // request so it won't slow down non-installed file downloads.
334  bool OpenURLFast(const nacl::string& url, FileDownloader* downloader);
335
336  void SetExitStatusOnMainThread(int32_t pp_error, int exit_status);
337
338  std::map<std::string, std::string> args_;
339
340  // Keep track of the NaCl module subprocess that was spun up in the plugin.
341  NaClSubprocess main_subprocess_;
342
343  nacl::string plugin_base_url_;
344  nacl::string manifest_base_url_;
345  nacl::string manifest_url_;
346  bool uses_nonsfi_mode_;
347  bool nexe_error_reported_;  // error or crash reported
348
349  nacl::DescWrapperFactory* wrapper_factory_;
350
351  // File download support.  |nexe_downloader_| can be opened with a specific
352  // callback to run when the file has been downloaded and is opened for
353  // reading.  We use one downloader for all URL downloads to prevent issuing
354  // multiple GETs that might arrive out of order.  For example, this will
355  // prevent a GET of a NaCl manifest while a .nexe GET is pending.  Note that
356  // this will also prevent simultaneous handling of multiple .nexes on a page.
357  FileDownloader nexe_downloader_;
358  pp::CompletionCallbackFactory<Plugin> callback_factory_;
359
360  nacl::scoped_ptr<PnaclCoordinator> pnacl_coordinator_;
361
362  // The manifest dictionary.  Used for looking up resources to be loaded.
363  nacl::scoped_ptr<Manifest> manifest_;
364  // URL processing interface for use in looking up resources in manifests.
365  const pp::URLUtil_Dev* url_util_;
366
367  // PPAPI Dev interfaces are disabled by default.
368  bool enable_dev_interfaces_;
369
370  // A flag indicating if the NaCl executable is being loaded from an installed
371  // application.  This flag is used to bucket UMA statistics more precisely to
372  // help determine whether nexe loading problems are caused by networking
373  // issues.  (Installed applications will be loaded from disk.)
374  // Unfortunately, the definition of what it means to be part of an installed
375  // application is a little murky - for example an installed application can
376  // register a mime handler that loads NaCl executables into an arbitrary web
377  // page.  As such, the flag actually means "our best guess, based on the URLs
378  // for NaCl resources that we have seen so far".
379  bool is_installed_;
380
381  // If we get a DidChangeView event before the nexe is loaded, we store it and
382  // replay it to nexe after it's loaded. We need to replay when this View
383  // resource is non-is_null().
384  pp::View view_to_replay_;
385
386  // If we get a HandleDocumentLoad event before the nexe is loaded, we store
387  // it and replay it to nexe after it's loaded. We need to replay when this
388  // URLLoader resource is non-is_null().
389  pp::URLLoader document_load_to_replay_;
390
391  nacl::string mime_type_;
392
393  // Keep track of the FileDownloaders created to fetch urls.
394  std::set<FileDownloader*> url_downloaders_;
395  // Keep track of file descriptors opened by StreamAsFile().
396  // These are owned by the browser.
397  std::map<nacl::string, NaClFileInfoAutoCloser*> url_file_info_map_;
398
399  // Used for NexeFileDidOpenContinuation
400  int64_t load_start_;
401
402  int64_t init_time_;
403  int64_t ready_time_;
404
405  // Callback to receive .nexe and .dso download progress notifications.
406  static void UpdateDownloadProgress(
407      PP_Instance pp_instance,
408      PP_Resource pp_resource,
409      int64_t bytes_sent,
410      int64_t total_bytes_to_be_sent,
411      int64_t bytes_received,
412      int64_t total_bytes_to_be_received);
413
414  // Finds the file downloader which owns the given URL loader. This is used
415  // in UpdateDownloadProgress to map a url loader back to the URL being
416  // downloaded.
417  const FileDownloader* FindFileDownloader(PP_Resource url_loader) const;
418
419  int64_t time_of_last_progress_event_;
420  int exit_status_;
421
422  const PPB_NaCl_Private* nacl_interface_;
423  pp::UMAPrivate uma_interface_;
424};
425
426}  // namespace plugin
427
428#endif  // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_
429