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.
6 // The portable representation of an instance and root scriptable object.
7 // The PPAPI version of the plugin instantiates a subclass of this class.
9 #ifndef NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_
10 #define NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_
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"
24 #include "ppapi/c/private/ppb_nacl_private.h"
25 #include "ppapi/cpp/private/var_private.h"
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"
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"
39 struct NaClSrpcChannel
;
43 class DescWrapperFactory
;
56 namespace ppapi_proxy
{
65 class ScriptablePlugin
;
67 class Plugin
: public pp::InstancePrivate
{
69 // Factory method for creation.
70 static Plugin
* New(PP_Instance instance
);
72 // ----- Methods inherited from pp::Instance:
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
[]);
79 // Handles document load, when the plugin is a MIME type handler.
80 virtual bool HandleDocumentLoad(const pp::URLLoader
& url_loader
);
82 // Returns a scriptable reference to this plugin element.
83 // Called by JavaScript document.getElementById(plugin_id).
84 virtual pp::Var
GetInstanceObject();
86 // ----- Plugin interface support.
89 // NaCl module can be loaded given a DescWrapper.
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
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.
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 bool enable_crash_throttling
,
110 const pp::CompletionCallback
& init_done_cb
,
111 const pp::CompletionCallback
& crash_cb
);
113 // Finish hooking interfaces up, after low-level initialization is
115 bool LoadNaClModuleContinuationIntern(ErrorInfo
* error_info
);
117 // Continuation for starting SRPC/JSProxy services as appropriate.
118 // This is invoked as a callback when the NaCl module makes the
119 // init_done reverse RPC to tell us that low-level initialization
120 // such as ld.so processing is done. That initialization requires
121 // that the main thread be free in order to do Pepper
122 // main-thread-only operations such as file processing.
123 bool LoadNaClModuleContinuation(int32_t pp_error
);
126 // A helper SRPC NaCl module can be loaded given a DescWrapper.
127 // Blocks until the helper module signals initialization is done.
128 // Does not update nacl_module_origin().
129 // Returns NULL or the NaClSubprocess of the new helper NaCl module.
130 NaClSubprocess
* LoadHelperNaClModule(nacl::DescWrapper
* wrapper
,
131 const Manifest
* manifest
,
132 ErrorInfo
* error_info
);
134 // Returns the argument value for the specified key, or NULL if not found.
135 // The callee retains ownership of the result.
136 char* LookupArgument(const char* key
);
138 enum LengthComputable
{
139 LENGTH_IS_NOT_COMPUTABLE
= 0,
140 LENGTH_IS_COMPUTABLE
= 1
142 // Report successful loading of a module.
143 void ReportLoadSuccess(LengthComputable length_computable
,
144 uint64_t loaded_bytes
,
145 uint64_t total_bytes
);
146 // Report an error that was encountered while loading a module.
147 void ReportLoadError(const ErrorInfo
& error_info
);
148 // Report loading a module was aborted, typically due to user action.
149 void ReportLoadAbort();
151 // Write a text string on the JavaScript console.
152 void AddToConsole(const nacl::string
& text
);
154 // Dispatch a JavaScript event to indicate a key step in loading.
155 // |event_type| is a character string indicating which type of progress
156 // event (loadstart, progress, error, abort, load, loadend). Events are
157 // enqueued on the JavaScript event loop, which then calls back through
158 // DispatchProgressEvent.
159 void EnqueueProgressEvent(const char* event_type
);
160 void EnqueueProgressEvent(const char* event_type
,
161 const nacl::string
& url
,
162 LengthComputable length_computable
,
163 uint64_t loaded_bytes
,
164 uint64_t total_bytes
);
166 // Progress event types.
167 static const char* const kProgressEventLoadStart
;
168 static const char* const kProgressEventProgress
;
169 static const char* const kProgressEventError
;
170 static const char* const kProgressEventAbort
;
171 static const char* const kProgressEventLoad
;
172 static const char* const kProgressEventLoadEnd
;
173 static const char* const kProgressEventCrash
;
175 // Report the error code that sel_ldr produces when starting a nexe.
176 void ReportSelLdrLoadStatus(int status
);
178 // Report nexe death after load to JS and shut down the proxy.
179 void ReportDeadNexe();
181 // The embed/object tag argument list.
182 int argc() const { return argc_
; }
183 char** argn() const { return argn_
; }
184 char** argv() const { return argv_
; }
186 Plugin
* plugin() const { return const_cast<Plugin
*>(this); }
188 // URL resolution support.
189 // plugin_base_url is the URL used for resolving relative URLs used in
191 nacl::string
plugin_base_url() const { return plugin_base_url_
; }
192 void set_plugin_base_url(const nacl::string
& url
) { plugin_base_url_
= url
; }
193 // manifest_base_url is the URL used for resolving relative URLs mentioned
194 // in manifest files. If the manifest is a data URI, this is an empty string.
195 nacl::string
manifest_base_url() const { return manifest_base_url_
; }
196 void set_manifest_base_url(const nacl::string
& url
) {
197 manifest_base_url_
= url
;
200 // The URL of the manifest file as set by the "src" attribute.
201 // It is not the fully resolved URL if it was set as relative.
202 const nacl::string
& manifest_url() const { return manifest_url_
; }
203 void set_manifest_url(const nacl::string
& manifest_url
) {
204 manifest_url_
= manifest_url
;
207 // The state of readiness of the plugin.
209 // The trusted plugin begins in this ready state.
211 // The manifest file has been requested, but not yet received.
213 // This state is unused.
214 HEADERS_RECEIVED
= 2,
215 // The manifest file has been received and the nexe successfully requested.
217 // The nexe has been loaded and the proxy started, so it is ready for
218 // interaction with the page.
221 ReadyState
nacl_ready_state() const { return nacl_ready_state_
; }
222 void set_nacl_ready_state(ReadyState nacl_ready_state
) {
223 nacl_ready_state_
= nacl_ready_state
;
225 bool nexe_error_reported() const { return nexe_error_reported_
; }
226 void set_nexe_error_reported(bool val
) {
227 nexe_error_reported_
= val
;
230 nacl::DescWrapperFactory
* wrapper_factory() const { return wrapper_factory_
; }
232 // Requests a NaCl manifest download from a |url| relative to the page origin.
233 void RequestNaClManifest(const nacl::string
& url
);
235 // Support for property getting.
236 typedef void (Plugin::* PropertyGetter
)(NaClSrpcArg
* prop_value
);
237 void AddPropertyGet(const nacl::string
& prop_name
, PropertyGetter getter
);
238 bool HasProperty(const nacl::string
& prop_name
);
239 bool GetProperty(const nacl::string
& prop_name
, NaClSrpcArg
* prop_value
);
240 // The supported property getters.
241 void GetExitStatus(NaClSrpcArg
* prop_value
);
242 void GetLastError(NaClSrpcArg
* prop_value
);
243 void GetReadyStateProperty(NaClSrpcArg
* prop_value
);
245 // The size returned when a file download operation is unable to determine
246 // the size of the file to load. W3C ProgressEvents specify that unknown
248 static const uint64_t kUnknownBytes
= 0;
250 // Called back by CallOnMainThread. Dispatches the first enqueued progress
252 void DispatchProgressEvent(int32_t result
);
254 // Requests a URL asynchronously resulting in a call to pp_callback with
255 // a PP_Error indicating status. On success an open file descriptor
256 // corresponding to the url body is recorded for further lookup.
257 bool StreamAsFile(const nacl::string
& url
,
258 PP_CompletionCallback pp_callback
);
260 // Returns rich information for a file retrieved by StreamAsFile(). This info
261 // contains a file descriptor. The caller must take ownership of this
263 struct NaClFileInfo
GetFileInfo(const nacl::string
& url
);
265 // A helper function that gets the scheme type for |url|. Uses URLUtil_Dev
266 // interface which this class has as a member.
267 UrlSchemeType
GetUrlScheme(const std::string
& url
);
269 // A helper function that indicates if |url| can be requested by the document
270 // under the same-origin policy. Strictly speaking, it may be possible for the
271 // document to request the URL using CORS even if this function returns false.
272 bool DocumentCanRequest(const std::string
& url
);
274 // Get the text description of the last error reported by the plugin.
275 const nacl::string
& last_error_string() const { return last_error_string_
; }
276 void set_last_error_string(const nacl::string
& error
) {
277 last_error_string_
= error
;
280 // The MIME type used to instantiate this instance of the NaCl plugin.
281 // Typically, the MIME type will be application/x-nacl. However, if the NEXE
282 // is being used as a content type handler for another content type (such as
283 // PDF), then this function will return that type.
284 const nacl::string
& mime_type() const { return mime_type_
; }
285 // The default MIME type for the NaCl plugin.
286 static const char* const kNaClMIMEType
;
287 // The MIME type for the plugin when using PNaCl.
288 static const char* const kPnaclMIMEType
;
289 // Returns true if PPAPI Dev interfaces should be allowed.
290 bool enable_dev_interfaces() { return enable_dev_interfaces_
; }
292 Manifest
const* manifest() const { return manifest_
.get(); }
293 const pp::URLUtil_Dev
* url_util() const { return url_util_
; }
295 // Extracts the exit status from the (main) service runtime.
296 int exit_status() const {
297 if (NULL
== main_service_runtime()) {
300 return main_service_runtime()->exit_status();
303 const PPB_NaCl_Private
* nacl_interface() const { return nacl_interface_
; }
306 NACL_DISALLOW_COPY_AND_ASSIGN(Plugin
);
307 // Prevent construction and destruction from outside the class:
308 // must use factory New() method instead.
309 explicit Plugin(PP_Instance instance
);
310 // The browser will invoke the destructor via the pp::Instance
311 // pointer to this object, not from base's Delete().
314 bool Init(int argc
, char* argn
[], char* argv
[]);
315 // Shuts down socket connection, service runtime, and receive thread,
316 // in this order, for the main nacl subprocess.
317 void ShutDownSubprocesses();
319 ScriptablePlugin
* scriptable_plugin() const { return scriptable_plugin_
; }
320 void set_scriptable_plugin(ScriptablePlugin
* scriptable_plugin
) {
321 scriptable_plugin_
= scriptable_plugin
;
324 // Access the service runtime for the main NaCl subprocess.
325 ServiceRuntime
* main_service_runtime() const {
326 return main_subprocess_
.service_runtime();
329 // Help load a nacl module, from the file specified in wrapper.
330 // This will fully initialize the |subprocess| if the load was successful.
331 bool LoadNaClModuleCommon(nacl::DescWrapper
* wrapper
,
332 NaClSubprocess
* subprocess
,
333 const Manifest
* manifest
,
334 bool should_report_uma
,
335 const SelLdrStartParams
& params
,
336 const pp::CompletionCallback
& init_done_cb
,
337 const pp::CompletionCallback
& crash_cb
);
339 // Start sel_ldr from the main thread, given the start params.
340 // Sets |success| to true on success.
341 // |pp_error| is set by CallOnMainThread (should be PP_OK).
342 void StartSelLdrOnMainThread(int32_t pp_error
,
343 ServiceRuntime
* service_runtime
,
344 const SelLdrStartParams
& params
,
347 // Callback used when getting the URL for the .nexe file. If the URL loading
348 // is successful, the file descriptor is opened and can be passed to sel_ldr
349 // with the sandbox on.
350 void NexeFileDidOpen(int32_t pp_error
);
351 void NexeFileDidOpenContinuation(int32_t pp_error
);
353 // Callback used when the reverse channel closes. This is an
354 // asynchronous event that might turn into a JavaScript error or
355 // crash event -- this is controlled by the two state variables
356 // nacl_ready_state_ and nexe_error_reported_: If an error or crash
357 // had already been reported, no additional crash event is
358 // generated. If no error has been reported but nacl_ready_state_
359 // is not DONE, then the loadend event has not been reported, and we
360 // enqueue an error event followed by loadend. If nacl_ready_state_
361 // is DONE, then we are in the post-loadend (we need temporal
362 // predicate symbols), and we enqueue a crash event.
363 void NexeDidCrash(int32_t pp_error
);
365 // Callback used when a .nexe is translated from bitcode. If the translation
366 // is successful, the file descriptor is opened and can be passed to sel_ldr
367 // with the sandbox on.
368 void BitcodeDidTranslate(int32_t pp_error
);
369 void BitcodeDidTranslateContinuation(int32_t pp_error
);
371 // NaCl ISA selection manifest file support. The manifest file is specified
372 // using the "nacl" attribute in the <embed> tag. First, the manifest URL (or
373 // data: URI) is fetched, then the JSON is parsed. Once a valid .nexe is
374 // chosen for the sandbox ISA, any current service runtime is shut down, the
375 // .nexe is loaded and run.
377 // Callback used when getting the manifest file as a buffer (e.g., data URIs)
378 void NaClManifestBufferReady(int32_t pp_error
);
380 // Callback used when getting the manifest file as a local file descriptor.
381 void NaClManifestFileDidOpen(int32_t pp_error
);
383 // Processes the JSON manifest string and starts loading the nexe.
384 void ProcessNaClManifest(const nacl::string
& manifest_json
);
386 // Parses the JSON in |manifest_json| and retains a Manifest in
387 // |manifest_| for use by subsequent resource lookups.
388 // On success, |true| is returned and |manifest_| is updated to
389 // contain a Manifest that is used by SelectNexeURLFromManifest.
390 // On failure, |false| is returned, and |manifest_| is unchanged.
391 bool SetManifestObject(const nacl::string
& manifest_json
,
392 ErrorInfo
* error_info
);
394 // Logs timing information to a UMA histogram, and also logs the same timing
395 // information divided by the size of the nexe to another histogram.
396 void HistogramStartupTimeSmall(const std::string
& name
, float dt
);
397 void HistogramStartupTimeMedium(const std::string
& name
, float dt
);
399 // This NEXE is being used as a content type handler rather than directly by
401 bool NexeIsContentHandler() const;
403 // Callback used when loading a URL for SRPC-based StreamAsFile().
404 void UrlDidOpenForStreamAsFile(int32_t pp_error
,
405 FileDownloader
*& url_downloader
,
406 PP_CompletionCallback pp_callback
);
408 // Copy the main service runtime's most recent NaClLog output to the
409 // JavaScript console. Valid to use only after a crash, e.g., via a
410 // detail level LOG_FATAL NaClLog entry. If the crash was not due
411 // to a LOG_FATAL this method will do nothing.
412 void CopyCrashLogToJsConsole();
414 // Open an app file by requesting a file descriptor from the browser. This
415 // method first checks that the url is for an installed file before making the
416 // request so it won't slow down non-installed file downloads.
417 bool OpenURLFast(const nacl::string
& url
, FileDownloader
* downloader
);
419 ScriptablePlugin
* scriptable_plugin_
;
425 // Keep track of the NaCl module subprocess that was spun up in the plugin.
426 NaClSubprocess main_subprocess_
;
428 nacl::string plugin_base_url_
;
429 nacl::string manifest_base_url_
;
430 nacl::string manifest_url_
;
431 ReadyState nacl_ready_state_
;
432 bool nexe_error_reported_
; // error or crash reported
434 nacl::DescWrapperFactory
* wrapper_factory_
;
436 std::map
<nacl::string
, PropertyGetter
> property_getters_
;
438 // File download support. |nexe_downloader_| can be opened with a specific
439 // callback to run when the file has been downloaded and is opened for
440 // reading. We use one downloader for all URL downloads to prevent issuing
441 // multiple GETs that might arrive out of order. For example, this will
442 // prevent a GET of a NaCl manifest while a .nexe GET is pending. Note that
443 // this will also prevent simultaneous handling of multiple .nexes on a page.
444 FileDownloader nexe_downloader_
;
445 pp::CompletionCallbackFactory
<Plugin
> callback_factory_
;
447 nacl::scoped_ptr
<PnaclCoordinator
> pnacl_coordinator_
;
449 // The manifest dictionary. Used for looking up resources to be loaded.
450 nacl::scoped_ptr
<Manifest
> manifest_
;
451 // URL processing interface for use in looking up resources in manifests.
452 const pp::URLUtil_Dev
* url_util_
;
454 // A string containing the text description of the last error
455 // produced by this plugin.
456 nacl::string last_error_string_
;
458 // PPAPI Dev interfaces are disabled by default.
459 bool enable_dev_interfaces_
;
461 // A flag indicating if the NaCl executable is being loaded from an installed
462 // application. This flag is used to bucket UMA statistics more precisely to
463 // help determine whether nexe loading problems are caused by networking
464 // issues. (Installed applications will be loaded from disk.)
465 // Unfortunately, the definition of what it means to be part of an installed
466 // application is a little murky - for example an installed application can
467 // register a mime handler that loads NaCl executables into an arbitrary web
468 // page. As such, the flag actually means "our best guess, based on the URLs
469 // for NaCl resources that we have seen so far".
472 // If we get a DidChangeView event before the nexe is loaded, we store it and
473 // replay it to nexe after it's loaded. We need to replay when this View
474 // resource is non-is_null().
475 pp::View view_to_replay_
;
477 // If we get a HandleDocumentLoad event before the nexe is loaded, we store
478 // it and replay it to nexe after it's loaded. We need to replay when this
479 // URLLoader resource is non-is_null().
480 pp::URLLoader document_load_to_replay_
;
482 nacl::string mime_type_
;
484 // Keep track of the FileDownloaders created to fetch urls.
485 std::set
<FileDownloader
*> url_downloaders_
;
486 // Keep track of file descriptors opened by StreamAsFile().
487 // These are owned by the browser.
488 std::map
<nacl::string
, struct NaClFileInfo
> url_file_info_map_
;
490 // Pending progress events.
491 std::queue
<ProgressEvent
*> progress_events_
;
493 // Used for NexeFileDidOpenContinuation
500 // Callback to receive .nexe and .dso download progress notifications.
501 static void UpdateDownloadProgress(
502 PP_Instance pp_instance
,
503 PP_Resource pp_resource
,
505 int64_t total_bytes_to_be_sent
,
506 int64_t bytes_received
,
507 int64_t total_bytes_to_be_received
);
509 // Finds the file downloader which owns the given URL loader. This is used
510 // in UpdateDownloadProgress to map a url loader back to the URL being
512 const FileDownloader
* FindFileDownloader(PP_Resource url_loader
) const;
514 int64_t time_of_last_progress_event_
;
516 const PPB_NaCl_Private
* nacl_interface_
;
519 } // namespace plugin
521 #endif // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_