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) { ...@@ -36,4 +36,13 @@ async_test(function(t) {
})); }));
}, 'Exceedingly long scope URLs are rejected by unregister()'); }, '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> </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 @@ ...@@ -54,6 +54,28 @@
namespace blink { 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) ServiceWorkerContainer* ServiceWorkerContainer::create(ExecutionContext* executionContext)
{ {
return new ServiceWorkerContainer(executionContext); return new ServiceWorkerContainer(executionContext);
...@@ -163,6 +185,32 @@ ScriptPromise ServiceWorkerContainer::unregisterServiceWorker(ScriptState* scrip ...@@ -163,6 +185,32 @@ ScriptPromise ServiceWorkerContainer::unregisterServiceWorker(ScriptState* scrip
return promise; 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() ServiceWorkerContainer::ReadyProperty* ServiceWorkerContainer::createReadyProperty()
{ {
return new ReadyProperty(executionContext(), this, ReadyProperty::Ready); return new ReadyProperty(executionContext(), this, ReadyProperty::Ready);
......
...@@ -71,6 +71,7 @@ public: ...@@ -71,6 +71,7 @@ public:
ScriptPromise registerServiceWorker(ScriptState*, const String& pattern, const Dictionary&); ScriptPromise registerServiceWorker(ScriptState*, const String& pattern, const Dictionary&);
ScriptPromise unregisterServiceWorker(ScriptState*, const String& scope); ScriptPromise unregisterServiceWorker(ScriptState*, const String& scope);
ScriptPromise getRegistration(ScriptState*, const String& documentURL);
// WebServiceWorkerProviderClient overrides. // WebServiceWorkerProviderClient overrides.
virtual void setController(WebServiceWorker*) OVERRIDE; virtual void setController(WebServiceWorker*) OVERRIDE;
......
...@@ -38,4 +38,5 @@ ...@@ -38,4 +38,5 @@
[CallWith=ScriptState, ImplementedAs=registerServiceWorker] Promise register(ScalarValueString url, optional Dictionary options); [CallWith=ScriptState, ImplementedAs=registerServiceWorker] Promise register(ScalarValueString url, optional Dictionary options);
[CallWith=ScriptState, ImplementedAs=unregisterServiceWorker] Promise unregister(optional ScalarValueString scope = "/"); [CallWith=ScriptState, ImplementedAs=unregisterServiceWorker] Promise unregister(optional ScalarValueString scope = "/");
[CallWith=ScriptState] Promise getRegistration(optional ScalarValueString documentURL = "");
}; };
...@@ -205,6 +205,18 @@ protected: ...@@ -205,6 +205,18 @@ protected:
container->willBeDetachedFromFrame(); 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: private:
OwnPtr<DummyPageHolder> m_page; OwnPtr<DummyPageHolder> m_page;
}; };
...@@ -252,11 +264,28 @@ TEST_F(ServiceWorkerContainerTest, Unregister_CrossOriginScopeIsRejected) ...@@ -252,11 +264,28 @@ TEST_F(ServiceWorkerContainerTest, Unregister_CrossOriginScopeIsRejected)
ExpectDOMException("SecurityError", "The scope must match the current origin.")); 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 { class StubWebServiceWorkerProvider {
public: public:
StubWebServiceWorkerProvider() StubWebServiceWorkerProvider()
: m_registerCallCount(0) : m_registerCallCount(0)
, m_unregisterCallCount(0) , m_unregisterCallCount(0)
, m_getRegistrationCallCount(0)
{ {
} }
...@@ -274,6 +303,8 @@ public: ...@@ -274,6 +303,8 @@ public:
const WebURL& registerScriptURL() { return m_registerScriptURL; } const WebURL& registerScriptURL() { return m_registerScriptURL; }
size_t unregisterCallCount() { return m_unregisterCallCount; } size_t unregisterCallCount() { return m_unregisterCallCount; }
const WebURL& unregisterScope() { return m_unregisterScope; } const WebURL& unregisterScope() { return m_unregisterScope; }
size_t getRegistrationCallCount() { return m_getRegistrationCallCount; }
const WebURL& getRegistrationURL() { return m_getRegistrationURL; }
private: private:
class WebServiceWorkerProviderImpl : public WebServiceWorkerProvider { class WebServiceWorkerProviderImpl : public WebServiceWorkerProvider {
...@@ -300,10 +331,18 @@ private: ...@@ -300,10 +331,18 @@ private:
m_unregistrationCallbacksToDelete.append(adoptPtr(callbacks)); 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: private:
StubWebServiceWorkerProvider& m_owner; StubWebServiceWorkerProvider& m_owner;
Vector<OwnPtr<WebServiceWorkerRegistrationCallbacks> > m_registrationCallbacksToDelete; Vector<OwnPtr<WebServiceWorkerRegistrationCallbacks> > m_registrationCallbacksToDelete;
Vector<OwnPtr<WebServiceWorkerUnregistrationCallbacks> > m_unregistrationCallbacksToDelete; Vector<OwnPtr<WebServiceWorkerUnregistrationCallbacks> > m_unregistrationCallbacksToDelete;
Vector<OwnPtr<WebServiceWorkerGetRegistrationCallbacks> > m_getRegistrationCallbacksToDelete;
}; };
private: private:
...@@ -312,6 +351,8 @@ private: ...@@ -312,6 +351,8 @@ private:
WebURL m_registerScriptURL; WebURL m_registerScriptURL;
size_t m_unregisterCallCount; size_t m_unregisterCallCount;
WebURL m_unregisterScope; WebURL m_unregisterScope;
size_t m_getRegistrationCallCount;
WebURL m_getRegistrationURL;
}; };
TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelegatesToProvider) TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelegatesToProvider)
...@@ -347,5 +388,24 @@ TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelega ...@@ -347,5 +388,24 @@ TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelega
container->willBeDetachedFromFrame(); 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
} // namespace blink } // 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