blob: d9b6d9fb104818590dafac2dfe503e0f46bcd808 [file] [log] [blame]
// Copyright 2014 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/shared_worker/shared_worker_host.h"
#include "base/metrics/histogram.h"
#include "content/browser/devtools/shared_worker_devtools_manager.h"
#include "content/browser/frame_host/render_frame_host_delegate.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/message_port_service.h"
#include "content/browser/shared_worker/shared_worker_instance.h"
#include "content/browser/shared_worker/shared_worker_message_filter.h"
#include "content/browser/shared_worker/shared_worker_service_impl.h"
#include "content/browser/shared_worker/worker_document_set.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/render_process_host.h"
#include "content/public/common/content_client.h"
namespace content {
namespace {
// 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 NotifyWorkerReadyForInspection(int worker_process_id,
int worker_route_id) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(NotifyWorkerReadyForInspection,
worker_process_id,
worker_route_id));
return;
}
SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
worker_process_id, worker_route_id);
}
void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
return;
}
SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
worker_process_id, worker_route_id);
}
} // namespace
SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
SharedWorkerMessageFilter* filter,
int worker_route_id)
: instance_(instance),
worker_document_set_(new WorkerDocumentSet()),
container_render_filter_(filter),
worker_process_id_(filter->render_process_id()),
worker_route_id_(worker_route_id),
load_failed_(false),
closed_(false),
creation_time_(base::TimeTicks::Now()),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
SharedWorkerHost::~SharedWorkerHost() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
base::TimeTicks::Now() - creation_time_);
// If we crashed, tell the RenderViewHosts.
if (instance_ && !load_failed_) {
const WorkerDocumentSet::DocumentInfoSet& parents =
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()));
}
}
if (!closed_)
NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
worker_process_id_, worker_route_id_);
}
bool SharedWorkerHost::Send(IPC::Message* message) {
if (!container_render_filter_) {
delete message;
return false;
}
return container_render_filter_->Send(message);
}
void SharedWorkerHost::Start(bool pause_on_start) {
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 = worker_route_id_;
Send(new WorkerProcessMsg_CreateWorker(params));
for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
++i) {
i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
}
}
bool SharedWorkerHost::FilterMessage(const IPC::Message& message,
SharedWorkerMessageFilter* filter) {
if (!instance_)
return false;
if (!closed_ && HasFilter(filter, message.routing_id())) {
RelayMessage(message, filter);
return true;
}
return false;
}
void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) {
if (!instance_)
return;
RemoveFilters(filter);
worker_document_set_->RemoveAll(filter);
if (worker_document_set_->IsEmpty()) {
// This worker has no more associated documents - shut it down.
Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
}
}
void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter,
unsigned long long document_id) {
if (!instance_)
return;
// Walk all instances and remove the document from their document set.
worker_document_set_->Remove(filter, document_id);
if (worker_document_set_->IsEmpty()) {
// This worker has no more associated documents - shut it down.
Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
}
}
void SharedWorkerHost::WorkerContextClosed() {
if (!instance_)
return;
// 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).
closed_ = true;
NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
}
void SharedWorkerHost::WorkerContextDestroyed() {
if (!instance_)
return;
instance_.reset();
worker_document_set_ = NULL;
}
void SharedWorkerHost::WorkerReadyForInspection() {
NotifyWorkerReadyForInspection(worker_process_id_, worker_route_id_);
}
void SharedWorkerHost::WorkerScriptLoaded() {
UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
base::TimeTicks::Now() - creation_time_);
}
void SharedWorkerHost::WorkerScriptLoadFailed() {
UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
base::TimeTicks::Now() - creation_time_);
if (!instance_)
return;
load_failed_ = true;
for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
++i) {
i->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i->route_id()));
}
}
void SharedWorkerHost::WorkerConnected(int message_port_id) {
if (!instance_)
return;
for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
++i) {
if (i->message_port_id() != message_port_id)
continue;
i->filter()->Send(new ViewMsg_WorkerConnected(i->route_id()));
return;
}
}
void SharedWorkerHost::AllowDatabase(const GURL& url,
const base::string16& name,
const base::string16& display_name,
unsigned long estimated_size,
bool* result) {
if (!instance_)
return;
*result = GetContentClient()->browser()->AllowWorkerDatabase(
url,
name,
display_name,
estimated_size,
instance_->resource_context(),
GetRenderFrameIDsForWorker());
}
void SharedWorkerHost::AllowFileSystem(const GURL& url,
scoped_ptr<IPC::Message> reply_msg) {
if (!instance_)
return;
GetContentClient()->browser()->AllowWorkerFileSystem(
url,
instance_->resource_context(),
GetRenderFrameIDsForWorker(),
base::Bind(&SharedWorkerHost::AllowFileSystemResponse,
weak_factory_.GetWeakPtr(),
base::Passed(&reply_msg)));
}
void SharedWorkerHost::AllowFileSystemResponse(
scoped_ptr<IPC::Message> reply_msg,
bool allowed) {
WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
reply_msg.get(),
allowed);
Send(reply_msg.release());
}
void SharedWorkerHost::AllowIndexedDB(const GURL& url,
const base::string16& name,
bool* result) {
if (!instance_)
return;
*result = GetContentClient()->browser()->AllowWorkerIndexedDB(
url, name, instance_->resource_context(), GetRenderFrameIDsForWorker());
}
void SharedWorkerHost::RelayMessage(
const IPC::Message& message,
SharedWorkerMessageFilter* incoming_filter) {
if (!instance_)
return;
if (message.type() == WorkerMsg_Connect::ID) {
// Crack the SharedWorker Connect message to setup routing for the port.
WorkerMsg_Connect::Param param;
if (!WorkerMsg_Connect::Read(&message, &param))
return;
int sent_message_port_id = get<0>(param);
int new_routing_id = get<1>(param);
DCHECK(container_render_filter_);
new_routing_id = container_render_filter_->GetNextRoutingID();
MessagePortService::GetInstance()->UpdateMessagePort(
sent_message_port_id,
container_render_filter_->message_port_message_filter(),
new_routing_id);
SetMessagePortID(
incoming_filter, message.routing_id(), sent_message_port_id);
// Resend the message with the new routing id.
Send(new WorkerMsg_Connect(
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(worker_route_id_);
Send(new_message);
return;
}
}
void SharedWorkerHost::TerminateWorker() {
Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
}
std::vector<std::pair<int, int> >
SharedWorkerHost::GetRenderFrameIDsForWorker() {
std::vector<std::pair<int, int> > result;
if (!instance_)
return result;
const WorkerDocumentSet::DocumentInfoSet& documents =
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()));
}
return result;
}
void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter,
int route_id) {
CHECK(filter);
if (!HasFilter(filter, route_id)) {
FilterInfo info(filter, route_id);
filters_.push_back(info);
}
}
void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) {
for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
if (i->filter() == filter)
i = filters_.erase(i);
else
++i;
}
}
bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* 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;
}
void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter* 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;
}
}
}
} // namespace content