diff --git a/service-workers/service-worker/navigation-redirect-resolution.https.html b/service-workers/service-worker/navigation-redirect-resolution.https.html
new file mode 100644
index 00000000000000..59e1cafec3484a
--- /dev/null
+++ b/service-workers/service-worker/navigation-redirect-resolution.https.html
@@ -0,0 +1,58 @@
+
+
Service Worker: Navigation Redirect Resolution
+
+
+
+
+
+
diff --git a/service-workers/service-worker/redirected-response.https.html b/service-workers/service-worker/redirected-response.https.html
index d2c7858beddfcf..71b35d0e120506 100644
--- a/service-workers/service-worker/redirected-response.https.html
+++ b/service-workers/service-worker/redirected-response.https.html
@@ -297,7 +297,7 @@
.then(() => {
const url = host_info['HTTPS_ORIGIN'] + base_path() +
'sample?url=' + encodeURIComponent(TARGET_URL) +
- '&original-redirect-mode=follow&sw=gen';
+ '&original-redirect-mode=manual&sw=gen';
return redirected_test({url: url,
fetch_option: {redirect: 'manual'},
fetch_method: frame.contentWindow.fetch,
@@ -307,6 +307,102 @@
}),
'mode: "manual", generated redirect response');
+// =======================================================
+// Tests for requests that are in-scope of the service worker. The service
+// worker returns a generated redirect response manually with the Response
+// constructor.
+// =======================================================
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=' + encodeURIComponent(TARGET_URL) +
+ '&original-redirect-mode=follow&sw=gen-manual';
+ return redirected_test({url: url,
+ fetch_option: {redirect: 'follow'},
+ fetch_method: frame.contentWindow.fetch,
+ expected_type: 'basic',
+ expected_redirected: true,
+ expected_intercepted_urls: [url, TARGET_URL]})
+ }),
+ 'mode: "follow", manually-generated redirect response');
+
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=' + encodeURIComponent(TARGET_URL) +
+ '&original-redirect-mode=error&sw=gen-manual';
+ return promise_rejects_js(
+ t, frame.contentWindow.TypeError,
+ frame.contentWindow.fetch(url, {redirect: 'error'}),
+ 'The generated redirect response from the service worker should ' +
+ 'be treated as an error when the redirect flag of request was' +
+ ' \'error\'.')
+ .then(() => check_intercepted_urls([url]));
+ }),
+ 'mode: "error", manually-generated redirect response');
+
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=' + encodeURIComponent(TARGET_URL) +
+ '&original-redirect-mode=manual&sw=gen-manual';
+ return redirected_test({url: url,
+ fetch_option: {redirect: 'manual'},
+ fetch_method: frame.contentWindow.fetch,
+ expected_type: 'opaqueredirect',
+ expected_redirected: false,
+ expected_intercepted_urls: [url]})
+ }),
+ 'mode: "manual", manually-generated redirect response');
+
+// =======================================================
+// Tests for requests that are in-scope of the service worker. The service
+// worker returns a generated redirect response with a relative location header.
+// Generated responses do not have URLs, so this should fail to resolve.
+// =======================================================
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=blank.html' +
+ '&original-redirect-mode=follow&sw=gen-manual';
+ return promise_rejects_js(
+ t, frame.contentWindow.TypeError,
+ frame.contentWindow.fetch(url, {redirect: 'follow'}),
+ 'Following the generated redirect response from the service worker '+
+ 'should result fail.')
+ .then(() => check_intercepted_urls([url]));
+ }),
+ 'mode: "follow", generated relative redirect response');
+
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=blank.html' +
+ '&original-redirect-mode=error&sw=gen-manual';
+ return promise_rejects_js(
+ t, frame.contentWindow.TypeError,
+ frame.contentWindow.fetch(url, {redirect: 'error'}),
+ 'The generated redirect response from the service worker should ' +
+ 'be treated as an error when the redirect flag of request was' +
+ ' \'error\'.')
+ .then(() => check_intercepted_urls([url]));
+ }),
+ 'mode: "error", generated relative redirect response');
+
+promise_test(t => setup_and_clean()
+ .then(() => {
+ const url = host_info['HTTPS_ORIGIN'] + base_path() +
+ 'sample?url=blank.html' +
+ '&original-redirect-mode=manual&sw=gen-manual';
+ return redirected_test({url: url,
+ fetch_option: {redirect: 'manual'},
+ fetch_method: frame.contentWindow.fetch,
+ expected_type: 'opaqueredirect',
+ expected_redirected: false,
+ expected_intercepted_urls: [url]})
+ }),
+ 'mode: "manual", generated relative redirect response');
+
// =======================================================
// Tests for requests that are in-scope of the service worker. The service
// worker returns a generated redirect response. And the fetch follows the
diff --git a/service-workers/service-worker/resources/fetch-rewrite-worker.js b/service-workers/service-worker/resources/fetch-rewrite-worker.js
index f2d49e2706a40f..4631e83e0ceaab 100644
--- a/service-workers/service-worker/resources/fetch-rewrite-worker.js
+++ b/service-workers/service-worker/resources/fetch-rewrite-worker.js
@@ -133,6 +133,10 @@ self.addEventListener('fetch', function(event) {
}
}
+ if (params['clone']) {
+ response = response.clone();
+ }
+
// |cache| means to bounce responses through Cache Storage and back.
if (params['cache']) {
var cacheName = "cached-fetches-" + performance.now() + "-" +
diff --git a/service-workers/service-worker/resources/redirect-worker.js b/service-workers/service-worker/resources/redirect-worker.js
index ddcc2cf5478942..82e21fc26fdb94 100644
--- a/service-workers/service-worker/resources/redirect-worker.js
+++ b/service-workers/service-worker/resources/redirect-worker.js
@@ -117,6 +117,13 @@ self.addEventListener('fetch', function(event) {
event.respondWith(waitUntilPromise.then(async () => {
if (params['sw'] == 'gen') {
return Response.redirect(params['url']);
+ } else if (params['sw'] == 'gen-manual') {
+ // Note this differs from Response.redirect() in that relative URLs are
+ // preserved.
+ return new Response("", {
+ status: 301,
+ headers: {location: params['url']},
+ });
} else if (params['sw'] == 'fetch') {
return fetch(event.request);
} else if (params['sw'] == 'fetch-url') {
diff --git a/service-workers/service-worker/resources/subdir/blank.html b/service-workers/service-worker/resources/subdir/blank.html
new file mode 100644
index 00000000000000..a3c3a4689a62b4
--- /dev/null
+++ b/service-workers/service-worker/resources/subdir/blank.html
@@ -0,0 +1,2 @@
+
+Empty doc