| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "content/browser/worker_host/worker_process_host.h" | |
| 6 | |
| 7 #include <set> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/base_switches.h" | |
| 12 #include "base/bind.h" | |
| 13 #include "base/bind_helpers.h" | |
| 14 #include "base/callback.h" | |
| 15 #include "base/command_line.h" | |
| 16 #include "base/message_loop/message_loop.h" | |
| 17 #include "base/strings/string_util.h" | |
| 18 #include "base/strings/utf_string_conversions.h" | |
| 19 #include "content/browser/appcache/appcache_dispatcher_host.h" | |
| 20 #include "content/browser/appcache/chrome_appcache_service.h" | |
| 21 #include "content/browser/browser_child_process_host_impl.h" | |
| 22 #include "content/browser/child_process_security_policy_impl.h" | |
| 23 #include "content/browser/devtools/worker_devtools_manager.h" | |
| 24 #include "content/browser/devtools/worker_devtools_message_filter.h" | |
| 25 #include "content/browser/fileapi/fileapi_message_filter.h" | |
| 26 #include "content/browser/frame_host/render_frame_host_delegate.h" | |
| 27 #include "content/browser/frame_host/render_frame_host_impl.h" | |
| 28 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" | |
| 29 #include "content/browser/loader/resource_message_filter.h" | |
| 30 #include "content/browser/message_port_message_filter.h" | |
| 31 #include "content/browser/message_port_service.h" | |
| 32 #include "content/browser/mime_registry_message_filter.h" | |
| 33 #include "content/browser/quota_dispatcher_host.h" | |
| 34 #include "content/browser/renderer_host/database_message_filter.h" | |
| 35 #include "content/browser/renderer_host/file_utilities_message_filter.h" | |
| 36 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
| 37 #include "content/browser/renderer_host/render_view_host_impl.h" | |
| 38 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" | |
| 39 #include "content/browser/renderer_host/websocket_dispatcher_host.h" | |
| 40 #include "content/browser/resource_context_impl.h" | |
| 41 #include "content/browser/worker_host/worker_message_filter.h" | |
| 42 #include "content/browser/worker_host/worker_service_impl.h" | |
| 43 #include "content/common/child_process_host_impl.h" | |
| 44 #include "content/common/view_messages.h" | |
| 45 #include "content/common/worker_messages.h" | |
| 46 #include "content/public/browser/browser_thread.h" | |
| 47 #include "content/public/browser/content_browser_client.h" | |
| 48 #include "content/public/browser/user_metrics.h" | |
| 49 #include "content/public/common/content_switches.h" | |
| 50 #include "content/public/common/resource_type.h" | |
| 51 #include "content/public/common/result_codes.h" | |
| 52 #include "content/public/common/sandboxed_process_launcher_delegate.h" | |
| 53 #include "ipc/ipc_switches.h" | |
| 54 #include "net/base/mime_util.h" | |
| 55 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
| 56 #include "net/url_request/url_request_context_getter.h" | |
| 57 #include "ui/base/ui_base_switches.h" | |
| 58 #include "webkit/browser/fileapi/file_system_context.h" | |
| 59 #include "webkit/browser/fileapi/sandbox_file_system_backend.h" | |
| 60 | |
| 61 #if defined(OS_WIN) | |
| 62 #include "content/common/sandbox_win.h" | |
| 63 #endif | |
| 64 | |
| 65 namespace content { | |
| 66 namespace { | |
| 67 | |
| 68 // NOTE: changes to this class need to be reviewed by the security team. | |
| 69 class WorkerSandboxedProcessLauncherDelegate | |
| 70 : public content::SandboxedProcessLauncherDelegate { | |
| 71 public: | |
| 72 WorkerSandboxedProcessLauncherDelegate(ChildProcessHost* host, | |
| 73 bool debugging_child) | |
| 74 #if defined(OS_POSIX) | |
| 75 : ipc_fd_(host->TakeClientFileDescriptor()), | |
| 76 debugging_child_(debugging_child) | |
| 77 #endif // OS_POSIX | |
| 78 {} | |
| 79 | |
| 80 virtual ~WorkerSandboxedProcessLauncherDelegate() {} | |
| 81 | |
| 82 #if defined(OS_WIN) | |
| 83 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy, | |
| 84 bool* success) { | |
| 85 AddBaseHandleClosePolicy(policy); | |
| 86 } | |
| 87 #elif defined(OS_POSIX) | |
| 88 virtual bool ShouldUseZygote() OVERRIDE { | |
| 89 return !debugging_child_; | |
| 90 } | |
| 91 virtual int GetIpcFd() OVERRIDE { | |
| 92 return ipc_fd_; | |
| 93 } | |
| 94 #endif // OS_WIN | |
| 95 | |
| 96 private: | |
| 97 #if defined(OS_POSIX) | |
| 98 int ipc_fd_; | |
| 99 bool debugging_child_; | |
| 100 #endif // OS_POSIX | |
| 101 }; | |
| 102 | |
| 103 // Notifies RenderViewHost that one or more worker objects crashed. | |
| 104 void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) { | |
| 105 RenderFrameHostImpl* host = | |
| 106 RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id); | |
| 107 if (host) | |
| 108 host->delegate()->WorkerCrashed(host); | |
| 109 } | |
| 110 | |
| 111 void WorkerCreatedCallback(int render_process_id, | |
| 112 int render_frame_id, | |
| 113 int worker_process_id) { | |
| 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 115 RenderFrameHost* render_frame_host = | |
| 116 RenderFrameHost::FromID(render_process_id, render_frame_id); | |
| 117 if (!render_frame_host) | |
| 118 return; | |
| 119 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
| 120 GetContentClient()->browser()->WorkerProcessCreated(site_instance, | |
| 121 worker_process_id); | |
| 122 } | |
| 123 | |
| 124 void WorkerTerminatedCallback(int render_process_id, | |
| 125 int render_frame_id, | |
| 126 int worker_process_id) { | |
| 127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 128 RenderFrameHost* render_frame_host = | |
| 129 RenderFrameHost::FromID(render_process_id, render_frame_id); | |
| 130 if (!render_frame_host) | |
| 131 return; | |
| 132 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
| 133 GetContentClient()->browser()->WorkerProcessTerminated(site_instance, | |
| 134 worker_process_id); | |
| 135 } | |
| 136 | |
| 137 } // namespace | |
| 138 | |
| 139 WorkerProcessHost::WorkerProcessHost( | |
| 140 ResourceContext* resource_context, | |
| 141 const WorkerStoragePartition& partition) | |
| 142 : resource_context_(resource_context), | |
| 143 partition_(partition), | |
| 144 process_launched_(false), | |
| 145 weak_factory_(this) { | |
| 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 147 DCHECK(resource_context_); | |
| 148 process_.reset( | |
| 149 new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this)); | |
| 150 } | |
| 151 | |
| 152 WorkerProcessHost::~WorkerProcessHost() { | |
| 153 // If we crashed, tell the RenderViewHosts. | |
| 154 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 155 if (!i->load_failed()) { | |
| 156 const WorkerDocumentSet::DocumentInfoSet& parents = | |
| 157 i->worker_document_set()->documents(); | |
| 158 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = | |
| 159 parents.begin(); parent_iter != parents.end(); ++parent_iter) { | |
| 160 BrowserThread::PostTask( | |
| 161 BrowserThread::UI, FROM_HERE, | |
| 162 base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(), | |
| 163 parent_iter->render_frame_id())); | |
| 164 } | |
| 165 } | |
| 166 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( | |
| 167 this, i->worker_route_id()); | |
| 168 } | |
| 169 | |
| 170 ChildProcessSecurityPolicyImpl::GetInstance()->Remove( | |
| 171 process_->GetData().id); | |
| 172 } | |
| 173 | |
| 174 bool WorkerProcessHost::Send(IPC::Message* message) { | |
| 175 return process_->Send(message); | |
| 176 } | |
| 177 | |
| 178 bool WorkerProcessHost::Init(int render_process_id, int render_frame_id) { | |
| 179 std::string channel_id = process_->GetHost()->CreateChannel(); | |
| 180 if (channel_id.empty()) | |
| 181 return false; | |
| 182 | |
| 183 #if defined(OS_LINUX) | |
| 184 int flags = ChildProcessHost::CHILD_ALLOW_SELF; | |
| 185 #else | |
| 186 int flags = ChildProcessHost::CHILD_NORMAL; | |
| 187 #endif | |
| 188 | |
| 189 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); | |
| 190 if (exe_path.empty()) | |
| 191 return false; | |
| 192 | |
| 193 CommandLine* cmd_line = new CommandLine(exe_path); | |
| 194 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess); | |
| 195 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | |
| 196 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); | |
| 197 cmd_line->AppendSwitchASCII(switches::kLang, locale); | |
| 198 | |
| 199 static const char* const kSwitchNames[] = { | |
| 200 switches::kDisableApplicationCache, | |
| 201 switches::kDisableDatabases, | |
| 202 #if defined(OS_WIN) | |
| 203 switches::kDisableDesktopNotifications, | |
| 204 #endif | |
| 205 switches::kDisableFileSystem, | |
| 206 switches::kDisableSeccompFilterSandbox, | |
| 207 switches::kEnableExperimentalWebPlatformFeatures, | |
| 208 switches::kEnablePreciseMemoryInfo, | |
| 209 #if defined(OS_MACOSX) | |
| 210 switches::kEnableSandboxLogging, | |
| 211 #endif | |
| 212 switches::kJavaScriptFlags, | |
| 213 switches::kNoSandbox | |
| 214 }; | |
| 215 cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames, | |
| 216 arraysize(kSwitchNames)); | |
| 217 | |
| 218 bool debugging_child = false; | |
| 219 #if defined(OS_POSIX) | |
| 220 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 221 switches::kWaitForDebuggerChildren)) { | |
| 222 // Look to pass-on the kWaitForDebugger flag. | |
| 223 std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 224 switches::kWaitForDebuggerChildren); | |
| 225 if (value.empty() || value == switches::kWorkerProcess) { | |
| 226 cmd_line->AppendSwitch(switches::kWaitForDebugger); | |
| 227 debugging_child = true; | |
| 228 } | |
| 229 } | |
| 230 #endif | |
| 231 | |
| 232 process_->Launch( | |
| 233 new WorkerSandboxedProcessLauncherDelegate(process_->GetHost(), | |
| 234 debugging_child), | |
| 235 cmd_line); | |
| 236 | |
| 237 ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker( | |
| 238 process_->GetData().id, render_process_id); | |
| 239 CreateMessageFilters(render_process_id); | |
| 240 | |
| 241 BrowserThread::PostTask( | |
| 242 BrowserThread::UI, FROM_HERE, | |
| 243 base::Bind(&WorkerCreatedCallback, | |
| 244 render_process_id, | |
| 245 render_frame_id, | |
| 246 process_->GetData().id)); | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 void WorkerProcessHost::CreateMessageFilters(int render_process_id) { | |
| 251 ChromeBlobStorageContext* blob_storage_context = | |
| 252 GetChromeBlobStorageContextForResourceContext(resource_context_); | |
| 253 StreamContext* stream_context = | |
| 254 GetStreamContextForResourceContext(resource_context_); | |
| 255 | |
| 256 net::URLRequestContextGetter* url_request_context = | |
| 257 partition_.url_request_context(); | |
| 258 | |
| 259 ResourceMessageFilter::GetContextsCallback get_contexts_callback( | |
| 260 base::Bind(&WorkerProcessHost::GetContexts, | |
| 261 base::Unretained(this))); | |
| 262 | |
| 263 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( | |
| 264 process_->GetData().id, PROCESS_TYPE_WORKER, | |
| 265 partition_.appcache_service(), | |
| 266 blob_storage_context, | |
| 267 partition_.filesystem_context(), | |
| 268 partition_.service_worker_context(), | |
| 269 get_contexts_callback); | |
| 270 process_->AddFilter(resource_message_filter); | |
| 271 | |
| 272 MessagePortMessageFilter* message_port_message_filter = | |
| 273 new MessagePortMessageFilter( | |
| 274 base::Bind(&WorkerServiceImpl::next_worker_route_id, | |
| 275 base::Unretained(WorkerServiceImpl::GetInstance()))); | |
| 276 process_->AddFilter(message_port_message_filter); | |
| 277 worker_message_filter_ = new WorkerMessageFilter(render_process_id, | |
| 278 resource_context_, | |
| 279 partition_, | |
| 280 message_port_message_filter); | |
| 281 process_->AddFilter(worker_message_filter_.get()); | |
| 282 process_->AddFilter(new AppCacheDispatcherHost( | |
| 283 partition_.appcache_service(), process_->GetData().id)); | |
| 284 process_->AddFilter(new FileAPIMessageFilter( | |
| 285 process_->GetData().id, | |
| 286 url_request_context, | |
| 287 partition_.filesystem_context(), | |
| 288 blob_storage_context, | |
| 289 stream_context)); | |
| 290 process_->AddFilter(new FileUtilitiesMessageFilter( | |
| 291 process_->GetData().id)); | |
| 292 process_->AddFilter(new MimeRegistryMessageFilter()); | |
| 293 process_->AddFilter(new DatabaseMessageFilter(partition_.database_tracker())); | |
| 294 process_->AddFilter(new QuotaDispatcherHost( | |
| 295 process_->GetData().id, | |
| 296 partition_.quota_manager(), | |
| 297 GetContentClient()->browser()->CreateQuotaPermissionContext())); | |
| 298 | |
| 299 SocketStreamDispatcherHost::GetRequestContextCallback | |
| 300 request_context_callback( | |
| 301 base::Bind(&WorkerProcessHost::GetRequestContext, | |
| 302 base::Unretained(this))); | |
| 303 | |
| 304 SocketStreamDispatcherHost* socket_stream_dispatcher_host = | |
| 305 new SocketStreamDispatcherHost( | |
| 306 render_process_id, | |
| 307 request_context_callback, | |
| 308 resource_context_); | |
| 309 socket_stream_dispatcher_host_ = socket_stream_dispatcher_host; | |
| 310 process_->AddFilter(socket_stream_dispatcher_host); | |
| 311 | |
| 312 WebSocketDispatcherHost::GetRequestContextCallback | |
| 313 websocket_request_context_callback( | |
| 314 base::Bind(&WorkerProcessHost::GetRequestContext, | |
| 315 base::Unretained(this), | |
| 316 ResourceType::SUB_RESOURCE)); | |
| 317 | |
| 318 process_->AddFilter(new WebSocketDispatcherHost( | |
| 319 render_process_id, websocket_request_context_callback)); | |
| 320 | |
| 321 process_->AddFilter(new WorkerDevToolsMessageFilter(process_->GetData().id)); | |
| 322 process_->AddFilter( | |
| 323 new IndexedDBDispatcherHost(process_->GetData().id, | |
| 324 url_request_context, | |
| 325 partition_.indexed_db_context(), | |
| 326 blob_storage_context)); | |
| 327 } | |
| 328 | |
| 329 void WorkerProcessHost::CreateWorker(const WorkerInstance& instance, | |
| 330 bool pause_on_start) { | |
| 331 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( | |
| 332 process_->GetData().id, instance.url()); | |
| 333 | |
| 334 instances_.push_back(instance); | |
| 335 | |
| 336 WorkerProcessMsg_CreateWorker_Params params; | |
| 337 params.url = instance.url(); | |
| 338 params.name = instance.name(); | |
| 339 params.content_security_policy = instance.content_security_policy(); | |
| 340 params.security_policy_type = instance.security_policy_type(); | |
| 341 params.pause_on_start = pause_on_start; | |
| 342 params.route_id = instance.worker_route_id(); | |
| 343 Send(new WorkerProcessMsg_CreateWorker(params)); | |
| 344 | |
| 345 UpdateTitle(); | |
| 346 | |
| 347 // Walk all pending filters and let them know the worker has been created | |
| 348 // (could be more than one in the case where we had to queue up worker | |
| 349 // creation because the worker process limit was reached). | |
| 350 for (WorkerInstance::FilterList::const_iterator i = | |
| 351 instance.filters().begin(); | |
| 352 i != instance.filters().end(); ++i) { | |
| 353 i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id())); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 bool WorkerProcessHost::FilterMessage(const IPC::Message& message, | |
| 358 WorkerMessageFilter* filter) { | |
| 359 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 360 if (!i->closed() && i->HasFilter(filter, message.routing_id())) { | |
| 361 RelayMessage(message, filter, &(*i)); | |
| 362 return true; | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 void WorkerProcessHost::OnProcessLaunched() { | |
| 370 process_launched_ = true; | |
| 371 | |
| 372 WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated(); | |
| 373 } | |
| 374 | |
| 375 bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) { | |
| 376 bool handled = true; | |
| 377 IPC_BEGIN_MESSAGE_MAP(WorkerProcessHost, message) | |
| 378 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed, | |
| 379 OnWorkerContextClosed) | |
| 380 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed, | |
| 381 OnWorkerContextDestroyed) | |
| 382 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded, | |
| 383 OnWorkerScriptLoaded) | |
| 384 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed, | |
| 385 OnWorkerScriptLoadFailed) | |
| 386 IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected, | |
| 387 OnWorkerConnected) | |
| 388 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase) | |
| 389 IPC_MESSAGE_HANDLER_DELAY_REPLY( | |
| 390 WorkerProcessHostMsg_RequestFileSystemAccessSync, | |
| 391 OnRequestFileSystemAccess) | |
| 392 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB) | |
| 393 IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker, | |
| 394 OnForceKillWorkerProcess) | |
| 395 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 396 IPC_END_MESSAGE_MAP() | |
| 397 | |
| 398 return handled; | |
| 399 } | |
| 400 | |
| 401 // Sent to notify the browser process when a worker context invokes close(), so | |
| 402 // no new connections are sent to shared workers. | |
| 403 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) { | |
| 404 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 405 if (i->worker_route_id() == worker_route_id) { | |
| 406 // Set the closed flag - this will stop any further messages from | |
| 407 // being sent to the worker (messages can still be sent from the worker, | |
| 408 // for exception reporting, etc). | |
| 409 i->set_closed(true); | |
| 410 break; | |
| 411 } | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 void WorkerProcessHost::OnWorkerContextDestroyed(int worker_route_id) { | |
| 416 WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed( | |
| 417 this, worker_route_id); | |
| 418 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 419 if (i->worker_route_id() == worker_route_id) { | |
| 420 instances_.erase(i); | |
| 421 UpdateTitle(); | |
| 422 return; | |
| 423 } | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 void WorkerProcessHost::OnWorkerScriptLoaded(int worker_route_id) { | |
| 428 WorkerDevToolsManager::GetInstance()->WorkerContextStarted(this, | |
| 429 worker_route_id); | |
| 430 } | |
| 431 | |
| 432 void WorkerProcessHost::OnWorkerScriptLoadFailed(int worker_route_id) { | |
| 433 bool shutdown = true; | |
| 434 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 435 if (i->worker_route_id() != worker_route_id) { | |
| 436 shutdown = false; | |
| 437 continue; | |
| 438 } | |
| 439 i->set_load_failed(true); | |
| 440 for (WorkerInstance::FilterList::const_iterator j = i->filters().begin(); | |
| 441 j != i->filters().end(); ++j) { | |
| 442 j->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(j->route_id())); | |
| 443 } | |
| 444 } | |
| 445 if (shutdown) { | |
| 446 base::KillProcess( | |
| 447 process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false); | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 void WorkerProcessHost::OnWorkerConnected(int message_port_id, | |
| 452 int worker_route_id) { | |
| 453 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 454 if (i->worker_route_id() != worker_route_id) | |
| 455 continue; | |
| 456 for (WorkerInstance::FilterList::const_iterator j = i->filters().begin(); | |
| 457 j != i->filters().end(); ++j) { | |
| 458 if (j->message_port_id() != message_port_id) | |
| 459 continue; | |
| 460 j->filter()->Send(new ViewMsg_WorkerConnected(j->route_id())); | |
| 461 return; | |
| 462 } | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 void WorkerProcessHost::OnAllowDatabase(int worker_route_id, | |
| 467 const GURL& url, | |
| 468 const base::string16& name, | |
| 469 const base::string16& display_name, | |
| 470 unsigned long estimated_size, | |
| 471 bool* result) { | |
| 472 *result = GetContentClient()->browser()->AllowWorkerDatabase( | |
| 473 url, name, display_name, estimated_size, resource_context_, | |
| 474 GetRenderFrameIDsForWorker(worker_route_id)); | |
| 475 } | |
| 476 | |
| 477 void WorkerProcessHost::OnRequestFileSystemAccess(int worker_route_id, | |
| 478 const GURL& url, | |
| 479 IPC::Message* reply_msg) { | |
| 480 GetContentClient()->browser()->AllowWorkerFileSystem( | |
| 481 url, | |
| 482 resource_context_, | |
| 483 GetRenderFrameIDsForWorker(worker_route_id), | |
| 484 base::Bind(&WorkerProcessHost::OnRequestFileSystemAccessResponse, | |
| 485 weak_factory_.GetWeakPtr(), | |
| 486 base::Passed(scoped_ptr<IPC::Message>(reply_msg)))); | |
| 487 } | |
| 488 | |
| 489 void WorkerProcessHost::OnRequestFileSystemAccessResponse( | |
| 490 scoped_ptr<IPC::Message> reply_msg, | |
| 491 bool allowed) { | |
| 492 WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams( | |
| 493 reply_msg.get(), | |
| 494 allowed); | |
| 495 Send(reply_msg.release()); | |
| 496 } | |
| 497 | |
| 498 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id, | |
| 499 const GURL& url, | |
| 500 const base::string16& name, | |
| 501 bool* result) { | |
| 502 *result = GetContentClient()->browser()->AllowWorkerIndexedDB( | |
| 503 url, name, resource_context_, | |
| 504 GetRenderFrameIDsForWorker(worker_route_id)); | |
| 505 } | |
| 506 | |
| 507 void WorkerProcessHost::OnForceKillWorkerProcess() { | |
| 508 if (process_ && process_launched_) | |
| 509 base::KillProcess( | |
| 510 process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false); | |
| 511 else | |
| 512 RecordAction(base::UserMetricsAction("WorkerProcess_BadProcessToKill")); | |
| 513 } | |
| 514 | |
| 515 void WorkerProcessHost::RelayMessage( | |
| 516 const IPC::Message& message, | |
| 517 WorkerMessageFilter* incoming_filter, | |
| 518 WorkerInstance* instance) { | |
| 519 if (message.type() == WorkerMsg_Connect::ID) { | |
| 520 // Crack the SharedWorker Connect message to setup routing for the port. | |
| 521 WorkerMsg_Connect::Param params; | |
| 522 if (!WorkerMsg_Connect::Read(&message, ¶ms)) | |
| 523 return; | |
| 524 | |
| 525 int sent_message_port_id = params.a; | |
| 526 int new_routing_id = params.b; | |
| 527 new_routing_id = worker_message_filter_->GetNextRoutingID(); | |
| 528 MessagePortService::GetInstance()->UpdateMessagePort( | |
| 529 sent_message_port_id, | |
| 530 worker_message_filter_->message_port_message_filter(), | |
| 531 new_routing_id); | |
| 532 | |
| 533 instance->SetMessagePortID(incoming_filter, | |
| 534 message.routing_id(), | |
| 535 sent_message_port_id); | |
| 536 // Resend the message with the new routing id. | |
| 537 worker_message_filter_->Send(new WorkerMsg_Connect( | |
| 538 instance->worker_route_id(), sent_message_port_id, new_routing_id)); | |
| 539 | |
| 540 // Send any queued messages for the sent port. | |
| 541 MessagePortService::GetInstance()->SendQueuedMessagesIfPossible( | |
| 542 sent_message_port_id); | |
| 543 } else { | |
| 544 IPC::Message* new_message = new IPC::Message(message); | |
| 545 new_message->set_routing_id(instance->worker_route_id()); | |
| 546 worker_message_filter_->Send(new_message); | |
| 547 return; | |
| 548 } | |
| 549 } | |
| 550 | |
| 551 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() { | |
| 552 if (!instances_.size() && socket_stream_dispatcher_host_.get()) { | |
| 553 // We can assume that this object is going to delete, because | |
| 554 // currently a WorkerInstance will never be added to a WorkerProcessHost | |
| 555 // once it is initialized. | |
| 556 | |
| 557 // SocketStreamDispatcherHost should be notified now that the worker | |
| 558 // process will shutdown soon. | |
| 559 socket_stream_dispatcher_host_->Shutdown(); | |
| 560 socket_stream_dispatcher_host_ = NULL; | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) { | |
| 565 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { | |
| 566 bool shutdown = false; | |
| 567 i->RemoveFilters(filter); | |
| 568 | |
| 569 int render_frame_id = 0; | |
| 570 const WorkerDocumentSet::DocumentInfoSet& documents = | |
| 571 i->worker_document_set()->documents(); | |
| 572 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
| 573 documents.begin(); doc != documents.end(); ++doc) { | |
| 574 if (doc->filter() == filter) { | |
| 575 render_frame_id = doc->render_frame_id(); | |
| 576 break; | |
| 577 } | |
| 578 } | |
| 579 i->worker_document_set()->RemoveAll(filter); | |
| 580 if (i->worker_document_set()->IsEmpty()) { | |
| 581 shutdown = true; | |
| 582 } | |
| 583 if (shutdown) { | |
| 584 BrowserThread::PostTask( | |
| 585 BrowserThread::UI, FROM_HERE, | |
| 586 base::Bind(&WorkerTerminatedCallback, | |
| 587 filter->render_process_id(), | |
| 588 render_frame_id, | |
| 589 process_->GetData().id)); | |
| 590 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); | |
| 591 i = instances_.erase(i); | |
| 592 } else { | |
| 593 ++i; | |
| 594 } | |
| 595 } | |
| 596 ShutdownSocketStreamDispatcherHostIfNecessary(); | |
| 597 } | |
| 598 | |
| 599 bool WorkerProcessHost::CanShutdown() { | |
| 600 return instances_.empty(); | |
| 601 } | |
| 602 | |
| 603 void WorkerProcessHost::UpdateTitle() { | |
| 604 std::set<std::string> titles; | |
| 605 for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { | |
| 606 // Allow the embedder first crack at special casing the title. | |
| 607 std::string title = GetContentClient()->browser()-> | |
| 608 GetWorkerProcessTitle(i->url(), resource_context_); | |
| 609 | |
| 610 if (title.empty()) { | |
| 611 title = net::registry_controlled_domains::GetDomainAndRegistry( | |
| 612 i->url(), | |
| 613 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | |
| 614 } | |
| 615 | |
| 616 // Use the host name if the domain is empty, i.e. localhost or IP address. | |
| 617 if (title.empty()) | |
| 618 title = i->url().host(); | |
| 619 | |
| 620 // If the host name is empty, i.e. file url, use the path. | |
| 621 if (title.empty()) | |
| 622 title = i->url().path(); | |
| 623 titles.insert(title); | |
| 624 } | |
| 625 | |
| 626 std::string display_title; | |
| 627 for (std::set<std::string>::iterator i = titles.begin(); | |
| 628 i != titles.end(); ++i) { | |
| 629 if (!display_title.empty()) | |
| 630 display_title += ", "; | |
| 631 display_title += *i; | |
| 632 } | |
| 633 | |
| 634 process_->SetName(base::UTF8ToUTF16(display_title)); | |
| 635 } | |
| 636 | |
| 637 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter, | |
| 638 unsigned long long document_id) { | |
| 639 // Walk all instances and remove the document from their document set. | |
| 640 for (Instances::iterator i = instances_.begin(); i != instances_.end();) { | |
| 641 int render_frame_id = 0; | |
| 642 const WorkerDocumentSet::DocumentInfoSet& documents = | |
| 643 i->worker_document_set()->documents(); | |
| 644 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
| 645 documents.begin(); doc != documents.end(); ++doc) { | |
| 646 if (doc->filter() == filter && doc->document_id() == document_id) { | |
| 647 render_frame_id = doc->render_frame_id(); | |
| 648 break; | |
| 649 } | |
| 650 } | |
| 651 i->worker_document_set()->Remove(filter, document_id); | |
| 652 if (i->worker_document_set()->IsEmpty()) { | |
| 653 BrowserThread::PostTask( | |
| 654 BrowserThread::UI, FROM_HERE, | |
| 655 base::Bind(&WorkerTerminatedCallback, | |
| 656 filter->render_process_id(), | |
| 657 render_frame_id, | |
| 658 process_->GetData().id)); | |
| 659 // This worker has no more associated documents - shut it down. | |
| 660 Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); | |
| 661 i = instances_.erase(i); | |
| 662 } else { | |
| 663 ++i; | |
| 664 } | |
| 665 } | |
| 666 ShutdownSocketStreamDispatcherHostIfNecessary(); | |
| 667 } | |
| 668 | |
| 669 void WorkerProcessHost::TerminateWorker(int worker_route_id) { | |
| 670 Send(new WorkerMsg_TerminateWorkerContext(worker_route_id)); | |
| 671 } | |
| 672 | |
| 673 void WorkerProcessHost::SetBackgrounded(bool backgrounded) { | |
| 674 process_->SetBackgrounded(backgrounded); | |
| 675 } | |
| 676 | |
| 677 const ChildProcessData& WorkerProcessHost::GetData() { | |
| 678 return process_->GetData(); | |
| 679 } | |
| 680 | |
| 681 std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker( | |
| 682 int worker_route_id) { | |
| 683 std::vector<std::pair<int, int> > result; | |
| 684 WorkerProcessHost::Instances::const_iterator i; | |
| 685 for (i = instances_.begin(); i != instances_.end(); ++i) { | |
| 686 if (i->worker_route_id() != worker_route_id) | |
| 687 continue; | |
| 688 const WorkerDocumentSet::DocumentInfoSet& documents = | |
| 689 i->worker_document_set()->documents(); | |
| 690 for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = | |
| 691 documents.begin(); doc != documents.end(); ++doc) { | |
| 692 result.push_back( | |
| 693 std::make_pair(doc->render_process_id(), doc->render_frame_id())); | |
| 694 } | |
| 695 break; | |
| 696 } | |
| 697 return result; | |
| 698 } | |
| 699 | |
| 700 void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request& request, | |
| 701 ResourceContext** resource_context, | |
| 702 net::URLRequestContext** request_context) { | |
| 703 *resource_context = resource_context_; | |
| 704 *request_context = partition_.url_request_context()->GetURLRequestContext(); | |
| 705 } | |
| 706 | |
| 707 net::URLRequestContext* WorkerProcessHost::GetRequestContext( | |
| 708 ResourceType::Type resource_type) { | |
| 709 return partition_.url_request_context()->GetURLRequestContext(); | |
| 710 } | |
| 711 | |
| 712 WorkerProcessHost::WorkerInstance::WorkerInstance( | |
| 713 const GURL& url, | |
| 714 const base::string16& name, | |
| 715 const base::string16& content_security_policy, | |
| 716 blink::WebContentSecurityPolicyType security_policy_type, | |
| 717 int worker_route_id, | |
| 718 int render_frame_id, | |
| 719 ResourceContext* resource_context, | |
| 720 const WorkerStoragePartition& partition) | |
| 721 : url_(url), | |
| 722 closed_(false), | |
| 723 name_(name), | |
| 724 content_security_policy_(content_security_policy), | |
| 725 security_policy_type_(security_policy_type), | |
| 726 worker_route_id_(worker_route_id), | |
| 727 render_frame_id_(render_frame_id), | |
| 728 worker_document_set_(new WorkerDocumentSet()), | |
| 729 resource_context_(resource_context), | |
| 730 partition_(partition), | |
| 731 load_failed_(false) { | |
| 732 DCHECK(resource_context_); | |
| 733 } | |
| 734 | |
| 735 WorkerProcessHost::WorkerInstance::~WorkerInstance() { | |
| 736 } | |
| 737 | |
| 738 void WorkerProcessHost::WorkerInstance::SetMessagePortID( | |
| 739 WorkerMessageFilter* filter, | |
| 740 int route_id, | |
| 741 int message_port_id) { | |
| 742 for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) { | |
| 743 if (i->filter() == filter && i->route_id() == route_id) { | |
| 744 i->set_message_port_id(message_port_id); | |
| 745 return; | |
| 746 } | |
| 747 } | |
| 748 } | |
| 749 | |
| 750 // Compares an instance based on the algorithm in the WebWorkers spec - an | |
| 751 // instance matches if the origins of the URLs match, and: | |
| 752 // a) the names are non-empty and equal | |
| 753 // -or- | |
| 754 // b) the names are both empty, and the urls are equal | |
| 755 bool WorkerProcessHost::WorkerInstance::Matches( | |
| 756 const GURL& match_url, | |
| 757 const base::string16& match_name, | |
| 758 const WorkerStoragePartition& partition, | |
| 759 ResourceContext* resource_context) const { | |
| 760 // Only match open shared workers. | |
| 761 if (closed_) | |
| 762 return false; | |
| 763 | |
| 764 // ResourceContext equivalence is being used as a proxy to ensure we only | |
| 765 // matched shared workers within the same BrowserContext. | |
| 766 if (resource_context_ != resource_context) | |
| 767 return false; | |
| 768 | |
| 769 // We must be in the same storage partition otherwise sharing will violate | |
| 770 // isolation. | |
| 771 if (!partition_.Equals(partition)) | |
| 772 return false; | |
| 773 | |
| 774 if (url_.GetOrigin() != match_url.GetOrigin()) | |
| 775 return false; | |
| 776 | |
| 777 if (name_.empty() && match_name.empty()) | |
| 778 return url_ == match_url; | |
| 779 | |
| 780 return name_ == match_name; | |
| 781 } | |
| 782 | |
| 783 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter, | |
| 784 int route_id) { | |
| 785 CHECK(filter); | |
| 786 if (!HasFilter(filter, route_id)) { | |
| 787 FilterInfo info(filter, route_id); | |
| 788 filters_.push_back(info); | |
| 789 } | |
| 790 } | |
| 791 | |
| 792 void WorkerProcessHost::WorkerInstance::RemoveFilter( | |
| 793 WorkerMessageFilter* filter, int route_id) { | |
| 794 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { | |
| 795 if (i->filter() == filter && i->route_id() == route_id) | |
| 796 i = filters_.erase(i); | |
| 797 else | |
| 798 ++i; | |
| 799 } | |
| 800 // Should not be duplicate copies in the filter set. | |
| 801 DCHECK(!HasFilter(filter, route_id)); | |
| 802 } | |
| 803 | |
| 804 void WorkerProcessHost::WorkerInstance::RemoveFilters( | |
| 805 WorkerMessageFilter* filter) { | |
| 806 for (FilterList::iterator i = filters_.begin(); i != filters_.end();) { | |
| 807 if (i->filter() == filter) | |
| 808 i = filters_.erase(i); | |
| 809 else | |
| 810 ++i; | |
| 811 } | |
| 812 } | |
| 813 | |
| 814 bool WorkerProcessHost::WorkerInstance::HasFilter( | |
| 815 WorkerMessageFilter* filter, int route_id) const { | |
| 816 for (FilterList::const_iterator i = filters_.begin(); i != filters_.end(); | |
| 817 ++i) { | |
| 818 if (i->filter() == filter && i->route_id() == route_id) | |
| 819 return true; | |
| 820 } | |
| 821 return false; | |
| 822 } | |
| 823 | |
| 824 bool WorkerProcessHost::WorkerInstance::FrameIsParent( | |
| 825 int render_process_id, int render_frame_id) const { | |
| 826 const WorkerDocumentSet::DocumentInfoSet& parents = | |
| 827 worker_document_set()->documents(); | |
| 828 for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = | |
| 829 parents.begin(); | |
| 830 parent_iter != parents.end(); ++parent_iter) { | |
| 831 if (parent_iter->render_process_id() == render_process_id && | |
| 832 parent_iter->render_frame_id() == render_frame_id) { | |
| 833 return true; | |
| 834 } | |
| 835 } | |
| 836 return false; | |
| 837 } | |
| 838 | |
| 839 WorkerProcessHost::WorkerInstance::FilterInfo | |
| 840 WorkerProcessHost::WorkerInstance::GetFilter() const { | |
| 841 DCHECK(NumFilters() == 1); | |
| 842 return *filters_.begin(); | |
| 843 } | |
| 844 | |
| 845 } // namespace content | |
| OLD | NEW |