| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "build/build_config.h" | 5 #include "build/build_config.h" |
| 6 | 6 |
| 7 #include "chrome/browser/nacl_host/nacl_process_host.h" | 7 #include "chrome/browser/nacl_host/nacl_process_host.h" |
| 8 | 8 |
| 9 #if defined(OS_POSIX) | 9 #if defined(OS_POSIX) |
| 10 #include <fcntl.h> | 10 #include <fcntl.h> |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 } | 45 } |
| 46 #endif | 46 #endif |
| 47 | 47 |
| 48 } // namespace | 48 } // namespace |
| 49 | 49 |
| 50 struct NaClProcessHost::NaClInternal { | 50 struct NaClProcessHost::NaClInternal { |
| 51 std::vector<nacl::Handle> sockets_for_renderer; | 51 std::vector<nacl::Handle> sockets_for_renderer; |
| 52 std::vector<nacl::Handle> sockets_for_sel_ldr; | 52 std::vector<nacl::Handle> sockets_for_sel_ldr; |
| 53 }; | 53 }; |
| 54 | 54 |
| 55 bool NaClProcessHost::RunningOnWOW64() { |
| 56 #if defined(OS_WIN) |
| 57 return (base::win::OSInfo::GetInstance()->wow64_status() == |
| 58 base::win::OSInfo::WOW64_ENABLED); |
| 59 #else |
| 60 return false; |
| 61 #endif |
| 62 } |
| 63 |
| 55 NaClProcessHost::NaClProcessHost(const std::wstring& url) | 64 NaClProcessHost::NaClProcessHost(const std::wstring& url) |
| 56 : BrowserChildProcessHost(NACL_LOADER_PROCESS), | 65 : BrowserChildProcessHost(NACL_LOADER_PROCESS), |
| 57 reply_msg_(NULL), | 66 reply_msg_(NULL), |
| 58 internal_(new NaClInternal()), | 67 internal_(new NaClInternal()), |
| 59 running_on_wow64_(false), | |
| 60 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 68 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
| 61 set_name(WideToUTF16Hack(url)); | 69 set_name(WideToUTF16Hack(url)); |
| 62 #if defined(OS_WIN) | |
| 63 running_on_wow64_ = (base::win::OSInfo::GetInstance()->wow64_status() == | |
| 64 base::win::OSInfo::WOW64_ENABLED); | |
| 65 #endif | |
| 66 } | 70 } |
| 67 | 71 |
| 68 NaClProcessHost::~NaClProcessHost() { | 72 NaClProcessHost::~NaClProcessHost() { |
| 69 // nacl::Close() is not available at link time if DISABLE_NACL is | 73 // nacl::Close() is not available at link time if DISABLE_NACL is |
| 70 // defined, but we still compile a bunch of other code from this | 74 // defined, but we still compile a bunch of other code from this |
| 71 // file anyway. TODO(mseaborn): Make this less messy. | 75 // file anyway. TODO(mseaborn): Make this less messy. |
| 72 #ifndef DISABLE_NACL | 76 #ifndef DISABLE_NACL |
| 73 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { | 77 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { |
| 74 if (nacl::Close(internal_->sockets_for_renderer[i]) != 0) { | 78 if (nacl::Close(internal_->sockets_for_renderer[i]) != 0) { |
| 75 LOG(ERROR) << "nacl::Close() failed"; | 79 LOG(ERROR) << "nacl::Close() failed"; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 98 NOTIMPLEMENTED() << "Native Client disabled at build time"; | 102 NOTIMPLEMENTED() << "Native Client disabled at build time"; |
| 99 return false; | 103 return false; |
| 100 #else | 104 #else |
| 101 // Place an arbitrary limit on the number of sockets to limit | 105 // Place an arbitrary limit on the number of sockets to limit |
| 102 // exposure in case the renderer is compromised. We can increase | 106 // exposure in case the renderer is compromised. We can increase |
| 103 // this if necessary. | 107 // this if necessary. |
| 104 if (socket_count > 8) { | 108 if (socket_count > 8) { |
| 105 return false; | 109 return false; |
| 106 } | 110 } |
| 107 | 111 |
| 112 if (irt_platform_file_ == base::kInvalidPlatformFileValue) { |
| 113 LOG(ERROR) << "Cannot launch NaCl process after IRT file open failed"; |
| 114 return false; |
| 115 } |
| 116 |
| 108 // Rather than creating a socket pair in the renderer, and passing | 117 // Rather than creating a socket pair in the renderer, and passing |
| 109 // one side through the browser to sel_ldr, socket pairs are created | 118 // one side through the browser to sel_ldr, socket pairs are created |
| 110 // in the browser and then passed to the renderer and sel_ldr. | 119 // in the browser and then passed to the renderer and sel_ldr. |
| 111 // | 120 // |
| 112 // This is mainly for the benefit of Windows, where sockets cannot | 121 // This is mainly for the benefit of Windows, where sockets cannot |
| 113 // be passed in messages, but are copied via DuplicateHandle(). | 122 // be passed in messages, but are copied via DuplicateHandle(). |
| 114 // This means the sandboxed renderer cannot send handles to the | 123 // This means the sandboxed renderer cannot send handles to the |
| 115 // browser process. | 124 // browser process. |
| 116 | 125 |
| 117 for (int i = 0; i < socket_count; i++) { | 126 for (int i = 0; i < socket_count; i++) { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 switches::kNaClLoaderProcess); | 182 switches::kNaClLoaderProcess); |
| 174 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); | 183 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); |
| 175 if (logging::DialogsAreSuppressed()) | 184 if (logging::DialogsAreSuppressed()) |
| 176 cmd_line->AppendSwitch(switches::kNoErrorDialogs); | 185 cmd_line->AppendSwitch(switches::kNoErrorDialogs); |
| 177 | 186 |
| 178 if (!nacl_loader_prefix.empty()) | 187 if (!nacl_loader_prefix.empty()) |
| 179 cmd_line->PrependWrapper(nacl_loader_prefix); | 188 cmd_line->PrependWrapper(nacl_loader_prefix); |
| 180 | 189 |
| 181 // On Windows we might need to start the broker process to launch a new loader | 190 // On Windows we might need to start the broker process to launch a new loader |
| 182 #if defined(OS_WIN) | 191 #if defined(OS_WIN) |
| 183 if (running_on_wow64_) { | 192 if (RunningOnWOW64()) { |
| 184 return NaClBrokerService::GetInstance()->LaunchLoader( | 193 return NaClBrokerService::GetInstance()->LaunchLoader( |
| 185 this, ASCIIToWide(channel_id())); | 194 this, ASCIIToWide(channel_id())); |
| 186 } else { | 195 } else { |
| 187 BrowserChildProcessHost::Launch(FilePath(), cmd_line); | 196 BrowserChildProcessHost::Launch(FilePath(), cmd_line); |
| 188 } | 197 } |
| 189 #elif defined(OS_POSIX) | 198 #elif defined(OS_POSIX) |
| 190 BrowserChildProcessHost::Launch(nacl_loader_prefix.empty(), // use_zygote | 199 BrowserChildProcessHost::Launch(nacl_loader_prefix.empty(), // use_zygote |
| 191 base::environment_vector(), | 200 base::environment_vector(), |
| 192 cmd_line); | 201 cmd_line); |
| 193 #endif | 202 #endif |
| 194 | 203 |
| 195 return true; | 204 return true; |
| 196 } | 205 } |
| 197 | 206 |
| 198 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) { | 207 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) { |
| 199 set_handle(handle); | 208 set_handle(handle); |
| 200 OnProcessLaunched(); | 209 OnProcessLaunched(); |
| 201 } | 210 } |
| 202 | 211 |
| 203 base::TerminationStatus NaClProcessHost::GetChildTerminationStatus( | 212 base::TerminationStatus NaClProcessHost::GetChildTerminationStatus( |
| 204 int* exit_code) { | 213 int* exit_code) { |
| 205 if (running_on_wow64_) | 214 if (RunningOnWOW64()) |
| 206 return base::GetTerminationStatus(handle(), exit_code); | 215 return base::GetTerminationStatus(handle(), exit_code); |
| 207 return BrowserChildProcessHost::GetChildTerminationStatus(exit_code); | 216 return BrowserChildProcessHost::GetChildTerminationStatus(exit_code); |
| 208 } | 217 } |
| 209 | 218 |
| 210 void NaClProcessHost::OnChildDied() { | 219 void NaClProcessHost::OnChildDied() { |
| 211 int exit_code; | 220 int exit_code; |
| 212 GetChildTerminationStatus(&exit_code); | 221 GetChildTerminationStatus(&exit_code); |
| 213 std::string message = | 222 std::string message = |
| 214 base::StringPrintf("NaCl process exited with status %i (0x%x)", | 223 base::StringPrintf("NaCl process exited with status %i (0x%x)", |
| 215 exit_code, exit_code); | 224 exit_code, exit_code); |
| 216 if (exit_code == 0) { | 225 if (exit_code == 0) { |
| 217 LOG(INFO) << message; | 226 LOG(INFO) << message; |
| 218 } else { | 227 } else { |
| 219 LOG(ERROR) << message; | 228 LOG(ERROR) << message; |
| 220 } | 229 } |
| 221 | 230 |
| 222 #if defined(OS_WIN) | 231 #if defined(OS_WIN) |
| 223 NaClBrokerService::GetInstance()->OnLoaderDied(); | 232 NaClBrokerService::GetInstance()->OnLoaderDied(); |
| 224 #endif | 233 #endif |
| 225 BrowserChildProcessHost::OnChildDied(); | 234 BrowserChildProcessHost::OnChildDied(); |
| 226 } | 235 } |
| 227 | 236 |
| 228 FilePath::StringType NaClProcessHost::GetIrtLibraryFilename() { | 237 base::PlatformFile NaClProcessHost::irt_platform_file_; |
| 229 bool on_x86_64 = running_on_wow64_; | |
| 230 #if defined(__x86_64__) | |
| 231 on_x86_64 = true; | |
| 232 #endif | |
| 233 if (on_x86_64) { | |
| 234 return FILE_PATH_LITERAL("nacl_irt_x86_64.nexe"); | |
| 235 } else { | |
| 236 return FILE_PATH_LITERAL("nacl_irt_x86_32.nexe"); | |
| 237 } | |
| 238 } | |
| 239 | 238 |
| 240 void NaClProcessHost::OnProcessLaunched() { | 239 void NaClProcessHost::OpenIrtLibraryFile() { |
| 241 // TODO(mseaborn): Opening the IRT file every time a NaCl process is | 240 irt_platform_file_ = base::kInvalidPlatformFileValue; |
| 242 // launched probably does not work with auto-update on Linux. We | 241 |
| 243 // might need to open the file on startup. If so, we would need to | 242 FilePath irt_filepath; |
| 244 // ensure that NaCl's ELF loader does not use lseek() on the shared | 243 |
| 245 // IRT file descriptor, otherwise there would be a race condition. | |
| 246 FilePath irt_path; | |
| 247 // Allow the IRT library to be overridden via an environment | 244 // Allow the IRT library to be overridden via an environment |
| 248 // variable. This allows the NaCl/Chromium integration bot to | 245 // variable. This allows the NaCl/Chromium integration bot to |
| 249 // specify a newly-built IRT rather than using a prebuilt one | 246 // specify a newly-built IRT rather than using a prebuilt one |
| 250 // downloaded via Chromium's DEPS file. We use the same environment | 247 // downloaded via Chromium's DEPS file. We use the same environment |
| 251 // variable that the standalone NaCl PPAPI plugin accepts. | 248 // variable that the standalone NaCl PPAPI plugin accepts. |
| 252 const char* irt_path_var = getenv("NACL_IRT_LIBRARY"); | 249 const char* irt_path_var = getenv("NACL_IRT_LIBRARY"); |
| 253 if (irt_path_var != NULL) { | 250 if (irt_path_var != NULL) { |
| 254 FilePath::StringType string(irt_path_var, | 251 FilePath::StringType path_string( |
| 255 irt_path_var + strlen(irt_path_var)); | 252 irt_path_var, const_cast<const char*>(strchr(irt_path_var, '\0'))); |
| 256 irt_path = FilePath(string); | 253 irt_filepath = FilePath(path_string); |
| 257 } else { | 254 } else { |
| 258 FilePath plugin_dir; | 255 FilePath plugin_dir; |
| 259 if (!PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &plugin_dir)) { | 256 if (!PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &plugin_dir)) { |
| 260 LOG(ERROR) << "Failed to locate the plugins directory"; | 257 LOG(ERROR) << "Failed to locate the plugins directory"; |
| 261 delete this; | |
| 262 return; | 258 return; |
| 263 } | 259 } |
| 264 irt_path = plugin_dir.Append(GetIrtLibraryFilename()); | 260 |
| 261 bool on_x86_64 = RunningOnWOW64(); |
| 262 #if defined(__x86_64__) |
| 263 on_x86_64 = true; |
| 264 #endif |
| 265 FilePath::StringType irt_name; |
| 266 if (on_x86_64) { |
| 267 irt_name = FILE_PATH_LITERAL("nacl_irt_x86_64.nexe"); |
| 268 } else { |
| 269 irt_name = FILE_PATH_LITERAL("nacl_irt_x86_32.nexe"); |
| 270 } |
| 271 |
| 272 irt_filepath = plugin_dir.Append(irt_name); |
| 265 } | 273 } |
| 266 | 274 |
| 267 if (!base::FileUtilProxy::CreateOrOpen( | 275 base::PlatformFileError error_code; |
| 268 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 276 irt_platform_file_ = base::CreatePlatformFile(irt_filepath, |
| 269 irt_path, | 277 base::PLATFORM_FILE_OPEN | |
| 270 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, | 278 base::PLATFORM_FILE_READ, |
| 271 base::Bind(&NaClProcessHost::OpenIrtFileDone, | 279 NULL, |
| 272 weak_factory_.GetWeakPtr()))) { | 280 &error_code); |
| 273 delete this; | 281 if (error_code != base::PLATFORM_FILE_OK) { |
| 282 LOG(ERROR) << "Failed to open NaCl IRT file \"" |
| 283 << irt_filepath.LossyDisplayName() |
| 284 << "\": " << error_code; |
| 274 } | 285 } |
| 275 } | 286 } |
| 276 | 287 |
| 277 void NaClProcessHost::OpenIrtFileDone(base::PlatformFileError error_code, | 288 void NaClProcessHost::OnProcessLaunched() { |
| 278 base::PassPlatformFile file, | 289 SendStart(irt_platform_file_); |
| 279 bool created) { | 290 } |
| 291 |
| 292 static bool SendHandleToSelLdr( |
| 293 base::ProcessHandle processh, |
| 294 nacl::Handle sourceh, bool close_source, |
| 295 std::vector<nacl::FileDescriptor> *handles_for_sel_ldr) { |
| 296 #if defined(OS_WIN) |
| 297 HANDLE channel; |
| 298 int flags = DUPLICATE_SAME_ACCESS; |
| 299 if (close_source) |
| 300 flags |= DUPLICATE_CLOSE_SOURCE; |
| 301 if (!DuplicateHandle(GetCurrentProcess(), |
| 302 reinterpret_cast<HANDLE>(sourceh), |
| 303 processh, |
| 304 &channel, |
| 305 0, // Unused given DUPLICATE_SAME_ACCESS. |
| 306 FALSE, |
| 307 flags)) { |
| 308 LOG(ERROR) << "DuplicateHandle() failed"; |
| 309 return false; |
| 310 } |
| 311 handles_for_sel_ldr->push_back( |
| 312 reinterpret_cast<nacl::FileDescriptor>(channel)); |
| 313 #else |
| 314 nacl::FileDescriptor channel; |
| 315 channel.fd = sourceh; |
| 316 channel.auto_close = close_source; |
| 317 handles_for_sel_ldr->push_back(channel); |
| 318 #endif |
| 319 return true; |
| 320 } |
| 321 |
| 322 void NaClProcessHost::SendStart(base::PlatformFile irt_file) { |
| 280 std::vector<nacl::FileDescriptor> handles_for_renderer; | 323 std::vector<nacl::FileDescriptor> handles_for_renderer; |
| 281 base::ProcessHandle nacl_process_handle; | 324 base::ProcessHandle nacl_process_handle; |
| 282 bool have_irt_file = false; | |
| 283 if (base::PLATFORM_FILE_OK == error_code) { | |
| 284 internal_->sockets_for_sel_ldr.push_back(file.ReleaseValue()); | |
| 285 have_irt_file = true; | |
| 286 } else { | |
| 287 LOG(ERROR) << "Failed to open the NaCl IRT library file"; | |
| 288 } | |
| 289 | 325 |
| 290 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { | 326 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { |
| 291 #if defined(OS_WIN) | 327 #if defined(OS_WIN) |
| 292 // Copy the handle into the renderer process. | 328 // Copy the handle into the renderer process. |
| 293 HANDLE handle_in_renderer; | 329 HANDLE handle_in_renderer; |
| 294 if (!DuplicateHandle(base::GetCurrentProcessHandle(), | 330 if (!DuplicateHandle(base::GetCurrentProcessHandle(), |
| 295 reinterpret_cast<HANDLE>( | 331 reinterpret_cast<HANDLE>( |
| 296 internal_->sockets_for_renderer[i]), | 332 internal_->sockets_for_renderer[i]), |
| 297 chrome_render_message_filter_->peer_handle(), | 333 chrome_render_message_filter_->peer_handle(), |
| 298 &handle_in_renderer, | 334 &handle_in_renderer, |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 | 374 |
| 339 ChromeViewHostMsg_LaunchNaCl::WriteReplyParams( | 375 ChromeViewHostMsg_LaunchNaCl::WriteReplyParams( |
| 340 reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id); | 376 reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id); |
| 341 chrome_render_message_filter_->Send(reply_msg_); | 377 chrome_render_message_filter_->Send(reply_msg_); |
| 342 chrome_render_message_filter_ = NULL; | 378 chrome_render_message_filter_ = NULL; |
| 343 reply_msg_ = NULL; | 379 reply_msg_ = NULL; |
| 344 internal_->sockets_for_renderer.clear(); | 380 internal_->sockets_for_renderer.clear(); |
| 345 | 381 |
| 346 std::vector<nacl::FileDescriptor> handles_for_sel_ldr; | 382 std::vector<nacl::FileDescriptor> handles_for_sel_ldr; |
| 347 for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { | 383 for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { |
| 348 #if defined(OS_WIN) | 384 if (!SendHandleToSelLdr(handle(), |
| 349 HANDLE channel; | 385 internal_->sockets_for_sel_ldr[i], true, |
| 350 if (!DuplicateHandle(GetCurrentProcess(), | 386 &handles_for_sel_ldr)) { |
| 351 reinterpret_cast<HANDLE>( | |
| 352 internal_->sockets_for_sel_ldr[i]), | |
| 353 handle(), | |
| 354 &channel, | |
| 355 0, // Unused given DUPLICATE_SAME_ACCESS. | |
| 356 FALSE, | |
| 357 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { | |
| 358 LOG(ERROR) << "DuplicateHandle() failed"; | |
| 359 delete this; | 387 delete this; |
| 360 return; | 388 return; |
| 361 } | 389 } |
| 362 handles_for_sel_ldr.push_back( | 390 } |
| 363 reinterpret_cast<nacl::FileDescriptor>(channel)); | 391 |
| 364 #else | 392 // Send over the IRT file handle. We don't close our own copy! |
| 365 nacl::FileDescriptor channel; | 393 if (!SendHandleToSelLdr(handle(), irt_file, false, &handles_for_sel_ldr)) { |
| 366 channel.fd = internal_->sockets_for_sel_ldr[i]; | 394 delete this; |
| 367 channel.auto_close = true; | 395 return; |
| 368 handles_for_sel_ldr.push_back(channel); | |
| 369 #endif | |
| 370 } | 396 } |
| 371 | 397 |
| 372 #if defined(OS_MACOSX) | 398 #if defined(OS_MACOSX) |
| 373 // For dynamic loading support, NaCl requires a file descriptor that | 399 // For dynamic loading support, NaCl requires a file descriptor that |
| 374 // was created in /tmp, since those created with shm_open() are not | 400 // was created in /tmp, since those created with shm_open() are not |
| 375 // mappable with PROT_EXEC. Rather than requiring an extra IPC | 401 // mappable with PROT_EXEC. Rather than requiring an extra IPC |
| 376 // round trip out of the sandbox, we create an FD here. | 402 // round trip out of the sandbox, we create an FD here. |
| 377 base::SharedMemory memory_buffer; | 403 base::SharedMemory memory_buffer; |
| 378 if (!memory_buffer.CreateAnonymous(/* size= */ 1)) { | 404 if (!memory_buffer.CreateAnonymous(/* size= */ 1)) { |
| 379 LOG(ERROR) << "Failed to allocate memory buffer"; | 405 LOG(ERROR) << "Failed to allocate memory buffer"; |
| 380 delete this; | 406 delete this; |
| 381 return; | 407 return; |
| 382 } | 408 } |
| 383 nacl::FileDescriptor memory_fd; | 409 nacl::FileDescriptor memory_fd; |
| 384 memory_fd.fd = dup(memory_buffer.handle().fd); | 410 memory_fd.fd = dup(memory_buffer.handle().fd); |
| 385 if (memory_fd.fd < 0) { | 411 if (memory_fd.fd < 0) { |
| 386 LOG(ERROR) << "Failed to dup() a file descriptor"; | 412 LOG(ERROR) << "Failed to dup() a file descriptor"; |
| 387 delete this; | 413 delete this; |
| 388 return; | 414 return; |
| 389 } | 415 } |
| 390 memory_fd.auto_close = true; | 416 memory_fd.auto_close = true; |
| 391 handles_for_sel_ldr.push_back(memory_fd); | 417 handles_for_sel_ldr.push_back(memory_fd); |
| 392 #endif | 418 #endif |
| 393 | 419 |
| 394 Send(new NaClProcessMsg_Start(handles_for_sel_ldr, have_irt_file)); | 420 Send(new NaClProcessMsg_Start(handles_for_sel_ldr)); |
| 395 internal_->sockets_for_sel_ldr.clear(); | 421 internal_->sockets_for_sel_ldr.clear(); |
| 396 } | 422 } |
| 397 | 423 |
| 398 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { | 424 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { |
| 399 NOTREACHED() << "Invalid message with type = " << msg.type(); | 425 NOTREACHED() << "Invalid message with type = " << msg.type(); |
| 400 return false; | 426 return false; |
| 401 } | 427 } |
| 402 | 428 |
| 403 bool NaClProcessHost::CanShutdown() { | 429 bool NaClProcessHost::CanShutdown() { |
| 404 return true; | 430 return true; |
| 405 } | 431 } |
| OLD | NEW |