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

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: Do not open testing namespace for Windows build. 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
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/MockWebServiceWorkerProvider.h"
19 #include "modules/serviceworkers/ServiceWorkerContainerClient.h"
20 #include "platform/weborigin/KURL.h"
21 #include "platform/weborigin/SecurityOrigin.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 <gmock/gmock.h>
27 #include <gtest/gtest.h>
28 #include <v8.h>
29
30 using namespace WebCore;
31
32 namespace {
33
34 using testing::_;
35 using testing::AtLeast;
36 using testing::Eq;
37 using testing::InSequence;
38 using testing::Matcher;
39 using testing::MockWebServiceWorkerProvider;
40 using testing::PrintToString;
41 using testing::Return;
42 using testing::Test;
43
44 // Matches a ScriptValue and a DOMException with a specific name and message.
45 MATCHER_P2(
46 IsDOMException, name, message,
47 std::string(negation ? "isn't " : "is ") + name + " where message is " + Pri ntToString(message))
48 {
49 DOMException* exception = V8DOMException::toNativeWithTypeCheck(arg.isolate( ), arg.v8Value());
50 if (!exception) {
51 *result_listener << "where value is not a DOMException";
52 return false;
53 }
54
55 *result_listener << "where name is " << exception->name().utf8().data() << " and message is " << PrintToString(exception->message().utf8().data());
56 return exception->name() == name && exception->message() == message;
57 }
58
59 class MockScriptFunction : public ScriptFunction {
60 public:
61 MockScriptFunction(v8::Isolate* isolate) : ScriptFunction(isolate)
62 {
63 ON_CALL(*this, call(_)).WillByDefault(Return(ScriptValue()));
64 }
65
66 MOCK_METHOD1(call, ScriptValue(ScriptValue arg));
67 };
68
69 // Runs microtasks and expects |promise| to be rejected.
70 void expectRejected(ScriptPromise& promise, Matcher<ScriptValue> argumentMatcher )
71 {
72 InSequence sequence;
73 OwnPtr<MockScriptFunction> resolved = adoptPtr(new MockScriptFunction(promis e.isolate()));
74 EXPECT_CALL(*resolved, call(_)).Times(0);
75 OwnPtr<MockScriptFunction> rejected = adoptPtr(new MockScriptFunction(promis e.isolate()));
76 EXPECT_CALL(*rejected, call(argumentMatcher));
77 promise.then(resolved.release(), rejected.release());
78 promise.isolate()->RunMicrotasks();
79 }
80
81 class ServiceWorkerContainerTest : public Test {
82 protected:
83 ServiceWorkerContainerTest()
84 : m_page(DummyPageHolder::create())
85 {
86 }
87
88 ~ServiceWorkerContainerTest()
89 {
90 // It is necessary to collect garbage so that the
91 // MockScriptFunctions used by ScriptPromises are deleted and
92 // can check their expectations.
93 m_page.clear();
94 V8GCController::collectGarbage(isolate());
95 }
96
97 ExecutionContext* executionContext() { return &(m_page->document()); }
98 v8::Isolate* isolate() { return v8::Isolate::GetCurrent(); }
99 ScriptState* scriptState() { return ScriptState::forMainWorld(m_page->docume nt().frame()); }
100
101 void provide(PassOwnPtr<WebServiceWorkerProvider> provider)
102 {
103 m_page->document().DocumentSupplementable::provideSupplement(ServiceWork erContainerClient::supplementName(), ServiceWorkerContainerClient::create(provid er));
104 }
105
106 void setPageURL(const String& url)
107 {
108 m_page->document().setBaseURLOverride(KURL(KURL(), url));
109 m_page->document().setSecurityOrigin(SecurityOrigin::createFromString(ur l));
110 }
111
112 void testRegisterRejected(const String& scriptURL, const String& scope, Matc her<ScriptValue> rejectValue)
113 {
114 OwnPtr<MockWebServiceWorkerProvider> provider = adoptPtr(new MockWebServ iceWorkerProvider());
115 EXPECT_CALL(*provider, setClient(_));
116 // When the registration is rejected, a register call must not reach
117 // the provider.
118 EXPECT_CALL(*provider, registerServiceWorker(_, _, _)).Times(0);
119 EXPECT_CALL(*provider, setClient(Eq(nullptr))).Times(AtLeast(1));
120 provide(provider.release());
121
122 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerCont ainer::create(executionContext());
123 ScriptState::Scope scriptScope(scriptState());
124 Dictionary options = Dictionary::createEmpty(isolate());
125 EXPECT_TRUE(options.set("scope", scope));
126 ScriptPromise promise = container->registerServiceWorker(scriptState(), scriptURL, options);
127 expectRejected(promise, rejectValue);
128
129 container->willBeDetachedFromFrame();
130 }
131
132 void testUnregisterRejected(const String& scope, Matcher<ScriptValue> reject Value)
133 {
134 OwnPtr<MockWebServiceWorkerProvider> provider = adoptPtr(new MockWebServ iceWorkerProvider());
135 EXPECT_CALL(*provider, setClient(_));
136 // An unregister call must not reach the provider.
137 EXPECT_CALL(*provider, unregisterServiceWorker(_, _)).Times(0);
138 EXPECT_CALL(*provider, setClient(Eq(nullptr))).Times(AtLeast(1));
139 provide(provider.release());
140
141 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerCont ainer::create(executionContext());
142 ScriptState::Scope scriptScope(scriptState());
143 Dictionary options = Dictionary::createEmpty(isolate());
144 EXPECT_TRUE(options.set("scope", scope));
145 ScriptPromise promise = container->unregisterServiceWorker(scriptState() , scope);
146 expectRejected(promise, rejectValue);
147
148 container->willBeDetachedFromFrame();
149 }
150
151 private:
152 OwnPtr<DummyPageHolder> m_page;
153 };
154
155 TEST_F(ServiceWorkerContainerTest, Register_NonSecureOriginIsRejected)
156 {
157 setPageURL("http://www.example.com/");
158 testRegisterRejected(
159 "http://www.example.com/worker.js",
160 "http://www.example.com/*",
161 IsDOMException("SecurityError", "Service Workers are only supported over secure origins."));
162 }
163
164 TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScriptIsRejected)
165 {
166 setPageURL("https://www.example.com");
167 testRegisterRejected(
168 "https://www.example.com:8080/", // Differs by port
169 "https://www.example.com/*",
170 IsDOMException("SecurityError", "The origin of the script must match the current origin."));
171 }
172
173 TEST_F(ServiceWorkerContainerTest, Register_CrossOriginScopeIsRejected)
174 {
175 setPageURL("https://www.example.com");
176 testRegisterRejected(
177 "https://www.example.com",
178 "wss://www.example.com/*", // Differs by protocol
179 IsDOMException("SecurityError", "The scope must match the current origin ."));
180 }
181
182 TEST_F(ServiceWorkerContainerTest, Unregister_NonSecureOriginIsRejected)
183 {
184 setPageURL("http://www.example.com");
185 testUnregisterRejected(
186 "*",
187 IsDOMException("SecurityError", "Service Workers are only supported over secure origins."));
188 }
189
190 TEST_F(ServiceWorkerContainerTest, Unregister_CrossOriginScopeIsRejected)
191 {
192 setPageURL("https://www.example.com");
193 testUnregisterRejected(
194 "https://example.com/*", // Differs by host
195 IsDOMException("SecurityError", "The scope must match the current origin ."));
196 }
197
198 TEST_F(ServiceWorkerContainerTest, RegisterUnregister_NonHttpsSecureOriginDelega tesToProvider)
199 {
200 setPageURL("http://localhost/x/index.html");
201
202 OwnPtr<MockWebServiceWorkerProvider> provider = adoptPtr(new MockWebServiceW orkerProvider());
203 EXPECT_CALL(*provider, setClient(_));
204 EXPECT_CALL(
205 *provider,
206 registerServiceWorker(
207 Eq(WebURL(KURL(KURL(), "http://localhost/x/y/*"))),
208 Eq(WebURL(KURL(KURL(), "http://localhost/z/worker.js"))),
209 _));
210 EXPECT_CALL(
211 *provider,
212 unregisterServiceWorker(
213 Eq(WebURL(KURL(KURL(), "http://localhost/x/y/*"))),
214 _));
215 EXPECT_CALL(*provider, setClient(Eq(nullptr))).Times(AtLeast(1));
216 provide(provider.release());
217
218 RefPtrWillBeRawPtr<ServiceWorkerContainer> container = ServiceWorkerContaine r::create(executionContext());
219
220 // register
221 {
222 ScriptState::Scope scriptScope(scriptState());
223 Dictionary options = Dictionary::createEmpty(isolate());
224 EXPECT_TRUE(options.set("scope", "y/*"));
225 container->registerServiceWorker(scriptState(), "/z/worker.js", options) ;
226 }
227
228 // unregister
229 {
230 ScriptState::Scope scriptScope(scriptState());
231 container->unregisterServiceWorker(scriptState(), "y/*");
232 }
233
234 container->willBeDetachedFromFrame();
235 }
236
237 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698