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
,
89 struct PP_Var
* error_message
) {
90 nacl::FileDescriptor result_socket
;
91 IPC::Sender
* sender
= content::RenderThread::Get();
93 *error_message
= PP_MakeUndefined();
95 // If the nexe uses ppapi APIs, we need a routing ID.
96 // To get the routing ID, we must be on the main thread.
97 // Some nexes do not use ppapi and launch from the background thread,
98 // so those nexes can skip finding a routing_id.
100 routing_id
= GetRoutingID(instance
);
102 return PP_EXTERNAL_PLUGIN_FAILED
;
105 InstanceInfo instance_info
;
106 instance_info
.url
= GURL(alleged_url
);
108 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
109 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
110 // it's clearer to developers when they are using 'Dev' inappropriately. We
111 // must also check on the trusted side of the proxy.
112 if (enable_ppapi_dev
)
113 perm_bits
|= ppapi::PERMISSION_DEV
;
114 instance_info
.permissions
=
115 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
116 std::string error_message_string
;
117 nacl::NaClLaunchResult launch_result
;
119 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
120 nacl::NaClLaunchParams(instance_info
.url
.spec(),
124 PP_ToBool(enable_dyncode_syscalls
),
125 PP_ToBool(enable_exception_handling
)),
127 &error_message_string
))) {
128 return PP_EXTERNAL_PLUGIN_FAILED
;
130 if (!error_message_string
.empty()) {
131 *error_message
= ppapi::StringVar::StringToPPVar(error_message_string
);
132 return PP_EXTERNAL_PLUGIN_FAILED
;
134 result_socket
= launch_result
.imc_channel_handle
;
135 instance_info
.channel_handle
= launch_result
.ipc_channel_handle
;
136 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
137 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
138 // Don't save instance_info if channel handle is invalid.
139 bool invalid_handle
= instance_info
.channel_handle
.name
.empty();
140 #if defined(OS_POSIX)
142 invalid_handle
= (instance_info
.channel_handle
.socket
.fd
== -1);
145 g_instance_info
.Get()[instance
] = instance_info
;
147 *(static_cast<NaClHandle
*>(imc_handle
)) =
148 nacl::ToNativeHandle(result_socket
);
150 return PP_EXTERNAL_PLUGIN_OK
;
153 PP_ExternalPluginResult
StartPpapiProxy(PP_Instance instance
) {
154 InstanceInfoMap
& map
= g_instance_info
.Get();
155 InstanceInfoMap::iterator it
= map
.find(instance
);
156 if (it
== map
.end()) {
157 DLOG(ERROR
) << "Could not find instance ID";
158 return PP_EXTERNAL_PLUGIN_FAILED
;
160 InstanceInfo instance_info
= it
->second
;
163 content::PepperPluginInstance
* plugin_instance
=
164 content::PepperPluginInstance::Get(instance
);
165 if (!plugin_instance
) {
166 DLOG(ERROR
) << "GetInstance() failed";
167 return PP_EXTERNAL_PLUGIN_ERROR_MODULE
;
170 return plugin_instance
->SwitchToOutOfProcessProxy(
171 base::FilePath().AppendASCII(instance_info
.url
.spec()),
172 instance_info
.permissions
,
173 instance_info
.channel_handle
,
174 instance_info
.plugin_pid
,
175 instance_info
.plugin_child_id
);
178 int UrandomFD(void) {
179 #if defined(OS_POSIX)
180 return base::GetUrandomFD();
186 PP_Bool
Are3DInterfacesDisabled() {
187 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
188 switches::kDisable3DAPIs
));
191 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
193 PP_FileHandle
* target_handle
,
194 uint32_t desired_access
,
197 return content::BrokerDuplicateHandle(source_handle
, process_id
,
198 target_handle
, desired_access
,
205 int32_t EnsurePnaclInstalled(PP_Instance instance
,
206 PP_CompletionCallback callback
) {
207 ppapi::thunk::EnterInstance
enter(instance
, callback
);
209 return enter
.retval();
210 if (!InitializePnaclResourceHost())
211 return enter
.SetResult(PP_ERROR_FAILED
);
212 g_pnacl_resource_host
.Get()->EnsurePnaclInstalled(
215 return enter
.SetResult(PP_OK_COMPLETIONPENDING
);
218 PP_FileHandle
GetReadonlyPnaclFD(const char* filename
) {
219 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
220 IPC::Sender
* sender
= content::RenderThread::Get();
222 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
223 std::string(filename
),
225 return base::kInvalidPlatformFileValue
;
227 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
228 return base::kInvalidPlatformFileValue
;
230 base::PlatformFile handle
=
231 IPC::PlatformFileForTransitToPlatformFile(out_fd
);
235 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
236 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
237 IPC::Sender
* sender
= content::RenderThread::Get();
239 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
241 return base::kInvalidPlatformFileValue
;
244 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
245 return base::kInvalidPlatformFileValue
;
248 base::PlatformFile handle
= IPC::PlatformFileForTransitToPlatformFile(
253 int32_t GetNexeFd(PP_Instance instance
,
254 const char* pexe_url
,
255 uint32_t abi_version
,
257 const char* last_modified
,
260 PP_FileHandle
* handle
,
261 struct PP_CompletionCallback callback
) {
262 ppapi::thunk::EnterInstance
enter(instance
, callback
);
264 return enter
.retval();
265 if (!pexe_url
|| !last_modified
|| !etag
|| !is_hit
|| !handle
)
266 return enter
.SetResult(PP_ERROR_BADARGUMENT
);
267 if (!InitializePnaclResourceHost())
268 return enter
.SetResult(PP_ERROR_FAILED
);
270 base::Time last_modified_time
;
271 // If FromString fails, it doesn't touch last_modified_time and we just send
272 // the default-constructed null value.
273 base::Time::FromString(last_modified
, &last_modified_time
);
275 nacl::PnaclCacheInfo cache_info
;
276 cache_info
.pexe_url
= GURL(pexe_url
);
277 cache_info
.abi_version
= abi_version
;
278 cache_info
.opt_level
= opt_level
;
279 cache_info
.last_modified
= last_modified_time
;
280 cache_info
.etag
= std::string(etag
);
282 g_pnacl_resource_host
.Get()->RequestNexeFd(
283 GetRoutingID(instance
),
290 return enter
.SetResult(PP_OK_COMPLETIONPENDING
);
293 void ReportTranslationFinished(PP_Instance instance
, PP_Bool success
) {
294 // If the resource host isn't initialized, don't try to do that here.
295 // Just return because something is already very wrong.
296 if (g_pnacl_resource_host
.Get() == NULL
)
298 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
301 PP_Bool
IsOffTheRecord() {
302 return PP_FromBool(ChromeRenderProcessObserver::is_incognito_process());
305 PP_Bool
IsPnaclEnabled() {
307 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisablePnacl
));
310 PP_ExternalPluginResult
ReportNaClError(PP_Instance instance
,
311 PP_NaClError error_id
) {
312 IPC::Sender
* sender
= content::RenderThread::Get();
315 new NaClHostMsg_NaClErrorStatus(
316 // TODO(dschuff): does this enum need to be sent as an int,
317 // or is it safe to include the appropriate headers in
318 // render_messages.h?
319 GetRoutingID(instance
), static_cast<int>(error_id
)))) {
320 return PP_EXTERNAL_PLUGIN_FAILED
;
322 return PP_EXTERNAL_PLUGIN_OK
;
325 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
326 const char* file_url
,
328 uint64_t* nonce_hi
) {
329 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
330 IPC::Sender
* sender
= content::RenderThread::Get();
334 base::FilePath file_path
;
336 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
341 return base::kInvalidPlatformFileValue
;
344 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
345 return base::kInvalidPlatformFileValue
;
348 base::PlatformFile handle
=
349 IPC::PlatformFileForTransitToPlatformFile(out_fd
);
353 const PPB_NaCl_Private nacl_interface
= {
357 &Are3DInterfacesDisabled
,
358 &BrokerDuplicateHandle
,
359 &EnsurePnaclInstalled
,
361 &CreateTemporaryFile
,
363 &ReportTranslationFinished
,
372 const PPB_NaCl_Private
* PPB_NaCl_Private_Impl::GetInterface() {
373 return &nacl_interface
;
376 #endif // DISABLE_NACL