Update web-platform-tests to revision e3cf1284464a4a3e46fd15e4138f8e32c6cecdd8

This commit is contained in:
WPT Sync Bot 2019-04-18 21:48:35 -04:00
parent b20333a324
commit c5c325d8bb
57 changed files with 1422 additions and 493 deletions

View file

@ -3,7 +3,7 @@ function get_host_info() {
var HTTP_PORT = '{{ports[http][0]}}';
var HTTP_PORT2 = '{{ports[http][1]}}';
var HTTPS_PORT = '{{ports[https][0]}}';
var PROTOCOL = window.location.protocol;
var PROTOCOL = self.location.protocol;
var IS_HTTPS = (PROTOCOL == "https:");
var HTTP_PORT_ELIDED = HTTP_PORT == "80" ? "" : (":" + HTTP_PORT);
var HTTP_PORT2_ELIDED = HTTP_PORT2 == "80" ? "" : (":" + HTTP_PORT2);

View file

@ -745,6 +745,136 @@ function requestViaWebSocket(url) {
});
}
// Subresource paths and invokers.
const subresourceMap = {
"a-tag": {
path: "/common/security-features/subresource/document.py",
invoker: requestViaAnchor,
},
"area-tag": {
path: "/common/security-features/subresource/document.py",
invoker: requestViaArea,
},
"audio-tag": {
path: "/common/security-features/subresource/audio.py",
invoker: requestViaAudio,
},
"beacon-request": {
path: "/common/security-features/subresource/empty.py",
invoker: requestViaSendBeacon,
},
"fetch-request": {
path: "/common/security-features/subresource/xhr.py",
invoker: requestViaFetch,
},
"form-tag": {
path: "/common/security-features/subresource/empty.py",
invoker: requestViaForm,
},
"iframe-tag": {
path: "/common/security-features/subresource/document.py",
invoker: requestViaIframe,
},
"img-tag": {
path: "/common/security-features/subresource/image.py",
invoker: requestViaImage,
invokerForReferrerPolicy: requestViaImageForReferrerPolicy,
},
"link-css-tag": {
path: "/common/security-features/subresource/empty.py",
invoker: requestViaLinkStylesheet,
},
"link-prefetch-tag": {
path: "/common/security-features/subresource/empty.py",
invoker: requestViaLinkPrefetch,
},
"object-tag": {
path: "/common/security-features/subresource/empty.py",
invoker: requestViaObject,
},
"picture-tag": {
path: "/common/security-features/subresource/image.py",
invoker: requestViaPicture,
},
"script-tag": {
path: "/common/security-features/subresource/script.py",
invoker: requestViaScript,
},
"video-tag": {
path: "/common/security-features/subresource/video.py",
invoker: requestViaVideo,
},
"xhr-request": {
path: "/common/security-features/subresource/xhr.py",
invoker: requestViaXhr,
},
"worker-request": {
path: "/common/security-features/subresource/worker.py",
invoker: url => requestViaDedicatedWorker(url),
},
// TODO: Merge "module-worker" and "module-worker-top-level".
"module-worker": {
path: "/common/security-features/subresource/worker.py",
invoker: url => requestViaDedicatedWorker(url, {type: "module"}),
},
"module-worker-top-level": {
path: "/common/security-features/subresource/worker.py",
invoker: url => requestViaDedicatedWorker(url, {type: "module"}),
},
"module-data-worker-import": {
path: "/common/security-features/subresource/worker.py",
invoker: url =>
requestViaDedicatedWorker(workerUrlThatImports(url), {type: "module"}),
},
"classic-data-worker-fetch": {
path: "/common/security-features/subresource/empty.py",
invoker: url =>
requestViaDedicatedWorker(dedicatedWorkerUrlThatFetches(url), {}),
},
"shared-worker": {
path: "/common/security-features/subresource/shared-worker.py",
invoker: requestViaSharedWorker,
},
"websocket-request": {
path: "/stash_responder",
invoker: requestViaWebSocket,
},
};
for (const workletType of ['animation', 'audio', 'layout', 'paint']) {
subresourceMap[`worklet-${workletType}-top-level`] = {
path: "/common/security-features/subresource/worker.py",
invoker: url => requestViaWorklet(workletType, url)
};
subresourceMap[`worklet-${workletType}-data-import`] = {
path: "/common/security-features/subresource/worker.py",
invoker: url =>
requestViaWorklet(workletType, workerUrlThatImports(url))
};
}
function getRequestURLs(subresourceType, originType, redirectionType) {
const key = guid();
const value = guid();
// We use the same stash path for both HTTP/S and WS/S stash requests.
const stashPath = encodeURIComponent("/mixed-content");
const stashEndpoint = "/common/security-features/subresource/xhr.py?key=" +
key + "&path=" + stashPath;
return {
testUrl:
getSubresourceOrigin(originType) +
subresourceMap[subresourceType].path +
"?redirection=" + encodeURIComponent(redirectionType) +
"&action=purge&key=" + key +
"&path=" + stashPath,
announceUrl: stashEndpoint + "&action=put&value=" + value,
assertUrl: stashEndpoint + "&action=take",
};
}
// SanityChecker does nothing in release mode. See sanity-checker.js for debug
// mode.
function SanityChecker() {}

View file

@ -68,4 +68,29 @@ test(() => {
assert_equals(scroller.scrollTop, 0);
}, "A scroll with intended end position should always choose the closest snap " +
"position regardless of the scroll-snap-stop value.")
// Tests for programmatic scrolls beyond the scroller bounds.
test(() => {
scroller.scrollTo(0, 0);
assert_equals(scroller.scrollLeft, 0);
assert_equals(scroller.scrollTop, 0);
scroller.scrollBy(100000, 0);
assert_equals(scroller.scrollLeft, 100);
assert_equals(scroller.scrollTop, 0);
}, "A scroll outside bounds in the snapping axis with intended direction and " +
"end position should not pass a snap area with scroll-snap-stop: always.")
test(() => {
scroller.scrollTo(0, 0);
assert_equals(scroller.scrollLeft, 0);
assert_equals(scroller.scrollTop, 0);
scroller.scrollBy(300, -10);
assert_equals(scroller.scrollLeft, 100);
assert_equals(scroller.scrollTop, 0);
}, "A scroll outside bounds in the non-snapping axis with intended direction " +
"and end position should not pass a snap area with scroll-snap-stop: always.")
</script>

View file

@ -74,7 +74,9 @@ var viewport = document.scrollingElement;
[
[{left: 800}, 1000, 0],
[{top: 900}, 0, 1000],
[{left: 900, top: 800}, 1000, 1000]
[{left: 900, top: 800}, 1000, 1000],
[{left: 800, top: -100}, 1000, 0], /* outside bounds on y axis */
[{left: 10000, top: -100}, 1000, 0] /* outside bounds on both axes */
].forEach(([input, expectedX, expectedY]) => {
test(() => {
divScroller.scrollTo(0, 0);

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<style>
.container {
position: relative;
width: 8em;
height: 7em;
padding: 1em;
}
</style>
<div class="container" style="writing-mode:horizontal-tb;">
<br><span class="target"></span><span>ref</span>
</div>
<div class="container" style="writing-mode:vertical-lr;">
<br><span class="target"></span><span>ref</span>
</div>
<div class="container" style="writing-mode:vertical-rl;">
<br><span class="target"></span><span>ref</span>
</div>
<script>
var i = 0;
document.querySelectorAll('span.target').forEach((target) => {
var ref = target.nextSibling;
test(() => {
assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
}, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
i++;
});
</script>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<style>
.container {
position: relative;
width: 8em;
height: 7em;
padding: 1em;
}
</style>
<div class="container" style="writing-mode:horizontal-tb;">
<br><span class="target"> </span><span>ref</span>
</div>
<div class="container" style="writing-mode:vertical-lr;">
<br><span class="target"> </span><span>ref</span>
</div>
<div class="container" style="writing-mode:vertical-rl;">
<br><span class="target"> </span><span>ref</span>
</div>
<script>
var i = 0;
document.querySelectorAll('span.target').forEach((target) => {
var ref = target.nextSibling;
test(() => {
assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
}, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
i++;
});
</script>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<style>
.container {
position: relative;
width: 8em;
height: 7em;
padding: 1em;
}
</style>
<div class="container" style="writing-mode:horizontal-tb;">
<br><span>ref</span><span class="target"> </span>
</div>
<div class="container" style="writing-mode:vertical-lr;">
<br><span>ref</span><span class="target"> </span>
</div>
<div class="container" style="writing-mode:vertical-rl;">
<br><span>ref</span><span class="target"> </span>
</div>
<script>
var i = 0;
document.querySelectorAll('span.target').forEach((target) => {
var ref = target.previousSibling;
test(() => {
assert_equals(target.offsetLeft,
ref.offsetLeft + (i ? 0 : ref.offsetWidth),
'offsetLeft');
assert_equals(target.offsetTop,
ref.offsetTop + (i ? ref.offsetHeight : 0),
'offsetTop');
}, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
i++;
});
</script>

View file

@ -0,0 +1,23 @@
const barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"];
test(() => {
for(const prop of barProps) {
assert_true(window[prop].visible);
}
}, "All bars visible");
["noopener", "noreferrer"].forEach(openerStyle => {
async_test(t => {
const channelName = "5454" + openerStyle + "34324";
const channel = new BroadcastChannel(channelName);
window.open("support/BarProp-target.html?" + channelName, "", openerStyle);
channel.onmessage = t.step_func_done(e => {
// Send message first so if asserts throw the popup is still closed
channel.postMessage(null);
for(const prop of barProps) {
assert_true(e.data[prop]);
}
});
}, `window.open() with ${openerStyle} should have all bars visible`);
});

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<script>
const barProps = ["locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar"];
const barPropsObj = {};
const channelName = location.search.substr(1);
const channel = new BroadcastChannel(channelName);
for (const prop of barProps) {
barPropsObj[prop] = window[prop].visible;
}
channel.postMessage(barPropsObj);
// Because messages are not delivered synchronously and because closing a
// browsing context prompts the eventual clearing of all task sources, this
// document should not be closed until the opener document has confirmed
// receipt.
channel.onmessage = () => { window.close() };
</script>

View file

@ -21,16 +21,27 @@
<img src="/images/green-2x2.png" data-desc="src set to same value">
<img data-desc="crossorigin absent to empty">
<img data-desc="crossorigin absent to anonymous">
<img data-desc="crossorigin absent to use-credentials">
<img crossorigin data-desc="crossorigin empty to absent">
<img crossorigin data-desc="crossorigin empty to use-credentials">
<img crossorigin=anonymous data-desc="crossorigin anonymous to absent">
<img crossorigin=anonymous data-desc="crossorigin anonymous to use-credentials">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to absent">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to empty">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to anonymous">
<img data-desc="crossorigin absent to empty, src absent">
<img data-desc="crossorigin absent to anonymous, src absent">
<img data-desc="crossorigin absent to use-credentials, src absent">
<img crossorigin data-desc="crossorigin empty to absent, src absent">
<img crossorigin data-desc="crossorigin empty to use-credentials, src absent">
<img crossorigin=anonymous data-desc="crossorigin anonymous to absent, src absent">
<img crossorigin=anonymous data-desc="crossorigin anonymous to use-credentials, src absent">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to absent, src absent">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to empty, src absent">
<img crossorigin=use-credentials data-desc="crossorigin use-credentials to anonymous, src absent">
<img src="/images/green-2x2.png" data-desc="crossorigin absent to empty, src already set">
<img src="/images/green-2x2.png" data-desc="crossorigin absent to anonymous, src already set">
<img src="/images/green-2x2.png" data-desc="crossorigin absent to use-credentials, src already set">
<img src="/images/green-2x2.png" crossorigin data-desc="crossorigin empty to absent, src already set">
<img src="/images/green-2x2.png" crossorigin data-desc="crossorigin empty to use-credentials, src already set">
<img src="/images/green-2x2.png" crossorigin=anonymous data-desc="crossorigin anonymous to absent, src already set">
<img src="/images/green-2x2.png" crossorigin=anonymous data-desc="crossorigin anonymous to use-credentials, src already set">
<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to absent, src already set">
<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to empty, src already set">
<img src="/images/green-2x2.png" crossorigin=use-credentials data-desc="crossorigin use-credentials to anonymous, src already set">
<img src="/images/green-2x2.png" data-desc="inserted into picture"><picture></picture>
@ -165,46 +176,92 @@ onload = function() {
img.src = '/images/green-2x2.png';
}, 'load');
t('crossorigin absent to empty', function(img) {
// When src is absent, changing the crossorigin attribute state MUST NOT
// generate events.
t('crossorigin absent to empty, src absent', function(img) {
img.crossOrigin = '';
}, 'timeout');
t('crossorigin absent to anonymous', function(img) {
t('crossorigin absent to anonymous, src absent', function(img) {
img.crossOrigin = 'anonymous';
}, 'timeout');
t('crossorigin absent to use-credentials', function(img) {
t('crossorigin absent to use-credentials, src absent', function(img) {
img.crossOrigin = 'use-credentials';
}, 'timeout');
t('crossorigin empty to absent', function(img) {
t('crossorigin empty to absent, src absent', function(img) {
img.removeAttribute('crossorigin');
}, 'timeout');
t('crossorigin empty to use-credentials', function(img) {
t('crossorigin empty to use-credentials, src absent', function(img) {
img.crossOrigin = 'use-credentials';
}, 'timeout');
t('crossorigin anonymous to absent', function(img) {
t('crossorigin anonymous to absent, src absent', function(img) {
img.removeAttribute('crossorigin');
}, 'timeout');
t('crossorigin anonymous to use-credentials', function(img) {
t('crossorigin anonymous to use-credentials, src absent', function(img) {
img.crossOrigin = 'use-credentials';
}, 'timeout');
t('crossorigin use-credentials to absent', function(img) {
t('crossorigin use-credentials to absent, src absent', function(img) {
img.removeAttribute('crossorigin');
}, 'timeout');
t('crossorigin use-credentials to empty', function(img) {
t('crossorigin use-credentials to empty, src absent', function(img) {
img.crossOrigin = '';
}, 'timeout');
t('crossorigin use-credentials to anonymous', function(img) {
t('crossorigin use-credentials to anonymous, src absent', function(img) {
img.crossOrigin = 'anonymous';
}, 'timeout');
// When src is set, changing the crossorigin attribute state MUST generate
// events.
t('crossorigin absent to empty, src already set', function(img) {
img.crossOrigin = '';
}, 'load');
t('crossorigin absent to anonymous, src already set', function(img) {
img.crossOrigin = 'anonymous';
}, 'load');
t('crossorigin absent to use-credentials, src already set', function(img) {
img.crossOrigin = 'use-credentials';
}, 'load');
t('crossorigin empty to absent, src already set', function(img) {
img.removeAttribute('crossorigin');
}, 'load');
t('crossorigin empty to use-credentials, src already set', function(img) {
img.crossOrigin = 'use-credentials';
}, 'load');
t('crossorigin anonymous to absent, src already set', function(img) {
img.removeAttribute('crossorigin');
}, 'load');
t('crossorigin anonymous to use-credentials, src already set', function(img) {
img.crossOrigin = 'use-credentials';
}, 'load');
t('crossorigin use-credentials to absent, src already set', function(img) {
img.removeAttribute('crossorigin');
}, 'load');
t('crossorigin use-credentials to empty, src already set', function(img) {
img.crossOrigin = '';
}, 'load');
t('crossorigin use-credentials to anonymous, src already set', function(img) {
img.crossOrigin = 'anonymous';
}, 'load');
t('inserted into picture', function(img) {
img.nextSibling.appendChild(img);
}, 'load');

View file

@ -0,0 +1,90 @@
<!DOCTYPE html>
<!--
Tentative due to:
https://github.com/whatwg/html/issues/4364
-->
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
</head>
<body>
<h1>User activation can be transferred to a cross-origin child frame
via a postMessage option.</h1>
<ol id="instructions">
<li>Click this instruction text.
</ol>
<iframe id="child1" width="200" height="200"></iframe>
<iframe id="child2" width="200" height="200"></iframe>
<script>
async_test(function(t) {
var child1 = document.getElementById("child1");
var child2 = document.getElementById("child2");
var is_child_four_loaded = false;
var is_child_five_loaded = false;
assert_false(navigator.userActivation.isActive);
assert_false(navigator.userActivation.hasBeenActive);
function tryClickInstructions() {
if (is_child_four_loaded && is_child_five_loaded)
test_driver.click(document.getElementById('instructions'));
}
window.addEventListener("message", t.step_func(event => {
var msg = JSON.parse(event.data);
if (msg.type == 'child-four-loaded') {
// state should be false after load
assert_false(msg.isActive);
assert_false(msg.hasBeenActive);
// click in parent document after both child frames load
is_child_four_loaded = true;
tryClickInstructions();
} else if (msg.type == 'child-four-report') {
assert_true(msg.isActive);
assert_true(msg.hasBeenActive);
// check sender's activation state again
assert_false(navigator.userActivation.isActive);
assert_false(navigator.userActivation.hasBeenActive);
child2.contentWindow.postMessage('report', '*');
} else if (msg.type == 'child-five-loaded') {
// state should be false after load
assert_false(msg.isActive);
assert_false(msg.hasBeenActive);
// click in parent document after both child frames load
is_child_five_loaded = true;
tryClickInstructions();
} else if (msg.type == 'child-five-report') {
assert_false(msg.isActive);
assert_false(msg.hasBeenActive);
// check sender's activation state again
assert_false(navigator.userActivation.isActive);
assert_false(navigator.userActivation.hasBeenActive);
t.done();
}
}));
window.addEventListener("click", t.step_func(event => {
assert_true(navigator.userActivation.isActive);
assert_true(navigator.userActivation.hasBeenActive);
// transfer user activation to the child frame
child1.contentWindow.postMessage("transfer_user_activation",
{targetOrigin: "*", transferUserActivation: true});
// sender's activation state is updated synchronously
assert_false(navigator.userActivation.isActive);
assert_false(navigator.userActivation.hasBeenActive);
}));
child1.src = "http://{{domains[www]}}:{{ports[http][0]}}/html/user-activation/resources/child-four.html";
child2.src = "http://{{domains[www1]}}:{{ports[http][0]}}/html/user-activation/resources/child-five.html";
}, "Cross-origin user activation transfer through postMessages");
</script>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<body style="background: red;">
<script>
window.parent.postMessage(JSON.stringify({"type": "child-five-loaded", "isActive": navigator.userActivation.isActive,
"hasBeenActive": navigator.userActivation.hasBeenActive}), "*");
window.addEventListener("message", event => {
if (event.source === window.parent && event.data == "report") {
window.parent.postMessage(JSON.stringify({"type": "child-five-report", "isActive": navigator.userActivation.isActive,
"hasBeenActive": navigator.userActivation.hasBeenActive}), "*");
}
});
</script>
</body>

View file

@ -1,6 +1,6 @@
<!DOCTYPE html>
<body style="background: lightgrey;">
<script>
<script>
window.parent.postMessage(JSON.stringify({"type": "child-four-loaded", "isActive": navigator.userActivation.isActive,
"hasBeenActive": navigator.userActivation.hasBeenActive}), "*");

View file

@ -3,6 +3,39 @@
* @author burnik@google.com (Kristijan Burnik)
*/
// TODO: This function is currently placed and duplicated at:
// - mixed-content/generic/mixed-content-test-case.js
// - referrer-policy/generic/referrer-policy-test-case.js
// but should be moved to /common/security-features/resources/common.js.
function getSubresourceOrigin(originType) {
const httpProtocol = "http";
const httpsProtocol = "https";
const wsProtocol = "ws";
const wssProtocol = "wss";
const sameOriginHost = "{{host}}";
const crossOriginHost = "{{domains[www1]}}";
// These values can evaluate to either empty strings or a ":port" string.
const httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
const httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
const wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
const wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
const originMap = {
"same-https": httpsProtocol + "://" + sameOriginHost + httpsPort,
"same-http": httpProtocol + "://" + sameOriginHost + httpPort,
"cross-https": httpsProtocol + "://" + crossOriginHost + httpsPort,
"cross-http": httpProtocol + "://" + crossOriginHost + httpPort,
"same-wss": wssProtocol + "://" + sameOriginHost + wssPort,
"same-ws": wsProtocol + "://" + sameOriginHost + wsPort,
"cross-wss": wssProtocol + "://" + crossOriginHost + wssPort,
"cross-ws": wsProtocol + "://" + crossOriginHost + wsPort,
};
return originMap[originType];
}
/**
* MixedContentTestCase exercises all the tests for checking browser behavior
* when resources regarded as mixed-content are requested. A single run covers
@ -16,155 +49,43 @@
* @return {object} Object wrapping the start method used to run the test.
*/
function MixedContentTestCase(scenario, description, sanityChecker) {
const subresourcePath = {
"a-tag": "/common/security-features/subresource/document.py",
"area-tag": "/common/security-features/subresource/document.py",
"beacon-request": "/common/security-features/subresource/empty.py",
"fetch-request": "/common/security-features/subresource/xhr.py",
"form-tag": "/common/security-features/subresource/empty.py",
"iframe-tag": "/common/security-features/subresource/document.py",
"img-tag": "/common/security-features/subresource/image.py",
"picture-tag": "/common/security-features/subresource/image.py",
"script-tag": "/common/security-features/subresource/script.py",
"worker-request": "/common/security-features/subresource/worker.py",
"module-worker-top-level": "/common/security-features/subresource/worker.py",
"module-data-worker-import": "/common/security-features/subresource/worker.py",
"object-tag": "/common/security-features/subresource/empty.py",
"link-css-tag": "/common/security-features/subresource/empty.py",
"link-prefetch-tag": "/common/security-features/subresource/empty.py",
"classic-data-worker-fetch": "/common/security-features/subresource/empty.py",
"xhr-request": "/common/security-features/subresource/xhr.py",
"audio-tag": "/common/security-features/subresource/audio.py",
"video-tag": "/common/security-features/subresource/video.py",
"websocket-request": "/stash_responder"
sanityChecker.checkScenario(scenario, subresourceMap);
const originTypeConversion = {
"same-host-https": "same-https",
"same-host-http": "same-http",
"cross-origin-https": "cross-https",
"cross-origin-http": "cross-http",
"same-host-wss": "same-wss",
"same-host-ws": "same-ws",
"cross-origin-wss": "cross-wss",
"cross-origin-ws": "cross-ws",
};
// Mapping all the resource requesting methods to the scenario.
var resourceMap = {
"a-tag": requestViaAnchor,
"area-tag": requestViaArea,
"beacon-request": requestViaSendBeacon,
"fetch-request": requestViaFetch,
"form-tag": requestViaForm,
"iframe-tag": requestViaIframe,
"img-tag": requestViaImage,
"script-tag": requestViaScript,
"worker-request":
url => requestViaDedicatedWorker(url),
"module-worker-top-level":
url => requestViaDedicatedWorker(url, {type: "module"}),
"module-data-worker-import":
url => requestViaDedicatedWorker(workerUrlThatImports(url), {type: "module"}),
"classic-data-worker-fetch":
url => requestViaDedicatedWorker(dedicatedWorkerUrlThatFetches(url), {}),
"xhr-request": requestViaXhr,
"audio-tag": requestViaAudio,
"video-tag": requestViaVideo,
"picture-tag": requestViaPicture,
"object-tag": requestViaObject,
"link-css-tag": requestViaLinkStylesheet,
"link-prefetch-tag": requestViaLinkPrefetch,
"websocket-request": requestViaWebSocket
const urls = getRequestURLs(scenario.subresource,
originTypeConversion[scenario.origin],
scenario.redirection);
const invoker = subresourceMap[scenario.subresource].invoker;
const checkResult = _ => {
// Send request to check if the key has been torn down.
return xhrRequest(urls.assertUrl)
.then(assertResult => {
// Now check if the value has been torn down. If it's still there,
// we have blocked the request to mixed-content.
assert_equals(assertResult.status, scenario.expectation,
"The resource request should be '" + scenario.expectation + "'.");
});
};
for (const workletType of ['animation', 'audio', 'layout', 'paint']) {
resourceMap[`worklet-${workletType}-top-level`] =
url => requestViaWorklet(workletType, url);
subresourcePath[`worklet-${workletType}-top-level`] =
"/common/security-features/subresource/worker.py";
resourceMap[`worklet-${workletType}-data-import`] =
url => requestViaWorklet(workletType, workerUrlThatImports(url));
subresourcePath[`worklet-${workletType}-data-import`] =
"/common/security-features/subresource/worker.py";
}
var httpProtocol = "http";
var httpsProtocol = "https";
var wsProtocol = "ws";
var wssProtocol = "wss";
var sameOriginHost = location.hostname;
var crossOriginHost = "{{domains[www1]}}";
// These values can evaluate to either empty strings or a ":port" string.
var httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
var httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
var wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
var wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
const resourcePath = subresourcePath[scenario.subresource];
// Map all endpoints to scenario for use in the test.
var endpoint = {
"same-origin":
location.origin + resourcePath,
"same-host-https":
httpsProtocol + "://" + sameOriginHost + httpsPort + resourcePath,
"same-host-http":
httpProtocol + "://" + sameOriginHost + httpPort + resourcePath,
"cross-origin-https":
httpsProtocol + "://" + crossOriginHost + httpsPort + resourcePath,
"cross-origin-http":
httpProtocol + "://" + crossOriginHost + httpPort + resourcePath,
"same-host-wss":
wssProtocol + "://" + sameOriginHost + wssPort + resourcePath,
"same-host-ws":
wsProtocol + "://" + sameOriginHost + wsPort + resourcePath,
"cross-origin-wss":
wssProtocol + "://" + crossOriginHost + wssPort + resourcePath,
"cross-origin-ws":
wsProtocol + "://" + crossOriginHost + wsPort + resourcePath
};
sanityChecker.checkScenario(scenario, resourceMap);
var mixed_content_test = async_test(description);
function runTest() {
sanityChecker.setFailTimeout(mixed_content_test);
var key = guid();
var value = guid();
// We use the same path for both HTTP/S and WS/S stash requests.
var stash_path = encodeURIComponent("/mixed-content");
const stashEndpoint = "/common/security-features/subresource/xhr.py?key=" +
key + "&path=" + stash_path;
const announceResourceRequestUrl = stashEndpoint + "&action=put&value=" +
value;
const assertResourceRequestUrl = stashEndpoint + "&action=take";
const resourceRequestUrl = endpoint[scenario.origin] + "?redirection=" +
scenario.redirection + "&action=purge&key=" + key +
"&path=" + stash_path;
xhrRequest(announceResourceRequestUrl)
.then(mixed_content_test.step_func(_ => {
promise_test(() => {
return xhrRequest(urls.announceUrl)
// Send out the real resource request.
// This should tear down the key if it's not blocked.
return resourceMap[scenario.subresource](resourceRequestUrl);
}))
.then(mixed_content_test.step_func(_ => {
// Send request to check if the key has been torn down.
return xhrRequest(assertResourceRequestUrl);
}))
.catch(mixed_content_test.step_func(e => {
// When requestResource fails, we also check the key state.
return xhrRequest(assertResourceRequestUrl);
}))
.then(mixed_content_test.step_func_done(response => {
// Now check if the value has been torn down. If it's still there,
// we have blocked the request to mixed-content.
assert_equals(response.status, scenario.expectation,
"The resource request should be '" + scenario.expectation + "'.");
}));
.then(_ => invoker(urls.testUrl))
// We check the key state, regardless of whether the main request
// succeeded or failed.
.then(checkResult, checkResult);
}, description);
} // runTest
return {start: mixed_content_test.step_func(runTest) };
return {start: runTest};
} // MixedContentTestCase

View file

@ -19,8 +19,9 @@ async function openPortalAndActivate(logic, activateOptions) {
portal.src = new URL('resources/portal-activate-data-portal.html?logic=' + encodeURIComponent(logic), location.href);
w.document.body.appendChild(portal);
assert_equals((await nextMessage(bc)).data, 'ready');
let replyPromise = nextMessage(bc);
await portal.activate(activateOptions);
return (await nextMessage(bc)).data;
return (await replyPromise).data;
} finally {
w.close();
bc.close();

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(async () => {
var win = window.open();
var portal = win.document.createElement("portal");
portal.src = new URL("resources/simple-portal.html", location.href)
await new Promise((resolve, reject) => {
var bc = new BroadcastChannel("simple-portal");
bc.onmessage = () => {
bc.close();
resolve();
}
win.document.body.appendChild(portal);
});
return portal.activate();
});
</script>

View file

@ -10,7 +10,6 @@
assert_equals(e.data, "passed");
bc.close();
});
var portalUrl = encodeURIComponent(`portal-activate-event-portal.html?test=${test}`);
window.open(`resources/portals-adopt-predecessor.html?test=${test}`);
}, "Tests that a portal can adopt its predecessor.");
@ -21,7 +20,6 @@
assert_equals(e.data, "passed");
bc.close();
});
var portalUrl = encodeURIComponent(`portal-activate-event-portal.html?test=${test}`);
window.open(`resources/portals-adopt-predecessor.html?test=${test}`);
}, "Tests that trying to adopt the predecessor twice will throw an exception.");
</script>

View file

@ -1,19 +0,0 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
promise_test(async () => {
let waitForMessage = new Promise((resolve, reject) => {
var bc = new BroadcastChannel("portals-create-orphaned");
bc.onmessage = e => {
bc.close();
resolve(e.data);
}
});
window.open("resources/portal-create-orphaned.html");
let message = await waitForMessage;
assert_equals(message, "portal loaded");
}, "creating a portal from an orphaned portal should succeed");
</script>
</body>

View file

@ -1,28 +0,0 @@
<!DOCTYPE html>
<body>
<script>
var portal = document.createElement("portal");
portal.src = "simple-portal.html";
let waitForMessage = new Promise((resolve, reject) => {
var bc_portal = new BroadcastChannel("simple-portal");
bc_portal.onmessage = e => {
bc_portal.close();
portal.activate();
var portal2 = document.createElement("portal");
portal2.src = "simple-portal.html";
document.body.appendChild(portal2);
var bc2 = new BroadcastChannel("simple-portal");
bc2.onmessage = e => {
bc2.close();
resolve("portal loaded");
}
}
});
document.body.appendChild(portal);
waitForMessage.then(message => {
var bc = new BroadcastChannel("portals-create-orphaned");
bc.postMessage(message);
bc.close();
});
</script>
</body>

View file

@ -2,7 +2,6 @@
<script>
var searchParams = new URL(location).searchParams;
var test = searchParams.get("test");
var bc = new BroadcastChannel(`portal-${test}`);
window.onportalactivate = function(e) {
var portal = e.adoptPredecessor();
@ -10,19 +9,19 @@
if (test == "adopt-once") {
if (portal instanceof HTMLPortalElement) {
bc.postMessage("passed");
bc.close();
portal.postMessage("adopted", "*");
}
}
if (test == "adopt-twice") {
try {
portal = e.adoptPredecessor();
e.adoptPredecessor();
} catch(e) {
bc.postMessage("passed");
bc.close();
portal.postMessage("passed", "*");
}
}
}
var bc = new BroadcastChannel(`portal-${test}`);
bc.postMessage("loaded");
bc.close();
</script>

View file

@ -5,18 +5,15 @@
var searchParams = new URL(location).searchParams;
var test = searchParams.get("test");
var bc = new BroadcastChannel(`portal-${test}`);
bc.onmessage = function(e) {
switch (e.data) {
case "loaded":
document.querySelector("portal").activate();
break;
case "passed":
bc.close();
bc.onmessage = e => {
bc.close();
document.querySelector("portal").activate().then(() => {
window.portalHost.addEventListener("message", () => {
var bc_test = new BroadcastChannel(`test-${test}`);
bc_test.postMessage("passed");
bc_test.close();
}
});
});
}
var portal = document.createElement("portal");

View file

@ -1,3 +1,36 @@
// TODO: This function is currently placed and duplicated at:
// - mixed-content/generic/mixed-content-test-case.js
// - referrer-policy/generic/referrer-policy-test-case.js
// but should be moved to /common/security-features/resources/common.js.
function getSubresourceOrigin(originType) {
const httpProtocol = "http";
const httpsProtocol = "https";
const wsProtocol = "ws";
const wssProtocol = "wss";
const sameOriginHost = "{{host}}";
const crossOriginHost = "{{domains[www1]}}";
// These values can evaluate to either empty strings or a ":port" string.
const httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
const httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
const wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
const wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
const originMap = {
"same-https": httpsProtocol + "://" + sameOriginHost + httpsPort,
"same-http": httpProtocol + "://" + sameOriginHost + httpPort,
"cross-https": httpsProtocol + "://" + crossOriginHost + httpsPort,
"cross-http": httpProtocol + "://" + crossOriginHost + httpPort,
"same-wss": wssProtocol + "://" + sameOriginHost + wssPort,
"same-ws": wsProtocol + "://" + sameOriginHost + wsPort,
"cross-wss": wssProtocol + "://" + crossOriginHost + wssPort,
"cross-ws": wsProtocol + "://" + crossOriginHost + wsPort,
};
return originMap[originType];
}
// NOTE: This method only strips the fragment and is not in accordance to the
// recommended draft specification:
// https://w3c.github.io/webappsec/specs/referrer-policy/#null
@ -7,14 +40,6 @@ function stripUrlForUseAsReferrer(url) {
return url.replace(/#.*$/, "");
}
function normalizePort(targetPort) {
var defaultPorts = [80, 443];
var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0);
return (targetPort == "" || isDefaultPortForProtocol) ?
"" : ":" + targetPort;
}
function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
// Pass and skip rest of the test if browser does not support fetch.
if (scenario.subresource == "fetch-request" && !window.fetch) {
@ -30,120 +55,65 @@ function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
// This check is A NOOP in release.
sanityChecker.checkScenario(scenario);
var subresourceInvoker = {
"a-tag": requestViaAnchor,
"area-tag": requestViaArea,
"fetch-request": requestViaFetch,
"iframe-tag": requestViaIframe,
"img-tag": requestViaImageForReferrerPolicy,
"script-tag": requestViaScript,
"worker-request": url => requestViaDedicatedWorker(url, {}),
"module-worker": url => requestViaDedicatedWorker(url, {type: "module"}),
"shared-worker": requestViaSharedWorker,
"xhr-request": requestViaXhr
const originTypeConversion = {
"same-origin-http": "same-http",
"same-origin-https": "same-https",
"cross-origin-http": "cross-http",
"cross-origin-https": "cross-https"
};
const subresourcePath = {
"a-tag": "/common/security-features/subresource/document.py",
"area-tag": "/common/security-features/subresource/document.py",
"fetch-request": "/common/security-features/subresource/xhr.py",
"iframe-tag": "/common/security-features/subresource/document.py",
"img-tag": "/common/security-features/subresource/image.py",
"script-tag": "/common/security-features/subresource/script.py",
"worker-request": "/common/security-features/subresource/worker.py",
"module-worker": "/common/security-features/subresource/worker.py",
"shared-worker": "/common/security-features/subresource/shared-worker.py",
"xhr-request": "/common/security-features/subresource/xhr.py"
};
var referrerUrlResolver = {
"omitted": function() {
return undefined;
},
"origin": function() {
return self.origin + "/";
},
"stripped-referrer": function() {
return stripUrlForUseAsReferrer(location.toString());
}
};
var t = {
_scenario: scenario,
_testDescription: testDescription,
_constructSubresourceUrl: function() {
// TODO(kristijanburnik): We should assert that these two domains are
// different. E.g. If someone runs the tets over www, this would fail.
var domainForOrigin = {
"cross-origin":"{{domains[www1]}}",
"same-origin": location.hostname
};
// Values obtained and replaced by the wptserve pipeline:
// http://wptserve.readthedocs.org/en/latest/pipes.html#built-in-pipes
var portForProtocol = {
"http": parseInt("{{ports[http][0]}}"),
"https": parseInt("{{ports[https][0]}}")
const urls = getRequestURLs(
scenario.subresource,
originTypeConversion[scenario.origin + '-' + scenario.target_protocol],
scenario.redirection);
const invoker =
subresourceMap[scenario.subresource].invokerForReferrerPolicy ||
subresourceMap[scenario.subresource].invoker;
const checkResult = result => {
const referrerUrlResolver = {
"omitted": function() {
return undefined;
},
"origin": function() {
return self.origin + "/";
},
"stripped-referrer": function() {
return stripUrlForUseAsReferrer(location.toString());
}
};
const expectedReferrerUrl =
referrerUrlResolver[scenario.referrer_url]();
var targetPort = portForProtocol[t._scenario.target_protocol];
// Check if the result is in valid format. NOOP in release.
sanityChecker.checkSubresourceResult(scenario, urls.testUrl, result);
return t._scenario.target_protocol + "://" +
domainForOrigin[t._scenario.origin] +
normalizePort(targetPort) +
subresourcePath[t._scenario.subresource] +
"?redirection=" + t._scenario["redirection"] +
"&cache_destroyer=" + (new Date()).getTime();
},
// Check the reported URL.
assert_equals(result.referrer,
expectedReferrerUrl,
"Reported Referrer URL is '" +
scenario.referrer_url + "'.");
assert_equals(result.headers.referer,
expectedReferrerUrl,
"Reported Referrer URL from HTTP header is '" +
expectedReferrerUrl + "'");
};
_constructExpectedReferrerUrl: function() {
return referrerUrlResolver[t._scenario.referrer_url]();
},
// Returns a promise.
_invokeSubresource: function(resourceRequestUrl) {
var invoker = subresourceInvoker[t._scenario.subresource];
function runTest() {
promise_test(_ => {
// Depending on the delivery method, extend the subresource element with
// these attributes.
var elementAttributesForDeliveryMethod = {
"attr-referrer": {referrerPolicy: t._scenario.referrer_policy},
"attr-referrer": {referrerPolicy: scenario.referrer_policy},
"rel-noreferrer": {rel: "noreferrer"}
};
var delivery_method = t._scenario.delivery_method;
if (delivery_method in elementAttributesForDeliveryMethod) {
return invoker(resourceRequestUrl,
elementAttributesForDeliveryMethod[delivery_method],
t._scenario.referrer_policy);
} else {
return invoker(resourceRequestUrl, {}, t._scenario.referrer_policy);
var deliveryMethod = scenario.delivery_method;
let elementAttributes = {};
if (deliveryMethod in elementAttributesForDeliveryMethod) {
elementAttributes = elementAttributesForDeliveryMethod[deliveryMethod];
}
},
start: function() {
promise_test(test => {
const resourceRequestUrl = t._constructSubresourceUrl();
const expectedReferrerUrl = t._constructExpectedReferrerUrl();
return t._invokeSubresource(resourceRequestUrl)
.then(result => {
// Check if the result is in valid format. NOOP in release.
sanityChecker.checkSubresourceResult(
test, t._scenario, resourceRequestUrl, result);
// Check the reported URL.
assert_equals(result.referrer,
expectedReferrerUrl,
"Reported Referrer URL is '" +
t._scenario.referrer_url + "'.");
assert_equals(result.headers.referer,
expectedReferrerUrl,
"Reported Referrer URL from HTTP header is '" +
expectedReferrerUrl + "'");
});
}, t._testDescription);
}
return invoker(urls.testUrl, elementAttributes, scenario.referrer_policy)
.then(checkResult);
}, testDescription);
}
return t;
return {start: runTest};
}

View file

@ -28,22 +28,19 @@ SanityChecker.prototype.checkScenario = function(scenario) {
}, "[ReferrerPolicyTestCase] The test scenario is valid.");
}
SanityChecker.prototype.checkSubresourceResult = function(test,
scenario,
SanityChecker.prototype.checkSubresourceResult = function(scenario,
subresourceUrl,
result) {
test.step(function() {
assert_equals(Object.keys(result).length, 3);
assert_own_property(result, "location");
assert_own_property(result, "referrer");
assert_own_property(result, "headers");
assert_equals(Object.keys(result).length, 3);
assert_own_property(result, "location");
assert_own_property(result, "referrer");
assert_own_property(result, "headers");
// Skip location check for scripts.
if (scenario.subresource == "script-tag")
return;
// Skip location check for scripts.
if (scenario.subresource == "script-tag")
return;
// Sanity check: location of sub-resource matches reported location.
assert_equals(result.location, subresourceUrl,
"Subresource reported location.");
}, "Running a valid test scenario.");
// Sanity check: location of sub-resource matches reported location.
assert_equals(result.location, subresourceUrl,
"Subresource reported location.");
};

View file

@ -0,0 +1,138 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<title>Restrictions on return value from `test`</title>
</head>
<body>
<script>
function makeTest(...bodies) {
const closeScript = '<' + '/script>';
let src = `
<!DOCTYPE HTML>
<html>
<head>
<title>Document title</title>
<script src="/resources/testharness.js?${Math.random()}">${closeScript}
</head>
<body>
<div id="log"></div>`;
bodies.forEach((body) => {
src += '<script>(' + body + ')();' + closeScript;
});
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.contentDocument.write(src);
return new Promise((resolve) => {
window.addEventListener('message', function onMessage(e) {
if (e.source !== iframe.contentWindow) {
return;
}
if (!e.data || e.data.type !=='complete') {
return;
}
window.removeEventListener('message', onMessage);
resolve(e.data);
});
iframe.contentDocument.close();
}).then(({ tests, status }) => {
const summary = {
harness: {
status: getEnumProp(status, status.status),
message: status.message
},
tests: {}
};
tests.forEach((test) => {
summary.tests[test.name] = getEnumProp(test, test.status);
});
return summary;
});
}
function getEnumProp(object, value) {
for (let property in object) {
if (!/^[A-Z]+$/.test(property)) {
continue;
}
if (object[property] === value) {
return property;
}
}
}
promise_test(() => {
return makeTest(
() => {
test(() => {}, 'before');
test(() => {}, 'U+d7ff is not modified: \ud7ff');
test(() => {}, 'U+e000 is not modified: \ue000');
test(() => {}, 'after');
}
).then(({harness, tests}) => {
assert_equals(harness.status, 'OK');
assert_equals(harness.message, null);
assert_equals(tests.before, 'PASS');
assert_equals(tests['U+d7ff is not modified: \ud7ff'], 'PASS');
assert_equals(tests['U+e000 is not modified: \ue000'], 'PASS');
assert_equals(tests.after, 'PASS');
});
}, 'sub-test names which include valid code units');
promise_test(() => {
return makeTest(
() => {
test(() => {}, 'before');
test(() => {}, 'U+d800U+dfff is not modified: \ud800\udfff');
test(() => {}, 'U+dbffU+dc00 is not modified: \udbff\udc00');
test(() => {}, 'after');
}
).then(({harness, tests}) => {
assert_equals(harness.status, 'OK');
assert_equals(harness.message, null);
assert_equals(tests.before, 'PASS');
assert_equals(tests['U+d800U+dfff is not modified: \ud800\udfff'], 'PASS');
assert_equals(tests['U+dbffU+dc00 is not modified: \udbff\udc00'], 'PASS');
assert_equals(tests.after, 'PASS');
});
}, 'sub-test names which include paired surrogates');
promise_test(() => {
return makeTest(
() => {
test(() => {}, 'before');
test(() => {}, 'U+d800 must be sanitized: \ud800');
test(() => {}, 'U+d800U+d801 must be sanitized: \ud800\ud801');
test(() => {}, 'U+dfff must be sanitized: \udfff');
test(() => {}, 'U+dc00U+d800U+dc00U+d800 must be sanitized: \udc00\ud800\udc00\ud800');
test(() => {}, 'after');
}
).then(({harness, tests}) => {
assert_equals(harness.status, 'OK');
assert_equals(harness.message, null);
assert_equals(tests.before, 'PASS');
assert_equals(tests['U+d800 must be sanitized: U+d800'], 'PASS');
assert_equals(tests['U+dfff must be sanitized: U+dfff'], 'PASS');
assert_equals(
tests['U+d800U+d801 must be sanitized: U+d800U+d801'],
'PASS'
);
assert_equals(
tests['U+dc00U+d800U+dc00U+d800 must be sanitized: U+dc00\ud800\udc00U+d800'],
'PASS'
);
assert_equals(tests.after, 'PASS');
});
}, 'sub-test names which include unpaired surrogates');
</script>
</body>
</html>

View file

@ -2383,6 +2383,42 @@ policies and contribution forms [3].
return duplicates;
};
function code_unit_str(char) {
return 'U+' + char.charCodeAt(0).toString(16);
}
function sanitize_unpaired_surrogates(str) {
return str.replace(/([\ud800-\udbff])(?![\udc00-\udfff])/g,
function(_, unpaired)
{
return code_unit_str(unpaired);
})
// This replacement is intentionally implemented without an
// ES2018 negative lookbehind assertion to support runtimes
// which do not yet implement that language feature.
.replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g,
function(_, previous, unpaired) {
if (/[\udc00-\udfff]/.test(previous)) {
previous = code_unit_str(previous);
}
return previous + code_unit_str(unpaired);
});
}
function sanitize_all_unpaired_surrogates(tests) {
forEach (tests,
function (test)
{
var sanitized = sanitize_unpaired_surrogates(test.name);
if (test.name !== sanitized) {
test.name = sanitized;
delete test._structured_clone;
}
});
}
Tests.prototype.notify_complete = function() {
var this_obj = this;
var duplicates;
@ -2390,6 +2426,11 @@ policies and contribution forms [3].
if (this.status.status === null) {
duplicates = this.find_duplicates();
// Some transports adhere to UTF-8's restriction on unpaired
// surrogates. Sanitize the titles so that the results can be
// consistently sent via all transports.
sanitize_all_unpaired_surrogates(this.tests);
// Test names are presumed to be unique within test files--this
// allows consumers to use them for identification purposes.
// Duplicated names violate this expectation and should therefore

View file

@ -6,7 +6,7 @@ import zipfile
from io import BytesIO
try:
from typing import Any
from typing import Any, Callable
except ImportError:
pass
@ -17,10 +17,11 @@ class Kwargs(dict):
def set_if_none(self,
name, # type: str
value, # type: Any
err_fn=None, # type: (Kwargs, str) -> Any
err_fn=None, # type: Callable[[Kwargs, str], Any]
desc=None, # type: str
extra_cond=None # type: (Kwargs) -> bool
extra_cond=None # type: Callable[[Kwargs], Any]
):
# type: (...) -> Any
if desc is None:
desc = name

View file

@ -261,8 +261,123 @@ promise_test(async t => {
localQuicTransport.transport.stop();
const promise = localQuicTransport.getStats();
promise_rejects(t, 'InvalidStateError', promise);
}, 'getStats() promises immediately rejected if called after' +
}, 'getStats() promises immediately rejected if called after ' +
`'closed' state.`);
test(t => {
const quicTransport = makeStandaloneQuicTransport(t);
assert_throws('InvalidStateError',
() => quicTransport.sendDatagram(new Uint8Array([1])));
}, `sendDatagram() throws InvalidStateError if called before 'connected'.`);
test(t => {
const quicTransport = makeStandaloneQuicTransport(t);
quicTransport.stop();
assert_equals(quicTransport.state, 'closed');
assert_throws('InvalidStateError',
() => quicTransport.sendDatagram(new Uint8Array([1])));
}, `sendDatagram() throws InvalidStateError if called when 'closed'.`);
test(t => {
const quicTransport = makeStandaloneQuicTransport(t);
assert_equals(quicTransport.maxDatagramLength, null);
}, 'maxDatagramLength 0 before connected.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
assert_greater_than(localQuicTransport.maxDatagramLength, 0);
}, 'maxDatagramLength larger than 0 after connected.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const bigData = new Uint8Array(localQuicTransport.maxDatagramLength + 1);
assert_throws('InvalidStateError',
() => localQuicTransport.sendDatagram(bigData));
}, 'sendDatagram() throws InvalidStateError if called with data larger ' +
'than maxDatagramLength()');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const datagram = new Uint8Array([42]);
await localQuicTransport.readyToSendDatagram();
localQuicTransport.sendDatagram(datagram);
const receiveDatagrams = await remoteQuicTransport.receiveDatagrams();
assert_equals(receiveDatagrams.length, 1);
const receiveDatagram = new Uint8Array(receiveDatagrams[0]);
assert_array_equals(receiveDatagram, datagram);
}, 'sendDatagram() sends a datagram to remote side');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const datagram = new Uint8Array([42]);
const datagram2 = new Uint8Array([43]);
await localQuicTransport.readyToSendDatagram();
localQuicTransport.sendDatagram(datagram);
const receiveDatagrams = await remoteQuicTransport.receiveDatagrams();
assert_equals(receiveDatagrams.length, 1);
const receiveDatagram = new Uint8Array(receiveDatagrams[0]);
assert_array_equals(receiveDatagram, datagram);
await localQuicTransport.readyToSendDatagram();
localQuicTransport.sendDatagram(datagram2);
const receiveDatagrams2 = await remoteQuicTransport.receiveDatagrams();
assert_equals(receiveDatagrams2.length, 1);
const receiveDatagram2 = new Uint8Array(receiveDatagrams2[0]);
assert_array_equals(receiveDatagram2, datagram2);
}, 'sendDatagram() sends a multiple datagrams to remote side');
test(t => {
const quicTransport = makeStandaloneQuicTransport(t);
const promise = quicTransport.readyToSendDatagram();
promise_rejects(t, 'InvalidStateError', promise);
}, 'readyToSendDatagram() promise immediately rejected if called before ' +
'connecting');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
localQuicTransport.stop();
const promise = localQuicTransport.readyToSendDatagram();
promise_rejects(t, 'InvalidStateError', promise);
}, 'readyToSendDatagram() promise immediately rejected if called after ' +
`'closed' state.`);
test(t => {
const quicTransport = makeStandaloneQuicTransport(t);
const promise = quicTransport.receiveDatagrams();
promise_rejects(t, 'InvalidStateError', promise);
}, 'receiveDatagrams() promise immediately rejected if called before ' +
'connecting.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
localQuicTransport.stop();
const promise = localQuicTransport.receiveDatagrams();
promise_rejects(t, 'InvalidStateError', promise);
}, 'receiveDatagrams() promise immediately rejected if called after ' +
`'closed' state.`);
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const promise = localQuicTransport.receiveDatagrams();
localQuicTransport.stop();
promise_rejects(t, 'InvalidStateError', promise);
}, 'receiveDatagrams() promise rejected with InvalidStateError if stop() ' +
'is called before being fulfilled.');
promise_test(async t => {
const [ localQuicTransport, remoteQuicTransport ] =
await makeTwoConnectedQuicTransports(t);
const promise = localQuicTransport.receiveDatagrams();
localQuicTransport.transport.stop();
promise_rejects(t, 'InvalidStateError', promise);
}, 'receiveDatagrams() promises rejected with InvalidStateError if ' +
'RTCIceTransport calls stop() before being fulfilled.');
</script>

View file

@ -91,6 +91,14 @@
if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
assert_equals(xhr.status, 200, "JS never gets to see the 30x status code");
}
// The UA may fire multiple "readystatechange" events while in
// the "loading" state.
// https://xhr.spec.whatwg.org/#the-send()-method
if (xhr.readyState === 3 && actual[actual.length - 1] === "xhr onreadystatechange 3") {
return;
}
actual.push("xhr onreadystatechange " + xhr.readyState);
});
xhr.onload = test.step_func(function(e)