1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/pepper/ppb_nacl_private_impl.h"
9 #include "base/command_line.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/renderer/chrome_render_process_observer.h"
15 #include "chrome/renderer/pepper/pnacl_translation_resource_host.h"
16 #include "components/nacl/common/nacl_host_messages.h"
17 #include "components/nacl/common/nacl_types.h"
18 #include "content/public/common/content_client.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/public/common/sandbox_init.h"
21 #include "content/public/renderer/pepper_plugin_instance.h"
22 #include "content/public/renderer/renderer_ppapi_host.h"
23 #include "content/public/renderer/render_thread.h"
24 #include "content/public/renderer/render_view.h"
25 #include "ppapi/c/pp_bool.h"
26 #include "ppapi/c/private/pp_file_handle.h"
27 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
28 #include "ppapi/shared_impl/ppapi_permissions.h"
29 #include "ppapi/shared_impl/ppapi_preferences.h"
30 #include "ppapi/shared_impl/var.h"
31 #include "ppapi/thunk/enter.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebElement.h"
34 #include "third_party/WebKit/public/web/WebFrame.h"
35 #include "third_party/WebKit/public/web/WebPluginContainer.h"
36 #include "third_party/WebKit/public/web/WebView.h"
40 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
41 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
43 static bool InitializePnaclResourceHost() {
44 // Must run on the main thread.
45 content::RenderThread* render_thread = content::RenderThread::Get();
48 if (!g_pnacl_resource_host.Get()) {
49 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
50 render_thread->GetIOMessageLoopProxy());
51 render_thread->AddFilter(g_pnacl_resource_host.Get());
57 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
59 ppapi::PpapiPermissions permissions;
60 base::ProcessId plugin_pid;
62 IPC::ChannelHandle channel_handle;
65 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
67 base::LazyInstance<InstanceInfoMap> g_instance_info =
68 LAZY_INSTANCE_INITIALIZER;
70 static int GetRoutingID(PP_Instance instance) {
71 // Check that we are on the main renderer thread.
72 DCHECK(content::RenderThread::Get());
73 content::RendererPpapiHost *host =
74 content::RendererPpapiHost::GetForPPInstance(instance);
77 return host->GetRoutingIDForWidget(instance);
80 // Launch NaCl's sel_ldr process.
81 PP_ExternalPluginResult LaunchSelLdr(PP_Instance instance,
82 const char* alleged_url,
85 PP_Bool enable_ppapi_dev,
86 PP_Bool enable_dyncode_syscalls,
87 PP_Bool enable_exception_handling,
88 PP_Bool enable_crash_throttling,
90 struct PP_Var* error_message) {
91 nacl::FileDescriptor result_socket;
92 IPC::Sender* sender = content::RenderThread::Get();
94 *error_message = PP_MakeUndefined();
96 // If the nexe uses ppapi APIs, we need a routing ID.
97 // To get the routing ID, we must be on the main thread.
98 // Some nexes do not use ppapi and launch from the background thread,
99 // so those nexes can skip finding a routing_id.
101 routing_id = GetRoutingID(instance);
103 return PP_EXTERNAL_PLUGIN_FAILED;
106 InstanceInfo instance_info;
107 instance_info.url = GURL(alleged_url);
109 uint32_t perm_bits = ppapi::PERMISSION_NONE;
110 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
111 // it's clearer to developers when they are using 'Dev' inappropriately. We
112 // must also check on the trusted side of the proxy.
113 if (enable_ppapi_dev)
114 perm_bits |= ppapi::PERMISSION_DEV;
115 instance_info.permissions =
116 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
117 std::string error_message_string;
118 nacl::NaClLaunchResult launch_result;
120 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
121 nacl::NaClLaunchParams(instance_info.url.spec(),
125 PP_ToBool(enable_dyncode_syscalls),
126 PP_ToBool(enable_exception_handling),
127 PP_ToBool(enable_crash_throttling)),
129 &error_message_string))) {
130 return PP_EXTERNAL_PLUGIN_FAILED;
132 if (!error_message_string.empty()) {
133 *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
134 return PP_EXTERNAL_PLUGIN_FAILED;
136 result_socket = launch_result.imc_channel_handle;
137 instance_info.channel_handle = launch_result.ipc_channel_handle;
138 instance_info.plugin_pid = launch_result.plugin_pid;
139 instance_info.plugin_child_id = launch_result.plugin_child_id;
140 // Don't save instance_info if channel handle is invalid.
141 bool invalid_handle = instance_info.channel_handle.name.empty();
142 #if defined(OS_POSIX)
144 invalid_handle = (instance_info.channel_handle.socket.fd == -1);
147 g_instance_info.Get()[instance] = instance_info;
149 *(static_cast<NaClHandle*>(imc_handle)) =
150 nacl::ToNativeHandle(result_socket);
152 return PP_EXTERNAL_PLUGIN_OK;
155 PP_ExternalPluginResult StartPpapiProxy(PP_Instance instance) {
156 InstanceInfoMap& map = g_instance_info.Get();
157 InstanceInfoMap::iterator it = map.find(instance);
158 if (it == map.end()) {
159 DLOG(ERROR) << "Could not find instance ID";
160 return PP_EXTERNAL_PLUGIN_FAILED;
162 InstanceInfo instance_info = it->second;
165 content::PepperPluginInstance* plugin_instance =
166 content::PepperPluginInstance::Get(instance);
167 if (!plugin_instance) {
168 DLOG(ERROR) << "GetInstance() failed";
169 return PP_EXTERNAL_PLUGIN_ERROR_MODULE;
172 return plugin_instance->SwitchToOutOfProcessProxy(
173 base::FilePath().AppendASCII(instance_info.url.spec()),
174 instance_info.permissions,
175 instance_info.channel_handle,
176 instance_info.plugin_pid,
177 instance_info.plugin_child_id);
180 int UrandomFD(void) {
181 #if defined(OS_POSIX)
182 return base::GetUrandomFD();
188 PP_Bool Are3DInterfacesDisabled() {
189 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
190 switches::kDisable3DAPIs));
193 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
195 PP_FileHandle* target_handle,
196 uint32_t desired_access,
199 return content::BrokerDuplicateHandle(source_handle, process_id,
200 target_handle, desired_access,
207 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
208 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
209 IPC::Sender* sender = content::RenderThread::Get();
211 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
212 std::string(filename),
214 return base::kInvalidPlatformFileValue;
216 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
217 return base::kInvalidPlatformFileValue;
219 base::PlatformFile handle =
220 IPC::PlatformFileForTransitToPlatformFile(out_fd);
224 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
225 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
226 IPC::Sender* sender = content::RenderThread::Get();
228 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
230 return base::kInvalidPlatformFileValue;
233 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
234 return base::kInvalidPlatformFileValue;
237 base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
242 int32_t GetNexeFd(PP_Instance instance,
243 const char* pexe_url,
244 uint32_t abi_version,
246 const char* last_modified,
248 PP_Bool has_no_store_header,
250 PP_FileHandle* handle,
251 struct PP_CompletionCallback callback) {
252 ppapi::thunk::EnterInstance enter(instance, callback);
254 return enter.retval();
255 if (!pexe_url || !last_modified || !etag || !is_hit || !handle)
256 return enter.SetResult(PP_ERROR_BADARGUMENT);
257 if (!InitializePnaclResourceHost())
258 return enter.SetResult(PP_ERROR_FAILED);
260 base::Time last_modified_time;
261 // If FromString fails, it doesn't touch last_modified_time and we just send
262 // the default-constructed null value.
263 base::Time::FromString(last_modified, &last_modified_time);
265 nacl::PnaclCacheInfo cache_info;
266 cache_info.pexe_url = GURL(pexe_url);
267 cache_info.abi_version = abi_version;
268 cache_info.opt_level = opt_level;
269 cache_info.last_modified = last_modified_time;
270 cache_info.etag = std::string(etag);
271 cache_info.has_no_store_header = PP_ToBool(has_no_store_header);
273 g_pnacl_resource_host.Get()->RequestNexeFd(
274 GetRoutingID(instance),
281 return enter.SetResult(PP_OK_COMPLETIONPENDING);
284 void ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
285 // If the resource host isn't initialized, don't try to do that here.
286 // Just return because something is already very wrong.
287 if (g_pnacl_resource_host.Get() == NULL)
289 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
292 PP_Bool IsOffTheRecord() {
293 return PP_FromBool(ChromeRenderProcessObserver::is_incognito_process());
296 PP_ExternalPluginResult ReportNaClError(PP_Instance instance,
297 PP_NaClError error_id) {
298 IPC::Sender* sender = content::RenderThread::Get();
301 new NaClHostMsg_NaClErrorStatus(
302 // TODO(dschuff): does this enum need to be sent as an int,
303 // or is it safe to include the appropriate headers in
304 // render_messages.h?
305 GetRoutingID(instance), static_cast<int>(error_id)))) {
306 return PP_EXTERNAL_PLUGIN_FAILED;
308 return PP_EXTERNAL_PLUGIN_OK;
311 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
312 const char* file_url,
314 uint64_t* nonce_hi) {
315 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
316 IPC::Sender* sender = content::RenderThread::Get();
320 base::FilePath file_path;
322 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
327 return base::kInvalidPlatformFileValue;
330 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
331 return base::kInvalidPlatformFileValue;
334 base::PlatformFile handle =
335 IPC::PlatformFileForTransitToPlatformFile(out_fd);
339 const PPB_NaCl_Private nacl_interface = {
343 &Are3DInterfacesDisabled,
344 &BrokerDuplicateHandle,
346 &CreateTemporaryFile,
348 &ReportTranslationFinished,
356 const PPB_NaCl_Private* PPB_NaCl_Private_Impl::GetInterface() {
357 return &nacl_interface;
360 #endif // DISABLE_NACL