Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: Source/modules/serviceworkers/ServiceWorkerContainerTest.cpp

Issue 400903002: Check that Service Workers are registered from secure origins. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: using namespace blink. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/modules/serviceworkers/ServiceWorkerContainer.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "modules/serviceworkers/ServiceWorkerContainer.h"
7
8 #include "bindings/core/v8/Dictionary.h"
9 #include "bindings/core/v8/ScriptFunction.h"
10 #include "bindings/core/v8/ScriptPromise.h"
11 #include "bindings/core/v8/ScriptState.h"
12 #include "bindings/core/v8/V8DOMException.h"
13 #include "bindings/core/v8/V8GCController.h"
14 #include "core/dom/DOMException.h"
15 #include "core/dom/Document.h"
16 #include "core/dom/ExecutionContext.h"
17 #include "core/testing/DummyPageHolder.h"
18 #include "modules/serviceworkers/ServiceWorkerContainerClient.h"
19 #include "platform/weborigin/KURL.h"
20 #include "platform/weborigin/SecurityOrigin.h"
21 #include "public/platform/WebServiceWorkerProvider.h"
22 #include "public/platform/WebURL.h"
23 #include "wtf/OwnPtr.h"
24 #include "wtf/PassOwnPtr.h"
25 #include "wtf/text/WTFString.h"
26 #include <gtest/gtest.h>
27 #include <v8.h>
28
29 using namespace blink;
30
31 namespace {
32
33 // Promise-related test support.
34
35 struct StubScriptFunction {
36 public:
37 StubScriptFunction()
38 : m_callCount(0)
39 {
40 }
41
42 // The returned ScriptFunction can outlive the StubScriptFunction,
43 // but it should not be called after the StubScriptFunction dies.
44 PassOwnPtr<ScriptFunction> function(v8::Isolate* isolate)
45 {
46 return adoptPtr(new ScriptFunctionImpl(isolate, *this));
47 }
48
49 size_t callCount() { return m_callCount; }
50 ScriptValue arg() { return m_arg; }
51
52 private:
53 size_t m_callCount;
54 ScriptValue m_arg;
55
56 class ScriptFunctionImpl : public ScriptFunction {
57 public:
58 ScriptFunctionImpl(v8::Isolate* isolate, StubScriptFunction& owner)
59 : ScriptFunction(isolate)
60 , m_owner(owner)
61 {
62 }
63
64 virtual ScriptValue call(ScriptValue arg) OVERRIDE
65 {
66 m_owner.m_arg = arg;
67 m_owner.m_callCount++;
68 return ScriptValue();
69 }
70
71 private:
72 StubScriptFunction& m_owner;
73 };
74 };
75
76 class ScriptValueTest {
77 public:
78 virtual ~ScriptValueTest() { }
79 virtual void operator()(ScriptValue) const = 0;
80 };
81
82 // Runs microtasks and expects |promise| to be rejected. Calls
83 // |valueTest| with the value passed to |reject|, if any.
84 void expectRejected(ScriptPromise& promise, const ScriptValueTest& valueTest)
85 {
86 StubScriptFunction resolved, rejected;
87 promise.then(resolved.function(promise.isolate()), rejected.function(promise .isolate()));
88 promise.isolate()->RunMicrotasks();
89 EXPECT_EQ(0ul, resolved.callCount());
90 EXPECT_EQ(1ul, rejected.callCount());
91 if (rejected.callCount())
92 valueTest(rejected.arg());
93 }
94
95 // DOM-related test support.
96
97 // Matches a ScriptValue and a DOMException with a specific name and message.
98 class ExpectDOMException : public ScriptValueTest {
99 public:
100 ExpectDOMException(const String& expectedName, const String& expectedMessage )
101 : m_expectedName(expectedName)
102 , m_expectedMessage(expectedMessage)
103 {
104 }
105
106 virtual ~ExpectDOMException() OVERRIDE { }
107
108 virtual void operator()(ScriptValue value) const OVERRIDE
109 {
110 DOMException* exception = V8DOMException::toNativeWithTypeCheck(value.is olate(), value.v8Value());
111 EXPECT_TRUE(exception) << "the value should be a DOMException";
112 if (!exception)
113 return;
114 EXPECT_EQ(m_expectedName, exception->name());
115 EXPECT_EQ(m_expectedMessage, exception->message());
116 }
117
118 private:
119 String m_expectedName;
120 String m_expectedMessage;
121 };
122
123 // Service Worker-specific tests.
124
125 class NotReachedWebServiceWorkerProvider : public WebServiceWorkerProvider {
126 public:
127 virtual ~NotReachedWebServiceWorkerProvider() OVERRIDE { }
128
129 virtual void registerServiceWorker(const WebURL& pattern, const WebURL& scri ptURL, WebServiceWorkerCallbacks* callbacks) OVERRIDE
130 {
131 ADD_FAILURE() << "the provider should not be called to register a Servic e Worker";
132 delete callbacks;
133 }
134
135 virtual void unregisterServiceWorker(const WebURL& pattern, WebServiceWorker Callbacks* callbacks) OVERRIDE
136 {
137 ADD_FAILURE() << "the provider should not be called to unregister a Serv ice Worker";
138 delete callbacks;
139 }
140 };
141
142 class ServiceWorkerContainerTest : public ::testing::Test {
143 protected:
144 ServiceWorkerContainerTest()
145 : m_page(DummyPageHolder::create())
146 {
147 }
148
149 ~ServiceWorkerContainerTest()
150 {
151 m_page.clear();
152 V8GCController::collectGarbage(isolate());
153 }
154
155 ExecutionContext* executionContext() { return &(m_page->document()); }
156 v8::Isolate* isolate() { return v8::Isolate::GetCurrent(); }
157 ScriptState* scriptState() { return ScriptState::forMainWorld(m_page->docume nt().frame()); }
158
159 void provide(PassOwnPtr<WebServiceWorkerProvider> provider)
160 {
161 m_page->document().DocumentSupplementable::provideSupplement(ServiceWork erContainerClient::supplementName(), ServiceWorkerContainerClient::create(provid er));
162 }
163
164 void setPageURL(const String& url)
165 {
166 // For URL completion.
167 m_page->document().setBaseURLOverride(KURL(KURL(), url));
168
169 // The basis for security checks.
170 m_page->document().setSecurityOrigin(SecurityOrigin::createFromString(ur l));
171 }
172
173 void testRegisterRejected(const String& scriptURL, const String& scope, cons t ScriptValueTest& valueTest)
174 {
175 // When the registration is rejected, a register call must not reach
176 // the provider.
177 provide(adoptPtr(new NotReachedWebServiceWorkerProvider()));
178
179 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerCont ainer::create(executionContext());
180 ScriptState::Scope scriptScope(scriptState());
181 Dictionary options = Dictionary::createEmpty(isolate());
182 EXPECT_TRUE(options.set("scope", scope));
183 ScriptPromise promise = container->registerServiceWorker(scriptState(), scriptURL, options);
184 expectRejected(promise, valueTest);
185
186 container->willBeDetachedFromFrame();
187 }
188
189 void testUnregisterRejected(const String& scope, const ScriptValueTest& valu eTest)
190 {
191 provide(adoptPtr(new NotReachedWebServiceWorkerProvider()));
192
193 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerCont ainer::create(executionContext());
194 ScriptState::Scope scriptScope(scriptState());
195 Dictionary options = Dictionary::createEmpty(isolate());
196 EXPECT_TRUE(options.set("scope", scope));
197 ScriptPromise promise = container->unregisterServiceWorker(scriptState() , scope);
198 expectRejected(promise, valueTest);
199
200 container->willBeDetachedFromFrame();
201 }
202
203 private:
204 OwnPtr<DummyPageHolder> m_page;
205 };
206
207 TEST_F(ServiceWorkerContainerTest, Register_NonSecureOriginIsRejected)
208 {
209 setPageURL("http://www.example.com/");
210 testRegisterRejected(
211 "http://www.example.com/worker.js",
212 "http://www.example.com/*",
213 ExpectDOMException("SecurityError", "Service Workers are only supported over secure origins."));
214 }
215
216 TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScriptIsRejected)
217 {
218 setPageURL("https://www.example.com");
219 testRegisterRejected(
220 "https://www.example.com:8080/", // Differs by port
221 "https://www.example.com/*",
222 ExpectDOMException("SecurityError", "The origin of the script must match the current origin."));
223 }
224
225 TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScopeIsRejected)
226 {
227 setPageURL("https://www.example.com");
228 testRegisterRejected(
229 "https://www.example.com",
230 "wss://www.example.com/*", // Differs by protocol
231 ExpectDOMException("SecurityError", "The scope must match the current or igin."));
232 }
233
234 TEST_F(ServiceWorkerContainerTest, Unregister_NonSecureOriginIsRejected)
235 {
236 setPageURL("http://www.example.com");
237 testUnregisterRejected(
238 "*",
239 ExpectDOMException("SecurityError", "Service Workers are only supported over secure origins."));
240 }
241
242 TEST_F(ServiceWorkerContainerTest, Unregister_CrossOriginScopeIsRejected)
243 {
244 setPageURL("https://www.example.com");
245 testUnregisterRejected(
246 "https://example.com/*", // Differs by host
247 ExpectDOMException("SecurityError", "The scope must match the current or igin."));
248 }
249
250 class StubWebServiceWorkerProvider {
251 public:
252 StubWebServiceWorkerProvider()
253 : m_registerCallCount(0)
254 , m_unregisterCallCount(0)
255 {
256 }
257
258 // Creates a WebServiceWorkerProvider. This can outlive the
259 // StubWebServiceWorkerProvider, but |registerServiceWorker| and
260 // other methods must not be called after the
261 // StubWebServiceWorkerProvider dies.
262 PassOwnPtr<WebServiceWorkerProvider> provider()
263 {
264 return adoptPtr(new WebServiceWorkerProviderImpl(*this));
265 }
266
267 size_t registerCallCount() { return m_registerCallCount; }
268 const WebURL& registerScope() { return m_registerScope; }
269 const WebURL& registerScriptURL() { return m_registerScriptURL; }
270 size_t unregisterCallCount() { return m_unregisterCallCount; }
271 const WebURL& unregisterScope() { return m_unregisterScope; }
272
273 private:
274 class WebServiceWorkerProviderImpl : public WebServiceWorkerProvider {
275 public:
276 WebServiceWorkerProviderImpl(StubWebServiceWorkerProvider& owner)
277 : m_owner(owner)
278 {
279 }
280
281 virtual ~WebServiceWorkerProviderImpl() OVERRIDE { }
282
283 virtual void registerServiceWorker(const WebURL& pattern, const WebURL& scriptURL, WebServiceWorkerCallbacks* callbacks) OVERRIDE
284 {
285 m_owner.m_registerCallCount++;
286 m_owner.m_registerScope = pattern;
287 m_owner.m_registerScriptURL = scriptURL;
288 m_callbacksToDelete.append(adoptPtr(callbacks));
289 }
290
291 virtual void unregisterServiceWorker(const WebURL& pattern, WebServiceWo rkerCallbacks* callbacks) OVERRIDE
292 {
293 m_owner.m_unregisterCallCount++;
294 m_owner.m_unregisterScope = pattern;
295 m_callbacksToDelete.append(adoptPtr(callbacks));
296 }
297
298 private:
299 StubWebServiceWorkerProvider& m_owner;
300 Vector<OwnPtr<WebServiceWorkerCallbacks> > m_callbacksToDelete;
301 };
302
303 private:
304 size_t m_registerCallCount;
305 WebURL m_registerScope;
306 WebURL m_registerScriptURL;
307 size_t m_unregisterCallCount;
308 WebURL m_unregisterScope;
309 };
310
311 TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelega tesToProvider)
312 {
313 setPageURL("http://localhost/x/index.html");
314
315 StubWebServiceWorkerProvider stubProvider;
316 provide(stubProvider.provider());
317
318 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerContaine r::create(executionContext());
319
320 // register
321 {
322 ScriptState::Scope scriptScope(scriptState());
323 Dictionary options = Dictionary::createEmpty(isolate());
324 EXPECT_TRUE(options.set("scope", "y/*"));
325 container->registerServiceWorker(scriptState(), "/z/worker.js", options) ;
326
327 EXPECT_EQ(1ul, stubProvider.registerCallCount());
328 EXPECT_EQ(WebURL(KURL(KURL(), "http://localhost/x/y/*")), stubProvider.r egisterScope());
329 EXPECT_EQ(WebURL(KURL(KURL(), "http://localhost/z/worker.js")), stubProv ider.registerScriptURL());
330 }
331
332 // unregister
333 {
334 ScriptState::Scope scriptScope(scriptState());
335 container->unregisterServiceWorker(scriptState(), "y/*");
336
337 EXPECT_EQ(1ul, stubProvider.unregisterCallCount());
338 EXPECT_EQ(WebURL(KURL(KURL(), "http://localhost/x/y/*")), stubProvider.u nregisterScope());
339 }
340
341 container->willBeDetachedFromFrame();
342 }
343
344 } // namespace
OLDNEW
« no previous file with comments | « Source/modules/serviceworkers/ServiceWorkerContainer.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698