blob: 19c6e12d346ba7a11cb4aa71e44d98218a20c25a [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/renderer/service_worker/service_worker_script_context.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "content/child/thread_safe_sender.h"
#include "content/child/webmessageportchannel_impl.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/renderer/service_worker/embedded_worker_context_client.h"
#include "ipc/ipc_message.h"
#include "third_party/WebKit/public/platform/WebNotificationData.h"
#include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerRequest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/web/WebServiceWorkerContextClient.h"
#include "third_party/WebKit/public/web/WebServiceWorkerContextProxy.h"
namespace content {
namespace {
void SendPostMessageToDocumentOnMainThread(
ThreadSafeSender* sender,
int routing_id,
int client_id,
const base::string16& message,
scoped_ptr<blink::WebMessagePortChannelArray> channels) {
sender->Send(new ServiceWorkerHostMsg_PostMessageToDocument(
routing_id, client_id, message,
WebMessagePortChannelImpl::ExtractMessagePortIDs(channels.release())));
}
blink::WebURLRequest::FetchRequestMode GetBlinkFetchRequestMode(
FetchRequestMode mode) {
return static_cast<blink::WebURLRequest::FetchRequestMode>(mode);
}
blink::WebURLRequest::FetchCredentialsMode GetBlinkFetchCredentialsMode(
FetchCredentialsMode credentials_mode) {
return static_cast<blink::WebURLRequest::FetchCredentialsMode>(
credentials_mode);
}
blink::WebURLRequest::RequestContext GetBlinkRequestContext(
RequestContextType request_context_type) {
return static_cast<blink::WebURLRequest::RequestContext>(
request_context_type);
}
blink::WebURLRequest::FrameType GetBlinkFrameType(
RequestContextFrameType frame_type) {
return static_cast<blink::WebURLRequest::FrameType>(frame_type);
}
} // namespace
ServiceWorkerScriptContext::ServiceWorkerScriptContext(
EmbeddedWorkerContextClient* embedded_context,
blink::WebServiceWorkerContextProxy* proxy)
: cache_storage_dispatcher_(new ServiceWorkerCacheStorageDispatcher(this)),
embedded_context_(embedded_context),
proxy_(proxy) {
}
ServiceWorkerScriptContext::~ServiceWorkerScriptContext() {}
void ServiceWorkerScriptContext::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ServiceWorkerScriptContext, message)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ActivateEvent, OnActivateEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_FetchEvent, OnFetchEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_InstallEvent, OnInstallEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SyncEvent, OnSyncEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_PushEvent, OnPushEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GeofencingEvent, OnGeofencingEvent)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToWorker, OnPostMessage)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetClientDocuments,
OnDidGetClientDocuments)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_FocusClientResponse,
OnFocusClientResponse)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
// TODO(gavinp): Would it be preferable to put an AddListener() method to
// EmbeddedWorkerContextClient?
if (!handled)
handled = cache_storage_dispatcher_->OnMessageReceived(message);
DCHECK(handled);
}
void ServiceWorkerScriptContext::DidHandleActivateEvent(
int request_id,
blink::WebServiceWorkerEventResult result) {
UMA_HISTOGRAM_TIMES(
"ServiceWorker.ActivateEventExecutionTime",
base::TimeTicks::Now() - activate_start_timings_[request_id]);
activate_start_timings_.erase(request_id);
Send(new ServiceWorkerHostMsg_ActivateEventFinished(
GetRoutingID(), request_id, result));
}
void ServiceWorkerScriptContext::DidHandleInstallEvent(
int request_id,
blink::WebServiceWorkerEventResult result) {
UMA_HISTOGRAM_TIMES(
"ServiceWorker.InstallEventExecutionTime",
base::TimeTicks::Now() - install_start_timings_[request_id]);
install_start_timings_.erase(request_id);
Send(new ServiceWorkerHostMsg_InstallEventFinished(
GetRoutingID(), request_id, result));
}
void ServiceWorkerScriptContext::DidHandleFetchEvent(
int request_id,
ServiceWorkerFetchEventResult result,
const ServiceWorkerResponse& response) {
UMA_HISTOGRAM_TIMES(
"ServiceWorker.FetchEventExecutionTime",
base::TimeTicks::Now() - fetch_start_timings_[request_id]);
fetch_start_timings_.erase(request_id);
Send(new ServiceWorkerHostMsg_FetchEventFinished(
GetRoutingID(), request_id, result, response));
}
void ServiceWorkerScriptContext::DidHandleNotificationClickEvent(
int request_id,
blink::WebServiceWorkerEventResult result) {
UMA_HISTOGRAM_TIMES(
"ServiceWorker.NotificationClickEventExecutionTime",
base::TimeTicks::Now() - notification_click_start_timings_[request_id]);
notification_click_start_timings_.erase(request_id);
Send(new ServiceWorkerHostMsg_NotificationClickEventFinished(
GetRoutingID(), request_id));
}
void ServiceWorkerScriptContext::DidHandlePushEvent(
int request_id,
blink::WebServiceWorkerEventResult result) {
if (result == blink::WebServiceWorkerEventResultCompleted) {
UMA_HISTOGRAM_TIMES(
"ServiceWorker.PushEventExecutionTime",
base::TimeTicks::Now() - push_start_timings_[request_id]);
}
push_start_timings_.erase(request_id);
Send(new ServiceWorkerHostMsg_PushEventFinished(
GetRoutingID(), request_id, result));
}
void ServiceWorkerScriptContext::DidHandleSyncEvent(int request_id) {
Send(new ServiceWorkerHostMsg_SyncEventFinished(
GetRoutingID(), request_id));
}
void ServiceWorkerScriptContext::GetClientDocuments(
blink::WebServiceWorkerClientsCallbacks* callbacks) {
DCHECK(callbacks);
int request_id = pending_clients_callbacks_.Add(callbacks);
Send(new ServiceWorkerHostMsg_GetClientDocuments(
GetRoutingID(), request_id));
}
void ServiceWorkerScriptContext::PostMessageToDocument(
int client_id,
const base::string16& message,
scoped_ptr<blink::WebMessagePortChannelArray> channels) {
// This may send channels for MessagePorts, and all internal book-keeping
// messages for MessagePort (e.g. QueueMessages) are sent from main thread
// (with thread hopping), so we need to do the same thread hopping here not
// to overtake those messages.
embedded_context_->main_thread_proxy()->PostTask(
FROM_HERE,
base::Bind(&SendPostMessageToDocumentOnMainThread,
make_scoped_refptr(embedded_context_->thread_safe_sender()),
GetRoutingID(), client_id, message, base::Passed(&channels)));
}
void ServiceWorkerScriptContext::FocusClient(
int client_id, blink::WebServiceWorkerClientFocusCallback* callback) {
DCHECK(callback);
int request_id = pending_focus_client_callbacks_.Add(callback);
Send(new ServiceWorkerHostMsg_FocusClient(
GetRoutingID(), request_id, client_id));
}
void ServiceWorkerScriptContext::Send(IPC::Message* message) {
embedded_context_->Send(message);
}
int ServiceWorkerScriptContext::GetRoutingID() const {
return embedded_context_->embedded_worker_id();
}
void ServiceWorkerScriptContext::OnActivateEvent(int request_id) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnActivateEvent");
activate_start_timings_[request_id] = base::TimeTicks::Now();
proxy_->dispatchActivateEvent(request_id);
}
void ServiceWorkerScriptContext::OnInstallEvent(int request_id,
int active_version_id) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnInstallEvent");
install_start_timings_[request_id] = base::TimeTicks::Now();
proxy_->dispatchInstallEvent(request_id);
}
void ServiceWorkerScriptContext::OnFetchEvent(
int request_id,
const ServiceWorkerFetchRequest& request) {
blink::WebServiceWorkerRequest webRequest;
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnFetchEvent");
webRequest.setURL(blink::WebURL(request.url));
webRequest.setMethod(blink::WebString::fromUTF8(request.method));
for (ServiceWorkerHeaderMap::const_iterator it = request.headers.begin();
it != request.headers.end();
++it) {
webRequest.setHeader(blink::WebString::fromUTF8(it->first),
blink::WebString::fromUTF8(it->second));
}
if (!request.blob_uuid.empty()) {
webRequest.setBlob(blink::WebString::fromUTF8(request.blob_uuid),
request.blob_size);
}
webRequest.setReferrer(blink::WebString::fromUTF8(request.referrer.spec()),
blink::WebReferrerPolicyDefault);
webRequest.setMode(GetBlinkFetchRequestMode(request.mode));
webRequest.setCredentialsMode(
GetBlinkFetchCredentialsMode(request.credentials_mode));
webRequest.setRequestContext(
GetBlinkRequestContext(request.request_context_type));
webRequest.setFrameType(GetBlinkFrameType(request.frame_type));
webRequest.setIsReload(request.is_reload);
fetch_start_timings_[request_id] = base::TimeTicks::Now();
proxy_->dispatchFetchEvent(request_id, webRequest);
}
void ServiceWorkerScriptContext::OnSyncEvent(int request_id) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnSyncEvent");
proxy_->dispatchSyncEvent(request_id);
}
void ServiceWorkerScriptContext::OnNotificationClickEvent(
int request_id, const std::string& notification_id) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnNotificationClickEvent");
notification_click_start_timings_[request_id] = base::TimeTicks::Now();
blink::WebNotificationData notification;
// TODO(peter): Initialize |notification| with the actual contents.
proxy_->dispatchNotificationClickEvent(
request_id,
blink::WebString::fromUTF8(notification_id),
notification);
}
void ServiceWorkerScriptContext::OnPushEvent(int request_id,
const std::string& data) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnPushEvent");
push_start_timings_[request_id] = base::TimeTicks::Now();
proxy_->dispatchPushEvent(request_id, blink::WebString::fromUTF8(data));
}
void ServiceWorkerScriptContext::OnGeofencingEvent(
int request_id,
blink::WebGeofencingEventType event_type,
const std::string& region_id,
const blink::WebCircularGeofencingRegion& region) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnGeofencingEvent");
proxy_->dispatchGeofencingEvent(
request_id, event_type, blink::WebString::fromUTF8(region_id), region);
Send(new ServiceWorkerHostMsg_GeofencingEventFinished(GetRoutingID(),
request_id));
}
void ServiceWorkerScriptContext::OnPostMessage(
const base::string16& message,
const std::vector<int>& sent_message_port_ids,
const std::vector<int>& new_routing_ids) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnPostEvent");
std::vector<WebMessagePortChannelImpl*> ports;
if (!sent_message_port_ids.empty()) {
base::MessageLoopProxy* loop_proxy = embedded_context_->main_thread_proxy();
ports.resize(sent_message_port_ids.size());
for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
ports[i] = new WebMessagePortChannelImpl(
new_routing_ids[i], sent_message_port_ids[i], loop_proxy);
}
}
// dispatchMessageEvent is expected to execute onmessage function
// synchronously.
base::TimeTicks before = base::TimeTicks::Now();
proxy_->dispatchMessageEvent(message, ports);
UMA_HISTOGRAM_TIMES(
"ServiceWorker.MessageEventExecutionTime",
base::TimeTicks::Now() - before);
}
void ServiceWorkerScriptContext::OnDidGetClientDocuments(
int request_id, const std::vector<int>& client_ids) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnDidGetClientDocuments");
blink::WebServiceWorkerClientsCallbacks* callbacks =
pending_clients_callbacks_.Lookup(request_id);
if (!callbacks) {
NOTREACHED() << "Got stray response: " << request_id;
return;
}
scoped_ptr<blink::WebServiceWorkerClientsInfo> info(
new blink::WebServiceWorkerClientsInfo);
info->clientIDs = client_ids;
callbacks->onSuccess(info.release());
pending_clients_callbacks_.Remove(request_id);
}
void ServiceWorkerScriptContext::OnFocusClientResponse(int request_id,
bool result) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnFocusClientResponse");
blink::WebServiceWorkerClientFocusCallback* callback =
pending_focus_client_callbacks_.Lookup(request_id);
if (!callback) {
NOTREACHED() << "Got stray response: " << request_id;
return;
}
callback->onSuccess(&result);
pending_focus_client_callbacks_.Remove(request_id);
}
} // namespace content