ServiceWorker: Implement navigator.serviceWorker.getRegistration [3/3]

This completes the implementation of ServiceWorkerContainer#getRegistration().

Spec: https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#navigator-service-worker-getRegistration

[1/3] https://codereview.chromium.org/553983010/
[2/3] https://codereview.chromium.org/535753002/

BUG=404951
TEST=LayoutTests/http/tests/serviceworker/getregistration.html,webkit_unit_tests

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

git-svn-id: svn://svn.chromium.org/blink/trunk@181952 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 9615c43b
......@@ -36,4 +36,13 @@ async_test(function(t) {
}));
}, 'Exceedingly long scope URLs are rejected by unregister()');
async_test(function(t) {
navigator.serviceWorker.getRegistration(long_url).
then(t.unreached_func('getRegistration with a long url should fail')).
catch(t.step_func(function(reason) {
assert_equals(reason.name, 'SecurityError');
t.done();
}));
}, 'Exceedingly long document URLs are rejected by getRegistration()');
</script>
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="resources/test-helpers.js"></script>
<script>
async_test(function(t) {
var documentURL = 'no-such-worker';
navigator.serviceWorker.getRegistration(documentURL)
.then(function(value) {
assert_equals(value, undefined,
'getRegistration should resolve with undefined');
t.done();
})
.catch(unreached_rejection(t));
}, 'getRegistration');
async_test(function(t) {
var scope = 'scope/worker/';
var registration;
service_worker_unregister_and_register(t, 'resources/empty-worker.js',
scope)
.then(function(r) {
registration = r;
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(value) {
assert_equals(value, registration,
'getRegistration should resolve with registration');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Register then getRegistration');
async_test(function(t) {
var documentURL = 'http://example.com/';
navigator.serviceWorker.getRegistration(documentURL)
.then(function() {
assert_unreached(
'getRegistration with an out of origin URL should fail');
}, function(reason) {
assert_equals(
reason.name, 'SecurityError',
'getRegistration with an out of origin URL should fail');
t.done();
})
.catch(unreached_rejection(t));
}, 'getRegistration with a cross origin URL');
async_test(function(t) {
var scope = 'scope/worker/';
service_worker_unregister_and_register(t, 'resources/empty-worker.js',
scope)
.then(function(registration) {
return registration.unregister();
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(value) {
assert_equals(value, undefined,
'getRegistration should resolve with undefined');
t.done();
})
.catch(unreached_rejection(t));
}, 'Register then Unregister then getRegistration');
</script>
......@@ -54,6 +54,28 @@
namespace blink {
// This wraps CallbackPromiseAdapter and resolves the promise with undefined
// when nullptr is given to onSuccess.
class GetRegistrationCallback : public WebServiceWorkerProvider::WebServiceWorkerGetRegistrationCallbacks {
public:
explicit GetRegistrationCallback(PassRefPtr<ScriptPromiseResolver> resolver)
: m_resolver(resolver)
, m_adapter(m_resolver) { }
virtual ~GetRegistrationCallback() { }
virtual void onSuccess(WebServiceWorkerRegistration* registration) OVERRIDE
{
if (registration)
m_adapter.onSuccess(registration);
else if (m_resolver->executionContext() && !m_resolver->executionContext()->activeDOMObjectsAreStopped())
m_resolver->resolve();
}
virtual void onError(WebServiceWorkerError* error) OVERRIDE { m_adapter.onError(error); }
private:
RefPtr<ScriptPromiseResolver> m_resolver;
CallbackPromiseAdapter<ServiceWorkerRegistration, ServiceWorkerError> m_adapter;
WTF_MAKE_NONCOPYABLE(GetRegistrationCallback);
};
ServiceWorkerContainer* ServiceWorkerContainer::create(ExecutionContext* executionContext)
{
return new ServiceWorkerContainer(executionContext);
......@@ -163,6 +185,32 @@ ScriptPromise ServiceWorkerContainer::unregisterServiceWorker(ScriptState* scrip
return promise;
}
ScriptPromise ServiceWorkerContainer::getRegistration(ScriptState* scriptState, const String& documentURL)
{
ASSERT(RuntimeEnabledFeatures::serviceWorkerEnabled());
RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
// FIXME: This should use the container's execution context, not
// the callers.
ExecutionContext* executionContext = scriptState->executionContext();
RefPtr<SecurityOrigin> documentOrigin = executionContext->securityOrigin();
String errorMessage;
if (!documentOrigin->canAccessFeatureRequiringSecureOrigin(errorMessage)) {
resolver->reject(DOMException::create(NotSupportedError, errorMessage));
return promise;
}
KURL completedURL = executionContext->completeURL(documentURL);
if (!documentOrigin->canRequest(completedURL)) {
resolver->reject(DOMException::create(SecurityError, "The documentURL must match the current origin."));
return promise;
}
m_provider->getRegistration(completedURL, new GetRegistrationCallback(resolver));
return promise;
}
ServiceWorkerContainer::ReadyProperty* ServiceWorkerContainer::createReadyProperty()
{
return new ReadyProperty(executionContext(), this, ReadyProperty::Ready);
......
......@@ -71,6 +71,7 @@ public:
ScriptPromise registerServiceWorker(ScriptState*, const String& pattern, const Dictionary&);
ScriptPromise unregisterServiceWorker(ScriptState*, const String& scope);
ScriptPromise getRegistration(ScriptState*, const String& documentURL);
// WebServiceWorkerProviderClient overrides.
virtual void setController(WebServiceWorker*) OVERRIDE;
......
......@@ -38,4 +38,5 @@
[CallWith=ScriptState, ImplementedAs=registerServiceWorker] Promise register(ScalarValueString url, optional Dictionary options);
[CallWith=ScriptState, ImplementedAs=unregisterServiceWorker] Promise unregister(optional ScalarValueString scope = "/");
[CallWith=ScriptState] Promise getRegistration(optional ScalarValueString documentURL = "");
};
......@@ -205,6 +205,18 @@ protected:
container->willBeDetachedFromFrame();
}
void testGetRegistrationRejected(const String& documentURL, const ScriptValueTest& valueTest)
{
provide(adoptPtr(new NotReachedWebServiceWorkerProvider()));
ServiceWorkerContainer* container = ServiceWorkerContainer::create(executionContext());
ScriptState::Scope scriptScope(scriptState());
ScriptPromise promise = container->getRegistration(scriptState(), documentURL);
expectRejected(scriptState(), promise, valueTest);
container->willBeDetachedFromFrame();
}
private:
OwnPtr<DummyPageHolder> m_page;
};
......@@ -252,11 +264,28 @@ TEST_F(ServiceWorkerContainerTest, Unregister_CrossOriginScopeIsRejected)
ExpectDOMException("SecurityError", "The scope must match the current origin."));
}
TEST_F(ServiceWorkerContainerTest, GetRegistration_NonSecureOriginIsRejected)
{
setPageURL("http://www.example.com/");
testGetRegistrationRejected(
"http://www.example.com/",
ExpectDOMException("NotSupportedError", "Only secure origins are allowed. http://goo.gl/lq4gCo"));
}
TEST_F(ServiceWorkerContainerTest, GetRegistration_CrossOriginURLIsRejected)
{
setPageURL("https://www.example.com/");
testGetRegistrationRejected(
"https://foo.example.com/", // Differs by host
ExpectDOMException("SecurityError", "The documentURL must match the current origin."));
}
class StubWebServiceWorkerProvider {
public:
StubWebServiceWorkerProvider()
: m_registerCallCount(0)
, m_unregisterCallCount(0)
, m_getRegistrationCallCount(0)
{
}
......@@ -274,6 +303,8 @@ public:
const WebURL& registerScriptURL() { return m_registerScriptURL; }
size_t unregisterCallCount() { return m_unregisterCallCount; }
const WebURL& unregisterScope() { return m_unregisterScope; }
size_t getRegistrationCallCount() { return m_getRegistrationCallCount; }
const WebURL& getRegistrationURL() { return m_getRegistrationURL; }
private:
class WebServiceWorkerProviderImpl : public WebServiceWorkerProvider {
......@@ -300,10 +331,18 @@ private:
m_unregistrationCallbacksToDelete.append(adoptPtr(callbacks));
}
virtual void getRegistration(const WebURL& documentURL, WebServiceWorkerGetRegistrationCallbacks* callbacks) OVERRIDE
{
m_owner.m_getRegistrationCallCount++;
m_owner.m_getRegistrationURL = documentURL;
m_getRegistrationCallbacksToDelete.append(adoptPtr(callbacks));
}
private:
StubWebServiceWorkerProvider& m_owner;
Vector<OwnPtr<WebServiceWorkerRegistrationCallbacks> > m_registrationCallbacksToDelete;
Vector<OwnPtr<WebServiceWorkerUnregistrationCallbacks> > m_unregistrationCallbacksToDelete;
Vector<OwnPtr<WebServiceWorkerGetRegistrationCallbacks> > m_getRegistrationCallbacksToDelete;
};
private:
......@@ -312,6 +351,8 @@ private:
WebURL m_registerScriptURL;
size_t m_unregisterCallCount;
WebURL m_unregisterScope;
size_t m_getRegistrationCallCount;
WebURL m_getRegistrationURL;
};
TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelegatesToProvider)
......@@ -347,5 +388,24 @@ TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelega
container->willBeDetachedFromFrame();
}
TEST_F(ServiceWorkerContainerTest, GetRegistration_OmittedDocumentURLDefaultsToPageURL)
{
setPageURL("http://localhost/x/index.html");
StubWebServiceWorkerProvider stubProvider;
provide(stubProvider.provider());
ServiceWorkerContainer* container = ServiceWorkerContainer::create(executionContext());
{
ScriptState::Scope scriptScope(scriptState());
container->getRegistration(scriptState(), "");
EXPECT_EQ(1ul, stubProvider.getRegistrationCallCount());
EXPECT_EQ(WebURL(KURL(KURL(), "http://localhost/x/index.html")), stubProvider.getRegistrationURL());
}
container->willBeDetachedFromFrame();
}
} // namespace
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment