| // 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 |