OLD | NEW |
| (Empty) |
1 var SCOPE = BASE_ORIGIN + | |
2 '/fetch/resources/fetch-access-control-iframe.html?' + TEST_OPTIONS; | |
3 var IFRAME_ORIGIN = BASE_ORIGIN; | |
4 var BASE_URL = BASE_ORIGIN + | |
5 '/serviceworker/resources/fetch-access-control.php?'; | |
6 var OTHER_BASE_URL = OTHER_ORIGIN + | |
7 '/serviceworker/resources/fetch-access-control.php?'; | |
8 var REDIRECT_URL = BASE_ORIGIN + | |
9 '/serviceworker/resources/redirect.php?Redirect='; | |
10 var OTHER_REDIRECT_URL = OTHER_ORIGIN + | |
11 '/serviceworker/resources/redirect.php?Redirect='; | |
12 var REDIRECT_LOOP_URL = BASE_ORIGIN + | |
13 '/fetch/resources/redirect-loop.php?Redirect='; | |
14 var OTHER_REDIRECT_LOOP_URL = OTHER_ORIGIN + | |
15 '/fetch/resources/redirect-loop.php?Redirect='; | |
16 var IFRAME_URL = SCOPE; | |
17 var WORKER_URL = BASE_ORIGIN + | |
18 '/fetch/resources/fetch-access-control-worker.js?' + | |
19 TEST_OPTIONS; | |
20 | |
21 function onlyOnServiceWorkerProxiedTest(checkFuncs) { | |
22 return []; | |
23 } | |
24 | |
25 // Functions to check the result from the ServiceWorker. | |
26 var checkFetchResult = function(expected, url, data) { | |
27 assert_equals(data.fetchResult, expected, url + ' should be ' + expected); | |
28 }; | |
29 var checkFetchResponseBody = function(hasBody, url, data) { | |
30 assert_equals(data.fetchResult, | |
31 'resolved', | |
32 'fetchResult must be resolved. url: ' + url); | |
33 if (hasBody) { | |
34 assert_not_equals(data.body, '', | |
35 'response must have body. url: ' + url); | |
36 } else { | |
37 assert_equals(data.body, '', | |
38 'response must not have body. url: ' + url); | |
39 } | |
40 }; | |
41 var checkFetchResponseHeader = function(name, expected, url, data) { | |
42 assert_equals(data.fetchResult, | |
43 'resolved', | |
44 'fetchResult must be resolved. url: ' + url); | |
45 var exist = false; | |
46 for (var i = 0; i < data.headers.length; ++i) { | |
47 if (data.headers[i][0] === name) { | |
48 exist = true; | |
49 } | |
50 } | |
51 assert_equals(exist, | |
52 expected, | |
53 'header check failed url: ' + url + ' name: ' + name); | |
54 }; | |
55 var checkFetchResponseType = function(type, url, data) { | |
56 assert_equals(data.fetchResult, | |
57 'resolved', | |
58 'fetchResult must be resolved. url = ' + url); | |
59 assert_equals(data.type, | |
60 type, | |
61 'type must match. url: ' + url); | |
62 }; | |
63 | |
64 var showComment = function(url, data) { | |
65 assert_true(!data.comment, 'Show comment: ' + data.comment + ' url: ' + url); | |
66 } | |
67 | |
68 var fetchIgnored = checkFetchResult.bind(this, 'ignored'); | |
69 var fetchResolved = checkFetchResult.bind(this, 'resolved'); | |
70 var fetchRejected = checkFetchResult.bind(this, 'rejected'); | |
71 var fetchError = checkFetchResult.bind(this, 'error'); | |
72 var hasBody = checkFetchResponseBody.bind(this, true); | |
73 var noBody = checkFetchResponseBody.bind(this, false); | |
74 var hasContentLength = | |
75 checkFetchResponseHeader.bind(this, 'content-length', true); | |
76 var noContentLength = | |
77 checkFetchResponseHeader.bind(this, 'content-length', false); | |
78 var hasServerHeader = | |
79 checkFetchResponseHeader.bind(this, 'x-serviceworker-serverheader', true); | |
80 var noServerHeader = | |
81 checkFetchResponseHeader.bind(this, 'x-serviceworker-serverheader', false); | |
82 var typeBasic = checkFetchResponseType.bind(this, 'basic'); | |
83 var typeCors = checkFetchResponseType.bind(this, 'cors'); | |
84 var typeOpaque = checkFetchResponseType.bind(this, 'opaque'); | |
85 | |
86 // Functions to check the result of JSONP which is evaluated in | |
87 // fetch-access-control-iframe.html by appending <script> element. | |
88 var checkJsonpResult = function(expected, url, data) { | |
89 assert_equals(data.jsonpResult, | |
90 expected, | |
91 url + ' jsonpResult should match'); | |
92 }; | |
93 var checkJsonpHeader = function(name, value, url, data) { | |
94 assert_equals(data.jsonpResult, | |
95 'success', | |
96 url + ' jsonpResult must be success'); | |
97 assert_equals(data.headers[name], | |
98 value, | |
99 'Request header check failed url:' + url + ' name:' + name); | |
100 }; | |
101 var checkJsonpMethod = function(method, url, data) { | |
102 assert_equals(data.jsonpResult, | |
103 'success', | |
104 url + ' jsonpResult must be success'); | |
105 assert_equals(data.method, | |
106 method, | |
107 'Method must match url:' + url); | |
108 }; | |
109 var checkJsonpAuth = function(username, password, cookie, url, data) { | |
110 assert_equals(data.jsonpResult, | |
111 'success', | |
112 url + ' jsonpResult must be success'); | |
113 assert_equals(data.username, | |
114 username, | |
115 'Username must match. url: ' + url); | |
116 assert_equals(data.password, | |
117 password, | |
118 'Password must match. url: ' + url); | |
119 assert_equals(data.cookie, | |
120 cookie, | |
121 'Cookie must match. url: ' + url); | |
122 }; | |
123 var checkJsonpCookie = function(cookie, url, data) { | |
124 assert_equals(data.jsonpResult, | |
125 'success', | |
126 url + ' jsonpResult must be success'); | |
127 assert_equals(data.cookie, | |
128 cookie, | |
129 'Cookie must match. url: ' + url); | |
130 }; | |
131 var checkJsonpError = checkJsonpResult.bind(this, 'error'); | |
132 var checkJsonpSuccess = checkJsonpResult.bind(this, 'success'); | |
133 var checkJsonpNoRedirect = checkJsonpResult.bind(this, 'noredirect'); | |
134 var hasCustomHeader = | |
135 checkJsonpHeader.bind(this, 'x-serviceworker-test', 'test'); | |
136 var hasCustomHeader2 = function(url, data) { | |
137 checkJsonpHeader('x-serviceworker-s', 'test1', url, data); | |
138 checkJsonpHeader('x-serviceworker-test', 'test2,test3', url, data); | |
139 checkJsonpHeader('x-serviceworker-ua', 'test4', url, data); | |
140 checkJsonpHeader('x-serviceworker-u', 'test5', url, data); | |
141 checkJsonpHeader('x-serviceworker-v', 'test6', url, data); | |
142 }; | |
143 var noCustomHeader = | |
144 checkJsonpHeader.bind(this, 'x-serviceworker-test', undefined); | |
145 var methodIsGET = checkJsonpMethod.bind(this, 'GET'); | |
146 var methodIsPOST = checkJsonpMethod.bind(this, 'POST'); | |
147 var methodIsPUT = checkJsonpMethod.bind(this, 'PUT'); | |
148 var methodIsXXX = checkJsonpMethod.bind(this, 'XXX'); | |
149 var authCheckNone = | |
150 checkJsonpAuth.bind(this, 'undefined', 'undefined', 'undefined'); | |
151 var authCheck1 = checkJsonpAuth.bind(this, 'username1', 'password1', 'cookie1'); | |
152 var authCheck2 = checkJsonpAuth.bind(this, 'username2', 'password2', 'cookie2'); | |
153 | |
154 var cookieCheck1 = checkJsonpCookie.bind(this, 'cookie1'); | |
155 var cookieCheck2 = checkJsonpCookie.bind(this, 'cookie2'); | |
156 var cookieCheckA = checkJsonpCookie.bind(this, 'cookieA'); | |
157 var cookieCheckB = checkJsonpCookie.bind(this, 'cookieB'); | |
158 var cookieCheckC = checkJsonpCookie.bind(this, 'cookieC'); | |
159 var cookieCheckNone = checkJsonpCookie.bind(this, 'undefined'); | |
160 | |
161 if (location.href.indexOf('base-https') >= 0) | |
162 authCheck1 = checkJsonpAuth.bind(this, 'username1s', 'password1s', 'cookie1'); | |
163 | |
164 if (location.href.indexOf('other-https') >= 0) | |
165 authCheck2 = checkJsonpAuth.bind(this, 'username2s', 'password2s', 'cookie2'); | |
166 | |
167 function executeServiceWorkerProxiedTests(test_targets) { | |
168 var test = async_test('Verify access control of fetch() in a Service Worker'); | |
169 test.step(function() { | |
170 var worker = undefined; | |
171 var frameWindow = {}; | |
172 var counter = 0; | |
173 window.addEventListener('message', test.step_func(onMessage), false); | |
174 | |
175 Promise.resolve() | |
176 .then(function() { | |
177 return service_worker_unregister_and_register(test, | |
178 WORKER_URL, | |
179 SCOPE); | |
180 }) | |
181 .then(function(registration) { | |
182 worker = registration.installing; | |
183 var messageChannel = new MessageChannel(); | |
184 messageChannel.port1.onmessage = test.step_func(onWorkerMessage); | |
185 worker.postMessage( | |
186 {port: messageChannel.port2}, [messageChannel.port2]); | |
187 return wait_for_state(test, worker, 'activated'); | |
188 }) | |
189 .then(function() { | |
190 return with_iframe(SCOPE); | |
191 }) | |
192 .then(function(frame) { | |
193 frameWindow = frame.contentWindow; | |
194 // Start tests. | |
195 loadNext(); | |
196 }) | |
197 .catch(unreached_rejection(test)); | |
198 | |
199 var readyFromWorkerReceived = undefined; | |
200 var resultFromWorkerReceived = undefined; | |
201 var resultFromIframeReceived = undefined; | |
202 | |
203 function onMessage(e) { | |
204 // The message is sent from fetch-access-control-iframe.html in report() | |
205 // which is called by appending <script> element which source code is | |
206 // generated by fetch-access-control.php. | |
207 if (test_targets[counter][2]) { | |
208 test_targets[counter][2].forEach(function(checkFunc) { | |
209 checkFunc.call(this, test_targets[counter][0], e.data); | |
210 }); | |
211 } | |
212 resultFromIframeReceived(); | |
213 } | |
214 | |
215 function onWorkerMessage(e) { | |
216 // The message is sent from the ServiceWorker. | |
217 var message = e.data; | |
218 if (message.msg === 'READY') { | |
219 readyFromWorkerReceived(); | |
220 return; | |
221 } | |
222 var checks = test_targets[counter][1].concat(showComment); | |
223 checks.forEach(function(checkFunc) { | |
224 checkFunc.call(this, test_targets[counter][0], message); | |
225 }); | |
226 resultFromWorkerReceived(); | |
227 } | |
228 | |
229 function loadNext() { | |
230 var workerPromise = new Promise(function(resolve, reject) { | |
231 resultFromWorkerReceived = resolve; | |
232 }); | |
233 var iframePromise = new Promise(function(resolve, reject) { | |
234 resultFromIframeReceived = resolve; | |
235 }); | |
236 Promise.all([workerPromise, iframePromise]) | |
237 .then(test.step_func(function() { | |
238 ++counter; | |
239 if (counter === test_targets.length) { | |
240 service_worker_unregister_and_done(test, SCOPE); | |
241 } else { | |
242 loadNext(); | |
243 } | |
244 })); | |
245 (new Promise(function(resolve, reject) { | |
246 readyFromWorkerReceived = resolve; | |
247 worker.postMessage({msg: 'START TEST CASE'}); | |
248 })) | |
249 .then(test.step_func(function() { | |
250 frameWindow.postMessage( | |
251 {url: test_targets[counter][0]}, | |
252 IFRAME_ORIGIN); | |
253 })); | |
254 } | |
255 }); | |
256 } | |
257 | |
258 function getQueryParams(url) { | |
259 var search = (new URL(url)).search; | |
260 if (!search) { | |
261 return {}; | |
262 } | |
263 var ret = {}; | |
264 var params = search.substring(1).split('&'); | |
265 params.forEach(function(param) { | |
266 var element = param.split('='); | |
267 ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]); | |
268 }); | |
269 return ret; | |
270 } | |
271 | |
272 function getRequestInit(params) { | |
273 var init = {}; | |
274 if (params['method']) { | |
275 init['method'] = params['method']; | |
276 } | |
277 if (params['mode']) { | |
278 init['mode'] = params['mode']; | |
279 } | |
280 if (params['credentials']) { | |
281 init['credentials'] = params['credentials']; | |
282 } | |
283 if (params['headers'] === 'CUSTOM') { | |
284 init['headers'] = {'X-ServiceWorker-Test': 'test'}; | |
285 } else if (params['headers'] === 'CUSTOM2') { | |
286 init['headers'] = [['X-ServiceWorker-Test', 'test2'], | |
287 ['X-ServiceWorker-ua', 'test4'], | |
288 ['X-ServiceWorker-V', 'test6'], | |
289 ['X-ServiceWorker-s', 'test1'], | |
290 ['X-ServiceWorker-Test', 'test3'], | |
291 ['X-ServiceWorker-U', 'test5']]; | |
292 } else if (params['headers'] === '{}') { | |
293 init['headers'] = {}; | |
294 } | |
295 return init; | |
296 } | |
297 | |
298 function headersToArray(headers) { | |
299 var ret = []; | |
300 | |
301 // Workaround for Firefox. iterable is not implemented yet. | |
302 // This is used only by checkFetchResponseHeader, and | |
303 // checkFetchResponseHeader is used only for the header names listed below. | |
304 // FIXME: Replace it with the original code below when Firefox supports | |
305 // iterable. | |
306 ['content-length', 'x-serviceworker-serverheader'].forEach(function(name) { | |
307 for (var value of headers.getAll(name)) | |
308 ret.push([name, value]); | |
309 }); | |
310 | |
311 // Original code: | |
312 // for (var header of headers) { | |
313 // ret.push(header); | |
314 // } | |
315 | |
316 return ret; | |
317 } | |
318 | |
319 function doFetch(request) { | |
320 var originalURL = request.url; | |
321 var params = getQueryParams(originalURL); | |
322 var init = getRequestInit(params); | |
323 var url = params['url']; | |
324 try { | |
325 if (url) { | |
326 request = new Request(url, init); | |
327 } else { | |
328 request = new Request(request, init); | |
329 } | |
330 var response; | |
331 return fetch(request) | |
332 .then(function(res) { | |
333 response = res; | |
334 return res.clone().text() | |
335 .then(function(body) { | |
336 return Promise.resolve({ | |
337 // Setting a comment will fail the test and show the | |
338 // comment in the result. Use this for debugging | |
339 // tests. | |
340 comment: undefined, | |
341 | |
342 fetchResult: 'resolved', | |
343 body: body, | |
344 status: response.status, | |
345 headers: headersToArray(response.headers), | |
346 type: response.type, | |
347 response: response, | |
348 originalURL: originalURL | |
349 }); | |
350 }) | |
351 .catch(function(e) { | |
352 return Promise.resolve({fetchResult: 'error'}); | |
353 }); | |
354 }) | |
355 .catch(function(e) { | |
356 return Promise.resolve({fetchResult: 'rejected'}); | |
357 }); | |
358 } catch (e) { | |
359 return Promise.resolve({fetchResult: 'error'}); | |
360 } | |
361 } | |
362 | |
363 var report_data = {}; | |
364 function report(data) { | |
365 report_data = data; | |
366 } | |
367 | |
368 function executeTest(test_target) { | |
369 if (test_target.length == 0) { | |
370 return Promise.resolve(); | |
371 } | |
372 return doFetch(new Request(test_target[0], | |
373 {credentials: 'same-origin', mode: 'no-cors'})) | |
374 .then(function(message) { | |
375 var checks = test_target[1].concat(showComment); | |
376 checks.forEach(function(checkFunc) { | |
377 checkFunc.call(this, test_target[0], message); | |
378 }); | |
379 | |
380 if (test_target[2]) { | |
381 report_data = {}; | |
382 if (message.fetchResult !== 'resolved' || | |
383 message.body === '' || | |
384 400 <= message.status) { | |
385 report({jsonpResult:'error'}); | |
386 } else { | |
387 eval(message.body); | |
388 } | |
389 assert_not_equals(report_data, {}, 'data should be set'); | |
390 | |
391 test_target[2].forEach(function(checkFunc) { | |
392 checkFunc.call(this, test_target[0], report_data); | |
393 }); | |
394 } | |
395 }); | |
396 } | |
397 | |
398 function executeTests(test_targets) { | |
399 for (var i = 0; i < test_targets.length; ++i) { | |
400 sequential_promise_test( | |
401 function(counter, t) { | |
402 return executeTest(test_targets[counter]); | |
403 }.bind(this, i), | |
404 "executeTest-" + i); | |
405 } | |
406 } | |
OLD | NEW |