#include "components/nacl/renderer/ppb_nacl_private_impl.h"
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/command_line.h"
-#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/cpu.h"
+#include "base/files/file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/rand_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
#include "components/nacl/common/nacl_host_messages.h"
#include "components/nacl/common/nacl_messages.h"
+#include "components/nacl/common/nacl_nonsfi_util.h"
#include "components/nacl/common/nacl_switches.h"
#include "components/nacl/common/nacl_types.h"
+#include "components/nacl/renderer/file_downloader.h"
+#include "components/nacl/renderer/histogram.h"
+#include "components/nacl/renderer/json_manifest.h"
+#include "components/nacl/renderer/manifest_downloader.h"
+#include "components/nacl/renderer/manifest_service_channel.h"
#include "components/nacl/renderer/nexe_load_manager.h"
+#include "components/nacl/renderer/platform_info.h"
#include "components/nacl/renderer/pnacl_translation_resource_host.h"
-#include "components/nacl/renderer/sandbox_arch.h"
+#include "components/nacl/renderer/progress_event.h"
#include "components/nacl/renderer/trusted_plugin_channel.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/renderer_ppapi_host.h"
+#include "native_client/src/public/imc_types.h"
+#include "net/base/data_url.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_util.h"
#include "ppapi/c/pp_bool.h"
#include "ppapi/c/private/pp_file_handle.h"
-#include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/ppapi_permissions.h"
#include "ppapi/shared_impl/ppapi_preferences.h"
#include "ppapi/shared_impl/var.h"
+#include "ppapi/shared_impl/var_tracker.h"
#include "ppapi/thunk/enter.h"
+#include "third_party/WebKit/public/platform/WebURLLoader.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebPluginContainer.h"
+#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
+#include "third_party/jsoncpp/source/include/json/reader.h"
+#include "third_party/jsoncpp/source/include/json/value.h"
+namespace nacl {
namespace {
+// The pseudo-architecture used to indicate portable native client.
+const char* const kPortableArch = "portable";
+
+// The base URL for resources used by the PNaCl translator processes.
+const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
+
base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
-static bool InitializePnaclResourceHost() {
+bool InitializePnaclResourceHost() {
// Must run on the main thread.
content::RenderThread* render_thread = content::RenderThread::Get();
if (!render_thread)
return false;
- if (!g_pnacl_resource_host.Get()) {
+ if (!g_pnacl_resource_host.Get().get()) {
g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
render_thread->GetIOMessageLoopProxy());
- render_thread->AddFilter(g_pnacl_resource_host.Get());
+ render_thread->AddFilter(g_pnacl_resource_host.Get().get());
}
return true;
}
base::LazyInstance<InstanceInfoMap> g_instance_info =
LAZY_INSTANCE_INITIALIZER;
-typedef base::ScopedPtrHashMap<PP_Instance, nacl::NexeLoadManager>
- NexeLoadManagerMap;
-
-base::LazyInstance<NexeLoadManagerMap> g_load_manager_map =
- LAZY_INSTANCE_INITIALIZER;
-
-nacl::NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
- NexeLoadManagerMap& map = g_load_manager_map.Get();
- NexeLoadManagerMap::iterator iter = map.find(instance);
- if (iter != map.end())
- return iter->second;
- return NULL;
-}
+static const PP_NaClFileInfo kInvalidNaClFileInfo = {
+ PP_kInvalidFileHandle,
+ 0, // token_lo
+ 0, // token_hi
+};
-static int GetRoutingID(PP_Instance instance) {
+int GetRoutingID(PP_Instance instance) {
// Check that we are on the main renderer thread.
DCHECK(content::RenderThread::Get());
content::RendererPpapiHost *host =
return host->GetRoutingIDForWidget(instance);
}
+// Returns whether the channel_handle is valid or not.
+bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
+ if (channel_handle.name.empty()) {
+ return false;
+ }
+
+#if defined(OS_POSIX)
+ if (channel_handle.socket.fd == -1) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void PostPPCompletionCallback(PP_CompletionCallback callback,
+ int32_t status) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data, status));
+}
+
+bool ManifestResolveKey(PP_Instance instance,
+ bool is_helper_process,
+ const std::string& key,
+ std::string* full_url,
+ PP_PNaClOptions* pnacl_options);
+
+typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
+DownloadFileCallback;
+
+void DownloadFile(PP_Instance instance,
+ const std::string& url,
+ const DownloadFileCallback& callback);
+
+PP_Bool StartPpapiProxy(PP_Instance instance);
+
+// Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
+// Note that user_data is managed by the caller of LaunchSelLdr. Please see
+// also PP_ManifestService's comment for more details about resource
+// management.
+class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
+ public:
+ ManifestServiceProxy(PP_Instance pp_instance)
+ : pp_instance_(pp_instance) {
+ }
+
+ virtual ~ManifestServiceProxy() { }
+
+ virtual void StartupInitializationComplete() OVERRIDE {
+ if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
+ JsonManifest* manifest = GetJsonManifest(pp_instance_);
+ NexeLoadManager* load_manager = NexeLoadManager::Get(pp_instance_);
+ if (load_manager && manifest) {
+ std::string full_url;
+ PP_PNaClOptions pnacl_options;
+ bool uses_nonsfi_mode;
+ JsonManifest::ErrorInfo error_info;
+ if (manifest->GetProgramURL(&full_url,
+ &pnacl_options,
+ &uses_nonsfi_mode,
+ &error_info)) {
+ int64_t nexe_size = load_manager->nexe_size();
+ load_manager->ReportLoadSuccess(full_url, nexe_size, nexe_size);
+ }
+ }
+ }
+ }
+
+ virtual void OpenResource(
+ const std::string& key,
+ const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
+ DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
+ BelongsToCurrentThread());
+
+ std::string url;
+ // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
+ // have to initialize it like this here.
+ PP_PNaClOptions pnacl_options;
+ pnacl_options.translate = PP_FALSE;
+ pnacl_options.is_debug = PP_FALSE;
+ pnacl_options.opt_level = 2;
+ if (!ManifestResolveKey(pp_instance_, false, key, &url, &pnacl_options)) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, base::Passed(base::File()), 0, 0));
+ return;
+ }
+
+ // We have to call DidDownloadFile, even if this object is destroyed, so
+ // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
+ // callback passed to this function shouldn't have a weak pointer to an
+ // object either.
+ //
+ // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
+ // that would close the file handle on destruction.
+ DownloadFile(pp_instance_, url,
+ base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
+ }
+
+ private:
+ static void DidDownloadFile(
+ ManifestServiceChannel::OpenResourceCallback callback,
+ int32_t pp_error,
+ const PP_NaClFileInfo& file_info) {
+ if (pp_error != PP_OK) {
+ callback.Run(base::File(), 0, 0);
+ return;
+ }
+ callback.Run(base::File(file_info.handle),
+ file_info.token_lo,
+ file_info.token_hi);
+ }
+
+ PP_Instance pp_instance_;
+ DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
+};
+
+blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
+ const GURL& gurl) {
+ blink::WebURLLoaderOptions options;
+ options.untrustedHTTP = true;
+
+ // Options settings here follow the original behavior in the trusted
+ // plugin and PepperURLLoaderHost.
+ if (document.securityOrigin().canRequest(gurl)) {
+ options.allowCredentials = true;
+ } else {
+ // Allow CORS.
+ options.crossOriginRequestPolicy =
+ blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
+ }
+ return document.frame()->createAssociatedURLLoader(options);
+}
+
+blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
+ const GURL& gurl) {
+ blink::WebURLRequest request;
+ request.initialize();
+ request.setURL(gurl);
+ request.setFirstPartyForCookies(document.firstPartyForCookies());
+ return request;
+}
+
+int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
+ switch (status) {
+ case FileDownloader::SUCCESS:
+ return PP_OK;
+ case FileDownloader::ACCESS_DENIED:
+ return PP_ERROR_NOACCESS;
+ case FileDownloader::FAILED:
+ return PP_ERROR_FAILED;
+ // No default case, to catch unhandled Status values.
+ }
+ return PP_ERROR_FAILED;
+}
+
// Launch NaCl's sel_ldr process.
void LaunchSelLdr(PP_Instance instance,
+ PP_Bool main_service_runtime,
const char* alleged_url,
+ const PP_NaClFileInfo* nexe_file_info,
PP_Bool uses_irt,
PP_Bool uses_ppapi,
PP_Bool uses_nonsfi_mode,
PP_Bool enable_exception_handling,
PP_Bool enable_crash_throttling,
void* imc_handle,
- struct PP_Var* error_message,
PP_CompletionCallback callback) {
CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
BelongsToCurrentThread());
- nacl::FileDescriptor result_socket;
+ // Create the manifest service proxy here, so on error case, it will be
+ // destructed (without passing it to ManifestServiceChannel).
+ scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
+ new ManifestServiceProxy(instance));
+
+ FileDescriptor result_socket;
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
- *error_message = PP_MakeUndefined();
int routing_id = 0;
// If the nexe uses ppapi APIs, we need a routing ID.
// To get the routing ID, we must be on the main thread.
if (uses_ppapi) {
routing_id = GetRoutingID(instance);
if (!routing_id) {
+ if (nexe_file_info->handle != PP_kInvalidFileHandle) {
+ base::File closer(nexe_file_info->handle);
+ }
ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
FROM_HERE,
base::Bind(callback.func, callback.user_data,
instance_info.permissions =
ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
std::string error_message_string;
- nacl::NaClLaunchResult launch_result;
+ NaClLaunchResult launch_result;
+ IPC::PlatformFileForTransit nexe_for_transit =
+ IPC::InvalidPlatformFileForTransit();
+#if defined(OS_POSIX)
+ if (nexe_file_info->handle != PP_kInvalidFileHandle)
+ nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
+#elif defined(OS_WIN)
+ // Duplicate the handle on the browser side instead of the renderer.
+ // This is because BrokerGetFileForProcess isn't part of content/public, and
+ // it's simpler to do the duplication in the browser anyway.
+ nexe_for_transit = nexe_file_info->handle;
+#else
+#error Unsupported target platform.
+#endif
if (!sender->Send(new NaClHostMsg_LaunchNaCl(
- nacl::NaClLaunchParams(instance_info.url.spec(),
- routing_id,
- perm_bits,
- PP_ToBool(uses_irt),
- PP_ToBool(uses_nonsfi_mode),
- PP_ToBool(enable_dyncode_syscalls),
- PP_ToBool(enable_exception_handling),
- PP_ToBool(enable_crash_throttling)),
+ NaClLaunchParams(
+ instance_info.url.spec(),
+ nexe_for_transit,
+ nexe_file_info->token_lo,
+ nexe_file_info->token_hi,
+ routing_id,
+ perm_bits,
+ PP_ToBool(uses_irt),
+ PP_ToBool(uses_nonsfi_mode),
+ PP_ToBool(enable_dyncode_syscalls),
+ PP_ToBool(enable_exception_handling),
+ PP_ToBool(enable_crash_throttling)),
&launch_result,
&error_message_string))) {
ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
static_cast<int32_t>(PP_ERROR_FAILED)));
return;
}
+
if (!error_message_string.empty()) {
- *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
+ if (PP_ToBool(main_service_runtime)) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ if (load_manager) {
+ load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
+ "ServiceRuntime: failed to start",
+ error_message_string);
+ }
+ }
ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
FROM_HERE,
base::Bind(callback.func, callback.user_data,
instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
instance_info.plugin_pid = launch_result.plugin_pid;
instance_info.plugin_child_id = launch_result.plugin_child_id;
+
// Don't save instance_info if channel handle is invalid.
- bool invalid_handle = instance_info.channel_handle.name.empty();
-#if defined(OS_POSIX)
- if (!invalid_handle)
- invalid_handle = (instance_info.channel_handle.socket.fd == -1);
-#endif
- if (!invalid_handle)
+ if (IsValidChannelHandle(instance_info.channel_handle))
g_instance_info.Get()[instance] = instance_info;
- // Stash the trusted handle as well.
- invalid_handle = launch_result.trusted_ipc_channel_handle.name.empty();
-#if defined(OS_POSIX)
- if (!invalid_handle)
- invalid_handle = (launch_result.trusted_ipc_channel_handle.socket.fd == -1);
-#endif
- if (!invalid_handle) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
- DCHECK(load_manager);
- if (load_manager) {
- scoped_ptr<nacl::TrustedPluginChannel> trusted_plugin_channel(
- new nacl::TrustedPluginChannel(
- launch_result.trusted_ipc_channel_handle,
- callback,
- content::RenderThread::Get()->GetShutdownEvent()));
- load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
- }
+ *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
+
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (!load_manager) {
+ PostPPCompletionCallback(callback, PP_ERROR_FAILED);
+ base::SharedMemory::CloseHandle(launch_result.crash_info_shmem_handle);
+ return;
+ }
+
+ // Store the crash information shared memory handle.
+ load_manager->set_crash_info_shmem_handle(
+ launch_result.crash_info_shmem_handle);
+
+ // Create the trusted plugin channel.
+ if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
+ bool report_exit_status = PP_ToBool(main_service_runtime);
+ scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
+ new TrustedPluginChannel(
+ load_manager,
+ launch_result.trusted_ipc_channel_handle,
+ content::RenderThread::Get()->GetShutdownEvent(),
+ report_exit_status));
+ load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
+ } else {
+ PostPPCompletionCallback(callback, PP_ERROR_FAILED);
+ return;
}
- *(static_cast<NaClHandle*>(imc_handle)) =
- nacl::ToNativeHandle(result_socket);
+ // Create the manifest service handle as well.
+ // For security hardening, disable the IPCs for open_resource() when they
+ // aren't needed. PNaCl doesn't expose open_resource(). Note that
+ // enable_dyncode_syscalls is true if and only if the plugin is a non-PNaCl
+ // plugin.
+ if (load_manager &&
+ enable_dyncode_syscalls &&
+ IsValidChannelHandle(
+ launch_result.manifest_service_ipc_channel_handle)) {
+ scoped_ptr<ManifestServiceChannel> manifest_service_channel(
+ new ManifestServiceChannel(
+ launch_result.manifest_service_ipc_channel_handle,
+ base::Bind(&PostPPCompletionCallback, callback),
+ manifest_service_proxy.Pass(),
+ content::RenderThread::Get()->GetShutdownEvent()));
+ load_manager->set_manifest_service_channel(
+ manifest_service_channel.Pass());
+ } else {
+ // Currently, manifest service works only on linux/non-SFI mode.
+ // On other platforms, the socket will not be created, and thus this
+ // condition needs to be handled as success.
+ PostPPCompletionCallback(callback, PP_OK);
+ }
}
-PP_ExternalPluginResult StartPpapiProxy(PP_Instance instance) {
+PP_Bool StartPpapiProxy(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (!load_manager)
+ return PP_FALSE;
+
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!plugin_instance) {
+ DLOG(ERROR) << "GetInstance() failed";
+ return PP_FALSE;
+ }
+
InstanceInfoMap& map = g_instance_info.Get();
InstanceInfoMap::iterator it = map.find(instance);
if (it == map.end()) {
DLOG(ERROR) << "Could not find instance ID";
- return PP_EXTERNAL_PLUGIN_FAILED;
+ return PP_FALSE;
}
InstanceInfo instance_info = it->second;
map.erase(it);
- content::PepperPluginInstance* plugin_instance =
- content::PepperPluginInstance::Get(instance);
- if (!plugin_instance) {
- DLOG(ERROR) << "GetInstance() failed";
- return PP_EXTERNAL_PLUGIN_ERROR_MODULE;
- }
-
- return plugin_instance->SwitchToOutOfProcessProxy(
+ PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
base::FilePath().AppendASCII(instance_info.url.spec()),
instance_info.permissions,
instance_info.channel_handle,
instance_info.plugin_pid,
instance_info.plugin_child_id);
+
+ if (result == PP_EXTERNAL_PLUGIN_OK) {
+ // Log the amound of time that has passed between the trusted plugin being
+ // initialized and the untrusted plugin being initialized. This is
+ // (roughly) the cost of using NaCl, in terms of startup time.
+ load_manager->ReportStartupOverhead();
+ return PP_TRUE;
+ } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
+ load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
+ "could not initialize module.");
+ } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
+ load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
+ "could not create instance.");
+ }
+ return PP_FALSE;
}
int UrandomFD(void) {
#endif
}
-PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
+// Convert a URL to a filename for GetReadonlyPnaclFd.
+// Must be kept in sync with PnaclCanOpenFile() in
+// components/nacl/browser/nacl_file_host.cc.
+std::string PnaclComponentURLToFilename(const std::string& url) {
+ // PNaCl component URLs aren't arbitrary URLs; they are always either
+ // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
+ // So, it's safe to just use string parsing operations here instead of
+ // URL-parsing ones.
+ DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
+ std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
+
+ // Use white-listed-chars.
+ size_t replace_pos;
+ static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
+ replace_pos = r.find_first_not_of(white_list);
+ while(replace_pos != std::string::npos) {
+ r = r.replace(replace_pos, 1, "_");
+ replace_pos = r.find_first_not_of(white_list);
+ }
+ return r;
+}
+
+PP_FileHandle GetReadonlyPnaclFd(const char* url,
+ bool is_executable,
+ uint64_t* nonce_lo,
+ uint64_t* nonce_hi) {
+ std::string filename = PnaclComponentURLToFilename(url);
IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
- std::string(filename),
- &out_fd))) {
- return base::kInvalidPlatformFileValue;
+ std::string(filename), is_executable,
+ &out_fd, nonce_lo, nonce_hi))) {
+ return PP_kInvalidFileHandle;
}
if (out_fd == IPC::InvalidPlatformFileForTransit()) {
- return base::kInvalidPlatformFileValue;
+ return PP_kInvalidFileHandle;
}
- base::PlatformFile handle =
- IPC::PlatformFileForTransitToPlatformFile(out_fd);
- return handle;
+ return IPC::PlatformFileForTransitToPlatformFile(out_fd);
+}
+
+void GetReadExecPnaclFd(const char* url,
+ PP_NaClFileInfo* out_file_info) {
+ *out_file_info = kInvalidNaClFileInfo;
+ out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
+ &out_file_info->token_lo,
+ &out_file_info->token_hi);
}
PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
DCHECK(sender);
if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
&transit_fd))) {
- return base::kInvalidPlatformFileValue;
+ return PP_kInvalidFileHandle;
}
if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
- return base::kInvalidPlatformFileValue;
+ return PP_kInvalidFileHandle;
}
- base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
- transit_fd);
- return handle;
+ return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
}
int32_t GetNumberOfProcessors() {
- int32_t num_processors;
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
- if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
- return 1;
- }
- return num_processors;
+ int32_t num_processors = 1;
+ return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
+ num_processors : 1;
}
-PP_Bool IsNonSFIModeEnabled() {
-#if defined(OS_LINUX)
- return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableNaClNonSfiMode));
-#else
- return PP_FALSE;
-#endif
+PP_Bool PPIsNonSFIModeEnabled() {
+ return PP_FromBool(IsNonSFIModeEnabled());
}
-int32_t GetNexeFd(PP_Instance instance,
- const char* pexe_url,
- uint32_t abi_version,
- uint32_t opt_level,
- const char* last_modified,
- const char* etag,
- PP_Bool has_no_store_header,
- const char* sandbox_isa,
- const char* extra_flags,
- PP_Bool* is_hit,
- PP_FileHandle* handle,
- struct PP_CompletionCallback callback) {
- ppapi::thunk::EnterInstance enter(instance, callback);
- if (enter.failed())
- return enter.retval();
- if (!pexe_url || !last_modified || !etag || !is_hit || !handle)
- return enter.SetResult(PP_ERROR_BADARGUMENT);
- if (!InitializePnaclResourceHost())
- return enter.SetResult(PP_ERROR_FAILED);
-
- base::Time last_modified_time;
- // If FromString fails, it doesn't touch last_modified_time and we just send
- // the default-constructed null value.
- base::Time::FromString(last_modified, &last_modified_time);
-
- nacl::PnaclCacheInfo cache_info;
+void GetNexeFd(PP_Instance instance,
+ const std::string& pexe_url,
+ uint32_t opt_level,
+ const base::Time& last_modified_time,
+ const std::string& etag,
+ bool has_no_store_header,
+ base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
+ if (!InitializePnaclResourceHost()) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ static_cast<int32_t>(PP_ERROR_FAILED),
+ false,
+ PP_kInvalidFileHandle));
+ return;
+ }
+
+ PnaclCacheInfo cache_info;
cache_info.pexe_url = GURL(pexe_url);
- cache_info.abi_version = abi_version;
+ // TODO(dschuff): Get this value from the pnacl json file after it
+ // rolls in from NaCl.
+ cache_info.abi_version = 1;
cache_info.opt_level = opt_level;
cache_info.last_modified = last_modified_time;
- cache_info.etag = std::string(etag);
- cache_info.has_no_store_header = PP_ToBool(has_no_store_header);
- cache_info.sandbox_isa = std::string(sandbox_isa);
- cache_info.extra_flags = std::string(extra_flags);
+ cache_info.etag = etag;
+ cache_info.has_no_store_header = has_no_store_header;
+ cache_info.sandbox_isa = GetSandboxArch();
+ cache_info.extra_flags = GetCpuFeatures();
g_pnacl_resource_host.Get()->RequestNexeFd(
GetRoutingID(instance),
instance,
cache_info,
- is_hit,
- handle,
- enter.callback());
-
- return enter.SetResult(PP_OK_COMPLETIONPENDING);
+ callback);
}
-void ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
+void ReportTranslationFinished(PP_Instance instance,
+ PP_Bool success,
+ int32_t opt_level,
+ int64_t pexe_size,
+ int64_t compile_time_us) {
+ if (success == PP_TRUE) {
+ static const int32_t kUnknownOptLevel = 4;
+ if (opt_level < 0 || opt_level > 3)
+ opt_level = kUnknownOptLevel;
+ HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
+ opt_level,
+ kUnknownOptLevel + 1);
+ HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
+ pexe_size / 1024,
+ compile_time_us);
+ HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
+
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ if (load_manager) {
+ base::TimeDelta total_time = base::Time::Now() -
+ load_manager->pnacl_start_time();
+ HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
+ total_time.InMilliseconds());
+ HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
+ pexe_size / 1024,
+ total_time.InMicroseconds());
+ }
+ }
+
// If the resource host isn't initialized, don't try to do that here.
// Just return because something is already very wrong.
- if (g_pnacl_resource_host.Get() == NULL)
+ if (g_pnacl_resource_host.Get().get() == NULL)
return;
g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
}
const char* file_url,
uint64_t* nonce_lo,
uint64_t* nonce_hi) {
+ // Fast path only works for installed file URLs.
+ GURL gurl(file_url);
+ if (!gurl.SchemeIs("chrome-extension"))
+ return PP_kInvalidFileHandle;
+
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!plugin_instance)
+ return PP_kInvalidFileHandle;
+ // IMPORTANT: Make sure the document can request the given URL. If we don't
+ // check, a malicious app could probe the extension system. This enforces a
+ // same-origin policy which prevents the app from requesting resources from
+ // another app.
+ blink::WebSecurityOrigin security_origin =
+ plugin_instance->GetContainer()->element().document().securityOrigin();
+ if (!security_origin.canRequest(gurl))
+ return PP_kInvalidFileHandle;
+
IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
base::FilePath file_path;
if (!sender->Send(
new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
- GURL(file_url),
- &out_fd,
- nonce_lo,
- nonce_hi))) {
- return base::kInvalidPlatformFileValue;
+ GURL(file_url),
+ &out_fd,
+ nonce_lo,
+ nonce_hi))) {
+ return PP_kInvalidFileHandle;
}
- if (out_fd == IPC::InvalidPlatformFileForTransit()) {
- return base::kInvalidPlatformFileValue;
- }
+ if (out_fd == IPC::InvalidPlatformFileForTransit())
+ return PP_kInvalidFileHandle;
- base::PlatformFile handle =
- IPC::PlatformFileForTransitToPlatformFile(out_fd);
- return handle;
+ return IPC::PlatformFileForTransitToPlatformFile(out_fd);
}
-void DispatchEventOnMainThread(PP_Instance instance,
- PP_NaClEventType event_type,
- const std::string& resource_url,
- PP_Bool length_is_computable,
- uint64_t loaded_bytes,
- uint64_t total_bytes);
-
void DispatchEvent(PP_Instance instance,
PP_NaClEventType event_type,
const char *resource_url,
PP_Bool length_is_computable,
uint64_t loaded_bytes,
uint64_t total_bytes) {
- ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
- FROM_HERE,
- base::Bind(&DispatchEventOnMainThread,
- instance,
- event_type,
- std::string(resource_url),
- length_is_computable,
- loaded_bytes,
- total_bytes));
-}
-
-void DispatchEventOnMainThread(PP_Instance instance,
- PP_NaClEventType event_type,
- const std::string& resource_url,
- PP_Bool length_is_computable,
- uint64_t loaded_bytes,
- uint64_t total_bytes) {
- nacl::NexeLoadManager* load_manager =
- GetNexeLoadManager(instance);
- // The instance may have been destroyed after we were scheduled, so do
- // nothing if it's gone.
+ ProgressEvent event(event_type,
+ resource_url,
+ PP_ToBool(length_is_computable),
+ loaded_bytes,
+ total_bytes);
+ DispatchProgressEvent(instance, event);
+}
+
+void ReportLoadSuccess(PP_Instance instance,
+ uint64_t loaded_bytes,
+ uint64_t total_bytes) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager) {
- nacl::NexeLoadManager::ProgressEvent event(event_type);
- event.resource_url = resource_url;
- event.length_is_computable = PP_ToBool(length_is_computable);
- event.loaded_bytes = loaded_bytes;
- event.total_bytes = total_bytes;
- load_manager->DispatchEvent(event);
+ load_manager->ReportLoadSuccess(load_manager->program_url(),
+ loaded_bytes,
+ total_bytes);
}
}
-void SetReadOnlyProperty(PP_Instance instance,
- struct PP_Var key,
- struct PP_Var value) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+void ReportLoadError(PP_Instance instance,
+ PP_NaClError error,
+ const char* error_message) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager)
- load_manager->SetReadOnlyProperty(key, value);
+ load_manager->ReportLoadError(error, error_message);
}
-void ReportLoadError(PP_Instance instance,
- PP_NaClError error,
- const char* error_message,
- const char* console_message) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+void ReportLoadAbort(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager)
- load_manager->ReportLoadError(error, error_message, console_message);
+ load_manager->ReportLoadAbort();
}
void InstanceCreated(PP_Instance instance) {
- scoped_ptr<nacl::NexeLoadManager> new_load_manager(
- new nacl::NexeLoadManager(instance));
- NexeLoadManagerMap& map = g_load_manager_map.Get();
- DLOG_IF(ERROR, map.count(instance) != 0) << "Instance count should be 0";
- map.add(instance, new_load_manager.Pass());
+ NexeLoadManager::Create(instance);
}
void InstanceDestroyed(PP_Instance instance) {
- NexeLoadManagerMap& map = g_load_manager_map.Get();
- DLOG_IF(ERROR, map.count(instance) == 0) << "Could not find instance ID";
- map.erase(instance);
+ DeleteJsonManifest(instance);
+ NexeLoadManager::Delete(instance);
}
PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
return PP_FALSE;
- bool should_debug;
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
- if(!sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(
- GURL(alleged_nmf_url),
- &should_debug))) {
- return PP_FALSE;
- }
- return PP_FromBool(should_debug);
+ bool should_debug = false;
+ return PP_FromBool(
+ sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
+ &should_debug)) &&
+ should_debug);
}
-const char* GetSandboxArch() {
- return nacl::GetSandboxArch();
+void LogToConsole(PP_Instance instance, const char* message) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (load_manager)
+ load_manager->LogToConsole(std::string(message));
}
-PP_UrlSchemeType GetUrlScheme(PP_Var url) {
- scoped_refptr<ppapi::StringVar> url_string = ppapi::StringVar::FromPPVar(url);
- if (!url_string)
- return PP_SCHEME_OTHER;
+PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (load_manager)
+ return load_manager->nacl_ready_state();
+ return PP_NACL_READY_STATE_UNSENT;
+}
- GURL gurl(url_string->value());
- if (gurl.SchemeIs("chrome-extension"))
- return PP_SCHEME_CHROME_EXTENSION;
- if (gurl.SchemeIs("data"))
- return PP_SCHEME_DATA;
- return PP_SCHEME_OTHER;
+void Vlog(const char* message) {
+ VLOG(1) << message;
}
-void LogToConsole(PP_Instance instance, const char* message) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+void InitializePlugin(PP_Instance instance,
+ uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
if (load_manager)
- load_manager->LogToConsole(std::string(message));
+ load_manager->InitializePlugin(argc, argn, argv);
}
-PP_Bool GetNexeErrorReported(PP_Instance instance) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+int64_t GetNexeSize(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
if (load_manager)
- return PP_FromBool(load_manager->nexe_error_reported());
- return PP_FALSE;
+ return load_manager->nexe_size();
+ return 0;
}
-void SetNexeErrorReported(PP_Instance instance, PP_Bool error_reported) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+void DownloadManifestToBuffer(PP_Instance instance,
+ struct PP_CompletionCallback callback);
+
+bool CreateJsonManifest(PP_Instance instance,
+ const std::string& manifest_url,
+ const std::string& manifest_data);
+
+void RequestNaClManifest(PP_Instance instance,
+ PP_CompletionCallback callback) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
- if (load_manager)
- load_manager->set_nexe_error_reported(PP_ToBool(error_reported));
+ if (!load_manager) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data,
+ static_cast<int32_t>(PP_ERROR_FAILED)));
+ return;
+ }
+
+ std::string url = load_manager->GetManifestURLArgument();
+ if (url.empty() || !load_manager->RequestNaClManifest(url)) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data,
+ static_cast<int32_t>(PP_ERROR_FAILED)));
+ return;
+ }
+
+ const GURL& base_url = load_manager->manifest_base_url();
+ if (base_url.SchemeIs("data")) {
+ GURL gurl(base_url);
+ std::string mime_type;
+ std::string charset;
+ std::string data;
+ int32_t error = PP_ERROR_FAILED;
+ if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
+ if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
+ if (CreateJsonManifest(instance, base_url.spec(), data))
+ error = PP_OK;
+ } else {
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
+ "manifest file too large.");
+ }
+ } else {
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
+ "could not load manifest url.");
+ }
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data, error));
+ } else {
+ DownloadManifestToBuffer(instance, callback);
+ }
}
-PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+PP_Var GetManifestBaseURL(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
+ if (!load_manager)
+ return PP_MakeUndefined();
+ const GURL& gurl = load_manager->manifest_base_url();
+ if (!gurl.is_valid())
+ return PP_MakeUndefined();
+ return ppapi::StringVar::StringToPPVar(gurl.spec());
+}
+
+void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager)
- return load_manager->nacl_ready_state();
- return PP_NACL_READY_STATE_UNSENT;
+ load_manager->ProcessNaClManifest(program_url);
}
-void SetNaClReadyState(PP_Instance instance, PP_NaClReadyState ready_state) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
- DCHECK(load_manager);
+PP_Bool DevInterfacesEnabled(PP_Instance instance) {
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager)
- load_manager->set_nacl_ready_state(ready_state);
+ return PP_FromBool(load_manager->DevInterfacesEnabled());
+ return PP_FALSE;
}
-PP_Bool GetIsInstalled(PP_Instance instance) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+void DownloadManifestToBufferCompletion(PP_Instance instance,
+ struct PP_CompletionCallback callback,
+ base::Time start_time,
+ PP_NaClError pp_nacl_error,
+ const std::string& data);
+
+void DownloadManifestToBuffer(PP_Instance instance,
+ struct PP_CompletionCallback callback) {
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!load_manager || !plugin_instance) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data,
+ static_cast<int32_t>(PP_ERROR_FAILED)));
+ }
+ const blink::WebDocument& document =
+ plugin_instance->GetContainer()->element().document();
+
+ const GURL& gurl = load_manager->manifest_base_url();
+ scoped_ptr<blink::WebURLLoader> url_loader(
+ CreateWebURLLoader(document, gurl));
+ blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
+
+ // ManifestDownloader deletes itself after invoking the callback.
+ ManifestDownloader* manifest_downloader = new ManifestDownloader(
+ url_loader.Pass(),
+ load_manager->is_installed(),
+ base::Bind(DownloadManifestToBufferCompletion,
+ instance, callback, base::Time::Now()));
+ manifest_downloader->Load(request);
+}
+
+void DownloadManifestToBufferCompletion(PP_Instance instance,
+ struct PP_CompletionCallback callback,
+ base::Time start_time,
+ PP_NaClError pp_nacl_error,
+ const std::string& data) {
+ base::TimeDelta download_time = base::Time::Now() - start_time;
+ HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
+ download_time.InMilliseconds());
+
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ if (!load_manager) {
+ callback.func(callback.user_data, PP_ERROR_ABORTED);
+ return;
+ }
+
+ int32_t pp_error;
+ switch (pp_nacl_error) {
+ case PP_NACL_ERROR_LOAD_SUCCESS:
+ pp_error = PP_OK;
+ break;
+ case PP_NACL_ERROR_MANIFEST_LOAD_URL:
+ pp_error = PP_ERROR_FAILED;
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
+ "could not load manifest url.");
+ break;
+ case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
+ pp_error = PP_ERROR_FILETOOBIG;
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
+ "manifest file too large.");
+ break;
+ case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
+ pp_error = PP_ERROR_NOACCESS;
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
+ "access to manifest url was denied.");
+ break;
+ default:
+ NOTREACHED();
+ pp_error = PP_ERROR_FAILED;
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
+ "could not load manifest url.");
+ }
+
+ if (pp_error == PP_OK) {
+ std::string base_url = load_manager->manifest_base_url().spec();
+ if (!CreateJsonManifest(instance, base_url, data))
+ pp_error = PP_ERROR_FAILED;
+ }
+ callback.func(callback.user_data, pp_error);
+}
+
+bool CreateJsonManifest(PP_Instance instance,
+ const std::string& manifest_url,
+ const std::string& manifest_data) {
+ HistogramSizeKB("NaCl.Perf.Size.Manifest",
+ static_cast<int32_t>(manifest_data.length() / 1024));
+
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ if (!load_manager)
+ return false;
+
+ const char* isa_type;
+ if (load_manager->IsPNaCl())
+ isa_type = kPortableArch;
+ else
+ isa_type = GetSandboxArch();
+
+ scoped_ptr<nacl::JsonManifest> j(
+ new nacl::JsonManifest(
+ manifest_url.c_str(),
+ isa_type,
+ IsNonSFIModeEnabled(),
+ PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
+ JsonManifest::ErrorInfo error_info;
+ if (j->Init(manifest_data.c_str(), &error_info)) {
+ AddJsonManifest(instance, j.Pass());
+ return true;
+ }
+ load_manager->ReportLoadError(error_info.error, error_info.string);
+ return false;
+}
+
+PP_Bool ManifestGetProgramURL(PP_Instance instance,
+ PP_Var* pp_full_url,
+ PP_PNaClOptions* pnacl_options,
+ PP_Bool* pp_uses_nonsfi_mode) {
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+
+ JsonManifest* manifest = GetJsonManifest(instance);
+ if (manifest == NULL)
+ return PP_FALSE;
+
+ bool uses_nonsfi_mode;
+ std::string full_url;
+ JsonManifest::ErrorInfo error_info;
+ if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
+ &error_info)) {
+ *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
+ *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
+ return PP_TRUE;
+ }
+
if (load_manager)
- return PP_FromBool(load_manager->is_installed());
+ load_manager->ReportLoadError(error_info.error, error_info.string);
return PP_FALSE;
}
-void SetIsInstalled(PP_Instance instance, PP_Bool installed) {
- nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
+bool ManifestResolveKey(PP_Instance instance,
+ bool is_helper_process,
+ const std::string& key,
+ std::string* full_url,
+ PP_PNaClOptions* pnacl_options) {
+ // For "helper" processes (llc and ld, for PNaCl translation), we resolve
+ // keys manually as there is no existing .nmf file to parse.
+ if (is_helper_process) {
+ pnacl_options->translate = PP_FALSE;
+ // We can only resolve keys in the files/ namespace.
+ const std::string kFilesPrefix = "files/";
+ if (key.find(kFilesPrefix) == std::string::npos) {
+ nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ if (load_manager)
+ load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
+ "key did not start with files/");
+ return false;
+ }
+ std::string key_basename = key.substr(kFilesPrefix.length());
+ *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
+ key_basename;
+ return true;
+ }
+
+ JsonManifest* manifest = GetJsonManifest(instance);
+ if (manifest == NULL)
+ return false;
+
+ return manifest->ResolveKey(key, full_url, pnacl_options);
+}
+
+PP_Bool GetPNaClResourceInfo(PP_Instance instance,
+ PP_Var* llc_tool_name,
+ PP_Var* ld_tool_name) {
+ static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (!load_manager)
+ return PP_FALSE;
+
+ uint64_t nonce_lo = 0;
+ uint64_t nonce_hi = 0;
+ base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
+ &nonce_lo, &nonce_hi));
+ if (!file.IsValid()) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ "The Portable Native Client (pnacl) component is not "
+ "installed. Please consult chrome://components for more "
+ "information.");
+ return PP_FALSE;
+ }
+
+ base::File::Info file_info;
+ if (!file.GetInfo(&file_info)) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
+ kFilename);
+ return PP_FALSE;
+ }
+
+ if (file_info.size > 1 << 20) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
+ return PP_FALSE;
+ }
+
+ scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
+ if (buffer.get() == NULL) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
+ kFilename);
+ return PP_FALSE;
+ }
+
+ int rc = file.Read(0, buffer.get(), file_info.size);
+ if (rc < 0) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
+ return PP_FALSE;
+ }
+
+ // Null-terminate the bytes we we read from the file.
+ buffer.get()[rc] = 0;
+
+ // Expect the JSON file to contain a top-level object (dictionary).
+ Json::Reader json_reader;
+ Json::Value json_data;
+ if (!json_reader.parse(buffer.get(), json_data)) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ std::string("Parsing resource info failed: JSON parse error: ") +
+ json_reader.getFormattedErrorMessages());
+ return PP_FALSE;
+ }
+
+ if (!json_data.isObject()) {
+ load_manager->ReportLoadError(
+ PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
+ "Parsing resource info failed: Malformed JSON dictionary");
+ return PP_FALSE;
+ }
+
+ if (json_data.isMember("pnacl-llc-name")) {
+ Json::Value json_name = json_data["pnacl-llc-name"];
+ if (json_name.isString()) {
+ std::string llc_tool_name_str = json_name.asString();
+ *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
+ }
+ }
+
+ if (json_data.isMember("pnacl-ld-name")) {
+ Json::Value json_name = json_data["pnacl-ld-name"];
+ if (json_name.isString()) {
+ std::string ld_tool_name_str = json_name.asString();
+ *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
+ }
+ }
+ return PP_TRUE;
+}
+
+PP_Var GetCpuFeatureAttrs() {
+ return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
+}
+
+// Encapsulates some of the state for a call to DownloadNexe to prevent
+// argument lists from getting too long.
+struct DownloadNexeRequest {
+ PP_Instance instance;
+ std::string url;
+ PP_CompletionCallback callback;
+ base::Time start_time;
+};
+
+// A utility class to ensure that we don't send progress events more often than
+// every 10ms for a given file.
+class ProgressEventRateLimiter {
+ public:
+ explicit ProgressEventRateLimiter(PP_Instance instance)
+ : instance_(instance) { }
+
+ void ReportProgress(const std::string& url,
+ int64_t total_bytes_received,
+ int64_t total_bytes_to_be_received) {
+ base::Time now = base::Time::Now();
+ if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
+ DispatchProgressEvent(instance_,
+ ProgressEvent(PP_NACL_EVENT_PROGRESS,
+ url,
+ total_bytes_to_be_received >= 0,
+ total_bytes_received,
+ total_bytes_to_be_received));
+ last_event_ = now;
+ }
+ }
+
+ private:
+ PP_Instance instance_;
+ base::Time last_event_;
+};
+
+void DownloadNexeCompletion(const DownloadNexeRequest& request,
+ PP_NaClFileInfo* out_file_info,
+ FileDownloader::Status status,
+ base::File target_file,
+ int http_status);
+
+void DownloadNexe(PP_Instance instance,
+ const char* url,
+ PP_NaClFileInfo* out_file_info,
+ PP_CompletionCallback callback) {
+ CHECK(url);
+ CHECK(out_file_info);
+ DownloadNexeRequest request;
+ request.instance = instance;
+ request.url = url;
+ request.callback = callback;
+ request.start_time = base::Time::Now();
+
+ // Try the fast path for retrieving the file first.
+ PP_FileHandle handle = OpenNaClExecutable(instance,
+ url,
+ &out_file_info->token_lo,
+ &out_file_info->token_hi);
+ if (handle != PP_kInvalidFileHandle) {
+ DownloadNexeCompletion(request,
+ out_file_info,
+ FileDownloader::SUCCESS,
+ base::File(handle),
+ 200);
+ return;
+ }
+
+ // The fast path didn't work, we'll fetch the file using URLLoader and write
+ // it to local storage.
+ base::File target_file(CreateTemporaryFile(instance));
+ GURL gurl(url);
+
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!plugin_instance) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(callback.func, callback.user_data,
+ static_cast<int32_t>(PP_ERROR_FAILED)));
+ }
+ const blink::WebDocument& document =
+ plugin_instance->GetContainer()->element().document();
+ scoped_ptr<blink::WebURLLoader> url_loader(
+ CreateWebURLLoader(document, gurl));
+ blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
+
+ ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
+
+ // FileDownloader deletes itself after invoking DownloadNexeCompletion.
+ FileDownloader* file_downloader = new FileDownloader(
+ url_loader.Pass(),
+ target_file.Pass(),
+ base::Bind(&DownloadNexeCompletion, request, out_file_info),
+ base::Bind(&ProgressEventRateLimiter::ReportProgress,
+ base::Owned(tracker), std::string(url)));
+ file_downloader->Load(url_request);
+}
+
+void DownloadNexeCompletion(const DownloadNexeRequest& request,
+ PP_NaClFileInfo* out_file_info,
+ FileDownloader::Status status,
+ base::File target_file,
+ int http_status) {
+ int32_t pp_error = FileDownloaderToPepperError(status);
+ int64_t bytes_read = -1;
+ if (pp_error == PP_OK && target_file.IsValid()) {
+ base::File::Info info;
+ if (target_file.GetInfo(&info))
+ bytes_read = info.size;
+ }
+
+ if (bytes_read == -1) {
+ target_file.Close();
+ pp_error = PP_ERROR_FAILED;
+ }
+
+ base::TimeDelta download_time = base::Time::Now() - request.start_time;
+
+ NexeLoadManager* load_manager = NexeLoadManager::Get(request.instance);
+ if (load_manager) {
+ load_manager->NexeFileDidOpen(pp_error,
+ target_file,
+ http_status,
+ bytes_read,
+ request.url,
+ download_time);
+ }
+
+ if (pp_error == PP_OK && target_file.IsValid())
+ out_file_info->handle = target_file.TakePlatformFile();
+ else
+ out_file_info->handle = PP_kInvalidFileHandle;
+
+ request.callback.func(request.callback.user_data, pp_error);
+}
+
+void DownloadFileCompletion(
+ const DownloadFileCallback& callback,
+ FileDownloader::Status status,
+ base::File file,
+ int http_status) {
+ int32_t pp_error = FileDownloaderToPepperError(status);
+ PP_NaClFileInfo file_info;
+ if (pp_error == PP_OK) {
+ file_info.handle = file.TakePlatformFile();
+ file_info.token_lo = 0;
+ file_info.token_hi = 0;
+ } else {
+ file_info = kInvalidNaClFileInfo;
+ }
+
+ callback.Run(pp_error, file_info);
+}
+
+void DownloadFile(PP_Instance instance,
+ const std::string& url,
+ const DownloadFileCallback& callback) {
+ DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
+ BelongsToCurrentThread());
+
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
+ DCHECK(load_manager);
+ if (!load_manager) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ static_cast<int32_t>(PP_ERROR_FAILED),
+ kInvalidNaClFileInfo));
+ return;
+ }
+
+ // Handle special PNaCl support files which are installed on the user's
+ // machine.
+ if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
+ PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
+ PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
+ false /* is_executable */,
+ &file_info.token_lo,
+ &file_info.token_hi);
+ if (handle == PP_kInvalidFileHandle) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ static_cast<int32_t>(PP_ERROR_FAILED),
+ kInvalidNaClFileInfo));
+ return;
+ }
+ file_info.handle = handle;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
+ return;
+ }
+
+ // We have to ensure that this url resolves relative to the plugin base url
+ // before downloading it.
+ const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
+ if (!test_gurl.is_valid()) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ static_cast<int32_t>(PP_ERROR_FAILED),
+ kInvalidNaClFileInfo));
+ return;
+ }
+
+ // Try the fast path for retrieving the file first.
+ uint64_t file_token_lo = 0;
+ uint64_t file_token_hi = 0;
+ PP_FileHandle file_handle = OpenNaClExecutable(instance,
+ url.c_str(),
+ &file_token_lo,
+ &file_token_hi);
+ if (file_handle != PP_kInvalidFileHandle) {
+ PP_NaClFileInfo file_info;
+ file_info.handle = file_handle;
+ file_info.token_lo = file_token_lo;
+ file_info.token_hi = file_token_hi;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
+ return;
+ }
+
+ // The fast path didn't work, we'll fetch the file using URLLoader and write
+ // it to local storage.
+ base::File target_file(CreateTemporaryFile(instance));
+ GURL gurl(url);
+
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!plugin_instance) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ static_cast<int32_t>(PP_ERROR_FAILED),
+ kInvalidNaClFileInfo));
+ }
+ const blink::WebDocument& document =
+ plugin_instance->GetContainer()->element().document();
+ scoped_ptr<blink::WebURLLoader> url_loader(
+ CreateWebURLLoader(document, gurl));
+ blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
+
+ ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
+
+ // FileDownloader deletes itself after invoking DownloadNexeCompletion.
+ FileDownloader* file_downloader = new FileDownloader(
+ url_loader.Pass(),
+ target_file.Pass(),
+ base::Bind(&DownloadFileCompletion, callback),
+ base::Bind(&ProgressEventRateLimiter::ReportProgress,
+ base::Owned(tracker), std::string(url)));
+ file_downloader->Load(url_request);
+}
+
+void ReportSelLdrStatus(PP_Instance instance,
+ int32_t load_status,
+ int32_t max_status) {
+ HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
DCHECK(load_manager);
+ if (!load_manager)
+ return;
+
+ // Gather data to see if being installed changes load outcomes.
+ const char* name = load_manager->is_installed() ?
+ "NaCl.LoadStatus.SelLdr.InstalledApp" :
+ "NaCl.LoadStatus.SelLdr.NotInstalledApp";
+ HistogramEnumerate(name, load_status, max_status);
+}
+
+void LogTranslateTime(const char* histogram_name,
+ int64_t time_in_us) {
+ ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(&HistogramTimeTranslation,
+ std::string(histogram_name),
+ time_in_us / 1000));
+}
+
+void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
+ PP_CompletionCallback callback,
+ int32_t pp_error,
+ const PP_NaClFileInfo& file_info) {
+ if (pp_error == PP_OK)
+ *out_file_info = file_info;
+ callback.func(callback.user_data, pp_error);
+}
+
+void OpenManifestEntry(PP_Instance instance,
+ PP_Bool is_helper_process,
+ const char* key,
+ PP_NaClFileInfo* out_file_info,
+ PP_CompletionCallback callback) {
+ std::string url;
+ PP_PNaClOptions pnacl_options;
+ pnacl_options.translate = PP_FALSE;
+ pnacl_options.is_debug = PP_FALSE;
+ pnacl_options.opt_level = 2;
+ if (!ManifestResolveKey(instance,
+ PP_ToBool(is_helper_process),
+ key,
+ &url,
+ &pnacl_options)) {
+ PostPPCompletionCallback(callback, PP_ERROR_FAILED);
+ }
+
+ // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
+ // that would close the file handle on destruction.
+ DownloadFile(instance, url,
+ base::Bind(&DidOpenManifestEntry, out_file_info, callback));
+}
+
+void SetPNaClStartTime(PP_Instance instance) {
+ NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
if (load_manager)
- load_manager->set_is_installed(PP_ToBool(installed));
+ load_manager->set_pnacl_start_time(base::Time::Now());
+}
+
+// PexeDownloader is responsible for deleting itself when the download
+// finishes.
+class PexeDownloader : public blink::WebURLLoaderClient {
+ public:
+ PexeDownloader(PP_Instance instance,
+ scoped_ptr<blink::WebURLLoader> url_loader,
+ const std::string& pexe_url,
+ int32_t pexe_opt_level,
+ const PPP_PexeStreamHandler* stream_handler,
+ void* stream_handler_user_data)
+ : instance_(instance),
+ url_loader_(url_loader.Pass()),
+ pexe_url_(pexe_url),
+ pexe_opt_level_(pexe_opt_level),
+ stream_handler_(stream_handler),
+ stream_handler_user_data_(stream_handler_user_data),
+ success_(false),
+ expected_content_length_(-1),
+ weak_factory_(this) { }
+
+ void Load(const blink::WebURLRequest& request) {
+ url_loader_->loadAsynchronously(request, this);
+ }
+
+ private:
+ virtual void didReceiveResponse(blink::WebURLLoader* loader,
+ const blink::WebURLResponse& response) {
+ success_ = (response.httpStatusCode() == 200);
+ if (!success_)
+ return;
+
+ expected_content_length_ = response.expectedContentLength();
+
+ // Defer loading after receiving headers. This is because we may already
+ // have a cached translated nexe, so check for that now.
+ url_loader_->setDefersLoading(true);
+
+ std::string etag = response.httpHeaderField("etag").utf8();
+ std::string last_modified =
+ response.httpHeaderField("last-modified").utf8();
+ base::Time last_modified_time;
+ base::Time::FromString(last_modified.c_str(), &last_modified_time);
+
+ bool has_no_store_header = false;
+ std::string cache_control =
+ response.httpHeaderField("cache-control").utf8();
+
+ std::vector<std::string> values;
+ base::SplitString(cache_control, ',', &values);
+ for (std::vector<std::string>::const_iterator it = values.begin();
+ it != values.end();
+ ++it) {
+ if (base::StringToLowerASCII(*it) == "no-store")
+ has_no_store_header = true;
+ }
+
+ GetNexeFd(instance_,
+ pexe_url_,
+ pexe_opt_level_,
+ last_modified_time,
+ etag,
+ has_no_store_header,
+ base::Bind(&PexeDownloader::didGetNexeFd,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ virtual void didGetNexeFd(int32_t pp_error,
+ bool cache_hit,
+ PP_FileHandle file_handle) {
+ if (!content::PepperPluginInstance::Get(instance_)) {
+ delete this;
+ return;
+ }
+
+ HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
+ if (cache_hit) {
+ stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
+
+ // We delete the PexeDownloader at this point since we successfully got a
+ // cached, translated nexe.
+ delete this;
+ return;
+ }
+ stream_handler_->DidCacheMiss(stream_handler_user_data_,
+ expected_content_length_,
+ file_handle);
+
+ // No translated nexe was found in the cache, so we should download the
+ // file to start streaming it.
+ url_loader_->setDefersLoading(false);
+ }
+
+ virtual void didReceiveData(blink::WebURLLoader* loader,
+ const char* data,
+ int data_length,
+ int encoded_data_length) {
+ if (content::PepperPluginInstance::Get(instance_)) {
+ // Stream the data we received to the stream callback.
+ stream_handler_->DidStreamData(stream_handler_user_data_,
+ data,
+ data_length);
+ }
+ }
+
+ virtual void didFinishLoading(blink::WebURLLoader* loader,
+ double finish_time,
+ int64_t total_encoded_data_length) {
+ int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
+
+ if (content::PepperPluginInstance::Get(instance_))
+ stream_handler_->DidFinishStream(stream_handler_user_data_, result);
+ delete this;
+ }
+
+ virtual void didFail(blink::WebURLLoader* loader,
+ const blink::WebURLError& error) {
+ success_ = false;
+ }
+
+ PP_Instance instance_;
+ scoped_ptr<blink::WebURLLoader> url_loader_;
+ std::string pexe_url_;
+ int32_t pexe_opt_level_;
+ const PPP_PexeStreamHandler* stream_handler_;
+ void* stream_handler_user_data_;
+ bool success_;
+ int64_t expected_content_length_;
+ base::WeakPtrFactory<PexeDownloader> weak_factory_;
+};
+
+void StreamPexe(PP_Instance instance,
+ const char* pexe_url,
+ int32_t opt_level,
+ const PPP_PexeStreamHandler* handler,
+ void* handler_user_data) {
+ content::PepperPluginInstance* plugin_instance =
+ content::PepperPluginInstance::Get(instance);
+ if (!plugin_instance) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(handler->DidFinishStream,
+ handler_user_data,
+ static_cast<int32_t>(PP_ERROR_FAILED)));
+ return;
+ }
+
+ GURL gurl(pexe_url);
+ const blink::WebDocument& document =
+ plugin_instance->GetContainer()->element().document();
+ scoped_ptr<blink::WebURLLoader> url_loader(
+ CreateWebURLLoader(document, gurl));
+ PexeDownloader* downloader = new PexeDownloader(instance,
+ url_loader.Pass(),
+ pexe_url,
+ opt_level,
+ handler,
+ handler_user_data);
+
+ blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
+ // Mark the request as requesting a PNaCl bitcode file,
+ // so that component updater can detect this user action.
+ url_request.addHTTPHeaderField(
+ blink::WebString::fromUTF8("Accept"),
+ blink::WebString::fromUTF8("application/x-pnacl, */*"));
+ url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
+ downloader->Load(url_request);
}
const PPB_NaCl_Private nacl_interface = {
&UrandomFD,
&Are3DInterfacesDisabled,
&BrokerDuplicateHandle,
- &GetReadonlyPnaclFD,
+ &GetReadExecPnaclFd,
&CreateTemporaryFile,
&GetNumberOfProcessors,
- &IsNonSFIModeEnabled,
- &GetNexeFd,
+ &PPIsNonSFIModeEnabled,
&ReportTranslationFinished,
- &OpenNaClExecutable,
&DispatchEvent,
- &SetReadOnlyProperty,
+ &ReportLoadSuccess,
&ReportLoadError,
+ &ReportLoadAbort,
&InstanceCreated,
&InstanceDestroyed,
&NaClDebugEnabledForURL,
&GetSandboxArch,
- &GetUrlScheme,
&LogToConsole,
- &GetNexeErrorReported,
- &SetNexeErrorReported,
&GetNaClReadyState,
- &SetNaClReadyState,
- &GetIsInstalled,
- &SetIsInstalled
+ &Vlog,
+ &InitializePlugin,
+ &GetNexeSize,
+ &RequestNaClManifest,
+ &GetManifestBaseURL,
+ &ProcessNaClManifest,
+ &DevInterfacesEnabled,
+ &ManifestGetProgramURL,
+ &GetPNaClResourceInfo,
+ &GetCpuFeatureAttrs,
+ &DownloadNexe,
+ &ReportSelLdrStatus,
+ &LogTranslateTime,
+ &OpenManifestEntry,
+ &SetPNaClStartTime,
+ &StreamPexe
};
} // namespace
-namespace nacl {
-
const PPB_NaCl_Private* GetNaClPrivateInterface() {
return &nacl_interface;
}