Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /service-workers/service-worker/fetch-response-taint.https.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<title>Service Worker: Tainting of responses fetched via SW.</title>
<!-- This test makes a large number of requests sequentially. -->
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var host_info = get_host_info();
var BASE_ORIGIN = host_info.HTTPS_ORIGIN;
var OTHER_ORIGIN = host_info.HTTPS_REMOTE_ORIGIN;
var BASE_URL = BASE_ORIGIN + base_path() +
'resources/fetch-access-control.py?';
var OTHER_BASE_URL = OTHER_ORIGIN + base_path() +
'resources/fetch-access-control.py?';
function frame_fetch(frame, url, mode, credentials) {
var foreignPromise = frame.contentWindow.fetch(
new Request(url, {mode: mode, credentials: credentials}))
// Event loops should be shared between contexts of similar origin, not all
// browsers adhere to this expectation at the time of this writing. Incorrect
// behavior in this regard can interfere with test execution when the
// provided iframe is removed from the document.
//
// WPT maintains a test dedicated the expected treatment of event loops, so
// the following workaround is acceptable in this context.
return Promise.resolve(foreignPromise);
}
var login_and_register;
promise_test(function(t) {
var SCOPE = 'resources/fetch-response-taint-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var registration;
login_and_register = login_https(t, host_info.HTTPS_ORIGIN, host_info.HTTPS_REMOTE_ORIGIN)
.then(function() {
return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
})
.then(function(r) {
registration = r;
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
// This test should not be considered complete until after the
// service worker has been unregistered. Currently, `testharness.js`
// does not support asynchronous global "tear down" logic, so this
// must be expressed using a dedicated `promise_test`. Because the
// other sub-tests in this file are declared synchronously, this
// test will be the final test executed.
promise_test(function(t) {
f.remove();
return registration.unregister();
}, 'restore global state');
return f;
});
return login_and_register;
}, 'initialize global state');
function ng_test(url, mode, credentials) {
promise_test(function(t) {
return login_and_register
.then(function(frame) {
var fetchRequest = frame_fetch(frame, url, mode, credentials);
return promise_rejects_js(t, frame.contentWindow.TypeError, fetchRequest);
});
}, 'url:\"' + url + '\" mode:\"' + mode +
'\" credentials:\"' + credentials + '\" should fail.');
}
function ok_test(url, mode, credentials, expected_type, expected_username) {
promise_test(function() {
return login_and_register.then(function(frame) {
return frame_fetch(frame, url, mode, credentials)
})
.then(function(res) {
assert_equals(res.type, expected_type, 'response type');
return res.text();
})
.then(function(text) {
if (expected_type == 'opaque') {
assert_equals(text, '');
} else {
return new Promise(function(resolve) {
var report = resolve;
// text must contain report() call.
eval(text);
})
.then(function(result) {
assert_equals(result.username, expected_username);
});
}
});
}, 'fetching url:\"' + url + '\" mode:\"' + mode +
'\" credentials:\"' + credentials + '\" should ' +
'succeed.');
}
function build_rewrite_url(origin, url, mode, credentials) {
return origin + '/?url=' + encodeURIComponent(url) + '&mode=' + mode +
'&credentials=' + credentials + '&';
}
function for_each_origin_mode_credentials(callback) {
[BASE_ORIGIN, OTHER_ORIGIN].forEach(function(origin) {
['same-origin', 'no-cors', 'cors'].forEach(function(mode) {
['omit', 'same-origin', 'include'].forEach(function(credentials) {
callback(origin, mode, credentials);
});
});
});
}
ok_test(BASE_URL, 'same-origin', 'omit', 'basic', 'undefined');
ok_test(BASE_URL, 'same-origin', 'same-origin', 'basic', 'username2s');
ok_test(BASE_URL, 'same-origin', 'include', 'basic', 'username2s');
ok_test(BASE_URL, 'no-cors', 'omit', 'basic', 'undefined');
ok_test(BASE_URL, 'no-cors', 'same-origin', 'basic', 'username2s');
ok_test(BASE_URL, 'no-cors', 'include', 'basic', 'username2s');
ok_test(BASE_URL, 'cors', 'omit', 'basic', 'undefined');
ok_test(BASE_URL, 'cors', 'same-origin', 'basic', 'username2s');
ok_test(BASE_URL, 'cors', 'include', 'basic', 'username2s');
ng_test(OTHER_BASE_URL, 'same-origin', 'omit');
ng_test(OTHER_BASE_URL, 'same-origin', 'same-origin');
ng_test(OTHER_BASE_URL, 'same-origin', 'include');
ok_test(OTHER_BASE_URL, 'no-cors', 'omit', 'opaque');
ok_test(OTHER_BASE_URL, 'no-cors', 'same-origin', 'opaque');
ok_test(OTHER_BASE_URL, 'no-cors', 'include', 'opaque');
ng_test(OTHER_BASE_URL, 'cors', 'omit');
ng_test(OTHER_BASE_URL, 'cors', 'same-origin');
ng_test(OTHER_BASE_URL, 'cors', 'include');
ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit', 'cors', 'undefined');
ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'same-origin', 'cors',
'undefined');
ng_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'include');
ok_test(OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + '&ACACredentials=true',
'cors', 'include', 'cors', 'username1s')
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin, BASE_URL, 'same-origin', 'omit');
// Fetch to the other origin with same-origin mode should fail.
if (origin == OTHER_ORIGIN && mode == 'same-origin') {
ng_test(url, mode, credentials);
} else {
// The response type from the SW should be basic
ok_test(url, mode, credentials, 'basic', 'undefined');
}
});
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin, BASE_URL, 'same-origin', 'same-origin');
// Fetch to the other origin with same-origin mode should fail.
if (origin == OTHER_ORIGIN && mode == 'same-origin') {
ng_test(url, mode, credentials);
} else {
// The response type from the SW should be basic.
ok_test(url, mode, credentials, 'basic', 'username2s');
}
});
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin, OTHER_BASE_URL, 'same-origin', 'omit');
// The response from the SW should be an error.
ng_test(url, mode, credentials);
});
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin, OTHER_BASE_URL, 'no-cors', 'omit');
// SW can respond only to no-cors requests.
if (mode != 'no-cors') {
ng_test(url, mode, credentials);
} else {
// The response type from the SW should be opaque.
ok_test(url, mode, credentials, 'opaque');
}
});
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit');
// Fetch to the other origin with same-origin mode should fail.
if (origin == OTHER_ORIGIN && mode == 'same-origin') {
ng_test(url, mode, credentials);
} else if (origin == BASE_ORIGIN && mode == 'same-origin') {
// Cors type response to a same-origin mode request should fail
ng_test(url, mode, credentials);
} else {
// The response from the SW should be cors.
ok_test(url, mode, credentials, 'cors', 'undefined');
}
});
for_each_origin_mode_credentials(function(origin, mode, credentials) {
var url = build_rewrite_url(
origin,
OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN +
'&ACACredentials=true',
'cors', 'include');
// Fetch to the other origin with same-origin mode should fail.
if (origin == OTHER_ORIGIN && mode == 'same-origin') {
ng_test(url, mode, credentials);
} else if (origin == BASE_ORIGIN && mode == 'same-origin') {
// Cors type response to a same-origin mode request should fail
ng_test(url, mode, credentials);
} else {
// The response from the SW should be cors.
ok_test(url, mode, credentials, 'cors', 'username1s');
}
});
</script>
</body>