blob: 3c84afe5d1ee4a16187fef0b715342f5b239acf4 [file] [log] [blame]
// Copyright 2013 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.
#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/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/common/sandbox_init.h"
#include "content/public/renderer/pepper_plugin_instance.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/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;
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().get()) {
g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
render_thread->GetIOMessageLoopProxy());
render_thread->AddFilter(g_pnacl_resource_host.Get().get());
}
return true;
}
// This contains state that is produced by LaunchSelLdr() and consumed
// by StartPpapiProxy().
struct InstanceInfo {
InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
GURL url;
ppapi::PpapiPermissions permissions;
base::ProcessId plugin_pid;
int plugin_child_id;
IPC::ChannelHandle channel_handle;
};
class NaClPluginInstance {
public:
NaClPluginInstance(PP_Instance instance):
nexe_load_manager(instance), pexe_size(0) {}
NexeLoadManager nexe_load_manager;
scoped_ptr<JsonManifest> json_manifest;
scoped_ptr<InstanceInfo> instance_info;
// When translation is complete, this records the size of the pexe in
// bytes so that it can be reported in a later load event.
uint64_t pexe_size;
};
typedef base::ScopedPtrHashMap<PP_Instance, NaClPluginInstance> InstanceMap;
base::LazyInstance<InstanceMap> g_instance_map = LAZY_INSTANCE_INITIALIZER;
NaClPluginInstance* GetNaClPluginInstance(PP_Instance instance) {
InstanceMap& map = g_instance_map.Get();
InstanceMap::iterator iter = map.find(instance);
if (iter == map.end())
return NULL;
return iter->second;
}
NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
if (!nacl_plugin_instance)
return NULL;
return &nacl_plugin_instance->nexe_load_manager;
}
JsonManifest* GetJsonManifest(PP_Instance instance) {
NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
if (!nacl_plugin_instance)
return NULL;
return nacl_plugin_instance->json_manifest.get();
}
static const PP_NaClFileInfo kInvalidNaClFileInfo = {
PP_kInvalidFileHandle,
0, // token_lo
0, // token_hi
};
int GetRoutingID(PP_Instance instance) {
// Check that we are on the main renderer thread.
DCHECK(content::RenderThread::Get());
content::RendererPpapiHost* host =
content::RendererPpapiHost::GetForPPInstance(instance);
if (!host)
return 0;
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, NaClAppProcessType process_type)
: pp_instance_(pp_instance), process_type_(process_type) {}
~ManifestServiceProxy() override {}
void StartupInitializationComplete() override {
if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
NaClPluginInstance* nacl_plugin_instance =
GetNaClPluginInstance(pp_instance_);
JsonManifest* manifest = GetJsonManifest(pp_instance_);
if (nacl_plugin_instance && manifest) {
NexeLoadManager* load_manager =
&nacl_plugin_instance->nexe_load_manager;
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 exe_size = nacl_plugin_instance->pexe_size;
if (exe_size == 0)
exe_size = load_manager->nexe_size();
load_manager->ReportLoadSuccess(full_url, exe_size, exe_size);
}
}
}
}
void OpenResource(
const std::string& key,
const ManifestServiceChannel::OpenResourceCallback& callback) override {
DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
BelongsToCurrentThread());
// For security hardening, disable open_resource() when it is isn't
// needed. PNaCl pexes can't use open_resource(), but general nexes
// and the PNaCl translator nexes may use it.
if (process_type_ != kNativeNaClProcessType &&
process_type_ != kPNaClTranslatorProcessType) {
// Return an error.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(callback, base::Passed(base::File()), 0, 0));
return;
}
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;
bool is_helper_process = process_type_ == kPNaClTranslatorProcessType;
if (!ManifestResolveKey(pp_instance_, is_helper_process, 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_;
NaClAppProcessType process_type_;
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;
}
NaClAppProcessType PP_ToNaClAppProcessType(
PP_NaClAppProcessType pp_process_type) {
#define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
"PP_NaClAppProcessType differs from NaClAppProcessType");
STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE,
kUnknownNaClProcessType);
STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE,
kNativeNaClProcessType);
STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE,
kPNaClProcessType);
STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE,
kPNaClTranslatorProcessType);
STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES,
kNumNaClProcessTypes);
#undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
DCHECK(pp_process_type > PP_UNKNOWN_NACL_PROCESS_TYPE &&
pp_process_type < PP_NUM_NACL_PROCESS_TYPES);
return static_cast<NaClAppProcessType>(pp_process_type);
}
// 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_nonsfi_mode,
PP_NaClAppProcessType pp_process_type,
void* imc_handle,
PP_CompletionCallback callback) {
CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
BelongsToCurrentThread());
NaClAppProcessType process_type = PP_ToNaClAppProcessType(pp_process_type);
// 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, process_type));
FileDescriptor result_socket;
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
int routing_id = GetRoutingID(instance);
NexeLoadManager* load_manager = GetNexeLoadManager(instance);
DCHECK(load_manager);
if (!routing_id || !load_manager) {
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,
static_cast<int32_t>(PP_ERROR_FAILED)));
return;
}
InstanceInfo instance_info;
instance_info.url = GURL(alleged_url);
uint32_t perm_bits = ppapi::PERMISSION_NONE;
// Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
// it's clearer to developers when they are using 'Dev' inappropriately. We
// must also check on the trusted side of the proxy.
if (load_manager->DevInterfacesEnabled())
perm_bits |= ppapi::PERMISSION_DEV;
instance_info.permissions =
ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
std::string error_message_string;
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(
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_nonsfi_mode),
process_type),
&launch_result,
&error_message_string))) {
ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
FROM_HERE,
base::Bind(callback.func, callback.user_data,
static_cast<int32_t>(PP_ERROR_FAILED)));
return;
}
load_manager->set_nonsfi(PP_ToBool(uses_nonsfi_mode));
if (!error_message_string.empty()) {
if (PP_ToBool(main_service_runtime)) {
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,
static_cast<int32_t>(PP_ERROR_FAILED)));
return;
}
result_socket = launch_result.imc_channel_handle;
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.
if (IsValidChannelHandle(instance_info.channel_handle)) {
NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
nacl_plugin_instance->instance_info.reset(new InstanceInfo(instance_info));
}
*(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
// 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;
}
// Create the manifest service handle as well.
if (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());
}
}
PP_Bool StartPpapiProxy(PP_Instance instance) {
NexeLoadManager* load_manager = GetNexeLoadManager(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;
}
NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
if (!nacl_plugin_instance->instance_info) {
DLOG(ERROR) << "Could not find instance ID";
return PP_FALSE;
}
scoped_ptr<InstanceInfo> instance_info =
nacl_plugin_instance->instance_info.Pass();
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) {
#if defined(OS_POSIX)
return base::GetUrandomFD();
#else
return -1;
#endif
}
int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
uint32_t process_id,
PP_FileHandle* target_handle,
uint32_t desired_access,
uint32_t options) {
#if defined(OS_WIN)
return content::BrokerDuplicateHandle(source_handle, process_id,
target_handle, desired_access,
options);
#else
return 0;
#endif
}
// 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), is_executable,
&out_fd, nonce_lo, nonce_hi))) {
return PP_kInvalidFileHandle;
}
if (out_fd == IPC::InvalidPlatformFileForTransit()) {
return PP_kInvalidFileHandle;
}
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) {
IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
&transit_fd))) {
return PP_kInvalidFileHandle;
}
if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
return PP_kInvalidFileHandle;
}
return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
}
int32_t GetNumberOfProcessors() {
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
int32_t num_processors = 1;
return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
num_processors : 1;
}
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);
// 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 = 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,
callback);
}
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 = GetNexeLoadManager(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().get() == NULL)
return;
g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
// Record the pexe size for reporting in a later load event.
NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
if (nacl_plugin_instance) {
nacl_plugin_instance->pexe_size = pexe_size;
}
}
PP_FileHandle OpenNaClExecutable(PP_Instance instance,
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;
NexeLoadManager* load_manager = GetNexeLoadManager(instance);
DCHECK(load_manager);
if (!load_manager)
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);
*nonce_lo = 0;
*nonce_hi = 0;
base::FilePath file_path;
if (!sender->Send(
new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
GURL(file_url),
!load_manager->nonsfi(),
&out_fd,
nonce_lo,
nonce_hi))) {
return PP_kInvalidFileHandle;
}
if (out_fd == IPC::InvalidPlatformFileForTransit())
return PP_kInvalidFileHandle;
return IPC::PlatformFileForTransitToPlatformFile(out_fd);
}
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) {
ProgressEvent event(event_type,
resource_url,
PP_ToBool(length_is_computable),
loaded_bytes,
total_bytes);
DispatchProgressEvent(instance, event);
}
void ReportLoadError(PP_Instance instance,
PP_NaClError error,
const char* error_message) {
NexeLoadManager* load_manager = GetNexeLoadManager(instance);
if (load_manager)
load_manager->ReportLoadError(error, error_message);
}
void InstanceCreated(PP_Instance instance) {
InstanceMap& map = g_instance_map.Get();
CHECK(map.find(instance) == map.end()); // Sanity check.
scoped_ptr<NaClPluginInstance> new_instance(new NaClPluginInstance(instance));
map.add(instance, new_instance.Pass());
}
void InstanceDestroyed(PP_Instance instance) {
InstanceMap& map = g_instance_map.Get();
InstanceMap::iterator iter = map.find(instance);
CHECK(iter != map.end());
// The erase may call NexeLoadManager's destructor prior to removing it from
// the map. In that case, it is possible for the trusted Plugin to re-enter
// the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
// NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
// from the map prior to the destructor being invoked.
scoped_ptr<NaClPluginInstance> temp(map.take(instance));
map.erase(iter);
}
PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableNaClDebug))
return PP_FALSE;
IPC::Sender* sender = content::RenderThread::Get();
DCHECK(sender);
bool should_debug = false;
return PP_FromBool(
sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
&should_debug)) &&
should_debug);
}
void Vlog(const char* message) {
VLOG(1) << message;
}
void InitializePlugin(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
NexeLoadManager* load_manager = GetNexeLoadManager(instance);
DCHECK(load_manager);
if (load_manager)
load_manager->InitializePlugin(argc, argn, argv);
}
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 = GetNexeLoadManager(instance);
DCHECK(load_manager);
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_Var GetManifestBaseURL(PP_Instance instance) {
NexeLoadManager* load_manager = GetNexeLoadManager(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 = GetNexeLoadManager(instance);
if (load_manager)
load_manager->ProcessNaClManifest(program_url);
}
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 = GetNexeLoadManager(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 = GetNexeLoadManager(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 = GetNexeLoadManager(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)) {
GetNaClPluginInstance(instance)->json_manifest.reset(j.release());
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 = GetNexeLoadManager(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)
load_manager->ReportLoadError(error_info.error, error_info.string);
return PP_FALSE;
}
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 = GetNexeLoadManager(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 = GetNexeLoadManager(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 = GetNexeLoadManager(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 = GetNexeLoadManager(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 = GetNexeLoadManager(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 SetPNaClStartTime(PP_Instance instance) {
NexeLoadManager* load_manager = GetNexeLoadManager(instance);
if (load_manager)
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 = {
&LaunchSelLdr,
&UrandomFD,
&BrokerDuplicateHandle,
&GetReadExecPnaclFd,
&CreateTemporaryFile,
&GetNumberOfProcessors,
&ReportTranslationFinished,
&DispatchEvent,
&ReportLoadError,
&InstanceCreated,
&InstanceDestroyed,
&GetSandboxArch,
&Vlog,
&InitializePlugin,
&RequestNaClManifest,
&GetManifestBaseURL,
&ProcessNaClManifest,
&ManifestGetProgramURL,
&GetPNaClResourceInfo,
&GetCpuFeatureAttrs,
&DownloadNexe,
&ReportSelLdrStatus,
&LogTranslateTime,
&SetPNaClStartTime,
&StreamPexe
};
} // namespace
const PPB_NaCl_Private* GetNaClPrivateInterface() {
return &nacl_interface;
}
} // namespace nacl