Home | History | Annotate | Download | only in worker_host
      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/indexed_db/indexed_db_dispatcher_host.h"
     27 #include "content/browser/mime_registry_message_filter.h"
     28 #include "content/browser/quota_dispatcher_host.h"
     29 #include "content/browser/renderer_host/database_message_filter.h"
     30 #include "content/browser/renderer_host/file_utilities_message_filter.h"
     31 #include "content/browser/renderer_host/render_view_host_delegate.h"
     32 #include "content/browser/renderer_host/render_view_host_impl.h"
     33 #include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
     34 #include "content/browser/resource_context_impl.h"
     35 #include "content/browser/worker_host/message_port_service.h"
     36 #include "content/browser/worker_host/worker_message_filter.h"
     37 #include "content/browser/worker_host/worker_service_impl.h"
     38 #include "content/common/child_process_host_impl.h"
     39 #include "content/common/view_messages.h"
     40 #include "content/common/worker_messages.h"
     41 #include "content/public/browser/browser_thread.h"
     42 #include "content/public/browser/content_browser_client.h"
     43 #include "content/public/browser/user_metrics.h"
     44 #include "content/public/common/content_switches.h"
     45 #include "content/public/common/result_codes.h"
     46 #include "ipc/ipc_switches.h"
     47 #include "net/base/mime_util.h"
     48 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     49 #include "net/url_request/url_request_context_getter.h"
     50 #include "ui/base/ui_base_switches.h"
     51 #include "webkit/browser/fileapi/file_system_context.h"
     52 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     53 #include "webkit/common/resource_type.h"
     54 
     55 #if defined(OS_WIN)
     56 #include "content/common/sandbox_win.h"
     57 #include "content/public/common/sandboxed_process_launcher_delegate.h"
     58 #endif
     59 
     60 namespace content {
     61 namespace {
     62 
     63 #if defined(OS_WIN)
     64 // NOTE: changes to this class need to be reviewed by the security team.
     65 class WorkerSandboxedProcessLauncherDelegate
     66     : public content::SandboxedProcessLauncherDelegate {
     67  public:
     68   WorkerSandboxedProcessLauncherDelegate() {}
     69   virtual ~WorkerSandboxedProcessLauncherDelegate() {}
     70 
     71   virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
     72                               bool* success) {
     73     AddBaseHandleClosePolicy(policy);
     74   }
     75 };
     76 #endif  // OS_WIN
     77 
     78 // Helper class that we pass to SocketStreamDispatcherHost so that it can find
     79 // the right net::URLRequestContext for a request.
     80 class URLRequestContextSelector
     81     : public ResourceMessageFilter::URLRequestContextSelector {
     82  public:
     83   explicit URLRequestContextSelector(
     84       net::URLRequestContextGetter* url_request_context,
     85       net::URLRequestContextGetter* media_url_request_context)
     86       : url_request_context_(url_request_context),
     87         media_url_request_context_(media_url_request_context) {
     88   }
     89   virtual ~URLRequestContextSelector() {}
     90 
     91   virtual net::URLRequestContext* GetRequestContext(
     92       ResourceType::Type resource_type) OVERRIDE {
     93     if (resource_type == ResourceType::MEDIA)
     94       return media_url_request_context_->GetURLRequestContext();
     95     return url_request_context_->GetURLRequestContext();
     96   }
     97 
     98  private:
     99   net::URLRequestContextGetter* url_request_context_;
    100   net::URLRequestContextGetter* media_url_request_context_;
    101 };
    102 
    103 }  // namespace
    104 
    105 // Notifies RenderViewHost that one or more worker objects crashed.
    106 void WorkerCrashCallback(int render_process_unique_id, int render_view_id) {
    107   RenderViewHostImpl* host =
    108       RenderViewHostImpl::FromID(render_process_unique_id, render_view_id);
    109   if (host)
    110     host->GetDelegate()->WorkerCrashed();
    111 }
    112 
    113 WorkerProcessHost::WorkerProcessHost(
    114     ResourceContext* resource_context,
    115     const WorkerStoragePartition& partition)
    116     : resource_context_(resource_context),
    117       partition_(partition),
    118       process_launched_(false) {
    119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    120   DCHECK(resource_context_);
    121   process_.reset(
    122       new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this));
    123 }
    124 
    125 WorkerProcessHost::~WorkerProcessHost() {
    126   // If we crashed, tell the RenderViewHosts.
    127   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    128     const WorkerDocumentSet::DocumentInfoSet& parents =
    129         i->worker_document_set()->documents();
    130     for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
    131              parents.begin(); parent_iter != parents.end(); ++parent_iter) {
    132       BrowserThread::PostTask(
    133           BrowserThread::UI, FROM_HERE,
    134           base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(),
    135                      parent_iter->render_view_id()));
    136     }
    137     WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
    138         this, i->worker_route_id());
    139   }
    140 
    141   ChildProcessSecurityPolicyImpl::GetInstance()->Remove(
    142       process_->GetData().id);
    143 }
    144 
    145 bool WorkerProcessHost::Send(IPC::Message* message) {
    146   return process_->Send(message);
    147 }
    148 
    149 bool WorkerProcessHost::Init(int render_process_id) {
    150   std::string channel_id = process_->GetHost()->CreateChannel();
    151   if (channel_id.empty())
    152     return false;
    153 
    154 #if defined(OS_LINUX)
    155   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
    156 #else
    157   int flags = ChildProcessHost::CHILD_NORMAL;
    158 #endif
    159 
    160   base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
    161   if (exe_path.empty())
    162     return false;
    163 
    164   CommandLine* cmd_line = new CommandLine(exe_path);
    165   cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess);
    166   cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
    167   std::string locale = GetContentClient()->browser()->GetApplicationLocale();
    168   cmd_line->AppendSwitchASCII(switches::kLang, locale);
    169 
    170   static const char* const kSwitchNames[] = {
    171     switches::kDisableApplicationCache,
    172     switches::kDisableDatabases,
    173 #if defined(OS_WIN)
    174     switches::kDisableDesktopNotifications,
    175 #endif
    176     switches::kDisableFileSystem,
    177     switches::kDisableSeccompFilterSandbox,
    178     switches::kEnableExperimentalWebPlatformFeatures,
    179 #if defined(OS_MACOSX)
    180     switches::kEnableSandboxLogging,
    181 #endif
    182   };
    183   cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames,
    184                              arraysize(kSwitchNames));
    185 
    186 #if defined(OS_POSIX)
    187   bool use_zygote = true;
    188 
    189   if (CommandLine::ForCurrentProcess()->HasSwitch(
    190           switches::kWaitForDebuggerChildren)) {
    191     // Look to pass-on the kWaitForDebugger flag.
    192     std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    193         switches::kWaitForDebuggerChildren);
    194     if (value.empty() || value == switches::kWorkerProcess) {
    195       cmd_line->AppendSwitch(switches::kWaitForDebugger);
    196       use_zygote = false;
    197     }
    198   }
    199 
    200   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren)) {
    201     // Look to pass-on the kDebugOnStart flag.
    202     std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    203         switches::kDebugChildren);
    204     if (value.empty() || value == switches::kWorkerProcess) {
    205       // launches a new xterm, and runs the worker process in gdb, reading
    206       // optional commands from gdb_chrome file in the working directory.
    207       cmd_line->PrependWrapper("xterm -e gdb -x gdb_chrome --args");
    208       use_zygote = false;
    209     }
    210   }
    211 #endif
    212 
    213   process_->Launch(
    214 #if defined(OS_WIN)
    215       new WorkerSandboxedProcessLauncherDelegate,
    216 #elif defined(OS_POSIX)
    217       use_zygote,
    218       base::EnvironmentVector(),
    219 #endif
    220       cmd_line);
    221 
    222   ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker(
    223       process_->GetData().id, render_process_id);
    224   CreateMessageFilters(render_process_id);
    225 
    226   return true;
    227 }
    228 
    229 void WorkerProcessHost::CreateMessageFilters(int render_process_id) {
    230   ChromeBlobStorageContext* blob_storage_context =
    231       GetChromeBlobStorageContextForResourceContext(resource_context_);
    232   StreamContext* stream_context =
    233       GetStreamContextForResourceContext(resource_context_);
    234 
    235   net::URLRequestContextGetter* url_request_context =
    236       partition_.url_request_context();
    237   net::URLRequestContextGetter* media_url_request_context =
    238       partition_.url_request_context();
    239 
    240   ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter(
    241       process_->GetData().id, PROCESS_TYPE_WORKER, resource_context_,
    242       partition_.appcache_service(),
    243       blob_storage_context,
    244       partition_.filesystem_context(),
    245       new URLRequestContextSelector(url_request_context,
    246                                     media_url_request_context));
    247   process_->GetHost()->AddFilter(resource_message_filter);
    248 
    249   worker_message_filter_ = new WorkerMessageFilter(
    250       render_process_id, resource_context_, partition_,
    251       base::Bind(&WorkerServiceImpl::next_worker_route_id,
    252                  base::Unretained(WorkerServiceImpl::GetInstance())));
    253   process_->GetHost()->AddFilter(worker_message_filter_.get());
    254   process_->GetHost()->AddFilter(new AppCacheDispatcherHost(
    255       partition_.appcache_service(), process_->GetData().id));
    256   process_->GetHost()->AddFilter(new FileAPIMessageFilter(
    257       process_->GetData().id,
    258       url_request_context,
    259       partition_.filesystem_context(),
    260       blob_storage_context,
    261       stream_context));
    262   process_->GetHost()->AddFilter(new FileUtilitiesMessageFilter(
    263       process_->GetData().id));
    264   process_->GetHost()->AddFilter(new MimeRegistryMessageFilter());
    265   process_->GetHost()->AddFilter(
    266       new DatabaseMessageFilter(partition_.database_tracker()));
    267   process_->GetHost()->AddFilter(new QuotaDispatcherHost(
    268       process_->GetData().id,
    269       partition_.quota_manager(),
    270       GetContentClient()->browser()->CreateQuotaPermissionContext()));
    271 
    272   SocketStreamDispatcherHost* socket_stream_dispatcher_host =
    273       new SocketStreamDispatcherHost(
    274           render_process_id,
    275           new URLRequestContextSelector(url_request_context,
    276                                         media_url_request_context),
    277           resource_context_);
    278   socket_stream_dispatcher_host_ = socket_stream_dispatcher_host;
    279   process_->GetHost()->AddFilter(socket_stream_dispatcher_host);
    280   process_->GetHost()->AddFilter(
    281       new WorkerDevToolsMessageFilter(process_->GetData().id));
    282   process_->GetHost()->AddFilter(new IndexedDBDispatcherHost(
    283       process_->GetData().id, partition_.indexed_db_context()));
    284 }
    285 
    286 void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
    287   ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
    288       process_->GetData().id, instance.url());
    289 
    290   instances_.push_back(instance);
    291 
    292   WorkerProcessMsg_CreateWorker_Params params;
    293   params.url = instance.url();
    294   params.name = instance.name();
    295   params.route_id = instance.worker_route_id();
    296   params.creator_process_id = instance.parent_process_id();
    297   params.shared_worker_appcache_id = instance.main_resource_appcache_id();
    298   Send(new WorkerProcessMsg_CreateWorker(params));
    299 
    300   UpdateTitle();
    301 
    302   // Walk all pending filters and let them know the worker has been created
    303   // (could be more than one in the case where we had to queue up worker
    304   // creation because the worker process limit was reached).
    305   for (WorkerInstance::FilterList::const_iterator i =
    306            instance.filters().begin();
    307        i != instance.filters().end(); ++i) {
    308     CHECK(i->first);
    309     i->first->Send(new ViewMsg_WorkerCreated(i->second));
    310   }
    311 }
    312 
    313 bool WorkerProcessHost::FilterMessage(const IPC::Message& message,
    314                                       WorkerMessageFilter* filter) {
    315   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    316     if (!i->closed() && i->HasFilter(filter, message.routing_id())) {
    317       RelayMessage(message, worker_message_filter_.get(), i->worker_route_id());
    318       return true;
    319     }
    320   }
    321 
    322   return false;
    323 }
    324 
    325 void WorkerProcessHost::OnProcessLaunched() {
    326   process_launched_ = true;
    327 
    328   WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated();
    329 }
    330 
    331 bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
    332   bool msg_is_ok = true;
    333   bool handled = true;
    334   IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
    335     IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
    336                         OnWorkerContextClosed)
    337     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
    338     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem)
    339     IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB)
    340     IPC_MESSAGE_UNHANDLED(handled = false)
    341   IPC_END_MESSAGE_MAP_EX()
    342 
    343   if (!msg_is_ok) {
    344     NOTREACHED();
    345     RecordAction(UserMetricsAction("BadMessageTerminate_WPH"));
    346     base::KillProcess(
    347         process_->GetData().handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
    348   }
    349 
    350   if (handled)
    351     return true;
    352 
    353   if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
    354     WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
    355         this, message.routing_id());
    356   }
    357 
    358   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    359     if (i->worker_route_id() == message.routing_id()) {
    360       if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
    361         instances_.erase(i);
    362         UpdateTitle();
    363       }
    364       return true;
    365     }
    366   }
    367   return false;
    368 }
    369 
    370 // Sent to notify the browser process when a worker context invokes close(), so
    371 // no new connections are sent to shared workers.
    372 void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) {
    373   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    374     if (i->worker_route_id() == worker_route_id) {
    375       // Set the closed flag - this will stop any further messages from
    376       // being sent to the worker (messages can still be sent from the worker,
    377       // for exception reporting, etc).
    378       i->set_closed(true);
    379       break;
    380     }
    381   }
    382 }
    383 
    384 void WorkerProcessHost::OnAllowDatabase(int worker_route_id,
    385                                         const GURL& url,
    386                                         const string16& name,
    387                                         const string16& display_name,
    388                                         unsigned long estimated_size,
    389                                         bool* result) {
    390   *result = GetContentClient()->browser()->AllowWorkerDatabase(
    391       url, name, display_name, estimated_size, resource_context_,
    392       GetRenderViewIDsForWorker(worker_route_id));
    393 }
    394 
    395 void WorkerProcessHost::OnAllowFileSystem(int worker_route_id,
    396                                           const GURL& url,
    397                                           bool* result) {
    398   *result = GetContentClient()->browser()->AllowWorkerFileSystem(
    399       url, resource_context_, GetRenderViewIDsForWorker(worker_route_id));
    400 }
    401 
    402 void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id,
    403                                          const GURL& url,
    404                                          const string16& name,
    405                                          bool* result) {
    406   *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
    407       url, name, resource_context_, GetRenderViewIDsForWorker(worker_route_id));
    408 }
    409 
    410 void WorkerProcessHost::RelayMessage(
    411     const IPC::Message& message,
    412     WorkerMessageFilter* filter,
    413     int route_id) {
    414   if (message.type() == WorkerMsg_PostMessage::ID) {
    415     // We want to send the receiver a routing id for the new channel, so
    416     // crack the message first.
    417     string16 msg;
    418     std::vector<int> sent_message_port_ids;
    419     std::vector<int> new_routing_ids;
    420     if (!WorkerMsg_PostMessage::Read(
    421             &message, &msg, &sent_message_port_ids, &new_routing_ids)) {
    422       return;
    423     }
    424     if (sent_message_port_ids.size() != new_routing_ids.size())
    425       return;
    426 
    427     for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
    428       new_routing_ids[i] = filter->GetNextRoutingID();
    429       MessagePortService::GetInstance()->UpdateMessagePort(
    430           sent_message_port_ids[i], filter, new_routing_ids[i]);
    431     }
    432 
    433     filter->Send(new WorkerMsg_PostMessage(
    434         route_id, msg, sent_message_port_ids, new_routing_ids));
    435 
    436     // Send any queued messages to the sent message ports.  We can only do this
    437     // after sending the above message, since it's the one that sets up the
    438     // message port route which the queued messages are sent to.
    439     for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
    440       MessagePortService::GetInstance()->
    441           SendQueuedMessagesIfPossible(sent_message_port_ids[i]);
    442     }
    443   } else if (message.type() == WorkerMsg_Connect::ID) {
    444     // Crack the SharedWorker Connect message to setup routing for the port.
    445     int sent_message_port_id;
    446     int new_routing_id;
    447     if (!WorkerMsg_Connect::Read(
    448             &message, &sent_message_port_id, &new_routing_id)) {
    449       return;
    450     }
    451     new_routing_id = filter->GetNextRoutingID();
    452     MessagePortService::GetInstance()->UpdateMessagePort(
    453         sent_message_port_id, filter, new_routing_id);
    454 
    455     // Resend the message with the new routing id.
    456     filter->Send(new WorkerMsg_Connect(
    457         route_id, sent_message_port_id, new_routing_id));
    458 
    459     // Send any queued messages for the sent port.
    460     MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
    461         sent_message_port_id);
    462   } else {
    463     IPC::Message* new_message = new IPC::Message(message);
    464     new_message->set_routing_id(route_id);
    465     filter->Send(new_message);
    466     if (message.type() == WorkerMsg_StartWorkerContext::ID) {
    467       WorkerDevToolsManager::GetInstance()->WorkerContextStarted(
    468           this, route_id);
    469     }
    470     return;
    471   }
    472 }
    473 
    474 void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() {
    475   if (!instances_.size() && socket_stream_dispatcher_host_.get()) {
    476     // We can assume that this object is going to delete, because
    477     // currently a WorkerInstance will never be added to a WorkerProcessHost
    478     // once it is initialized.
    479 
    480     // SocketStreamDispatcherHost should be notified now that the worker
    481     // process will shutdown soon.
    482     socket_stream_dispatcher_host_->Shutdown();
    483     socket_stream_dispatcher_host_ = NULL;
    484   }
    485 }
    486 
    487 void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) {
    488   for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
    489     bool shutdown = false;
    490     i->RemoveFilters(filter);
    491 
    492     i->worker_document_set()->RemoveAll(filter);
    493     if (i->worker_document_set()->IsEmpty()) {
    494       shutdown = true;
    495     }
    496     if (shutdown) {
    497       Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
    498       i = instances_.erase(i);
    499     } else {
    500       ++i;
    501     }
    502   }
    503   ShutdownSocketStreamDispatcherHostIfNecessary();
    504 }
    505 
    506 bool WorkerProcessHost::CanShutdown() {
    507   return instances_.empty();
    508 }
    509 
    510 void WorkerProcessHost::UpdateTitle() {
    511   std::set<std::string> titles;
    512   for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    513     // Allow the embedder first crack at special casing the title.
    514     std::string title = GetContentClient()->browser()->
    515         GetWorkerProcessTitle(i->url(), resource_context_);
    516 
    517     if (title.empty()) {
    518       title = net::registry_controlled_domains::GetDomainAndRegistry(
    519           i->url(),
    520           net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    521     }
    522 
    523     // Use the host name if the domain is empty, i.e. localhost or IP address.
    524     if (title.empty())
    525       title = i->url().host();
    526 
    527     // If the host name is empty, i.e. file url, use the path.
    528     if (title.empty())
    529       title = i->url().path();
    530     titles.insert(title);
    531   }
    532 
    533   std::string display_title;
    534   for (std::set<std::string>::iterator i = titles.begin();
    535        i != titles.end(); ++i) {
    536     if (!display_title.empty())
    537       display_title += ", ";
    538     display_title += *i;
    539   }
    540 
    541   process_->SetName(UTF8ToUTF16(display_title));
    542 }
    543 
    544 void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter,
    545                                          unsigned long long document_id) {
    546   // Walk all instances and remove the document from their document set.
    547   for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
    548     i->worker_document_set()->Remove(filter, document_id);
    549     if (i->worker_document_set()->IsEmpty()) {
    550       // This worker has no more associated documents - shut it down.
    551       Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
    552       i = instances_.erase(i);
    553     } else {
    554       ++i;
    555     }
    556   }
    557   ShutdownSocketStreamDispatcherHostIfNecessary();
    558 }
    559 
    560 void WorkerProcessHost::TerminateWorker(int worker_route_id) {
    561   Send(new WorkerMsg_TerminateWorkerContext(worker_route_id));
    562 }
    563 
    564 void WorkerProcessHost::SetBackgrounded(bool backgrounded) {
    565   process_->SetBackgrounded(backgrounded);
    566 }
    567 
    568 const ChildProcessData& WorkerProcessHost::GetData() {
    569   return process_->GetData();
    570 }
    571 
    572 std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderViewIDsForWorker(
    573     int worker_route_id) {
    574   std::vector<std::pair<int, int> > result;
    575   WorkerProcessHost::Instances::const_iterator i;
    576   for (i = instances_.begin(); i != instances_.end(); ++i) {
    577     if (i->worker_route_id() != worker_route_id)
    578       continue;
    579     const WorkerDocumentSet::DocumentInfoSet& documents =
    580         i->worker_document_set()->documents();
    581     for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
    582          documents.begin(); doc != documents.end(); ++doc) {
    583       result.push_back(
    584           std::make_pair(doc->render_process_id(), doc->render_view_id()));
    585     }
    586     break;
    587   }
    588   return result;
    589 }
    590 
    591 WorkerProcessHost::WorkerInstance::WorkerInstance(
    592     const GURL& url,
    593     const string16& name,
    594     int worker_route_id,
    595     int parent_process_id,
    596     int64 main_resource_appcache_id,
    597     ResourceContext* resource_context,
    598     const WorkerStoragePartition& partition)
    599     : url_(url),
    600       closed_(false),
    601       name_(name),
    602       worker_route_id_(worker_route_id),
    603       parent_process_id_(parent_process_id),
    604       main_resource_appcache_id_(main_resource_appcache_id),
    605       worker_document_set_(new WorkerDocumentSet()),
    606       resource_context_(resource_context),
    607       partition_(partition) {
    608   DCHECK(resource_context_);
    609 }
    610 
    611 WorkerProcessHost::WorkerInstance::WorkerInstance(
    612     const GURL& url,
    613     bool shared,
    614     const string16& name,
    615     ResourceContext* resource_context,
    616     const WorkerStoragePartition& partition)
    617     : url_(url),
    618       closed_(false),
    619       name_(name),
    620       worker_route_id_(MSG_ROUTING_NONE),
    621       parent_process_id_(0),
    622       main_resource_appcache_id_(0),
    623       worker_document_set_(new WorkerDocumentSet()),
    624       resource_context_(resource_context),
    625       partition_(partition) {
    626   DCHECK(resource_context_);
    627 }
    628 
    629 WorkerProcessHost::WorkerInstance::~WorkerInstance() {
    630 }
    631 
    632 // Compares an instance based on the algorithm in the WebWorkers spec - an
    633 // instance matches if the origins of the URLs match, and:
    634 // a) the names are non-empty and equal
    635 // -or-
    636 // b) the names are both empty, and the urls are equal
    637 bool WorkerProcessHost::WorkerInstance::Matches(
    638     const GURL& match_url,
    639     const string16& match_name,
    640     const WorkerStoragePartition& partition,
    641     ResourceContext* resource_context) const {
    642   // Only match open shared workers.
    643   if (closed_)
    644     return false;
    645 
    646   // ResourceContext equivalence is being used as a proxy to ensure we only
    647   // matched shared workers within the same BrowserContext.
    648   if (resource_context_ != resource_context)
    649     return false;
    650 
    651   // We must be in the same storage partition otherwise sharing will violate
    652   // isolation.
    653   if (!partition_.Equals(partition))
    654     return false;
    655 
    656   if (url_.GetOrigin() != match_url.GetOrigin())
    657     return false;
    658 
    659   if (name_.empty() && match_name.empty())
    660     return url_ == match_url;
    661 
    662   return name_ == match_name;
    663 }
    664 
    665 void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter,
    666                                                   int route_id) {
    667   CHECK(filter);
    668   if (!HasFilter(filter, route_id)) {
    669     FilterInfo info(filter, route_id);
    670     filters_.push_back(info);
    671   }
    672 }
    673 
    674 void WorkerProcessHost::WorkerInstance::RemoveFilter(
    675     WorkerMessageFilter* filter, int route_id) {
    676   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
    677     if (i->first == filter && i->second == route_id)
    678       i = filters_.erase(i);
    679     else
    680       ++i;
    681   }
    682   // Should not be duplicate copies in the filter set.
    683   DCHECK(!HasFilter(filter, route_id));
    684 }
    685 
    686 void WorkerProcessHost::WorkerInstance::RemoveFilters(
    687     WorkerMessageFilter* filter) {
    688   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
    689     if (i->first == filter)
    690       i = filters_.erase(i);
    691     else
    692       ++i;
    693   }
    694 }
    695 
    696 bool WorkerProcessHost::WorkerInstance::HasFilter(
    697     WorkerMessageFilter* filter, int route_id) const {
    698   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
    699        ++i) {
    700     if (i->first == filter && i->second == route_id)
    701       return true;
    702   }
    703   return false;
    704 }
    705 
    706 bool WorkerProcessHost::WorkerInstance::RendererIsParent(
    707     int render_process_id, int render_view_id) const {
    708   const WorkerDocumentSet::DocumentInfoSet& parents =
    709       worker_document_set()->documents();
    710   for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
    711            parents.begin();
    712        parent_iter != parents.end(); ++parent_iter) {
    713     if (parent_iter->render_process_id() == render_process_id &&
    714         parent_iter->render_view_id() == render_view_id) {
    715       return true;
    716     }
    717   }
    718   return false;
    719 }
    720 
    721 WorkerProcessHost::WorkerInstance::FilterInfo
    722 WorkerProcessHost::WorkerInstance::GetFilter() const {
    723   DCHECK(NumFilters() == 1);
    724   return *filters_.begin();
    725 }
    726 
    727 }  // namespace content
    728