Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / nacl / renderer / ppb_nacl_private_impl.cc
index 0e771b8..edb4228 100644 (file)
@@ -4,18 +4,34 @@
 
 #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;
 }
@@ -65,21 +102,13 @@ typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
 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 =
@@ -89,9 +118,168 @@ static int GetRoutingID(PP_Instance instance) {
   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,
@@ -100,15 +288,18 @@ void LaunchSelLdr(PP_Instance instance,
                   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.
@@ -117,6 +308,9 @@ void LaunchSelLdr(PP_Instance instance,
   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,
@@ -137,17 +331,34 @@ void LaunchSelLdr(PP_Instance instance,
   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(
@@ -156,8 +367,16 @@ void LaunchSelLdr(PP_Instance instance,
                    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,
@@ -168,61 +387,108 @@ void LaunchSelLdr(PP_Instance instance,
   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) {
@@ -252,21 +518,53 @@ int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
 #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) {
@@ -275,87 +573,96 @@ 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);
 }
@@ -364,6 +671,24 @@ 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;
+
+  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);
@@ -372,178 +697,952 @@ PP_FileHandle OpenNaClExecutable(PP_Instance instance,
   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 = {
@@ -552,34 +1651,41 @@ 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;
 }