root/content/browser/worker_host/worker_process_host.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. debugging_child_
  2. PreSpawnTarget
  3. ShouldUseZygote
  4. GetIpcFd
  5. WorkerCrashCallback
  6. WorkerCreatedCallback
  7. WorkerTerminatedCallback
  8. process_launched_
  9. Send
  10. Init
  11. CreateMessageFilters
  12. CreateWorker
  13. FilterMessage
  14. OnProcessLaunched
  15. OnMessageReceived
  16. OnWorkerContextClosed
  17. OnWorkerContextDestroyed
  18. OnWorkerScriptLoaded
  19. OnWorkerScriptLoadFailed
  20. OnWorkerConnected
  21. OnAllowDatabase
  22. OnAllowFileSystem
  23. OnAllowIndexedDB
  24. OnForceKillWorkerProcess
  25. RelayMessage
  26. ShutdownSocketStreamDispatcherHostIfNecessary
  27. FilterShutdown
  28. CanShutdown
  29. UpdateTitle
  30. DocumentDetached
  31. TerminateWorker
  32. SetBackgrounded
  33. GetData
  34. GetRenderFrameIDsForWorker
  35. GetContexts
  36. GetRequestContext
  37. load_failed_
  38. SetMessagePortID
  39. Matches
  40. AddFilter
  41. RemoveFilter
  42. RemoveFilters
  43. HasFilter
  44. FrameIsParent
  45. GetFilter

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/worker_host/worker_process_host.h"

#include <set>
#include <string>
#include <vector>

#include "base/base_switches.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/appcache/appcache_dispatcher_host.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/browser_child_process_host_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/worker_devtools_manager.h"
#include "content/browser/devtools/worker_devtools_message_filter.h"
#include "content/browser/fileapi/fileapi_message_filter.h"
#include "content/browser/frame_host/render_frame_host_delegate.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/message_port_service.h"
#include "content/browser/mime_registry_message_filter.h"
#include "content/browser/quota_dispatcher_host.h"
#include "content/browser/renderer_host/database_message_filter.h"
#include "content/browser/renderer_host/file_utilities_message_filter.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/worker_host/worker_message_filter.h"
#include "content/browser/worker_host/worker_service_impl.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/view_messages.h"
#include "content/common/worker_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "ipc/ipc_switches.h"
#include "net/base/mime_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/url_request/url_request_context_getter.h"
#include "ui/base/ui_base_switches.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
#include "webkit/common/resource_type.h"

#if defined(OS_WIN)
#include "content/common/sandbox_win.h"
#endif

namespace content {
namespace {

// NOTE: changes to this class need to be reviewed by the security team.
class WorkerSandboxedProcessLauncherDelegate
    : public content::SandboxedProcessLauncherDelegate {
 public:
  WorkerSandboxedProcessLauncherDelegate(ChildProcessHost* host,
                                         bool debugging_child)
#if defined(OS_POSIX)
      : ipc_fd_(host->TakeClientFileDescriptor()),
        debugging_child_(debugging_child)
#endif  // OS_POSIX
  {}

  virtual ~WorkerSandboxedProcessLauncherDelegate() {}

#if defined(OS_WIN)
  virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
                              bool* success) {
    AddBaseHandleClosePolicy(policy);
  }
#elif defined(OS_POSIX)
  virtual bool ShouldUseZygote() OVERRIDE {
    return !debugging_child_;
  }
  virtual int GetIpcFd() OVERRIDE {
    return ipc_fd_;
  }
#endif  // OS_WIN

 private:
#if defined(OS_POSIX)
  int ipc_fd_;
  bool debugging_child_;
#endif  // OS_POSIX
};

// Notifies RenderViewHost that one or more worker objects crashed.
void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
  RenderFrameHostImpl* host =
      RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
  if (host)
    host->delegate()->WorkerCrashed(host);
}

void WorkerCreatedCallback(int render_process_id,
                           int render_frame_id,
                           int worker_process_id) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  RenderFrameHost* render_frame_host =
      RenderFrameHost::FromID(render_process_id, render_frame_id);
  if (!render_frame_host)
    return;
  SiteInstance* site_instance = render_frame_host->GetSiteInstance();
  GetContentClient()->browser()->WorkerProcessCreated(site_instance,
                                                      worker_process_id);
}

void WorkerTerminatedCallback(int render_process_id,
                              int render_frame_id,
                              int worker_process_id) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  RenderFrameHost* render_frame_host =
      RenderFrameHost::FromID(render_process_id, render_frame_id);
  if (!render_frame_host)
    return;
  SiteInstance* site_instance = render_frame_host->GetSiteInstance();
  GetContentClient()->browser()->WorkerProcessTerminated(site_instance,
                                                         worker_process_id);
}

}  // namespace

WorkerProcessHost::WorkerProcessHost(
    ResourceContext* resource_context,
    const WorkerStoragePartition& partition)
    : resource_context_(resource_context),
      partition_(partition),
      process_launched_(false) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(resource_context_);
  process_.reset(
      new BrowserChildProcessHostImpl(PROCESS_TYPE_WORKER, this));
}

WorkerProcessHost::~WorkerProcessHost() {
  // If we crashed, tell the RenderViewHosts.
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (!i->load_failed()) {
      const WorkerDocumentSet::DocumentInfoSet& parents =
          i->worker_document_set()->documents();
      for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
               parents.begin(); parent_iter != parents.end(); ++parent_iter) {
        BrowserThread::PostTask(
            BrowserThread::UI, FROM_HERE,
            base::Bind(&WorkerCrashCallback, parent_iter->render_process_id(),
                       parent_iter->render_frame_id()));
      }
    }
    WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
        this, i->worker_route_id());
  }

  ChildProcessSecurityPolicyImpl::GetInstance()->Remove(
      process_->GetData().id);
}

bool WorkerProcessHost::Send(IPC::Message* message) {
  return process_->Send(message);
}

bool WorkerProcessHost::Init(int render_process_id, int render_frame_id) {
  std::string channel_id = process_->GetHost()->CreateChannel();
  if (channel_id.empty())
    return false;

#if defined(OS_LINUX)
  int flags = ChildProcessHost::CHILD_ALLOW_SELF;
#else
  int flags = ChildProcessHost::CHILD_NORMAL;
#endif

  base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
  if (exe_path.empty())
    return false;

  CommandLine* cmd_line = new CommandLine(exe_path);
  cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess);
  cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
  std::string locale = GetContentClient()->browser()->GetApplicationLocale();
  cmd_line->AppendSwitchASCII(switches::kLang, locale);

  static const char* const kSwitchNames[] = {
    switches::kDisableApplicationCache,
    switches::kDisableDatabases,
#if defined(OS_WIN)
    switches::kDisableDesktopNotifications,
#endif
    switches::kDisableFileSystem,
    switches::kDisableSeccompFilterSandbox,
    switches::kEnableExperimentalWebPlatformFeatures,
    switches::kEnableServiceWorker,
#if defined(OS_MACOSX)
    switches::kEnableSandboxLogging,
#endif
    switches::kJavaScriptFlags,
    switches::kNoSandbox
  };
  cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames,
                             arraysize(kSwitchNames));

bool debugging_child = false;
#if defined(OS_POSIX)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kWaitForDebuggerChildren)) {
    // Look to pass-on the kWaitForDebugger flag.
    std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
        switches::kWaitForDebuggerChildren);
    if (value.empty() || value == switches::kWorkerProcess) {
      cmd_line->AppendSwitch(switches::kWaitForDebugger);
      debugging_child = true;
    }
  }

  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren)) {
    // Look to pass-on the kDebugOnStart flag.
    std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
        switches::kDebugChildren);
    if (value.empty() || value == switches::kWorkerProcess) {
      // launches a new xterm, and runs the worker process in gdb, reading
      // optional commands from gdb_chrome file in the working directory.
      cmd_line->PrependWrapper("xterm -e gdb -x gdb_chrome --args");
      debugging_child = true;
    }
  }
#endif

  process_->Launch(
      new WorkerSandboxedProcessLauncherDelegate(process_->GetHost(),
                                                 debugging_child),
      cmd_line);

  ChildProcessSecurityPolicyImpl::GetInstance()->AddWorker(
      process_->GetData().id, render_process_id);
  CreateMessageFilters(render_process_id);

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&WorkerCreatedCallback,
                 render_process_id,
                 render_frame_id,
                 process_->GetData().id));
  return true;
}

void WorkerProcessHost::CreateMessageFilters(int render_process_id) {
  ChromeBlobStorageContext* blob_storage_context =
      GetChromeBlobStorageContextForResourceContext(resource_context_);
  StreamContext* stream_context =
      GetStreamContextForResourceContext(resource_context_);

  net::URLRequestContextGetter* url_request_context =
      partition_.url_request_context();

  ResourceMessageFilter::GetContextsCallback get_contexts_callback(
      base::Bind(&WorkerProcessHost::GetContexts,
      base::Unretained(this)));

  ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter(
      process_->GetData().id, PROCESS_TYPE_WORKER,
      partition_.appcache_service(),
      blob_storage_context,
      partition_.filesystem_context(),
      partition_.service_worker_context(),
      get_contexts_callback);
  process_->AddFilter(resource_message_filter);

  MessagePortMessageFilter* message_port_message_filter =
      new MessagePortMessageFilter(
          base::Bind(&WorkerServiceImpl::next_worker_route_id,
                     base::Unretained(WorkerServiceImpl::GetInstance())));
  process_->AddFilter(message_port_message_filter);
  worker_message_filter_ = new WorkerMessageFilter(render_process_id,
                                                   resource_context_,
                                                   partition_,
                                                   message_port_message_filter);
  process_->AddFilter(worker_message_filter_.get());
  process_->AddFilter(new AppCacheDispatcherHost(
      partition_.appcache_service(), process_->GetData().id));
  process_->AddFilter(new FileAPIMessageFilter(
      process_->GetData().id,
      url_request_context,
      partition_.filesystem_context(),
      blob_storage_context,
      stream_context));
  process_->AddFilter(new FileUtilitiesMessageFilter(
      process_->GetData().id));
  process_->AddFilter(new MimeRegistryMessageFilter());
  process_->AddFilter(new DatabaseMessageFilter(partition_.database_tracker()));
  process_->AddFilter(new QuotaDispatcherHost(
      process_->GetData().id,
      partition_.quota_manager(),
      GetContentClient()->browser()->CreateQuotaPermissionContext()));

  SocketStreamDispatcherHost::GetRequestContextCallback
      request_context_callback(
          base::Bind(&WorkerProcessHost::GetRequestContext,
          base::Unretained(this)));

  SocketStreamDispatcherHost* socket_stream_dispatcher_host =
      new SocketStreamDispatcherHost(
          render_process_id,
          request_context_callback,
          resource_context_);
  socket_stream_dispatcher_host_ = socket_stream_dispatcher_host;
  process_->AddFilter(socket_stream_dispatcher_host);
  process_->AddFilter(new WorkerDevToolsMessageFilter(process_->GetData().id));
  process_->AddFilter(
      new IndexedDBDispatcherHost(partition_.indexed_db_context()));
}

void WorkerProcessHost::CreateWorker(const WorkerInstance& instance,
                                     bool pause_on_start) {
  ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL(
      process_->GetData().id, instance.url());

  instances_.push_back(instance);

  WorkerProcessMsg_CreateWorker_Params params;
  params.url = instance.url();
  params.name = instance.name();
  params.content_security_policy = instance.content_security_policy();
  params.security_policy_type = instance.security_policy_type();
  params.pause_on_start = pause_on_start;
  params.route_id = instance.worker_route_id();
  Send(new WorkerProcessMsg_CreateWorker(params));

  UpdateTitle();

  // Walk all pending filters and let them know the worker has been created
  // (could be more than one in the case where we had to queue up worker
  // creation because the worker process limit was reached).
  for (WorkerInstance::FilterList::const_iterator i =
           instance.filters().begin();
       i != instance.filters().end(); ++i) {
    i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
  }
}

bool WorkerProcessHost::FilterMessage(const IPC::Message& message,
                                      WorkerMessageFilter* filter) {
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (!i->closed() && i->HasFilter(filter, message.routing_id())) {
      RelayMessage(message, filter, &(*i));
      return true;
    }
  }

  return false;
}

void WorkerProcessHost::OnProcessLaunched() {
  process_launched_ = true;

  WorkerServiceImpl::GetInstance()->NotifyWorkerProcessCreated();
}

bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
  bool msg_is_ok = true;
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
    IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
                        OnWorkerContextClosed)
    IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextDestroyed,
                        OnWorkerContextDestroyed)
    IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoaded,
                        OnWorkerScriptLoaded)
    IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerScriptLoadFailed,
                        OnWorkerScriptLoadFailed)
    IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerConnected,
                        OnWorkerConnected)
    IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
    IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem)
    IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowIndexedDB, OnAllowIndexedDB)
    IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_ForceKillWorker,
                        OnForceKillWorkerProcess)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP_EX()

  if (!msg_is_ok) {
    NOTREACHED();
    RecordAction(base::UserMetricsAction("BadMessageTerminate_WPH"));
    base::KillProcess(
        process_->GetData().handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
  }

  return handled;
}

// Sent to notify the browser process when a worker context invokes close(), so
// no new connections are sent to shared workers.
void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) {
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (i->worker_route_id() == worker_route_id) {
      // Set the closed flag - this will stop any further messages from
      // being sent to the worker (messages can still be sent from the worker,
      // for exception reporting, etc).
      i->set_closed(true);
      break;
    }
  }
}

void WorkerProcessHost::OnWorkerContextDestroyed(int worker_route_id) {
  WorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
      this, worker_route_id);
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (i->worker_route_id() == worker_route_id) {
      instances_.erase(i);
      UpdateTitle();
      return;
    }
  }
}

void WorkerProcessHost::OnWorkerScriptLoaded(int worker_route_id) {
  WorkerDevToolsManager::GetInstance()->WorkerContextStarted(this,
                                                             worker_route_id);
}

void WorkerProcessHost::OnWorkerScriptLoadFailed(int worker_route_id) {
  bool shutdown = true;
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (i->worker_route_id() != worker_route_id) {
      shutdown = false;
      continue;
    }
    i->set_load_failed(true);
    for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
          j != i->filters().end(); ++j) {
      j->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(j->route_id()));
    }
  }
  if (shutdown) {
    base::KillProcess(
          process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
  }
}

void WorkerProcessHost::OnWorkerConnected(int message_port_id,
                                          int worker_route_id) {
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    if (i->worker_route_id() != worker_route_id)
      continue;
    for (WorkerInstance::FilterList::const_iterator j = i->filters().begin();
          j != i->filters().end(); ++j) {
      if (j->message_port_id() != message_port_id)
        continue;
      j->filter()->Send(new ViewMsg_WorkerConnected(j->route_id()));
      return;
    }
  }
}

void WorkerProcessHost::OnAllowDatabase(int worker_route_id,
                                        const GURL& url,
                                        const base::string16& name,
                                        const base::string16& display_name,
                                        unsigned long estimated_size,
                                        bool* result) {
  *result = GetContentClient()->browser()->AllowWorkerDatabase(
      url, name, display_name, estimated_size, resource_context_,
      GetRenderFrameIDsForWorker(worker_route_id));
}

void WorkerProcessHost::OnAllowFileSystem(int worker_route_id,
                                          const GURL& url,
                                          bool* result) {
  *result = GetContentClient()->browser()->AllowWorkerFileSystem(
      url, resource_context_, GetRenderFrameIDsForWorker(worker_route_id));
}

void WorkerProcessHost::OnAllowIndexedDB(int worker_route_id,
                                         const GURL& url,
                                         const base::string16& name,
                                         bool* result) {
  *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
      url, name, resource_context_,
      GetRenderFrameIDsForWorker(worker_route_id));
}

void WorkerProcessHost::OnForceKillWorkerProcess() {
  if (process_ && process_launched_)
    base::KillProcess(
          process_->GetData().handle, RESULT_CODE_NORMAL_EXIT, false);
  else
    RecordAction(base::UserMetricsAction("WorkerProcess_BadProcessToKill"));
}

void WorkerProcessHost::RelayMessage(
    const IPC::Message& message,
    WorkerMessageFilter* incoming_filter,
    WorkerInstance* instance) {
  if (message.type() == WorkerMsg_Connect::ID) {
    // Crack the SharedWorker Connect message to setup routing for the port.
    int sent_message_port_id;
    int new_routing_id;
    if (!WorkerMsg_Connect::Read(
            &message, &sent_message_port_id, &new_routing_id)) {
      return;
    }
    new_routing_id = worker_message_filter_->GetNextRoutingID();
    MessagePortService::GetInstance()->UpdateMessagePort(
        sent_message_port_id,
        worker_message_filter_->message_port_message_filter(),
        new_routing_id);

    instance->SetMessagePortID(incoming_filter,
                               message.routing_id(),
                               sent_message_port_id);
    // Resend the message with the new routing id.
    worker_message_filter_->Send(new WorkerMsg_Connect(
        instance->worker_route_id(), sent_message_port_id, new_routing_id));

    // Send any queued messages for the sent port.
    MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
        sent_message_port_id);
  } else {
    IPC::Message* new_message = new IPC::Message(message);
    new_message->set_routing_id(instance->worker_route_id());
    worker_message_filter_->Send(new_message);
    return;
  }
}

void WorkerProcessHost::ShutdownSocketStreamDispatcherHostIfNecessary() {
  if (!instances_.size() && socket_stream_dispatcher_host_.get()) {
    // We can assume that this object is going to delete, because
    // currently a WorkerInstance will never be added to a WorkerProcessHost
    // once it is initialized.

    // SocketStreamDispatcherHost should be notified now that the worker
    // process will shutdown soon.
    socket_stream_dispatcher_host_->Shutdown();
    socket_stream_dispatcher_host_ = NULL;
  }
}

void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) {
  for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
    bool shutdown = false;
    i->RemoveFilters(filter);

    int render_frame_id = 0;
    const WorkerDocumentSet::DocumentInfoSet& documents =
        i->worker_document_set()->documents();
    for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
         documents.begin(); doc != documents.end(); ++doc) {
      if (doc->filter() == filter) {
        render_frame_id = doc->render_frame_id();
        break;
      }
    }
    i->worker_document_set()->RemoveAll(filter);
    if (i->worker_document_set()->IsEmpty()) {
      shutdown = true;
    }
    if (shutdown) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          base::Bind(&WorkerTerminatedCallback,
                     filter->render_process_id(),
                     render_frame_id,
                     process_->GetData().id));
      Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
      i = instances_.erase(i);
    } else {
      ++i;
    }
  }
  ShutdownSocketStreamDispatcherHostIfNecessary();
}

bool WorkerProcessHost::CanShutdown() {
  return instances_.empty();
}

void WorkerProcessHost::UpdateTitle() {
  std::set<std::string> titles;
  for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
    // Allow the embedder first crack at special casing the title.
    std::string title = GetContentClient()->browser()->
        GetWorkerProcessTitle(i->url(), resource_context_);

    if (title.empty()) {
      title = net::registry_controlled_domains::GetDomainAndRegistry(
          i->url(),
          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
    }

    // Use the host name if the domain is empty, i.e. localhost or IP address.
    if (title.empty())
      title = i->url().host();

    // If the host name is empty, i.e. file url, use the path.
    if (title.empty())
      title = i->url().path();
    titles.insert(title);
  }

  std::string display_title;
  for (std::set<std::string>::iterator i = titles.begin();
       i != titles.end(); ++i) {
    if (!display_title.empty())
      display_title += ", ";
    display_title += *i;
  }

  process_->SetName(base::UTF8ToUTF16(display_title));
}

void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter,
                                         unsigned long long document_id) {
  // Walk all instances and remove the document from their document set.
  for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
    int render_frame_id = 0;
    const WorkerDocumentSet::DocumentInfoSet& documents =
        i->worker_document_set()->documents();
    for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
         documents.begin(); doc != documents.end(); ++doc) {
      if (doc->filter() == filter && doc->document_id() == document_id) {
        render_frame_id = doc->render_frame_id();
        break;
      }
    }
    i->worker_document_set()->Remove(filter, document_id);
    if (i->worker_document_set()->IsEmpty()) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          base::Bind(&WorkerTerminatedCallback,
                     filter->render_process_id(),
                     render_frame_id,
                     process_->GetData().id));
      // This worker has no more associated documents - shut it down.
      Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
      i = instances_.erase(i);
    } else {
      ++i;
    }
  }
  ShutdownSocketStreamDispatcherHostIfNecessary();
}

void WorkerProcessHost::TerminateWorker(int worker_route_id) {
  Send(new WorkerMsg_TerminateWorkerContext(worker_route_id));
}

void WorkerProcessHost::SetBackgrounded(bool backgrounded) {
  process_->SetBackgrounded(backgrounded);
}

const ChildProcessData& WorkerProcessHost::GetData() {
  return process_->GetData();
}

std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderFrameIDsForWorker(
    int worker_route_id) {
  std::vector<std::pair<int, int> > result;
  WorkerProcessHost::Instances::const_iterator i;
  for (i = instances_.begin(); i != instances_.end(); ++i) {
    if (i->worker_route_id() != worker_route_id)
      continue;
    const WorkerDocumentSet::DocumentInfoSet& documents =
        i->worker_document_set()->documents();
    for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
         documents.begin(); doc != documents.end(); ++doc) {
      result.push_back(
          std::make_pair(doc->render_process_id(), doc->render_frame_id()));
    }
    break;
  }
  return result;
}

void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request& request,
                                    ResourceContext** resource_context,
                                    net::URLRequestContext** request_context) {
  *resource_context = resource_context_;
  *request_context = partition_.url_request_context()->GetURLRequestContext();
}

net::URLRequestContext* WorkerProcessHost::GetRequestContext(
    ResourceType::Type resource_type) {
  return partition_.url_request_context()->GetURLRequestContext();
}

WorkerProcessHost::WorkerInstance::WorkerInstance(
    const GURL& url,
    const base::string16& name,
    const base::string16& content_security_policy,
    blink::WebContentSecurityPolicyType security_policy_type,
    int worker_route_id,
    int render_frame_id,
    ResourceContext* resource_context,
    const WorkerStoragePartition& partition)
    : url_(url),
      closed_(false),
      name_(name),
      content_security_policy_(content_security_policy),
      security_policy_type_(security_policy_type),
      worker_route_id_(worker_route_id),
      render_frame_id_(render_frame_id),
      worker_document_set_(new WorkerDocumentSet()),
      resource_context_(resource_context),
      partition_(partition),
      load_failed_(false) {
  DCHECK(resource_context_);
}

WorkerProcessHost::WorkerInstance::~WorkerInstance() {
}

void WorkerProcessHost::WorkerInstance::SetMessagePortID(
    WorkerMessageFilter* filter,
    int route_id,
    int message_port_id) {
  for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
    if (i->filter() == filter && i->route_id() == route_id) {
      i->set_message_port_id(message_port_id);
      return;
    }
  }
}

// Compares an instance based on the algorithm in the WebWorkers spec - an
// instance matches if the origins of the URLs match, and:
// a) the names are non-empty and equal
// -or-
// b) the names are both empty, and the urls are equal
bool WorkerProcessHost::WorkerInstance::Matches(
    const GURL& match_url,
    const base::string16& match_name,
    const WorkerStoragePartition& partition,
    ResourceContext* resource_context) const {
  // Only match open shared workers.
  if (closed_)
    return false;

  // ResourceContext equivalence is being used as a proxy to ensure we only
  // matched shared workers within the same BrowserContext.
  if (resource_context_ != resource_context)
    return false;

  // We must be in the same storage partition otherwise sharing will violate
  // isolation.
  if (!partition_.Equals(partition))
    return false;

  if (url_.GetOrigin() != match_url.GetOrigin())
    return false;

  if (name_.empty() && match_name.empty())
    return url_ == match_url;

  return name_ == match_name;
}

void WorkerProcessHost::WorkerInstance::AddFilter(WorkerMessageFilter* filter,
                                                  int route_id) {
  CHECK(filter);
  if (!HasFilter(filter, route_id)) {
    FilterInfo info(filter, route_id);
    filters_.push_back(info);
  }
}

void WorkerProcessHost::WorkerInstance::RemoveFilter(
    WorkerMessageFilter* filter, int route_id) {
  for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
    if (i->filter() == filter && i->route_id() == route_id)
      i = filters_.erase(i);
    else
      ++i;
  }
  // Should not be duplicate copies in the filter set.
  DCHECK(!HasFilter(filter, route_id));
}

void WorkerProcessHost::WorkerInstance::RemoveFilters(
    WorkerMessageFilter* filter) {
  for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
    if (i->filter() == filter)
      i = filters_.erase(i);
    else
      ++i;
  }
}

bool WorkerProcessHost::WorkerInstance::HasFilter(
    WorkerMessageFilter* filter, int route_id) const {
  for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
       ++i) {
    if (i->filter() == filter && i->route_id() == route_id)
      return true;
  }
  return false;
}

bool WorkerProcessHost::WorkerInstance::FrameIsParent(
    int render_process_id, int render_frame_id) const {
  const WorkerDocumentSet::DocumentInfoSet& parents =
      worker_document_set()->documents();
  for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
           parents.begin();
       parent_iter != parents.end(); ++parent_iter) {
    if (parent_iter->render_process_id() == render_process_id &&
        parent_iter->render_frame_id() == render_frame_id) {
      return true;
    }
  }
  return false;
}

WorkerProcessHost::WorkerInstance::FilterInfo
WorkerProcessHost::WorkerInstance::GetFilter() const {
  DCHECK(NumFilters() == 1);
  return *filters_.begin();
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */