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/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