import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1 @@
@ehsan

View file

@ -0,0 +1,2 @@
@inexorabletash
@wanderview

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<title>Cache Storage: Verify that Window and Workers see same storage</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script>
function wait_for_message(worker) {
return new Promise(function(resolve) {
worker.addEventListener('message', function listener(e) {
resolve(e.data);
worker.removeEventListener('message', listener);
});
});
}
promise_test(function(t) {
var cache_name = 'common-test';
return self.caches.delete(cache_name)
.then(function() {
var worker = new Worker('resources/common-worker.js');
worker.postMessage({name: cache_name});
return wait_for_message(worker);
})
.then(function(message) {
return self.caches.open(cache_name);
})
.then(function(cache) {
return Promise.all([
cache.match('https://example.com/a'),
cache.match('https://example.com/b'),
cache.match('https://example.com/c')
]);
})
.then(function(responses) {
return Promise.all(responses.map(
function(response) { return response.text(); }
));
})
.then(function(bodies) {
assert_equals(bodies[0], 'a',
'Body should match response put by worker');
assert_equals(bodies[1], 'b',
'Body should match response put by worker');
assert_equals(bodies[2], 'c',
'Body should match response put by worker');
});
}, 'Window sees cache puts by Worker');
</script>

View file

@ -0,0 +1,2 @@
<!DOCTYPE html>
<title>Empty doc</title>

View file

@ -0,0 +1,15 @@
self.onmessage = function(e) {
var cache_name = e.data.name;
self.caches.open(cache_name)
.then(function(cache) {
return Promise.all([
cache.put('https://example.com/a', new Response('a')),
cache.put('https://example.com/b', new Response('b')),
cache.put('https://example.com/c', new Response('c'))
]);
})
.then(function() {
self.postMessage('ok');
});
};

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Controlled frame for Cache API test with credentials</title>
<script>
function xhr(url, username, password) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest(), async = true;
xhr.open('GET', url, async, username, password);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState !== XMLHttpRequest.DONE)
return;
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.statusText));
}
};
});
}
window.onmessage = function(e) {
Promise.all(e.data.map(function(item) {
return xhr(item.name, item.username, item.password);
}))
.then(function() {
navigator.serviceWorker.controller.postMessage('keys');
navigator.serviceWorker.onmessage = function(e) {
window.parent.postMessage(e.data, '*');
};
});
};
</script>
<body>
Hello? Yes, this is iframe.
</body>

View file

@ -0,0 +1,59 @@
var cache_name = 'credentials';
function assert_equals(actual, expected, message) {
if (!Object.is(actual, expected))
throw Error(message + ': expected: ' + expected + ', actual: ' + actual);
}
self.onfetch = function(e) {
if (!/\.txt$/.test(e.request.url)) return;
var content = e.request.url;
var cache;
e.respondWith(
self.caches.open(cache_name)
.then(function(result) {
cache = result;
return cache.put(e.request, new Response(content));
})
.then(function() { return cache.match(e.request); })
.then(function(result) { return result.text(); })
.then(function(text) {
assert_equals(text, content, 'Cache.match() body should match');
})
.then(function() { return cache.matchAll(e.request); })
.then(function(results) {
assert_equals(results.length, 1, 'Should have one response');
return results[0].text();
})
.then(function(text) {
assert_equals(text, content, 'Cache.matchAll() body should match');
})
.then(function() { return self.caches.match(e.request); })
.then(function(result) { return result.text(); })
.then(function(text) {
assert_equals(text, content, 'CacheStorage.match() body should match');
})
.then(function() {
return new Response('dummy');
})
);
};
self.onmessage = function(e) {
if (e.data === 'keys') {
self.caches.open(cache_name)
.then(function(cache) { return cache.keys(); })
.then(function(requests) {
var urls = requests.map(function(request) { return request.url; });
self.clients.matchAll().then(function(clients) {
clients.forEach(function(client) {
client.postMessage(urls);
});
});
});
}
};

View file

@ -0,0 +1,2 @@
def main(request, response):
return int(request.GET["status"]), [], ""

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<title>ok</title>
<script>
window.onmessage = function(e) {
var id = e.data.id;
try {
var name = 'checkallowed';
self.caches.open(name).then(function (cache) {
self.caches.delete(name);
window.parent.postMessage({id: id, result: 'allowed'}, '*');
}).catch(function(e) {
window.parent.postMessage({id: id, result: 'denied', name: e.name, message: e.message}, '*');
});
} catch (e) {
window.parent.postMessage({id: id, result: 'unexpecteddenied', name: e.name, message: e.message}, '*');
}
};
</script>

View file

@ -0,0 +1 @@
a simple text file

View file

@ -0,0 +1,237 @@
(function() {
var next_cache_index = 1;
// Returns a promise that resolves to a newly created Cache object. The
// returned Cache will be destroyed when |test| completes.
function create_temporary_cache(test) {
var uniquifier = String(++next_cache_index);
var cache_name = self.location.pathname + '/' + uniquifier;
test.add_cleanup(function() {
self.caches.delete(cache_name);
});
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
});
}
self.create_temporary_cache = create_temporary_cache;
})();
// Runs |test_function| with a temporary unique Cache passed in as the only
// argument. The function is run as a part of Promise chain owned by
// promise_test(). As such, it is expected to behave in a manner identical (with
// the exception of the argument) to a function passed into promise_test().
//
// E.g.:
// cache_test(function(cache) {
// // Do something with |cache|, which is a Cache object.
// }, "Some Cache test");
function cache_test(test_function, description) {
promise_test(function(test) {
return create_temporary_cache(test)
.then(test_function);
}, description);
}
// A set of Request/Response pairs to be used with prepopulated_cache_test().
var simple_entries = [
{
name: 'a',
request: new Request('http://example.com/a'),
response: new Response('')
},
{
name: 'b',
request: new Request('http://example.com/b'),
response: new Response('')
},
{
name: 'a_with_query',
request: new Request('http://example.com/a?q=r'),
response: new Response('')
},
{
name: 'A',
request: new Request('http://example.com/A'),
response: new Response('')
},
{
name: 'a_https',
request: new Request('https://example.com/a'),
response: new Response('')
},
{
name: 'a_org',
request: new Request('http://example.org/a'),
response: new Response('')
},
{
name: 'cat',
request: new Request('http://example.com/cat'),
response: new Response('')
},
{
name: 'catmandu',
request: new Request('http://example.com/catmandu'),
response: new Response('')
},
{
name: 'cat_num_lives',
request: new Request('http://example.com/cat?lives=9'),
response: new Response('')
},
{
name: 'cat_in_the_hat',
request: new Request('http://example.com/cat/in/the/hat'),
response: new Response('')
},
{
name: 'non_2xx_response',
request: new Request('http://example.com/non2xx'),
response: new Response('', {status: 404, statusText: 'nope'})
},
{
name: 'error_response',
request: new Request('http://example.com/error'),
response: Response.error()
},
];
// A set of Request/Response pairs to be used with prepopulated_cache_test().
// These contain a mix of test cases that use Vary headers.
var vary_entries = [
{
name: 'vary_cookie_is_cookie',
request: new Request('http://example.com/c',
{headers: {'Cookies': 'is-for-cookie'}}),
response: new Response('',
{headers: {'Vary': 'Cookies'}})
},
{
name: 'vary_cookie_is_good',
request: new Request('http://example.com/c',
{headers: {'Cookies': 'is-good-enough-for-me'}}),
response: new Response('',
{headers: {'Vary': 'Cookies'}})
},
{
name: 'vary_cookie_absent',
request: new Request('http://example.com/c'),
response: new Response('',
{headers: {'Vary': 'Cookies'}})
}
];
// Run |test_function| with a Cache object and a map of entries. Prior to the
// call, the Cache is populated by cache entries from |entries|. The latter is
// expected to be an Object mapping arbitrary keys to objects of the form
// {request: <Request object>, response: <Response object>}. There's no
// guarantee on the order in which entries will be added to the cache.
//
// |test_function| should return a Promise that can be used with promise_test.
function prepopulated_cache_test(entries, test_function, description) {
cache_test(function(cache) {
var p = Promise.resolve();
var hash = {};
return Promise.all(entries.map(function(entry) {
hash[entry.name] = entry;
return cache.put(entry.request.clone(),
entry.response.clone())
.catch(function(e) {
assert_unreached(
'Test setup failed for entry ' + entry.name + ': ' + e);
});
}))
.then(function() {
assert_equals(Object.keys(hash).length, entries.length);
})
.then(function() {
return test_function(cache, hash);
});
}, description);
}
// Helper for testing with Headers objects. Compares Headers instances
// by serializing |expected| and |actual| to arrays and comparing.
function assert_header_equals(actual, expected, description) {
assert_class_string(actual, "Headers", description);
var header;
var actual_headers = [];
var expected_headers = [];
for (header of actual)
actual_headers.push(header[0] + ": " + header[1]);
for (header of expected)
expected_headers.push(header[0] + ": " + header[1]);
assert_array_equals(actual_headers, expected_headers,
description + " Headers differ.");
}
// Helper for testing with Response objects. Compares simple
// attributes defined on the interfaces, as well as the headers. It
// does not compare the response bodies.
function assert_response_equals(actual, expected, description) {
assert_class_string(actual, "Response", description);
["type", "url", "status", "ok", "statusText"].forEach(function(attribute) {
assert_equals(actual[attribute], expected[attribute],
description + " Attributes differ: " + attribute + ".");
});
assert_header_equals(actual.headers, expected.headers, description);
}
// Assert that the two arrays |actual| and |expected| contain the same
// set of Responses as determined by assert_response_equals. The order
// is not significant.
//
// |expected| is assumed to not contain any duplicates.
function assert_response_array_equivalent(actual, expected, description) {
assert_true(Array.isArray(actual), description);
assert_equals(actual.length, expected.length, description);
expected.forEach(function(expected_element) {
// assert_response_in_array treats the first argument as being
// 'actual', and the second as being 'expected array'. We are
// switching them around because we want to be resilient
// against the |actual| array containing duplicates.
assert_response_in_array(expected_element, actual, description);
});
}
// Asserts that two arrays |actual| and |expected| contain the same
// set of Responses as determined by assert_response_equals(). The
// corresponding elements must occupy corresponding indices in their
// respective arrays.
function assert_response_array_equals(actual, expected, description) {
assert_true(Array.isArray(actual), description);
assert_equals(actual.length, expected.length, description);
actual.forEach(function(value, index) {
assert_response_equals(value, expected[index],
description + " : object[" + index + "]");
});
}
// Equivalent to assert_in_array, but uses assert_response_equals.
function assert_response_in_array(actual, expected_array, description) {
assert_true(expected_array.some(function(element) {
try {
assert_response_equals(actual, element);
return true;
} catch (e) {
return false;
}
}), description);
}

View file

@ -0,0 +1,33 @@
/*
* testharness-helpers contains various useful extensions to testharness.js to
* allow them to be used across multiple tests before they have been
* upstreamed. This file is intended to be usable from both document and worker
* environments, so code should for example not rely on the DOM.
*/
// Returns a promise that fulfills after the provided |promise| is fulfilled.
// The |test| succeeds only if |promise| rejects with an exception matching
// |code|. Accepted values for |code| follow those accepted for assert_throws().
// The optional |description| describes the test being performed.
//
// E.g.:
// assert_promise_rejects(
// new Promise(...), // something that should throw an exception.
// 'NotFoundError',
// 'Should throw NotFoundError.');
//
// assert_promise_rejects(
// new Promise(...),
// new TypeError(),
// 'Should throw TypeError');
function assert_promise_rejects(promise, code, description) {
return promise.then(
function() {
throw 'assert_promise_rejects: ' + description + ' Promise did not reject.';
},
function(e) {
if (code !== undefined) {
assert_throws(code, function() { throw e; }, description);
}
});
}

View file

@ -0,0 +1,238 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
cache_test(function(cache) {
return assert_promise_rejects(
cache.add(),
new TypeError(),
'Cache.add should throw a TypeError when no arguments are given.');
}, 'Cache.add called with no arguments');
cache_test(function(cache) {
return cache.add('../resources/simple.txt')
.then(function(result) {
assert_equals(result, undefined,
'Cache.add should resolve with undefined on success.');
return cache.match('../resources/simple.txt');
})
.then(function(response) {
assert_class_string(response, 'Response',
'Cache.add should put a resource in the cache.');
return response.text();
})
.then(function(body) {
assert_equals(body, 'a simple text file\n',
'Cache.add should retrieve the correct body.');
});
}, 'Cache.add called with relative URL specified as a string');
cache_test(function(cache) {
return assert_promise_rejects(
cache.add('javascript://this-is-not-http-mmkay'),
new TypeError(),
'Cache.add should throw a TypeError for non-HTTP/HTTPS URLs.');
}, 'Cache.add called with non-HTTP/HTTPS URL');
cache_test(function(cache) {
var request = new Request('../resources/simple.txt');
return cache.add(request)
.then(function(result) {
assert_equals(result, undefined,
'Cache.add should resolve with undefined on success.');
});
}, 'Cache.add called with Request object');
cache_test(function(cache) {
var request = new Request('../resources/simple.txt',
{method: 'POST', body: 'This is a body.'});
return assert_promise_rejects(
cache.add(request),
new TypeError(),
'Cache.add should throw a TypeError for non-GET requests.');
}, 'Cache.add called with POST request');
cache_test(function(cache) {
var request = new Request('../resources/simple.txt');
return cache.add(request)
.then(function(result) {
assert_equals(result, undefined,
'Cache.add should resolve with undefined on success.');
})
.then(function() {
return cache.add(request);
})
.then(function(result) {
assert_equals(result, undefined,
'Cache.add should resolve with undefined on success.');
});
}, 'Cache.add called twice with the same Request object');
cache_test(function(cache) {
var request = new Request('../resources/simple.txt');
return request.text()
.then(function() {
assert_false(request.bodyUsed);
})
.then(function() {
return cache.add(request);
});
}, 'Cache.add with request with null body (not consumed)');
cache_test(function(cache) {
return assert_promise_rejects(
cache.add('this-does-not-exist-please-dont-create-it'),
new TypeError(),
'Cache.add should reject if response is !ok');
}, 'Cache.add with request that results in a status of 404');
cache_test(function(cache) {
return assert_promise_rejects(
cache.add('../resources/fetch-status.php?status=500'),
new TypeError(),
'Cache.add should reject if response is !ok');
}, 'Cache.add with request that results in a status of 500');
cache_test(function(cache) {
return assert_promise_rejects(
cache.addAll(),
new TypeError(),
'Cache.addAll with no arguments should throw TypeError.');
}, 'Cache.addAll with no arguments');
cache_test(function(cache) {
// Assumes the existence of ../resources/simple.txt and ../resources/blank.html
var urls = ['../resources/simple.txt', undefined, '../resources/blank.html'];
return assert_promise_rejects(
cache.addAll(),
new TypeError(),
'Cache.addAll should throw TypeError for an undefined argument.');
}, 'Cache.addAll with a mix of valid and undefined arguments');
cache_test(function(cache) {
return cache.addAll([])
.then(function(result) {
assert_equals(result, undefined,
'Cache.addAll should resolve with undefined on ' +
'success.');
return cache.keys();
})
.then(function(result) {
assert_equals(result.length, 0,
'There should be no entry in the cache.');
});
}, 'Cache.addAll with an empty array');
cache_test(function(cache) {
// Assumes the existence of ../resources/simple.txt and
// ../resources/blank.html
var urls = ['../resources/simple.txt',
self.location.href,
'../resources/blank.html'];
return cache.addAll(urls)
.then(function(result) {
assert_equals(result, undefined,
'Cache.addAll should resolve with undefined on ' +
'success.');
return Promise.all(
urls.map(function(url) { return cache.match(url); }));
})
.then(function(responses) {
assert_class_string(
responses[0], 'Response',
'Cache.addAll should put a resource in the cache.');
assert_class_string(
responses[1], 'Response',
'Cache.addAll should put a resource in the cache.');
assert_class_string(
responses[2], 'Response',
'Cache.addAll should put a resource in the cache.');
return Promise.all(
responses.map(function(response) { return response.text(); }));
})
.then(function(bodies) {
assert_equals(
bodies[0], 'a simple text file\n',
'Cache.add should retrieve the correct body.');
assert_equals(
bodies[2], '<!DOCTYPE html>\n<title>Empty doc</title>\n',
'Cache.add should retrieve the correct body.');
});
}, 'Cache.addAll with string URL arguments');
cache_test(function(cache) {
// Assumes the existence of ../resources/simple.txt and
// ../resources/blank.html
var urls = ['../resources/simple.txt',
self.location.href,
'../resources/blank.html'];
var requests = urls.map(function(url) {
return new Request(url);
});
return cache.addAll(requests)
.then(function(result) {
assert_equals(result, undefined,
'Cache.addAll should resolve with undefined on ' +
'success.');
return Promise.all(
urls.map(function(url) { return cache.match(url); }));
})
.then(function(responses) {
assert_class_string(
responses[0], 'Response',
'Cache.addAll should put a resource in the cache.');
assert_class_string(
responses[1], 'Response',
'Cache.addAll should put a resource in the cache.');
assert_class_string(
responses[2], 'Response',
'Cache.addAll should put a resource in the cache.');
return Promise.all(
responses.map(function(response) { return response.text(); }));
})
.then(function(bodies) {
assert_equals(
bodies[0], 'a simple text file\n',
'Cache.add should retrieve the correct body.');
assert_equals(
bodies[2], '<!DOCTYPE html>\n<title>Empty doc</title>\n',
'Cache.add should retrieve the correct body.');
});
}, 'Cache.addAll with Request arguments');
cache_test(function(cache) {
// Assumes that ../resources/simple.txt and ../resources/blank.html exist.
// The second resource does not.
var urls = ['../resources/simple.txt',
'this-resource-should-not-exist',
'../resources/blank.html'];
var requests = urls.map(function(url) {
return new Request(url);
});
return assert_promise_rejects(
cache.addAll(requests),
new TypeError(),
'Cache.addAll should reject with TypeError if any request fails')
.then(function() {
return Promise.all(urls.map(function(url) { return cache.match(url); }));
})
.then(function(matches) {
assert_array_equals(
matches,
[undefined, undefined, undefined],
'If any response fails, no response should be added to cache');
});
}, 'Cache.addAll with a mix of succeeding and failing requests');
cache_test(function(cache) {
var request = new Request('../resources/simple.txt');
return assert_promise_rejects(
cache.addAll([request, request]),
'InvalidStateError',
'Cache.addAll should throw InvalidStateError if the same request is added ' +
'twice.');
}, 'Cache.addAll called with the same Request object specified twice');
done();

View file

@ -0,0 +1,96 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
var test_url = 'https://example.com/foo';
// Construct a generic Request object. The URL is |test_url|. All other fields
// are defaults.
function new_test_request() {
return new Request(test_url);
}
// Construct a generic Response object.
function new_test_response() {
return new Response('Hello world!', { status: 200 });
}
cache_test(function(cache) {
return assert_promise_rejects(
cache.delete(),
new TypeError(),
'Cache.delete should reject with a TypeError when called with no ' +
'arguments.');
}, 'Cache.delete with no arguments');
cache_test(function(cache) {
return cache.put(new_test_request(), new_test_response())
.then(function() {
return cache.delete(test_url);
})
.then(function(result) {
assert_true(result,
'Cache.delete should resolve with "true" if an entry ' +
'was successfully deleted.');
return cache.match(test_url);
})
.then(function(result) {
assert_equals(result, undefined,
'Cache.delete should remove matching entries from cache.');
});
}, 'Cache.delete called with a string URL');
cache_test(function(cache) {
var request = new Request(test_url);
return cache.put(request, new_test_response())
.then(function() {
return cache.delete(request);
})
.then(function(result) {
assert_true(result,
'Cache.delete should resolve with "true" if an entry ' +
'was successfully deleted.');
});
}, 'Cache.delete called with a Request object');
cache_test(function(cache) {
return cache.delete(test_url)
.then(function(result) {
assert_false(result,
'Cache.delete should resolve with "false" if there ' +
'are no matching entries.');
});
}, 'Cache.delete with a non-existent entry');
var cache_entries = {
a: {
request: new Request('http://example.com/abc'),
response: new Response('')
},
b: {
request: new Request('http://example.com/b'),
response: new Response('')
},
a_with_query: {
request: new Request('http://example.com/abc?q=r'),
response: new Response('')
}
};
function prepopulated_cache_test(test_function, description) {
cache_test(function(cache) {
return Promise.all(Object.keys(cache_entries).map(function(k) {
return cache.put(cache_entries[k].request.clone(),
cache_entries[k].response.clone());
}))
.then(function() {
return test_function(cache);
});
}, description);
}
done();

View file

@ -0,0 +1,193 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match('not-present-in-the-cache')
.then(function(result) {
assert_equals(result, undefined,
'Cache.match failures should resolve with undefined.');
});
}, 'Cache.match with no matching entries');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(entries.a.request.url)
.then(function(result) {
assert_response_equals(result, entries.a.response,
'Cache.match should match by URL.');
});
}, 'Cache.match with URL');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(entries.a.request)
.then(function(result) {
assert_response_equals(result, entries.a.response,
'Cache.match should match by Request.');
});
}, 'Cache.match with Request');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(new Request(entries.a.request.url))
.then(function(result) {
assert_response_equals(result, entries.a.response,
'Cache.match should match by Request.');
});
}, 'Cache.match with new Request');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(entries.a.request,
{ignoreSearch: true})
.then(function(result) {
assert_response_in_array(
result,
[
entries.a.response,
entries.a_with_query.response
],
'Cache.match with ignoreSearch should ignore the ' +
'search parameters of cached request.');
});
},
'Cache.match with ignoreSearch option (request with no search ' +
'parameters)');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(entries.a_with_query.request,
{ignoreSearch: true})
.then(function(result) {
assert_response_in_array(
result,
[
entries.a.response,
entries.a_with_query.response
],
'Cache.match with ignoreSearch should ignore the ' +
'search parameters of request.');
});
},
'Cache.match with ignoreSearch option (request with search parameter)');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match(entries.cat.request.url + '#mouse')
.then(function(result) {
assert_response_equals(result, entries.cat.response,
'Cache.match should ignore URL fragment.');
});
}, 'Cache.match with URL containing fragment');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.match('http')
.then(function(result) {
assert_equals(
result, undefined,
'Cache.match should treat query as a URL and not ' +
'just a string fragment.');
});
}, 'Cache.match with string fragment "http" as query');
prepopulated_cache_test(vary_entries, function(cache, entries) {
return cache.match('http://example.com/c')
.then(function(result) {
assert_response_in_array(
result,
[
entries.vary_cookie_absent.response
],
'Cache.match should honor "Vary" header.');
});
}, 'Cache.match with responses containing "Vary" header');
cache_test(function(cache) {
var request = new Request('http://example.com');
var response;
var request_url = new URL('../resources/simple.txt', location.href).href;
return fetch(request_url)
.then(function(fetch_result) {
response = fetch_result;
assert_equals(
response.url, request_url,
'[https://fetch.spec.whatwg.org/#dom-response-url] ' +
'Reponse.url should return the URL of the response.');
return cache.put(request, response.clone());
})
.then(function() {
return cache.match(request.url);
})
.then(function(result) {
assert_response_equals(
result, response,
'Cache.match should return a Response object that has the same ' +
'properties as the stored response.');
return cache.match(response.url);
})
.then(function(result) {
assert_equals(
result, undefined,
'Cache.match should not match cache entry based on response URL.');
});
}, 'Cache.match with Request and Response objects with different URLs');
cache_test(function(cache) {
var request_url = new URL('../resources/simple.txt', location.href).href;
return fetch(request_url)
.then(function(fetch_result) {
return cache.put(new Request(request_url), fetch_result);
})
.then(function() {
return cache.match(request_url);
})
.then(function(result) {
return result.text();
})
.then(function(body_text) {
assert_equals(body_text, 'a simple text file\n',
'Cache.match should return a Response object with a ' +
'valid body.');
})
.then(function() {
return cache.match(request_url);
})
.then(function(result) {
return result.text();
})
.then(function(body_text) {
assert_equals(body_text, 'a simple text file\n',
'Cache.match should return a Response object with a ' +
'valid body each time it is called.');
});
}, 'Cache.match invoked multiple times for the same Request/Response');
prepopulated_cache_test(simple_entries, function(cache, entries) {
var request = new Request(entries.a.request.clone(), {method: 'POST'});
return cache.match(request)
.then(function(result) {
assert_equals(result, undefined,
'Cache.match should not find a match');
});
}, 'Cache.match with POST Request');
prepopulated_cache_test(simple_entries, function(cache, entries) {
var response = entries.non_2xx_response.response;
return cache.match(entries.non_2xx_response.request.url)
.then(function(result) {
assert_response_equals(
result, entries.non_2xx_response.response,
'Cache.match should return a Response object that has the ' +
'same properties as a stored non-2xx response.');
});
}, 'Cache.match with a non-2xx Response');
prepopulated_cache_test(simple_entries, function(cache, entries) {
var response = entries.error_response.response;
return cache.match(entries.error_response.request.url)
.then(function(result) {
assert_response_equals(
result, entries.error_response.response,
'Cache.match should return a Response object that has the ' +
'same properties as a stored network error response.');
});
}, 'Cache.match with a network error Response');
done();

View file

@ -0,0 +1,154 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll('not-present-in-the-cache')
.then(function(result) {
assert_response_array_equivalent(
result, [],
'Cache.matchAll should resolve with an empty array on failure.');
});
}, 'Cache.matchAll with no matching entries');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(entries.a.request.url)
.then(function(result) {
assert_response_array_equals(result, [entries.a.response],
'Cache.matchAll should match by URL.');
});
}, 'Cache.matchAll with URL');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(entries.a.request)
.then(function(result) {
assert_response_array_equals(
result, [entries.a.response],
'Cache.matchAll should match by Request.');
});
}, 'Cache.matchAll with Request');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(new Request(entries.a.request.url))
.then(function(result) {
assert_response_array_equals(
result, [entries.a.response],
'Cache.matchAll should match by Request.');
});
}, 'Cache.matchAll with new Request');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(entries.a.request,
{ignoreSearch: true})
.then(function(result) {
assert_response_array_equivalent(
result,
[
entries.a.response,
entries.a_with_query.response
],
'Cache.matchAll with ignoreSearch should ignore the ' +
'search parameters of cached request.');
});
},
'Cache.matchAll with ignoreSearch option (request with no search ' +
'parameters)');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(entries.a_with_query.request,
{ignoreSearch: true})
.then(function(result) {
assert_response_array_equivalent(
result,
[
entries.a.response,
entries.a_with_query.response
],
'Cache.matchAll with ignoreSearch should ignore the ' +
'search parameters of request.');
});
},
'Cache.matchAll with ignoreSearch option (request with search parameter)');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll(entries.cat.request.url + '#mouse')
.then(function(result) {
assert_response_array_equivalent(
result,
[
entries.cat.response,
],
'Cache.matchAll should ignore URL fragment.');
});
}, 'Cache.matchAll with URL containing fragment');
prepopulated_cache_test(simple_entries, function(cache, entries) {
return cache.matchAll('http')
.then(function(result) {
assert_response_array_equivalent(
result, [],
'Cache.matchAll should treat query as a URL and not ' +
'just a string fragment.');
});
}, 'Cache.matchAll with string fragment "http" as query');
prepopulated_cache_test(vary_entries, function(cache, entries) {
return cache.matchAll('http://example.com/c')
.then(function(result) {
assert_response_array_equivalent(
result,
[
entries.vary_cookie_absent.response
],
'Cache.matchAll should exclude matches if a vary header is ' +
'missing in the query request, but is present in the cached ' +
'request.');
})
.then(function() {
return cache.matchAll(
new Request('http://example.com/c',
{headers: {'Cookies': 'none-of-the-above'}}));
})
.then(function(result) {
assert_response_array_equivalent(
result,
[
],
'Cache.matchAll should exclude matches if a vary header is ' +
'missing in the cached request, but is present in the query ' +
'request.');
})
.then(function() {
return cache.matchAll(
new Request('http://example.com/c',
{headers: {'Cookies': 'is-for-cookie'}}));
})
.then(function(result) {
assert_response_array_equivalent(
result,
[entries.vary_cookie_is_cookie.response],
'Cache.matchAll should match the entire header if a vary header ' +
'is present in both the query and cached requests.');
});
}, 'Cache.matchAll with responses containing "Vary" header');
prepopulated_cache_test(vary_entries, function(cache, entries) {
return cache.matchAll('http://example.com/c',
{ignoreVary: true})
.then(function(result) {
assert_response_array_equivalent(
result,
[
entries.vary_cookie_is_cookie.response,
entries.vary_cookie_is_good.response,
entries.vary_cookie_absent.response
],
'Cache.matchAll should honor "ignoreVary" parameter.');
});
}, 'Cache.matchAll with "ignoreVary" parameter');
done();

View file

@ -0,0 +1,305 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
var test_url = 'https://example.com/foo';
var test_body = 'Hello world!';
cache_test(function(cache) {
var request = new Request(test_url);
var response = new Response(test_body);
return cache.put(request, response)
.then(function(result) {
assert_equals(result, undefined,
'Cache.put should resolve with undefined on success.');
});
}, 'Cache.put called with simple Request and Response');
cache_test(function(cache) {
var test_url = new URL('../resources/simple.txt', location.href).href;
var request = new Request(test_url);
var response;
return fetch(test_url)
.then(function(fetch_result) {
response = fetch_result.clone();
return cache.put(request, fetch_result);
})
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_response_equals(result, response,
'Cache.put should update the cache with ' +
'new request and response.');
return result.text();
})
.then(function(body) {
assert_equals(body, 'a simple text file\n',
'Cache.put should store response body.');
});
}, 'Cache.put called with Request and Response from fetch()');
cache_test(function(cache) {
var request = new Request(test_url);
var response = new Response(test_body);
assert_false(request.bodyUsed,
'[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' +
'Request.bodyUsed should be initially false.');
return cache.put(request, response)
.then(function() {
assert_false(request.bodyUsed,
'Cache.put should not mark empty request\'s body used');
});
}, 'Cache.put with Request without a body');
cache_test(function(cache) {
var request = new Request(test_url);
var response = new Response();
assert_false(response.bodyUsed,
'[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' +
'Response.bodyUsed should be initially false.');
return cache.put(request, response)
.then(function() {
assert_false(response.bodyUsed,
'Cache.put should not mark empty response\'s body used');
});
}, 'Cache.put with Response without a body');
cache_test(function(cache) {
var request = new Request(test_url);
var response = new Response(test_body);
return cache.put(request, response.clone())
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_response_equals(result, response,
'Cache.put should update the cache with ' +
'new Request and Response.');
});
}, 'Cache.put with a Response containing an empty URL');
cache_test(function(cache) {
var request = new Request(test_url);
var response = new Response('', {
status: 200,
headers: [['Content-Type', 'text/plain']]
});
return cache.put(request, response)
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_equals(result.status, 200, 'Cache.put should store status.');
assert_equals(result.headers.get('Content-Type'), 'text/plain',
'Cache.put should store headers.');
return result.text();
})
.then(function(body) {
assert_equals(body, '',
'Cache.put should store response body.');
});
}, 'Cache.put with an empty response body');
cache_test(function(cache) {
var test_url = new URL('../resources/fetch-status.py?status=500', location.href).href;
var request = new Request(test_url);
var response;
return fetch(test_url)
.then(function(fetch_result) {
assert_equals(fetch_result.status, 500,
'Test framework error: The status code should be 500.');
response = fetch_result.clone();
return cache.put(request, fetch_result);
})
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_response_equals(result, response,
'Cache.put should update the cache with ' +
'new request and response.');
return result.text();
})
.then(function(body) {
assert_equals(body, '',
'Cache.put should store response body.');
});
}, 'Cache.put with HTTP 500 response');
cache_test(function(cache) {
var alternate_response_body = 'New body';
var alternate_response = new Response(alternate_response_body,
{ statusText: 'New status' });
return cache.put(new Request(test_url),
new Response('Old body', { statusText: 'Old status' }))
.then(function() {
return cache.put(new Request(test_url), alternate_response.clone());
})
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_response_equals(result, alternate_response,
'Cache.put should replace existing ' +
'response with new response.');
return result.text();
})
.then(function(body) {
assert_equals(body, alternate_response_body,
'Cache put should store new response body.');
});
}, 'Cache.put called twice with matching Requests and different Responses');
cache_test(function(cache) {
var first_url = test_url;
var second_url = first_url + '#(O_o)';
var alternate_response_body = 'New body';
var alternate_response = new Response(alternate_response_body,
{ statusText: 'New status' });
return cache.put(new Request(first_url),
new Response('Old body', { statusText: 'Old status' }))
.then(function() {
return cache.put(new Request(second_url), alternate_response.clone());
})
.then(function() {
return cache.match(test_url);
})
.then(function(result) {
assert_response_equals(result, alternate_response,
'Cache.put should replace existing ' +
'response with new response.');
return result.text();
})
.then(function(body) {
assert_equals(body, alternate_response_body,
'Cache put should store new response body.');
});
}, 'Cache.put called twice with request URLs that differ only by a fragment');
cache_test(function(cache) {
var url = 'http://example.com/foo';
return cache.put(url, new Response('some body'))
.then(function() { return cache.match(url); })
.then(function(response) { return response.text(); })
.then(function(body) {
assert_equals(body, 'some body',
'Cache.put should accept a string as request.');
});
}, 'Cache.put with a string request');
cache_test(function(cache) {
return assert_promise_rejects(
cache.put(new Request(test_url), 'Hello world!'),
new TypeError(),
'Cache.put should only accept a Response object as the response.');
}, 'Cache.put with an invalid response');
cache_test(function(cache) {
return assert_promise_rejects(
cache.put(new Request('file:///etc/passwd'),
new Response(test_body)),
new TypeError(),
'Cache.put should reject non-HTTP/HTTPS requests with a TypeError.');
}, 'Cache.put with a non-HTTP/HTTPS request');
cache_test(function(cache) {
var response = new Response(test_body);
return cache.put(new Request('relative-url'), response.clone())
.then(function() {
return cache.match(new URL('relative-url', location.href).href);
})
.then(function(result) {
assert_response_equals(result, response,
'Cache.put should accept a relative URL ' +
'as the request.');
});
}, 'Cache.put with a relative URL');
cache_test(function(cache) {
var request = new Request('http://example.com/foo', { method: 'HEAD' });
return assert_promise_rejects(
cache.put(request, new Response(test_body)),
new TypeError(),
'Cache.put should throw a TypeError for non-GET requests.');
}, 'Cache.put with a non-GET request');
cache_test(function(cache) {
return assert_promise_rejects(
cache.put(new Request(test_url), null),
new TypeError(),
'Cache.put should throw a TypeError for a null response.');
}, 'Cache.put with a null response');
cache_test(function(cache) {
var request = new Request(test_url, {method: 'POST', body: test_body});
return assert_promise_rejects(
cache.put(request, new Response(test_body)),
new TypeError(),
'Cache.put should throw a TypeError for a POST request.');
}, 'Cache.put with a POST request');
cache_test(function(cache) {
var response = new Response(test_body);
assert_false(response.bodyUsed,
'[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' +
'Response.bodyUsed should be initially false.');
return response.text().then(function() {
assert_true(
response.bodyUsed,
'[https://fetch.spec.whatwg.org/#concept-body-consume-body] ' +
'The text() method should make the body disturbed.');
var request = new Request(test_url);
return cache.put(request, response).then(() => {
assert_unreached('cache.put should be rejected');
}, () => {});
});
}, 'Cache.put with a used response body');
cache_test(function(cache) {
var response = new Response(test_body);
return cache.put(new Request(test_url), response)
.then(function() {
assert_throws(new TypeError(), () => response.body.getReader());
});
}, 'getReader() after Cache.put');
cache_test(function(cache) {
return assert_promise_rejects(
cache.put(new Request(test_url),
new Response(test_body, { headers: { VARY: '*' }})),
new TypeError(),
'Cache.put should reject VARY:* Responses with a TypeError.');
}, 'Cache.put with a VARY:* Response');
cache_test(function(cache) {
return assert_promise_rejects(
cache.put(new Request(test_url),
new Response(test_body,
{ headers: { VARY: 'Accept-Language,*' }})),
new TypeError(),
'Cache.put should reject Responses with an embedded VARY:* with a ' +
'TypeError.');
}, 'Cache.put with an embedded VARY:* Response');
cache_test(function(cache) {
var url = 'foo.html';
var redirectURL = 'http://example.com/foo-bar.html';
var redirectResponse = Response.redirect(redirectURL);
assert_equals(redirectResponse.headers.get('Location'), redirectURL,
'Response.redirect() should set Location header.');
return cache.put(url, redirectResponse.clone())
.then(function() {
return cache.match(url);
})
.then(function(response) {
assert_response_equals(response, redirectResponse,
'Redirect response is reproduced by the Cache API');
assert_equals(response.headers.get('Location'), redirectURL,
'Location header is preserved by Cache API.');
});
}, 'Cache.put should store Response.redirect() correctly');
done();

View file

@ -0,0 +1,36 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
var test_cache_list =
['', 'example', 'Another cache name', 'A', 'a', 'ex ample'];
promise_test(function(test) {
return self.caches.keys()
.then(function(keys) {
assert_true(Array.isArray(keys),
'CacheStorage.keys should return an Array.');
return Promise.all(keys.map(function(key) {
return self.caches.delete(key);
}));
})
.then(function() {
return Promise.all(test_cache_list.map(function(key) {
return self.caches.open(key);
}));
})
.then(function() { return self.caches.keys(); })
.then(function(keys) {
assert_true(Array.isArray(keys),
'CacheStorage.keys should return an Array.');
assert_array_equals(keys,
test_cache_list,
'CacheStorage.keys should only return ' +
'existing caches.');
});
}, 'CacheStorage keys');
done();

View file

@ -0,0 +1,124 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
(function() {
var next_index = 1;
// Returns a transaction (request, response, and url) for a unique URL.
function create_unique_transaction(test) {
var uniquifier = String(next_index++);
var url = 'http://example.com/' + uniquifier;
return {
request: new Request(url),
response: new Response('hello'),
url: url
};
}
self.create_unique_transaction = create_unique_transaction;
})();
cache_test(function(cache) {
var transaction = create_unique_transaction();
return cache.put(transaction.request.clone(), transaction.response.clone())
.then(function() {
return self.caches.match(transaction.request);
})
.then(function(response) {
assert_response_equals(response, transaction.response,
'The response should not have changed.');
});
}, 'CacheStorageMatch with no cache name provided');
cache_test(function(cache) {
var transaction = create_unique_transaction();
var test_cache_list = ['a', 'b', 'c'];
return cache.put(transaction.request.clone(), transaction.response.clone())
.then(function() {
return Promise.all(test_cache_list.map(function(key) {
return self.caches.open(key);
}));
})
.then(function() {
return self.caches.match(transaction.request);
})
.then(function(response) {
assert_response_equals(response, transaction.response,
'The response should not have changed.');
});
}, 'CacheStorageMatch from one of many caches');
promise_test(function(test) {
var transaction = create_unique_transaction();
var test_cache_list = ['x', 'y', 'z'];
return Promise.all(test_cache_list.map(function(key) {
return self.caches.open(key);
}))
.then(function() { return caches.open('x'); })
.then(function(cache) {
return cache.put(transaction.request.clone(),
transaction.response.clone());
})
.then(function() {
return self.caches.match(transaction.request, {cacheName: 'x'});
})
.then(function(response) {
assert_response_equals(response, transaction.response,
'The response should not have changed.');
})
.then(function() {
return self.caches.match(transaction.request, {cacheName: 'y'});
})
.then(function(response) {
assert_equals(response, undefined,
'Cache y should not have a response for the request.');
});
}, 'CacheStorageMatch from one of many caches by name');
cache_test(function(cache) {
var transaction = create_unique_transaction();
return cache.put(transaction.url, transaction.response.clone())
.then(function() {
return self.caches.match(transaction.request);
})
.then(function(response) {
assert_response_equals(response, transaction.response,
'The response should not have changed.');
});
}, 'CacheStorageMatch a string request');
promise_test(function(test) {
var transaction = create_unique_transaction();
return self.caches.match(transaction.request)
.then(function(response) {
assert_equals(response, undefined,
'The response should not be found.');
})
}, 'CacheStorageMatch with no cached entry');
promise_test(function(test) {
var transaction = create_unique_transaction();
return self.caches.has('foo')
.then(function(has_foo) {
assert_false(has_foo, "The cache should not exist.");
return self.caches.match(transaction.request, {cacheName: 'foo'});
})
.then(function(response) {
assert_equals(response, undefined,
'The match with bad cache name should resolve to ' +
'undefined.');
return self.caches.has('foo');
})
.then(function(has_foo) {
assert_false(has_foo, "The cache should still not exist.");
})
}, 'CacheStorageMatch with no caches available but name provided');
done();

View file

@ -0,0 +1,197 @@
if (self.importScripts) {
importScripts('/resources/testharness.js');
importScripts('../resources/testharness-helpers.js');
importScripts('../resources/test-helpers.js');
}
promise_test(function(t) {
var cache_name = 'cache-storage/foo';
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
})
.then(function(cache) {
assert_true(cache instanceof Cache,
'CacheStorage.open should return a Cache.');
});
}, 'CacheStorage.open');
promise_test(function(t) {
// Note that this test may collide with other tests running in the same
// origin that also uses an empty cache name.
var cache_name = '';
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
})
.then(function(cache) {
assert_true(cache instanceof Cache,
'CacheStorage.open should accept an empty name.');
});
}, 'CacheStorage.open with an empty name');
promise_test(function(t) {
return assert_promise_rejects(
self.caches.open(),
new TypeError(),
'CacheStorage.open should throw TypeError if called with no arguments.');
}, 'CacheStorage.open with no arguments');
promise_test(function(t) {
var test_cases = [
{
name: 'cache-storage/lowercase',
should_not_match:
[
'cache-storage/Lowercase',
' cache-storage/lowercase',
'cache-storage/lowercase '
]
},
{
name: 'cache-storage/has a space',
should_not_match:
[
'cache-storage/has'
]
},
{
name: 'cache-storage/has\000_in_the_name',
should_not_match:
[
'cache-storage/has',
'cache-storage/has_in_the_name'
]
}
];
return Promise.all(test_cases.map(function(testcase) {
var cache_name = testcase.name;
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
})
.then(function() {
return self.caches.has(cache_name);
})
.then(function(result) {
assert_true(result,
'CacheStorage.has should return true for existing ' +
'cache.');
})
.then(function() {
return Promise.all(
testcase.should_not_match.map(function(cache_name) {
return self.caches.has(cache_name)
.then(function(result) {
assert_false(result,
'CacheStorage.has should only perform ' +
'exact matches on cache names.');
});
}));
})
.then(function() {
return self.caches.delete(cache_name);
});
}));
}, 'CacheStorage.has with existing cache');
promise_test(function(t) {
return self.caches.has('cheezburger')
.then(function(result) {
assert_false(result,
'CacheStorage.has should return false for ' +
'nonexistent cache.');
});
}, 'CacheStorage.has with nonexistent cache');
promise_test(function(t) {
var cache_name = 'cache-storage/open';
var cache;
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
})
.then(function(result) {
cache = result;
})
.then(function() {
return cache.add('../resources/simple.txt');
})
.then(function() {
return self.caches.open(cache_name);
})
.then(function(result) {
assert_true(result instanceof Cache,
'CacheStorage.open should return a Cache object');
assert_not_equals(result, cache,
'CacheStorage.open should return a new Cache ' +
'object each time its called.');
return Promise.all([cache.keys(), result.keys()]);
})
.then(function(results) {
var expected_urls = results[0].map(function(r) { return r.url });
var actual_urls = results[1].map(function(r) { return r.url });
assert_array_equals(actual_urls, expected_urls,
'CacheStorage.open should return a new Cache ' +
'object for the same backing store.');
});
}, 'CacheStorage.open with existing cache');
promise_test(function(t) {
var cache_name = 'cache-storage/delete';
return self.caches.delete(cache_name)
.then(function() {
return self.caches.open(cache_name);
})
.then(function() { return self.caches.delete(cache_name); })
.then(function(result) {
assert_true(result,
'CacheStorage.delete should return true after ' +
'deleting an existing cache.');
})
.then(function() { return self.caches.has(cache_name); })
.then(function(cache_exists) {
assert_false(cache_exists,
'CacheStorage.has should return false after ' +
'fulfillment of CacheStorage.delete promise.');
});
}, 'CacheStorage.delete with existing cache');
promise_test(function(t) {
return self.caches.delete('cheezburger')
.then(function(result) {
assert_false(result,
'CacheStorage.delete should return false for a ' +
'nonexistent cache.');
});
}, 'CacheStorage.delete with nonexistent cache');
promise_test(function(t) {
var bad_name = 'unpaired\uD800';
var converted_name = 'unpaired\uFFFD'; // Don't create cache with this name.
return self.caches.has(converted_name)
.then(function(cache_exists) {
assert_false(cache_exists,
'Test setup failure: cache should not exist');
})
.then(function() { return self.caches.open(bad_name); })
.then(function() { return self.caches.keys(); })
.then(function(keys) {
assert_true(keys.indexOf(bad_name) !== -1,
'keys should include cache with bad name');
})
.then(function() { return self.caches.has(bad_name); })
.then(function(cache_exists) {
assert_true(cache_exists,
'CacheStorage names should be not be converted.');
})
.then(function() { return self.caches.has(converted_name); })
.then(function(cache_exists) {
assert_false(cache_exists,
'CacheStorage names should be not be converted.');
});
}, 'CacheStorage names are DOMStrings not USVStrings');
done();

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>Cache.add and Cache.addAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-add">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-add.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>Cache.delete</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-delete">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-delete.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>Cache.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-match.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>Cache.matchAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-matchall">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-matchAll.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>Cache.put</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-put">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-put.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>CacheStorage.keys</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage-keys.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>CacheStorage.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage-match.js');
</script>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<title>CacheStorage</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage.js');
</script>

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Cache Storage: Verify credentials are respected by Cache operations</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<style>iframe { display: none; }</style>
<script>
var worker = "../resources/credentials-worker.js";
var scope = "../resources/credentials-iframe.html";
promise_test(function(t) {
return self.caches.delete('credentials')
.then(function() {
return service_worker_unregister_and_register(t, worker, scope)
})
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(frame) {
frame.contentWindow.postMessage([
{name: 'file.txt', username: 'aa', password: 'bb'},
{name: 'file.txt', username: 'cc', password: 'dd'},
{name: 'file.txt'}
], '*');
return new Promise(function(resolve, reject) {
window.onmessage = t.step_func(function(e) {
resolve(e.data);
});
});
})
.then(function(data) {
assert_equals(data.length, 3, 'three entries should be present');
assert_equals(data.filter(function(url) { return /@/.test(url); }).length, 2,
'two entries should contain credentials');
assert_true(data.some(function(url) { return /aa:bb@/.test(url); }),
'entry with credentials aa:bb should be present');
assert_true(data.some(function(url) { return /cc:dd@/.test(url); }),
'entry with credentials cc:dd should be present');
});
}, "Cache API matching includes credentials");
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: Cache.add and Cache.addAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-add">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-add.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: Cache.delete</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-delete">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-delete.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: Cache.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-match.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.matchAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-matchall">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-matchAll.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: Cache.put</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-put">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-put.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: CacheStorage.keys</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-storage-keys.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: CacheStorage.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-storage-match.js"></script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache Storage: CacheStorage</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script src="../resources/test-helpers.js"></script>
<script src="../script-tests/cache-storage.js"></script>

View file

@ -0,0 +1,67 @@
<!DOCTYPE html>
<title>Cache Storage: Verify access in sandboxed iframes</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testharness-helpers.js"></script>
<script>
function load_iframe(src, sandbox) {
return new Promise(function(resolve, reject) {
var iframe = document.createElement('iframe');
iframe.onload = function() { resolve(iframe); };
iframe.sandbox = sandbox;
iframe.src = src;
document.documentElement.appendChild(iframe);
});
}
function wait_for_message(id) {
return new Promise(function(resolve) {
self.addEventListener('message', function listener(e) {
if (e.data.id === id) {
resolve(e.data);
self.removeEventListener('message', listener);
}
});
});
}
var counter = 0;
promise_test(function(t) {
return load_iframe('../resources/iframe.html',
'allow-scripts allow-same-origin')
.then(function(iframe) {
var id = ++counter;
iframe.contentWindow.postMessage({id: id}, '*');
return wait_for_message(id);
})
.then(function(message) {
assert_equals(
message.result, 'allowed',
'Access should be allowed if sandbox has allow-same-origin');
});
}, 'Sandboxed iframe with allow-same-origin is allowed access');
promise_test(function(t) {
return load_iframe('../resources/iframe.html',
'allow-scripts')
.then(function(iframe) {
var id = ++counter;
iframe.contentWindow.postMessage({id: id}, '*');
return wait_for_message(id);
})
.then(function(message) {
assert_equals(
message.result, 'denied',
'Access should be denied if sandbox lacks allow-same-origin');
assert_equals(message.name, 'SecurityError',
'Failure should be a SecurityError');
});
}, 'Sandboxed iframe without allow-same-origin is denied access');
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.add and Cache.addAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-add">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-add.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.delete</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-delete">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-delete.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-match.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.matchAll</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-matchall">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-matchAll.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>Cache.put</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-put">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-put.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>CacheStorage.keys</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-storage-keys.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>CacheStorage.match</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage-match">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-storage-match.js'));
</script>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<title>CacheStorage</title>
<link rel="help" href="https://w3c.github.io/ServiceWorker/#cache-storage">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
fetch_tests_from_worker(new Worker('../script-tests/cache-storage.js'));
</script>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<title>ServiceWorkerGlobalScope: registration</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src='../resources/test-helpers.sub.js'></script>
<script>
promise_test(function(t) {
var script = 'resources/registration-attribute-worker.js';
var scope = 'resources/scope/registration-attribute';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
var expected_events_seen = [
'updatefound',
'install',
'statechange(installed)',
'statechange(activating)',
'activate',
'statechange(activated)',
'fetch',
];
assert_equals(
frame.contentDocument.body.textContent,
expected_events_seen.toString(),
'Service Worker should respond to fetch');
frame.remove();
return service_worker_unregister_and_done(t, scope);
});
}, 'Verify registration attribute on ServiceWorkerGlobalScope');
</script>

View file

@ -0,0 +1,132 @@
importScripts('../../resources/test-helpers.sub.js');
importScripts('../../resources/worker-testharness.js');
var events_seen = [];
assert_equals(
self.registration.scope,
normalizeURL('scope/registration-attribute'),
'On worker script evaluation, registration attribute should be set');
assert_equals(
self.registration.installing,
null,
'On worker script evaluation, installing worker should be null');
assert_equals(
self.registration.waiting,
null,
'On worker script evaluation, waiting worker should be null');
assert_equals(
self.registration.active,
null,
'On worker script evaluation, active worker should be null');
self.registration.addEventListener('updatefound', function() {
events_seen.push('updatefound');
assert_equals(
self.registration.scope,
normalizeURL('scope/registration-attribute'),
'On updatefound event, registration attribute should be set');
assert_equals(
self.registration.installing.scriptURL,
normalizeURL('registration-attribute-worker.js'),
'On updatefound event, installing worker should be set');
assert_equals(
self.registration.waiting,
null,
'On updatefound event, waiting worker should be null');
assert_equals(
self.registration.active,
null,
'On updatefound event, active worker should be null');
assert_equals(
self.registration.installing.state,
'installing',
'On updatefound event, worker should be in the installing state');
var worker = self.registration.installing;
self.registration.installing.addEventListener('statechange', function() {
events_seen.push('statechange(' + worker.state + ')');
});
});
self.addEventListener('install', function(e) {
events_seen.push('install');
assert_equals(
self.registration.scope,
normalizeURL('scope/registration-attribute'),
'On install event, registration attribute should be set');
assert_equals(
self.registration.installing.scriptURL,
normalizeURL('registration-attribute-worker.js'),
'On install event, installing worker should be set');
assert_equals(
self.registration.waiting,
null,
'On install event, waiting worker should be null');
assert_equals(
self.registration.active,
null,
'On install event, active worker should be null');
assert_equals(
self.registration.installing.state,
'installing',
'On install event, worker should be in the installing state');
});
self.addEventListener('activate', function(e) {
events_seen.push('activate');
assert_equals(
self.registration.scope,
normalizeURL('scope/registration-attribute'),
'On activate event, registration attribute should be set');
assert_equals(
self.registration.installing,
null,
'On activate event, installing worker should be null');
assert_equals(
self.registration.waiting,
null,
'On activate event, waiting worker should be null');
assert_equals(
self.registration.active.scriptURL,
normalizeURL('registration-attribute-worker.js'),
'On activate event, active worker should be set');
assert_equals(
self.registration.active.state,
'activating',
'On activate event, worker should be in the activating state');
});
self.addEventListener('fetch', function(e) {
events_seen.push('fetch');
assert_equals(
self.registration.scope,
normalizeURL('scope/registration-attribute'),
'On fetch event, registration attribute should be set');
assert_equals(
self.registration.installing,
null,
'On fetch event, installing worker should be null');
assert_equals(
self.registration.waiting,
null,
'On fetch event, waiting worker should be null');
assert_equals(
self.registration.active.scriptURL,
normalizeURL('registration-attribute-worker.js'),
'On fetch event, active worker should be set');
assert_equals(
self.registration.active.state,
'activated',
'On fetch event, worker should be in the activated state');
e.respondWith(new Response(events_seen));
});

View file

@ -0,0 +1,23 @@
function matchQuery(query) {
return self.location.href.indexOf(query) != -1;
}
if (matchQuery('?evaluation'))
self.registration.unregister();
self.addEventListener('install', function(e) {
if (matchQuery('?install'))
self.registration.unregister();
});
self.addEventListener('activate', function(e) {
if (matchQuery('?activate'))
self.registration.unregister();
});
self.addEventListener('message', function(e) {
self.registration.unregister()
.then(function(result) {
e.data.port.postMessage({result: result});
});
});

View file

@ -0,0 +1,25 @@
importScripts('../../resources/test-helpers.sub.js');
importScripts('../../resources/worker-testharness.js');
var events_seen = [];
self.registration.addEventListener('updatefound', function() {
events_seen.push('updatefound');
});
self.addEventListener('activate', function(e) {
events_seen.push('activate');
});
self.addEventListener('fetch', function(e) {
events_seen.push('fetch');
e.respondWith(new Response(events_seen));
});
self.addEventListener('message', function(e) {
events_seen.push('message');
self.registration.update();
});
// update() during the script evaluation should be ignored.
self.registration.update();

View file

@ -0,0 +1,14 @@
import os
import time
def main(request, response):
# update() does not bypass cache so set the max-age to 0 such that update()
# can find a new version in the network.
headers = [('Cache-Control', 'max-age: 0'),
('Content-Type', 'application/javascript')]
with open(os.path.join(os.path.dirname(__file__),
'update-worker.js'), 'r') as file:
script = file.read()
# Return a different script for each access.
return headers, '// %s\n%s' % (time.time(), script)

View file

@ -0,0 +1,127 @@
<!DOCTYPE html>
<title>ServiceWorkerGlobalScope: unregister</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src='../resources/test-helpers.sub.js'></script>
<script>
promise_test(function(t) {
var script = 'resources/unregister-worker.js?evaluation';
var scope = 'resources/scope/unregister-on-script-evaluation';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'redundant');
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(result) {
assert_equals(
result,
undefined,
'After unregister(), the registration should not found');
return service_worker_unregister_and_done(t, scope);
});
}, 'Unregister on script evaluation');
promise_test(function(t) {
var script = 'resources/unregister-worker.js?install';
var scope = 'resources/scope/unregister-on-install-event';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'redundant');
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(result) {
assert_equals(
result,
undefined,
'After unregister(), the registration should not found');
return service_worker_unregister_and_done(t, scope);
});
}, 'Unregister on installing event');
promise_test(function(t) {
var script = 'resources/unregister-worker.js?activate';
var scope = 'resources/scope/unregister-on-activate-event';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'redundant');
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(result) {
assert_equals(
result,
undefined,
'After unregister(), the registration should not found');
return service_worker_unregister_and_done(t, scope);
});
}, 'Unregister on activate event');
promise_test(function(t) {
var script = 'resources/unregister-worker.js';
var scope = 'resources/unregister-controlling-worker.html';
var controller;
var frame;
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
controller = frame.contentWindow.navigator.serviceWorker.controller;
assert_equals(
controller.scriptURL,
normalizeURL(script),
'Service worker should control a new document')
// Wait for the completion of unregister() on the worker.
var channel = new MessageChannel();
var promise = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_true(e.data.result,
'unregister() should successfully finish');
resolve();
});
});
controller.postMessage({port: channel.port2}, [channel.port2]);
return promise;
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(result) {
assert_equals(
result,
undefined,
'After unregister(), the registration should not found');
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller,
controller,
'After unregister(), the worker should still control the document');
return with_iframe(scope);
})
.then(function(new_frame) {
assert_equals(
new_frame.contentWindow.navigator.serviceWorker.controller,
null,
'After unregister(), the worker should not control a new document');
frame.remove();
new_frame.remove();
return service_worker_unregister_and_done(t, scope);
})
}, 'Unregister controlling service worker');
</script>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<title>ServiceWorkerGlobalScope: update</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src='../resources/test-helpers.sub.js'></script>
<script>
promise_test(function(t) {
var script = 'resources/update-worker.py';
var scope = 'resources/scope/update';
var registration;
var frame1;
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) {
frame1 = f;
registration.active.postMessage('update');
return wait_for_update(t, registration);
})
.then(function() { return with_iframe(scope); })
.then(function(frame2) {
var expected_events_seen = [
'updatefound', // by register().
'activate',
'fetch',
'message',
'updatefound', // by update() in the message handler.
'fetch',
];
assert_equals(
frame2.contentDocument.body.textContent,
expected_events_seen.toString(),
'events seen by the worker');
frame1.remove();
frame2.remove();
return service_worker_unregister_and_done(t, scope);
});
}, 'Update a registration on ServiceWorkerGlobalScope');
</script>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<title>Service Worker: registration events</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var script = 'resources/empty-worker.js';
var scope = 'resources/blank.html';
var registration;
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
var sw = registration.installing;
return new Promise(t.step_func(function(resolve) {
sw.onstatechange = t.step_func(function() {
if (sw.state === 'installed') {
assert_equals(registration.active, null,
'installed event should be fired before activating service worker');
resolve();
}
});
}));
})
.then(function() {
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'installed event should be fired before activating service worker');
</script>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<title>Service Worker: Activation occurs after registration</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var t = async_test('activation occurs after registration');
t.step(function() {
var scope = 'resources/blank.html';
var registration;
service_worker_unregister_and_register(
t, 'resources/empty-worker.js', scope)
.then(function(r) {
registration = r;
assert_equals(
r.installing.state,
'installing',
'worker should be in the "installing" state upon registration');
return wait_for_state(t, r.installing, 'activated');
})
.then(function() {
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
});
</script>
</body>

View file

@ -0,0 +1,179 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>service worker: activation</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
// Registers, waits for activation, then unregisters on a dummy scope.
//
// This helper can be used in tests that assert that activation doesn't happen.
// It would not be sufficient to check the .waiting/.active properties once,
// since activation could be scheduled and just hasn't happened yet. Since this
// helper shows that activation of another registration completed, we can be
// sure that activation really will not happen.
function wait_for_activation_on_dummy_scope(t) {
var dummy_scope = 'resources/there/is/no/there/there';
var registration;
return navigator.serviceWorker.register('resources/empty-worker.js',
{ scope: dummy_scope })
.then(r => {
registration = r;
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => registration.unregister());
}
// Returns {registration, iframe}, where |registration| has an active and
// waiting worker. The active worker controls |iframe| and has an inflight
// message event that can be finished by calling
// |registration.active.postMessage('go')|.
function setup_activation_test(t, scope, worker_url) {
var registration;
var iframe;
return navigator.serviceWorker.getRegistration(scope)
.then(r => {
if (r)
return r.unregister();
})
.then(() => {
// Create an in-scope iframe. Do this prior to registration to avoid
// racing between an update triggered by navigation and the update()
// call below.
return with_iframe(scope);
})
.then(f => {
iframe = f;
// Create an active worker.
return navigator.serviceWorker.register(worker_url, { scope: scope });
})
.then(r => {
registration = r;
add_result_callback(() => registration.unregister());
return wait_for_state(t, r.installing, 'activated');
})
.then(() => {
// Check that the frame was claimed.
assert_not_equals(
iframe.contentWindow.navigator.serviceWorker.controller, null);
// Create an in-flight request.
registration.active.postMessage('wait');
// Now there is both a controllee and an in-flight request.
// Initiate an update.
return registration.update();
})
.then(() => {
// Wait for a waiting worker.
return wait_for_state(t, registration.installing, 'installed');
})
.then(() => {
return wait_for_activation_on_dummy_scope(t);
})
.then(() => {
assert_not_equals(registration.waiting, null);
assert_not_equals(registration.active, null);
return Promise.resolve({registration: registration, iframe: iframe});
});
}
promise_test(t => {
var scope = 'resources/no-controllee';
var worker_url = 'resources/mint-new-worker.py';
var registration;
var iframe;
var new_worker;
return setup_activation_test(t, scope, worker_url)
.then(result => {
registration = result.registration;
iframe = result.iframe;
// Finish the in-flight request.
registration.active.postMessage('go');
return wait_for_activation_on_dummy_scope(t);
})
.then(() => {
// The new worker is still waiting. Remove the frame and it should
// activate.
new_worker = registration.waiting;
assert_equals(new_worker.state, 'installed');
var reached_active = wait_for_state(t, new_worker, 'activating');
iframe.remove();
return reached_active;
})
.then(() => {
assert_equals(new_worker, registration.active);
});
}, 'loss of controllees triggers activation');
promise_test(t => {
var scope = 'resources/no-request';
var worker_url = 'resources/mint-new-worker.py';
var registration;
var iframe;
var new_worker;
return setup_activation_test(t, scope, worker_url)
.then(result => {
registration = result.registration;
iframe = result.iframe;
// Remove the iframe.
iframe.remove();
return new Promise(resolve => setTimeout(resolve, 0));
})
.then(() => {
// Finish the request.
new_worker = registration.waiting;
var reached_active = wait_for_state(t, new_worker, 'activating');
registration.active.postMessage('go');
return reached_active;
})
.then(() => {
assert_equals(registration.active, new_worker);
});
}, 'finishing a request triggers activation');
promise_test(t => {
var scope = 'resources/skip-waiting';
var worker_url = 'resources/mint-new-worker.py?skip-waiting';
var registration;
var new_worker;
return setup_activation_test(t, scope, worker_url)
.then(result => {
registration = result.registration;
// Finish the request. The iframe does not need to be removed because
// skipWaiting() was called.
new_worker = registration.waiting;
var reached_active = wait_for_state(t, new_worker, 'activating');
registration.active.postMessage('go');
return reached_active;
})
.then(() => {
assert_equals(registration.active, new_worker);
});
}, 'skipWaiting bypasses no controllee requirement');
// This test is not really about activation, but otherwise is very
// similar to the other tests here.
promise_test(t => {
var scope = 'resources/unregister';
var worker_url = 'resources/mint-new-worker.py';
var registration;
var iframe;
var new_worker;
return setup_activation_test(t, scope, worker_url)
.then(result => {
registration = result.registration;
iframe = result.iframe;
// Remove the iframe.
iframe.remove();
return registration.unregister();
})
.then(() => {
// The unregister operation should wait for the active worker to
// finish processing its events before clearing the registration.
new_worker = registration.waiting;
var reached_redundant = wait_for_state(t, new_worker, 'redundant');
registration.active.postMessage('go');
return reached_redundant;
})
.then(() => {
assert_equals(registration.installing, null);
assert_equals(registration.waiting, null);
assert_equals(registration.active, null);
});
}, 'finishing a request triggers unregister');
</script>

View file

@ -0,0 +1,55 @@
<!DOCTYPE html>
<title>ServiceWorker: navigator.serviceWorker.active</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
// "active" is set
async_test(function(t) {
var step = t.step_func.bind(t);
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html';
var frame;
var registration;
service_worker_unregister(t, scope)
.then(step(function() { return with_iframe(scope); }))
.then(step(function(f) {
frame = f;
return navigator.serviceWorker.register(url, {scope: scope});
}))
.then(step(function(r) {
registration = r;
return wait_for_state(t, r.installing, 'activating');
}))
.then(step(function() {
var container = frame.contentWindow.navigator.serviceWorker;
assert_equals(
container.controller,
null,
'On activating state a document should not have a controller');
assert_equals(
registration.active.scriptURL,
normalizeURL(url),
'On activating state a document should have an active worker ');
assert_equals(
registration.waiting,
null,
'On activating state a document should not have a waiting worker');
assert_equals(
registration.installing,
null,
'On activating state a document should not have an installing ' +
'worker');
// FIXME: Add a test for a frame created after installation.
// Should the existing frame ("frame") block activation?
}))
.then(step(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
}))
.catch(unreached_rejection(t));
}, 'active is set');
</script>

View file

@ -0,0 +1,91 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var INSTALL_APPCACHE_URL = "resources/appcache-ordering.install.html";
var IS_APPCACHED_URL = "resources/appcache-ordering.is-appcached.html";
var SERVICE_WORKER_SCOPE = "resources/appcache-ordering";
var SERVICE_WORKER_SCRIPT = "resources/empty-worker.js";
var resolve_install_appcache = undefined;
var reject_install_appcache = undefined;
var frames = [];
// Called by the INSTALL_APPCACHE_URL child frame.
function notify_appcache_installed(success) {
if (success)
resolve_install_appcache();
else
reject_install_appcache();
}
function install_appcache() {
return new Promise(function(resolve, reject) {
var frame = document.createElement('iframe');
frames.push(frame);
frame.src = INSTALL_APPCACHE_URL;
document.body.appendChild(frame);
resolve_install_appcache = function() {
document.body.removeChild(frame);
resolve();
};
reject_install_appcache = function() {
document.body.removeChild(frame);
reject();
};
});
}
var resolve_is_appcached = undefined;
// Called by the IS_APPCACHED_URL child frame.
function notify_is_appcached(is) {
resolve_is_appcached(is);
}
function is_appcached() {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frames.push(frame);
frame.src = IS_APPCACHED_URL;
document.body.appendChild(frame);
resolve_is_appcached = function(is) {
document.body.removeChild(frame);
resolve(is);
};
});
}
async_test(function(t) {
service_worker_unregister(t, SERVICE_WORKER_SCOPE)
.then(function() {
return install_appcache();
})
.then(function() {
return is_appcached();
})
.then(function(result) {
assert_true(result, 'appcache should initially be utilized');
return service_worker_unregister_and_register(
t, SERVICE_WORKER_SCRIPT, SERVICE_WORKER_SCOPE);
})
.then(function(r) {
return wait_for_state(t, r.installing, 'activated');
})
.then(function() {
return is_appcached();
})
.then(function(result) {
assert_false(result, 'but serviceworkers should take priority');
frames.forEach(function(f) { f.remove(); });
service_worker_unregister_and_done(t, SERVICE_WORKER_SCOPE);
})
.catch(unreached_rejection(t));
}, 'serviceworkers take priority over appcaches');
</script>
</body>

View file

@ -0,0 +1,123 @@
<!DOCTYPE html>
<title>Service Worker: claim client not using registration</title>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
promise_test(function(t) {
var init_scope = 'resources/blank.html?not-using-init';
var claim_scope = 'resources/blank.html?not-using';
var init_worker_url = 'resources/empty.js';
var claim_worker_url = 'resources/claim-worker.js';
var claim_worker, claim_registration, frame1, frame2;
return service_worker_unregister_and_register(
t, init_worker_url, init_scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return Promise.all(
[with_iframe(init_scope), with_iframe(claim_scope)]);
})
.then(function(frames) {
frame1 = frames[0];
frame2 = frames[1];
assert_equals(
frame1.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(init_worker_url),
'Frame1 controller should not be null');
assert_equals(
frame2.contentWindow.navigator.serviceWorker.controller, null,
'Frame2 controller should be null');
return navigator.serviceWorker.register(claim_worker_url,
{scope: claim_scope});
})
.then(function(registration) {
claim_worker = registration.installing;
claim_registration = registration;
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
var saw_controllerchanged = new Promise(function(resolve) {
frame2.contentWindow.navigator.serviceWorker.oncontrollerchange =
function() { resolve(); }
});
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'PASS',
'Worker call to claim() should fulfill.');
resolve();
});
});
claim_worker.postMessage({port: channel.port2}, [channel.port2]);
return Promise.all([saw_controllerchanged, saw_message]);
})
.then(function() {
assert_equals(
frame1.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(init_worker_url),
'Frame1 should not be influenced');
assert_equals(
frame2.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(claim_worker_url),
'Frame2 should be controlled by the new registration');
frame1.remove();
frame2.remove();
return claim_registration.unregister();
})
.then(function() {
return service_worker_unregister_and_done(t, init_scope);
});
}, 'Test claim client which is not using registration');
promise_test(function(t) {
var scope = 'resources/blank.html?longer-matched';
var claim_scope = 'resources/blank.html?longer';
var claim_worker_url = 'resources/claim-worker.js';
var installing_worker_url = 'resources/empty-worker.js';
var frame, claim_worker;
return with_iframe(scope)
.then(function(f) {
frame = f;
return navigator.serviceWorker.register(
claim_worker_url, {scope: claim_scope});
})
.then(function(registration) {
claim_worker = registration.installing;
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return navigator.serviceWorker.register(
installing_worker_url, {scope: scope});
})
.then(function() {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'PASS',
'Worker call to claim() should fulfill.');
resolve();
});
});
claim_worker.postMessage({port: channel.port2}, [channel.port2]);
return saw_message;
})
.then(function() {
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller, null,
'Frame should not be claimed when a longer-matched ' +
'registration exists');
frame.remove();
return service_worker_unregister(t, claim_scope);
})
.then(function() {
return service_worker_unregister_and_done(t, scope);
});
}, 'Test claim client when there\'s a longer-matched registration not ' +
'already used by the page');
</script>

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<title>Service Worker: claim client using registration</title>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var scope = 'resources/';
var frame_url = 'resources/blank.html?using-different-registration';
var url1 = 'resources/empty.js';
var url2 = 'resources/claim-worker.js';
var worker, sw_registration, frame;
return service_worker_unregister_and_register(t, url1, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(frame_url);
})
.then(function(f) {
frame = f;
return navigator.serviceWorker.register(url2, {scope: frame_url});
})
.then(function(registration) {
worker = registration.installing;
sw_registration = registration;
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
var saw_controllerchanged = new Promise(function(resolve) {
frame.contentWindow.navigator.serviceWorker.oncontrollerchange =
function() { resolve(); }
});
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'PASS',
'Worker call to claim() should fulfill.');
resolve();
});
});
worker.postMessage({port: channel.port2}, [channel.port2]);
return Promise.all([saw_controllerchanged, saw_message]);
})
.then(function() {
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(url2),
'Frame1 controller scriptURL should be changed to url2');
frame.remove();
return sw_registration.unregister();
})
.then(function() {
return service_worker_unregister_and_done(t, scope);
});
}, 'Test worker claims client which is using another registration');
promise_test(function(t) {
var scope = 'resources/blank.html?using-same-registration';
var url1 = 'resources/empty.js';
var url2 = 'resources/claim-worker.js';
var frame, worker;
return service_worker_unregister_and_register(t, url1, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(f) {
frame = f;
return navigator.serviceWorker.register(url2, {scope: scope});
})
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, registration.installing, 'installed');
})
.then(function() {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'FAIL: exception: InvalidStateError',
'Worker call to claim() should reject with ' +
'InvalidStateError');
resolve();
});
});
worker.postMessage({port: channel.port2}, [channel.port2]);
return saw_message;
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
});
}, 'Test for the waiting worker claims a client which is using the the ' +
'same registration');
</script>

View file

@ -0,0 +1,117 @@
<!doctype html>
<meta charset=utf-8>
<title>Service Worker: WindowClient.navigate</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
function wait_for_message(msg) {
return new Promise(function(resolve, reject) {
var get_message_data = function get_message_data(e) {
window.removeEventListener("message", get_message_data);
resolve(e.data);
}
window.addEventListener("message", get_message_data, false);
});
}
function run_test(controller, clientId, test) {
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = function(e) {
resolve(e.data);
};
var message = {
port: channel.port2,
test: test,
clientId: clientId,
};
controller.postMessage(
message, [channel.port2]);
});
}
promise_test(function(t) {
var worker = "resources/client-navigate-worker.js";
var scope = "resources/client-navigate-frame.html";
var controller, frame, clientId;
return service_worker_unregister_and_register(t, worker, scope)
.then(reg => wait_for_state(t, reg.installing, "activated"))
.then(___ => with_iframe(scope))
.then(f => {
frame = f;
controller = frame.contentWindow.navigator.serviceWorker.controller;
fetch_tests_from_worker(controller);
return wait_for_message()
})
.then(({id}) => clientId = id)
.then(___ => run_test(controller, clientId, "test_client_navigate_success"))
.then(({result, url}) => {
assert_equals(result, "test_client_navigate_success");
assert_equals(
url, new URL("resources/client-navigated-frame.html",
location).toString());
assert_equals(
frame.contentWindow.location.href,
new URL("resources/client-navigated-frame.html",
location).toString());
})
.catch(unreached_rejection(t))
.then(___ => service_worker_unregister(t, scope));
}, "Frame location should update on successful navigation");
promise_test(function(t) {
var worker = "resources/client-navigate-worker.js";
var scope = "resources/client-navigate-frame.html";
var controller, frame, clientId;
return service_worker_unregister_and_register(t, worker, scope)
.then(reg => wait_for_state(t, reg.installing, "activated"))
.then(___ => with_iframe(scope))
.then(f => {
frame = f;
controller = frame.contentWindow.navigator.serviceWorker.controller;
fetch_tests_from_worker(controller);
return wait_for_message()
})
.then(({id}) => clientId = id)
.then(___ => run_test(controller, clientId, "test_client_navigate_redirect"))
.then(({result, url}) => {
assert_equals(result, "test_client_navigate_redirect");
assert_equals(url, "");
assert_throws(null, function() { return frame.contentWindow.location.href });
})
.catch(unreached_rejection(t))
.then(___ => service_worker_unregister(t, scope));
}, "Frame location should not be accessible after redirect");
promise_test(function(t) {
var worker = "resources/client-navigate-worker.js";
var scope = "resources/client-navigate-frame.html";
var controller, frame, clientId;
return service_worker_unregister_and_register(t, worker, scope)
.then(reg => wait_for_state(t, reg.installing, "activated"))
.then(___ => with_iframe(scope))
.then(f => {
frame = f;
controller = frame.contentWindow.navigator.serviceWorker.controller;
fetch_tests_from_worker(controller);
return wait_for_message()
})
.then(({id}) => clientId = id)
.then(___ => run_test(controller, clientId, "test_client_navigate_failure"))
.then(({result, url}) => {
assert_equals(result, "test_client_navigate_failure");
assert_equals(
frame.contentWindow.location.href,
new URL("resources/client-navigate-frame.html",
location).toString());
frame.contentWindow.document.body.style = "background-color: green"
})
.catch(unreached_rejection(t))
.then(___ => service_worker_unregister(t, scope));
}, "Frame location should not update on failed navigation");
</script>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<title>Service Worker: Clients.get across origins</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var host_info = get_host_info();
var scope = 'resources/blank.html?clients-get';
var t = async_test('Test Clients.get() cross origin');
var other_origin_iframe = host_info['HTTPS_REMOTE_ORIGIN'] + base_path() +
'resources/clients-get-other-origin.html';
var myOriginClientId;
t.step(function() {
service_worker_unregister_and_register(
t, 'resources/clients-get-worker.js', scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(frame1) {
myOriginClientId = frame1.contentDocument.body.textContent;
return with_iframe(other_origin_iframe);
})
.then(function(frame2) {
window.addEventListener('message', on_message_other_origin, false);
frame2.contentWindow.postMessage(
{clientId: myOriginClientId,
message: 'get_client_id'},
host_info['HTTPS_REMOTE_ORIGIN']);
})
.catch(unreached_rejection(t));
});
function on_message_other_origin(e) {
assert_equals(e.data.result, undefined);
t.done();
}
</script>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<title>Service Worker: Clients.get</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var host_info = get_host_info();
var scope = 'resources/clients-get-frame.html';
var t = async_test('Test Clients.get()');
var clientIds = [];
var frame;
t.step(function() {
service_worker_unregister_and_register(
t, 'resources/clients-get-worker.js', scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope + '#1');
})
.then(function(frame1) {
frame1.focus();
return wait_for_clientId();
})
.then(function(clientId) {
clientIds.push(clientId);
return with_iframe(scope + '#2');
})
.then(function(frame2) {
frame = frame2;
return wait_for_clientId();
})
.then(function(clientId) {
clientIds.push(clientId);
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(on_message);
frame.contentWindow.navigator.serviceWorker.controller.postMessage(
{port:channel.port2, clientIds:clientIds,
message: 'get_client_ids'}, [channel.port2]);
})
.catch(unreached_rejection(t));
});
function wait_for_clientId() {
return new Promise(function(resolve, reject) {
function get_client_id(e) {
window.removeEventListener("message", get_client_id);
resolve(e.data.clientId);
}
window.addEventListener("message", get_client_id, false);
});
}
var expected = [
/* visibilityState, focused, url, frameType */
['visible', true, new URL(scope + '#1', location).toString(), 'nested'],
['visible', false, new URL(scope + '#2', location).toString(), 'nested'],
undefined
];
function on_message(e) {
assert_equals(e.data.length, 3);
assert_array_equals(e.data[0], expected[0]);
assert_array_equals(e.data[1], expected[1]);
assert_equals(e.data[2], expected[2]);
service_worker_unregister_and_done(t, scope);
}
</script>

View file

@ -0,0 +1,78 @@
<!DOCTYPE html>
<title>Service Worker: Clients.matchAll with various clientTypes</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var scope = 'resources/clients-matchall-client-types';
var iframe_url = scope + '-iframe.html';
var shared_worker_url = scope + '-shared-worker.js';
/* visibilityState, focused, url, frameType */
var expected_without_type = [
['visible', true, new URL(iframe_url, location).href, 'nested']
];
var expected_with_window = [
['visible', true, new URL(iframe_url, location).href, 'nested']
];
var expected_with_shared_worker = [
[,,new URL(shared_worker_url, location).href, 'none']
];
var expected_with_all = [
['visible', true, new URL(iframe_url, location).href, 'nested'],
[,,new URL(shared_worker_url, location).href, 'none']
];
function test_matchall(frame, expected, query_options) {
// Make sure the frame gets focus.
frame.focus();
expected.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = function(e) {
assert_equals(e.data.length, expected.length);
for (var i = 0; i < e.data.length; i++)
assert_array_equals(e.data[i], expected[i]);
resolve();
};
frame.contentWindow.navigator.serviceWorker.controller.postMessage(
{port:channel.port2, options:query_options},
[channel.port2]);
});
}
promise_test(function(t) {
var frame;
return service_worker_unregister_and_register(
t, 'resources/clients-matchall-worker.js', scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(iframe_url); })
.then(function(f) {
frame = f;
return new Promise(function(resolve, reject) {
var w = new SharedWorker(shared_worker_url);
w.port.onmessage = resolve;
});
})
.then(function() {
return test_matchall(frame, expected_without_type, {});
})
.then(function() {
return test_matchall(frame, expected_with_window, {type:'window'});
})
//.then(function() {
// return test_matchall(frame, expected_with_shared_worker,
// {type:'sharedworker'});
// })
//.then(function() {
// return test_matchall(frame, expected_with_all, {type:'all'});
// })
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
});
}, 'Verify matchAll() with various client types');
</script>

View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<title>Service Worker: Clients.matchAll with includeUncontrolled</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var base_url = 'resources/blank.html'; // This is out-of-scope.
var scope = base_url + '?clients-matchAll-includeUncontrolled';
var frames = [];
// Creates 3 iframes, 2 for in-scope and 1 for out-of-scope.
// The frame opened for scope + '#2' is returned via a promise.
function create_iframes(scope) {
return with_iframe(base_url)
.then(function(frame0) {
frames.push(frame0);
return with_iframe(scope + '#1');
})
.then(function(frame1) {
frames.push(frame1);
return with_iframe(scope + '#2');
})
.then(function(frame2) {
frames.push(frame2);
return frame2;
})
}
var expected_without_include_uncontrolled = [
/* visibilityState, focused, url, frameType */
['visible', false, new URL(scope + '#1', location).toString(), 'nested'],
['visible', true, new URL(scope + '#2', location).toString(), 'nested']
];
var expected_with_include_uncontrolled = [
/* visibilityState, focused, url, frameType */
['visible', true, location.href, 'top-level'],
['visible', false, new URL(scope + '#1', location).toString(), 'nested'],
['visible', true, new URL(scope + '#2', location).toString(), 'nested'],
['visible', false, new URL(base_url, location).toString(), 'nested']
];
function test_matchall(frame, expected, query_options) {
// Make sure we have focus for '#2' frame and its parent window.
frame.focus();
frame.contentWindow.focus();
expected.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = function(e) {
// Ignore hidden clients which may be coming from background tabs, or
// clients unrelated to this test.
var data = e.data.filter(function(info) {
return info[0] == 'visible' &&
info[2].indexOf('service-worker') > -1;
});
data.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
assert_equals(data.length, expected.length);
for (var i = 0; i < data.length; i++)
assert_array_equals(data[i], expected[i]);
resolve(frame);
};
frame.contentWindow.navigator.serviceWorker.controller.postMessage(
{port:channel.port2, options:query_options},
[channel.port2]);
});
}
// Run clients.matchAll without and with includeUncontrolled=true.
// (We want to run the two tests sequentially in the same async_test
// so that we can use the same set of iframes without intefering each other.
async_test(function(t) {
service_worker_unregister_and_register(
t, 'resources/clients-matchall-worker.js', scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return create_iframes(scope); })
.then(function(frame) {
return test_matchall(frame, expected_without_include_uncontrolled);
})
.then(function(frame) {
return test_matchall(frame, expected_with_include_uncontrolled,
{includeUncontrolled:true});
})
.then(function() {
frames.forEach(function(f) { f.remove() });
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Verify matchAll() respect includeUncontrolled');
</script>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<title>Service Worker: Clients.matchAll</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var scope = 'resources/blank.html?clients-matchAll';
var t = async_test('Test Clients.matchAll()');
var frames = [];
t.step(function() {
service_worker_unregister_and_register(
t, 'resources/clients-matchall-worker.js', scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(scope + '#1'); })
.then(function(frame1) {
frames.push(frame1);
frame1.focus();
return with_iframe(scope + '#2');
})
.then(function(frame2) {
frames.push(frame2);
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(onMessage);
frame2.contentWindow.navigator.serviceWorker.controller.postMessage(
{port:channel.port2}, [channel.port2]);
})
.catch(unreached_rejection(t));
});
var expected = [
/* visibilityState, focused, url, frameType */
['visible', true, new URL(scope + '#1', location).toString(), 'nested'],
['visible', false, new URL(scope + '#2', location).toString(), 'nested']
];
function onMessage(e) {
assert_equals(e.data.length, 2);
assert_array_equals(e.data[0], expected[0]);
assert_array_equals(e.data[1], expected[1]);
frames.forEach(function(f) { f.remove(); });
service_worker_unregister_and_done(t, scope);
}
</script>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<title>Service Worker: Controller on load</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var t = async_test('controller is cleared on disconnected window');
t.step(function() {
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html';
var registration;
var controller;
var frame;
service_worker_unregister_and_register(t, url, scope)
.then(t.step_func(function(swr) {
registration = swr;
return wait_for_state(t, registration.installing, 'activated');
}))
.then(t.step_func(function() {
return with_iframe(scope)
}))
.then(t.step_func(function(f) {
frame = f;
var w = frame.contentWindow;
var swc = w.navigator.serviceWorker;
assert_true(swc.controller instanceof w.ServiceWorker,
'controller should be a ServiceWorker object');
frame.remove();
assert_equals(swc.controller, null,
'disconnected frame should not be controlled');
service_worker_unregister_and_done(t, scope);
}))
.catch(unreached_rejection(t));
});
</script>
</body>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<title>Service Worker: Controller on load</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var t = async_test('controller is set for a controlled document');
t.step(function() {
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html';
var registration;
var controller;
var frame;
service_worker_unregister_and_register(t, url, scope)
.then(t.step_func(function(swr) {
registration = swr;
return wait_for_state(t, registration.installing, 'activated');
}))
.then(t.step_func(function() {
return with_iframe(scope)
}))
.then(t.step_func(function(f) {
frame = f;
var w = frame.contentWindow;
controller = w.navigator.serviceWorker.controller;
assert_true(controller instanceof w.ServiceWorker,
'controller should be a ServiceWorker object');
assert_equals(controller.scriptURL, normalizeURL(url));
// objects from different windows should not be equal
assert_not_equals(controller, registration.active);
return w.navigator.serviceWorker.getRegistration();
}))
.then(t.step_func(function(frameRegistration) {
// SW objects from same window should be equal
assert_equals(frameRegistration.active, controller);
frame.remove();
service_worker_unregister_and_done(t, scope);
}))
.catch(unreached_rejection(t));
});
</script>
</body>

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<title>Service Worker: Controller on reload</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
promise_test(function(t) {
var scope = 'resources/blank.html';
var frame;
var registration;
var controller;
return service_worker_unregister(t, scope)
.then(function() {
return with_iframe(scope);
})
.then(function(f) {
frame = f;
return frame.contentWindow.navigator.serviceWorker.register(
'resources/empty-worker.js', {scope: scope});
})
.then(function(swr) {
registration = swr;
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
var w = frame.contentWindow;
assert_equals(w.navigator.serviceWorker.controller, null,
'controller should be null until the document is ' +
'reloaded');
return new Promise(function(resolve) {
frame.onload = function() { resolve(); }
w.location.reload();
});
})
.then(function() {
var w = frame.contentWindow;
controller = w.navigator.serviceWorker.controller;
assert_true(controller instanceof w.ServiceWorker,
'controller should be a ServiceWorker object upon reload');
// objects from separate windows should not be equal
assert_not_equals(controller, registration.active);
return w.navigator.serviceWorker.getRegistration();
})
.then(function(frameRegistration) {
assert_equals(frameRegistration.active, controller);
frame.remove();
service_worker_unregister_and_done(t, scope);
});
}, 'controller is set upon reload after registration');
</script>
</body>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var script = 'resources/extendable-event-async-waituntil.js';
var scope = 'resources/async-waituntil';
var worker;
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = function(e) { resolve(e.data); }
});
worker.postMessage({port: channel.port2}, [channel.port2]);
return saw_message;
})
.then(function(message) {
assert_equals(message, 'PASS');
return service_worker_unregister_and_done(t, scope);
})
}, 'Calling waitUntil asynchronously throws an exception');
</script>

View file

@ -0,0 +1,125 @@
<!DOCTYPE html>
<title>ExtendableEvent: waitUntil</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
function runTest(test, scope, onRegister) {
var script = 'resources/extendable-event-waituntil.js?' + scope;
service_worker_unregister_and_register(test, script, scope)
.then(function(registration) {
onRegister(registration.installing);
});
}
// Sends a SYN to the worker and asynchronously listens for an ACK; sets
// |obj.synced| to true once ack'd.
function syncWorker(test, worker, obj) {
var channel = new MessageChannel();
channel.port1.onmessage = test.step_func(function(e) {
var message = e.data;
assert_equals(message, 'SYNC',
'Should receive sync message from worker.');
obj.synced = true;
channel.port1.postMessage('ACK');
});
worker.postMessage({port: channel.port2}, [channel.port2]);
}
async_test(function(t) {
// Passing scope as the test switch for worker script.
var scope = 'resources/install-fulfilled';
var onRegister = function(worker) {
var obj = {};
wait_for_state(t, worker, 'installed')
.then(function() {
assert_true(
obj.synced,
'state should be "installed" after the waitUntil promise ' +
'for "oninstall" is fulfilled.');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
syncWorker(t, worker, obj);
};
runTest(t, scope, onRegister);
}, 'Test install event waitUntil fulfilled');
async_test(function(t) {
var scope = 'resources/install-multiple-fulfilled';
var onRegister = function(worker) {
var obj1 = {};
var obj2 = {};
wait_for_state(t, worker, 'installed')
.then(function() {
assert_true(
obj1.synced && obj2.synced,
'state should be "installed" after all waitUntil promises ' +
'for "oninstall" are fulfilled.');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
syncWorker(t, worker, obj1);
syncWorker(t, worker, obj2);
};
runTest(t, scope, onRegister);
}, 'Test ExtendableEvent multiple waitUntil fulfilled.');
async_test(function(t) {
var scope = 'resources/install-reject-precedence';
var onRegister = function(worker) {
wait_for_state(t, worker, 'redundant')
.then(function() {
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
};
runTest(t, scope, onRegister);
}, 'Test ExtendableEvent waitUntil reject precedence.');
async_test(function(t) {
var scope = 'resources/activate-fulfilled';
var onRegister = function(worker) {
var obj = {};
wait_for_state(t, worker, 'activating')
.then(function() {
syncWorker(t, worker, obj);
return wait_for_state(t, worker, 'activated');
})
.then(function() {
assert_true(
obj.synced,
'state should be "activated" after the waitUntil promise ' +
'for "onactivate" is fulfilled.');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
};
runTest(t, scope, onRegister);
}, 'Test activate event waitUntil fulfilled');
async_test(function(t) {
var scope = 'resources/install-rejected';
var onRegister = function(worker) {
wait_for_state(t, worker, 'redundant')
.then(function() {
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
};
runTest(t, scope, onRegister);
}, 'Test install event waitUntil rejected');
async_test(function(t) {
var scope = 'resources/activate-rejected';
var onRegister = function(worker) {
wait_for_state(t, worker, 'activated')
.then(function() {
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
};
runTest(t, scope, onRegister);
}, 'Test activate event waitUntil rejected.');
</script>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<title>Service Worker: canvas tainting of the fetched image using cached responses</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<body>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-canvas-tainting-iframe.html?cache';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var host_info = get_host_info();
login_https(t)
.then(function() {
return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
})
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data.results, 'finish');
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
});
})
.catch(unreached_rejection(t));
}, 'Verify canvas tainting of fetched image in a Service Worker');
</script>
</body>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<title>Service Worker: canvas tainting of the fetched image</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<body>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-canvas-tainting-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var host_info = get_host_info();
login_https(t)
.then(function() {
return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
})
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data.results, 'finish');
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
});
})
.catch(unreached_rejection(t));
}, 'Verify canvas tainting of fetched image in a Service Worker');
</script>
</body>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<title>Service Worker: CORS XHR of fetch()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<body>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-cors-xhr-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var host_info = get_host_info();
login_https(t)
.then(function() {
return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
})
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data.results, 'finish');
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
});
})
.catch(unreached_rejection(t));
}, 'Verify CORS XHR of fetch() in a Service Worker');
</script>
</body>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<title>Service Worker: CSP control of fetch()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-csp-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var host_info = get_host_info();
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
return new Promise(function(resolve, reject) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data.results, 'finish');
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
});
})
.catch(unreached_rejection(t));
}, 'Verify CSP control of fetch() in a Service Worker');
</script>

View file

@ -0,0 +1,65 @@
<!DOCTYPE html>
<title>ServiceWorker: navigator.serviceWorker.waiting</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
promise_test(function(t) {
var scope =
'resources/fetch-event-after-navigation-within-page-iframe.html' +
'?hashchange';
var worker = 'resources/simple-intercept-worker.js';
var frame;
return service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return frame.contentWindow.fetch_url('simple.txt');
})
.then(function(response) {
assert_equals(response, 'intercepted by service worker');
frame.contentWindow.location.hash = 'foo';
return frame.contentWindow.fetch_url('simple.txt');
})
.then(function(response) {
assert_equals(response, 'intercepted by service worker');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
}, 'Service Worker should respond to fetch event after the hash changes');
promise_test(function(t) {
var scope =
'resources/fetch-event-after-navigation-within-page-iframe.html' +
'?pushState';
var worker = 'resources/simple-intercept-worker.js';
var frame;
return service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return frame.contentWindow.fetch_url('simple.txt');
})
.then(function(response) {
assert_equals(response, 'intercepted by service worker');
frame.contentWindow.history.pushState('', '', 'bar');
return frame.contentWindow.fetch_url('simple.txt');
})
.then(function(response) {
assert_equals(response, 'intercepted by service worker');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
}, 'Service Worker should respond to fetch event after the pushState');
</script>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var script = 'resources/fetch-event-async-respond-with-worker.js';
var scope = 'resources/simple.html';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(frame) {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = function(e) { resolve(e.data); }
});
var worker = frame.contentWindow.navigator.serviceWorker.controller;
worker.postMessage({port: channel.port2}, [channel.port2]);
frame.remove();
return saw_message;
})
.then(function(message) {
assert_equals(message, 'PASS');
return service_worker_unregister_and_done(t, scope);
})
}, 'Calling respondWith asynchronously throws an exception');
</script>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<title>Service Worker: Fetch event network error</title>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
var resolve_test_done;
var test_done_promise = new Promise(function(resolve) {
resolve_test_done = resolve;
});
// Called by the child frame.
function notify_test_done(result) {
resolve_test_done(result);
}
promise_test(function(t) {
var scope = 'resources/fetch-event-network-error-controllee-iframe.html';
var script = 'resources/fetch-event-network-error-worker.js';
var frame;
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(f) {
frame = f;
return test_done_promise;
})
.then(function(result) {
frame.remove();
assert_equals(result, 'PASS');
return service_worker_unregister_and_done(t, scope);
});
}, 'Rejecting the fetch event or using preventDefault() causes a network ' +
'error');
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var script =
'resources/fetch-event-respond-with-stops-propagation-worker.js';
var scope = 'resources/simple.html';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(frame) {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve) {
channel.port1.onmessage = function(e) { resolve(e.data); }
});
var worker = frame.contentWindow.navigator.serviceWorker.controller;
worker.postMessage({port: channel.port2}, [channel.port2]);
frame.remove();
return saw_message;
})
.then(function(message) {
assert_equals(message, 'PASS');
return service_worker_unregister_and_done(t, scope);
})
}, 'respondWith() invokes stopImmediatePropagation()');
</script>

View file

@ -0,0 +1,575 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var worker = 'resources/fetch-event-test-worker.js';
async_test(function(t) {
var scope = 'resources/simple.html?string';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(
frame.contentDocument.body.textContent,
'Test string',
'Service Worker should respond to fetch with a test string');
assert_equals(
frame.contentDocument.contentType,
'text/plain',
'The content type of the response created with a string should be text/plain');
assert_equals(
frame.contentDocument.characterSet,
'UTF-8',
'The character set of the response created with a string should be UTF-8');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with string');
async_test(function(t) {
var scope = 'resources/simple.html?blob';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(
frame.contentDocument.body.textContent,
'Test blob',
'Service Worker should respond to fetch with a test string');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with blob body');
async_test(function(t) {
var scope = 'resources/simple.html?referrer';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(
frame.contentDocument.body.textContent,
'Referrer: ' + document.location.href,
'Service Worker should respond to fetch with the referrer URL');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the referrer URL');
function run_referrer_policy_tests(frame, referrer, href, origin) {
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{method: "GET", referrer: referrer})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the referrer URL when a member of RequestInit is present');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{method: "GET", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer when a member of RequestInit is present with an HTTP request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the referrer with ""');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with ""');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '/' + '\n' +
'ReferrerPolicy: origin',
'Service Worker should respond to fetch with the referrer origin with "origin" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '/' + '\n' +
'ReferrerPolicy: origin',
'Service Worker should respond to fetch with the referrer origin with "origin" and a cross origin request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "origin-when-cross-origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: origin-when-cross-origin',
'Service Worker should respond to fetch with the referrer URL with "origin-when-cross-origin" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "origin-when-cross-origin", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + origin + '/' + '\n' +
'ReferrerPolicy: origin-when-cross-origin',
'Service Worker should respond to fetch with the referrer origin with "origin-when-cross-origin" and a cross origin request');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "no-referrer-when-downgrade", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with "no-referrer-when-downgrade" and a same origin request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url,
{referrerPolicy: "no-referrer-when-downgrade", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with no referrer with "no-referrer-when-downgrade" and an HTTP request');
var http_url = get_host_info()['HTTP_ORIGIN'] + base_path() +
'/resources/simple.html?referrerFull';
return frame.contentWindow.fetch(http_url, {referrerPolicy: "unsafe-url", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: ' + href + '\n' +
'ReferrerPolicy: unsafe-url',
'Service Worker should respond to fetch with no referrer with "unsafe-url"');
return frame.contentWindow.fetch('resources/simple.html?referrerFull',
{referrerPolicy: "no-referrer", referrer: referrer});
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
assert_equals(
response_text,
'Referrer: about:client\n' +
'ReferrerPolicy: no-referrer',
'Service Worker should respond to fetch with no referrer URL with "no-referrer"');
});
}
async_test(function(t) {
var scope = 'resources/simple.html?referrerPolicy';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
assert_equals(
frame.contentDocument.body.textContent,
'ReferrerPolicy: no-referrer-when-downgrade',
'Service Worker should respond to fetch with the default referrer policy');
// First, run the referrer policy tests without passing a referrer in RequestInit.
return run_referrer_policy_tests(frame, undefined, frame.contentDocument.location.href,
frame.contentDocument.location.origin);
})
.then(function() {
// Now, run the referrer policy tests while passing a referrer in RequestInit.
var referrer = get_host_info()['HTTPS_ORIGIN'] + base_path() + 'fake-referrer';
return run_referrer_policy_tests(frame, 'fake-referrer', referrer,
frame.contentDocument.location.origin);
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the referrer URL');
async_test(function(t) {
var scope = 'resources/simple.html?clientId';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
assert_equals(
frame.contentDocument.body.textContent,
'Client ID Not Found',
'Service Worker should respond to fetch with a client id');
return frame.contentWindow.fetch('resources/other.html?clientId');
})
.then(function(response) { return response.text(); })
.then(function(response_text) {
var new_client_id = response_text.substr(17);
assert_equals(
response_text.substr(0, 15),
'Client ID Found',
'Service Worker should respond to fetch with an existing client id');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with an existing client id');
async_test(function(t) {
var scope = 'resources/simple.html?ignore';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(frame.contentDocument.body.textContent,
'Here\'s a simple html file.\n',
'Response should come from fallback to native fetch');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker does not respond to fetch event');
async_test(function(t) {
var scope = 'resources/simple.html?null';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(frame.contentDocument.body.textContent,
'',
'Response should be the empty string');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with null response body');
async_test(function(t) {
var scope = 'resources/simple.html?fetch';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(frame.contentDocument.body.textContent,
'Here\'s an other html file.\n',
'Response should come from fetched other file');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker fetches other file in fetch event');
async_test(function(t) {
var scope = 'resources/simple.html?form-post';
var frame_name = 'xhr-post-frame';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function(sw) {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.name = frame_name;
document.body.appendChild(frame);
var form = document.createElement('form');
form.target = frame_name;
form.action = scope;
form.method = 'post';
var input1 = document.createElement('input');
input1.type = 'text';
input1.value = 'testValue1';
input1.name = 'testName1'
form.appendChild(input1);
var input2 = document.createElement('input');
input2.type = 'text';
input2.value = 'testValue2';
input2.name = 'testName2'
form.appendChild(input2);
document.body.appendChild(form);
frame.onload = function() {
document.body.removeChild(form);
resolve(frame);
};
form.submit();
});
})
.then(function(frame) {
assert_equals(frame.contentDocument.body.textContent,
'POST:application/x-www-form-urlencoded:' +
'testName1=testValue1&testName2=testValue2');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with POST form');
async_test(function(t) {
var scope = 'resources/simple.html?multiple-respond-with';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(
frame.contentDocument.body.textContent,
'(0)(1)[InvalidStateError](2)[InvalidStateError]',
'Multiple calls of respondWith must throw InvalidStateErrors.');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Multiple calls of respondWith must throw InvalidStateErrors');
async_test(function(t) {
var scope = 'resources/simple.html?used-check';
var first_frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(frame) {
assert_equals(frame.contentDocument.body.textContent,
'Here\'s an other html file.\n',
'Response should come from fetched other file');
first_frame = frame;
return with_iframe(scope);
})
.then(function(frame) {
// When we access to the scope in the second time, the content of the
// response is generated inside the ServiceWorker. The body contains
// the value of bodyUsed of the first response which is already
// consumed by FetchEvent.respondWith method.
assert_equals(
frame.contentDocument.body.textContent,
'bodyUsed: true',
'event.respondWith must set the used flag.');
first_frame.remove();
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker event.respondWith must set the used flag');
async_test(function(t) {
var scope = 'resources/simple.html?fragment-check';
var fragment = '#/some/fragment';
var first_frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope + fragment); })
.then(function(frame) {
assert_equals(
frame.contentDocument.body.textContent,
'Fragment Found :' + fragment,
'Service worker should expose URL fragments in request.');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker must not expose FetchEvent URL fragments.');
async_test(function(t) {
var scope = 'resources/simple.html?cache';
var frame;
var cacheTypes = [
undefined, 'default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached'
];
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
assert_equals(frame.contentWindow.document.body.textContent, 'default');
var tests = cacheTypes.map(function(type) {
return new Promise(function(resolve, reject) {
var init = {cache: type};
if (type === 'only-if-cached') {
// For privacy reasons, for the time being, only-if-cached
// requires the mode to be same-origin.
init.mode = 'same-origin';
}
return frame.contentWindow.fetch(scope + '=' + type, init)
.then(function(response) { return response.text(); })
.then(function(response_text) {
var expected = (type === undefined) ? 'default' : type;
assert_equals(response_text, expected,
'Service Worker should respond to fetch with the correct type');
})
.then(resolve)
.catch(reject);
});
});
})
.then(function() {
return new Promise(function(resolve, reject) {
frame.addEventListener('load', function onLoad() {
frame.removeEventListener('load', onLoad);
try {
assert_equals(frame.contentWindow.document.body.textContent,
'no-cache');
resolve();
} catch (e) {
reject(e);
}
});
frame.contentWindow.location.reload();
});
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the correct cache types');
async_test(function(t) {
var scope = 'resources/simple.html?eventsource';
var frame;
function test_eventsource(opts) {
return new Promise(function(resolve, reject) {
var eventSource = new frame.contentWindow.EventSource(scope, opts);
eventSource.addEventListener('message', function(msg) {
eventSource.close();
try {
var data = JSON.parse(msg.data);
assert_equals(data.mode, 'cors',
'EventSource should make CORS requests.');
assert_equals(data.cache, 'no-store',
'EventSource should bypass the http cache.');
var expectedCredentials = opts.withCredentials ? 'include'
: 'same-origin';
assert_equals(data.credentials, expectedCredentials,
'EventSource should pass correct credentials mode.');
resolve();
} catch (e) {
reject(e);
}
});
eventSource.addEventListener('error', function(e) {
eventSource.close();
reject('The EventSource fired an error event.');
});
});
}
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return test_eventsource({ withCredentials: false });
})
.then(function() {
return test_eventsource({ withCredentials: true });
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker should intercept EventSource');
async_test(function(t) {
var scope = 'resources/simple.html?integrity';
var frame;
var integrity_metadata = 'gs0nqru8KbsrIt5YToQqS9fYao4GQJXtcId610g7cCU=';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
// A request has associated integrity metadata (a string).
// Unless stated otherwise, it is the empty string.
assert_equals(
frame.contentDocument.body.textContent, '');
return new Promise(function(resolve, reject) {
return frame.contentWindow.fetch(scope,
{'integrity': integrity_metadata})
.then(function(response) {
return response.text();
})
.then(function(response_text) {
// Should get the same integrity metadata.
assert_equals(response_text, integrity_metadata,
'Service Worker should respond to fetch with the correct integrity');
})
.then(resolve())
.catch(reject());
});
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the correct integrity_metadata');
</script>
</body>

View file

@ -0,0 +1,221 @@
<!DOCTYPE html>
<title>Service Worker: Fetch for the frame loading.</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var worker = 'resources/fetch-rewrite-worker.js';
var path = base_path() + 'resources/fetch-access-control.py';
var host_info = get_host_info();
if (window.testRunner) {
testRunner.setCanOpenWindows();
}
function getLoadedObject(win, contentFunc, closeFunc) {
return new Promise(function(resolve) {
function done(contentString) {
var result = null;
// fetch-access-control.py returns a string like "report( <json> )".
// Eval the returned string with a report functionto get the json
// object.
try {
function report(obj) { result = obj };
eval(contentString);
} catch(e) {
// just resolve null if we get unexpected page content
}
closeFunc(win);
resolve(result);
}
// We can't catch the network error on window. So we use the timer.
var timeout = setTimeout(function() {
// Failure pages are considered cross-origin in some browsers. This
// means you cannot even .resolve() the window because the check for
// the .then property will throw. Instead, treat cross-origin
// failure pages as the empty string which will fail to parse as the
// expected json result.
var content = '';
try {
content = contentFunc(win);
} catch(e) {
// use default empty string for cross-domain window
}
done(content);
}, 10000);
win.onload = function() {
clearTimeout(timeout);
var content = contentFunc(win);
done(content);
};
});
}
function getLoadedFrameAsObject(frame) {
return getLoadedObject(frame, function(f) {
return f.contentDocument.body.textContent;
}, function(f) {
f.parentNode.removeChild(f);
});
}
function getLoadedWindowAsObject(win) {
return getLoadedObject(win, function(w) {
return w.document.body.textContent
}, function(w) {
w.close();
});
}
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/frame-basic';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
frame = document.createElement('iframe');
frame.src =
scope + '?url=' +
encodeURIComponent(host_info['HTTPS_ORIGIN'] + path);
document.body.appendChild(frame);
return getLoadedFrameAsObject(frame);
})
.then(function(result) {
assert_equals(
result.jsonpResult,
'success',
'Basic type response could be loaded in the iframe.');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Basic type response could be loaded in the iframe.');
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/frame-cors';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
frame = document.createElement('iframe');
frame.src =
scope + '?mode=cors&url=' +
encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path +
'?ACAOrigin=' + host_info['HTTPS_ORIGIN']);
document.body.appendChild(frame);
return getLoadedFrameAsObject(frame);
})
.then(function(result) {
assert_equals(
result.jsonpResult,
'success',
'CORS type response could be loaded in the iframe.');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'CORS type response could be loaded in the iframe.');
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/frame-opaque';
var frame;
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
frame = document.createElement('iframe');
frame.src =
scope + '?mode=no-cors&url=' +
encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path);
document.body.appendChild(frame);
return getLoadedFrameAsObject(frame);
})
.then(function(result) {
assert_equals(
result,
null,
'Opaque type response could not be loaded in the iframe.');
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Opaque type response could not be loaded in the iframe.');
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/window-basic';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
var win = window.open(
scope + '?url=' +
encodeURIComponent(host_info['HTTPS_ORIGIN'] + path));
return getLoadedWindowAsObject(win);
})
.then(function(result) {
assert_equals(
result.jsonpResult,
'success',
'Basic type response could be loaded in the new window.');
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Basic type response could be loaded in the new window.');
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/window-cors';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
var win = window.open(
scope + '?mode=cors&url=' +
encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path +
'?ACAOrigin=' + host_info['HTTPS_ORIGIN']));
return getLoadedWindowAsObject(win);
})
.then(function(result) {
assert_equals(
result.jsonpResult,
'success',
'CORS type response could be loaded in the new window.');
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'CORS type response could be loaded in the new window.');
async_test(function(t) {
var scope = 'resources/fetch-frame-resource/window-opaque';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
var win = window.open(
scope + '?mode=no-cors&url=' +
encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path));
return getLoadedWindowAsObject(win);
})
.then(function(result) {
assert_equals(
result,
null,
'Opaque type response could not be loaded in the new window.');
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Opaque type response could not be loaded in the new window.');
</script>
</body>

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<title>Service Worker: Visibility of headers during fetch.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var worker = 'resources/fetch-rewrite-worker.js';
var path = base_path() + 'resources/fetch-access-control.py';
var host_info = get_host_info();
var frame;
async_test(function(t) {
var scope = 'resources/fetch-header-visibility-iframe.html';
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() {
frame = document.createElement('iframe');
frame.src = scope;
document.body.appendChild(frame);
// Resolve a promise when we recieve 2 success messages
return new Promise(function(resolve, reject) {
var remaining = 4;
function onMessage(e) {
if (e.data == 'PASS') {
remaining--;
if (remaining == 0) {
resolve();
} else {
return;
}
} else {
reject(e.data);
}
window.removeEventListener('message', onMessage);
}
window.addEventListener('message', onMessage);
});
})
.then(function(result) {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Visibility of defaulted headers during interception');
</script>
</body>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<title>Service Worker: Mixed content of fetch()</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<body></body>
<script>
if (window.testRunner) {
// In Chromium we need to change the setting to disallow displaying insecure
// contents.
testRunner.overridePreference('WebKitAllowDisplayingInsecureContent', false);
}
async_test(function(t) {
var host_info = get_host_info();
window.addEventListener('message', t.step_func(on_message), false);
with_iframe(
host_info['HTTPS_ORIGIN'] + base_path() +
'resources/fetch-mixed-content-iframe.html?target=inscope');
function on_message(e) {
assert_equals(e.data.results, 'finish');
t.done();
}
}, 'Verify Mixed content of fetch() in a Service Worker');
</script>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<title>Service Worker: Mixed content of fetch()</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<body></body>
<script>
if (window.testRunner) {
// In Chromium we need to change the setting to disallow displaying insecure
// contents.
testRunner.overridePreference('WebKitAllowDisplayingInsecureContent', false);
}
async_test(function(t) {
var host_info = get_host_info();
window.addEventListener('message', t.step_func(on_message), false);
with_iframe(
host_info['HTTPS_ORIGIN'] + base_path() +
'resources/fetch-mixed-content-iframe.html?target=outscope');
function on_message(e) {
assert_equals(e.data.results, 'finish');
t.done();
}
}, 'Verify Mixed content of fetch() in a Service Worker');
</script>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<title>Service Worker: CSS's base URL must be the request URL even when fetched from other URL</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-request-css-base-url-iframe.html';
var SCRIPT = 'resources/fetch-request-css-base-url-worker.js';
var worker;
var testDonePromise;
return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() {
return new Promise(function(resolve) {
var channel = new MessageChannel();
testDonePromise = new Promise(function(resolveTestDone) {
channel.port1.onmessage = t.step_func(function(msg) {
if (msg.data.ready) {
resolve();
return;
}
var result = msg.data;
var base = get_host_info()['HTTPS_ORIGIN'] + base_path();
assert_equals(
result.url,
base + 'resources/dummy.png',
'The base URL while loading the images referred from CSS ' +
'must be the request URL of CSS.');
assert_equals(
result.referrer,
base + 'resources/fetch-request-css-base-url-style.css',
'While loading the image defined in CSS the referrer must ' +
'be the request URL of CSS.');
resolveTestDone();
});
});
worker.postMessage(
{port: channel.port2}, [channel.port2]);
});
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
return testDonePromise.then(function() {
f.remove();
return service_worker_unregister_and_done(t, SCOPE);
});
})
.catch(unreached_rejection(t));
}, 'CSS\'s base URL must be the request URL even when fetched from other URL.');
</script>

View file

@ -0,0 +1,176 @@
<!DOCTYPE html>
<title>Service Worker: FetchEvent for css image</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
var SCOPE = 'resources/fetch-request-resources-iframe.https.html';
var SCRIPT = 'resources/fetch-request-resources-worker.js';
var host_info = get_host_info();
var LOCAL_URL =
host_info['HTTPS_ORIGIN'] + base_path() + 'resources/dummy?test';
var REMOTE_URL =
host_info['HTTPS_REMOTE_ORIGIN'] + base_path() + 'resources/dummy?test';
function css_image_test(expected_results, frame, url, type,
expected_mode, expected_credentials) {
expected_results[url] = {
url: url,
mode: expected_mode,
credentials: expected_credentials,
message: 'CSSImage load (url:' + url + ' type:' + type + ')'
};
return frame.contentWindow.load_css_image(url, type);
}
function css_image_set_test(expected_results, frame, url, type,
expected_mode, expected_credentials) {
expected_results[url] = {
url: url,
mode: expected_mode,
credentials: expected_credentials,
message: 'CSSImageSet load (url:' + url + ' type:' + type + ')'
};
return frame.contentWindow.load_css_image_set(url, type);
}
function create_message_promise(t, expected_results, frame,
worker, test_count, scope) {
return new Promise(function(resolve) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(msg) {
if (msg.data.ready) {
resolve();
return;
}
var result = msg.data;
var expected = expected_results[result.url];
if (!expected) {
return;
}
assert_equals(
result.mode, expected.mode,
'mode of ' + expected.message + ' must be ' +
expected.mode + '.');
assert_equals(
result.credentials, expected.credentials,
'credentials of ' + expected.message + ' must be ' +
expected.credentials + '.');
--test_count;
delete expected_results[result.url];
if (test_count == 0) {
frame.remove();
service_worker_unregister_and_done(t, scope);
}
});
worker.postMessage(
{port: channel.port2}, [channel.port2]);
});
}
async_test(function(t) {
var scope = SCOPE + "?img=backgroundImage";
var test_count = 2;
var expected_results = {};
var worker;
var frame;
service_worker_unregister_and_register(t, SCRIPT, scope)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return create_message_promise(t, expected_results, frame,
worker, test_count, scope);
})
.then(function() {
css_image_test(expected_results, frame, LOCAL_URL + Date.now(),
'backgroundImage', 'no-cors', 'include');
css_image_test(expected_results, frame, REMOTE_URL + Date.now(),
'backgroundImage', 'no-cors', 'include');
})
.catch(unreached_rejection(t));
}, 'Verify FetchEvent for css image (backgroundImage).');
async_test(function(t) {
var scope = SCOPE + "?img=shapeOutside";
var test_count = 2;
var expected_results = {};
var worker;
var frame;
service_worker_unregister_and_register(t, SCRIPT, scope)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return create_message_promise(t, expected_results, frame,
worker, test_count, scope);
})
.then(function() {
css_image_test(expected_results, frame, LOCAL_URL + Date.now(),
'shapeOutside', 'cors', 'same-origin');
css_image_test(expected_results, frame, REMOTE_URL + Date.now(),
'shapeOutside', 'cors', 'same-origin');
})
.catch(unreached_rejection(t));
}, 'Verify FetchEvent for css image (shapeOutside).');
async_test(function(t) {
var scope = SCOPE + "?img_set=backgroundImage";
var test_count = 2;
var expected_results = {};
var worker;
var frame;
service_worker_unregister_and_register(t, SCRIPT, scope)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return create_message_promise(t, expected_results, frame,
worker, test_count, scope);
})
.then(function() {
css_image_set_test(expected_results, frame, LOCAL_URL + Date.now(),
'backgroundImage', 'no-cors', 'include');
css_image_set_test(expected_results, frame, REMOTE_URL + Date.now(),
'backgroundImage', 'no-cors', 'include');
})
.catch(unreached_rejection(t));
}, 'Verify FetchEvent for css image-set (backgroundImage).');
async_test(function(t) {
var scope = SCOPE + "?img_set=shapeOutside";
var test_count = 2;
var expected_results = {};
var worker;
var frame;
service_worker_unregister_and_register(t, SCRIPT, scope)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return create_message_promise(t, expected_results, frame,
worker, test_count, scope);
})
.then(function() {
css_image_set_test(expected_results, frame, LOCAL_URL + Date.now(),
'shapeOutside', 'cors', 'same-origin');
css_image_set_test(expected_results, frame, REMOTE_URL + Date.now(),
'shapeOutside', 'cors', 'same-origin');
})
.catch(unreached_rejection(t));
}, 'Verify FetchEvent for css image-set (shapeOutside).');
</script>

View file

@ -0,0 +1,113 @@
<!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="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
var expected_urls = [];
function xhr_fail_test(frame, url) {
expected_urls.push(url);
return new Promise(function(resolve, reject) {
frame.contentWindow.xhr(url)
.then(function(){
reject(url + ' should fail.');
})
.catch(function(){
resolve();
});
});
}
function xhr_succeed_test(frame, url) {
expected_urls.push(url);
return new Promise(function(resolve, reject) {
frame.contentWindow.xhr(url)
.then(function(){
resolve();
})
.catch(function(){
reject(url + ' should succeed.');
});
});
}
async_test(function(t) {
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 OTHER_BASE_URL = host_info['HTTPS_REMOTE_ORIGIN'] +
path + 'resources/fetch-access-control.py?';
var REDIRECT_URL = host_info['HTTPS_ORIGIN'] +
path + 'resources/redirect.py?Redirect=';
var frame;
var worker;
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
frame = f;
return xhr_succeed_test(frame, BASE_URL);
})
.then(function(f) {
return xhr_fail_test(frame, OTHER_BASE_URL);
})
.then(function(f) {
return xhr_succeed_test(frame, OTHER_BASE_URL + 'ACAOrigin=*');
})
.then(function(f) {
return xhr_succeed_test(frame,
REDIRECT_URL + encodeURIComponent(BASE_URL));
})
.then(function() {
return xhr_fail_test(
frame,
REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL));
})
.then(function() {
return xhr_succeed_test(
frame,
REDIRECT_URL +
encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'));
})
.then(function() {
return new Promise(function(resolve) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(msg) {
frame.remove();
resolve(msg);
});
worker.postMessage({port: channel.port2}, [channel.port2]);
});
})
.then(function(msg) {
var requests = msg.data.requests;
assert_equals(requests.length, expected_urls.length + 1,
'The count of the requests which are passed to the ' +
'ServiceWorker must be correct.');
assert_equals(requests[0].url, new URL(SCOPE, location).toString(),
'The first request to the SW must be the request for ' +
'the page.');
assert_equals(requests[0].mode, 'navigate',
'The mode of the first request to the SW must be ' +
'navigate');
for (var i = 0; i < expected_urls.length; ++i) {
assert_equals(requests[i + 1].url, expected_urls[i],
'The URL of the request which was passed from XHR ' +
'to the ServiceWorker must be correct.');
assert_equals(requests[i + 1].mode, 'cors',
'The mode of the request which was passed from XHR ' +
'to the ServiceWorker must be cors.');
}
service_worker_unregister_and_done(t, SCOPE);
})
.catch(unreached_rejection(t));
}, 'Verify the fallback behavior of FetchEvent');
</script>

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<title>Service Worker: the headers of FetchEvent shouldn't contain freshness headers</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-request-no-freshness-headers-iframe.html';
var SCRIPT = 'resources/fetch-request-no-freshness-headers-worker.js';
var worker;
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
return new Promise(function(resolve) {
frame.onload = function() {
resolve(frame);
};
frame.contentWindow.location.reload();
});
})
.then(function(frame) {
return new Promise(function(resolve) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(msg) {
frame.remove();
resolve(msg);
});
worker.postMessage(
{port: channel.port2}, [channel.port2]);
});
})
.then(function(msg) {
var freshness_headers = {
'if-none-match': true,
'if-modified-since': true
};
msg.data.requests.forEach(t.step_func(function(request) {
request.headers.forEach(t.step_func(function(header) {
assert_false(
!!freshness_headers[header[0]],
header[0] + ' header must not be set in the ' +
'FetchEvent\'s request. (url = ' + request.url + ')');
}));
}))
service_worker_unregister_and_done(t, SCOPE);
})
.catch(unreached_rejection(t));
}, 'The headers of FetchEvent shouldn\'t contain freshness headers.');
</script>

View file

@ -0,0 +1,372 @@
<!DOCTYPE html>
<title>Service Worker: FetchEvent for resources</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
function assert_resolves(promise, description) {
return promise.catch(function(reason) {
throw new Error(description + ' - ' + reason.message);
});
}
function assert_rejects(promise, description) {
return promise.then(
function() { throw new Error(description); },
function() {});
}
function iframe_test(url, timeout_enabled) {
return new Promise(function(resolve, reject) {
var frame = document.createElement('iframe');
frame.src = url;
if (timeout_enabled) {
// We can't catch the network error on iframe. So we use the timer for
// failure detection.
var timer = setTimeout(function() {
reject(new Error('iframe load timeout'));
frame.remove();
}, 10000);
}
frame.onload = function() {
if (timeout_enabled)
clearTimeout(timer);
if (frame.contentDocument.body.textContent == 'Hello world\n')
resolve();
else
reject(new Error('content mismatch'));
frame.remove();
};
document.body.appendChild(frame);
});
}
promise_test(function(t) {
var SCOPE = 'resources/fetch-request-redirect-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
var IMAGE_URL = base_path() + 'resources/square.png';
var AUDIO_URL = base_path() + 'resources/silence.oga';
var XHR_URL = base_path() + 'resources/simple.txt';
var HTML_URL = base_path() + 'resources/dummy.html';
var REDIRECT_TO_IMAGE_URL = REDIRECT_URL + encodeURIComponent(IMAGE_URL);
var REDIRECT_TO_AUDIO_URL = REDIRECT_URL + encodeURIComponent(AUDIO_URL);
var REDIRECT_TO_XHR_URL = REDIRECT_URL + encodeURIComponent(XHR_URL);
var REDIRECT_TO_HTML_URL = REDIRECT_URL + encodeURIComponent(HTML_URL);
var worker;
var frame;
return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
frame = f;
return Promise.all([
// XMLHttpRequest tests.
assert_resolves(frame.contentWindow.xhr(XHR_URL),
'Normal XHR should succeed.'),
assert_resolves(frame.contentWindow.xhr(REDIRECT_TO_XHR_URL),
'Redirected XHR should succeed.'),
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&redirect-mode=follow'),
'Redirected XHR with Request.redirect=follow should succeed.'),
assert_rejects(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&redirect-mode=error'),
'Redirected XHR with Request.redirect=error should fail.'),
assert_rejects(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&redirect-mode=manual'),
'Redirected XHR with Request.redirect=manual should fail.'),
// Image loading tests.
assert_resolves(frame.contentWindow.load_image(IMAGE_URL),
'Normal image resource should be loaded.'),
assert_resolves(
frame.contentWindow.load_image(REDIRECT_TO_IMAGE_URL),
'Redirected image resource should be loaded.'),
assert_resolves(
frame.contentWindow.load_image(
'./?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
'&redirect-mode=follow'),
'Loading redirected image with Request.redirect=follow should' +
' succeed.'),
assert_rejects(
frame.contentWindow.load_image(
'./?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
'&redirect-mode=error'),
'Loading redirected image with Request.redirect=error should ' +
'fail.'),
assert_rejects(
frame.contentWindow.load_image(
'./?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
'&redirect-mode=manual'),
'Loading redirected image with Request.redirect=manual should' +
' fail.'),
// Audio loading tests.
assert_resolves(frame.contentWindow.load_audio(AUDIO_URL),
'Normal audio resource should be loaded.'),
assert_resolves(
frame.contentWindow.load_audio(REDIRECT_TO_AUDIO_URL),
'Redirected audio resource should be loaded.'),
assert_resolves(
frame.contentWindow.load_audio(
'./?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
'&redirect-mode=follow'),
'Loading redirected audio with Request.redirect=follow should' +
' succeed.'),
assert_rejects(
frame.contentWindow.load_audio(
'./?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
'&redirect-mode=error'),
'Loading redirected audio with Request.redirect=error should ' +
'fail.'),
assert_rejects(
frame.contentWindow.load_audio(
'./?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
'&redirect-mode=manual'),
'Loading redirected audio with Request.redirect=manual should' +
' fail.'),
// Iframe tests.
assert_resolves(iframe_test(HTML_URL),
'Normal iframe loading should succeed.'),
assert_resolves(
iframe_test(REDIRECT_TO_HTML_URL),
'Normal redirected iframe loading should succeed.'),
assert_rejects(
iframe_test(SCOPE + '?url=' +
encodeURIComponent(REDIRECT_TO_HTML_URL) +
'&redirect-mode=follow',
true /* timeout_enabled */),
'Redirected iframe loading with Request.redirect=follow should'+
' fail.'),
assert_rejects(
iframe_test(SCOPE + '?url=' +
encodeURIComponent(REDIRECT_TO_HTML_URL) +
'&redirect-mode=error',
true /* timeout_enabled */),
'Redirected iframe loading with Request.redirect=error should '+
'fail.'),
assert_resolves(
iframe_test(SCOPE + '?url=' +
encodeURIComponent(REDIRECT_TO_HTML_URL) +
'&redirect-mode=manual',
true /* timeout_enabled */),
'Redirected iframe loading with Request.redirect=manual should'+
' succeed.'),
]);
})
.then(function() {
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
}, 'Verify redirect mode of Fetch API and ServiceWorker FetchEvent.');
// test for reponse.redirected
promise_test(function(t) {
var SCOPE = 'resources/fetch-request-redirect-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
var XHR_URL = base_path() + 'resources/simple.txt';
var IMAGE_URL = base_path() + 'resources/square.png';
var REDIRECT_TO_XHR_URL = REDIRECT_URL + encodeURIComponent(XHR_URL);
var host_info = get_host_info();
var CROSS_ORIGIN_URL = host_info['HTTPS_REMOTE_ORIGIN'] + IMAGE_URL;
var REDIRECT_TO_CROSS_ORIGIN = REDIRECT_URL +
encodeURIComponent(CROSS_ORIGIN_URL + '?ACAOrigin=*');
var worker;
var frame;
return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
frame = f;
return Promise.all([
// XMLHttpRequest tests.
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(XHR_URL) +
'&expected_redirected=false' +
'&expected_resolves=true'),
'Normal XHR should be resolved and response should not be ' +
'redirected.'),
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&expected_redirected=true' +
'&expected_resolves=true'),
'Redirected XHR should be resolved and response should be ' +
'redirected.'),
// tests for request's mode = cors
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(XHR_URL) +
'&mode=cors' +
'&expected_redirected=false' +
'&expected_resolves=true'),
'Normal XHR should be resolved and response should not be ' +
'redirected even with CORS mode.'),
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&mode=cors' +
'&redirect-mode=follow' +
'&expected_redirected=true' +
'&expected_resolves=true'),
'Redirected XHR should be resolved and response.redirected ' +
'should be redirected with CORS mode.'),
// tests for request's mode = no-cors
// The response.redirect should be false since we will not add
// redirected url list when redirect-mode is not follow.
assert_rejects(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&mode=no-cors' +
'&redirect-mode=manual' +
'&expected_redirected=false' +
'&expected_resolves=false'),
'Redirected XHR should be reject and response should be ' +
'redirected with NO-CORS mode and redirect-mode=manual.'),
// tests for redirecting to a cors
assert_resolves(
frame.contentWindow.load_image(
'./?url=' + encodeURIComponent(REDIRECT_TO_CROSS_ORIGIN) +
'&mode=no-cors' +
'&redirect-mode=follow' +
'&expected_redirected=false' +
'&expected_resolves=true'),
'Redirected COS image should be reject and response should ' +
'not be redirected with NO-CORS mode.'),
]);
})
.then(function() {
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
}, 'Verify redirected of Response(Fetch API) and ServiceWorker FetchEvent.');
// test for reponse.redirected after cached
promise_test(function(t) {
var SCOPE = 'resources/fetch-request-redirect-iframe.html';
var SCRIPT = 'resources/fetch-rewrite-worker.js';
var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
var XHR_URL = base_path() + 'resources/simple.txt';
var IMAGE_URL = base_path() + 'resources/square.png';
var REDIRECT_TO_XHR_URL = REDIRECT_URL + encodeURIComponent(XHR_URL);
var host_info = get_host_info();
var CROSS_ORIGIN_URL = host_info['HTTPS_REMOTE_ORIGIN'] + IMAGE_URL;
var REDIRECT_TO_CROSS_ORIGIN = REDIRECT_URL +
encodeURIComponent(CROSS_ORIGIN_URL + '?ACAOrigin=*');
var worker;
var frame;
return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
frame = f;
return Promise.all([
// XMLHttpRequest tests.
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(XHR_URL) +
'&expected_redirected=false' +
'&expected_resolves=true' +
'&cache'),
'Normal XHR should be resolved and response should not be ' +
'redirected.'),
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&expected_redirected=true' +
'&expected_resolves=true' +
'&cache'),
'Redirected XHR should be resolved and response should be ' +
'redirected.'),
// tests for request's mode = cors
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(XHR_URL) +
'&mode=cors' +
'&expected_redirected=false' +
'&expected_resolves=true' +
'&cache'),
'Normal XHR should be resolved and response should not be ' +
'redirected even with CORS mode.'),
assert_resolves(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&mode=cors' +
'&redirect-mode=follow' +
'&expected_redirected=true' +
'&expected_resolves=true' +
'&cache'),
'Redirected XHR should be resolved and response.redirected ' +
'should be redirected with CORS mode.'),
// tests for request's mode = no-cors
// The response.redirect should be false since we will not add
// redirected url list when redirect-mode is not follow.
assert_rejects(
frame.contentWindow.xhr(
'./?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
'&mode=no-cors' +
'&redirect-mode=manual' +
'&expected_redirected=false' +
'&expected_resolves=false' +
'&cache'),
'Redirected XHR should be reject and response should be ' +
'redirected with NO-CORS mode and redirect-mode=manual.'),
// tests for redirecting to a cors
assert_resolves(
frame.contentWindow.load_image(
'./?url=' + encodeURIComponent(REDIRECT_TO_CROSS_ORIGIN) +
'&mode=no-cors' +
'&redirect-mode=follow' +
'&expected_redirected=false' +
'&expected_resolves=true' +
'&cache'),
'Redirected COS image should be reject and response should ' +
'not be redirected with NO-CORS mode.'),
]);
})
.then(function() {
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
}, 'Verify redirected of Response(Fetch API), Cache API and ServiceWorker ' +
'FetchEvent.');
</script>

View file

@ -0,0 +1,189 @@
<!DOCTYPE html>
<title>Service Worker: FetchEvent for resources</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
var url_count = 0;
var expected_results = {};
function image_test(frame, url, cross_origin, expected_mode,
expected_credentials) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
cross_origin: cross_origin,
mode: expected_mode,
credentials: expected_credentials,
integrity: '',
message: 'Image load (url:' +
actual_url + ' cross_origin:' + cross_origin + ')'
};
return frame.contentWindow.load_image(actual_url, cross_origin);
}
function script_test(frame, url, cross_origin, expected_mode,
expected_credentials) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
cross_origin: cross_origin,
mode: expected_mode,
credentials: expected_credentials,
integrity: '',
message: 'Script load (url:' +
actual_url + ' cross_origin:' + cross_origin + ')'
};
return frame.contentWindow.load_script(actual_url, cross_origin);
}
function css_test(frame, url, cross_origin, expected_mode,
expected_credentials) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
cross_origin: cross_origin,
mode: expected_mode,
credentials: expected_credentials,
integrity: '',
message: 'CSS load (url:' +
actual_url + ' cross_origin:' + cross_origin + ')'
};
return frame.contentWindow.load_css(actual_url, cross_origin);
}
function font_face_test(frame, url, expected_mode, expected_credentials) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
url: actual_url,
mode: expected_mode,
credentials: expected_credentials,
integrity: '',
message: 'FontFace load (url:' + actual_url + ')'
};
return frame.contentWindow.load_font(actual_url);
}
function script_integrity_test(frame, url, integrity, expected_integrity) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
url: actual_url,
mode: 'no-cors',
credentials: 'include',
integrity: expected_integrity,
message: 'Script load (url:' + actual_url + ')'
};
return frame.contentWindow.load_script_with_integrity(actual_url, integrity);
}
function css_integrity_test(frame, url, integrity, expected_integrity) {
var actual_url = url + (++url_count);
expected_results[actual_url] = {
url: actual_url,
mode: 'no-cors',
credentials: 'include',
integrity: expected_integrity,
message: 'CSS load (url:' + actual_url + ')'
};
return frame.contentWindow.load_css_with_integrity(actual_url, integrity);
}
async_test(function(t) {
var SCOPE = 'resources/fetch-request-resources-iframe.https.html';
var SCRIPT = 'resources/fetch-request-resources-worker.js';
var host_info = get_host_info();
var LOCAL_URL =
host_info['HTTPS_ORIGIN'] + base_path() + 'resources/dummy?test';
var REMOTE_URL =
host_info['HTTPS_REMOTE_ORIGIN'] + base_path() + 'resources/dummy?test';
var worker;
var frame;
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(function() {
return new Promise(function(resolve) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(msg) {
if (msg.data.ready) {
resolve();
return;
}
var result = msg.data;
var expected = expected_results[result.url];
if (!expected) {
return;
}
assert_equals(
result.mode, expected.mode,
'mode of ' + expected.message + ' must be ' +
expected.mode + '.');
assert_equals(
result.credentials, expected.credentials,
'credentials of ' + expected.message + ' must be ' +
expected.credentials + '.');
assert_equals(
result.integrity, expected.integrity,
'integrity of ' + expected.message + ' must be ' +
expected.integrity + '.');
--url_count;
delete expected_results[result.url];
if (url_count == 0) {
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
}
});
worker.postMessage(
{port: channel.port2}, [channel.port2]);
});
})
.then(function() { return with_iframe(SCOPE); })
.then(function(f) {
frame = f;
image_test(f, LOCAL_URL, '', 'no-cors', 'include');
image_test(f, REMOTE_URL, '', 'no-cors', 'include');
css_test(f, LOCAL_URL, '', 'no-cors', 'include');
css_test(f, REMOTE_URL, '', 'no-cors', 'include');
image_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin');
image_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include');
image_test(f, REMOTE_URL, 'anonymous', 'cors', 'same-origin');
image_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include');
script_test(f, LOCAL_URL, '', 'no-cors', 'include');
script_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin');
script_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include');
script_test(f, REMOTE_URL, '', 'no-cors', 'include');
script_test(f, REMOTE_URL, 'anonymous', 'cors', 'same-origin');
script_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include');
css_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin');
css_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include');
css_test(f, REMOTE_URL, 'anonymous', 'cors', 'same-origin');
css_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include');
font_face_test(f, LOCAL_URL, 'cors', 'same-origin');
font_face_test(f, REMOTE_URL, 'cors', 'same-origin');
script_integrity_test(f, LOCAL_URL, ' ', ' ');
script_integrity_test(f, LOCAL_URL,
'This is not a valid integrity because it has no dashes',
'This is not a valid integrity because it has no dashes');
script_integrity_test(f, LOCAL_URL, 'sha256-', 'sha256-');
script_integrity_test(f, LOCAL_URL, 'sha256-foo?123', 'sha256-foo?123');
script_integrity_test(f, LOCAL_URL, 'sha256-foo sha384-abc ', 'sha256-foo sha384-abc ');
script_integrity_test(f, LOCAL_URL, 'sha256-foo sha256-abc', 'sha256-foo sha256-abc');
css_integrity_test(f, LOCAL_URL, ' ', ' ');
css_integrity_test(f, LOCAL_URL,
'This is not a valid integrity because it has no dashes',
'This is not a valid integrity because it has no dashes');
css_integrity_test(f, LOCAL_URL, 'sha256-', 'sha256-');
css_integrity_test(f, LOCAL_URL, 'sha256-foo?123', 'sha256-foo?123');
css_integrity_test(f, LOCAL_URL, 'sha256-foo sha384-abc ', 'sha256-foo sha384-abc ');
css_integrity_test(f, LOCAL_URL, 'sha256-foo sha256-abc', 'sha256-foo sha256-abc');
})
.catch(unreached_rejection(t));
}, 'Verify FetchEvent for resources.');
</script>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<title>Service Worker: the body of FetchEvent using XMLHttpRequest</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-request-xhr-iframe.https.html';
var SCRIPT = 'resources/fetch-request-xhr-worker.js';
var host_info = get_host_info();
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
if (e.data.results === 'finish') {
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
} else if (e.data.results == 'equals') {
assert_equals(e.data.got, e.data.expected);
}
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
})
.catch(unreached_rejection(t));
}, 'Verify the body of FetchEvent using XMLHttpRequest');
</script>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<title>Service Worker: the response of FetchEvent using XMLHttpRequest</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js?pipe=sub"></script>
<script>
async_test(function(t) {
var SCOPE = 'resources/fetch-response-xhr-iframe.https.html';
var SCRIPT = 'resources/fetch-response-xhr-worker.js';
var host_info = get_host_info();
window.addEventListener('message', t.step_func(on_message), false);
function on_message(e) {
assert_equals(e.data.results, 'foo, bar');
t.done();
}
service_worker_unregister_and_register(t, SCRIPT, SCOPE)
.then(function(registration) {
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(SCOPE); })
.then(function(frame) {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(function(e) {
assert_equals(e.data.results, 'finish');
frame.remove();
service_worker_unregister_and_done(t, SCOPE);
});
frame.contentWindow.postMessage({},
host_info['HTTPS_ORIGIN'],
[channel.port2]);
})
.catch(unreached_rejection(t));
}, 'Verify the response of FetchEvent using XMLHttpRequest');
</script>

View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<title>Service Worker: Fetch Event Waits for Activate Event</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var worker = 'resources/fetch-waits-for-activate-worker.js';
var expected_url = normalizeURL(worker);
var scope = 'resources/fetch-waits-for-activate/';
async_test(function(t) {
var registration;
var frameLoadPromise;
var frame;
service_worker_unregister_and_register(t, worker, scope).then(function(reg) {
registration = reg;
return wait_for_state(t, reg.installing, 'activating');
}).then(function() {
assert_equals(registration.active.scriptURL, expected_url,
'active worker should be present');
assert_equals(registration.active.state, 'activating',
'active worker should be in activating state');
// This should block until we message the worker to tell it to complete
// the activate event.
frameLoadPromise = with_iframe(scope).then(function(f) {
frame = f;
});
// Wait some time to allow frame loading to proceed. It should not,
// however, if the fetch event is blocked on the activate. I don't
// see any way to force this race without a timeout, unfortunately.
return new Promise(function(resolve) {
setTimeout(resolve, 1000);
});
}).then(function() {
assert_equals(frame, undefined, 'frame should not be loaded');
assert_equals(registration.active.scriptURL, expected_url,
'active worker should be present');
assert_equals(registration.active.state, 'activating',
'active worker should be in activating state');
// This signals the activate event to complete. The frame should now
// load.
registration.active.postMessage('GO');
return frameLoadPromise;
}).then(function() {
assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
expected_url, 'frame should now be loaded and controlled');
assert_equals(registration.active.state, 'activated',
'active worker should be in activated state');
frame.remove();
return service_worker_unregister_and_done(t, scope);
}).catch(unreached_rejection(t));
}, 'Fetch events should wait for the activate event to complete.');
</script>
</body>

View file

@ -0,0 +1,87 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
async_test(function(t) {
var documentURL = 'no-such-worker';
navigator.serviceWorker.getRegistration(documentURL)
.then(function(value) {
assert_equals(value, undefined,
'getRegistration should resolve with undefined');
t.done();
})
.catch(unreached_rejection(t));
}, 'getRegistration');
async_test(function(t) {
var scope = 'resources/scope/getregistration/normal';
var registration;
service_worker_unregister_and_register(t, 'resources/empty-worker.js',
scope)
.then(function(r) {
registration = r;
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(value) {
assert_equals(
value, registration,
'getRegistration should resolve to the same registration object');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Register then getRegistration');
async_test(function(t) {
var scope = 'resources/scope/getregistration/url-with-fragment';
var documentURL = scope + '#ref';
var registration;
service_worker_unregister_and_register(t, 'resources/empty-worker.js',
scope)
.then(function(r) {
registration = r;
return navigator.serviceWorker.getRegistration(documentURL);
})
.then(function(value) {
assert_equals(
value, registration,
'getRegistration should resolve to the same registration object');
service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Register then getRegistration with a URL having a fragment');
async_test(function(t) {
var documentURL = 'http://example.com/';
navigator.serviceWorker.getRegistration(documentURL)
.then(function() {
assert_unreached(
'getRegistration with an out of origin URL should fail');
}, function(reason) {
assert_equals(
reason.name, 'SecurityError',
'getRegistration with an out of origin URL should fail');
t.done();
})
.catch(unreached_rejection(t));
}, 'getRegistration with a cross origin URL');
async_test(function(t) {
var scope = 'resources/scope/getregistration/register-unregister';
service_worker_unregister_and_register(t, 'resources/empty-worker.js',
scope)
.then(function(registration) {
return registration.unregister();
})
.then(function() {
return navigator.serviceWorker.getRegistration(scope);
})
.then(function(value) {
assert_equals(value, undefined,
'getRegistration should resolve with undefined');
t.done();
})
.catch(unreached_rejection(t));
}, 'Register then Unregister then getRegistration');
</script>

View file

@ -0,0 +1,191 @@
<!DOCTYPE html>
<title>Service Worker: getRegistrations()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script src="resources/get-host-info.sub.js"></script>
<script src="../fetch/resources/fetch-test-helpers.sub.js"></script>
<script>
// Purge the existing registrations for the origin.
// getRegistrations() is used in order to avoid adding additional complexity
// e.g. adding an internal function.
promise_test(function(t) {
return navigator.serviceWorker.getRegistrations()
.then(function(registrations) {
return registrations.reduce(function(sequence, registration) {
return sequence.then(function() {
return registration.unregister();
});
}, Promise.resolve());
});
}, 'Purge the existing registrations.');
promise_test(function(t) {
return navigator.serviceWorker.getRegistrations()
.then(function(value) {
assert_array_equals(
value,
[],
'getRegistrations should resolve with an empty array.');
});
}, 'getRegistrations');
promise_test(function(t) {
var scope = 'resources/scope/getregistrations/normal';
var script = 'resources/empty-worker.js';
var registrations = [];
return service_worker_unregister_and_register(t, script, scope)
.then(function(r) {
registrations.push(r);
return navigator.serviceWorker.getRegistrations();
})
.then(function(value) {
assert_array_equals(
value,
registrations,
'getRegistrations should resolve with array of registrations.');
return service_worker_unregister(t, scope);
});
}, 'Register then getRegistrations');
promise_test(function(t) {
var scope1 = 'resources/scope/getregistrations/scope1';
var scope2 = 'resources/scope/getregistrations/scope2';
var script = 'resources/empty-worker.js';
var registrations = [];
return service_worker_unregister_and_register(t, script, scope1)
.then(function(r) {
registrations.push(r);
return service_worker_unregister_and_register(t, script, scope2);
})
.then(function(r) {
registrations.push(r);
return navigator.serviceWorker.getRegistrations();
})
.then(function(value) {
assert_array_equals(
value,
registrations,
'getRegistrations should resolve with array of registrations.');
return service_worker_unregister(t, scope1);
})
.then(function() {
return service_worker_unregister(t, scope2);
});
}, 'Register multiple times then getRegistrations');
promise_test(function(t) {
var scope = 'resources/scope/getregistrations/register-unregister';
var script = 'resources/empty-worker.js';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return registration.unregister();
})
.then(function() {
return navigator.serviceWorker.getRegistrations();
})
.then(function(value) {
assert_array_equals(
value,
[],
'getRegistrations should resolve with an empty array.');
});
}, 'Register then Unregister then getRegistrations');
promise_test(function(t) {
var scope = 'resources/scope/getregistrations/register-unregister-controlled';
var script = 'resources/empty-worker.js';
var registrations;
var frame;
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) {
frame = f;
return registration.unregister();
})
.then(function() {
return navigator.serviceWorker.getRegistrations();
})
.then(function(value) {
assert_array_equals(
value,
[],
'getRegistrations should resolve with an empty array.');
assert_equals(registration.installing, null);
assert_equals(registration.waiting, null);
assert_equals(registration.active.state, 'activated');
frame.remove();
});
}, 'Register then Unregister with controlled frame then getRegistrations');
promise_test(function(t) {
var host_info = get_host_info();
// Rewrite the url to point to remote origin.
var frame_same_origin_url = new URL("resources/frame-for-getregistrations.html", window.location);
var frame_url = host_info['HTTPS_REMOTE_ORIGIN'] + frame_same_origin_url.pathname;
var scope = 'resources/scope-for-getregistrations';
var script = 'resources/empty-worker.js';
var frame;
var registrations = [];
// Loads an iframe and waits for 'ready' message from it to resolve promise.
// Caller is responsible for removing frame.
function with_iframe_ready(url) {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.src = url;
window.addEventListener('message', function onMessage(e) {
window.removeEventListener('message', onMessage);
if (e.data == 'ready') {
resolve(frame);
}
});
document.body.appendChild(frame);
});
}
// We need this special frame loading function because the frame is going
// to register it's own service worker and there is the possibility that that
// register() finishes after the register() for the same domain later in the
// test. So we have to wait until the cross origin register() is done, and not
// just until the frame loads.
return with_iframe_ready(frame_url)
.then(function(f) {
frame = f;
return service_worker_unregister_and_register(t, script, scope);
})
.then(function(r) {
registrations.push(r);
return navigator.serviceWorker.getRegistrations();
})
.then(function(value) {
assert_array_equals(
value,
registrations,
'getRegistrations should only return same origin registrations.');
var channel = new MessageChannel();
var resolve;
var p = new Promise(function(r) { resolve = r; });
channel.port1.onmessage = function(e) {
if (e.data == 'unregistered')
resolve();
};
frame.contentWindow.postMessage('unregister', '*', [channel.port2]);
return p;
})
.then(function() {
frame.remove();
return service_worker_unregister(t, scope);
});
}, 'getRegistrations promise resolves only with same origin registrations.');
done();
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<title>Service Worker: Indexed DB</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
async_test(function(t) {
var scope = 'resources/blank.html';
service_worker_unregister_and_register(
t, 'resources/indexeddb-worker.js', scope)
.then(function(registration) {
var sw = registration.installing;
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = t.step_func(onMessage);
sw.postMessage({port: messageChannel.port2}, [messageChannel.port2]);
})
.catch(unreached_rejection(t));
function onMessage() {
var openRequest = indexedDB.open('db');
openRequest.onsuccess = t.step_func(function() {
var db = openRequest.result;
var tx = db.transaction('store');
var store = tx.objectStore('store');
var getRequest = store.get('key');
getRequest.onsuccess = t.step_func(function() {
assert_equals(
getRequest.result, 'value',
'The get() result should match what the worker put().');
service_worker_unregister_and_done(t, scope);
});
});
}
}, 'Verify Indexed DB operation in a Service Worker');
</script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
function wait_for_install_event(worker) {
return new Promise(function(resolve) {
worker.addEventListener('statechange', function(event) {
if (worker.state == 'installed')
resolve(true);
else if (worker.state == 'redundant')
resolve(false);
});
});
}
promise_test(function(t) {
var script = 'resources/install-event-type-worker.js';
var scope = 'resources/install-event-type';
return service_worker_unregister_and_register(t, script, scope)
.then(function(registration) {
return wait_for_install_event(registration.installing);
})
.then(function(did_install) {
assert_true(did_install, 'The worker was installed');
})
}, 'install event type');
</script>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<title>ServiceWorker: navigator.serviceWorker.installing</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
// "installing" is set
async_test(function(t) {
var step = t.step_func.bind(t);
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html';
var frame;
service_worker_unregister(t, scope)
.then(step(function() { return with_iframe(scope); }))
.then(step(function(f) {
frame = f;
return navigator.serviceWorker.register(url, {scope: scope});
}))
.then(step(function(registration) {
var container = frame.contentWindow.navigator.serviceWorker;
assert_equals(container.controller, null);
assert_equals(registration.active, null);
assert_equals(registration.waiting, null);
assert_equals(registration.installing.scriptURL, normalizeURL(url));
// FIXME: Add a test for a frame created after installation.
// Should the existing frame ("frame") block activation?
}))
.then(step(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
}))
.catch(unreached_rejection(t));
}, 'installing is set');
</script>

Some files were not shown because too many files have changed in this diff Show more