// -*- c++ -*-
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The portable representation of an instance and root scriptable object.
// The PPAPI version of the plugin instantiates a subclass of this class.
#ifndef NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_
#define NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_
#include <stdio.h>
#include <map>
#include <queue>
#include <set>
#include <string>
#include "native_client/src/include/nacl_macros.h"
#include "native_client/src/include/nacl_scoped_ptr.h"
#include "native_client/src/include/nacl_string.h"
#include "native_client/src/trusted/validator/nacl_file_info.h"
#include "ppapi/c/private/ppb_nacl_private.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/private/uma_private.h"
#include "ppapi/cpp/url_loader.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/view.h"
#include "ppapi/native_client/src/trusted/plugin/file_downloader.h"
#include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
#include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
#include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
#include "ppapi/native_client/src/trusted/plugin/utility.h"
namespace nacl {
class DescWrapper;
class DescWrapperFactory;
} // namespace nacl
namespace pp {
class CompletionCallback;
class URLLoader;
class URLUtil_Dev;
}
namespace plugin {
class ErrorInfo;
class Manifest;
class Plugin : public pp::Instance {
public:
// Factory method for creation.
static Plugin* New(PP_Instance instance);
// ----- Methods inherited from pp::Instance:
// Initializes this plugin with <embed/object ...> tag attribute count |argc|,
// names |argn| and values |argn|. Returns false on failure.
// Gets called by the browser right after New().
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
// Handles document load, when the plugin is a MIME type handler.
virtual bool HandleDocumentLoad(const pp::URLLoader& url_loader);
// ----- Plugin interface support.
// Load support.
// NaCl module can be loaded given a DescWrapper.
//
// Starts NaCl module but does not wait until low-level
// initialization (e.g., ld.so dynamic loading of manifest files) is
// done. The module will become ready later, asynchronously. Other
// event handlers should block until the module is ready before
// trying to communicate with it, i.e., until nacl_ready_state is
// DONE.
//
// NB: currently we do not time out, so if the untrusted code
// does not signal that it is ready, then we will deadlock the main
// thread of the renderer on this subsequent event delivery. We
// should include a time-out at which point we declare the
// nacl_ready_state to be done, and let the normal crash detection
// mechanism(s) take over.
//
// Updates nacl_module_origin() and nacl_module_url().
void LoadNaClModule(nacl::DescWrapper* wrapper,
bool uses_nonsfi_mode,
bool enable_dyncode_syscalls,
bool enable_exception_handling,
bool enable_crash_throttling,
const pp::CompletionCallback& init_done_cb,
const pp::CompletionCallback& crash_cb);
// Finish hooking interfaces up, after low-level initialization is
// complete.
bool LoadNaClModuleContinuationIntern(ErrorInfo* error_info);
// Continuation for starting SRPC/JSProxy services as appropriate.
// This is invoked as a callback when the NaCl module makes the
// init_done reverse RPC to tell us that low-level initialization
// such as ld.so processing is done. That initialization requires
// that the main thread be free in order to do Pepper
// main-thread-only operations such as file processing.
bool LoadNaClModuleContinuation(int32_t pp_error);
// Load support.
// A helper SRPC NaCl module can be loaded given a DescWrapper.
// Blocks until the helper module signals initialization is done.
// Does not update nacl_module_origin().
// Returns NULL or the NaClSubprocess of the new helper NaCl module.
NaClSubprocess* LoadHelperNaClModule(const nacl::string& helper_url,
nacl::DescWrapper* wrapper,
const Manifest* manifest,
ErrorInfo* error_info);
// Returns the argument value for the specified key, or NULL if not found.
std::string LookupArgument(const std::string& key) const;
enum LengthComputable {
LENGTH_IS_NOT_COMPUTABLE = 0,
LENGTH_IS_COMPUTABLE = 1
};
// Report successful loading of a module.
void ReportLoadSuccess(uint64_t loaded_bytes, uint64_t total_bytes);
// Report an error that was encountered while loading a module.
void ReportLoadError(const ErrorInfo& error_info);
// Report loading a module was aborted, typically due to user action.
void ReportLoadAbort();
// Dispatch a JavaScript event to indicate a key step in loading.
// |event_type| is a character string indicating which type of progress
// event (loadstart, progress, error, abort, load, loadend). Events are
// enqueued on the JavaScript event loop, which then calls back through
// DispatchProgressEvent.
void EnqueueProgressEvent(PP_NaClEventType event_type);
void EnqueueProgressEvent(PP_NaClEventType event_type,
const nacl::string& url,
LengthComputable length_computable,
uint64_t loaded_bytes,
uint64_t total_bytes);
// Report the error code that sel_ldr produces when starting a nexe.
void ReportSelLdrLoadStatus(int status);
// URL resolution support.
// plugin_base_url is the URL used for resolving relative URLs used in
// src="...".
nacl::string plugin_base_url() const { return plugin_base_url_; }
void set_plugin_base_url(const nacl::string& url) { plugin_base_url_ = url; }
// manifest_base_url is the URL used for resolving relative URLs mentioned
// in manifest files. If the manifest is a data URI, this is an empty string.
nacl::string manifest_base_url() const { return manifest_base_url_; }
void set_manifest_base_url(const nacl::string& url) {
manifest_base_url_ = url;
}
nacl::DescWrapperFactory* wrapper_factory() const { return wrapper_factory_; }
// Requests a NaCl manifest download from a |url| relative to the page origin.
void RequestNaClManifest(const nacl::string& url);
// The size returned when a file download operation is unable to determine
// the size of the file to load. W3C ProgressEvents specify that unknown
// sizes return 0.
static const uint64_t kUnknownBytes = 0;
// Called back by CallOnMainThread. Dispatches the first enqueued progress
// event.
void DispatchProgressEvent(int32_t result);
// Requests a URL asynchronously resulting in a call to pp_callback with
// a PP_Error indicating status. On success an open file descriptor
// corresponding to the url body is recorded for further lookup.
bool StreamAsFile(const nacl::string& url,
const pp::CompletionCallback& callback);
// Returns rich information for a file retrieved by StreamAsFile(). This info
// contains a file descriptor. The caller must take ownership of this
// descriptor.
struct NaClFileInfo GetFileInfo(const nacl::string& url);
// A helper function that indicates if |url| can be requested by the document
// under the same-origin policy. Strictly speaking, it may be possible for the
// document to request the URL using CORS even if this function returns false.
bool DocumentCanRequest(const std::string& url);
// The MIME type used to instantiate this instance of the NaCl plugin.
// Typically, the MIME type will be application/x-nacl. However, if the NEXE
// is being used as a content type handler for another content type (such as
// PDF), then this function will return that type.
const nacl::string& mime_type() const { return mime_type_; }
// The default MIME type for the NaCl plugin.
static const char* const kNaClMIMEType;
// The MIME type for the plugin when using PNaCl.
static const char* const kPnaclMIMEType;
// Returns true if PPAPI Dev interfaces should be allowed.
bool enable_dev_interfaces() { return enable_dev_interfaces_; }
Manifest const* manifest() const { return manifest_.get(); }
const pp::URLUtil_Dev* url_util() const { return url_util_; }
// set_exit_status may be called off the main thread.
void set_exit_status(int exit_status);
const PPB_NaCl_Private* nacl_interface() const { return nacl_interface_; }
pp::UMAPrivate& uma_interface() { return uma_interface_; }
private:
NACL_DISALLOW_COPY_AND_ASSIGN(Plugin);
// Prevent construction and destruction from outside the class:
// must use factory New() method instead.
explicit Plugin(PP_Instance instance);
// The browser will invoke the destructor via the pp::Instance
// pointer to this object, not from base's Delete().
~Plugin();
bool EarlyInit(int argc, const char* argn[], const char* argv[]);
// Shuts down socket connection, service runtime, and receive thread,
// in this order, for the main nacl subprocess.
void ShutDownSubprocesses();
// Access the service runtime for the main NaCl subprocess.
ServiceRuntime* main_service_runtime() const {
return main_subprocess_.service_runtime();
}
// Histogram helper functions, internal to Plugin so they can use
// uma_interface_ normally.
void HistogramTimeSmall(const std::string& name, int64_t ms);
void HistogramTimeMedium(const std::string& name, int64_t ms);
void HistogramTimeLarge(const std::string& name, int64_t ms);
void HistogramSizeKB(const std::string& name, int32_t sample);
void HistogramEnumerate(const std::string& name,
int sample,
int maximum,
int out_of_range_replacement);
void HistogramEnumerateOsArch(const std::string& sandbox_isa);
void HistogramEnumerateLoadStatus(PP_NaClError error_code);
void HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code);
void HistogramEnumerateManifestIsDataURI(bool is_data_uri);
void HistogramHTTPStatusCode(const std::string& name, int status);
// Load a nacl module from the file specified in wrapper.
// Only to be used from a background (non-main) thread.
// This will fully initialize the |subprocess| if the load was successful.
bool LoadNaClModuleFromBackgroundThread(nacl::DescWrapper* wrapper,
NaClSubprocess* subprocess,
const Manifest* manifest,
const SelLdrStartParams& params);
// Start sel_ldr from the main thread, given the start params.
// |pp_error| is set by CallOnMainThread (should be PP_OK).
void StartSelLdrOnMainThread(int32_t pp_error,
ServiceRuntime* service_runtime,
const SelLdrStartParams& params,
pp::CompletionCallback callback);
// Signals that StartSelLdr has finished.
void SignalStartSelLdrDone(int32_t pp_error,
bool* started,
ServiceRuntime* service_runtime);
void LoadNexeAndStart(int32_t pp_error,
nacl::DescWrapper* wrapper,
ServiceRuntime* service_runtime,
const pp::CompletionCallback& crash_cb);
// Callback used when getting the URL for the .nexe file. If the URL loading
// is successful, the file descriptor is opened and can be passed to sel_ldr
// with the sandbox on.
void NexeFileDidOpen(int32_t pp_error);
void NexeFileDidOpenContinuation(int32_t pp_error);
// Callback used when the reverse channel closes. This is an
// asynchronous event that might turn into a JavaScript error or
// crash event -- this is controlled by the two state variables
// nacl_ready_state_ and nexe_error_reported_: If an error or crash
// had already been reported, no additional crash event is
// generated. If no error has been reported but nacl_ready_state_
// is not DONE, then the loadend event has not been reported, and we
// enqueue an error event followed by loadend. If nacl_ready_state_
// is DONE, then we are in the post-loadend (we need temporal
// predicate symbols), and we enqueue a crash event.
void NexeDidCrash(int32_t pp_error);
// Callback used when a .nexe is translated from bitcode. If the translation
// is successful, the file descriptor is opened and can be passed to sel_ldr
// with the sandbox on.
void BitcodeDidTranslate(int32_t pp_error);
void BitcodeDidTranslateContinuation(int32_t pp_error);
// NaCl ISA selection manifest file support. The manifest file is specified
// using the "nacl" attribute in the <embed> tag. First, the manifest URL (or
// data: URI) is fetched, then the JSON is parsed. Once a valid .nexe is
// chosen for the sandbox ISA, any current service runtime is shut down, the
// .nexe is loaded and run.
// Callback used when getting the manifest file as a buffer (e.g., data URIs)
void NaClManifestBufferReady(int32_t pp_error);
// Callback used when getting the manifest file as a local file descriptor.
void NaClManifestFileDidOpen(int32_t pp_error);
// Processes the JSON manifest string and starts loading the nexe.
void ProcessNaClManifest(const nacl::string& manifest_json);
// Parses the JSON in |manifest_json| and retains a Manifest in
// |manifest_| for use by subsequent resource lookups.
// On success, |true| is returned and |manifest_| is updated to
// contain a Manifest that is used by SelectNexeURLFromManifest.
// On failure, |false| is returned, and |manifest_| is unchanged.
bool SetManifestObject(const nacl::string& manifest_json,
ErrorInfo* error_info);
// Logs timing information to a UMA histogram, and also logs the same timing
// information divided by the size of the nexe to another histogram.
void HistogramStartupTimeSmall(const std::string& name, float dt);
void HistogramStartupTimeMedium(const std::string& name, float dt);
// This NEXE is being used as a content type handler rather than directly by
// an HTML document.
bool NexeIsContentHandler() const;
// Callback used when loading a URL for SRPC-based StreamAsFile().
void UrlDidOpenForStreamAsFile(int32_t pp_error,
FileDownloader* url_downloader,
pp::CompletionCallback pp_callback);
// Open an app file by requesting a file descriptor from the browser. This
// method first checks that the url is for an installed file before making the
// request so it won't slow down non-installed file downloads.
bool OpenURLFast(const nacl::string& url, FileDownloader* downloader);
void SetExitStatusOnMainThread(int32_t pp_error, int exit_status);
std::map<std::string, std::string> args_;
// Keep track of the NaCl module subprocess that was spun up in the plugin.
NaClSubprocess main_subprocess_;
nacl::string plugin_base_url_;
nacl::string manifest_base_url_;
nacl::string manifest_url_;
bool uses_nonsfi_mode_;
bool nexe_error_reported_; // error or crash reported
nacl::DescWrapperFactory* wrapper_factory_;
// File download support. |nexe_downloader_| can be opened with a specific
// callback to run when the file has been downloaded and is opened for
// reading. We use one downloader for all URL downloads to prevent issuing
// multiple GETs that might arrive out of order. For example, this will
// prevent a GET of a NaCl manifest while a .nexe GET is pending. Note that
// this will also prevent simultaneous handling of multiple .nexes on a page.
FileDownloader nexe_downloader_;
pp::CompletionCallbackFactory<Plugin> callback_factory_;
nacl::scoped_ptr<PnaclCoordinator> pnacl_coordinator_;
// The manifest dictionary. Used for looking up resources to be loaded.
nacl::scoped_ptr<Manifest> manifest_;
// URL processing interface for use in looking up resources in manifests.
const pp::URLUtil_Dev* url_util_;
// PPAPI Dev interfaces are disabled by default.
bool enable_dev_interfaces_;
// A flag indicating if the NaCl executable is being loaded from an installed
// application. This flag is used to bucket UMA statistics more precisely to
// help determine whether nexe loading problems are caused by networking
// issues. (Installed applications will be loaded from disk.)
// Unfortunately, the definition of what it means to be part of an installed
// application is a little murky - for example an installed application can
// register a mime handler that loads NaCl executables into an arbitrary web
// page. As such, the flag actually means "our best guess, based on the URLs
// for NaCl resources that we have seen so far".
bool is_installed_;
// If we get a DidChangeView event before the nexe is loaded, we store it and
// replay it to nexe after it's loaded. We need to replay when this View
// resource is non-is_null().
pp::View view_to_replay_;
// If we get a HandleDocumentLoad event before the nexe is loaded, we store
// it and replay it to nexe after it's loaded. We need to replay when this
// URLLoader resource is non-is_null().
pp::URLLoader document_load_to_replay_;
nacl::string mime_type_;
// Keep track of the FileDownloaders created to fetch urls.
std::set<FileDownloader*> url_downloaders_;
// Keep track of file descriptors opened by StreamAsFile().
// These are owned by the browser.
std::map<nacl::string, NaClFileInfoAutoCloser*> url_file_info_map_;
// Used for NexeFileDidOpenContinuation
int64_t load_start_;
int64_t init_time_;
int64_t ready_time_;
size_t nexe_size_;
// Callback to receive .nexe and .dso download progress notifications.
static void UpdateDownloadProgress(
PP_Instance pp_instance,
PP_Resource pp_resource,
int64_t bytes_sent,
int64_t total_bytes_to_be_sent,
int64_t bytes_received,
int64_t total_bytes_to_be_received);
// Finds the file downloader which owns the given URL loader. This is used
// in UpdateDownloadProgress to map a url loader back to the URL being
// downloaded.
const FileDownloader* FindFileDownloader(PP_Resource url_loader) const;
int64_t time_of_last_progress_event_;
int exit_status_;
const PPB_NaCl_Private* nacl_interface_;
pp::UMAPrivate uma_interface_;
};
} // namespace plugin
#endif // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_PLUGIN_H_