Home | History | Annotate | Download | only in renderer
      1 // Copyright 2013 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.
      4 
      5 #include "components/nacl/renderer/ppb_nacl_private_impl.h"
      6 
      7 #ifndef DISABLE_NACL
      8 
      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 "components/nacl/common/nacl_host_messages.h"
     14 #include "components/nacl/common/nacl_types.h"
     15 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
     16 #include "content/public/common/content_client.h"
     17 #include "content/public/common/content_switches.h"
     18 #include "content/public/common/sandbox_init.h"
     19 #include "content/public/renderer/pepper_plugin_instance.h"
     20 #include "content/public/renderer/render_thread.h"
     21 #include "content/public/renderer/render_view.h"
     22 #include "content/public/renderer/renderer_ppapi_host.h"
     23 #include "ppapi/c/pp_bool.h"
     24 #include "ppapi/c/private/pp_file_handle.h"
     25 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
     26 #include "ppapi/shared_impl/ppapi_permissions.h"
     27 #include "ppapi/shared_impl/ppapi_preferences.h"
     28 #include "ppapi/shared_impl/var.h"
     29 #include "ppapi/thunk/enter.h"
     30 #include "third_party/WebKit/public/web/WebDOMResourceProgressEvent.h"
     31 #include "third_party/WebKit/public/web/WebDocument.h"
     32 #include "third_party/WebKit/public/web/WebElement.h"
     33 #include "third_party/WebKit/public/web/WebFrame.h"
     34 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     35 #include "third_party/WebKit/public/web/WebView.h"
     36 #include "v8/include/v8.h"
     37 
     38 namespace {
     39 
     40 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
     41     g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
     42 
     43 static bool InitializePnaclResourceHost() {
     44   // Must run on the main thread.
     45   content::RenderThread* render_thread = content::RenderThread::Get();
     46   if (!render_thread)
     47     return false;
     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());
     52   }
     53   return true;
     54 }
     55 
     56 struct InstanceInfo {
     57   InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
     58   GURL url;
     59   ppapi::PpapiPermissions permissions;
     60   base::ProcessId plugin_pid;
     61   int plugin_child_id;
     62   IPC::ChannelHandle channel_handle;
     63 };
     64 
     65 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
     66 
     67 base::LazyInstance<InstanceInfoMap> g_instance_info =
     68     LAZY_INSTANCE_INITIALIZER;
     69 
     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);
     75   if (!host)
     76     return 0;
     77   return host->GetRoutingIDForWidget(instance);
     78 }
     79 
     80 // Launch NaCl's sel_ldr process.
     81 PP_ExternalPluginResult LaunchSelLdr(PP_Instance instance,
     82                                      const char* alleged_url,
     83                                      PP_Bool uses_irt,
     84                                      PP_Bool uses_ppapi,
     85                                      PP_Bool enable_ppapi_dev,
     86                                      PP_Bool enable_dyncode_syscalls,
     87                                      PP_Bool enable_exception_handling,
     88                                      PP_Bool enable_crash_throttling,
     89                                      void* imc_handle,
     90                                      struct PP_Var* error_message) {
     91   nacl::FileDescriptor result_socket;
     92   IPC::Sender* sender = content::RenderThread::Get();
     93   DCHECK(sender);
     94   *error_message = PP_MakeUndefined();
     95   int routing_id = 0;
     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.
    100   if (uses_ppapi) {
    101     routing_id = GetRoutingID(instance);
    102     if (!routing_id)
    103       return PP_EXTERNAL_PLUGIN_FAILED;
    104   }
    105 
    106   InstanceInfo instance_info;
    107   instance_info.url = GURL(alleged_url);
    108 
    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;
    119 
    120   if (!sender->Send(new NaClHostMsg_LaunchNaCl(
    121           nacl::NaClLaunchParams(instance_info.url.spec(),
    122                                  routing_id,
    123                                  perm_bits,
    124                                  PP_ToBool(uses_irt),
    125                                  PP_ToBool(enable_dyncode_syscalls),
    126                                  PP_ToBool(enable_exception_handling),
    127                                  PP_ToBool(enable_crash_throttling)),
    128           &launch_result,
    129           &error_message_string))) {
    130     return PP_EXTERNAL_PLUGIN_FAILED;
    131   }
    132   if (!error_message_string.empty()) {
    133     *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
    134     return PP_EXTERNAL_PLUGIN_FAILED;
    135   }
    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)
    143   if (!invalid_handle)
    144     invalid_handle = (instance_info.channel_handle.socket.fd == -1);
    145 #endif
    146   if (!invalid_handle)
    147     g_instance_info.Get()[instance] = instance_info;
    148 
    149   *(static_cast<NaClHandle*>(imc_handle)) =
    150       nacl::ToNativeHandle(result_socket);
    151 
    152   return PP_EXTERNAL_PLUGIN_OK;
    153 }
    154 
    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;
    161   }
    162   InstanceInfo instance_info = it->second;
    163   map.erase(it);
    164 
    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;
    170   }
    171 
    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);
    178 }
    179 
    180 int UrandomFD(void) {
    181 #if defined(OS_POSIX)
    182   return base::GetUrandomFD();
    183 #else
    184   return -1;
    185 #endif
    186 }
    187 
    188 PP_Bool Are3DInterfacesDisabled() {
    189   return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
    190                          switches::kDisable3DAPIs));
    191 }
    192 
    193 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
    194                               uint32_t process_id,
    195                               PP_FileHandle* target_handle,
    196                               uint32_t desired_access,
    197                               uint32_t options) {
    198 #if defined(OS_WIN)
    199   return content::BrokerDuplicateHandle(source_handle, process_id,
    200                                         target_handle, desired_access,
    201                                         options);
    202 #else
    203   return 0;
    204 #endif
    205 }
    206 
    207 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
    208   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
    209   IPC::Sender* sender = content::RenderThread::Get();
    210   DCHECK(sender);
    211   if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
    212           std::string(filename),
    213           &out_fd))) {
    214     return base::kInvalidPlatformFileValue;
    215   }
    216   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
    217     return base::kInvalidPlatformFileValue;
    218   }
    219   base::PlatformFile handle =
    220       IPC::PlatformFileForTransitToPlatformFile(out_fd);
    221   return handle;
    222 }
    223 
    224 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
    225   IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
    226   IPC::Sender* sender = content::RenderThread::Get();
    227   DCHECK(sender);
    228   if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
    229           &transit_fd))) {
    230     return base::kInvalidPlatformFileValue;
    231   }
    232 
    233   if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
    234     return base::kInvalidPlatformFileValue;
    235   }
    236 
    237   base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
    238       transit_fd);
    239   return handle;
    240 }
    241 
    242 int32_t GetNexeFd(PP_Instance instance,
    243                   const char* pexe_url,
    244                   uint32_t abi_version,
    245                   uint32_t opt_level,
    246                   const char* last_modified,
    247                   const char* etag,
    248                   PP_Bool has_no_store_header,
    249                   PP_Bool* is_hit,
    250                   PP_FileHandle* handle,
    251                   struct PP_CompletionCallback callback) {
    252   ppapi::thunk::EnterInstance enter(instance, callback);
    253   if (enter.failed())
    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);
    259 
    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);
    264 
    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);
    272 
    273   g_pnacl_resource_host.Get()->RequestNexeFd(
    274       GetRoutingID(instance),
    275       instance,
    276       cache_info,
    277       is_hit,
    278       handle,
    279       enter.callback());
    280 
    281   return enter.SetResult(PP_OK_COMPLETIONPENDING);
    282 }
    283 
    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)
    288     return;
    289   g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
    290 }
    291 
    292 PP_ExternalPluginResult ReportNaClError(PP_Instance instance,
    293                               PP_NaClError error_id) {
    294   IPC::Sender* sender = content::RenderThread::Get();
    295 
    296   if (!sender->Send(
    297           new NaClHostMsg_NaClErrorStatus(
    298               // TODO(dschuff): does this enum need to be sent as an int,
    299               // or is it safe to include the appropriate headers in
    300               // render_messages.h?
    301               GetRoutingID(instance), static_cast<int>(error_id)))) {
    302     return PP_EXTERNAL_PLUGIN_FAILED;
    303   }
    304   return PP_EXTERNAL_PLUGIN_OK;
    305 }
    306 
    307 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
    308                                  const char* file_url,
    309                                  uint64_t* nonce_lo,
    310                                  uint64_t* nonce_hi) {
    311   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
    312   IPC::Sender* sender = content::RenderThread::Get();
    313   DCHECK(sender);
    314   *nonce_lo = 0;
    315   *nonce_hi = 0;
    316   base::FilePath file_path;
    317   if (!sender->Send(
    318       new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
    319                                                GURL(file_url),
    320                                                &out_fd,
    321                                                nonce_lo,
    322                                                nonce_hi))) {
    323     return base::kInvalidPlatformFileValue;
    324   }
    325 
    326   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
    327     return base::kInvalidPlatformFileValue;
    328   }
    329 
    330   base::PlatformFile handle =
    331       IPC::PlatformFileForTransitToPlatformFile(out_fd);
    332   return handle;
    333 }
    334 
    335 blink::WebString EventTypeToString(PP_NaClEventType event_type) {
    336   switch (event_type) {
    337     case PP_NACL_EVENT_LOADSTART:
    338       return blink::WebString::fromUTF8("loadstart");
    339     case PP_NACL_EVENT_PROGRESS:
    340       return blink::WebString::fromUTF8("progress");
    341     case PP_NACL_EVENT_ERROR:
    342       return blink::WebString::fromUTF8("error");
    343     case PP_NACL_EVENT_ABORT:
    344       return blink::WebString::fromUTF8("abort");
    345     case PP_NACL_EVENT_LOAD:
    346       return blink::WebString::fromUTF8("load");
    347     case PP_NACL_EVENT_LOADEND:
    348       return blink::WebString::fromUTF8("loadend");
    349     case PP_NACL_EVENT_CRASH:
    350       return blink::WebString::fromUTF8("crash");
    351   }
    352   NOTIMPLEMENTED();
    353   return blink::WebString();
    354 }
    355 
    356 void DispatchEvent(PP_Instance instance,
    357                    PP_NaClEventType event_type,
    358                    struct PP_Var resource_url,
    359                    PP_Bool length_is_computable,
    360                    uint64_t loaded_bytes,
    361                    uint64_t total_bytes) {
    362   content::PepperPluginInstance* plugin_instance =
    363       content::PepperPluginInstance::Get(instance);
    364   if (!plugin_instance) {
    365     NOTREACHED();
    366     return;
    367   }
    368   blink::WebPluginContainer* container = plugin_instance->GetContainer();
    369   // It's possible that container() is NULL if the plugin has been removed from
    370   // the DOM (but the PluginInstance is not destroyed yet).
    371   if (!container)
    372     return;
    373   blink::WebFrame* frame = container->element().document().frame();
    374   if (!frame)
    375     return;
    376   v8::HandleScope handle_scope(plugin_instance->GetIsolate());
    377   v8::Local<v8::Context> context(
    378       plugin_instance->GetIsolate()->GetCurrentContext());
    379   if (context.IsEmpty()) {
    380     // If there's no JavaScript on the stack, we have to make a new Context.
    381     context = v8::Context::New(plugin_instance->GetIsolate());
    382   }
    383   v8::Context::Scope context_scope(context);
    384 
    385   ppapi::StringVar* url_var = ppapi::StringVar::FromPPVar(resource_url);
    386   if (url_var) {
    387     blink::WebString url_string = blink::WebString::fromUTF8(
    388         url_var->value().data(), url_var->value().size());
    389     blink::WebDOMResourceProgressEvent event(EventTypeToString(event_type),
    390                                               PP_ToBool(length_is_computable),
    391                                               loaded_bytes,
    392                                               total_bytes,
    393                                               url_string);
    394     container->element().dispatchEvent(event);
    395   } else {
    396     blink::WebDOMProgressEvent event(EventTypeToString(event_type),
    397                                       PP_ToBool(length_is_computable),
    398                                       loaded_bytes,
    399                                       total_bytes);
    400     container->element().dispatchEvent(event);
    401   }
    402 }
    403 
    404 void SetReadOnlyProperty(PP_Instance instance,
    405                          struct PP_Var key,
    406                          struct PP_Var value) {
    407   content::PepperPluginInstance* plugin_instance =
    408       content::PepperPluginInstance::Get(instance);
    409   plugin_instance->SetEmbedProperty(key, value);
    410 }
    411 
    412 const PPB_NaCl_Private nacl_interface = {
    413   &LaunchSelLdr,
    414   &StartPpapiProxy,
    415   &UrandomFD,
    416   &Are3DInterfacesDisabled,
    417   &BrokerDuplicateHandle,
    418   &GetReadonlyPnaclFD,
    419   &CreateTemporaryFile,
    420   &GetNexeFd,
    421   &ReportTranslationFinished,
    422   &ReportNaClError,
    423   &OpenNaClExecutable,
    424   &DispatchEvent,
    425   &SetReadOnlyProperty
    426 };
    427 
    428 }  // namespace
    429 
    430 namespace nacl {
    431 
    432 const PPB_NaCl_Private* GetNaClPrivateInterface() {
    433   return &nacl_interface;
    434 }
    435 
    436 }  // namespace nacl
    437 
    438 #endif  // DISABLE_NACL
    439