<!DOCTYPE html>
<title>Service Worker: the fallback behavior of FetchEvent</title>
<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>
<script>
function get_fetched_urls(worker) {
return new Promise(function(resolve) {
var channel = new MessageChannel();
channel.port1.onmessage = function(msg) { resolve(msg); };
worker.postMessage({port: channel.port2}, [channel.port2]);
});
}
function check_urls(worker, expected_requests) {
return get_fetched_urls(worker)
.then(function(msg) {
var requests = msg.data.requests;
assert_object_equals(requests, expected_requests);
});
}
var path = new URL(".", window.location).pathname;
var SCOPE = 'resources/fetch-request-fallback-iframe.html';
var SCRIPT = 'resources/fetch-request-fallback-worker.js';
var host_info = get_host_info();
var BASE_URL = host_info['HTTPS_ORIGIN'] +
path + 'resources/fetch-access-control.py?';
var BASE_PNG_URL = BASE_URL + 'PNGIMAGE&';
var OTHER_BASE_URL = host_info['HTTPS_REMOTE_ORIGIN'] +
path + 'resources/fetch-access-control.py?';
var OTHER_BASE_PNG_URL = OTHER_BASE_URL + 'PNGIMAGE&';
var REDIRECT_URL = host_info['HTTPS_ORIGIN'] +
path + 'resources/redirect.py?Redirect=';
var register;
promise_test(function(t) {
var registration;
var worker;
register = service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(r) {
registration = r;
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
// 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) {
t.add_cleanup(function() {
frame.remove();
});
return registration.unregister();
}, 'restore global state');
return {frame: frame, worker: worker};
});
return register;
}, 'initialize global state');
function promise_frame_test(body, desc) {
promise_test(function(test) {
return register.then(function(result) {
return body(test, result.frame, result.worker);
});
}, desc);
}
promise_frame_test(function(t, frame, worker) {
return check_urls(
worker,
[{
url: host_info['HTTPS_ORIGIN'] + path + SCOPE,
mode: 'navigate'
}]);
}, 'The SW must intercept the request for a main resource.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.xhr(BASE_URL)
.then(function() {
return check_urls(
worker,
[{ url: BASE_URL, mode: 'cors' }]);
});
}, 'The SW must intercept the request of same origin XHR.');
promise_frame_test(function(t, frame, worker) {
return promise_rejects_js(
t,
frame.contentWindow.Error,
frame.contentWindow.xhr(OTHER_BASE_URL),
'SW fallbacked CORS-unsupported other origin XHR should fail.')
.then(function() {
return check_urls(
worker,
[{ url: OTHER_BASE_URL, mode: 'cors' }]);
});
}, 'The SW must intercept the request of CORS-unsupported other origin XHR.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.xhr(OTHER_BASE_URL + 'ACAOrigin=*')
.then(function() {
return check_urls(
worker,
[{ url: OTHER_BASE_URL + 'ACAOrigin=*', mode: 'cors' }]);
})
}, 'The SW must intercept the request of CORS-supported other origin XHR.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.xhr(
REDIRECT_URL + encodeURIComponent(BASE_URL))
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL + encodeURIComponent(BASE_URL),
mode: 'cors'
}]);
});
}, 'The SW must intercept only the first request of redirected XHR.');
promise_frame_test(function(t, frame, worker) {
return promise_rejects_js(
t,
frame.contentWindow.Error,
frame.contentWindow.xhr(
REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL)),
'SW fallbacked XHR which is redirected to CORS-unsupported ' +
'other origin should fail.')
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL),
mode: 'cors'
}]);
});
}, 'The SW must intercept only the first request for XHR which is' +
' redirected to CORS-unsupported other origin.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.xhr(
REDIRECT_URL +
encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'))
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL +
encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
mode: 'cors'
}]);
});
}, 'The SW must intercept only the first request for XHR which is ' +
'redirected to CORS-supported other origin.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(BASE_PNG_URL, '')
.then(function() {
return check_urls(
worker,
[{ url: BASE_PNG_URL, mode: 'no-cors' }]);
});
}, 'The SW must intercept the request for image.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(OTHER_BASE_PNG_URL, '')
.then(function() {
return check_urls(
worker,
[{ url: OTHER_BASE_PNG_URL, mode: 'no-cors' }]);
});
}, 'The SW must intercept the request for other origin image.');
promise_frame_test(function(t, frame, worker) {
return promise_rejects_js(
t,
frame.contentWindow.Error,
frame.contentWindow.load_image(OTHER_BASE_PNG_URL, 'anonymous'),
'SW fallbacked CORS-unsupported other origin image request ' +
'should fail.')
.then(function() {
return check_urls(
worker,
[{ url: OTHER_BASE_PNG_URL, mode: 'cors' }]);
})
}, 'The SW must intercept the request for CORS-unsupported other ' +
'origin image.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(
OTHER_BASE_PNG_URL + 'ACAOrigin=*', 'anonymous')
.then(function() {
return check_urls(
worker,
[{ url: OTHER_BASE_PNG_URL + 'ACAOrigin=*', mode: 'cors' }]);
});
}, 'The SW must intercept the request for CORS-supported other ' +
'origin image.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(
REDIRECT_URL + encodeURIComponent(BASE_PNG_URL), '')
.catch(function() {
assert_unreached(
'SW fallbacked redirected image request should succeed.');
})
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL + encodeURIComponent(BASE_PNG_URL),
mode: 'no-cors'
}]);
});
}, 'The SW must intercept only the first request for redirected ' +
'image resource.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(
REDIRECT_URL + encodeURIComponent(OTHER_BASE_PNG_URL), '')
.catch(function() {
assert_unreached(
'SW fallbacked image request which is redirected to ' +
'other origin should succeed.');
})
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL + encodeURIComponent(OTHER_BASE_PNG_URL),
mode: 'no-cors'
}]);
})
}, 'The SW must intercept only the first request for image ' +
'resource which is redirected to other origin.');
promise_frame_test(function(t, frame, worker) {
return promise_rejects_js(
t,
frame.contentWindow.Error,
frame.contentWindow.load_image(
REDIRECT_URL + encodeURIComponent(OTHER_BASE_PNG_URL),
'anonymous'),
'SW fallbacked image request which is redirected to ' +
'CORS-unsupported other origin should fail.')
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL + encodeURIComponent(OTHER_BASE_PNG_URL),
mode: 'cors'
}]);
});
}, 'The SW must intercept only the first request for image ' +
'resource which is redirected to CORS-unsupported other origin.');
promise_frame_test(function(t, frame, worker) {
return frame.contentWindow.load_image(
REDIRECT_URL +
encodeURIComponent(OTHER_BASE_PNG_URL + 'ACAOrigin=*'),
'anonymous')
.then(function() {
return check_urls(
worker,
[{
url: REDIRECT_URL +
encodeURIComponent(OTHER_BASE_PNG_URL + 'ACAOrigin=*'),
mode: 'cors'
}]);
});
}, 'The SW must intercept only the first request for image ' +
'resource which is redirected to CORS-supported other origin.');
</script>