| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2008 The Native Client Authors. All rights reserved. | |
| 3 * Use of this source code is governed by a BSD-style license that can | |
| 4 * be found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 #include "native_client/src/trusted/plugin/srpc/plugin.h" | |
| 8 | |
| 9 #include <assert.h> | |
| 10 #include <fcntl.h> | |
| 11 #include <stdarg.h> | |
| 12 #include <stdio.h> | |
| 13 #include <stdlib.h> | |
| 14 #include <string.h> | |
| 15 | |
| 16 #include <sys/types.h> | |
| 17 #include <sys/stat.h> | |
| 18 | |
| 19 #include "native_client/src/include/nacl_string.h" | |
| 20 #include "native_client/src/include/portability_string.h" | |
| 21 #include "native_client/src/trusted/desc/nacl_desc_base.h" | |
| 22 #include "native_client/src/trusted/desc/nacl_desc_conn_cap.h" | |
| 23 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h" | |
| 24 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" | |
| 25 #include "native_client/src/trusted/plugin/npapi/video.h" | |
| 26 #include "native_client/src/trusted/plugin/origin.h" | |
| 27 #include "native_client/src/trusted/plugin/srpc/browser_interface.h" | |
| 28 #include "native_client/src/trusted/plugin/srpc/connected_socket.h" | |
| 29 #include "native_client/src/trusted/plugin/srpc/nexe_arch.h" | |
| 30 #include "native_client/src/trusted/plugin/srpc/scriptable_handle.h" | |
| 31 #include "native_client/src/trusted/plugin/srpc/service_runtime.h" | |
| 32 #include "native_client/src/trusted/plugin/srpc/shared_memory.h" | |
| 33 #include "native_client/src/trusted/plugin/srpc/socket_address.h" | |
| 34 #include "native_client/src/trusted/plugin/srpc/stream_shm_buffer.h" | |
| 35 #include "native_client/src/trusted/plugin/srpc/string_encoding.h" | |
| 36 #include "native_client/src/trusted/plugin/srpc/utility.h" | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 static int32_t stringToInt32(char* src) { | |
| 41 return strtol(src, // NOLINT(runtime/deprecated_fn) | |
| 42 static_cast<char**>(NULL), 0); | |
| 43 } | |
| 44 | |
| 45 // TODO(sehr): using a static jmpbuf here has several problems. Notably we | |
| 46 // cannot reliably handle errors when there are multiple plugins in the same | |
| 47 // process. Issue 605. | |
| 48 PLUGIN_JMPBUF g_LoaderEnv; | |
| 49 | |
| 50 bool ShmFactory(void* obj, plugin::SrpcParams* params) { | |
| 51 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 52 | |
| 53 plugin::SharedMemory* portable_shared_memory = | |
| 54 plugin::SharedMemory::New(plugin, params->ins()[0]->u.ival); | |
| 55 plugin::ScriptableHandle* shared_memory = | |
| 56 plugin->browser_interface()->NewScriptableHandle(portable_shared_memory); | |
| 57 if (NULL == shared_memory) { | |
| 58 params->set_exception_string("out of memory"); | |
| 59 portable_shared_memory->Delete(); | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 params->outs()[0]->tag = NACL_SRPC_ARG_TYPE_OBJECT; | |
| 64 params->outs()[0]->u.oval = shared_memory; | |
| 65 return true; | |
| 66 } | |
| 67 | |
| 68 bool DefaultSocketAddress(void* obj, plugin::SrpcParams* params) { | |
| 69 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 70 if (NULL == plugin->socket_address()) { | |
| 71 params->set_exception_string("no socket address"); | |
| 72 return false; | |
| 73 } | |
| 74 plugin->socket_address()->AddRef(); | |
| 75 // Plug the scriptable object into the return values. | |
| 76 params->outs()[0]->tag = NACL_SRPC_ARG_TYPE_OBJECT; | |
| 77 params->outs()[0]->u.oval = plugin->socket_address(); | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 // A method to test the cost of invoking a method in a plugin without | |
| 82 // making an RPC to the service runtime. Used for performance evaluation. | |
| 83 bool NullPluginMethod(void* obj, plugin::SrpcParams* params) { | |
| 84 UNREFERENCED_PARAMETER(obj); | |
| 85 params->outs()[0]->tag = NACL_SRPC_ARG_TYPE_INT; | |
| 86 params->outs()[0]->u.ival = 0; | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 bool GetModuleReadyProperty(void* obj, plugin::SrpcParams* params) { | |
| 91 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 92 if (plugin->socket()) { | |
| 93 params->outs()[0]->u.ival = 1; | |
| 94 } else { | |
| 95 params->outs()[0]->u.ival = 0; | |
| 96 } | |
| 97 return true; | |
| 98 } | |
| 99 | |
| 100 bool SetModuleReadyProperty(void* obj, plugin::SrpcParams* params) { | |
| 101 UNREFERENCED_PARAMETER(obj); | |
| 102 params->set_exception_string("__moduleReady is a read-only property"); | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 bool GetNexesProperty(void* obj, plugin::SrpcParams* params) { | |
| 107 UNREFERENCED_PARAMETER(obj); | |
| 108 UNREFERENCED_PARAMETER(params); | |
| 109 // Note, "get" must be present in the method map for "set" to work. | |
| 110 PLUGIN_PRINTF(("GetNexesProperty not yet implemented.\n")); | |
| 111 return false; | |
| 112 } | |
| 113 | |
| 114 // Update "nexes", a write-only property that computes a value to | |
| 115 // assign to the "src" property based on the supported sandbox. | |
| 116 bool SetNexesProperty(void* obj, plugin::SrpcParams* params) { | |
| 117 return reinterpret_cast<plugin::Plugin*>(obj)-> | |
| 118 SetNexesPropertyImpl(params->ins()[0]->u.sval); | |
| 119 } | |
| 120 | |
| 121 bool GetSrcProperty(void* obj, plugin::SrpcParams* params) { | |
| 122 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 123 if (plugin->logical_url() != NULL) { | |
| 124 params->outs()[0]->u.sval = strdup(plugin->logical_url()); | |
| 125 PLUGIN_PRINTF(("GetSrcProperty 'src' = %s\n", plugin->logical_url())); | |
| 126 return true; | |
| 127 } else { | |
| 128 // (NULL is not an acceptable SRPC result.) | |
| 129 PLUGIN_PRINTF(("GetSrcProperty 'src' failed\n")); | |
| 130 return false; | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 bool SetSrcProperty(void* obj, plugin::SrpcParams* params) { | |
| 135 PLUGIN_PRINTF(("SetSrcProperty\n")); | |
| 136 return reinterpret_cast<plugin::Plugin*>(obj)-> | |
| 137 SetSrcPropertyImpl(params->ins()[0]->u.sval); | |
| 138 } | |
| 139 | |
| 140 bool GetHeightProperty(void* obj, plugin::SrpcParams* params) { | |
| 141 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 142 params->outs()[0]->u.ival = plugin->height(); | |
| 143 return true; | |
| 144 } | |
| 145 | |
| 146 bool SetHeightProperty(void* obj, plugin::SrpcParams* params) { | |
| 147 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 148 plugin->set_height(params->ins()[0]->u.ival); | |
| 149 return true; | |
| 150 } | |
| 151 | |
| 152 bool GetWidthProperty(void* obj, plugin::SrpcParams* params) { | |
| 153 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 154 params->outs()[0]->u.ival = plugin->width(); | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 bool SetWidthProperty(void* obj, plugin::SrpcParams* params) { | |
| 159 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 160 plugin->set_width(params->ins()[0]->u.ival); | |
| 161 return true; | |
| 162 } | |
| 163 | |
| 164 bool GetVideoUpdateModeProperty(void* obj, plugin::SrpcParams* params) { | |
| 165 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 166 params->outs()[0]->u.ival = plugin->video_update_mode(); | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 bool SetVideoUpdateModeProperty(void* obj, plugin::SrpcParams* params) { | |
| 171 plugin::Plugin* plugin = reinterpret_cast<plugin::Plugin*>(obj); | |
| 172 plugin->set_video_update_mode(params->ins()[0]->u.ival); | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 176 void SignalHandler(int value) { | |
| 177 PLUGIN_PRINTF(("Plugin::SignalHandler()\n")); | |
| 178 PLUGIN_LONGJMP(g_LoaderEnv, value); | |
| 179 } | |
| 180 | |
| 181 } // namespace | |
| 182 | |
| 183 namespace plugin { | |
| 184 | |
| 185 // TODO(mseaborn): Although this will usually not block, it will | |
| 186 // block if the socket's buffer fills up. | |
| 187 bool Plugin::SendAsyncMessage(void* obj, SrpcParams* params, | |
| 188 nacl::DescWrapper** fds, int fds_count) { | |
| 189 Plugin* plugin = reinterpret_cast<Plugin*>(obj); | |
| 190 if (plugin->service_runtime_ == NULL) { | |
| 191 params->set_exception_string("No subprocess running"); | |
| 192 return false; | |
| 193 } | |
| 194 | |
| 195 // TODO(mseaborn): Handle strings containing NULLs. This might | |
| 196 // involve using a different SRPC type. | |
| 197 char* utf8string = params->ins()[0]->u.sval; | |
| 198 char* data; | |
| 199 size_t data_size; | |
| 200 if (!ByteStringFromUTF8(utf8string, strlen(utf8string), &data, &data_size)) { | |
| 201 params->set_exception_string("Invalid string"); | |
| 202 return false; | |
| 203 } | |
| 204 nacl::DescWrapper::MsgIoVec iov; | |
| 205 nacl::DescWrapper::MsgHeader message; | |
| 206 iov.base = data; | |
| 207 iov.length = static_cast<nacl_abi_size_t>(data_size); | |
| 208 message.iov = &iov; | |
| 209 message.iov_length = 1; | |
| 210 message.ndescv = fds; | |
| 211 message.ndescv_length = fds_count; | |
| 212 message.flags = 0; | |
| 213 nacl::DescWrapper* socket = plugin->service_runtime_->async_send_desc; | |
| 214 ssize_t sent = socket->SendMsg(&message, 0); | |
| 215 free(data); | |
| 216 if (sent < 0) { | |
| 217 params->set_exception_string("Error sending message"); | |
| 218 return false; | |
| 219 } | |
| 220 return true; | |
| 221 } | |
| 222 | |
| 223 // TODO(mseaborn): Combine __sendAsyncMessage0 and __sendAsyncMessage1 | |
| 224 // into a single method that takes an array of FDs. SRPC does not | |
| 225 // provide a handle array type so there is not a simple way to do | |
| 226 // this. | |
| 227 bool Plugin::SendAsyncMessage0(void* obj, SrpcParams* params) { | |
| 228 return SendAsyncMessage(obj, params, NULL, 0); | |
| 229 } | |
| 230 | |
| 231 bool Plugin::SendAsyncMessage1(void* obj, SrpcParams* params) { | |
| 232 Plugin* plugin = reinterpret_cast<Plugin*>(obj); | |
| 233 nacl::DescWrapper* fd_to_send = | |
| 234 plugin->wrapper_factory()->MakeGeneric(params->ins()[1]->u.hval); | |
| 235 return SendAsyncMessage(obj, params, &fd_to_send, 1); | |
| 236 } | |
| 237 | |
| 238 static int const kAbiHeaderBuffer = 256; // must be at least EI_ABIVERSION + 1 | |
| 239 | |
| 240 void Plugin::LoadMethods() { | |
| 241 // Methods supported by Plugin. | |
| 242 AddMethodCall(ShmFactory, "__shmFactory", "i", "h"); | |
| 243 AddMethodCall(DefaultSocketAddress, "__defaultSocketAddress", "", "h"); | |
| 244 AddMethodCall(NullPluginMethod, "__nullPluginMethod", "s", "i"); | |
| 245 AddMethodCall(SendAsyncMessage0, "__sendAsyncMessage0", "s", ""); | |
| 246 AddMethodCall(SendAsyncMessage1, "__sendAsyncMessage1", "sh", ""); | |
| 247 // Properties implemented by Plugin. | |
| 248 AddPropertyGet(GetHeightProperty, "height", "i"); | |
| 249 AddPropertySet(SetHeightProperty, "height", "i"); | |
| 250 AddPropertyGet(GetModuleReadyProperty, "__moduleReady", "i"); | |
| 251 AddPropertySet(SetModuleReadyProperty, "__moduleReady", "i"); | |
| 252 AddPropertyGet(GetNexesProperty, "nexes", "s"); | |
| 253 AddPropertySet(SetNexesProperty, "nexes", "s"); | |
| 254 AddPropertyGet(GetSrcProperty, "src", "s"); | |
| 255 AddPropertySet(SetSrcProperty, "src", "s"); | |
| 256 AddPropertyGet(GetVideoUpdateModeProperty, "videoUpdateMode", "i"); | |
| 257 AddPropertySet(SetVideoUpdateModeProperty, "videoUpdateMode", "i"); | |
| 258 AddPropertyGet(GetWidthProperty, "width", "i"); | |
| 259 AddPropertySet(SetWidthProperty, "width", "i"); | |
| 260 } | |
| 261 | |
| 262 bool Plugin::HasMethodEx(uintptr_t method_id, CallType call_type) { | |
| 263 if (NULL == socket_) { | |
| 264 return false; | |
| 265 } | |
| 266 return socket_->handle()->HasMethod(method_id, call_type); | |
| 267 } | |
| 268 | |
| 269 bool Plugin::InvokeEx(uintptr_t method_id, | |
| 270 CallType call_type, | |
| 271 SrpcParams* params) { | |
| 272 if (NULL == socket_) { | |
| 273 return false; | |
| 274 } | |
| 275 return socket_->handle()->Invoke(method_id, call_type, params); | |
| 276 } | |
| 277 | |
| 278 bool Plugin::InitParamsEx(uintptr_t method_id, | |
| 279 CallType call_type, | |
| 280 SrpcParams* params) { | |
| 281 if (NULL == socket_) { | |
| 282 return false; | |
| 283 } | |
| 284 return socket_->handle()->InitParams(method_id, call_type, params); | |
| 285 } | |
| 286 | |
| 287 | |
| 288 bool Plugin::SetNexesPropertyImpl(const char* nexes_attr) { | |
| 289 PLUGIN_PRINTF(("Plugin::SetNexesPropertyImpl: %s\n", nexes_attr)); | |
| 290 nacl::string result; | |
| 291 if (!GetNexeURL(nexes_attr, &result)) { | |
| 292 // TODO(adonovan): Ideally we would print to the browser's | |
| 293 // JavaScript console: alert popups are annoying, and no-one can | |
| 294 // be expected to read stderr. | |
| 295 PLUGIN_PRINTF(("%s\n", result.c_str())); | |
| 296 browser_interface()->Alert(instance_id(), result); | |
| 297 return false; | |
| 298 } else { | |
| 299 return SetSrcPropertyImpl(result); | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 bool Plugin::SetSrcPropertyImpl(const nacl::string& url) { | |
| 304 if (NULL != service_runtime_) { | |
| 305 PLUGIN_PRINTF(("Plugin::SetProperty: unloading previous\n")); | |
| 306 // Plugin owns socket_address_ and socket_, so when we change to a new | |
| 307 // socket we need to give up ownership of the old one. | |
| 308 socket_address_->Unref(); | |
| 309 socket_address_ = NULL; | |
| 310 socket_->Unref(); | |
| 311 socket_ = NULL; | |
| 312 service_runtime_->Shutdown(); | |
| 313 delete service_runtime_; | |
| 314 service_runtime_ = NULL; | |
| 315 ShutDownReceiveThread(); | |
| 316 } | |
| 317 return RequestNaClModule(url); | |
| 318 } | |
| 319 | |
| 320 bool Plugin::Init(BrowserInterface* browser_interface, | |
| 321 InstanceIdentifier instance_id, | |
| 322 int argc, | |
| 323 char* argn[], | |
| 324 char* argv[]) { | |
| 325 PLUGIN_PRINTF(("Plugin::Init: instance_id=%p\n", | |
| 326 reinterpret_cast<void*>(instance_id))); | |
| 327 | |
| 328 browser_interface_ = browser_interface; | |
| 329 instance_id_ = instance_id; | |
| 330 // Remember the embed/object argn/argv pairs. | |
| 331 argn_ = new(std::nothrow) char*[argc]; | |
| 332 argv_ = new(std::nothrow) char*[argc]; | |
| 333 argc_ = 0; | |
| 334 // Set up the height and width attributes if passed (for Opera) | |
| 335 for (int i = 0; i < argc; ++i) { | |
| 336 if (!strncmp(argn[i], "height", 7)) { | |
| 337 set_height(stringToInt32(argv[i])); | |
| 338 } else if (!strncmp(argn[i], "width", 6)) { | |
| 339 set_width(stringToInt32(argv[i])); | |
| 340 } else if (!strncmp(argn[i], "update", 7)) { | |
| 341 set_video_update_mode(stringToInt32(argv[i])); | |
| 342 } else { | |
| 343 if (NULL != argn_ && NULL != argv_) { | |
| 344 argn_[argc_] = strdup(argn[i]); | |
| 345 argv_[argc_] = strdup(argv[i]); | |
| 346 if (NULL == argn_[argc_] || | |
| 347 NULL == argv_[argc_]) { | |
| 348 // Give up on passing arguments. | |
| 349 free(argn_[argc_]); | |
| 350 free(argv_[argc_]); | |
| 351 continue; | |
| 352 } | |
| 353 ++argc_; | |
| 354 } | |
| 355 } | |
| 356 } | |
| 357 // TODO(sehr): this leaks strings if there is a subsequent failure. | |
| 358 | |
| 359 // Set up the factory used to produce DescWrappers. | |
| 360 wrapper_factory_ = new nacl::DescWrapperFactory(); | |
| 361 if (NULL == wrapper_factory_) { | |
| 362 return false; | |
| 363 } | |
| 364 PLUGIN_PRINTF(("Plugin::Init: wrapper_factory=%p\n", | |
| 365 reinterpret_cast<void*>(wrapper_factory_))); | |
| 366 | |
| 367 // Check that the origin is allowed. | |
| 368 nacl::string href = ""; | |
| 369 if (browser_interface_->GetOrigin(instance_id_, &href)) { | |
| 370 origin_ = nacl::UrlToOrigin(href); | |
| 371 PLUGIN_PRINTF(("Plugin::Init: origin=%s\n", origin_.c_str())); | |
| 372 // Check that origin is in the list of permitted origins. | |
| 373 origin_valid_ = nacl::OriginIsInWhitelist(origin_); | |
| 374 // This implementation of same-origin policy does not take | |
| 375 // document.domain element into account. | |
| 376 } | |
| 377 | |
| 378 // Set up the scriptable methods for the plugin. | |
| 379 LoadMethods(); | |
| 380 | |
| 381 // If the <embed src='...'> attr was defined, the browser would have | |
| 382 // implicitly called GET on it, which calls Load() and set_logical_url(). | |
| 383 // In the absence of this attr, we use the "nexes" attribute if present. | |
| 384 if (logical_url() == NULL) { | |
| 385 const char* nexes_attr = LookupArgument("nexes"); | |
| 386 if (nexes_attr != NULL) { | |
| 387 SetNexesPropertyImpl(nexes_attr); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 PLUGIN_PRINTF(("Plugin::Init: done\n")); | |
| 392 // Return success. | |
| 393 return true; | |
| 394 } | |
| 395 | |
| 396 Plugin::Plugin() | |
| 397 : service_runtime_(NULL), | |
| 398 receive_thread_running_(false), | |
| 399 argc_(-1), | |
| 400 argn_(NULL), | |
| 401 argv_(NULL), | |
| 402 socket_address_(NULL), | |
| 403 socket_(NULL), | |
| 404 local_url_(NULL), | |
| 405 logical_url_(NULL), | |
| 406 origin_valid_(false), | |
| 407 height_(0), | |
| 408 width_(0), | |
| 409 video_update_mode_(kVideoUpdatePluginPaint), | |
| 410 wrapper_factory_(NULL) { | |
| 411 PLUGIN_PRINTF(("Plugin::Plugin(%p)\n", static_cast<void*>(this))); | |
| 412 } | |
| 413 | |
| 414 void Plugin::Invalidate() { | |
| 415 socket_address_ = NULL; | |
| 416 socket_ = NULL; | |
| 417 } | |
| 418 | |
| 419 void Plugin::ShutDownReceiveThread() { | |
| 420 if (receive_thread_running_) { | |
| 421 NaClThreadJoin(&receive_thread_); | |
| 422 receive_thread_running_ = false; | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 Plugin::~Plugin() { | |
| 427 PLUGIN_PRINTF(("Plugin::~Plugin(%p)\n", static_cast<void*>(this))); | |
| 428 | |
| 429 // After invalidation, the browser does not respect reference counting, | |
| 430 // so we shut down here what we can and prevent attempts to shut down | |
| 431 // other linked structures in Deallocate. | |
| 432 | |
| 433 // Free the socket address for this plugin, if any. | |
| 434 if (NULL != socket_address_) { | |
| 435 PLUGIN_PRINTF(("Plugin::~Plugin: unloading\n")); | |
| 436 // Deallocating a plugin releases ownership of the socket address. | |
| 437 socket_address_->Unref(); | |
| 438 } | |
| 439 // Free the connected socket for this plugin, if any. | |
| 440 if (NULL != socket_) { | |
| 441 PLUGIN_PRINTF(("Plugin::~Plugin: unloading\n")); | |
| 442 // Deallocating a plugin releases ownership of the socket. | |
| 443 socket_->Unref(); | |
| 444 } | |
| 445 // Clear the pointers to the connected socket and service runtime interface. | |
| 446 socket_address_ = NULL; | |
| 447 socket_ = NULL; | |
| 448 delete service_runtime_; | |
| 449 service_runtime_ = NULL; | |
| 450 ShutDownReceiveThread(); | |
| 451 PLUGIN_PRINTF(("Plugin::~Plugin(%p)\n", static_cast<void*>(this))); | |
| 452 free(local_url_); | |
| 453 free(logical_url_); | |
| 454 } | |
| 455 | |
| 456 void Plugin::set_local_url(const char* url) { | |
| 457 PLUGIN_PRINTF(("Plugin::set_local_url(%s)\n", url)); | |
| 458 if (local_url_ != NULL) free(local_url_); | |
| 459 local_url_ = strdup(url); | |
| 460 } | |
| 461 | |
| 462 void Plugin::set_logical_url(const char* url) { | |
| 463 PLUGIN_PRINTF(("Plugin::set_logical_url(%s)\n", url)); | |
| 464 if (logical_url_ != NULL) free(logical_url_); | |
| 465 logical_url_ = strdup(url); | |
| 466 } | |
| 467 | |
| 468 // Create a new service node from a downloaded service. | |
| 469 bool Plugin::Load(nacl::string logical_url, const char* local_url) { | |
| 470 return Load(logical_url, local_url, static_cast<StreamShmBuffer*>(NULL)); | |
| 471 } | |
| 472 | |
| 473 bool Plugin::Load(nacl::string logical_url, | |
| 474 const char* local_url, | |
| 475 StreamShmBuffer* shmbufp) { | |
| 476 BrowserInterface* browser_interface = this->browser_interface(); | |
| 477 | |
| 478 if (NULL == shmbufp) { | |
| 479 PLUGIN_PRINTF(("Plugin::Load(%s)\n", local_url)); | |
| 480 } else { | |
| 481 PLUGIN_PRINTF(("Plugin::Load(%p)\n", reinterpret_cast<void*>(shmbufp))); | |
| 482 } | |
| 483 | |
| 484 // Save the origin and local_url. | |
| 485 set_nacl_module_origin(nacl::UrlToOrigin(logical_url)); | |
| 486 set_logical_url(logical_url.c_str()); | |
| 487 set_local_url(local_url); | |
| 488 // If the page origin where the EMBED/OBJECT tag occurs is not in | |
| 489 // the whitelist, refuse to load. If the NaCl module's origin is | |
| 490 // not in the whitelist, also refuse to load. | |
| 491 // TODO(adonovan): JavaScript permits cross-origin loading, and so | |
| 492 // does Chrome ; why don't we? | |
| 493 if (!origin_valid_ || !nacl::OriginIsInWhitelist(nacl_module_origin())) { | |
| 494 nacl::string message = nacl::string("Load failed: NaCl module ") + | |
| 495 logical_url + " does not come ""from a whitelisted source. " | |
| 496 "See native_client/src/trusted/plugin/origin.cc for the list."; | |
| 497 browser_interface->Alert(instance_id(), message.c_str()); | |
| 498 return false; | |
| 499 } | |
| 500 // Catch any bad accesses, etc., while loading. | |
| 501 ScopedCatchSignals sigcatcher( | |
| 502 (ScopedCatchSignals::SigHandlerType) SignalHandler); | |
| 503 if (PLUGIN_SETJMP(g_LoaderEnv, 1)) { | |
| 504 return false; | |
| 505 } | |
| 506 | |
| 507 PLUGIN_PRINTF(("Load: NaCl module from '%s'\n", local_url_)); | |
| 508 | |
| 509 // Check ELF magic and ABI version compatibility. | |
| 510 bool success = false; | |
| 511 nacl::string error_string; | |
| 512 if (NULL == shmbufp) { | |
| 513 success = browser_interface->MightBeElfExecutable(local_url_, | |
| 514 &error_string); | |
| 515 } else { | |
| 516 // Read out first chunk for MightBeElfExecutable; this suffices for | |
| 517 // ELF headers etc. | |
| 518 char elf_hdr_buf[kAbiHeaderBuffer]; | |
| 519 ssize_t result; | |
| 520 result = shmbufp->read(0, sizeof elf_hdr_buf, elf_hdr_buf); | |
| 521 if (sizeof elf_hdr_buf == result) { // (const char*)(elf_hdr_buf) | |
| 522 success = browser_interface->MightBeElfExecutable(elf_hdr_buf, | |
| 523 sizeof elf_hdr_buf, | |
| 524 &error_string); | |
| 525 } | |
| 526 } | |
| 527 if (!success) { | |
| 528 browser_interface->Alert(instance_id(), error_string); | |
| 529 return false; | |
| 530 } | |
| 531 // Load a file via a forked sel_ldr process. | |
| 532 service_runtime_ = new(std::nothrow) ServiceRuntime(browser_interface, this); | |
| 533 if (NULL == service_runtime_) { | |
| 534 PLUGIN_PRINTF((" ServiceRuntime Ctor failed\n")); | |
| 535 browser_interface->Alert(instance_id(), | |
| 536 "ServiceRuntime Ctor failed"); | |
| 537 return false; | |
| 538 } | |
| 539 bool service_runtime_started = false; | |
| 540 if (NULL == shmbufp) { | |
| 541 service_runtime_started = service_runtime_->Start(local_url_); | |
| 542 } else { | |
| 543 int32_t size; | |
| 544 NaClDesc* raw_desc = shmbufp->shm(&size); | |
| 545 if (NULL == raw_desc) { | |
| 546 PLUGIN_PRINTF((" extracting shm failed\n")); | |
| 547 return false; | |
| 548 } | |
| 549 nacl::DescWrapper* wrapped_shm = | |
| 550 wrapper_factory_->MakeGeneric(NaClDescRef(raw_desc)); | |
| 551 service_runtime_started = service_runtime_->Start(local_url_, wrapped_shm); | |
| 552 // Start consumes the wrapped_shm. | |
| 553 } | |
| 554 if (!service_runtime_started) { | |
| 555 PLUGIN_PRINTF((" Load: FAILED to start service runtime")); | |
| 556 browser_interface->Alert(instance_id(), | |
| 557 "Load: FAILED to start service runtime"); | |
| 558 return false; | |
| 559 } | |
| 560 | |
| 561 PLUGIN_PRINTF((" Load: started sel_ldr\n")); | |
| 562 socket_address_ = service_runtime_->default_socket_address(); | |
| 563 PLUGIN_PRINTF((" Load: established socket address %p\n", | |
| 564 static_cast<void*>(socket_address_))); | |
| 565 socket_ = service_runtime_->default_socket(); | |
| 566 PLUGIN_PRINTF((" Load: established socket %p\n", | |
| 567 static_cast<void*>(socket_))); | |
| 568 // Plugin takes ownership of socket_ from service_runtime_, | |
| 569 // so we do not need to call NPN_RetainObject. | |
| 570 // Invoke the onload handler, if any. | |
| 571 RunOnloadHandler(); | |
| 572 return true; | |
| 573 } | |
| 574 | |
| 575 bool Plugin::LogAtServiceRuntime(int severity, nacl::string msg) { | |
| 576 return service_runtime_->Log(severity, msg); | |
| 577 } | |
| 578 | |
| 579 char* Plugin::LookupArgument(const char* key) { | |
| 580 char** keys = argn(); | |
| 581 for (int ii = 0, len = argc(); ii < len; ++ii) { | |
| 582 if (!strcmp(keys[ii], key)) { | |
| 583 return argv()[ii]; | |
| 584 } | |
| 585 } | |
| 586 return NULL; | |
| 587 } | |
| 588 | |
| 589 bool Plugin::RunOnloadHandler() { | |
| 590 BrowserInterface* browser = browser_interface(); | |
| 591 const char* onload_handler = LookupArgument("onload"); | |
| 592 if (onload_handler == NULL) { | |
| 593 return true; | |
| 594 } | |
| 595 return browser->EvalString(instance_id(), onload_handler); | |
| 596 } | |
| 597 | |
| 598 bool Plugin::RunOnfailHandler() { | |
| 599 BrowserInterface* browser = browser_interface(); | |
| 600 const char* onfail_handler = LookupArgument("onfail"); | |
| 601 if (onfail_handler == NULL) { | |
| 602 return true; | |
| 603 } | |
| 604 return browser->EvalString(instance_id(), onfail_handler); | |
| 605 } | |
| 606 | |
| 607 // The NaCl audio/video interface uses a global mutex to protect observed | |
| 608 // timing-sensitive accesses to the plugin's window when starting up and | |
| 609 // shutting down. Because there is a relatively complex relationship between | |
| 610 // ConnectedSocket and Plugin to synchronize accesses, this code is built in | |
| 611 // both the NPAPI and PPAPI plugins. This is unfortunate, because only | |
| 612 // the NPAPI plugin (when not used as part of the Chrome integration) actually | |
| 613 // needs the locking. | |
| 614 // TODO(sehr): move this code to somewhere in the npapi directory. | |
| 615 class GlobalVideoMutex { | |
| 616 public: | |
| 617 GlobalVideoMutex() { NaClMutexCtor(&mutex_); } | |
| 618 ~GlobalVideoMutex() { NaClMutexDtor(&mutex_); } | |
| 619 void Lock() { NaClMutexLock(&mutex_); } | |
| 620 void Unlock() { NaClMutexUnlock(&mutex_); } | |
| 621 | |
| 622 private: | |
| 623 NaClMutex mutex_; | |
| 624 }; | |
| 625 | |
| 626 static GlobalVideoMutex g_VideoMutex; | |
| 627 | |
| 628 void VideoGlobalLock() { | |
| 629 g_VideoMutex.Lock(); | |
| 630 } | |
| 631 | |
| 632 void VideoGlobalUnlock() { | |
| 633 g_VideoMutex.Unlock(); | |
| 634 } | |
| 635 | |
| 636 } // namespace plugin | |
| OLD | NEW |