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