0

ServiceWorker: add ServiceWorkerClients.claim() support (2/3).

The claiming registration will fetch all registrations from
DB and comparing them with existing provider hosts. If it
has the longest match, it will start controlling the
provider host.

Spec: https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#clients-claim

(1/3): https://codereview.chromium.org/868463004/
(2/3): This
(3/3): https://codereview.chromium.org/872593002/

BUG=450106
TEST=https://codereview.chromium.org/872593002/

Review URL: https://codereview.chromium.org/871853002

Cr-Commit-Position: refs/heads/master@{#314514}
This commit is contained in:
xiang.long
2015-02-03 23:04:57 -08:00
committed by Commit bot
parent d15575438e
commit afc4f16f4c
19 changed files with 221 additions and 11 deletions

@ -46,6 +46,7 @@ void NotificationClickEventFinished(
case SERVICE_WORKER_ERROR_IPC_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_STATE:
status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
break;
}
@ -93,6 +94,7 @@ void DispatchNotificationClickEventOnRegistration(
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
break;
case SERVICE_WORKER_OK:

@ -412,6 +412,7 @@ void PushMessagingMessageFilter::DoUnregister(
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
NOTREACHED() << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
@ -527,6 +528,7 @@ void PushMessagingMessageFilter::DidGetRegistration(
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
NOTREACHED() << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
get_status = PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR;

@ -114,6 +114,7 @@ void PushMessagingRouter::DeliverMessageEnd(
case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_STATE:
NOTREACHED() << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
delivery_status = PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR;

@ -57,7 +57,8 @@ ServiceWorkerProviderHost::ServiceWorkerProviderHost(
provider_id_(provider_id),
context_(context),
dispatcher_host_(dispatcher_host),
allow_association_(true) {
allow_association_(true),
is_claiming_(false) {
DCHECK_NE(ChildProcessHost::kInvalidUniqueID, render_process_id_);
if (render_frame_id == MSG_ROUTING_NONE) {
// Actual thread id is set when the worker context gets started.
@ -124,7 +125,7 @@ void ServiceWorkerProviderHost::SetControllerVersionAttribute(
return; // Could be NULL in some tests.
bool should_notify_controllerchange =
previous_version && version && version->skip_waiting();
is_claiming_ || (previous_version && version && version->skip_waiting());
// SetController message should be sent only for the document context.
DCHECK_EQ(kDocumentMainThreadId, render_thread_id_);
@ -159,10 +160,7 @@ bool ServiceWorkerProviderHost::SetHostedVersionId(int64 version_id) {
void ServiceWorkerProviderHost::AssociateRegistration(
ServiceWorkerRegistration* registration) {
DCHECK(CanAssociateRegistration(registration));
if (associated_registration_.get())
DecreaseProcessReference(associated_registration_->pattern());
IncreaseProcessReference(registration->pattern());
associated_registration_ = registration;
associated_registration_->AddListener(this);
SendAssociateRegistrationMessage();
@ -282,6 +280,19 @@ void ServiceWorkerProviderHost::AddScopedProcessReferenceToPattern(
IncreaseProcessReference(pattern);
}
void ServiceWorkerProviderHost::ClaimedByRegistration(
ServiceWorkerRegistration* registration) {
DCHECK(registration->active_version());
is_claiming_ = true;
if (registration == associated_registration_) {
SetControllerVersionAttribute(registration->active_version());
} else if (allow_association_) {
DisassociateRegistration();
AssociateRegistration(registration);
}
is_claiming_ = false;
}
void ServiceWorkerProviderHost::PrepareForCrossSiteTransfer() {
DCHECK_NE(ChildProcessHost::kInvalidUniqueID, render_process_id_);
DCHECK_NE(MSG_ROUTING_NONE, render_frame_id_);

@ -152,6 +152,9 @@ class CONTENT_EXPORT ServiceWorkerProviderHost
// be removed in destructor.
void AddScopedProcessReferenceToPattern(const GURL& pattern);
// |registration| claims the document to be controlled.
void ClaimedByRegistration(ServiceWorkerRegistration* registration);
// Methods to support cross site navigations.
void PrepareForCrossSiteTransfer();
void CompleteCrossSiteTransfer(
@ -220,6 +223,7 @@ class CONTENT_EXPORT ServiceWorkerProviderHost
base::WeakPtr<ServiceWorkerContextCore> context_;
ServiceWorkerDispatcherHost* dispatcher_host_;
bool allow_association_;
bool is_claiming_;
std::vector<base::Closure> queued_events_;

@ -161,6 +161,16 @@ void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
ActivateWaitingVersion();
}
void ServiceWorkerRegistration::ClaimClients(const StatusCallback& callback) {
DCHECK(context_);
DCHECK(active_version());
// TODO(xiang): Should better not hit the database http://crbug.com/454250.
context_->storage()->GetRegistrationsForOrigin(
pattern_.GetOrigin(),
base::Bind(&ServiceWorkerRegistration::DidGetRegistrationsForClaimClients,
this, callback, active_version_));
}
void ServiceWorkerRegistration::ClearWhenReady() {
DCHECK(context_);
if (is_uninstalling_)
@ -360,4 +370,48 @@ void ServiceWorkerRegistration::OnRestoreFinished(
callback.Run(status);
}
void ServiceWorkerRegistration::DidGetRegistrationsForClaimClients(
const StatusCallback& callback,
scoped_refptr<ServiceWorkerVersion> version,
const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
if (!context_) {
callback.Run(SERVICE_WORKER_ERROR_ABORT);
return;
}
if (!active_version() || version != active_version()) {
callback.Run(SERVICE_WORKER_ERROR_STATE);
return;
}
for (scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
context_->GetProviderHostIterator();
!it->IsAtEnd(); it->Advance()) {
ServiceWorkerProviderHost* host = it->GetProviderHost();
if (ShouldClaim(host, registrations))
host->ClaimedByRegistration(this);
}
callback.Run(SERVICE_WORKER_OK);
}
bool ServiceWorkerRegistration::ShouldClaim(
ServiceWorkerProviderHost* provider_host,
const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
if (provider_host->controlling_version() == active_version())
return false;
LongestScopeMatcher matcher(provider_host->document_url());
if (!matcher.MatchLongest(pattern_))
return false;
for (const ServiceWorkerRegistrationInfo& info : registrations) {
ServiceWorkerRegistration* registration =
context_->GetLiveRegistration(info.registration_id);
if (registration &&
(registration->is_uninstalling() || registration->is_uninstalled()))
continue;
if (matcher.MatchLongest(info.pattern))
return false;
}
return true;
}
} // namespace content

@ -109,6 +109,10 @@ class CONTENT_EXPORT ServiceWorkerRegistration
// initiated immediately.
void ActivateWaitingVersionWhenReady();
// Takes over control of provider hosts which are currently not controlled or
// controlled by other registrations.
void ClaimClients(const StatusCallback& callback);
// Triggers the [[ClearRegistration]] algorithm when the currently
// active version has no controllees. Deletes this registration
// from storage immediately.
@ -163,6 +167,14 @@ class CONTENT_EXPORT ServiceWorkerRegistration
scoped_refptr<ServiceWorkerVersion> version,
ServiceWorkerStatusCode status);
void DidGetRegistrationsForClaimClients(
const StatusCallback& callback,
scoped_refptr<ServiceWorkerVersion> version,
const std::vector<ServiceWorkerRegistrationInfo>& registrations);
bool ShouldClaim(
ServiceWorkerProviderHost* provider_host,
const std::vector<ServiceWorkerRegistrationInfo>& registration_infos);
const GURL pattern_;
const int64 registration_id_;
bool is_deleted_;

@ -53,6 +53,7 @@ void GetServiceWorkerRegistrationStatusResponse(
case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
case SERVICE_WORKER_ERROR_EXISTS:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
// Unexpected, or should have bailed out before calling this, or we don't
// have a corresponding blink error code yet.
break; // Fall through to NOTREACHED().

@ -102,6 +102,9 @@ TEST(ServiceWorkerUtilsTest, FindLongestScopeMatch) {
// "/xxx" should be matched longer than "/xx".
ASSERT_TRUE(matcher.MatchLongest(GURL("http://www.example.com/xxx")));
// The second call with the same URL should return false.
ASSERT_FALSE(matcher.MatchLongest(GURL("http://www.example.com/xxx")));
ASSERT_FALSE(matcher.MatchLongest(GURL("http://www.example.com/xxxx")));
}

@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/message_port_message_filter.h"
#include "content/browser/message_port_service.h"
#include "content/browser/service_worker/embedded_worker_instance.h"
@ -71,6 +72,12 @@ const int64 kStopDoomedWorkerDelay = 5; // 5 secs.
// Default delay for scheduled update.
const int kUpdateDelaySeconds = 1;
const char kClaimClientsStateErrorMesage[] =
"Only the active worker can claim clients.";
const char kClaimClientsShutdownErrorMesage[] =
"Failed to claim clients due to Service Worker system shutdown.";
void RunSoon(const base::Closure& callback) {
if (!callback.is_null())
base::MessageLoop::current()->PostTask(FROM_HERE, callback);
@ -800,6 +807,8 @@ bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
OnGetClientInfoError)
IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
OnSkipWaiting)
IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
OnClaimClients)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@ -1105,6 +1114,45 @@ void ServiceWorkerVersion::DidSkipWaiting(int request_id) {
embedded_worker_->SendMessage(ServiceWorkerMsg_DidSkipWaiting(request_id));
}
void ServiceWorkerVersion::OnClaimClients(int request_id) {
StatusCallback callback = base::Bind(&ServiceWorkerVersion::DidClaimClients,
weak_factory_.GetWeakPtr(), request_id);
if (status_ != ACTIVATING && status_ != ACTIVATED) {
callback.Run(SERVICE_WORKER_ERROR_STATE);
return;
}
if (!context_) {
callback.Run(SERVICE_WORKER_ERROR_ABORT);
return;
}
ServiceWorkerRegistration* registration =
context_->GetLiveRegistration(registration_id_);
if (!registration) {
callback.Run(SERVICE_WORKER_ERROR_ABORT);
return;
}
registration->ClaimClients(callback);
}
void ServiceWorkerVersion::DidClaimClients(
int request_id, ServiceWorkerStatusCode status) {
if (status == SERVICE_WORKER_ERROR_STATE) {
embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
request_id, blink::WebServiceWorkerError::ErrorTypeState,
base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
return;
}
if (status == SERVICE_WORKER_ERROR_ABORT) {
embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
return;
}
DCHECK(status == SERVICE_WORKER_OK);
embedded_worker_->SendMessage(ServiceWorkerMsg_DidClaimClients(request_id));
}
void ServiceWorkerVersion::DidGetClientInfo(
int client_id,
scoped_refptr<GetClientDocumentsCallback> callback,

@ -357,9 +357,11 @@ class CONTENT_EXPORT ServiceWorkerVersion
const std::vector<int>& sent_message_port_ids);
void OnFocusClient(int request_id, int client_id);
void OnSkipWaiting(int request_id);
void OnClaimClients(int request_id);
void OnFocusClientFinished(int request_id, bool result);
void DidSkipWaiting(int request_id);
void DidClaimClients(int request_id, ServiceWorkerStatusCode status);
void DidGetClientInfo(int client_id,
scoped_refptr<GetClientDocumentsCallback> callback,
ServiceWorkerStatusCode status,

@ -346,11 +346,11 @@ void ServiceWorkerDispatcher::OnDisassociateRegistration(
ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
if (provider == provider_contexts_.end())
return;
provider->second->OnDisassociateRegistration();
worker_to_provider_.erase(provider->second->installing_handle_id());
worker_to_provider_.erase(provider->second->waiting_handle_id());
worker_to_provider_.erase(provider->second->active_handle_id());
worker_to_provider_.erase(provider->second->controller_handle_id());
provider->second->OnDisassociateRegistration();
}
void ServiceWorkerDispatcher::OnRegistered(

@ -240,6 +240,11 @@ IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_GetClientInfoError,
IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_SkipWaiting,
int /* request_id */)
// Asks the browser to have this worker take control of pages that match
// its scope.
IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_ClaimClients,
int /* request_id */)
// CacheStorage operations in the browser.
IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_CacheStorageHas,
int /* request_id */,
@ -442,6 +447,12 @@ IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_CrossOriginMessageToWorker,
std::vector<int> /* new_routing_ids */)
IPC_MESSAGE_CONTROL1(ServiceWorkerMsg_DidSkipWaiting,
int /* request_id */)
IPC_MESSAGE_CONTROL1(ServiceWorkerMsg_DidClaimClients,
int /* request_id */)
IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_ClaimClientsError,
int /* request_id */,
blink::WebServiceWorkerError::ErrorType /* code */,
base::string16 /* message */)
// Sent via EmbeddedWorker as a response of GetClientDocuments.
IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_DidGetClientDocuments,

@ -37,6 +37,8 @@ const char* ServiceWorkerStatusToString(ServiceWorkerStatusCode status) {
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
return "ServiceWorker failed to handle event (event.waitUntil "
"Promise rejected)";
case SERVICE_WORKER_ERROR_STATE:
return "The ServiceWorker state was not valid";
}
NOTREACHED();
return "";

@ -51,6 +51,9 @@ enum ServiceWorkerStatusCode {
// Event handling failed (event.waitUntil Promise rejected).
SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED,
// An error triggered by invalid worker state.
SERVICE_WORKER_ERROR_STATE,
};
CONTENT_EXPORT const char* ServiceWorkerStatusToString(

@ -392,6 +392,12 @@ void EmbeddedWorkerContextClient::skipWaiting(
script_context_->SkipWaiting(callbacks);
}
void EmbeddedWorkerContextClient::claim(
blink::WebServiceWorkerClientsClaimCallbacks* callbacks) {
DCHECK(script_context_);
script_context_->ClaimClients(callbacks);
}
void EmbeddedWorkerContextClient::OnMessageToWorker(
int thread_id,
int embedded_worker_id,

@ -10,11 +10,6 @@
#include "base/strings/string16.h"
#include "content/common/service_worker/service_worker_types.h"
#include "ipc/ipc_listener.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerClientFocusCallback.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerClientsInfo.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerSkipWaitingCallbacks.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/web/WebServiceWorkerContextClient.h"
#include "url/gurl.h"
@ -117,6 +112,7 @@ class EmbeddedWorkerContextClient
blink::WebServiceWorkerClientFocusCallback*);
virtual void skipWaiting(
blink::WebServiceWorkerSkipWaitingCallbacks* callbacks);
virtual void claim(blink::WebServiceWorkerClientsClaimCallbacks* callbacks);
// TODO: Implement DevTools related method overrides.

@ -106,6 +106,8 @@ void ServiceWorkerScriptContext::OnMessageReceived(
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_FocusClientResponse,
OnFocusClientResponse)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidSkipWaiting, OnDidSkipWaiting)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidClaimClients, OnDidClaimClients)
IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ClaimClientsError, OnClaimClientsError)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@ -243,6 +245,13 @@ void ServiceWorkerScriptContext::FocusClient(
GetRoutingID(), request_id, client_id));
}
void ServiceWorkerScriptContext::ClaimClients(
blink::WebServiceWorkerClientsClaimCallbacks* callbacks) {
DCHECK(callbacks);
int request_id = pending_claim_clients_callbacks_.Add(callbacks);
Send(new ServiceWorkerHostMsg_ClaimClients(GetRoutingID(), request_id));
}
void ServiceWorkerScriptContext::SkipWaiting(
blink::WebServiceWorkerSkipWaitingCallbacks* callbacks) {
DCHECK(callbacks);
@ -460,4 +469,35 @@ void ServiceWorkerScriptContext::OnDidSkipWaiting(int request_id) {
pending_skip_waiting_callbacks_.Remove(request_id);
}
void ServiceWorkerScriptContext::OnDidClaimClients(int request_id) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnDidClaimClients");
blink::WebServiceWorkerClientsClaimCallbacks* callbacks =
pending_claim_clients_callbacks_.Lookup(request_id);
if (!callbacks) {
NOTREACHED() << "Got stray response: " << request_id;
return;
}
callbacks->onSuccess();
pending_claim_clients_callbacks_.Remove(request_id);
}
void ServiceWorkerScriptContext::OnClaimClientsError(
int request_id,
blink::WebServiceWorkerError::ErrorType error_type,
const base::string16& message) {
TRACE_EVENT0("ServiceWorker",
"ServiceWorkerScriptContext::OnClaimClientsError");
blink::WebServiceWorkerClientsClaimCallbacks* callbacks =
pending_claim_clients_callbacks_.Lookup(request_id);
if (!callbacks) {
NOTREACHED() << "Got stray response: " << request_id;
return;
}
scoped_ptr<blink::WebServiceWorkerError> error(
new blink::WebServiceWorkerError(error_type, message));
callbacks->onError(error.release());
pending_claim_clients_callbacks_.Remove(request_id);
}
} // namespace content

@ -20,7 +20,9 @@
#include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
#include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerClientFocusCallback.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerClientsClaimCallbacks.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerClientsInfo.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
#include "third_party/WebKit/public/platform/WebServiceWorkerSkipWaitingCallbacks.h"
@ -83,6 +85,7 @@ class ServiceWorkerScriptContext {
void FocusClient(int client_id,
blink::WebServiceWorkerClientFocusCallback* callback);
void SkipWaiting(blink::WebServiceWorkerSkipWaitingCallbacks* callbacks);
void ClaimClients(blink::WebServiceWorkerClientsClaimCallbacks* callbacks);
// Send a message to the browser. Takes ownership of |message|.
void Send(IPC::Message* message);
@ -98,6 +101,8 @@ class ServiceWorkerScriptContext {
private:
typedef IDMap<blink::WebServiceWorkerClientsCallbacks, IDMapOwnPointer>
ClientsCallbacksMap;
typedef IDMap<blink::WebServiceWorkerClientsClaimCallbacks, IDMapOwnPointer>
ClaimClientsCallbacksMap;
typedef IDMap<blink::WebServiceWorkerClientFocusCallback, IDMapOwnPointer>
FocusClientCallbacksMap;
typedef IDMap<blink::WebServiceWorkerSkipWaitingCallbacks, IDMapOwnPointer>
@ -130,6 +135,10 @@ class ServiceWorkerScriptContext {
int request_id, const std::vector<ServiceWorkerClientInfo>& clients);
void OnFocusClientResponse(int request_id, bool result);
void OnDidSkipWaiting(int request_id);
void OnDidClaimClients(int request_id);
void OnClaimClientsError(int request_id,
blink::WebServiceWorkerError::ErrorType error_type,
const base::string16& message);
scoped_ptr<ServiceWorkerCacheStorageDispatcher> cache_storage_dispatcher_;
@ -153,6 +162,9 @@ class ServiceWorkerScriptContext {
// Pending callbacks for SkipWaiting().
SkipWaitingCallbacksMap pending_skip_waiting_callbacks_;
// Pending callbacks for ClaimClients().
ClaimClientsCallbacksMap pending_claim_clients_callbacks_;
// Capture timestamps for UMA
std::map<int, base::TimeTicks> activate_start_timings_;
std::map<int, base::TimeTicks> fetch_start_timings_;